can't get values from variable in tkinter - python-2.7

app=Tk()
age=IntVar()
name=StringVar()
id=IntVar()
def add_user():
app1=Tk()
L1 = Message(app1,text="Name")
L1.pack( side = LEFT)
E1 = Entry(app1,textvariable=name)
E1.pack()
L2 = Message(app1,text="\nAge")
L2.pack( side = LEFT)
E2 = Spinbox(app1,from_=1,to_=100,textvariable=age)
E2.pack()
l3=Message(app1,text="\nId")
l3.pack()
e3=Spinbox(app1,from_=1,to_=100,textvariable=id)
e3.pack()
b5=Button(app1,text="submit",command=app1.destroy)
b5.pack()
app1.mainloop()
print age.get(),name.get(),id.get()
return
b1=Button(app,command=add_user,relief=RIDGE,text="add patient details")
b1.pack(side=BOTTOM)
app.mainloop()
the print statement doesn't print the correct values,it always prints the default values.I don't understand where I made a mistake

The reason you can't get the values is that the widgets have been destroyed once mainloop has exited.
The bigger problem in your code is that you are creating two instances of Tk. Tkinter isn't designed to work that way. A proper Tkinter program creates exactly one instance of Tk, and exits when that one instance is destroyed. If you want to create more than one window, the second and subsequent windows need to be instances of Toplevel.
You might find the answer to this question useful: Correct way to implement a custom popup tkinter dialog box

Related

Is it possible to use data outside of GUI?

I want to use GUI to input something (anything like numbers or words), then I want to analyze what I have inputted. But it seems that I cannot do it unless I quit this GUI.
def Ok():
global str_
str_=sth.get()
print str_
def input_():
App=tk.Tk()
global sth
sth=tk.Entry(App, width=10)
sth.pack(side=tk.TOP)
btn=tk.Button(App,text="OK", width=10, command=Ok)
btn.pack(side=tk.TOP)
App.mainloop()
def test():
input_()
print 'You input %d words'%len(str_)
if __name__=='__main__':
from tkinter import *
import tkinter as tk
test()
In this example, I hope I can get the length of the string. I tried and I can indeed get it if I close GUI first. Is it possible to get it without closing GUI?
What do you mean, exactly, Is it possible to use data outside of GUI?
? Your global variable str_ can be accessed inside of all the functions in your app, as you have written it. The print statement you have won't be triggered right now until the tkinter mainloop() is closed. If you create an event to trigger that print it will work. You could bind it to a button, or bind it to the user pressing enter. Right now your script simply isn't getting to that line until after mainoop() is quit.

Manual Scroll with Tkinter leaving last element

I've written this piece of code with intention of making something similar to Excel, where you can scroll up and down in a spreadsheet. I have found, however, that when you scroll back up, that it leaves the lowest cell instead of scrolling it. I can't seem to figure out where I went wrong, any help is appreciated.
from tkinter import *
def scroll(event): #changes from what element to start drawing
global starting_value
starting_value -= event.delta/120
draw_list(starting_value)
def draw_list(starting_value): #draws entries from specified index
for index,i in enumerate(list_of_numbers[starting_value:starting_value+10]):
i.grid(row=index)
list_of_numbers = [Entry() for i in range(100)]
for index,i in enumerate(list_of_numbers):
i.insert(0,index)
i.bind('<MouseWheel>',scroll)
starting_value = 0
draw_list(starting_value)
mainloop()
Every time you scroll, any items that are no longer supposed to be visible are left in their original grid positions: you have perhaps placed new items at the same row/column, but that doesn't actually remove the original items from the grid. You'd need something like this at the top of draw_list() to explicitly un-grid the old items:
for widget in container.grid_slaves():
widget.grid_forget()
Where container is the window or Frame containing your scrolling items - your example code does not actually give this object a name, you would need to do something like container = Tk() so that you can refer to it.

connecting to stateChange events for dynamicallly created QCheckBoxes

