I'm currently trying to make a small application with a GUI that pulls weather from a website and displays the results in a window. I've got it to work without the GUI and also with the GUI but when I wrote the latter it was all in one script and not very organized. Because it was so unorganized, I decided to make make a separate script that would draw the GUI when the class was called.
Part of the GUI is an 'Entry' box that can be added via Tkinter. The entry box stores it's content into a StringVar() and that content can displayed using .get(). This works fine and well when I wrote everything unorganized into one script but I can't for the life of me figure out how to pass this StringVar() from one method to another in my program. This is what it looks like:
from Tkinter import *
import Forecast
class Frames(object):
def __init__(self):
pass
def main_frame(self):
main = Tk()
main.title('WeatherMe')
main.geometry('300x100')
query = StringVar()
Label(main, text='Enter a city below').pack()
Entry(main, textvariable=query).pack()
Button(main, text="Submit", command=self.result_frame).pack()
main.mainloop()
def result_frame(self):
result = Tk()
result.title('City')
result.geometry('600x125')
Button(result, text="OK", command=result.destroy).pack()
result.mainloop()
Basically my goal is to have one window open when the program is launched with a label, an entry box, and a submit button. When a city is entered in the entry box and submit it clicked a new window will open displaying the results.
Because the entry is on the first window I need to pass the value of entry's StringVar() to the second window so it can then pull the data and display the labels. No matter what I try it doesn't seem to work, I either get a 404 error meaning something is wrong with that string making the link it tries to get a response from invalid or a concatenate error 'cannot concatenate str and instance objects'.
I've also tried saving StringVar() as a variable outside of either method but the issue with that is I need to then call another instance of Tk() before StringVar().
You are creating two separate instances of Tk. You shouldn't do that. One reason why is because of this exact problem: you can't share instances of widgets or tkinter variables between them.
If you need more than one window, create a single root window and then one or more instances of Toplevel. Also, call mainloop only for the root window.
Related
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 am a beginner and I am making a login system (just for practicing).
I'm using tkinter to develop a simple UI. The thing is that when I call a second root (sign_in root) with a button from another root (main_screen), and I try to get some values typed in entry with StringVars assigned to them, they return just an empty string ""
def main_screen():
root=Tk()
user=StringVar()
pas=StringVar()
btn2=Button(root,text='Sign-In',command=sign_in_screen)
btn2.place(x=125,y=160)
root.mainloop()
def sign_in_screen():
root1=Tk()
newuser=StringVar()
newpas=StringVar()
ent3=Entry(root1,width=28,textvariable=newuser)
ent3.place(x=100,y=50)
ent4=Entry(root1,width=28,textvariable=newpas,show="*")
ent4.place(x=100,y=100)
btn3=Button(root1,text='Sign-In',command=lambda:register(newuser.get(), newpas.get()))
btn3.place(x=50,y=160)
root1.mainloop()
main_screen()
Having multiple instances of Tk become hideously complicated because each one creates a separate tcl interpreter. This causes weird effects like what you see here. It's you almost always want to use the Toplevel widget.
The question's in the title, essentially: how do I get the validatecommand callback to continue being called after setting the Entry's textvariable? Here's the Minimum Working Example (MWE):
import tkinter as tk
root = tk.Tk()
sv = tk.StringVar()
def callback():
print(sv.get())
sv.set('Set Text.')
return True
e = tk.Entry(root, textvariable=sv, validate="focusout",
validatecommand=callback)
e.grid()
e = tk.Entry(root)
e.grid()
root.mainloop()
Note that the second tk.Entry widget is there to allow the first one to lose focus, which is the event we're trying to capture.
As the code is now, when you run it, you can change the top Entry widget's text once. It'll correctly get set to Set Text. Then, if you try to change the Entry's text again, the new text will be in the widget, but the callback doesn't happen.
On the other hand, if you comment out the sv.set('Set Text.') code, this behavior completely disappears, and the callback gets called as many times as you wish.
How can I have the sv.set() functionality, while still maintaining the callback getting called every time the Entry widget loses focus?
This is discussed in the Tk manual page for entry:
The validate option will also set itself to none when you edit the entry widget from within either the validateCommand or the invalidCommand. Such editions will override the one that was being validated.
Presumably, this is done to avoid infinite recursion.
You can run this (instead of the given Tcl code, after idle {%W config -validate %v})
root.after_idle(lambda: e.config(validate="focusout"))
from the callback to schedule a reconfiguration of the widget to enable validation again (after changing your sources so that e is the right Entry widget, i.e. not the second one).
I am quite new to tkinter, and I'm struggling with my code, which is supposed initiate a button and entry box in tkinter, and then wait for the user to input "train", click the button, and add 1 to the training variable, erase all widgets. Then, the code should come back to the while loop, re-initiate the widgets and repeat, while saving the training variable. However, when I run my code, the loop goes through once, and even if the user enters "train" more than once, the variable stays at 1. I've tried making the variable training an IntVar() type, but then I can't add 1 to it in the line "training=training+1". Any ideas?
import tkinter as tk
def main():
training=0
x="train"
while(x=="train"):
x=tk.Entry(root)
x.pack()
tr=tk.Button(root, text='Ok', command=lambda: amount(training,x.get()))
tr.pack()
def amount(training,x):
if(x=="train"):
training=training+1
list = root.slaves()
for slave in list:
slave.forget()
main()
I'm using TKinter (I'm new with GUI tools), and I would like to know if it is possible to add (or activate) a entry with base on the answer of a option menu. Below is a part of the code
from Tkinter import *
win=Tk()
Label(win, text="Is This a Data Cube?",font='20').grid(row=14, column=0,sticky=W)
DataCubeValue = StringVar(win)
DataCubeValue.set("False")
DataCube = OptionMenu(win,DataCubeValue,"True","False")
DataCube.grid(row=15, column=0,sticky=W)
If the answer is True is choosen I would like to display this:
Label(win, text="X and Y values (x,y)",font='20').grid(row=14, column=1,sticky=W)
XYValue = StringVar(win)
XYValue.set("10,7")
XY = Entry(win,textvariable=XYValue)
XY.grid(row=15, column=1,sticky=W)
A central idea of GUI programming is to register code to be executed in reaction of user actions. Such code is usually named callback (the toolkit call it back depending on user actions on the interface).
You can bind to DataCubeValue changes with the following line. callback method (to be defined before) will be cause each time the value of DataCubeValue change.
DataCubeValue.trace("w", callback)
In the callback method, you can either choose to place the block of code with Label and Entry instantiation, but think that callback will be called every time the user change the OptionMenu value. You could either deactivate the OptionMenu once the user used it, but I would advise to instantiate your widgets in the initial run, and just display or hide them from the callback.
def callback(*args):
if DataCubeValue.get() == "True":
label.grid(row=14, column=1,sticky=W)
XY.grid(row=15, column=1,sticky=W)
else:
label.grid_forget()
XY.grid_forget()