Hi i want to use the method checkbutton_value1 in another method AGM.
def AGM():
def A1():
print "A1"
def A2():
print "A2"
def checkbutton_value1():
x=var1.get()
I tried using checkbutton_value1 for checkbutton command but it won't work.
master = Tk() # Open up GUI connection
master.title('Program Application')
var1=IntVar()
checkbox_1 = Checkbutton(master, text='Interpolate Graph', variable=var1,command=checkbutton_value1)
checkbox_1.pack()
master.mainloop() # Continue loop till user close tab
Error message
NameError: name 'checkbutton_value1' is not defined
This is probably happening because you've defined checkbutton_value1 inside AGM's namespace.
What you need to do is this:
def checkbutton_value1():
x = var1.get()
master = Tk() # Open up GUI connection
master.title('Program Application')
var1 = IntVar()
checkbox_1 = Checkbutton(master, text='Interpolate Graph',
variable=var1, command=checkbutton_value1)
checkbox_1.pack()
master.mainloop() # Continue loop till user close tab
Now, this will work. However, in suck cases, its just better to use a lambda:
checkbox_1 = Checkbutton(master, text='Interpolate Graph',
variable=var1, command=lambda: var1.get())
Could you post a larger snippet? That is probably failing because checkbutton_value1 is being defined in a scope where the line referencing it doesn't have access.
For example, this doesn't produce that error:
class test:
def foo():
pass
print(test.foo())
Related
I have quite complex app with two, separated language version. I'm trying to make fluent switch between two versions. I'm trying to make it with multi threading to maintain tkinter GUI.
import time
import threading
from tkinter import *
language = ''
class PolishApp:
def _init__(self):
pass
def do_something(self):
while language == 'polish':
print('working in polish...')
time.sleep(0.5)
class EnglishApp:
def _init__(self):
pass
def do_something(self):
while language == 'english':
print('working in english...')
time.sleep(0.5)
def change_to_polish():
print('change to polish')
language = 'polish'
polish_app.do_something()
def change_to_english():
print('change to english')
language = 'english'
english_app.do_something()
english_app = EnglishApp()
polish_app = PolishApp()
window = Tk()
window.title("choose language")
window.geometry('350x200')
btn = Button(window, text="ENGLISH", command=threading.Thread(target=change_to_english).start())
btn2 = Button(window, text="POLISH", command=threading.Thread(target=change_to_polish).start())
btn.grid(column=1, row=0)
btn2.grid(column=2, row=0)
print(language)
window.mainloop()
When I run the code, it immediately executes functions: change_to_polish(), change_to_english() and do nothing when I click buttons.
output
Does anybody know how it is possible? I probably messed something up with multi threading concept.
there are multiple problems with your program, starting from this line
btn = Button(window, text="ENGLISH", command=threading.Thread(target=change_to_english).start())
the command=threading.Thread(target=change_to_english).start() the right part of the expression is evaluated first, which calls the function, then its return is passed as the command argument, which is not what you want.
you want the function itself to be the argument, not its return, so you should've removed the () brackets at the end so that the function itself is passed as an argument, instead of its return ..... which will still not work as expected because only one instance of threading.Thread is created, and therefore the function will only work once.
what you instead want is whenever the button is pressed, a new thread would be created to execute this, so you should use lambda functions to do this
btn = Button(window, text="ENGLISH", command=lambda: threading.Thread(target=change_to_english).start())
which is equivalent to:
def func():
threading.Thread(target=change_to_english).start()
btn = Button(window, text="ENGLISH", command=func)
this would work as intended, because each time func is called, a new threading.Thread is created, but there's also another typo keeping your program from functioning as you want, which is langauge and language are two different variables and are local to the function in which they are called, so instead you should have them named the same, and also make them both global at the start of the functions using them so that they aren't local to the functions which use them.
As "Ahmed AEK" said there are multiple problems in there. I've come up with the idea where both Apps are running paralell and however the language changes the App does it too.
import time
import threading
from tkinter import *
class PolishApp:
def _init__(self):
pass
def do_something(self):
while True:
if language == 'polish':
print('working in polish...')
time.sleep(0.5)
class EnglishApp:
def _init__(self):
pass
def do_something(self):
while True:
if language == 'english':
print('working in english...')
time.sleep(0.5)
def change_to_polish():
global language
print('change to polish')
language = 'polish'
def change_to_english():
global language
print('change to english')
language = 'english'
language = ''
EN = EnglishApp()
PL = PolishApp()
thread_english = threading.Thread(target=EN.do_something)
thread_english.start()
thread_polish = threading.Thread(target=PL.do_something)
thread_polish.start()
english_app = EnglishApp()
polish_app = PolishApp()
window = Tk()
window.title("choose language")
window.geometry('350x200')
btn = Button(window, text="ENGLISH", command=change_to_english)
btn2 = Button(window, text="POLISH", command=change_to_polish)
btn.grid(column=1, row=0)
btn2.grid(column=2, row=0)
print(language)
window.mainloop()
I have a simple GUI where the user selects a file, which becomes a variable for my main code. Here, my variable output should be the database path (gui_db_path) which the user inputs. When I run this code, called gui_test.py, the variable is printable, and prints to the console.
class GUI:
def __init__(self, window):
# 'StringVar()' is used to get the instance of input field
self.input_db_text = StringVar()
window.title("HyPep 1.0")
window.geometry("700x700")
ttk.Label(window, text='Database sequences .csv:').grid(row=1,column=0)
ttk.Button(window, text = "Browse", command = lambda: self.set_path_database_field()).grid(row = 1,column=2, ipadx=5, ipady=0)
ttk.Entry(window, textvariable = self.input_db_text, width = 70).grid( row = 1, column = 1, ipadx=1, ipady=1)
ttk.Button(window, text = "Analyze").grid(row = 10,column=1, ipadx=5, ipady=15)
def set_path_database_field(self):
self.path_db = askopenfilename()
self.input_db_text.set(self.path_db)
def get_database_path(self):
""" Function provides the database full file path."""
return self.path_db
if __name__ == '__main__':
window = tkinter.Tk()
gui = GUI(window)
window.mainloop()
print(gui.path_db, '\n', gui.path_qu)
gui_db_path = gui.path_db
print(gui_db_path)
My issue is that I need to retrieve this variable for use in another file, user_input.py, but is no longer callable. My code for user_input.py is:
from gui_test import gui_db_path
print(gui_db_path)
Instead of printing to the console in this instance, I get:
ImportError: cannot import name 'gui_db_path' from 'gui_test'
I'm sure there is a simple solution that I am missing, can anyone shed some light?
...
Update: much closer, need to expand the solution:
How would I go about expanding this to retrieve multiple paths? I have been trying this:
gui_test.py:
...
def get_db_path():
window = tkinter.Tk()
gui = GUI(window)
window.mainloop()
return gui.get_database_path()
def get_qu_path():
window = tkinter.Tk()
gui = GUI(window)
window.mainloop()
return gui.get_query_path()
user_input.py:
from gui_test import get_db_path
from gui_test import get_qu_path
gui_db_path = get_db_path()
gui_qu_path = get_qu_path()
Note that the code inside if __name__ == '__main__' block will not be executed when the file is imported. You need to put those code inside a function instead and returns the path at the end of the function:
gui_test.py
...
def get_db_path():
window = tkinter.Tk()
gui = GUI(window)
window.mainloop()
return gui.get_database_path()
Then import this function inside user_input.py:
from gui_test import get_db_path
gui_db_path = get_db_path()
print(gui_db_path)
I have an module that produces the following form (using Python Tkinter):
As you can see the form could have three states:
Uneditable - entry boxes are disabled - Cancel and Edit button active
Editable with no changes - entry boxes are active, Cancel button and
the Edit button becomes a disabled Save button and
Editable with changes - entry boxes are active, Cancel button becomes a revert button and the Save button becomes active
My code used to achieve this is as follows:
def display_selected_user(event, user_frame, editable):
def fill_entry_boxes():
# Clear entry boxes
f_name.entry.delete(0, 'end')
l_name.entry.delete(0, 'end')
email.entry.delete(0, 'end')
# Populate with original data
f_name.entry.insert(0, user_dict[user_id]['f_name'])
l_name.entry.insert(0, user_dict[user_id]['l_name'])
email.entry.insert(0, user_dict[user_id]['email'])
cancel_btn.config(text="Cancel")
def cancel_revert_process():
if not changes_made():
user_detail_frame.grid_forget()
close_btn.grid(row=2, column=2, padx=10, pady=10, sticky='E')
else:
MsgBox = messagebox.askquestion('Revert Changes',
f'Changes have been made - are you sure you want to revert?',
icon='question')
if MsgBox == 'yes':
fill_entry_boxes()
edit_save_btn.config(state='disabled', bg='gray80')
def save_or_edit(event, _editable):
if _editable:
save_process(_editable)
else:
make_editable_process(_editable)
def save_process(_editable):
# Notify user of process commencement
user_frame_messenger = MessageBox(user_frame, "")
user_frame_messenger.grid(row=3, column=0, columnspan=3, sticky='nsew')
user_frame_messenger.update_content(user_frame, "Saving user changes to database - one moment")
# Save Process
changes = record_changes()
client = MongoClient('mongodb+srv://xxxxxx.zzyri.mongodb.net/test?')
db = client['football_data']
collection = db['users']
for key in changes[1]:
collection.update_one({"_id": ObjectId(changes[0])},
{"$set": {key: changes[1][key]}}
)
# Notify user of process completion
user_frame_messenger.destroy()
messagebox.showinfo("User detail updated",
"The changes to the user details have been saved")
user_frame.grid_forget()
cancel_revert_process()
_editable = False
def make_editable_process(_editable):
f_name.entry.config(state='normal')
l_name.entry.config(state='normal')
email.entry.config(state='normal')
_editable = True
edit_save_btn.config(text="Save", state='disabled', bg='gray80')
def key_pressed(event):
if changes_made():
cancel_btn.config(text="Revert")
edit_save_btn.config(state='normal', bg="#43E082")
else:
cancel_btn.config(text="Cancel")
edit_save_btn.config(state='disabled', bg='gray80')
# Record data of selected user
user = user_lst.tree.focus()
user_data = user_lst.tree.item(user)
user_id = user_data['values'][0]
# Create objects
f_name = LabelEntryCombo(user_frame, "First name:")
l_name = LabelEntryCombo(user_frame, "Last name:")
email = LabelEntryCombo(user_frame, "Email:")
cancel_btn = ColourSchemedButton(user_frame, "PaleGreen", "Cancel")
edit_save_btn = ColourSchemedButton(user_frame, "GreenBlue", "Edit", state='normal')
# Fill entry boxes
fill_entry_boxes()
# Bind objects
f_name.entry.bind('<KeyRelease>', lambda e: key_pressed(e))
l_name.entry.bind('<KeyRelease>', lambda e: key_pressed(e))
email.entry.bind('<KeyRelease>', lambda e: key_pressed(e))
cancel_btn.config(command=cancel_revert_process)
edit_save_btn.config(command=lambda e=event, d=editable: save_or_edit(e, d))
# Place and removal of objects
close_btn.grid_forget()
user_detail_frame.grid(row=2, columnspan=3, padx=10, pady=10, sticky='NW')
f_name.frame.grid(row=0, columnspan=3, pady=10, padx=10)
l_name.frame.grid(row=1, columnspan=3, pady=10, padx=10)
email.frame.grid(row=2, columnspan=3, pady=10, padx=10)
cancel_btn.grid(row=3, column=1, pady=10, padx=10)
edit_save_btn.grid(row=3, column=2, pady=10, padx=10)
# Configure grid
user_detail_frame.grid_columnconfigure(0, weight=1)
user_detail_frame.grid_columnconfigure(1, weight=0)
user_detail_frame.grid_columnconfigure(2, weight=0)
# Set state of objects
f_name.entry.config(state='disabled')
l_name.entry.config(state='disabled')
email.entry.config(state='disabled')
As you may pick up - I bind save_edit_btn with the following config using a lambda function which passes the editable variable used as a state handler edit_save_btn.config(command=lambda e=event, d=editable: save_or_edit(e, d))
That editable variable is initalisated outside this function as False and passed to the function display_selected_user() (A callback function from the main programme if you will) at the very top. So when the save_edit_btn is pressed the function makes the entries active and changes the other widgets accordingly (as per function make_editable_process()) and changes the editable variable to True (even in the main program where it was initilised)
Problem is that after that the process goes back to the lambda line and resets the editable variable back to False - I don't understand why - and as a result the save_process() can never be initalised.
Any ideas on why this is the case will be appreciated - please let me know if you require any more code or detail.
Any change you do to a local variable, like _editable in make_editable_process, is only visible inside that function, so see that change outside, you need to either return it, save into some external to the function given mutable object (like those various button thing), or declare it as either global or nonlocal
For your case I think the nonlocal would suffice.
example
what you're experimenting is this
>>> def fun():
a=5
def f():
a=42
print(a)
f()
print(a)
>>> fun()
5
5
>>>
a in f is a local variable to f, any change to it is only visible inside, to change it outside do any of previously mentioned methods, for example with nonlocal:
>>> def fun2():
a=5
def f():
nonlocal a
a=42
print(a)
f()
print(a)
>>> fun2()
5
42
>>>
For values like True/False, numbers, strings Python sends its value to function (or other variable) - not reference to variable.
For example:
a = False
_editable = a
_editable = True
print(a)
and a is still False, not True.
In tkinter you would use tkinter.BooleanVar() for this.
import tkinter as tk
a = tk.BooleanVar(value=False)
# a.set(False)
_editable = a
_editable.set(True)
print(a.get())
and nowe a changes value when you change _editable
Remeber to use .get() to get value, and .set() instead of = to set value.
EDIT:
In other situations you would have to use return and assign result back to variable
def function(_editable):
# ... code ...
_editable = True
return _editable
a = False
a = function(a) # assign returned value to `a`
print(a)
but when function is assigned to Button (or in bind(), or after()) then it can't get returned value and assign back to variable - and then usually we use global to assing value directly to external variable
def function(_editable):
global a
# ... code ...
_editable = True
a = _editable
a = False
function(a) # without assignig returned value
print(a)
I'm writing a python script that requires the user to enter the name of a folder. For most cases, the default will suffice, but I want an entry box to appear that allows the user to over-ride the default. Here's what I have:
from Tkinter import *
import time
def main():
#some stuff
def getFolderName():
master = Tk()
folderName = Entry(master)
folderName.pack()
folderName.insert(END, 'dat' + time.strftime('%m%d%Y'))
folderName.focus_set()
createDirectoryName = folderName.get()
def callback():
global createDirectoryName
createDirectoryName = folderName.get()
return
b = Button(master, text="OK and Close", width=10, command=callback)
b.pack()
mainloop()
return createDirectoryName
getFolderName()
#other stuff happens....
return
if __name__ == '__main__':
main()
I know next to nothing about tkInter and have 2 questions.
Is over-riding the default entry using global createDirectoryName within the callback function the best way to do this?
How can I make the button close the window when you press it.
I've tried
def callback():
global createDirectoryName
createDirectoryName = folderName.get()
master.destroy
but that simply destroys the window upon running the script.
I don't know how experienced are you in Tkinter, but I suggest you use classes.
try:
from tkinter import * #3.x
except:
from Tkinter import * #2.x
class anynamehere(Tk): #you can make the class inherit from Tk directly,
def __init__(self): #__init__ is a special methoed that gets called anytime the class does
Tk.__init__(self) #it has to be called __init__
#further code here e.g.
self.frame = Frame()
self.frame.pack()
self.makeUI()
self.number = 0 # this will work in the class anywhere so you don't need global all the time
def makeUI(self):
#code to make the UI
self.number = 1 # no need for global
#answer to question No.2
Button(frame, command = self.destroy).pack()
anyname = anynamehere() #remember it alredy has Tk
anyname.mainloop()
Also why do you want to override the deafult Entry behavior ?
The solution would be to make another button and bind a command to it like this
self.enteredtext = StringVar()
self.entry = Entry(frame, textvariable = self.enteredtext)
self.entry.pack()
self.button = Button(frame, text = "Submit", command = self.getfolder, #someother options, check tkitner documentation for full list)
self.button.pack()
def getfolder(self): #make the UI in one method, command in other I suggest
text = self.enteredtext.get()
#text now has whats been entered to the entry, do what you need to with it
I'd like to have a generic method that will change the value of a tkinter variable. I want to be able to do this from a menu option that brings up a new dialog box which is from a generic method that can be used for different variables. the code I have so far is below:
import sys
from tkinter import *
from tkinter import ttk
from tkinter import filedialog
import sequencer as seq
class View(ttk.Frame):
"""Main Gui class"""
def __init__(self, master = None):
ttk.Frame.__init__(self, master, borderwidth=5, width=450, height=500)
self.master = master
self.grid(column=0, row=0, sticky=(N, S, E, W))
self.columnconfigure(0, weight=1)
###############################
### User editable variables ###
self.precision = IntVar(value=4, name='precision')
self.sensitivity = IntVar(value = 50, name='sensitivity')
### User editable variables ###
###############################
self.create_menus()
def create_menus(self):
"""Produces the menu layout for the main window"""
self.master.option_add('*tearOff', FALSE)
self.menubar = Menu(self.master)
self.master['menu'] = self.menubar
# Menu Variables
menu_file = Menu(self.menubar)
menu_edit = Menu(self.menubar)
# Add the menus to the menubar and assign their variables
self.menubar.add_cascade(menu=menu_file, label="File")
self.menubar.add_cascade(menu=menu_edit, label = "Edit")
### ADD COMMANDS TO THE MENUES ###
### File ###
menu_file.add_command(label="Quit", command=self.master.destroy)
### Edit ###
menu_edit.add_command(label="Backbone", command=lambda : self.edit_backbone())
menu_edit.add_command(label="Precision", command=lambda : self.precision.set(self.set_value_int("Precision")))
menu_edit.add_command(label="Sensitivity", command=lambda : self.sensitivity.set(self.set_value_int("Sensitivity")))
menu_edit.add_command(label="print precision", command=lambda : print(self.precision.get()))
menu_edit.add_command(label="print sensitivity", command=lambda : print(self.sensitivity.get()))
def set_value_int(self, name):
"""Standards dialog that return a user define value of a specific type"""
t = Toplevel(self)
t.title("Set " + name)
label = ttk.Label(t, text="Set "+name)
label.grid(row=0)
entry = ttk.Entry(t)
entry.grid(row=1)
cancel = ttk.Button(t, text="Cancel", command=lambda : t.destroy())
cancel.grid(column=0, row=2)
okey = ttk.Button(t, text="Okey", command=lambda : okey(entry.get()))
okey.grid(column=1, row=2)
def okey(value):
"""return value according to type"""
try:
t.destroy()
return int(value)
except:
self.error_box("value must be and integer")
def error_box(self, error_message="Unknown error"):
"""(Frame, String) -> None
Opens an window with an Okey button and a custom error message"""
t=Toplevel(self)
t.title("Error")
label = ttk.Label(t, text=error_message)
label.grid(row=0)
okey = ttk.Button(t, text="Okey", command=lambda : t.destroy())
okey.grid(row=1)
if __name__ == "__main__":
root = Tk()
root.title("Sequencer")
view = View(root)
root.mainloop()
print("End")
The Edit-> print xxxxxx commands are purely for testing purposes to check if the values have changed. If these are executed before trying to change the values of precision or sensitivity then they work as expected.
If you try to change either of the tkinter variables in the way I have tried to do they become None types and I can't see why. I can only assume that you are not allowed to change them in the way that I have but I can't think of another way to do it without having a separated method for each variable which I'd like to avoid.
Baicly I'd like the user to be able to customise the variables precision and sensitivity and use the same method in the code to change the values.
Extra but not necessarily vital:- If there is a way to define which type the variable should be in the methods arguments as well that would be even better as I will have other variables for the user to change later and they will be of different types.
Any help would be much appreciated. I have tried to be as clear as I can but let me know if anything is not.
Thanks
self.set_value_int always returns None, so it's always going to set your variable to none.
Instead of trying to write a complex lambda that is hard to debug, put all of your logic inside the function. Have the function set the value. All you need to do is tell it what variable to set:
menu_edit.add_command(label="Precision",
command=lambda name="Precision", var=self.precision: self.set_value_int(name, var))
...
def set_value_int(self, name, var):
...
def okey():
s = entry.get()
try:
var.set(int(s))
...