Threading and tkinter

G

gert

After reading the docs and seeing a few examples i think this should
work ?
Am I forgetting something here or am I doing something stupid ?
Anyway I see my yellow screen, that has to count for something :)

from tkinter import *
from threading import Thread

class Weegbrug(Thread):
def __init__(self,v):
self.v=v
Thread.__init__(self)
def run(self):
while True:
with open('com1','r') as f:
for line in f:
self.v.set(line[2:-1])

root = Tk()
v = StringVar()
v.set("00000")
w = Weegbrug(v)
w.start()
tx = Label(root, textvariable=v, width=800, height=600, bg="yellow",
font=("Helvetica", 300))
tx.pack(expand=YES, fill=BOTH)
root.title("Weegbrug")
root.overrideredirect(1)
root.geometry("%dx%d+0+0" % (root.winfo_screenwidth(),
root.winfo_screenheight()))
root.mainloop()
 
H

Hendrik van Rooyen

After reading the docs and seeing a few examples i think this should
work ?
Am I forgetting something here or am I doing something stupid ?
Anyway I see my yellow screen, that has to count for something :)

from tkinter import *
from threading import Thread

class Weegbrug(Thread):
def __init__(self,v):
self.v=v
Thread.__init__(self)
def run(self):
while True:
with open('com1','r') as f:
for line in f:
self.v.set(line[2:-1])

It is in general not a good idea to directly
access GUI variables from outside the
GUI main loop.
There is a recipe for doing this sort of thing,
but as usual I have lost the reference.
What it does is that instead of interfering directly
as above, you put the data on a queue.

Then, you use the after() call to set up a call
to a routine that reads the queue, and configures the
display, and then uses after again to call itself again
after a time, thereby keeping the GUI stuff in the GUI
mainloop.

HTH - Hendrik
 
G

gert

gert said:
After reading the docs and seeing a few examples i think this should
work ?
Am I forgetting something here or am I doing something stupid ?
Anyway I see my yellow screen, that has to count for something :)
from tkinter import *
from threading import Thread
class Weegbrug(Thread):
    def __init__(self,v):
        self.v=v
        Thread.__init__(self)
    def run(self):
        while True:
            with open('com1','r') as f:
                 for line in f:
                     self.v.set(line[2:-1])

It is in general not a good idea to directly
access GUI variables from outside the
GUI main loop.
There is a recipe for doing this sort of thing,
but as usual I have lost the reference.
What it does is that instead of interfering directly
as above, you put the data on a queue.

Then, you use the after() call to set up a call
to a routine that reads the queue, and configures the
display, and then uses after again to call itself again
after a time, thereby keeping the GUI stuff in the GUI
mainloop.

from tkinter import *
from threading import Thread

class Weegbrug(Thread):
def __init__(self):
self.display='00000'
Thread.__init__(self)
def run(self):
x=0
while True:
x=x+1
self.display=x
#with open('com1','r') as f:
# for l in f:
# self.display=l[2:-1]

root = Tk()
v = StringVar()
v.set('00000')
w = Weegbrug()
w.start()
tx = Label(root, textvariable=v, width=800, height=600, bg='yellow',
font=('Helvetica', 300))
tx.pack(expand=YES, fill=BOTH)
root.title('Weegbrug')
root.overrideredirect(1)
root.geometry('%dx%d+0+0' % (root.winfo_screenwidth(),
root.winfo_screenheight()))
root.after(500, v.set(w.display))
root.mainloop()

Why does this not work ?
It only shows one result ?
 
G

gert

Can you first explain why x stay's 0 please and how i should update x
using threads ?

from tkinter import *
from _thread import start_new_thread
from time import sleep

x=0
def weegbrug(x):
while True:
x=x+1
sleep(0.5)
start_new_thread(weegbrug,(x,))

root = Tk()
v = StringVar()
v.set("00000")
txt = Label(root, textvariable=v, width=800, height=600, bg="yellow",
font=("Helvetica", 300))
txt.pack(expand=YES, fill=BOTH)
root.title("Weegbrug")
root.after(500, lambda:v.set(x))
root.mainloop()
 
S

Steve Holden

gert said:
Can you first explain why x stay's 0 please and how i should update x
using threads ?

from tkinter import *
from _thread import start_new_thread
from time import sleep

x=0
def weegbrug(x):
while True:
x=x+1
sleep(0.5)
start_new_thread(weegbrug,(x,))

root = Tk()
v = StringVar()
v.set("00000")
txt = Label(root, textvariable=v, width=800, height=600, bg="yellow",
font=("Helvetica", 300))
txt.pack(expand=YES, fill=BOTH)
root.title("Weegbrug")
root.after(500, lambda:v.set(x))
root.mainloop()
The reason x stays at zero has nothing to do with threading.

