python GTK: Dialog with information that app is closing - python

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()

Related

How to display progress without multi-threading

Let's say I have a PyQt program that goes through a given directory, looks for *JPEG images, and does some processing every time it finds one. Depending on the size of the selected directory, this may take from some seconds to minutes.
I would like to keep my user updated with the status - preferably with something like "x files processed out of y files" . If not, a simple running pulse progress bar by setting progressbar.setRange(0,0) works too.
From my understanding, in order to prevent my GUI from freezing, I will need a seperate thread that process the images, and the original thread that updates the GUI every interval.
But I am wondering if there is any possible way for me to do both in the same thread?
Yes, you can easily do this using processEvents, which is provided for this exact purpose.
I have used this technique for implementing a simple find-in-files dialog box. All you need to do is launch the function that processes the files with a single-shot timer, and then periodically call processEvents in the loop. This is should be good enough to update a counter with the number of files processed, and also allow the user to cancel the process, if necessary.
The only real issue is deciding on how frequently to call processEvents. The more often you call it, the more responsive the GUI will be - but this comes at the cost of considerably slowing the processing of the files. So you may have to experiment a little bit in order to find an acceptable compromise.
UPDATE:
Here's a simple demo that shows how the code could be structured:
import sys, time
from PyQt5 import QtWidgets, QtCore
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.button = QtWidgets.QPushButton('Start')
self.progress = QtWidgets.QLabel('0')
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.button)
layout.addWidget(self.progress)
self.button.clicked.connect(self.test)
self._stop = False
self._stopped = True
def test(self):
if self._stopped:
self._stop = False
self.progress.setText('0')
self.button.setText('Stop')
QtCore.QTimer.singleShot(1, self.process)
else:
self._stop = True
def process(self):
self._stopped = False
for index in range(1, 1000):
time.sleep(0.01)
self.progress.setText(str(index))
if not index % 20:
QtWidgets.qApp.processEvents(
QtCore.QEventLoop.AllEvents, 50)
if self._stop:
break
self._stopped = True
self.button.setText('Start')
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
I could not achieve the thing you need without multi threading and this is not possible because gui can be only updated in main thread. Below is an algorithm how I did this with multithreading.
Let's say you have your application processing images. Then there are the following threads:
Main thread (that blocks by GUI/QApplication-derived classes.exec())
Timer with, for example, 1 second interval which updates a variable and calls a slot in GUI thread which updates a variable in user interface.
A thread which is processing images on your pc.
def process(self):
self._status = "processing image 1"
....
def _update(self):
self.status_label.setText(self._status)
def start_processing(self, image_path):
# create thread for process and run it
# create thread for updating by using QtCore.QTimer()
# connect qtimer triggered signal to and `self._update()` slot
# connect image processing thread (use connect signal to any slot, in this example I'll stop timer after processing thread finishes)
#pyqtSlot()
def _stop_timer():
self._qtimer.stop()
self._qtimer = None
_update_thread.finished.connect(_stop_timer)
In pyqt5 it is possible to assign a pyqtvariable from a one nested thread(first level). So you can make your variable a pyqtvariable with setter and getter and update gui in a setter or think how you can do this by yourself.
You could just use the python threading module and emit a signal in your threaded routine.
Here's a working example
from PyQt4 import QtGui, QtCore
import threading
import time
class MyWidget(QtGui.QWidget):
valueChanged = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
self.computeButton = QtGui.QPushButton("Compute", self)
self.progressBar = QtGui.QProgressBar()
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.computeButton)
layout.addWidget(self.progressBar)
self.computeButton.clicked.connect(self.compute)
self.valueChanged.connect(self.progressBar.setValue)
def compute(self):
nbFiles = 10
self.progressBar.setRange(0, nbFiles)
def inner():
for i in range(1, nbFiles+1):
time.sleep(0.5) # Process Image
self.valueChanged.emit(i) # Notify progress
self.thread = threading.Thread(target = inner)
self.thread.start()
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
widget = MyWidget()
widget.show()
sys.exit(app.exec_())

