I've been trying to sort this problem for hours now, scouring the web and reading the documentation hasn't helped. I'm trying to perform a long running task in a separate thread and display the progress in a progress bar in the UI. The new thread is started but is not given any time until the main loop quits, I have called Gdk.threads_init() before Gtk.main and I have surrounded UI calls with Gdk.threads_enter() and Gdk.threads_leave().
The following code reproduces the problem, clicking on the 'start' button has no effect, but once the window is closed and the main loop quits I see the second thread doing work (i.e I see the print statements half a second apart)
class App(Gtk.Window):
def __init__(self):
super(App, self).__init__()
self.connect("destroy", self.on_destroy)
self.layout = Gtk.VBox()
self.progress = Gtk.ProgressBar()
self.layout.pack_start(self.progress, False, False, 0)
self.set_size_request(100,100)
self.go_button = Gtk.Button("Start")
self.go_button.connect("clicked", self.do_work_subclass)
self.layout.pack_start(self.go_button, False, False, 0)
self.add(self.layout)
self.show_all()
def on_destroy(widget, event):
Gtk.main_quit()
def do_work(self, widget):
def worker_thread():
so_far = 0
while so_far < 10:
time.sleep(0.5)
print("work so far: %s" % so_far)
Gdk.threads_enter()
try:
if so_far > 0:
self.progress.set_fraction(so_far / 10.0)
finally:
Gdk.threads_leave()
so_far += 1
threading.Thread(target=worker_thread).start()
if __name__ == "__main__":
app = App()
Gdk.threads_init()
Gdk.threads_enter()
Gtk.main()
Gdk.threads_leave()
Could this be related to the fact that I am using Gtk3?
And having spent hours trying to find an answer before posting a question I find the solution minutes after posting. Turns out (from here) that for GTK3 you need to call GLib.threads_init(), the code works with an initial from gi.repository import GLib and a GLib.threads_init() before the call to Gtk.main(), so a working version of the code looks like this:
from gi.repository import Gtk,Gdk, GLib
import threading
import time
class App(Gtk.Window):
def __init__(self):
super(App, self).__init__()
self.connect("destroy", self.on_destroy)
self.layout = Gtk.VBox()
self.progress = Gtk.ProgressBar()
self.layout.pack_start(self.progress, False, False, 0)
self.set_size_request(100,100)
self.go_button = Gtk.Button("Start")
self.go_button.connect("clicked", self.do_work)
self.layout.pack_start(self.go_button, False, False, 0)
self.add(self.layout)
self.show_all()
def on_destroy(widget, event):
Gtk.main_quit()
def do_work(self, widget):
def worker_thread():
so_far = 0
while so_far < 10:
time.sleep(0.5)
print("work so far: %s" % so_far)
Gdk.threads_enter()
try:
if so_far > 0:
self.progress.set_fraction(so_far / 10.0)
finally:
Gdk.threads_leave()
so_far += 1
threading.Thread(target=worker_thread).start()
if __name__ == "__main__":
app = App()
GLib.threads_init()
Gdk.threads_init()
Gdk.threads_enter()
Gtk.main()
Gdk.threads_leave()
Related
So I have the following code (python 2.7) that checks for a combination of keypresses and shows/hides a gtk window with a few gtk entry boxes. The show/hide works until the window locks up and stops responding, and the inner part of the window turns black. I have to kill the process and start it up again once that window turns black. I've tried all different combinations of show_all() and returning True after hiding the window to no avail. I'm not sure what I'm doing wrong here.
In the end I'd like to be able to press this key combination and show/hide this window without the contents going away.
#!/usr/bin/python
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import pyxhook
class MyWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Configurator")
self.box = Gtk.Box(spacing=6)
self.add(self.box)
self.ipEntry = Gtk.Entry()
self.ipEntry.set_text("IP Address")
self.maskEntry = Gtk.Entry()
self.maskEntry.set_text("NetMask")
self.gatewayEntry = Gtk.Entry()
self.gatewayEntry.set_text("gatewayEntry")
self.button1 = Gtk.Button(label="Save")
self.button1.connect("clicked", self.on_button1_clicked)
self.box.pack_start(self.ipEntry, True, True, 0)
self.box.pack_start(self.maskEntry, True, True, 0)
self.box.pack_start(self.gatewayEntry, True, True, 0)
self.box.pack_start(self.button1, True, True, 0)
#catch window destroy event and stop it from happening
self.connect('delete-event', self.on_destroy)
def on_button1_clicked(self, widget):
print("Hello")
def on_destroy(self, widget=None, *data):
print("tried to destroy")
self.hide()
return True
#list of ascii keypresses to test if a certain combination of keys is pressed
keypresses = []
win = MyWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
win.set_keep_above(True)
def OnKeyboardEvent(event):
#ascii 227 is l_control, ascii 225 is l_shift, ascii 120 is x
#bring the following external variables into the scope of this function
global keypresses
global win
#check if gtk window is hidden or visible
isVisible = win.get_property("visible")
#store our keypress if it is worthy of being stored (we started with a control character)
if event.Ascii == 227 or ( len(keypresses) >= 1 and keypresses[0] == 227 ):
print("here" + str(len(keypresses)))
keypresses.append(event.Ascii)
#check if we have three items in keypresses to compare, then see if it's the right combination
if len(keypresses) == 3 and keypresses[0] == 227 and keypresses[1] == 225 and keypresses[2] == 120:
#show/hide our window
if isVisible:
win.hide()
del keypresses[:]
keypresses = []
else:
win.show_all()
#clear out our list to compare again
del keypresses[:]
keypresses = []
elif len(keypresses) >= 3:
del keypresses[:]
keypresses = []
#create instance of hook manager
HookManager = pyxhook.HookManager()
#define our callback to fire when a key is pressed
HookManager.KeyDown = OnKeyboardEvent
#hook the keyboard
HookManager.HookKeyboard()
#start our listener
HookManager.start()
Gtk.main()
#close the hook listener when gtk main loop exits
HookManager.cancel()
Here is a small hack that tests the window show/hide when pressing F5. When you press it a second window will show or hide and i've tested for sometime without problems. Maybe it's the HookManager that somehow is messing with the Gtk/Glib mainloop. Nonetheless my method only works if there is a window to grab the key presses, so you will need, indeed, some form of grabbing key presses if your goal is to have no GUI:
#!/usr/bin/python
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
#import pyxhook
class MyWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Configurator")
self.box = Gtk.Box(spacing=6)
self.add(self.box)
self.ipEntry = Gtk.Entry()
self.ipEntry.set_text("IP Address")
self.maskEntry = Gtk.Entry()
self.maskEntry.set_text("NetMask")
self.gatewayEntry = Gtk.Entry()
self.gatewayEntry.set_text("gatewayEntry")
self.button1 = Gtk.Button(label="Save")
self.button1.connect("clicked", self.on_button1_clicked)
self.box.pack_start(self.ipEntry, True, True, 0)
self.box.pack_start(self.maskEntry, True, True, 0)
self.box.pack_start(self.gatewayEntry, True, True, 0)
self.box.pack_start(self.button1, True, True, 0)
#catch window destroy event and stop it from happening
self.connect('delete-event', self.on_destroy)
self.connect('key-press-event', self.on_keypress)
def on_keypress(self, widget, event):
global w
if event.keyval == Gdk.KEY_F5:
isVisible = w.get_property("visible")
if (isVisible):
w.hide()
else:
w.show()
print("Keypress")
def on_button1_clicked(self, widget):
print("Hello")
def on_destroy(self, widget=None, *data):
print("tried to destroy")
self.hide()
return False
w = MyWindow()
win = MyWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
win.set_keep_above(True)
Gtk.main()
I'm writing a PyQT4 (4.11) program that performs a long slow task which ideally needs a progress bar. The program works almost perfectly if I don't use threads, sub-classing a QWidget which only contains a QProgressBar and a layout. Instantiating this sub-class as form, I can call form.show() to put it on screen, and then my long slow loop can update the progress by calling form.progressbar.setValue(progress). There are two problems with this:
if the user tries to interact with the window they get 'not responding' messages from the windows manager/OS desktop process. This is because events are not being processed.
because events aren't being processed, the user can't cancel the long slow loop by closing the window.
So I tried making the long slow loop run in a separate thread, using a signal to update the progress bar. I overrode the closeEvent of my QWidget so that it can cancel interaction with the hardware device (all wrapped in mutexes so the device communication doesn't get out of sync). Again, this almost works. If I cancel, the application quits. If I leave it to run to completion though, I have to close the window manually (i.e. click the close icon or press alt-f4), even though I'm sending a close signal to the QWidget. As you can see in the code below there are some complications, since the app can't close immediately if cancelled, because it has to wait for some hardware clean up to happen. Here is a minimal version of my code
import sys
import os
import time
from PyQt4 import QtCore, QtGui
class Ui_ProgressBarDialog(QtGui.QWidget):
def __init__(self, on_close=None):
QtGui.QWidget.__init__(self)
self.setupUi(self)
self.center()
#on_close is a function that is called to cancel
#the long slow loop
self.on_close = on_close
def center(self):
qr = self.frameGeometry()
cp = QtGui.QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def setupUi(self, ProgressBarDialog):
ProgressBarDialog.setObjectName(_fromUtf8("ProgressBarDialog"))
ProgressBarDialog.resize(400, 33)
self.verticalLayout = QtGui.QVBoxLayout(ProgressBarDialog)
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
self.progressBar = QtGui.QProgressBar(ProgressBarDialog)
self.progressBar.setProperty("value", 0)
self.progressBar.setObjectName(_fromUtf8("progressBar"))
self.verticalLayout.addWidget(self.progressBar)
self.retranslateUi(ProgressBarDialog)
#This allows the long slow loop to update the progress bar
QtCore.QObject.connect(
self,
QtCore.SIGNAL("updateProgress"),
self.progressBar.setValue
)
#Catch the close event so we can interrupt the long slow loop
QtCore.QObject.connect(
self,
QtCore.SIGNAL("closeDialog"),
self.closeEvent
)
#Repaint the window when the progress bar's value changes
QtCore.QObject.connect(
self.progressBar,
QtCore.SIGNAL("valueChanged(int)"),
self.repaint
)
QtCore.QMetaObject.connectSlotsByName(ProgressBarDialog)
def retranslateUi(self, ProgressBarDialog):
ProgressBarDialog.setWindowTitle("Please Wait")
def closeEvent(self, event, force=False):
if self.on_close is not None and not force:
self.on_close()
app = QtGui.QApplication(sys.argv)
filename = str(QtGui.QFileDialog.getSaveFileName(
None,
"Save as",
os.getcwd(),
"Data files: (*.dat)"
))
loop_mutex = thread.allocate_lock()
cancelled = False
can_quit = False
result = None
def cancel_download():
global cancelled
if can_quit:
return
if QtGui.QMessageBox.question(
None,
'Cancel Download',
"Are you sure you want to cancel the download?",
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
QtGui.QMessageBox.No) == QtGui.QMessageBox.Yes:
with loop_mutex:
selected_device.cancelDownload()
cancelled = True
while not can_quit:
time.sleep(0.25)
form = ProgressBarDialog.Ui_ProgressBarDialog(cancel_download)
form.setWindowTitle("Please Wait")
form.progressBar.setMaximum(1000)
form.progressBar.setValue(0)
form.show()
def long_slow_loop(mutex, filename):
global can_quit, result
progress = 0
temp_binary_file = open(filename, "wb")
#The iterator below does the actual work of interacting with a
#hardware device, so I'm locking around the "for" line. I must
#not fetch more data from the device while a cancel command is
#in progress, and vice versa
mutex.acquire()
for data in ComplexIteratorThatInteractsWithHardwareDevice():
mutex.release()
temp_binary_file.write(datablock)
progress += 1
form.emit(QtCore.SIGNAL("updateProgress"), progress)
mutex.acquire()
if cancelled:
break
mutex.release()
result = not cancelled
temp_binary_file.close()
if cancelled:
os.unlink(filename)
#having set can_quit to True the following emission SHOULD
#cause the app to exit by closing the last top level window
can_quit = True
form.emit(QtCore.SIGNAL("closeDialog"), QtGui.QCloseEvent(), True)
thread.start_new_thread(do_dump, (loop_mutex, filename))
app.exec_()
if result == True:
QtGui.QMessageBox.information(None, 'Success', "Save to disk successful", QtGui.QMessageBox.Ok)
It turns out the secret is to use a QThread which emits some signals when the thread exits. Either "finished" or "terminated" depending on whether the exit was normal or abnormal. Here's some code
import sys
import os
import time
from PyQt4 import QtCore, QtGui
class Ui_ProgressBarDialog(QtGui.QWidget):
def __init__(self, process, parent = None):
QtGui.QWidget.__init__(self, parent)
self.thread = process
self.setupUi(self)
self.center()
self.thread.start()
def center(self):
qr = self.frameGeometry()
cp = QtGui.QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def setupUi(self, ProgressBarDialog):
ProgressBarDialog.setObjectName("ProgressBarDialog")
ProgressBarDialog.resize(400, 33)
self.verticalLayout = QtGui.QVBoxLayout(ProgressBarDialog)
self.verticalLayout.setObjectName("verticalLayout")
self.progressBar = QtGui.QProgressBar(ProgressBarDialog)
self.progressBar.setProperty("value", 0)
self.progressBar.setObjectName("progressBar")
self.verticalLayout.addWidget(self.progressBar)
self.retranslateUi(ProgressBarDialog)
#Close when the thread finishes (normally)
QtCore.QObject.connect(
self.thread,
QtCore.SIGNAL("finished()"),
self.close
)
#Close when the thread is terminated (exception, cancelled etc)
QtCore.QObject.connect(
self.thread,
QtCore.SIGNAL("terminated()"),
self.close
)
#Let the thread update the progress bar position
QtCore.QObject.connect(
self.thread,
QtCore.SIGNAL("updateProgress"),
self.progressBar.setValue
)
#Repaint when the progress bar value changes
QtCore.QObject.connect(
self.progressBar,
QtCore.SIGNAL("valueChanged(int)"),
ProgressBarDialog.repaint
)
QtCore.QMetaObject.connectSlotsByName(ProgressBarDialog)
def retranslateUi(self, ProgressBarDialog):
ProgressBarDialog.setWindowTitle("Please Wait")
def closeEvent(self, event):
if self.thread.exit_status == self.thread.RUNNING:
if QtGui.QMessageBox.question(None, 'Cancel Download', "Are you sure you want to cancel the download?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) == QtGui.QMessageBox.Yes:
if self.thread.exit_status == self.thread.RUNNING:
self.thread.exiting = True
while self.thread.exiting:
time.sleep(0.25)
elif self.thread.exit_status == self.thread.SUCCESS:
self.thread.exit_status = self.thread.CANCELLED
else:
if self.thread.exit_status == self.thread.RUNNING:
event.ignore()
app = QtGui.QApplication(sys.argv)
filename = str(QtGui.QFileDialog.getSaveFileName(
None,
"Save as",
os.getcwd(),
"Data files: (*.dat)"
))
class DeviceDataStreamHandler(QtCore.QThread):
RUNNING = 1
SUCCESS = 0
FAILURE = -1
CANCELLED = -2
class CancelledError(Exception):
pass
def __init__(self, parent = None, **kwargs):
QtCore.QThread.__init__(self, parent)
self.exiting = False
self.exit_status = DeviceDataStreamHandler.RUNNING
self.device = device
self.filename = filename
def run(self):
progress = 0
try:
temp_binary_file = open(self.filename, "wb")
#getDataStream is an interator returing strings
for data in self.device.getDataStream():
temp_binary_file.write(data)
progress += 1
self.emit(QtCore.SIGNAL("updateProgress"), progress)
if self.exiting:
raise DeviceDataStreamHandler.CancelledError()
self.exit_status = DeviceDataStreamHandler.SUCCESS
except DeviceDataStreamHandler.CancelledError:
self.exit_status = DeviceDataStreamHandler.CANCELLED
self.device.cancelDownload()
except Exception as E:
self.exit_status = DeviceDataStreamHandler.FAILURE
self.error_details = str(E)
finally:
temp_binary_file.close()
if self.exit_status == DeviceDataStreamHandler.CANCELLED:
os.unlink(filename)
self.exiting = False
class HardwareDeviceObject(object):
def __init__(self):
#initialises comms with a hardware device
self.event_count = 0
self.capture = False
def startCapture(self):
self.capture = True
def cancelDownload():
self.capture = False
def read(self):
#returns a string sent from the device
time.sleep(1)
self.event_count += 1
return self.event_count
def getDataStream(self):
class DataStreamIterator(object):
def __init__(self, ds, max_capture_count = 100):
self.ds = ds
self.capture_count = 0
self.max_capture_count = max_capture_count
def __iter__(self):
return self
def next(self):
#return string received from device
if self.ds.capture and self.capture_count < self.max_capture_count:
self.capture_count += 1
return self.ds.read()
else:
raise StopIteration()
self.startCapture()
return DataStreamIterator(self)
capture = DeviceDataStreamHandler(device = HardwareDeviceObject(), filename = filename)
form = ProgressBarDialog.Ui_ProgressBarDialog(capture)
form.setWindowTitle("Dumping sessions")
form.progressBar.setMaximum(100) #expect 100 outputs from the device
form.progressBar.setValue(0)
form.show()
app.exec_()
if capture.exit_status == DeviceDataStreamHandler.SUCCESS:
QtGui.QMessageBox.information(None, 'Success', "Save to disk successful", QtGui.QMessageBox.Ok)
elif capture.exit_status == DeviceDataStreamHandler.FAILURE:
QtGui.QMessageBox.critical(None, 'Error interacting with device', "{}".format(capture.error_details), QtGui.QMessageBox.Ok)
I developed a GUI program based on python Gtk3. In the Main Window there is a button and a progressbar, my purpose is when pressing the button there is another thread running to do some work finally another GTK window shows up to display the result, in the meantime the preogress could update the progress in the main Window correctly.
But I always got a error as below:
[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
python: xcb_io.c:274: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed.
Aborted (core dumped)
I really don't know what is going wrong there and have no idea about it, could anyone help taking look at it and provide a example here ?
Thanks a lot !
I developed a test program here as below, I always got the follow errors under Linux platform:
(fileChooser.py:40834): Gtk-CRITICAL **: gtk_text_attributes_ref: assertion 'values != NULL' failed
(fileChooser.py:40834): Gtk-CRITICAL **: gtk_text_attributes_ref: assertion 'values != NULL' failed
(fileChooser.py:40834): Pango-CRITICAL **: pango_layout_new: assertion 'context != NULL' failed
Segmentation fault (core dumped)
#!/usr/bin/env python
import os
import re
import multiprocessing
import threading
import platform
from gi.repository import Gtk, GLib, Gdk, GObject
class ANA(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
self.tree_store = dict()
self.COL_NAME = 0
self.COL_COLOR = 1
self.window = Gtk.Window()
self.window.set_title("Analysing Results")
self.window.set_size_request(1000, 750)
self.connect('destroy', lambda *w: Gtk.main_quit())
self.main_vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=1)
self.window.add(self.main_vbox)
self.hpaned = Gtk.HPaned()
self.hpaned.set_position(295)
self.main_vbox.pack_start(self.hpaned, True, True, 0)
self.notebook = Gtk.Notebook()
for tab in ['A', 'B']:
scrolled_window = Gtk.ScrolledWindow()
scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
scrolled_window.set_shadow_type(Gtk.ShadowType.IN)
self._new_notebook_page(self.notebook, scrolled_window, tab)
treeview = self.__create_treeview(tab)
scrolled_window.add(treeview)
self.hpaned.add(self.notebook)
self.notebook1 = Gtk.Notebook()
self.hpaned.add(self.notebook1)
self.scrolled_window2, self.info_buffer, self.info_text_view = self.__create_text(True)
self._new_notebook_page(self.notebook1, self.scrolled_window2, '_Info')
info_text_view = self.info_text_view
self.window.show_all()
def _new_notebook_page(self, notebook, widget, label):
l = Gtk.Label(label='')
l.set_text_with_mnemonic(label)
notebook.append_page(widget, l)
def __create_text(self, is_source=False):
scrolled_window = Gtk.ScrolledWindow()
scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
scrolled_window.set_shadow_type(Gtk.ShadowType.IN)
text_view = Gtk.TextView()
scrolled_window.add(text_view)
buffer = Gtk.TextBuffer()
text_view.set_buffer(buffer)
text_view.set_editable(False)
text_view.set_cursor_visible(True)
return scrolled_window, buffer, text_view
def __create_treeview(self, tab_name):
treestore = Gtk.TreeStore(str, str)
self.tree_store[tab_name] = treestore
treeview = Gtk.TreeView(treestore)
selection = treeview.get_selection()
selection.set_mode(Gtk.SelectionMode.BROWSE)
treeview.set_size_request(200, -1)
cell = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(tab_name, cell, foreground=self.COL_COLOR)
column.add_attribute(cell, "text", self.COL_NAME)
treeview.append_column(column)
if tab_name == "A":
selection.connect('changed', self.selection_changed_A)
elif tab_name == "B":
selection.connect('changed', self.selection_changed_B)
treeview.expand_all()
return treeview
def selection_changed_A(self):
print "A"
def selection_changed_B(self):
print "B"
class ANALYSING_PROCESS(multiprocessing.Process):
def __init__(self):
super(ANALYSING_PROCESS, self).__init__()
def run(self):
import time
time.sleep(5)
ANA()
class ANALYSING_THREAD(threading.Thread):
def __init__(self, pbar, timer):
super(ANALYSING_THREAD, self).__init__()
self.pbar = pbar
self.timer = timer
def run(self):
Gdk.threads_init()
Gdk.threads_enter()
import time
time.sleep(5)
ANA()
Gdk.threads_leave()
self.pbar.set_text("Done")
self.pbar.set_show_text(True)
GObject.source_remove(self.timer)
class File_Chooser(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
self.connect('destroy', lambda *w: Gtk.main_quit())
self.set_title("Test")
self.set_border_width(8)
frame = Gtk.Frame()
self.add(frame)
self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
self.vbox.set_border_width(8)
frame.add(self.vbox)
label = Gtk.Label()
label.set_markup("<span font_desc=\"Serif 25\" foreground=\"#015F85\" size=\"x-large\"> Test</span>")
self.vbox.pack_start(label, False, False, 0)
self.button_entry = dict()
self.entry_name = dict()
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
button = Gtk.Button("Browse Files")
button.connect('clicked', self.browse_files)
hbox.pack_start(button, False, False, 0)
label = Gtk.Label()
label.set_markup("<span foreground=\"#015F85\"> files </span>")
label.set_use_underline(True)
hbox.pack_start(label, False, False, 0)
self.entry1 = Gtk.Entry()
hbox.pack_start(self.entry1, True, True, 0)
label.set_mnemonic_widget(self.entry1)
self.button_entry[button] = self.entry1
self.entry_name['files'] = self.entry1
self.vbox.pack_start(hbox, False, False, 0)
separator = Gtk.HSeparator()
self.vbox.pack_start(separator, False, False, 1)
alignment1 = Gtk.Alignment()
alignment1.set_halign(Gtk.Align.CENTER)
self.vbox.pack_start(alignment1, False, False, 1)
self.pbar = Gtk.ProgressBar()
alignment1.add(self.pbar)
self.pbar.set_text("Not Run")
self.pbar.set_show_text(True)
hbox2 = Gtk.HBox(False, 2)
self.vbox.pack_start(hbox2, False, False, 1)
button3 = Gtk.Button("Analyze")
button3.connect('clicked', self.tar_File_analyze)
hbox2.pack_end(button3, False, False, 1)
button4 = Gtk.Button("close")
button4.connect('clicked', self.__quit)
hbox2.pack_end(button4, False, False, 1)
self.show_all()
def browse_files(self, button):
dialog = Gtk.FileChooserDialog("Please choose a file", self,
Gtk.FileChooserAction.OPEN,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
filter = Gtk.FileFilter()
filter.set_name("Tar files")
filter.add_pattern("*.tar")
filter.add_pattern("*.rar")
filter.add_pattern("*.tar.gz")
dialog.add_filter(filter)
filter = Gtk.FileFilter()
filter.set_name("All files")
filter.add_pattern("*")
dialog.add_filter(filter)
filter = Gtk.FileFilter()
filter.set_name("Images")
filter.add_mime_type("image/png")
filter.add_mime_type("image/jpeg")
filter.add_mime_type("image/gif")
filter.add_pattern("*.png")
filter.add_pattern("*.jpg")
filter.add_pattern("*.gif")
filter.add_pattern("*.tif")
filter.add_pattern("*.xpm")
dialog.add_filter(filter)
response = dialog.run()
if response == Gtk.ResponseType.OK:
print dialog.get_filename(), 'selected'
self.button_entry[button].set_text(dialog.get_filename())
#entry.set_text(dialog.get_filename())
elif response == Gtk.ResponseType.CANCEL:
print 'Closed, no files selected'
dialog.destroy()
def tar_File_analyze(self, button):
#PROGRESS_THREAD(self.pbar).start()
self.pbar.set_text("Running")
self.pbar.set_show_text(True)
####self.timer = GObject.timeout_add (100, self.progress_timeout, self)
self.timer = GLib.timeout_add(100, self.progress_timeout)
t = ANALYSING_THREAD(self.pbar, self.timer)
#t = ANALYSING_PROCESS()
t.start()
def progress_timeout(self):
self.pbar.pulse()
return True
def warning(self, warnings):
dialog = Gtk.MessageDialog(self, 0, Gtk.MessageType.WARNING,
Gtk.ButtonsType.OK, "Warning!")
dialog.format_secondary_text(warnings)
response = dialog.run()
if response == Gtk.ResponseType.OK:
dialog.destroy()
def __quit(self, button):
self.destroy()
def run(self):
Gtk.main()
def main():
File_Chooser().run()
if __name__ == '__main__':
main()
Could anyone take a look at it ?
In general, GUI toolkits do not support multiple threads making toolkit calls. Usually only the initial thread is allowed to do that. Making a GUI toolkit really thread-safe is a hard problem and this is a way to avoid it.
A possible solution is to have the main thread use an idle function or timer callback to check the progress in the worker thread.
Another solution is to to the work in an external process instead of a thread. This has a number of advantages;
It makes communication explicit and tracable. GUI toolkits can generally watch e.g. a file descriptor or a socket, processing the info from it in a timeout or idle callback.
Keeping the GUI single-threaded makes locking mostly unnecessary.
And on CPython it can actually make use of multi-core machines by side-stepping the GIL.
Imagine I want to make a simple program with GUI like this.
this is my code:
from gi.repository import Gtk
import gobject
class gui():
def __init__(self):
self.window = Gtk.Window()
self.window.connect('delete-event', Gtk.main_quit)
self.box = Gtk.Box()
self.window.add(self.box)
self.progressbar = Gtk.ProgressBar()
self.box.pack_start(self.progressbar, True, True, 0)
self.button = Gtk.Button(label='Start')
self.button.connect('clicked', self.on_button_clicked)
self.box.pack_start(self.button, True, True, 0)
self.window.show_all()
Gtk.main()
def on_button_clicked(self, widget):
self.task = self.iters(100000)
gobject.idle_add(self.task.next)
def iters(self, j):
i = j
while i > 0 :
i -= 1
print i
self.progressbar.set_fraction((j - float(i)) / j)
yield True
yield False
if __name__ == '__main__':
gui = gui()
I know how to make progressbar work if I put everything in one script like that.
But, what if I want to separate gui part from logic part? So, it would be like this:
GUI.py
from gi.repository import Gtk
class gui():
def __init__(self):
self.window = Gtk.Window()
self.window.connect('delete-event', Gtk.main_quit)
self.box = Gtk.Box()
self.window.add(self.box)
self.progressbar = Gtk.ProgressBar()
self.box.pack_start(self.progressbar, True, True, 0)
self.button = Gtk.Button(label='Start')
self.button.connect('clicked', self.on_button_clicked)
self.box.pack_start(self.button, True, True, 0)
self.window.show_all()
Gtk.main()
def on_button_clicked(self, widget):
//how to implement it
if __name__ == '__main__':
gui = gui()
Iteration.py
class Iteration():
def iters(self, j):
i = j
while i > 0 :
i -= 1
print i
EDIT:
One more thing, the first code above works, but why do I get this message:
/usr/lib/python2.7/dist-packages/gobject/constants.py:24: Warning: g_boxed_type_register_static: assertion `g_type_from_name (name) == 0' failed
import gobject._gobject
EDIT:
Okay, imagine I have one module like this one:
Works.py
def getImageFromInternet(uri):
#some code to get image from internet
this getImageFromInternet function is not iterable. What I want to do is processing this function in the background while updating the progressbar and doing other thing with gui. Do I need a thread for this?
Create a generator that updates the GUI and drives the underlying data model generator, and pass that to idle_add. For example (untested):
def on_button_clicked(self, widget):
cnt = 10000
task = self.iters(cnt)
def gui_iter():
for i, ignore in enumerate(task, 1):
self.progressbar.set_fraction(i / cnt)
yield True
yield False
gobject.idle_add(gui_iter().next)
I am developing an app in python with pyGtk+Glade.
I want to execute a function every N seconds (just like in javascript the function setTimeout()). If the user perform a click, some action must be done, and the timer must be reset.
I was trying with threading.Timer, something like this:
def callback():
print "callback executed"
t = threading.Timer(10.0, callback)
t.start()
but it doesn't work for me because it blocks everything for the N secs and doesn't capture the user click.
Any other option?
Since you're using PyGTK, your program should probably be using the g_main_loop, in which case you can call glib.timeout_add (interval, callback) to add a callback that gets called (roughly) every X seconds.
Here's an example:
import glib
import gtk
def yo ():
print "yo"
return True
glib.timeout_add (1000, yo)
gtk.main ()
Unfortunately, to reset the timeout I couldn't come up with an elegant solution. But you can create your own main loop so you have control over when the timeout resets, sorta like this:
import glib
import gtk
import time
timeout = 1;
timer = time.time() + timeout
while (True):
gtk.main_iteration (False)
if (timer <= time.time()):
print "Time up!"
timer = time.time() + timeout
This creates a timer which calls MainWin.update() every second. When the button is pressed, the current timer is killed and a new timer is started.
import pygtk
pygtk.require('2.0')
import gtk
import gobject
import time
class MainWin(object):
def destroy(self, widget, data=None):
gtk.main_quit()
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.connect("destroy", self.destroy)
self.window.set_border_width(10)
self.button = gtk.Button("Reset")
self.button.connect("clicked", self.onclick)
self.window.add(self.button)
self.button.show()
self.window.show()
self.reset_timer = False
self.count = 0
self.duration = 1000
self.timer = gobject.timeout_add(self.duration, self.update)
def reset(self):
print('Resetting timer')
gobject.source_remove(self.timer)
# start a new period call to update
self.count = 0
self.timer = gobject.timeout_add(self.duration, self.update)
def onclick(self, widget):
# Every action which resets the timer should call self.reset_timer().
self.reset()
def update(self):
print('{t:.1f}: count = {i}'.format(t=time.time() % 100, i=self.count))
self.count += 1
return True
def main(self):
gtk.main()
if __name__=='__main__':
MainWin().main()