I am working on a Python application that plots data from a large file containing records from lots and lots of sources. One of the options I am trying to give the user is the option to only plot for a subset of these sources if so desired. I accomplish this by first reading the files, finding out how many unique things there are, and then creating a QCheckBox() for each, named after its source (each source has a unique name). In this particular case, the data file is parsed into a giant dictionary where the keys are the unique source. I want to connect to the stateChange() event for each checkbox and then disable plotting for that source when the box is unchecked. Which in this case would be adding/removing the source from a list of sources when the box is checked/unchecked. The problem I am running into is that all of my checkboxes end up connecting to the final source in my list.
Initially, the window that is created looks correct, each button is named appropriately. Every time a button gets pressed, the btnstate() is supposed to simply print the text associated with that button. The method works if you can explicitly define each button, as shown by the radio buttons in the example. If you click either, you will get the correct name of the button printed, but when unchecking/rechecking ANY of the check boxes, btnstate prints "test4".
What am I doing wrong?
Here is the code (sources changed to dummy values):
import sys
from PyQt4.QtGui import *
def btnstate(b):
print b.text()
def main():
app = QApplication([])
widget = QWidget()
layout = QVBoxLayout()
widget.setLayout(layout)
radio_layout = QHBoxLayout()
checkbox_layout = QHBoxLayout()
#setup radio buttons for config pop-up window
r1 = QRadioButton("Page Count")
r2 = QRadioButton("Date")
r1.toggled.connect(lambda:btnstate(r1))
r2.toggled.connect(lambda:btnstate(r2))
radio_layout.addWidget(r1)
radio_layout.addWidget(r2)
cbs = []
for idx, serial in enumerate(["test1", "test2", "test3", "test4"]):
temp = QCheckBox(serial)
temp.setText(serial)
temp.setChecked(True)
checkbox_layout.addWidget(temp)
temp.stateChanged.connect(lambda:btnstate(temp))
cbs.append(temp)
layout.addLayout(radio_layout)
layout.addLayout(checkbox_layout)
widget.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I think the reason that this is happening has to do with how Python binds and unbinds references inside the loop. Because temp is being redefined on each iteration, the slot is also being updated, so that it effectively calls the same lambda for every button. That's sort of hand-wavy, because my understanding of the details of Python referencing is not super deep. But I know that the Python bindings to Qt have a lot of problems with Python's referencing and garbage collection, e.g. when deleting widgets, because the Qt object hierarchy doesn't totally work with Python.
Anyway, more practically, there is a pretty easy fix. You can use the functools.partial method to define a partial function as the slot, rather than a lambda. Bind the button as the first object, leaving the button state (emitted as the signal argument) unbound. Like so:
import functools
def btnstate(button, state):
print button.text()
Then in the loop:
for idx, serial in enumerate(['test1', 'test2', 'test3', 'test4']):
temp = QCheckBox(serial)
checkbox_layout.addWidget(temp)
temp.stateChanged.connect(functools.partial(btnstate, serial))
Running this, I now get the correct labels printed when each box is check/unchecked.
Edit:
See this post for another example of Python's reference counting interacting with strange ways with Qt's object hierarchy.

Python 2.7 Tkinter on close function

im making a small app which opens a box using Tkinter when a certain condition is reached.
I dont want to spam the user with theses boxes so i want tkinter to set a variable to True when it starts up, then as it closes set it back to False.
Im making a down website checker/notifier so once the website is back up you get a pop up box letting you know. Right now if you close the box the code will continue and the box will pop up again. However the real problem is the code wont continue in the background.
If i make the code continue to run in the background the every 5 seconds of the conditions being met, another box will pop up and eventually spam the user which is something i dont want.
is there a way to check if there is a tkinter box open, or set a value to false when the close button (or X button) is pressed ?
You can redefine what the close button does:
win = Toplevel()
win.protocol('WM_DELETE_WINDOW', close(win))
def close(window):
window.withdraw()
someboolean = False
Hope this helps!
You can register a callback function for the WM_DELETE_WINDOW (the window is about to be deleted)
Example1:
top = Toplevel()
def on_close(t):
flag = False
t.protocol("WM_DELETE_WINDOW", on_close)
You can also override the destroy function:
Example2:
class CustomToplevel(Toplevel):
def destroy(self):
# Add you code here
Toplevel.destroy(self)

Is it possible to stop Tkinter window freezing during update?

I have a small python project that requires data to be pulled from a network and displayed every second (which is how often it changes) for a scientific application. 3 of these things are simply numbers, while another is a 128x128 camera image, which is brought in as an ndarray and drawn using matplotlib's imshow to a tkinter canvas.
I've tried two methods - using aniamtion.FuncAnimation() and after(interval, function), and both have the same result, which is that while the frame updates, the window can't be moved. and it feels jerky.
I assume that's something that can't be overcome (and probably doesn't matter)? I thought that perhaps multi-threading might help so the main window is on one thread, while the updated data can be on another?
Thanks!
Below is the basic code which now includes threading, and the error when closing the window is fixed by adding an event to the window close function, and also a few break commands while getting data that checks if the window is closed before it tries to interact with a GUI item that doesn't exist.
import blah, blah, blah
global safe_shutdown, window_status, my_thread
safe_shutdown = False
window_status = True
window = tk.Tk()
def widow_close():
window_status=False
while True:
if safe_shutdown == True:
window.destroy
return False
def get_updates():
while True:
#code to go get data from network
if window_status == False:
safe_shutdown == True:
break
#more code to place data on the GUI
if window_status == False:
safe_shutdown == True:
break
#only get updates once a second
time.sleep(1)
print "thread complete"
my_thread= threading.Thread(target=get_updates, args=()).start()
window.protocol("WM_DELETE_WINDOW", window_close)
window.mainloop()
I'm not an expert, so I'm not sure if there's a better solution. But I've had success with two separate solutions:
1: While it is not recomended to have the tkinter loop in a thread, you are allowed to have the data updating the tkinter app in a thread. This has worked pretty good for me in the past simply using the threading package. The thread will then just set the various stuff in need of a refresh.
2: Call the update_idletasks() on the window to force it to update. This can then be added at various places in you code where it would make sense to update the view.
Solution 1 whould take care of all stuttering, while solution 2 might just make it a bit better. I suppose it depends on your implementation.