The statement

x=x+1

(which, by the way, should stylistically be written

x = x + 1

if you want your code to be readable) doesn't change anything outside
the function. Function arguments are values: since x is a parameter of
the function, it exists only inside the function call's namespace.

regards
Steve
 
G

gert

The reason x stays at zero has nothing to do withthreading.

The statement

    x=x+1

(which, by the way, should stylistically be written

    x = x + 1

if you want your code to be readable) doesn't change anything outside
the function. Function arguments are values: since x is a parameter of
the function, it exists only inside the function call's namespace.

Oeps :) Anyway will this x work in tkinter

from _thread import start_new_thread
from time import sleep

x=0
def weegbrug():
global x
while True:
x=x+1
sleep(0.5)
start_new_thread(weegbrug,())

while True:
print(x)
 
H

Hendrik van Rooyen

gert said:
After reading the docs and seeing a few examples i think this should
work ?
Am I forgetting something here or am I doing something stupid ?
Anyway I see my yellow screen, that has to count for something :)
from tkinter import *
from threading import Thread
class Weegbrug(Thread):
def __init__(self,v): ..> > self.v=v
Thread.__init__(self)
def run(self):
while True:
with open('com1','r') as f:
for line in f:
self.v.set(line[2:-1])

It is in general not a good idea to directly
access GUI variables from outside the
GUI main loop.
There is a recipe for doing this sort of thing,
but as usual I have lost the reference.
What it does is that instead of interfering directly
as above, you put the data on a queue.

Then, you use the after() call to set up a call
to a routine that reads the queue, and configures the
display, and then uses after again to call itself again
after a time, thereby keeping the GUI stuff in the GUI
mainloop.

from tkinter import *
from threading import Thread

class Weegbrug(Thread):
def __init__(self):
self.display='00000'
Thread.__init__(self)
def run(self):
x=0
while True:
x=x+1
self.display=x

Still mucking around from outside the GUI.
#with open('com1','r') as f:
# for l in f:
# self.display=l[2:-1]

root = Tk()
v = StringVar()
v.set('00000')
w = Weegbrug()
w.start()
tx = Label(root, textvariable=v, width=800, height=600, bg='yellow',
font=('Helvetica', 300))
tx.pack(expand=YES, fill=BOTH)
root.title('Weegbrug')
root.overrideredirect(1)
root.geometry('%dx%d+0+0' % (root.winfo_screenwidth(),
root.winfo_screenheight()))
root.after(500, v.set(w.display))

root.after(500,displayer(q)) # You set up a "stutter thread" like this
root.mainloop()

Why does this not work ?
It only shows one result ?

You are only calling it once.
Read my story above again. (And Again)
Specially the bit about putting the
values into a queue.

You need to do something like this:

def displayer(q):
# stuff to read the queue and update the display
root.after(500, displayer(q)) # This makes sure it keeps on stuttering

Where q is an instance of Queue.Queue

There is a nice recipe by Thomas Haeller (?) but I have lost the link.

- Hendrik
 
G

gert

Hope you do not mind ignoring part of answers, so I can figure out
more why things work the way they are.
This two examples work, what i do not understand is that in function
display i do not have to declare root, v or x ?

----------
example 1
----------
from tkinter import *
from _thread import start_new_thread
from time import sleep

x=0
def weegbrug():
global x
while True:
x=x+1
sleep(0.5)
start_new_thread(weegbrug,())

def display():
v.set(x)
root.after(500, lambda:display())

root = Tk()
v = StringVar()
txt = Label(root, textvariable=v, width=800, height=600, bg='yellow',
font=('Helvetica', 300))
txt.pack(expand=YES, fill=BOTH)
root.title('Weegbrug')
root.overrideredirect(1)
root.geometry('%dx%d+0+0' % (root.winfo_screenwidth(),
root.winfo_screenheight()))
root.after(500, lambda:display())
root.mainloop()


----------
example 2
----------
from tkinter import *
from threading import Thread
from time import sleep

class Weegbrug(Thread):
def __init__(self):
self.x=0
Thread.__init__(self)
def run(self):
while True:
self.x=self.x+1
sleep(0.5)
w = Weegbrug()
w.start()

def display():
v.set(w.x)
root.after(500, lambda:display())

root = Tk()
v = StringVar()
txt = Label(root, textvariable=v, width=800, height=600, bg='yellow',
font=('Helvetica', 300))
txt.pack(expand=YES, fill=BOTH)
root.title('Weegbrug')
root.overrideredirect(1)
root.geometry('%dx%d+0+0' % (root.winfo_screenwidth(),
root.winfo_screenheight()))
root.after(500, lambda:display())
root.mainloop()
 
