Simple TK Question - refreshing the canvas when not in focus

B

blaine

Hey everyone!
I'm not very good with Tk, and I am using a very simple canvas to
draw some pictures (this relates to that nokia screen emulator I had a
post about a few days ago).

Anyway, all is well, except one thing. When I am not in the program,
and the program receives a draw command (from a FIFO pipe), the canvas
does not refresh until I click into the program. How do I force it to
refresh, or force the window to gain focus? It seems like pretty
common behavior, but a few things that I've tried have not worked.

Class screen():
def __init__(self):
self.root = Tkinter.Tk()
self.root.title('Nokia Canvas')
self.canvas = Tkinter.Canvas(self.root, width =130,
height=130)
self.canvas.pack()
self.root.mainloop()

Then somewhere a long the line I do:
self.canvas.create_line(args[0], args[1], args[2],
args[3], fill=color)
self.canvas.pack()

I've tried self.root.set_focus(), self.root.force_focus(),
self.canvas.update(), etc. but I can't get it.
Thanks!
Blaine
 
E

Eric Brunel

Hey everyone!
I'm not very good with Tk, and I am using a very simple canvas to
draw some pictures (this relates to that nokia screen emulator I had a
post about a few days ago).

Anyway, all is well, except one thing. When I am not in the program,
and the program receives a draw command (from a FIFO pipe), the canvas
does not refresh until I click into the program. How do I force it to
refresh, or force the window to gain focus? It seems like pretty
common behavior, but a few things that I've tried have not worked.

Class screen():
def __init__(self):
self.root = Tkinter.Tk()
self.root.title('Nokia Canvas')
self.canvas = Tkinter.Canvas(self.root, width =130,
height=130)
self.canvas.pack()
self.root.mainloop()

Then somewhere a long the line I do:
self.canvas.create_line(args[0], args[1], args[2],
args[3], fill=color)
self.canvas.pack()

Unrelated question: why are you doing a .pack() again here? Packing the
widget just inserts it at the right place in its container, so you only
have to do it once. So the one you did in __init__ is enough.
I've tried self.root.set_focus(), self.root.force_focus(),
self.canvas.update(), etc. but I can't get it.

IIRC:
- self.root.set_focus() will only work if your application already has the
focus, so it's not what you need here.
- self.root.force_focus() is usually considered as evil: it'll give the
focus to your application whatever the user is doing, which is usually
*really* annoying. So I guess a lot of window managers just refuse to do
it; this may be what happens here.

But self.canvas.update() should work. If it doesn't, then there are
probably limitations on your platform that prevents it to work... Do you
happen to have other applications that can update their display while they
don't have the focus? Do they have the same problem? If they do, it's
probably a limitation on the platform and I guess you won't be able to do
anything about it... BTW, what platform are you on?
Thanks!
Blaine

HTH
 
B

blaine

Hey everyone!
I'm not very good with Tk, and I am using a very simple canvas to
draw some pictures (this relates to that nokia screen emulator I had a
post about a few days ago).
Anyway, all is well, except one thing. When I am not in the program,
and the program receives a draw command (from a FIFO pipe), the canvas
does not refresh until I click into the program. How do I force it to
refresh, or force the window to gain focus? It seems like pretty
common behavior, but a few things that I've tried have not worked.
Class screen():
def __init__(self):
self.root = Tkinter.Tk()
self.root.title('Nokia Canvas')
self.canvas = Tkinter.Canvas(self.root, width =130,
height=130)
self.canvas.pack()
self.root.mainloop()
Then somewhere a long the line I do:
self.canvas.create_line(args[0], args[1], args[2],
args[3], fill=color)
self.canvas.pack()

Unrelated question: why are you doing a .pack() again here? Packing the
widget just inserts it at the right place in its container, so you only
have to do it once. So the one you did in __init__ is enough.
I've tried self.root.set_focus(), self.root.force_focus(),
self.canvas.update(), etc. but I can't get it.

IIRC:
- self.root.set_focus() will only work if your application already has the
focus, so it's not what you need here.
- self.root.force_focus() is usually considered as evil: it'll give the
focus to your application whatever the user is doing, which is usually
*really* annoying. So I guess a lot of window managers just refuse to do
it; this may be what happens here.

But self.canvas.update() should work. If it doesn't, then there are
probably limitations on your platform that prevents it to work... Do you
happen to have other applications that can update their display while they
don't have the focus? Do they have the same problem? If they do, it's
probably a limitation on the platform and I guess you won't be able to do
anything about it... BTW, what platform are you on?
Thanks!
Blaine

HTH

Thanks a lot for the response! To answer your first question - I'm
using pack() there because I didn't mean to slip it in - this is
something I wrote very quickly and was playing around with... and
pack() should not be there, heh.

I'm just using OS X with Python 2.5. For the record - I don't mind if
the application forces focus. The only time I will be running this
emulator is when I'm trying to use it.

I'll try the update() again. I would want to use that on the canvas
itself right? Not the root window?

Blaine
 
E

Eric Brunel

I'll try the update() again. I would want to use that on the canvas
itself right? Not the root window?

Well, in fact, there is no difference at all... In tcl/tk, update is a
function, and isn't applied to a particular widget. Any call to the update
method on any widget should refresh the whole GUI.

HTH
 
R

Robert.Spilleboudt

blaine said:
Hey everyone!
I'm not very good with Tk, and I am using a very simple canvas to
draw some pictures (this relates to that nokia screen emulator I had a
post about a few days ago).

Anyway, all is well, except one thing. When I am not in the program,
and the program receives a draw command (from a FIFO pipe), the canvas
does not refresh until I click into the program. How do I force it to
refresh, or force the window to gain focus? It seems like pretty
common behavior, but a few things that I've tried have not worked.

