I have the following code:
import time, os
from tkinter import *
class Chat():
def sPrint():
s=0
while s < 11:
s+=1
print (s, 'Sec')
time.sleep(1)
if s ==5:
print ("5..")
myapp.OpenWindow1()
if s ==10:
print ("10..")
myapp.OpenWindow2()
class App(Frame):
def ConnectButton1(self):
self.con1 = Button(self)
self.con1["text"] = "Connect1",
self.con1["command"] = lambda: Chat.sPrint()
self.con1.grid(row=0,column=2,padx=5, pady=3,sticky=W)
def OpenWindow1(self):
win1 = Toplevel()
def OpenWindow2(self):
win2 = Toplevel()
myapp = App()
if __name__ == "__main__":
myapp.ConnectButton1()
myapp.pack()
myapp.mainloop()
Problem is - the "print" works perfect every 5 seconds from inside the loop,
but the Toplevel functions runs only when the "while" end (and then shows two
toplevel windows at the same time)
Same happen when i use the
threading.Timer(1, sPrint1).start()
for running a function in a loop.. i can't load a new tkinter buttons/labels
while the function is in a loop.
Any suggestions?
Related
is there a way to automatically run a function besides my main.py every hour. Using while loop, it does not work, because my main.py is a tkinter class, which after its initialization must not remain "trapped" in a while loop.
This is a initialization part of my main.py:
class ChatApp:
index = 0
def __init__(self):
self.create_index()
self.window = Tk()
self._setup_main_window()
self.start_model()
...
if __name__ == "__main__":
app = ChatApp()
app.run()
You can run a separate thread which waits an hour between executing.
Although it depends on what you want the task to be.
import threading
import time
def func():
while True:
#do something
time.sleep(3600)
t1 = threading.thread(target=func)
class ChatApp:
index = 0
def __init__(self):
self.create_index()
self.window = Tk()
self._setup_main_window()
self.start_model()
...
if __name__ == "__main__":
t1.start()
app = ChatApp()
app.run()
I am trying to manipulate some data in a thread from the main function. The issue I am facing about modifying some of the variables which are part of the function which is running on a thread.
So I am trying to run a Tkinter based GUI loop in a thread to ensure it is always running. And want to modify some of the label corresponding to the status in the main function execution. I am facing an issue where it is unable to locate the label variables in the main loop as it is part of the function running on the thread.
Below is a simplified psuedo code for that approach. Please suggest if this a correct way to the above task or is there is any better and efficient way.
import threading
def thread_func():
i = 0
while True:
print('i from thread: ', i)
if __name__ == '__main__':
t = threading.Thread(target=thread_func)
t.start()
while True:
i += 1
Actual scaled down simplified code
import threading
import tkinter as tk
def gui():
window = tk.Tk()
label = tk.Label(text='On')
label.pack()
window.mainloop()
if __name__ == '__main__':
t = threading.Thread(target=gui)
t.start()
while True:
label['text'] = 'Active'
Error:
Traceback (most recent call last):
File "test.py", line 17, in <module>
label['text'] = 'Active'
NameError: name 'label' is not defined
Is there a better way to keep the tkinter gui always on and perform some task in the loop?
Using class and threading:
import tkinter
class Test(tkinter.Label):
x = False
def __init__(self):
super().__init__()
self.pack()
self['text'] = 'On'
def gui(self):
if not self.x:
self['text'] = 'Active'
self.mainloop()
else:
self.mainloop()
if __name__ == '__main__':
label = Test()
while isinstance(label, Test):
t = threading.Thread(target=label.gui())
t.start()
When you write code of label there then you will get error because when program start it starts from creating thread, that thread will only end when tkinter window is close and same for previous thread_fuc code. And you wrote the label code after tkinter window is closed.
The above issue will be solved by doing this :
import threading
def thread_func():
while True:
print('i from thread: ', i)
def tt():
global i
while True:
i += 1
if __name__ == '__main__':
i=0
t = threading.Thread(target=thread_func)
t.start()
yt = threading.Thread(target=tt)
yt.start()
Making i global and running 2 function parallely. We have to global because we can't use the variable of one function to another. And additionally we are running 2 function in 2 thread.
And for your tkinter file as #TheLizzard suggest, you can use .after insted of using thread in tkinter if you want to change content constantly/want to use loop.
Here's the basic example how you can implement it:
import random
import tkinter as tk
app = tk.Tk()
app.geometry("200x220")
label = tk.Label(app, text="0")
label.pack()
def change(b=0):
if b < 30:
a = random.randrange(1, 7, 1)
label.config(text=a)
app.after(100, change, b+1)
b1 = tk.Button(app, text="Get New Number", command=change)
b1.pack()
app.mainloop()
For more explanation about it you may visit here.
I have this code:
from tkinter import *
class logic():
def createComponent(self, event):
print("{0},{1}".format(event.x,event.y))
class gui(logic):
window = Tk()
obj = logic()
def initGui(self):
gui.window.mainloop()
def onClick(self):
gui.window.bind("<Button-1>",gui.obj.createComponent)
obj2 = gui()
obj2.initGui()
while True:
obj2.onClick()
In theory this code should print mouse coordinates on lmb click but "createComponent" isn't called for some reason (also no errors). What Im doing wrong?
Fixed the code:
window.mainloop() is already a loop putting it in while True breaks the code
The classes were setup wrong
from tkinter import *
window = Tk()
def createComponent(event):
print("{0},{1}".format(event.x,event.y))
window.bind("<Button-1>", createComponent)
window.mainloop()
OOP:
from tkinter import *
class windFuncs:
def createComponent(event):
print("{0},{1}".format(event.x,event.y))
class wind(Tk):
pass
window = wind()
window.bind("<Button-1>", windFuncs.createComponent)
window.mainloop()
You may wish to put createComponent in class wind
I am trying to display a count variable from a background task in the main task which is my tkinter GUI. Why? I want to display that the long time taking background task is performing and later use this count variable to visualize it with a progress bar.
My problem is, that even when using a Queue, I am not able to display the count variable. Maybe I've got problems in understanding python and its behaviour with objects and/or threads.
import threading
import time
import Queue
import Tkinter as Tk
import Tkconstants as TkConst
from ScrolledText import ScrolledText
from tkFont import Font
import loop_simulation as loop
def on_after_elapsed():
while True:
try:
v = dataQ.get(timeout=0.1)
except:
break
scrText.insert(TkConst.END, str(v)) # "value=%d\n" % v
scrText.see(TkConst.END)
scrText.update()
top.after(100, on_after_elapsed)
def thread_proc1():
x = -1
dataQ.put(x)
x = loop.loop_simulation().start_counting()
# th_proc = threading.Thread(target=x.start_counting())
# th_proc.start()
for i in range(5):
for j in range(20):
dataQ.put(x.get_i())
time.sleep(0.1)
# x += 1
time.sleep(0.5)
dataQ.put(x.get_i())
top = Tk.Tk()
dataQ = Queue.Queue(maxsize=0)
f = Font(family='Courier New', size=12)
scrText = ScrolledText(master=top, height=20, width=120, font=f)
scrText.pack(fill=TkConst.BOTH, side=TkConst.LEFT, padx=15, pady=15, expand=True)
th = threading.Thread(target=thread_proc1)
th.start()
top.after(100, on_after_elapsed)
top.mainloop()
th.join()
In thread_proc1() I want to get the value of the counter of background task. This is the background task:
import time
class loop_simulation:
def __init__(self):
self.j = 0
# self.start_counting()
def start_counting(self):
for i in range(0, 1000000):
self.j = i
time.sleep(0.5)
def get_i(self):
return str(self.j)
The reason the count variable isn't being displayed is due to the
x = loop.loop_simulation().start_counting()
statement in thread_proc1(). This creates a loop_simulation instance and calls its start_counting() method. However, other than already inserting a -1 into the dataQ, thread_proc1() doesn't do anything else until start_counting() returns, which won't be for a long time (500K seconds).
Meanwhile, the rest of your script is running and displaying only that initial -1 that was put in.
Also note that if start_counting() ever did return, its value of None is going to be assigned to x which later code attempts to use with: x.get_i().
Below is reworking of your code that fixes these issues and also follows the PEP 8 - Style Guide for Python Code more closely. To avoid the main problem of calling start_counting(), I changed your loop_simulation class into a subclass of threading.Thread and renamed it LoopSimulation, and create an instance of it in thread_proc1, so there are now two background threads in addition to the main one handling the tkinter-based GUI.
import loop_simulation as loop
from ScrolledText import ScrolledText
import threading
import Tkinter as Tk
import Tkconstants as TkConst
from tkFont import Font
import time
import Queue
def on_after_elapsed():
# removes all data currently in the queue and displays it in the text box
while True:
try:
v = dataQ.get_nowait()
scrText.insert(TkConst.END, str(v)+'\n')
scrText.see(TkConst.END)
except Queue.Empty:
top.after(100, on_after_elapsed)
break
def thread_proc1():
dataQ.put(-1)
ls = loop.LoopSimulation() # slow background task Thread
ls.start_counting()
while ls.is_alive(): # background task still running?
for i in range(5):
for j in range(20):
dataQ.put(ls.get_i())
time.sleep(0.1)
time.sleep(0.5)
dataQ.put('background task finished')
top = Tk.Tk()
dataQ = Queue.Queue(maxsize=0)
font = Font(family='Courier New', size=12)
scrText = ScrolledText(top, height=20, width=120, font=font)
scrText.pack(fill=TkConst.BOTH, side=TkConst.LEFT, padx=15, pady=15,
expand=TkConst.YES)
th = threading.Thread(target=thread_proc1)
th.daemon = True # OK for main to exit even if thread is still running
th.start()
top.after(100, on_after_elapsed)
top.mainloop()
loop_simulation.py module:
import threading
import time
class LoopSimulation(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.daemon = True # OK for main to exit even if instance still running
self.lock = threading.Lock()
self.j = 0
start_counting = threading.Thread.start # an alias for starting thread
def run(self):
for i in range(1000000):
with self.lock:
self.j = i
time.sleep(0.5)
def get_i(self):
with self.lock:
return self.j
I want to have a simple GUI with 4 buttons. If you just click the button, function A should be executed, for short button press (e.g.1sec) function B should be executed and finally a long press (e.g. > 2s) function C should be executed.
Imagine a counter.
If you click the button, it will be reset to 0
If you short press the button, counter will be increased by 1 for e.g t=1sec
If you long press the button, counter will be increased by 10 until button is released.
Is somebody haveing an idea. I was trying this to accomplish it with a 2nd thread but I haven't found a possibility to stop the thread like you can start it
This is easy to do in PyQt if you use a widget which inherits QAbstractButton. No need for any timers or separate threads. Just use the built-in auto-repeat functionality, and keep a record of the current state.
Here's a simple demo:
from PyQt4 import QtGui, QtCore
class Button(QtGui.QPushButton):
def __init__(self, *args, **kwargs):
QtGui.QPushButton.__init__(self, *args, **kwargs)
self.setAutoRepeat(True)
self.setAutoRepeatDelay(1000)
self.setAutoRepeatInterval(1000)
self.clicked.connect(self.handleClicked)
self._state = 0
def handleClicked(self):
if self.isDown():
if self._state == 0:
self._state = 1
self.setAutoRepeatInterval(50)
print 'press'
else:
print 'repeat'
elif self._state == 1:
self._state = 0
self.setAutoRepeatInterval(1000)
print 'release'
else:
print 'click'
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
button = Button('Test Button')
button.show()
sys.exit(app.exec_())
in wxPython I would do it like this ... however you may need to adjust it for your GUI library ...
start_time = None
def onLeftDown(e):
global running
running = True
ct =0
while running:
ct += 1
do_something(ct)
def onLeftUp(e):
print "You Pressed For %s Seconds!"%(time.time()-start_time)
my_btn = wx.Button(parent,-1,"Click Me!")
my_btn.Bind(wx.EVT_LEFT_DOWN,onLeftDown)
my_btn.Bind(wx.EVT_LEFT_UP,onLeftUp)
Im not very familliar with QT but maybe you can modify this wx code to do what you want...
import wx
ct = 0
def counting():
global running
global ct
if running:
ct +=1
print ct
wx.CallLater(1,counting)
else:
print "OK DONE COUNTING AT:",ct
def onLeftDown(evt):
global running
running = True
counting()
def onLeftUp(evt):
print "STOP NOW!!"
global running
running = False
a = wx.App(redirect=False)
f = wx.Frame(None,-1,"asdasd")
b = wx.Button(f,-1,"Click Me")
b.Bind(wx.EVT_LEFT_DOWN,onLeftDown)
b.Bind(wx.EVT_LEFT_UP,onLeftUp)
f.Show()
a.MainLoop()