H

Hendrik van Rooyen

gert said:
Hope you do not mind ignoring part of answers, so I can figure out
more why things work the way they are.
This two examples work, what i do not understand is that in function
display i do not have to declare root, v or x ?

x is easy - it was declared outside, in module scope, and you
modified it after declaring it global.

The others are more subtle, and have to do with how the interpreter
searches for stuff - first in the local scope, then up the stack in
the callers scope, up to finally in the module global scope.

That also explains why, if it is not found, you get an error message that
says "Global variable xxxxx not defined" (try it and see)

- Hendrik
 
C

Craig Allen

The statement

    x=x+1

(which, by the way, should stylistically be written

    x = x + 1

yes I was wondering what "x=x+1" meant until you translated it... oh,
"x = x + 1" of course! I thought to myself.

Oh wait no I'm sarcastic.
 
G

gert

[posted and e-mailed -- please reply to the group]

gert   said:
After reading the docs and seeing a few examples i think this should
work ?

This is a bit late, and I don't have time to review your code, but you
should see a good example here:

http://www.pythoncraft.com/OSCON2001/index.html
--
Aahz ([email protected])           <*>        http://www.pythoncraft.com/

"All problems in computer science can be solved by another level of    
indirection."  --Butler Lampson

Witch basically translate into stop being a smart ass and just do
this :)

from tkinter import *

def weegbrug():
with open('com1','r') as f:
for l in f:
v.set(l[2:-1])
root.after(500, weegbrug)

root = Tk()
v = StringVar()
v.set("00000")
txt = Label(root, textvariable=v, width=800, height=600, bg="yellow",
font=("Helvetica", 300))
txt.pack(expand=YES, fill=BOTH)
root.title("weegbrug")
root.overrideredirect(1)
root.geometry("%dx%d+0+0" % (root.winfo_screenwidth(),
root.winfo_screenheight()))
root.after(500, weegbrug)
root.mainloop()
 
G

gert

I have been using the following scheme:
  - Pass the root object to the thread object when creating the object
  - Define a event_handler: root.bind('<<SomeEvent>>', evt_handler) in
    the main thread.

  - When the thread has done something that the GUI part should now
    about, signal an event in the thread:
        root.event_generate('<<SomeEvent>>')    (no other arguments)

  - Call a method of the thread object in the event handler e.g. to get
    some data from a queue object.

This ensures that only the main thread accesses Tkinter-related things.

Thanks :)
PS why does the first example leave a running process (dos box) open
when you close the gui and yours not ?
Also root.after can make the program crash if the threat is waiting
for com1 response.
Speaking of com1 ports, for some reason I have to start up some other
serial terminal app and close it again before the device is returning
data to the python app ? Do you need to send something to the com1
device first ?

-----------first example--------------

from tkinter import *
from threading import Thread
from time import sleep

class Weegbrug(Thread):
def __init__(self):
Thread.__init__(self)
self.x=0
def run(self):
while True:
self.x=self.x+1
sleep(0.5)

w = Weegbrug()
w.start()

def display():
v.set(w.x)
root.after(500, display)

root = Tk()
v = StringVar()
txt = Label(root, textvariable=v, width=800, height=600, bg='yellow',
font=('Helvetica', 300))
txt.pack(expand=YES, fill=BOTH)
root.title('Weegbrug')
root.overrideredirect(1)
root.geometry('%dx%d+0+0' % (root.winfo_screenwidth(),
root.winfo_screenheight()))
root.after(500, display)
root.mainloop()

from tkinter import *
from threading import Thread
from queue import Queue
from time import sleep

----------second example-------------

class Weegbrug(Thread):
def __init__(self, gui):
Thread.__init__(self)
self.gui = gui
self.queue = Queue()

def run(self):
while True:
with open('com1', 'w+') as f:
for line in f:
self.queue.put(line)
self.gui.event_generate('<<LineRead>>')
time.sleep(0.5)

def get_line(self):
return self.queue.get()

def evt_handler(*args):
v.set(w.get_line())

r = Tk()
r.title('Weegbrug')
r.overrideredirect(1)
r.geometry('%dx%d+0+0' % (r.winfo_screenwidth(),r.winfo_screenheight
()))
r.bind('<<LineRead>>', evt_handler)
v = StringVar()
v.set('00000')
t = Label(r, textvariable=v, width=100, bg='yellow', font=
('Helvetica', 300))
t.pack(expand=YES, fill=BOTH)
w = Weegbrug(r)
w.start()
r.mainloop()
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top