Skip to content

Instantly share code, notes, and snippets.

@EBNull
Created February 16, 2012 19:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save EBNull/1847075 to your computer and use it in GitHub Desktop.
Save EBNull/1847075 to your computer and use it in GitHub Desktop.
Basis for a general PyQT app
import os
import sys
import ctypes
import traceback
import logging
log = logging.getLogger()
from QtBinding import QtCore, QtGui
def remove_window_context_help_button(qwindow):
#http://stackoverflow.com/questions/81627/how-can-i-hide-delete-the-help-button-on-the-title-bar-of-a-qt-dialog
icon = qwindow.windowIcon()
flags = qwindow.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint
qwindow.setWindowFlags(flags)
qwindow.setWindowIcon(icon)
class BasicQApplication(QtGui.QApplication):
remove_input_hook = True
def __init__(self, argv, install_excepthook=True):
if len(argv) == 0:
argv = ['']
super(BasicQApplication, self).__init__(argv)
self.setWindowIcon(self._loadIcon())
if install_excepthook:
sys.excepthook = self.on_unhandled_exception
def get_toplevel_window(self, allow_hidden=True):
top = self.activeWindow()
if not top:
others = []
for w in self.topLevelWidgets():
if not w.isHidden():
top = w
break
if allow_hidden:
others.append(w)
else:
if others:
top = others[0]
else:
top = None
return top
@QtCore.pyqtSlot(unicode, unicode, unicode, int)
def _main_thread_msgbox(self, title, text, details, icon):
top = self.get_toplevel_window()
mb = QtGui.QMessageBox(icon, title, text, QtGui.QMessageBox.Ok, top)
if details:
mb.setDetailedText(details)
mb.exec_()
@QtCore.pyqtSlot(unicode, unicode, unicode, int)
def _main_thread_msgbox_and_exit(self, title, text, details, icon):
top = self.get_toplevel_window()
mb = QtGui.QMessageBox(icon, title, text, QtGui.QMessageBox.Ok, top)
if details:
mb.setDetailedText(details)
mb.exec_()
self._main_thread_exit(1)
@QtCore.pyqtSlot(int)
def _main_thread_exit(self, exitcode):
self.exit(exitcode)
#Threadsafe
def on_unhandled_exception(self, type, value, tb):
msg = u"Unhandled Exception %s: %s"%(type.__name__, unicode(value))
log.error(msg, exc_info=(type, value, tb))
tbinfo = ''.join(traceback.format_exception(type, value, tb))
sys.stderr.write(tbinfo + '\n')
QtCore.QMetaObject.invokeMethod(self, "_main_thread_msgbox", QtCore.Qt.AutoConnection,
QtCore.Q_ARG("QString", u"Unhandled Exception"),
QtCore.Q_ARG("QString", u"An error has occured:\n\n%s: %s"%(type.__name__, unicode(value))),
QtCore.Q_ARG("QString", tbinfo),
QtCore.Q_ARG("int", QtGui.QMessageBox.Critical),
)
#Threadsafe
def queue_exit(self, exitcode):
QtCore.QMetaObject.invokeMethod(self, "_main_thread_exit", QtCore.Qt.AutoConnection,
QtCore.Q_ARG("int", exitcode),
)
def _loadIcon(self):
#TODO: Make this not depend on win32gui
import win32gui
icons = win32gui.ExtractIconEx(sys.executable, 0, 1)
# should call win32gui.DestroyIcon on all the icons returned by ExtractIconEx
info = win32gui.GetIconInfo(icons[0][0])
pixmap = QtGui.QPixmap.fromWinHBITMAP(int(info[4]), 2)
icon = QtGui.QIcon(pixmap)
info[3].close()
info[4].close()
return icon
def exec_(self):
super(BasicQApplication, self).exec_()
if self.remove_input_hook:
QtCore.pyqtRemoveInputHook()
class DemoApp(BasicQApplication):
def exit_me(self):
print "Bye!"
self.queue_exit(0)
def exec_(self):
print "DemoApp.exec()"
QtCore.QTimer.singleShot(2000, self.exit_me)
super(DemoApp, self).exec_()
print "DemoApp.exec() complete"
if __name__ == '__main__':
app = DemoApp(sys.argv)
sys.exit(app.exec_())
@EBNull
Copy link
Author

EBNull commented Jul 20, 2012

This is a simple BasicQApplication class for Qt that does a number of handy things:

  1. Installs a sys.excepthook that will pop up an error dialog parented to the current window
  2. This excepthook works even from separate threads
  3. Removes pyqt input hook so you can use pdb (from any thread)
  4. Basic cross thread communicaton for showing errors

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment