Python Forum
User Interaction with Graph in GUI
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
User Interaction with Graph in GUI
#1
So I am relatively new to gui's in python, but I am trying to make an interactive graph, such that a user enters in 3 values in the boxes given, this is called "sample_point" and an 'X' symbol is then plotted on the graph. The issue I am having is I want the graph to be present when the program is run, not after the button is pressed. I also want the 'X' to change according to the values entered by the user on the graph, 'X' will only change position when the user has pressed the button after changing the input in the boxes. I have been using the point 41,41,1 in the boxes to test. I hope someone can shed some light to my issues!

import tkinter as Tk
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.figure import Figure 
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg,  
NavigationToolbar2Tk) 

class App(object):
    def __init__(self):
        self.root = Tk.Tk()
        self.root.geometry("500x500") 
        self.root.wm_title("DGA Analysis Tool Using Duval Triangle")
        self.label = Tk.Label(self.root, text="C2H2")
        self.label.pack()

        self.C2H2 = Tk.StringVar()
        Tk.Entry(self.root, textvariable=self.C2H2).pack()

        self.C2H4 = Tk.StringVar()
        Tk.Entry(self.root, textvariable=self.C2H4).pack()

        self.CH4 = Tk.StringVar()
        Tk.Entry(self.root, textvariable=self.CH4).pack()

        self.buttontext = Tk.StringVar()
        self.buttontext.set("Calculate")
        
        Tk.Button(self.root,
                  textvariable=self.buttontext,
                  command=self.clicked1).pack()

        self.label = Tk.Label(self.root, text="")
        self.label.pack()

        fig = Figure(figsize = (5, 5), 
                     dpi = 100)
        
        
        self.root.mainloop()
    

    def clicked1(self):
        C2H2_CON = self.C2H2.get()
        C2H4_CON = self.C2H4.get()
        CH4_CON = self.CH4.get()

         
        #
        # Define the transformation matrix
        #
        A = np.array([[5, 2.5, 50], [0, 5 * np.sqrt(3)/2, 50], [0, 0, 1]])

        #
        # Define a set of points for Duval triangle regions
        #
        p = np.array([
            [0, 0, 1],          #0-p1
            [0, 100, 1],        #1-p2
            [100, 0, 1],        #2-p3
            [0, 87, 1],         #3-p4
            [0, 96, 1],         #4-p5
            [0, 98, 1],         #5-p6
            [2, 98, 1],         #6-p7
            [23, 0, 1],         #7-p8
            [23, 64, 1],        #8-p9
            [20, 76, 1],        #9-p10
            [20, 80, 1],        #10-p11
            [40, 31, 1],        #11-p12
            [40, 47, 1],        #12-p13
            [50, 35, 1],        #13-p14
            [50, 46, 1],        #14-p15
            [50, 50, 1],        #15-p16
            [71, 0, 1],         #16-p17
            [85, 0, 1]])        #17-p18

        #
        # Apply the coordinates transformation to all points
        #
        v = p @ np.transpose(A)

        #
        # Set one more sample point
        #

        sample_point = np.array([int(C2H2_CON), int(C2H4_CON), int(CH4_CON)]) @ np.transpose(A)
        
        #
        # Define each of the regions by the coordinates of its angle points
        #
        region_PD = v[[5, 1, 6], :]
        region_T1 = v[[4, 5, 6, 10, 9], :]
        region_T2 = v[[9, 10, 15, 14], :]
        region_T3 = v[[13, 15, 2, 17], :]
        region_D1 = v[[0, 3, 8, 7], :]
        region_D2 = v[[7, 8, 12, 11, 16], :]
        region_DT = v[[3, 4, 14, 13, 17, 16, 11, 12], :]

        #
        # Plot the results
        #
        fig, ax1 = plt.subplots()
        ax1.fill(region_PD[:, 0], region_PD[:, 1], '#2e962d')
        ax1.fill(region_T1[:, 0], region_T1[:, 1], '#bebe12')
        ax1.fill(region_T2[:, 0], region_T2[:, 1], '#ff642b')
        ax1.fill(region_T3[:, 0], region_T3[:, 1], '#b46414')
        ax1.fill(region_D1[:, 0], region_D1[:, 1], '#10b4a7')
        ax1.fill(region_D2[:, 0], region_D2[:, 1], '#121eb4')
        ax1.fill(region_DT[:, 0], region_DT[:, 1], '#f217d0')
        ax1.scatter(sample_point[0], sample_point[1], marker='x', c='r', zorder=2)
        ax1.grid(linestyle='--', alpha=0.4, axis='both')

        #
        # Also place axes captions
        #
        label1 = np.array([45, -5, 1]) @ np.transpose(A)
        ax1.text(label1[0], label1[1], '%C2H2')
        label11 = np.array([95, -5, 1]) @ np.transpose(A)
        ax1.text(label11[0], label11[1], '0')
        label12 = np.array([5, -5, 1]) @ np.transpose(A)
        ax1.text(label12[0], label12[1], '100')
        label2 = np.array([-10, 55, 1]) @ np.transpose(A)
        ax1.text(label2[0], label2[1], '%CH4')
        label21 = np.array([-7, 5, 1]) @ np.transpose(A)
        ax1.text(label21[0], label21[1], '0')
        label22 = np.array([-7, 95, 1]) @ np.transpose(A)
        ax1.text(label22[0], label22[1], '100')
        label3 = np.array([45, 55, 1]) @ np.transpose(A)
        ax1.text(label3[0], label3[1], '%C2H4')
        label31 = np.array([5, 95, 1]) @ np.transpose(A)
        ax1.text(label31[0], label31[1], '0')
        label22 = np.array([95, 5, 1]) @ np.transpose(A)
        ax1.text(label22[0], label22[1], '100')

        #
        # Show the final plot
        #
        ax1.set_xlim(0, 600)
        ax1.set_ylim(0, 550)
        
        canvas = FigureCanvasTkAgg(fig, self.root)   
        canvas.draw()
        
        # placing the canvas on the Tkinter window 
        canvas.get_tk_widget().pack() 
      
        # creating the Matplotlib toolbar 
        toolbar = NavigationToolbar2Tk(canvas, 
                                       self.root) 
        toolbar.update() 
      
        # placing the toolbar on the Tkinter window 
        canvas.get_tk_widget().pack()
        
      
        



App()
Reply
#2
Instead of a calculate button, the code below binds the plot update method to changes made in any of the entries. Insead of repeating the code three times for the three Entry widgets U made a special Entry class that does the binding. By making a new class, it was easy to add input validation and a value property.
import tkinter as tk
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)


class Entry(tk.Entry):
    """Special tk Entry for numbers with a command callback."""
    def __init__(self, *args, command=None, **kwargs):
        super().__init__(*args, width=10, justify=tk.RIGHT, **kwargs)
        self.command = command
        self.bg = self["bg"]
        self.var = tk.StringVar(self, "0")
        self.var.trace_add("write", self._validate)
        self.configure(textvariable=self.var)

    def _validate(self, *_):
        """Set red background if text is inalid, else call command."""
        if self.value is None:
            self["bg"] = "red"
        else:
            self["bg"] = self.bg
            if self.command:
                self.command()

    @property
    def value(self):
        """Return value as number or None if text is not valid str."""
        try:
            return float(self.var.get())
        except ValueError:
            pass
        return None

    @value.setter
    def value(self, new_value):
        """Set value."""
        self.var.set(str(new_value))
        self._validate


class App(tk.Tk):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.wm_title("DGA Analysis Tool Using Duval Triangle")
        self.hold = False

        tk.Label(self, text="C2H2").grid(row=0, column=0, sticky="e")
        self.c2h2 = Entry(self, command=self.plot)
        self.c2h2.grid(row=0, column=1, sticky="w")

        tk.Label(self, text="C2H4").grid(row=0, column=2, sticky="e")
        self.c2h4 = Entry(self, command=self.plot)
        self.c2h4.grid(row=0, column=3, sticky="w")

        tk.Label(self, text="CH4").grid(row=0, column=4, sticky="e")
        self.ch4 = Entry(self, command=self.plot)
        self.ch4.grid(row=0, column=5, sticky="w")

        fig, self.ax1 = plt.subplots()
        self.ax1.grid(linestyle='--', alpha=0.4, axis='both')
        self.ax1.set_xlim(0, 600)
        self.ax1.set_ylim(0, 550)
        self.canvas = FigureCanvasTkAgg(fig, self)
        self.canvas.get_tk_widget().grid(row=1, column=0, columnspan=6)
        self.toolbar = NavigationToolbar2Tk(
            self.canvas, self, pack_toolbar=False
        )
        self.toolbar.grid(row=2, column=0, columnspan=6, sticky="news")
        self.plot()

    def plot(self):
        """Update plot to show new sample point."""
        # Don't plot if held or an input is invalid.
        sample_point = self.c2h2.value, self.c2h4.value, self.ch4.value
        if self.hold or None in sample_point:
            return

        # Changing the sample point forces clearing the axis.
        # Need to create the regions and labels even though they
        # don't change.
        self.ax1.clear()
        AT = np.transpose(
            np.array([[5, 2.5, 50], [0, 5 * np.sqrt(3)/2, 50], [0, 0, 1]])
        )

        v = np.array([
            [0, 0, 1], [0, 100, 1], [100, 0, 1], [0, 87, 1], [0, 96, 1],
            [0, 98, 1], [2, 98, 1], [23, 0, 1], [23, 64, 1], [20, 76, 1],
            [20, 80, 1], [40, 31, 1], [40, 47, 1], [50, 35, 1], [50, 46, 1],
            [50, 50, 1], [71, 0, 1], [85, 0, 1]
        ]) @ AT
        for color, region in (
            ('#2e962d', v[[5, 1, 6], :]),
            ('#bebe12', v[[4, 5, 6, 10, 9], :]),
            ('#ff642b', v[[9, 10, 15, 14], :]),
            ('#b46414', v[[13, 15, 2, 17], :]),
            ('#10b4a7', v[[0, 3, 8, 7], :]),
            ('#121eb4', v[[7, 8, 12, 11, 16], :]),
            ('#f217d0', v[[3, 4, 14, 13, 17, 16, 11, 12], :])
        ):
            self.ax1.fill(region[:, 0], region[:, 1], color)

        for label, x, y in (
            ('%C2H2', 45, -5), ('0', 95, -5), ('100', 5, -5),
            ('%CH4', -10, 55), ('0', -7, 5), ('100', -7, 95),
            ('%C2H4', 45, 55), ('0', 5, 95), ('100', 95, 5)
        ):
            x, y, _ = (x, y, 1) @ AT
            self.ax1.text(x, y, label)

        # This is the part that changes.
        x, y, _ = np.array(sample_point) @ AT
        self.ax1.scatter(x, y, marker='o', color='r', edgecolors=['w'], zorder=2)
        self.canvas.draw()

    def set_concentrations(self, c2h2=None, c2h4=None, ch4=None):
        """Set concentration value(s)."""
        self.hold = True
        self.c2h2.value = c2h2 or self.c2h2.value
        self.c2h4.value = c2h4 or self.c2h4.value
        self.hold = False
        self.ch4.value = ch4 or self.ch4.value

window = App()
window.set_concentrations(41, 41, 1)
window.mainloop()
Marty23 likes this post
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Help with tkinter Button and Label interaction issues ... sealyons 0 4,727 Jun-01-2017, 06:58 PM
Last Post: sealyons

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020