I have a CLI application, which is digging some data, in case of need, fires up a thread which creates GTK window with some information. However the CLI (main thread) still analyzes the data in the background, so there could be numerous windows created. In case I close the window, the destroy event is actually fired up, I got a debug line in CLI, but the window locks up.
Some magical command that I have to use ?
I create window like this in the main thread:
gtk.gdk.threads_init()
notifyWindow = NotifyWindow()
notifyWindow.start()
This is NotifyWindow(Thread).destroy
def destroy(self, widget, data=None):
print "destroy signal occurred"
gtk.main_quit()
This is NotifyWindow(Thread).run
def run(self):
self.window = gtk.glade.XML( "hadinfo.glade" )
self.window_main = self.window.get_widget("window_main")
if (self.window_main):
self.window_main.connect("destroy", self.destroy)
self.window_main.connect("delete_event", self.delete_event)
self.button_cancel = self.window.get_widget("button_cancel")
self.button_cancel.connect("clicked", self.destroy)
self.window.get_widget("window_main").show()
gtk.main()
using a gtk.threads_enter() and leave around your main call should help.
Take a look at the PyGtk Faq : PyGtk FAQ
Related
Using python3 and gi.repository, I want to have a Gtk.HeaderBar with a Gtk.Button that is replaced by a Gtk.Spinner as soon as you click on it. After a calculation the button should appear again.
Here is an example of how I think it should work but the Gtk.Spinner only shows up after the calculation (in this example sleep) for a very short time. How can I achieve that the spinner shows up for the whole calculation (or sleep)?
from gi.repository import Gtk
import time
class window:
def __init__(self):
self.w = Gtk.Window()
self.button = Gtk.Button('x')
self.button.connect('clicked', self.on_button_clicked)
self.spinner = Gtk.Spinner()
self.hb = Gtk.HeaderBar()
self.hb.props.show_close_button = True
self.hb.pack_start(self.button)
self.w.set_titlebar(self.hb)
self.w.connect('delete-event', Gtk.main_quit)
self.w.show_all()
def on_button_clicked(self, widget):
self.button.hide()
self.hb.pack_start(self.spinner)
self.spinner.show()
self.spinner.start()
time.sleep(5)
self.spinner.stop()
self.hb.remove(self.spinner)
self.button.show()
if __name__ == '__main__':
w = window()
Gtk.main()
GTK+ is an event driven system where the mainloop should be left free to update the UI and everything that takes time (like reading from a file, making a network connection, long calculations) should happen asynchronously.
In your case this would look something like this:
def on_button_clicked(self, widget):
self.button.hide()
self.spinner.show()
self.spinner.start()
GLib.timeout_add_seconds (5, self.processing_finished)
def processing_finished(self):
self.spinner.stop()
self.spinner.hide()
self.button.show()
Note that I removed the pack and remove calls: do those in __init__(). You'll want from gi.repository import GLib in there as well.
This way the main loop is free to update the UI as often as it wants. If you really want to use a blocking call like sleep(), then you'll need to do that in another thread, but my suggestion is to use libraries that are asychronous like that timeout_add_seconds() call.
The problem is time.sleep(): it is a blocking function.
def on_button_clicked(self, widget):
self.button.hide()
self.hb.pack_start(self.spinner)
self.spinner.show()
self.spinner.start()
t = time.time()
while time.time() - t < 5:
Gtk.main_iteration()
self.spinner.stop()
self.hb.remove(self.spinner)
self.button.show()
I think that's what you expect.
Edit: You may put a time.sleep(.1) inside while loop, for cpu saving, but don't forget Gtk.main_iteration(): that is the function that exit from while loop to main loop (show spinner, progress bar and so on).
I have a problem
My application on close has to logout from web application. It's take some time. I want to inform user about it with " logging out" information
class Belt(gtk.Window):
def __init__(self):
super(Belt, self).__init__()
self.connect("destroy", self.destroy)
def destroy(self, widget, data=None):
if self.isLogged:
md = gtk.MessageDialog(None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, ico, gtk.BUTTONS_NONE, txt)
md.showall()
self.send('users/logout.json', {}, False, False)
gtk.main_quit()
def main(self):
if self.iniError is False:
gtk.gdk.threads_init()
gtk.gdk.threads_enter()
gtk.main()
gtk.gdk.threads_leave()
if __name__ == "__main__":
app = Belt()
app.main()
When I try to show dialog in destroy method only window does appear, without icon and text.
I want to, that this dialog have no confirm button, just the information, and dialog have to be destroy with all app.
Any ideas?
Sorry for my poor English
Basically, GTK has to have the chance to work through the event queue all the time. If some other processing takes a long time and the event queue is not processed in the meantime, your application will become unresponsive. This is usually not what you want, because it may result in your windows not being updated, remaining grey, having strange artefacts, or other kinds of visible glitches. It may even cause your window system to grey the window out and offer to kill the presumably frozen application.
The solutution is to make sure the event queue is being processed. There are two primary ways to do this. If the part that takes long consists of many incremental steps, you can periodically process the queue yourself:
def this_takes_really_long():
for _ in range(10000):
do_some_more_work()
while gtk.events_pending():
gtk.main_iteration()
In the general case, you'll have to resort to some kind of asynchronous processing. The typical way is to put the blocking part into its own thread, and then signal back to the main thread (which sits in the main loop) via idle callbacks. In your code, it might look something like this:
from threading import Thread
import gtk, gobject
class Belt(gtk.Window):
def __init__(self):
super(Belt, self).__init__()
self.connect("destroy", self.destroy)
self.show_all()
self.isLogged = True
self.iniError = False
def destroy(self, widget, data=None):
if self.isLogged:
md = gtk.MessageDialog(None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 0, gtk.BUTTONS_NONE, "Text")
md.show_all()
Thread(target=self._this_takes_very_long).start()
def main(self):
if self.iniError is False:
gtk.gdk.threads_init()
gtk.gdk.threads_enter()
gtk.main()
gtk.gdk.threads_leave()
def _this_takes_very_long(self):
self.send('users/logout.json', {}, False, False)
gobject.idle_add(gtk.main_quit)
if __name__ == "__main__":
app = Belt()
app.main()
I'm creating a simple Python UI via Tkinter, and I'd like use self.title to have the window title change when a callback event is generated.
If I bind the event to a button, or call the event handler directly within the Tk thread, the window title changes as expected. However, I intend this event to be invoked by a separate thread, and I've found that using title in the callback event handler causes the app to hang.
Other tasks that I have in the event handler (such as updating a label) work just fine, so I have to assume that the event is being invoked properly. I've tried wm_title instead of title, but didn't see a difference. I've dug around and found nothing odd about title's usage, just call it with a string to set the title.
Here's a stripped-down sample that replicates the problem (I'm running v2.7.1 on WinXP FYI); the app runs fine for 10 seconds (can move the window, resize, etc.), after which Timer generates the event and the app then freezes.
import Tkinter
import threading
class Gui(Tkinter.Tk):
def __init__(self, parent=None):
Tkinter.Tk.__init__(self, parent)
self.title('Original Title')
self.label = Tkinter.Label(self, text='Just a Label.',
width=30, anchor='center')
self.label.grid()
self.bind('<<change_title>>', self.change_title)
timer = threading.Timer(10, self.event_generate, ['<<change_title>>'])
timer.start()
def change_title(self, event=None):
self.title('New Title')
G = Gui(None)
G.mainloop()
I encountered the same problem, where the UI hangs when calling self.title() from a thread other than the main thread. Tkinter expects all UI stuff to be done in the same thread (the main thread).
My solution was to have the separate thread put functions in a queue. The queue is serviced periodically by the main thread, making use of the after(ms) function provided by Tkinter. Here's an example with your code:
import Tkinter
import threading
from Queue import Queue, Empty
class Gui(Tkinter.Tk):
def __init__(self, parent=None):
Tkinter.Tk.__init__(self, parent)
self.ui_queue = Queue()
self._handle_ui_request()
self.title('Original Title')
self.label = Tkinter.Label(self, text='Just a Label.',
width=30, anchor='center')
self.label.grid()
self.bind('<<change_title>>', self.change_title)
timer = threading.Timer(1, self.event_generate, ['<<change_title>>'])
timer.start()
def change_title(self, event=None):
# Separate the function name, it's args and keyword args,
# and put it in the queue as a tuple.
ui_function = (self.title, ('New Title',), {})
self.ui_queue.put(ui_function)
def _handle_ui_request(self):
'''
Periodically services the UI queue to handles UI requests in the main thread.
'''
try:
while True:
f, a, k = self.ui_queue.get_nowait()
f(*a, **k)
except Empty:
pass
self.after(200, self._handle_ui_request)
G = Gui(None)
G.mainloop()
Well, your code actually runs fine to me.
Except that, when interrupted before the ten secs, it says "RuntimeError: main thread is not in main loop"
I'm using python 2.6.6 under ubuntu 10.10
Hope this was of some help.
I tried it as well with 2.6.2 (Windows) and the caption/title didn't change. No runtime error though.
The protocol attribute in Tkinter allows one to run functions when the exit button of a window has been clicked (the button with the x on it, it's top right in Windows).
I'd like to run a function when the user try's to exit my application. Is there a wxPython equivalent?
snippet:
self.protocol("WM_DELETE_WINDOW", self.do_something)
When you click on the close button you are producing an EVT_CLOSE event so if you bind this event to an onClose method then you can execute whatever you want before actually closing the application. A simple example:
class ChildFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
self.Bind(wx.EVT_CLOSE, self.on_close)
def on_close(self, evt):
process_whatever_you_want()
self.Destroy()
I have a PyGTK program which is hidden most of the time, but with a keypress it shall come up as a popup. Therefore I want the program not to be activated when its opened. I tried several options to to that, with no success:
self.window.show()
self.window.set_focus(None)
Activates the program, but sets no focus.
self.window.set_accept_focus(False)
self.window.show()
self.window.set_accept_focus(True)
With the last command, the window gets activated.
self.window.show()
self.window.unset_flags(gtk.HAS_FOCUS)
Does nothing...
Btw. I am using Ubuntu 9.10 (metacity)
Build the window but don't call show() on it until it is ready to be activated. Then use self.window.present().
EDIT:
If you never want the window to be activated, why not try a notification popup? You need libnotify for this. There are Python bindings. Here is an example: http://roscidus.com/desktop/node/336
In combination with a toolbar applet, this could do what you want -- i.e. the notification is raised when the user either clicks on the applet or presses the key combination.
I figured out how to do it. See the example below:
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
import gobject
class HelloWorld:
window=None
def hello(self, widget, data=None, data2=None):
HelloWorld.window.set_accept_focus(True)
HelloWorld.window.present()
def __init__(self):
HelloWorld.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.button = gtk.Entry(50)
self.button.connect("focus-in-event", self.hello, None)
HelloWorld.window.add(self.button)
self.button.show()
HelloWorld.window.set_accept_focus(False)
self.button.connect('button-press-event', self.hello)
HelloWorld.window.show()
def main(self):
gtk.main()
if __name__ == "__main__":
hello = HelloWorld()
hello.main()