User Interaction with Graph in GUI - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: GUI (https://python-forum.io/forum-10.html) +--- Thread: User Interaction with Graph in GUI (/thread-41781.html) |
User Interaction with Graph in GUI - Marty23 - Mar-18-2024 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() RE: User Interaction with Graph in GUI - deanhystad - Mar-19-2024 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() |