Gtk widget shows up with delay

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).

wxPython: Stuck in .MainLoop()

I am not an experienced programmer. This is probably a simple problem to solve.
I have a function that is supposed to run every two minutes. This function is inside a simple wxPython system tray program. The problem is that I do not know how to run the function because wxPython never leave .MainLoop(). Where should I put the function?
Here is the code: (I have left out the function and import because it is not relevant.)
TRAY_TOOLTIP = 'System Tray Demo'
TRAY_ICON = 'star.png'
def create_menu_item(menu, label, func):
item = wx.MenuItem(menu, -1, label)
menu.Bind(wx.EVT_MENU, func, id=item.GetId())
menu.AppendItem(item)
return item
class TaskBarIcon(wx.TaskBarIcon):
def __init__(self):
super(TaskBarIcon, self).__init__()
self.set_icon(TRAY_ICON)
self.Bind(wx.EVT_TASKBAR_LEFT_DOWN, self.on_left_down)
def CreatePopupMenu(self):
menu = wx.Menu()
create_menu_item(menu, 'Say Hello', self.on_hello)
menu.AppendSeparator()
create_menu_item(menu, 'Exit', self.on_exit)
return menu
def set_icon(self, path):
icon = wx.IconFromBitmap(wx.Bitmap(path))
self.SetIcon(icon, TRAY_TOOLTIP)
def on_left_down(self, event):
print 'Tray icon was left-clicked.'
MailCheck()
def on_hello(self, event):
print 'Hello, world!'
def on_exit(self, event):
wx.CallAfter(self.Destroy)
def main():
app = wx.PySimpleApp()
TaskBarIcon()
app.MainLoop()
#This is my function I want to run
#But MainLoop() never ends. Where should I put MainCheck() ?
MailCheck()
if __name__=='__main__':
main()
wxPython, as with most GUI frameworks, uses an event-driven programming model. That means that bits of your program are run in response to actions that may have originated from the user, (such as a key press, a menu selection, etc.) the system or perhaps from some other program. The rest of the time it sits in MainLoop waiting for one of those things to happen.
For situations like yours, there is the wx.Timer class which can trigger an event once or perhaps periodically after N milliseconds. If you bind an event handler for the timer event, then that handler will be called when the timer expires.
I've never used wxPython but you could use the threading-module of Python's standard library.
A minimal example:
import threading
def work():
threading.Timer(0.25, work).start()
print "stackoverflow"
work()
Look at this thread (example is from there): Periodically execute function in thread in real time, every N seconds

My signals are being ignored!

So I developed a UI in Glade and am coding the program in Python. For some reason, all of my signals are being ignored! Though I've connected them correctly (I think), clicking on the buttons does absolutely nothing!
Below is the code that I'm using to load the ui and connect the signals. Can anyone see WHY they might be being ignored?
class mySampleClass(object):
def __init__(self):
self.uiFile = "MainWindow.glade"
self.wTree = gtk.Builder()
self.wTree.add_from_file(self.uiFile)
self.window = self.wTree.get_object("winMain")
if self.window:
self.window.connect("destroy", gtk.main_quit)
dic = { "on_btnExit_clicked" : self.clickButton, "on_winMain_destroy" : gtk.main_quit }
self.wTree.connect_signals(dic)
self.window.show()
else:
print "Could not load window"
sys.exit(1)
def clickButton(self, widget):
print "You clicked exit!"
def exit(self, widget):
gtk.main_quit()
def update_file_selection(self, widget, data=None):
selected_filename = FileChooser.get_filename()
print selected_filename
if __name__ == "__main__":
MyApp = MySampleClass()
gtk.main()
I'm not completely sure that this is the answer, but I know that a number of PyGTK objects cannot send signals themselves - gtk.Image and gtk.Label are two such examples. The solution is to place the widget inside of an event box (gtk.EventBox) and link the events to that.
I do not know if this is the case with tree objects, however. All the same, worth investigating, imho.

Python GTK window in Thread

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

Categories

Resources