Im new to python and started to create a gui with tkinter. My code works fine for usage in one class. But to have a good structure i want to put all Toplevels into an own class. Im facing following problem:
The function whatever in class A is creating a toplevel with options listed in a combobox via another class. I want to wait for an option to be selected until the following code in function whatever is execetued. But this doesnt work.
I was trying around with loops, threads and tkinter to solve this problem, but it didnt work.
I dont even now which way is right..
class OptionWindow:
def __init__(self, root, options, option)
#Toplevel with Combobox & Options
self.option = option
button = Button(toplevel, text = "Select", command=self.get_option)
def GetOption(self):
self.option = combobox.get
if self.option !="":
toplevel.destroy()
class A:
def __init__(self, root):
# GUI with Labels, TreeView, Buttons..
self.options = [A,B,C,D]
self.option = ""
def whatever(self):
OptionWindow(self.root, self.options, self.option)
#How to wait till self.option != "" ?
print("Option: ",option)
# continuing code were option is used
while loop till var is set: TopLevel pops up, but without combobox|
thread with flag: not even the toplevel is showing up|
tkinter wait variable, doesnt work ether, but im not sure how to use it correctly
Related
I'm using tkinter for the first time and I've created a window that has a listbox with values that the user can select. These values are passed into a function, and any console output is shown in a text box in the window.
Here's my code:
class Display(tk.Frame):
def __init__(self,parent=0):
tk.Frame.__init__(self,parent)
# code here that creates a bunch of widgets
sys.stdout = self
# code here that packs widgets into frame
def onSubmit(self):
self.years = list()
self.selection = self.lstbox.curselection()
for i in self.selection:
entry = self.lstbox.get(i)
self.years.append(int(entry))
batch_process(self.years)
def write(self, txt):
self.output.insert(tk.END,str(txt))
self.output.see(tk.END)
self.update_idletasks()
class MainW(tk.Tk):
def __init__(self, parent):
tk.Tk.__init__(self ,parent)
self.parent = parent
self.display = Display(self)
self.display.pack()
self.title('Test')
self.protocol('WM_DELETE_WINDOW', self.close_window)
def close_window(self):
if messagebox.askokcancel('Exit', 'Are you sure you want to close?'):
self.destroy()
if __name__=='__main__':
window = MainW(None)
window.mainloop()
The window works fine - listbox successfully passes values into the function and output is shown in the textbox.
However, when I click "X" and close the window, the console shows that the script is still running for some reason. I then have to completely close Spyder in order to run the script again. What's the issue? My understanding is that destroy() would terminate the mainloop.
EDIT: Had someone else run the script and got the following error:
Exception ignored in: <__main__.Display object .!display>
AttributeError: 'Display' object has no attribute 'flush'
I got it... even though I don't fully understand it. I had to add a "flush" function to my Display class.
def flush(self):
pass
So I've given myself a little project and I'm trying to make a little tool to connect to the OKEX exchange. Now I'm currently working on the GUI and I've decided to use Tkinter. After lots of research and what-not, I've come up with the following, but now I've become a bit stuck.
I've got 2 classes, one for the main window and one for the login window. However, some of the functions of the main window rely on what happens after I've submitted the login details. Now I know that Toplevel is for creating additional windows in Tkinter, and you normally close these windows with .destroy() , and if I want to pick up on this event in the main window class then I need to use the Toplevel.protocol("WM_DELETE_WINDOW", function_name) call ...but this isn't working for me.
It works as expected if I close using the cross in the top right, but not if I close with my function that calls .destroy() , can anyone explain to me why this isn't working as intended? Perhaps I have missed something ?
I want to change the text in the first frame to "Logged In" after the user (me) enters in their details, but I need to login first and pass through a user object to contain those details.
Anyways, here's the code, please help me out!
The code in question is the myquit function in the LoginBox class , and the goToLogin function in the MainBox class :)
from tkinter import *
from tkinter.ttk import *
class LoginBox:
def __init__(self, master): # Master is the frame that is passed in.
# Create Frame
self.master = master
self.master.title('~ Please Login ~')
def login_function(self):
user = "xxx"
secret_key = "yyy"
print("User - API Key: " + user)
print("User - Secret Key: " + secret_key)
# Perhaps check the login ...
# if it's a success then quit the function
self.myquit()
def myquit(self):
self.master.destroy()
class MainBox:
def __init__(self, master):
# Set the root window
self.master = master
self.master.geometry("500x500")
self.master.title("OkBot v0.1")
self.master.resizable(False, False)
# Initialize the frames
self.uiFrame1 = Frame(self.master) # The Top Layer -- Login Text + Login Button
# uiFrame1 Initialize --Login Text + Login Button
self.ui1_button = Button(self.uiFrame1, text="Login", command=self.goToLogin).grid(row=0, column=3, sticky=E, padx=1)
# Create Topview for popup , pass in User Object to LoginBox
def goToLogin(self):
loginMaster = Toplevel(self.master)
loginMaster.protocol("WM_DELETE_WINDOW", self.checkLogin) # This is if they close via X
loginGUI = LoginBox(loginMaster)
def checkLogin(self):
print("This function was called -- The Protocol for destroyed windows works")
# Initialize the objects and start the program
mainWindow = Tk()
myProgram = MainBox(mainWindow)
mainWindow.mainloop()
It works as expected if I close using the cross in the top right, but not if I close with my function that calls .destroy() , can anyone explain to me why this isn't working as intended? Perhaps I have missed something ?
loginMaster.protocol("WM_DELETE_WINDOW", self.checkLogin) only tells tkinter what to do when the window manager destroys the window. When you call some function which calls destroy(), that doesn't involve the window manager and therefore your callback is not called.
If you want something to happen when the window is destroyed, you should bind to the <Destroy> event.
I'm working on functions within a class, and one of the issues I'm running into is adding a button that terminates the program. Here is the current code:
class ClassName():
def __init__(self, root):
self.root = root
def close_root(self, root):
root.destroy()
root.quit()
def addExitButton(self, root):
Button(root, text = 'Exit', width = 10, command = self.close_root).grid(row = 5,
column = 0)
Within the button arguments, I have tried command = self.close_root(root) But this gives me an error because you can't call a function if you want the button to do something (I forget the reason as to why this is). I have also tried
def close_root(self):
self.destroy()
self.quit()
def addExitButton(self, root):
Button(..., command = self.close_root,...)
And this does not work either as the class doesn't have the attribute destroy. I'm not sure how to approach this after trying a few different ways.
You need to actually access the root's functions. So using self.root.destory() or self.root.quit() will work because your root object has those methods but your class does not.
You should also only use one of them, in this case, destroy is the best option I think. And you can probably just use that when creating the button. So replace the button callback (command) with self.root.destory.
More here on how to close a Tkinter window here.
I'm creating a simple class named Dialog. I only want it to return when init method is already finished.
from tkinter import *
class Dialog:
def __set_data(self):
self.data = self.entry_.get()
self.top.destroy()
def __init__(self, prompt_text = "Prompt:", submit_text = "OK", text_size = 5, button_size = 5, main_window = Tk()):
main_window.withdraw()
top = self.top = Toplevel(main_window)
Label(top, text=prompt_text).pack()
self.entry_ = Entry(top)
self.entry_.pack(padx = text_size)
button = Button(top, text = submit_text, command = self.__set_data)
button.pack(pady=button_size)
def get_data(self):
return self.data
data = 0
a = Dialog();
print (a.get_data())
If you run this code, you will get the output 0. I want to the output only show up after the user input. How can I do this?
Firstly, I don't think that you really want to postpone the __init__ method returning. If you do that, your code will never get to tk.mainloop(), and will never actually appear on screen.
Instead, what you want to do is be notified when the data in the Entry widget changes. This is a pretty common thing to do in GUI toolkits; the way tkinter handles it is with Events and Bindings. You can read about them generally here.
One way of performing your particular task (showing the data in self.entry once it has been changed by the user) might be to use the method shown in this question.
Alternatively, you could add a command to your submit button (see here) and read the value of the entry in that method.
Hello,
I'm struggling using multiple tkinter windows in python.
Basicly, I have two classes which are also related to two different windows.
The main class shows the main window (parentWindow) and the other class shows a secondWindow (childWindow).
The following code starts the MainWindow:
#START THE APPLICATION
root = Tkinter.Tk()
root.title ("GEMEINDESTECKBRIEF-Menü")
# My main Application
runGUI = MainWorkspaceConfig (root)
root.mainloop ()
So far there aren't any problems!
Now I'm trying to open a second Window calling a function in the Main Class (kind of onClickFunction to open the Window)
def opendirFactsheetHochwasserGebaeude (self) :
#validates the workspace resp. database directory and
#print self.checkFactsheet2.get()
#print self.inputSpace1.get()
try:
if self.checkFactsheet2.get()==1 :
if self.inputSpace1.get() or self.inputSpace2.get() != "":
#write workspace environment to __initFile__
if self.inputSpace1.get() != "":
self.writeWorkspEnv(self.inputSpace1.get())
#Copy file in seperate thread
start_new_thread(self.copyDefaultFactoWorkspace,())
if self.inputSpace2.get() != "":
self.writeWorkspEnv(self.inputSpace2.get())
# !!!!!!! START SECOND WINDOW !!!!!
facthwgeb = Tkinter.Tk()
facthwgeb.title ("Factsheet Hochwasser-Gebäude")
runGUI = Factsheet_hochwassergebaeude (facthwgeb)
facthwgeb.mainloop ()
#facthwgeb.protocol('WM_DELETE_WINDOW', runGUI.closeFactsheetHochwGeb)
else:
#self.inputSpace1.get() and self.inputSpace2.get () =="":
tkMessageBox.showwarning ("Keine Arbeitsumgebung festgelegt", "Bitte entweder einen neuen Workspace anlegen oder eine bestehende Datenbank auswählen!")
self.selectBox1.deselect()
Still everything works just fine!! The window opens as expected and also the GUI-widgets are displayed and useable.
After finishing the given tasks the Window has to be closed and HERE ALL TROUBLE STARTS!!!
To quit the Window I'm using a button with a command function which looks like this:
def closeFactsheetHochwGeb (self):
try:
if self.inputSpace1.get() and self.inputSpace2.get() != "":
with open('%s/__initFile__.txt'%os.path.dirname(os.path.realpath(__file__)), 'r') as file:
# read a list of lines into data
data = file.readlines()
data[13] = self.inputSpace1.get()+"\n"
data[14] = self.inputSpace2.get()+"\n"
# and write everything back
with open('%s/__initFile__.txt'%os.path.dirname(os.path.realpath(__file__)), 'w') as file:
file.writelines( data )
file.close()
# self.tkinterFrame.destroy()
self.tkinterFrame.quit()
The self.tkinterFrame.quit() closes not just the secondWindow (childWindow) it also closes the MainWindow (parentWindow) too. The self.tkinterFrame.destroy() function clears all widget from the window but the window still is active and visible!!
So, any Ideas how to solve the problem?
Would be thankful for any solutions!!!!!
Don't make a second Tk() instance; you can/should have only one root.
Use a Toplevel widget for facthwgeb instead. Also, get rid of the facthwgeb.mainloop() call, as again, there should only be one call to this.
YEESSS, Finally I found the solution to my issue!!!!
First Step: In the Main Class which starts the ChildWindow I changed the code in the function def opendirFactsheetHochwasserGebaeude (self) : from Tkinter.Tk() to Tkinter.Toplevel(parent) => the parent references the root Window. After changing the Tkinter typ the facthwgeb.mainloop()was also cleared because it is provided by the MainWindow (Parent)
Second Step: In the Second Class which implements the ChildWindow the function def closeFactsheetHochwGeb (self):did privously owned the commands self.tkinterFrame.destroy()which cleared the widget of the frame but not the window itself and the self.tkinterFrame.quit()closes the MainWindow and the ChildWindow => so both commands are useless!!
Final Step: The final resolution is to change the self.tkinterFrame.destroy ()to self.tkinterFrame.master.destroy() !!
Sometimes complex things can be very simple!! :-)
Try this self.Frame1.destroy()
Or whatever your frame name maybe
Sometimes you could have this
self.Frame1 = tk.Frame(top)