I am having problem with having a GUI interface to be implemented via a second file which just contains the file to read, plots made and some new functions to be evaluated based on that.
I am trying to create a GUI application using Tkinter. The way I am doing is as follows. I have a background script (say Background.py) which has two functions. Function X loads a data file, does some calculations and outputs a graph. The way I want to trigger this is via a GUI script in another file (GUI.py) which opens a panel with a button and when I click the button the function X in file Background.py should be evaluated and a plot should be shown. Once I check the plot, I can hit another button to close the plot and terminate the function X. Now I can choose to click another button to trigger the function Y in the file Background.py. These button should allow me to input three values, which should be the input to the function Y in the file Background.py. Once I hit this button, it should trigger the function Y and do what it it asks it to do. Now at the end, after that I can hit the button to close the gui.
How can I do this?. A general rough idea will be helpful.
I have put an example as much as I can:( at least the skeleton of the code)
I have a background file say (Background.py) and gui file ( say GUI.py)
Background.py
import numpy
import matplotlib.pyplot as plt
import pandas
def progX():
df = pd.read (myfile)
##df.stats # doing something and generating a plot from the file
plt.boxplot(df['col'])
plt.show()
def progY(y1, y2,y3):
## get the y1, y2, y3 from the GUI interface which the user has entered
#run a code... and generate an output file
GUI.py
import Background as bg
from tkinter import *
from tkinter.ttk import *
class GUI ():
def create widgets(self):
#....
def create_panel2(self):
#create buttons
panel1 = ...
btn1 = Button(panel1, text="yyyyy", command=bg.progA)
btn1.pack()
def create_panel2(self):
#create buttons
panel2 = ...
btn2 = Button(panel1, text="yyyyy", command=bg.progB)
btn2.pack()
All_Entries = []
window = Tk()
D=GUI(window)
window.mainloop()
runprogram1 = bg.progX()
runprogram2 = bg.probY(x, y, z)
My question is now, does the above makes sense? How can I call the background functions from the GUI? The statements runprogram1 & runprogram2 are definitely not correct, How can I implement that. Also how will I ensure that I call the proram Y in Background once I have close the output from the program X?
I guess the questions makes sense. I am new to GUI and having hard time working this out, which I need to. any help will be very much appreciated.
I'm assuming progA == progX, and progB == progY?
As your code is currently structured, some function in the GUI needs to get y1, y2 and y3 from a widget (Entry(), presumably), and pass to progY. progY can't fetch that info, b/c progY isn't aware of the widgets in the GUI. Get those values in the GUI class by binding the button to another function that 1) calls .get() on the Entry() widget, and then 2) passes those values to progY.
Make your Entry boxes in the GUI:
e1 = Entry(panel1)
e2 = Entry(panel1)
e3 = Entry(panel1)
self.entries = (e1, e2, e3)
for e in self.entries:
e.pack()
Make a function that gets values and calls progY in the GUI:
def get_entries_call_y(self):
e = [x.get() for x in self.entries]
bd.progY(e[0], e[1], e[2])
Bind your button to get_entries_call_y (not to bd.progY):
btn2 = Button(panel1, text="yyyyy", command=get_entries_call_y)
If you'd like advice on structuring a GUI program in general, try adhering (as best you can) to a standard user interface architecture like model-view-controller (https://en.wikipedia.org/wiki/Model-view-controller). The solution I described above should make your program work, but the way you've structured your program isn't really a good way to do it for a lot of reasons. Learning MVC will help you organize what task should go into what function/class/file, give you a logical framework for adding features and improving your code, and allow you to create new GUI programs more efficiently.
Or at least that's what I got out of learning MVC.
Related
I'm developing an application what have its functions set in different files.
The main file have a tkinter interface and the buttons, entrys and labels are in other file, like this:
Mainfile.py
from tkinter import *
class Program:
def __init__(self, root):
root.geometry('200x200')
self.main_frame = Frame(root)
self.main_frame.pack()
import Buttons
self.branch = Buttons.New_Button(self.main_frame)
#Here i wuold like to verify the hipotetic variable after the main_frame were destroyed
if self.branch.hipotetic_variable:
root.mainloop()
app = Program(Tk())
Buttons.py
from tkinter import *
import functools
class New_Button:
def __init__(self, using_frame):
self.button_1 = Button(using_frame, text = 'Button 1', command=functools.partial(self.Func, using_frame))
self.button_1.pack()
def Func(self, to_destroy):
to_destroy.destroy()
#Here is the hipotetic variable what i would like to verify with if statment
self.hipotetic_variable = True
The problem is that I want to keep managing the program in the main file calling the other functions and implementing it, but I cannot verify if it's time to update the screen because mainloop makes impossible to verify it using a while loop and an hipotetic variable that's created after user pressed button.
I wold like to know if there is an way to update an variable contained in the Buttons.py file on Mainfile.py to keep implementing all other canvas in this file.
Your if self.branch.hipotetic_variable: check in the Program.__init__() method is only going to be executed when the Program class instance gets created initially, which is before the button that could change the value of the variable could have been pressed. You also don't want to make the hipotetic_variable an attribute of the Button because that will be destroyed along with the Frame it is in when that's destroyed in the button callback function.
Tkinter applications are user-event driven, meaning that they're "run" by responding to events (that's what mainloop is all about). This type of programming paradigm is different from the procedural or imperative one you're probably used to.
Therefore to do what you want requires setting things up so an event that the program can respond to will be generated, which in this case to when the frame is destroyed. One way to do that is by taking advantage of tkinter Variable classes to hold this hipotetic variable you're interested in. It looks like a boolean, so I used a tkinter BooleanVar to hold its value. One interesting thing about Variables is that you can have changes to their values "traced" by defining functions to be called whenever that happens. That's what I have done in the code below, and the callback function in this case — check_hipotetic_variable() — updates a Label to display the new value of the variable when it's called.
Below is your code with the modifications necessary to use a tkinter BooleanVar and trace changes to its value.
Mainfile.py
from tkinter import *
import Buttons
class Program:
def __init__(self, root):
root.geometry('200x200')
self.main_frame = Frame(root)
self.main_frame.pack()
self.notice_lbl = Label(root, text='')
self.notice_lbl.pack(side=BOTTOM)
self.hipotetic_variable = BooleanVar(value=False)
# Set up a trace "write" callback for whenever its contents are changed.
self.hipotetic_variable.trace('w', self.check_hipotetic_variable)
self.branch = Buttons.New_Button(self.main_frame, self.hipotetic_variable)
root.mainloop()
def check_hipotetic_variable(self, *args):
"""Display value of the hipotetic variable."""
value = self.hipotetic_variable.get()
self.notice_lbl.config(text=f'hipotetic variable is: {value}')
app = Program(Tk())
Buttons.py
from tkinter import *
import functools
class New_Button:
def __init__(self, using_frame, variable):
self.button_1 = Button(using_frame, text = 'Button 1',
command=functools.partial(self.Func, using_frame))
self.button_1.pack()
self.variable = variable # Save for use in callback.
def Func(self, to_destroy):
to_destroy.destroy()
self.variable.set(True) # # Change value of the variable.
P.S. I noticed you're not following the PEP 8 - Style Guide for Python Code, which makes reading your code harder to read and follow that if you're were following them — for that reason I strongly suggest you read the guide and start following the suggestions, especially the Naming Conventions which apply to functions and variable names, as well as the names of script files.
I am looking for a simple way to display changing real time data in a GUI in python. I am connected to 2 devices and want to display data constantly (like 20 different values), and when I press a button I want to control the one device.
Unfortunately I fail already with the display of the data. For this I have looked at some tkinter tutorials and explanations.
My idea was to implement it with a config function and to overwrite the label continuously. As example how I wanted to display one value:
import tkinter as tk
from pydualsense import pydualsense
# connect to the device
dualsense = pydualsense()
dualsense.init()
# create a window
window = tk.Tk()
# function for updating data
def show_data():
global dualsense
data_label_output.config(text=dualsense.state.LX)
# showing the data as a lable
data_label_output = tk.Label(window)
data_label_output.grid(row=1, column=1)
show_data()
#### or different solution
# showing the data as a lable
data_label_output = tk.Label(window, comand=show_data)
data_label_output.grid(row=1, column=1)
window.mainloop()
Unfortunately, the value is displayed only once at the beginning and nothing changes after that.
Another problem:
When I press the button, I want to be able to control the one device. For this I have a while True loop that permanently checks if a button is pressed and then executes actions. As a separate program no problem, but how do I integrate this into the tkinter GUI? When I start this PyCharm always crashes.
I use PyCharm and Python 3.8
About simple and functional ideas I would be happy, also to other tools/modules etc., as long as you can easily and quickly implement the idea. It's only for a research project and the programming is only a means to an end.
You can use the after method in tkinter to run something after a short delay. The following code will run show_data once the GUI is ready and then again every 1000 milliseconds.
import tkinter as tk
from pydualsense import pydualsense
# connect to the device
dualsense = pydualsense()
dualsense.init()
# create a window
window = tk.Tk()
# function for updating data
def show_data():
global dualsense
data_label_output.config(text=dualsense.state.LX)
window.after(1000,show_data)
# showing the data as a lable
data_label_output = tk.Label(window)
data_label_output.grid(row=1, column=1)
window.after_idle(show_data)
window.mainloop()
This resolves the updating issue, I'm not sure what behaviour you want when you press the button but if you elaborate and explain, I might be able to help and update this answer.
I've made a script that uses a while True loop to constantly update a series of variables based on UDP packets that I am constantly recieving. I want to ultimately create a GUI that displays that data and updates the screen constantly, which I plan to do with tkinter (using my_label.after in a function which then calls itself, not sure if this is a good plan).
Here is some testing scripts that I can't get to work properly:
GUI2.py (my test looping script)
import time
var = 0
while True:
var += 1
time.sleep(0.1)
GUI Testing.py (the script that would be accessing those variables)
from GUI2 import *
import time
print('never')
print(var)
time.sleep(1)
The second script never reaches the print('never') line, I think because it gets stuck in the other script's while True loop and never returns.
How should I go about this? I have one script that I want in a constant loop to update my variables to the correct values based on incoming packets, and then another script updating a tkinter window. I went this way as most examples I could find using Tkinter didn't use any sort of while True loops. Could I just put my packet recieving code inside the Tkinter mainloop, and would that effectively act as a while True?
EDIT (added Tkinter loop that I can't get working):
This opens a Tkinter window, but the label stays at 99, then reopens a window when I close it with the new x value (ie. 98, 97, etc). I want the label to update every second.
import tkinter as tk
import time
x = 99
while True:
root = tk.Tk()
label = tk.Label(root, text=x)
label.pack()
x -= 1
time.sleep(1)
root.mainloop()
Below is a sample script to show you how you can update the value in the label widget at a certain time interval. I have provided you the hyperlinks to help you understand tkinter's methods. Best regards.
Key points:
use the textvariable option of the tk.Label widget.
use tkinter's control variable. I have shown you how to set and get it's value.
you can use tkinter's widget method called .after() without having to explicitly use a while-statement and time.sleep() method. Tkinter has it's own event loop that you can use.
writing your tkinter GUI as a class makes it easier to implement what you need.
Example Script:
import tkinter as tk
class App(tk.Frame):
def __init__( self, master, *args, **kw ):
super().__init__( master )
self.master = master
self.create_label()
self.update_label()
def create_label( self ):
self.var = tk.IntVar() # Holds an int; default value 0
self.label = tk.Label(self, textvariable=self.var ) # Use textvariable not text
self.label.pack()
def update_label( self ):
value = self.get_value()
self.var.set( value ) # Set the label widget textvariable value.
self.after(1000, self.update_label) # Call this method after 1000 ms.
def get_value( self ):
'''To simulate calling a function to return a value'''
value = self.var.get() + 1
return value
if __name__ == "__main__":
root = tk.Tk()
root.geometry('100x100+0+24')
app = App( root )
app.pack()
root.mainloop() #This command activates tkinter's event loop
Edit:
As a clarification, this answer shows how to utilize the .after() and .mainloop() methods in GUI Testing.py, i.e. using tkinter event loop and not use two while-loops, to achieve what you wanted to do. This is a way to simplify your GUI script.
For more sophisticated algorithms, e.g. more than one while-loop is involved, you have to look into using threads(note it has its issues) or more recently I found a way of using python's Asyncio approach to do it. The learning curve for these two approaches is a lot steeper. To use the asyncio approach, you can explore modifying my answer to do what you want.
Best solution is to use threads however If you plan to do in simplest possible manner then implement the main loop inside your Tkinter GUI and once you read the packet simply update it on your GUI in same loop. Here is the Updated and working Code.
import tkinter as tk
import time
def setvalue(self, x):
self.label.config(text=x, )
root.update()
time.sleep(1)
def changevalues(self):
x = 99
self.label = tk.Label(root, text=x)
self.label.pack()
while x >0:
x -= 1
setvalue(root,x)
root = tk.Tk()
changevalues(root)
root.mainloop()
I have a code that works well for bulk data analysis and plotting. But now i'm trying to incorporate it into a larger data analysis GUI. I find that when i run my code on its own, all goes well. But when i call it from the main code and run it from a tkinter button, it's not the same. Everything looks the same and it runs smoothly, the only difference is that no files are saved.
i think maybe it's a problem with which window is defined with "____init____"? or something with how i create and destroy Tk() windows within the subcode?
**the stackoverflow text editor uses underscores to make text bold/itallic, so for all cases that double underscores are used to wrap "init" or "main" in python, i had to use four on each side here
my code (saved as SubCode.py):
def AnalysisFunction():
*does things*
main = Tk()
os.chdir(OutputFolder)
plt.savefig('image.png')
main.destroy()
if __name__ == '__main__':
AnalysisFuntion()
the code i want to add mine into:
import SubCode
class TopLevel(Frame):
def __init__(self, master):
Frame.__init__(self,master)
*Creates main GUI window*
MyButton = Button(root, command = self.CallSubCode)
def CallSubCode(self):
SubCode.AnalysisFunction()
root = Tk()
main_window = TopLevel(root)
root.mainloop()
Any ideas why the subcode alone can save figures but it cannot when called by the larger GUI? FYI, it still creates all variables correctly when running through the larger GUI.
I think you should just save the image in SubCode.py without creating a tkinter window. i.e
def AnalysisFunction():
*does things*
os.chdir(OutputFolder)
plt.savefig('image.png')
if __name__ == '__main__':
AnalysisFuntion()
i figured it out, I had to put the whole SubCode within a class structure, then call it as its own Toplevel app. I think otherwise the plt.savefig command doesn't know which Tkinter window it is working with, and tries to find data in the "host" window, not the one which is handling the data.
I have a general problem, which I sketch here as the details will be too involved to post here. I know the problem statement, is a bit fuzzy and I may need to go back and forth with an expert in this site. (unfortunately, it is difficult to put everything up here based on the type of the problem. I will really appreciate any help).
I am trying to create a GUI application using Tkinter. The way I am doing is as follows. I have a background script (say back.py) which has the data loaded, calculations done and graph plotted.
Now the way I want to do is that I have a GUI script which uses Tkinter and calls the back.py ( using import). Now I have the window created, with a button. This is where I am stuck. I want to click the button to trigger the background script and generate the plots ( which the background script generates).
After this I want to close the plot and want my GUI to pop up some buttons to input me some parameters. These parameters will be input to the next part of the back.py code ( I chose the parameters based on the plot). When I again click the button ( with the parameters selected), I want to start running the background code again which will output me a file.
How can I do this?. A general rough idea will be helpful.
Let me put an example as much as I can:( at least the skeleton of the code)
I have a background file say (a.py) and gui file ( say g.py)
a.py
import ...
def progA():
# reading a file
# doing something and generating a plot from the file
# Once the GUI's first part is done generating the plot, I need to close that
# plot (or button) and then click the button 2 to run the next function
def progB(y1, y2,y3):
#run a code... and generate an output file
g.py
from tkinter import *
from tkinter.ttk import *
class GUI ():
def create widgets(self):
#....
def create panel(self):
#create buttons
panel1 = ...
btn1 = Button(panel1, text="yyyyy", command=progA)
btn1.pack()
def create_panel1(self):
#create buttons
panel1 = ...
btn1 = Button(panel1, text="yyyyy", command=progA)
btn1.pack()
def create_panel2(self):
#create buttons
panel2 = ...
btn2 = Button(panel1, text="yyyyy", command=progB)
btn2.pack()
All_Entries = []
window = Tk()
D=GUI(window)
window.mainloop()
import a
runprogram1 = a.progA()
runprogram2 = a.probB(x, y, z)
My question is now, does the above makes sense? So I have a couple of questions:
How will I ensure that when I close the plots (from the output of progA), that the second button will show up?
Where can I input the there values of the parameters in the second button?