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()
Related
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 a simple ui written by python tkinter, it only contains one button.
I found a problem here, if the button command is directed to a function, which includes creating an instance to perform its method. However, when i run this program, my pycharm told me I am passing one positional argument to the method, which i never did:
TypeError: tell_time() takes 0 positional arguments but 1 was given
For some reasons, I have to keep the method stay within the class. Could anyone tell me how to let the method run? Thanks a million!
def build_ui():
root = Tk()
root.title("Auto Hedger")
root.geometry("640x480")
btn1 = Button(root, text="get data", command=testing1)
btn1.pack()
root.mainloop()
class test_object():
def tell_time():
print(datetime.datetime.now())
def testing1():
aaa = test_object()
t1000 = Thread(target=aaa.tell_time, args=[])
t1000.start()
if __name__ == '__main__':
t_root = Thread(target=build_ui)
t_root.start()
Your tell_time method needs self as a parameter, since it is a class method and not a function. Adding that should make it work fine.
Try this:
from threading import Thread
from tkinter import *
import datetime
def build_ui():
root = Tk()
root.title("Auto Hedger")
root.geometry("640x480")
btn1 = Button(root, text="get data", command=testing1)
btn1.pack()
root.mainloop()
class test_object():
def tell_time(self):
print(datetime.datetime.now())
def testing1():
aaa = test_object()
t1000 = Thread(target=aaa.tell_time, args=[])
t1000.start()
if __name__ == '__main__':
t_root = Thread(target=build_ui)
t_root.start()
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
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())
How can I listen for a button press in Tkinter? I need to listen for a button and not run a function but, I want to run a function that listens for a button.
Update
Here is the code:
#!/usr/bin/env python
import time
import thread
from Tkinter import *
class at(Frame):
def __init__(self, *args, **params):
## Standard heading: initialization
apply(Frame.__init__, (self,) + args, params)
self._parent = None
if len(args) != 0: self._parent = args[0]
self._init_before()
self.v = StringVar()
## Widget creation
self._widgets = {}
self._widgets['button#1'] = Button(self, name='button#1', text='up',)
self._widgets['button#1'].grid(column=1, row=1)
self._widgets['entry#1'] = Entry(self, name='entry#1', textvariable=self.v)
self._widgets['entry#1'].grid(column=2, row=1)
self._widgets['button#2'] = Button(self, name='button#2', text='down',)
self._widgets['button#2'].grid(column=1, row=2)
## Scroll commands
## Resize behavior(s)
self.grid_rowconfigure(1, weight=0, minsize=30)
self.grid_rowconfigure(2, weight=0, minsize=31)
self.grid_columnconfigure(1, weight=0, minsize=30)
self.grid_columnconfigure(2, weight=0, minsize=65)
self.pack(side=TOP, fill=BOTH, expand=1)
## Call to post-init method
self._init_after()
def _init_before(self):
self._init_specificBefore()
def _init_after(self):
self._init_specificAfter()
def u(self):
if self.listening==True:
self.u=True
self.listening=False
def d(self):
if self.listening==True:
self.d=True
self.listening=False
def listen(self):
#listen for self.u and self.d
def _init_specificBefore(self):
pass
def _init_specificAfter(self):
range=[0,100]
current_guess=range[1]/2
n=1
x=""
while 1:
self.v.set(str(current_guess)+"?")
x=self.listen()
if x=="u":
range[0]=current_guess
if range[0]==0:
current_guess=round(current_guess+round(range[1]/2))
else:
current_guess=round(current_guess+round((range[1]-range[0])/2))
elif x=="d":
range[1]=current_guess
if range[0]==0:
current_guess=round(round(range[1]/2))
else:
current_guess=range[0]+(round((range[1]-range[0])/2))
elif x=="y":
print "It took me "+str(n)+" guesses."
break
n=n+1
if __name__ == '__main__':
root = Tk()
o = at(root)
o.pack(side=TOP, fill=BOTH, expand=1)
root.mainloop()
What you are trying to accomplish is unclear, but it sounds like you misunderstand how GUI programming works in general and are just asking the wrong question. Generally speaking, If you think you need to poll for button presses rather than take advantage of the event loop you are doing it wrong.
So, the best answer to "how can I listen for a button and not run a function" is "you can't". It's a bit like asking "how can I put this screw in the wall with a hammer?". You can, but it's not the right way to use the tool so for all practical purposes you can't and shouldn't be taught how to.
It seems to me you are making a guessing game, and I think you want the user to press the up or down button after each guess. is that correct? If so, why not just have the up and down buttons call a function that makes a single guess?
You'll want to create a loop along the lines of while not exiting where exiting is declared False at the start and is toggled when quitting the application. Then in that loop you can put functions that check certain state and update other state. Each time round the loop it will run each of these functions. The magic is to make each of these functions fast enough that it seems like they're running constantly.