I Am Unable To Record Selected Value In Combobox In A Variable
Solution 1:
GUI frameworks like tkinter
are usually "event-driven" which is a processing paradigm where the logic flow within the program is driven by events such as user actions, messages from other programs, and hardware inputs from device such as keyboards and mice — see event-driven programming. This means that the procedural or imperative way of programming that you are probably familiar with can't be used.
That said, tkinter
also has a special type-specific object that acts like a regular Python variable in that it is a container for a value, such as a number or string, and they can be used with many widgets in place of constant literal values. They remember all the widget they were used with and when their value changes, all the widget they are linked to are automatically updated.
In this case it means you can make a Label
whose text is a StringVar
and when its value is changed in your get_dbname()
handler, what's displayed by the label will be updated automatically. Note that in the code below get_dbname()
is no longer a <Button-1>
event-handler bound to the Button
widget. It is now a simple command=
callback function for it which is the more commend and simpler way to do accomplish the same thing.
Since I don't have your database, I commented all the lines of code in your question dealing with it in order to have something I could run. I also changed the layout of the widgets a little to make things more readable — but I think it will give you a good idea of concepts I've tried to explain.
#import pymssql
from tkinter import Button, Label, OptionMenu, StringVar, Tk, ttk
from tkinter.constants import *
#from sqlalchemy import create_engine,inspect
master = Tk()
def get_dbname():
dbname.set(db_select.get()) # Will automatically update label_dbname.
dbname = StringVar(value='?')
#engine = create_engine('mssql+pymssql://(local)')
#result = engine.execute("select name FROM sys.databases;")
#rows = result.fetchall()
#final_result = [list(i) for i in rows]
final_result = [f'database #{i}' for i in range(3)] # Create dummy db names.
db_select = ttk.Combobox(master, state='readonly', values=final_result)
db_select.current(0)
db_select.grid(row=0, column=0, columnspan=2)
buttn1 = Button(master, text='Select', command=get_dbname)
buttn1.grid(row=1, column=0, columnspan=2)
label_dbtitle = Label(master, text="chosen db = ")
label_dbtitle.grid(row=2, column=0, sticky=NE)
label_dbname = Label(master, textvariable=dbname)
label_dbname.grid(row=2, column=1, sticky=NW)
master.mainloop()
Solution 2:
You're encountering one of the brain-benders of how libraries like TKinter work. Everything prior to master.mainloop()
is actually just setup code--you're creating interface elements and attaching them to the master element. The interface doesn't render and start accepting input until you call master.mainloop()
, which kicks off the event loop that handles calling functions like get_dbname when Button1 is pressed. So, a print statement before the loop starts will always print the initial value of dbname, which is an empty string. Because of this, your main block of code should actually only hold Tkinter setup code and callback functions, because everything before the callback will only be run once, and nothing after the mainloop statement will execute until the tkinter window is closed.
The issue is that when using Tkinter, your code is "event-driven", meaning that--barring you creating separate threads--anything that happens in your app is driven by the user interacting with the app, and bound actions only run after mainloop start. To demonstrate this, if you create a second button with a second function that prints the value of dbname, then click that after you click "Button1", you should see that function print out your selection, so you are indeed saving to this variable.
This brings us to how one usually handles event-driven apps like this. The easiest approach is to build a class with methods that handle the "business logic" of the interface, and store important variables representing data or state on the class, then use simple callback functions to manipulate this class. You generally don't want many--if any--global variables. If you need background, non-freezing data processing, you might also look into how to start threads to handle background tasks--but for now, just focus on getting your UI to work.
TLDR: You are storing it, but you're printing the variable before the event loop starts, so you're printing it before the callback stores it. Create a second button and a callback to read and print the variable to see that it has indeed been stored.
Edit: TheLizzard makes a great point in a simpler way than I conveyed it: it's a good idea to try to handle variables within the function where they're created or first set, when possible. In this case, you could revise get_dbname
to something like this:
def get_dbname(event):
dbname = db_select.get()
label_dbname["text"] = db_select.get()
print(dbname)
# Do other things like update UI with the name or set other inputs here too!
Post a Comment for "I Am Unable To Record Selected Value In Combobox In A Variable"