Class screen():
def __init__(self):
self.root = Tkinter.Tk()
self.root.title('Nokia Canvas')
self.canvas = Tkinter.Canvas(self.root, width =130,
height=130)
self.canvas.pack()
self.root.mainloop()

Then somewhere a long the line I do:
self.canvas.create_line(args[0], args[1], args[2],
args[3], fill=color)
self.canvas.pack()

I've tried self.root.set_focus(), self.root.force_focus(),
self.canvas.update(), etc. but I can't get it.
Thanks!
Blaine
When you read the pipe, do you generate an event? Probably not , and Tk
is event-driven and should never update the canvas if there is no event.
This is how I understand Tk.

I have a Tk program who reads a audio signal (from an RC transmitter) .
I generate a event every 100 msec , and "process" refresh the canvas.
Starting the sequence:
id = root.after(100,process) will call "process" after 100 msec
At the end of "process", repeat:
id = root.after(100,process)
Robert
 
E

Eric Brunel

blaine said:
Hey everyone!
I'm not very good with Tk, and I am using a very simple canvas to
draw some pictures (this relates to that nokia screen emulator I had a
post about a few days ago).
Anyway, all is well, except one thing. When I am not in the program,
and the program receives a draw command (from a FIFO pipe), the canvas
does not refresh until I click into the program. How do I force it to
refresh, or force the window to gain focus? It seems like pretty
common behavior, but a few things that I've tried have not worked.
Class screen():
def __init__(self):
self.root = Tkinter.Tk()
self.root.title('Nokia Canvas')
self.canvas = Tkinter.Canvas(self.root, width =130,
height=130)
self.canvas.pack()
self.root.mainloop()
Then somewhere a long the line I do:
self.canvas.create_line(args[0], args[1], args[2],
args[3], fill=color)
self.canvas.pack()
I've tried self.root.set_focus(), self.root.force_focus(),
self.canvas.update(), etc. but I can't get it.
Thanks!
Blaine
When you read the pipe, do you generate an event? Probably not , and Tk
is event-driven and should never update the canvas if there is no event.
This is how I understand Tk.

I have a Tk program who reads a audio signal (from an RC transmitter) ..
I generate a event every 100 msec , and "process" refresh the canvas.
Starting the sequence:
id = root.after(100,process) will call "process" after 100 msec
At the end of "process", repeat:
id = root.after(100,process)
Robert

Your method actually works and is in fact even clearer: you explicitely
give back the control to the main event loop by sending the event. But the
call to 'update' should also work, since it also gives back the control to
the main event loop, implicitely however. BTW, the pending events *are*
treated by a call to update, which can cause some surprises...
 
B

blaine

Still doesn't work. I'm looking into using wx instead...

This is the full code - does it work for anyone else? Just do a echo
'line 0 0 10 10' > dev.file


import sys, os, time, Tkinter, threading

class nokia_fkscrn(Tkinter.Toplevel):
fp=None
def __init__(self, file):
self.fname = file
# Create the FIFO pipe (hopefully /dev/screen or similar)
if not os.path.exists(self.fname): os.mkfifo(self.fname)
self.readthread = threading.Thread(target=self.read)
self.readthread.start()
self.init_canvas()

def init_canvas(self):
# Set up our canvas
self.root = Tkinter.Tk()
self.root.title('Nokia Canvas')
self.canvas = Tkinter.Canvas(self.root, width =130,
height=130)

self.canvas.pack()
self.root.mainloop()


def read(self):
while 1:
self.fp = open(self.fname, 'r')
while 1:
st = self.fp.readline()
if st == '': break
self.process(st)
self.fp.close()

def process(self, line):
cmd = line.split()
if cmd[0] == 'line':
# Draw Line
args = map(int, cmd[1:])
try:
color=args[4]
except:
color='black'
if self.canvas:
print 'Drawing Line:', args
self.canvas.create_line(args[0], args[1], args[2],
args[3], fill=color)
self.canvas.update()
self.canvas.focus_force()

nokia = nokia_fkscrn('dev.file')
 
P

Peter Otten

blaine said:
Still doesn't work. I'm looking into using wx instead...

This is the full code - does it work for anyone else? Just do a echo
'line 0 0 10 10' > dev.file

Haven't tried it, but I think that the problem is that you are updating the
UI from the "readthread". A good example to model your app after is here:

http://effbot.org/zone/tkinter-threads.htm

Peter
 
E

Eric Brunel

Update: Not only is that a good model, its exactly what I'm trying to
do. Thanks again!

BTW, instead of reading the queue periodically, you can also use the event
loop for that: in the secondary thread, generate a custom event using the
event_generate method. Custom events are enclosed in '<<...>>', the '...'
can be whatever you like. Be sure to generate the event with the
when='tail' option or the event might get handled immediatly. To treat the
event, do a binding on it: it will be called in your main thread. It's a
common trick to switch threads so that GUI events are treated in the main
thread and not in secondary ones.

Example:

------------------------------------------
import threading
import time
import Queue
from Tkinter import *

root = Tk()

q = Queue.Queue()

def thread_action():
i = 1
while 1:
time.sleep(1)
q.put(i)
root.event_generate('<<Ping>>', when='tail')
i += 1

v = StringVar()

def ping_received(event):
v.set(q.get())

Label(root, textvariable=v, width=10).pack()
root.bind('<<Ping>>', ping_received)

th = threading.Thread(target=thread_action)
th.setDaemon(1)
th.start()

root.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,997
Messages
2,570,239
Members
46,828
Latest member
LauraCastr

Latest Threads

Top