class QSignalingMainWindow(QtGui.QMainWindow): closed = qt_compat.Signal() hidden = qt_compat.Signal() shown = qt_compat.Signal() resized = qt_compat.Signal() def __init__(self, *args, **kwd): QtGui.QMainWindow.__init__(*((self, )+args), **kwd) def closeEvent(self, event): val = QtGui.QMainWindow.closeEvent(self, event) self.closed.emit() return val def hideEvent(self, event): val = QtGui.QMainWindow.hideEvent(self, event) self.hidden.emit() return val def showEvent(self, event): val = QtGui.QMainWindow.showEvent(self, event) self.shown.emit() return val def resizeEvent(self, event): val = QtGui.QMainWindow.resizeEvent(self, event) self.resized.emit() return val
class _WorkerThread(QtCore.QObject): _taskComplete = qt_compat.Signal(object) def __init__(self, futureThread): QtCore.QObject.__init__(self) self._futureThread = futureThread self._futureThread._addTask.connect(self._on_task_added) self._taskComplete.connect(self._futureThread._on_task_complete) @qt_compat.Slot(object) @misc.log_exception(_moduleLogger) def _on_task_added(self, task): self.__on_task_added(task) @misc.log_exception(_moduleLogger) def __on_task_added(self, task): if not self._futureThread._isRunning: _moduleLogger.error("Dropping task") func, args, kwds, on_success, on_error = task try: result = func(*args, **kwds) isError = False except Exception, e: _moduleLogger.error("Error, passing it back to the main thread") result = e isError = True taskResult = on_success, on_error, isError, result self._taskComplete.emit(taskResult)
class QErrorMessage(QtCore.QObject): LEVEL_ERROR = logging.ERROR LEVEL_WARNING = logging.WARNING LEVEL_INFO = logging.INFO LEVEL_BUSY = -1 def __init__(self, message, level): QtCore.QObject.__init__(self) self._message = message self._level = level changed = qt_compat.Signal() level = qt_compat.Property(int, lambda self: self._level, notify=changed) message = qt_compat.Property(unicode, lambda self: self._message, notify=changed) def __repr__(self): return "%s.%s(%r, %r)" % (__name__, self.__class__.__name__, self._message, self._level)
class FutureThread(QtCore.QObject): _addTask = qt_compat.Signal(object) def __init__(self): QtCore.QObject.__init__(self) self._thread = QThread44() self._isRunning = False self._worker = _WorkerThread(self) self._worker.moveToThread(self._thread) def start(self): self._thread.start() self._isRunning = True def stop(self): self._isRunning = False self._thread.quit() self._thread.wait() def add_task(self, func, args, kwds, on_success, on_error): assert self._isRunning, "Task queue not started" task = func, args, kwds, on_success, on_error self._addTask.emit(task) @qt_compat.Slot(object) @misc.log_exception(_moduleLogger) def _on_task_complete(self, taskResult): self.__on_task_complete(taskResult) @misc.log_exception(_moduleLogger) def __on_task_complete(self, taskResult): on_success, on_error, isError, result = taskResult if not self._isRunning: if isError: _moduleLogger.error("Masking: %s" % (result, )) isError = True result = StopIteration("Cancelling all callbacks") callback = on_success if not isError else on_error try: callback(result) except Exception: _moduleLogger.exception("Callback errored")
class AutoQObject(QtCore.QObject): def __init__(self, **initKwargs): QtCore.QObject.__init__(self) def __repr__(self): return '<%s (wrapping %r)>' % ( kwargs.get('name', self.__class__.__name__), obj, ) changed = qt_compat.Signal() for key, value in members: qTypeName = obj_to_qtype(value) def _get(key): def _get(self): return getattr(obj, key) return _get def _set(key): if key == key.upper(): def _set_constant(self, v): raise NotImplementedError() return _set_constant else: def _set_mutable(self, v): setattr(obj, key, v) getattr(self, "changed").emit() return _set_mutable setter = locals()['_set_'+key] = _set(key) getter = locals()['_get_'+key] = _get(key) locals()[key] = qt_compat.Property(qTypeName, getter, setter, notify=changed) del _get, _set, getter, setter, qTypeName
class AutoQObject(QtCore.QObject): def __init__(self, **initKwargs): QtCore.QObject.__init__(self) for key, val in classDef: setattr(self, '_'+key, initKwargs.get(key, val())) def __repr__(self): qTypeNames = ( '%s=%r' % (key, getattr(self, '_'+key)) for key, qTypeName in classDef ) return '<%s (%s)>' % ( kwargs.get('name', self.__class__.__name__), ', '.join(qTypeNames), ) for key, qTypeName in classDef: nfy = locals()['_nfy_'+key] = qt_compat.Signal() def _get(key): def f(self): return self.__dict__['_'+key] return f def _set(key): def f(self, qTypeName): setattr(self, '_'+key, qTypeName) getattr(self, '_nfy_'+key).emit() return f setter = locals()['_set_'+key] = _set(key) getter = locals()['_get_'+key] = _get(key) locals()[key] = qt_compat.Property(qTypeName, getter, setter, notify=nfy) del nfy, _get, _set, getter, setter
class Buffer(QtCore.QObject): """A WeeChat buffer.""" bufferInput = qt_compat.Signal(str, str) def __init__(self, data={}): QtCore.QObject.__init__(self) self.data = data self.nicklist = {} self.widget = BufferWidget( display_nicklist=self.data.get('nicklist', 0)) self.update_title() self.update_prompt() self.widget.input.textSent.connect(self.input_text_sent) def pointer(self): """Return pointer on buffer.""" return self.data.get('__path', [''])[0] def update_title(self): """Update title.""" try: self.widget.set_title( color.remove(self.data['title'].decode('utf-8'))) except: # noqa: E722 self.widget.set_title(None) def update_prompt(self): """Update prompt.""" try: self.widget.set_prompt(self.data['local_variables']['nick']) except: # noqa: E722 self.widget.set_prompt(None) def input_text_sent(self, text): """Called when text has to be sent to buffer.""" if self.data: self.bufferInput.emit(self.data['full_name'], text) def nicklist_add_item(self, parent, group, prefix, name, visible): """Add a group/nick in nicklist.""" if group: self.nicklist[name] = {'visible': visible, 'nicks': []} else: self.nicklist[parent]['nicks'].append({ 'prefix': prefix, 'name': name, 'visible': visible, }) def nicklist_remove_item(self, parent, group, name): """Remove a group/nick from nicklist.""" if group: if name in self.nicklist: del self.nicklist[name] else: if parent in self.nicklist: self.nicklist[parent]['nicks'] = [ nick for nick in self.nicklist[parent]['nicks'] if nick['name'] != name ] def nicklist_update_item(self, parent, group, prefix, name, visible): """Update a group/nick in nicklist.""" if group: if name in self.nicklist: self.nicklist[name]['visible'] = visible else: if parent in self.nicklist: for nick in self.nicklist[parent]['nicks']: if nick['name'] == name: nick['prefix'] = prefix nick['visible'] = visible break def nicklist_refresh(self): """Refresh nicklist.""" self.widget.nicklist.clear() for group in sorted(self.nicklist): for nick in sorted(self.nicklist[group]['nicks'], key=lambda n: n['name']): prefix_color = { '': '', ' ': '', '+': 'yellow', } color = prefix_color.get(nick['prefix'], 'green') if color: icon = QtGui.QIcon( resource_filename( __name__, 'data/icons/bullet_%s_8x8.png' % color)) else: pixmap = QtGui.QPixmap(8, 8) pixmap.fill() icon = QtGui.QIcon(pixmap) item = QtGui.QListWidgetItem(icon, nick['name']) self.widget.nicklist.addItem(item) self.widget.nicklist.setVisible(True)
class InputLineEdit(QtGui.QLineEdit): """Input line.""" bufferSwitchPrev = qt_compat.Signal() bufferSwitchNext = qt_compat.Signal() textSent = qt_compat.Signal(str) def __init__(self, scroll_widget): QtGui.QLineEdit.__init__(self) self.scroll_widget = scroll_widget self._history = [] self._history_index = -1 self.returnPressed.connect(self._input_return_pressed) def keyPressEvent(self, event): key = event.key() modifiers = event.modifiers() bar = self.scroll_widget.verticalScrollBar() if modifiers == QtCore.Qt.ControlModifier: if key == QtCore.Qt.Key_PageUp: self.bufferSwitchPrev.emit() elif key == QtCore.Qt.Key_PageDown: self.bufferSwitchNext.emit() else: QtGui.QLineEdit.keyPressEvent(self, event) elif modifiers == QtCore.Qt.AltModifier: if key in (QtCore.Qt.Key_Left, QtCore.Qt.Key_Up): self.bufferSwitchPrev.emit() elif key in (QtCore.Qt.Key_Right, QtCore.Qt.Key_Down): self.bufferSwitchNext.emit() elif key == QtCore.Qt.Key_PageUp: bar.setValue(bar.value() - (bar.pageStep() / 10)) elif key == QtCore.Qt.Key_PageDown: bar.setValue(bar.value() + (bar.pageStep() / 10)) elif key == QtCore.Qt.Key_Home: bar.setValue(bar.minimum()) elif key == QtCore.Qt.Key_End: bar.setValue(bar.maximum()) else: QtGui.QLineEdit.keyPressEvent(self, event) elif key == QtCore.Qt.Key_PageUp: bar.setValue(bar.value() - bar.pageStep()) elif key == QtCore.Qt.Key_PageDown: bar.setValue(bar.value() + bar.pageStep()) elif key == QtCore.Qt.Key_Up: self._history_navigate(-1) elif key == QtCore.Qt.Key_Down: self._history_navigate(1) else: QtGui.QLineEdit.keyPressEvent(self, event) def _input_return_pressed(self): self._history.append(self.text().encode('utf-8')) self._history_index = len(self._history) self.textSent.emit(self.text()) self.clear() def _history_navigate(self, direction): if self._history: self._history_index += direction if self._history_index < 0: self._history_index = 0 return if self._history_index > len(self._history) - 1: self._history_index = len(self._history) self.clear() return self.setText(self._history[self._history_index])
class Network(QtCore.QObject): """I/O with WeeChat/relay.""" statusChanged = qt_compat.Signal(str, str) messageFromWeechat = qt_compat.Signal(QtCore.QByteArray) def __init__(self, *args): QtCore.QObject.__init__(*(self, ) + args) self.status_disconnected = 'disconnected' self.status_connecting = 'connecting...' self.status_connected = 'connected' self._server = None self._port = None self._ssl = None self._password = None self._lines = config.CONFIG_DEFAULT_RELAY_LINES self._buffer = QtCore.QByteArray() self._socket = QtNetwork.QSslSocket() self._socket.connected.connect(self._socket_connected) self._socket.error.connect(self._socket_error) self._socket.readyRead.connect(self._socket_read) self._socket.disconnected.connect(self._socket_disconnected) def _socket_connected(self): """Slot: socket connected.""" self.statusChanged.emit(self.status_connected, None) if self._password: self.send_to_weechat( '\n'.join(_PROTO_INIT_CMD + _PROTO_SYNC_CMDS) % { 'password': str(self._password), 'lines': self._lines }) def _socket_error(self, error): """Slot: socket error.""" self.statusChanged.emit( self.status_disconnected, 'Failed, error: %s' % self._socket.errorString()) def _socket_read(self): """Slot: data available on socket.""" data = self._socket.readAll() self._buffer.append(data) while len(self._buffer) >= 4: remainder = None length = struct.unpack('>i', self._buffer[0:4])[0] if len(self._buffer) < length: # partial message, just wait for end of message break # more than one message? if length < len(self._buffer): # save beginning of another message remainder = self._buffer[length:] self._buffer = self._buffer[0:length] self.messageFromWeechat.emit(self._buffer) if not self.is_connected(): return self._buffer.clear() if remainder: self._buffer.append(remainder) def _socket_disconnected(self): """Slot: socket disconnected.""" self._server = None self._port = None self._ssl = None self._password = None self.statusChanged.emit(self.status_disconnected, None) def is_connected(self): """Return True if the socket is connected, False otherwise.""" return self._socket.state() == QtNetwork.QAbstractSocket.ConnectedState def is_ssl(self): """Return True if SSL is used, False otherwise.""" return self._ssl def connect_weechat(self, server, port, ssl, password, lines): """Connect to WeeChat.""" self._server = server try: self._port = int(port) except ValueError: self._port = 0 self._ssl = ssl self._password = password try: self._lines = int(lines) except ValueError: self._lines = config.CONFIG_DEFAULT_RELAY_LINES if self._socket.state() == QtNetwork.QAbstractSocket.ConnectedState: return if self._socket.state() != QtNetwork.QAbstractSocket.UnconnectedState: self._socket.abort() self._socket.connectToHost(self._server, self._port) if self._ssl: self._socket.ignoreSslErrors() self._socket.startClientEncryption() self.statusChanged.emit(self.status_connecting, None) def disconnect_weechat(self): """Disconnect from WeeChat.""" if self._socket.state() == QtNetwork.QAbstractSocket.UnconnectedState: return if self._socket.state() == QtNetwork.QAbstractSocket.ConnectedState: self.send_to_weechat('quit\n') self._socket.waitForBytesWritten(1000) else: self.statusChanged.emit(self.status_disconnected, None) self._socket.abort() def send_to_weechat(self, message): """Send a message to WeeChat.""" self._socket.write(message.encode('utf-8')) def desync_weechat(self): """Desynchronize from WeeChat.""" self.send_to_weechat('desync\n') def sync_weechat(self): """Synchronize with WeeChat.""" self.send_to_weechat('\n'.join(_PROTO_SYNC_CMDS)) def status_icon(self, status): """Return the name of icon for a given status.""" icon = { self.status_disconnected: 'dialog-close.png', self.status_connecting: 'dialog-close.png', self.status_connected: 'dialog-ok-apply.png', } return icon.get(status, '') def get_options(self): """Get connection options.""" return { 'server': self._server, 'port': self._port, 'ssl': 'on' if self._ssl else 'off', 'password': self._password, 'lines': str(self._lines), }
class QPieMenu(QtGui.QWidget): activated = qt_compat.Signal(int) highlighted = qt_compat.Signal(int) canceled = qt_compat.Signal() aboutToShow = qt_compat.Signal() aboutToHide = qt_compat.Signal() def __init__(self, parent=None): QtGui.QWidget.__init__(self, parent) self._cachedCenterPosition = self.rect().center() self._filing = PieFiling() self._artist = PieArtist(self._filing) self._selectionIndex = PieFiling.SELECTION_NONE self._mousePosition = () self.setFocusPolicy(QtCore.Qt.StrongFocus) def popup(self, pos): self._update_selection(pos) self.show() def insertItem(self, item, index=-1): self._filing.insertItem(item, index) self.update() def removeItemAt(self, index): self._filing.removeItemAt(index) self.update() def set_center(self, item): self._filing.set_center(item) self.update() def clear(self): self._filing.clear() self.update() def itemAt(self, index): return self._filing.itemAt(index) def indexAt(self, point): return self._filing.indexAt(self._cachedCenterPosition, point) def innerRadius(self): return self._filing.innerRadius() def setInnerRadius(self, radius): self._filing.setInnerRadius(radius) self.update() def outerRadius(self): return self._filing.outerRadius() def setOuterRadius(self, radius): self._filing.setOuterRadius(radius) self.update() def sizeHint(self): return self._artist.pieSize() @misc_utils.log_exception(_moduleLogger) def mousePressEvent(self, mouseEvent): lastSelection = self._selectionIndex lastMousePos = mouseEvent.pos() self._update_selection(lastMousePos) self._mousePosition = lastMousePos if lastSelection != self._selectionIndex: self.highlighted.emit(self._selectionIndex) self.update() @misc_utils.log_exception(_moduleLogger) def mouseMoveEvent(self, mouseEvent): lastSelection = self._selectionIndex lastMousePos = mouseEvent.pos() self._update_selection(lastMousePos) if lastSelection != self._selectionIndex: self.highlighted.emit(self._selectionIndex) self.update() @misc_utils.log_exception(_moduleLogger) def mouseReleaseEvent(self, mouseEvent): lastSelection = self._selectionIndex lastMousePos = mouseEvent.pos() self._update_selection(lastMousePos) self._mousePosition = () self._activate_at(self._selectionIndex) self.update() @misc_utils.log_exception(_moduleLogger) def keyPressEvent(self, keyEvent): if keyEvent.key() in [ QtCore.Qt.Key_Right, QtCore.Qt.Key_Down, QtCore.Qt.Key_Tab ]: if self._selectionIndex != len(self._filing) - 1: nextSelection = self._selectionIndex + 1 else: nextSelection = 0 self._select_at(nextSelection) self.update() elif keyEvent.key() in [ QtCore.Qt.Key_Left, QtCore.Qt.Key_Up, QtCore.Qt.Key_Backtab ]: if 0 < self._selectionIndex: nextSelection = self._selectionIndex - 1 else: nextSelection = len(self._filing) - 1 self._select_at(nextSelection) self.update() elif keyEvent.key() in [ QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter, QtCore.Qt.Key_Space ]: self._activate_at(self._selectionIndex) elif keyEvent.key() in [QtCore.Qt.Key_Escape, QtCore.Qt.Key_Backspace]: self._activate_at(PieFiling.SELECTION_NONE) else: QtGui.QWidget.keyPressEvent(self, keyEvent) @misc_utils.log_exception(_moduleLogger) def showEvent(self, showEvent): self.aboutToShow.emit() self._cachedCenterPosition = self.rect().center() mask = self._artist.show(self.palette()) self.setMask(mask) lastMousePos = self.mapFromGlobal(QtGui.QCursor.pos()) self._update_selection(lastMousePos) QtGui.QWidget.showEvent(self, showEvent) @misc_utils.log_exception(_moduleLogger) def hideEvent(self, hideEvent): self._artist.hide() self._selectionIndex = PieFiling.SELECTION_NONE QtGui.QWidget.hideEvent(self, hideEvent) @misc_utils.log_exception(_moduleLogger) def paintEvent(self, paintEvent): canvas = self._artist.paint(self._selectionIndex) screen = QtGui.QPainter(self) screen.drawPixmap(QtCore.QPoint(0, 0), canvas) QtGui.QWidget.paintEvent(self, paintEvent) def __iter__(self): return iter(self._filing) def __len__(self): return len(self._filing) def _select_at(self, index): self._selectionIndex = index def _update_selection(self, lastMousePos): radius = _radius_at(self._cachedCenterPosition, lastMousePos) if radius < self._filing.innerRadius(): self._selectionIndex = PieFiling.SELECTION_CENTER elif radius <= self._filing.outerRadius(): self._select_at(self.indexAt(lastMousePos)) else: self._selectionIndex = PieFiling.SELECTION_NONE def _activate_at(self, index): if index == PieFiling.SELECTION_NONE: self.canceled.emit() self.aboutToHide.emit() self.hide() return elif index == PieFiling.SELECTION_CENTER: child = self._filing.center() else: child = self.itemAt(index) if child.isEnabled(): child.action().trigger() self.activated.emit(index) else: self.canceled.emit() self.aboutToHide.emit() self.hide()
class QPieButton(QtGui.QWidget): activated = qt_compat.Signal(int) highlighted = qt_compat.Signal(int) canceled = qt_compat.Signal() aboutToShow = qt_compat.Signal() aboutToHide = qt_compat.Signal() BUTTON_RADIUS = 24 DELAY = 250 def __init__(self, buttonSlice, parent=None, buttonSlices=None): # @bug Artifacts on Maemo 5 due to window 3D effects, find way to disable them for just these? # @bug The pie's are being pushed back on screen on Maemo, leading to coordinate issues QtGui.QWidget.__init__(self, parent) self._cachedCenterPosition = self.rect().center() self._filing = PieFiling() self._display = QPieDisplay(self._filing, None, QtCore.Qt.SplashScreen) self._selectionIndex = PieFiling.SELECTION_NONE self._buttonFiling = PieFiling() self._buttonFiling.set_center(buttonSlice) if buttonSlices is not None: for slice in buttonSlices: self._buttonFiling.insertItem(slice) self._buttonFiling.setOuterRadius(self.BUTTON_RADIUS) self._buttonArtist = PieArtist(self._buttonFiling, PieArtist.BACKGROUND_NOFILL) self._poppedUp = False self._pressed = False self._delayPopupTimer = QtCore.QTimer() self._delayPopupTimer.setInterval(self.DELAY) self._delayPopupTimer.setSingleShot(True) self._delayPopupTimer.timeout.connect(self._on_delayed_popup) self._popupLocation = None self._mousePosition = None self.setFocusPolicy(QtCore.Qt.StrongFocus) self.setSizePolicy( QtGui.QSizePolicy( QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.MinimumExpanding, )) def insertItem(self, item, index=-1): self._filing.insertItem(item, index) def removeItemAt(self, index): self._filing.removeItemAt(index) def set_center(self, item): self._filing.set_center(item) def set_button(self, item): self.update() def clear(self): self._filing.clear() def itemAt(self, index): return self._filing.itemAt(index) def indexAt(self, point): return self._filing.indexAt(self._cachedCenterPosition, point) def innerRadius(self): return self._filing.innerRadius() def setInnerRadius(self, radius): self._filing.setInnerRadius(radius) def outerRadius(self): return self._filing.outerRadius() def setOuterRadius(self, radius): self._filing.setOuterRadius(radius) def buttonRadius(self): return self._buttonFiling.outerRadius() def setButtonRadius(self, radius): self._buttonFiling.setOuterRadius(radius) self._buttonFiling.setInnerRadius(radius / 2) self._buttonArtist.show(self.palette()) def minimumSizeHint(self): return self._buttonArtist.centerSize() @misc_utils.log_exception(_moduleLogger) def mousePressEvent(self, mouseEvent): lastSelection = self._selectionIndex lastMousePos = mouseEvent.pos() self._mousePosition = lastMousePos self._update_selection(self._cachedCenterPosition) self.highlighted.emit(self._selectionIndex) self._display.selectAt(self._selectionIndex) self._pressed = True self.update() self._popupLocation = mouseEvent.globalPos() self._delayPopupTimer.start() @misc_utils.log_exception(_moduleLogger) def _on_delayed_popup(self): assert self._popupLocation is not None, "Widget location abuse" self._popup_child(self._popupLocation) @misc_utils.log_exception(_moduleLogger) def mouseMoveEvent(self, mouseEvent): lastSelection = self._selectionIndex lastMousePos = mouseEvent.pos() if self._mousePosition is None: # Absolute self._update_selection(lastMousePos) else: # Relative self._update_selection( self._cachedCenterPosition + (lastMousePos - self._mousePosition), ignoreOuter=True, ) if lastSelection != self._selectionIndex: self.highlighted.emit(self._selectionIndex) self._display.selectAt(self._selectionIndex) if self._selectionIndex != PieFiling.SELECTION_CENTER and self._delayPopupTimer.isActive( ): self._on_delayed_popup() @misc_utils.log_exception(_moduleLogger) def mouseReleaseEvent(self, mouseEvent): self._delayPopupTimer.stop() self._popupLocation = None lastSelection = self._selectionIndex lastMousePos = mouseEvent.pos() if self._mousePosition is None: # Absolute self._update_selection(lastMousePos) else: # Relative self._update_selection( self._cachedCenterPosition + (lastMousePos - self._mousePosition), ignoreOuter=True, ) self._mousePosition = None self._activate_at(self._selectionIndex) self._pressed = False self.update() self._hide_child() @misc_utils.log_exception(_moduleLogger) def keyPressEvent(self, keyEvent): if keyEvent.key() in [ QtCore.Qt.Key_Right, QtCore.Qt.Key_Down, QtCore.Qt.Key_Tab ]: self._popup_child(QtGui.QCursor.pos()) if self._selectionIndex != len(self._filing) - 1: nextSelection = self._selectionIndex + 1 else: nextSelection = 0 self._select_at(nextSelection) self._display.selectAt(self._selectionIndex) elif keyEvent.key() in [ QtCore.Qt.Key_Left, QtCore.Qt.Key_Up, QtCore.Qt.Key_Backtab ]: self._popup_child(QtGui.QCursor.pos()) if 0 < self._selectionIndex: nextSelection = self._selectionIndex - 1 else: nextSelection = len(self._filing) - 1 self._select_at(nextSelection) self._display.selectAt(self._selectionIndex) elif keyEvent.key() in [QtCore.Qt.Key_Space]: self._popup_child(QtGui.QCursor.pos()) self._select_at(PieFiling.SELECTION_CENTER) self._display.selectAt(self._selectionIndex) elif keyEvent.key() in [ QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter, QtCore.Qt.Key_Space ]: self._delayPopupTimer.stop() self._popupLocation = None self._activate_at(self._selectionIndex) self._hide_child() elif keyEvent.key() in [QtCore.Qt.Key_Escape, QtCore.Qt.Key_Backspace]: self._delayPopupTimer.stop() self._popupLocation = None self._activate_at(PieFiling.SELECTION_NONE) self._hide_child() else: QtGui.QWidget.keyPressEvent(self, keyEvent) @misc_utils.log_exception(_moduleLogger) def resizeEvent(self, resizeEvent): self.setButtonRadius( min(resizeEvent.size().width(), resizeEvent.size().height()) / 2 - 1) QtGui.QWidget.resizeEvent(self, resizeEvent) @misc_utils.log_exception(_moduleLogger) def showEvent(self, showEvent): self._buttonArtist.show(self.palette()) self._cachedCenterPosition = self.rect().center() QtGui.QWidget.showEvent(self, showEvent) @misc_utils.log_exception(_moduleLogger) def hideEvent(self, hideEvent): self._display.hide() self._select_at(PieFiling.SELECTION_NONE) QtGui.QWidget.hideEvent(self, hideEvent) @misc_utils.log_exception(_moduleLogger) def paintEvent(self, paintEvent): self.setButtonRadius( min(self.rect().width(), self.rect().height()) / 2 - 1) if self._poppedUp: selectionIndex = PieFiling.SELECTION_CENTER else: selectionIndex = PieFiling.SELECTION_NONE screen = QtGui.QStylePainter(self) screen.setRenderHint(QtGui.QPainter.Antialiasing, True) option = QtGui.QStyleOptionButton() option.initFrom(self) option.state = QtGui.QStyle.State_Sunken if self._pressed else QtGui.QStyle.State_Raised screen.drawControl(QtGui.QStyle.CE_PushButton, option) self._buttonArtist.paintPainter(selectionIndex, screen) QtGui.QWidget.paintEvent(self, paintEvent) def __iter__(self): return iter(self._filing) def __len__(self): return len(self._filing) def _popup_child(self, position): self._poppedUp = True self.aboutToShow.emit() self._delayPopupTimer.stop() self._popupLocation = None position = position - QtCore.QPoint(self._filing.outerRadius(), self._filing.outerRadius()) self._display.move(position) self._display.show() self.update() def _hide_child(self): self._poppedUp = False self.aboutToHide.emit() self._display.hide() self.update() def _select_at(self, index): self._selectionIndex = index def _update_selection(self, lastMousePos, ignoreOuter=False): radius = _radius_at(self._cachedCenterPosition, lastMousePos) if radius < self._filing.innerRadius(): self._select_at(PieFiling.SELECTION_CENTER) elif radius <= self._filing.outerRadius() or ignoreOuter: self._select_at(self.indexAt(lastMousePos)) else: self._select_at(PieFiling.SELECTION_NONE) def _activate_at(self, index): if index == PieFiling.SELECTION_NONE: self.canceled.emit() return elif index == PieFiling.SELECTION_CENTER: child = self._filing.center() else: child = self.itemAt(index) if child.action().isEnabled(): child.action().trigger() self.activated.emit(index) else: self.canceled.emit()
class FileSystemModel(QtCore.QAbstractListModel): """ Wrapper around QtGui.QFileSystemModel """ FILEINFOS = [ "fileName", "isDir", "filePath", "completeSuffix", "baseName", ] EXTINFOS = [ "type", ] ALLINFOS = FILEINFOS + EXTINFOS def __init__(self, model, path): QtCore.QAbstractListModel.__init__(self) self._path = path self._model = model self._rootIndex = self._model.index(self._path) self._child = None self.setRoleNames(dict(enumerate(self.ALLINFOS))) self._model.directoryLoaded.connect(self._on_directory_loaded) childChanged = qt_compat.Signal(QtCore.QObject) def _child(self): assert self._child is not None return self._child child = qt_compat.Property(QtCore.QObject, _child, notify=childChanged) backendChanged = qt_compat.Signal() def _parent(self): finfo = self._model.fileInfo(self._rootIndex) return finfo.fileName() parent = qt_compat.Property(str, _parent, notify=backendChanged) @qt_compat.Slot(str) @misc.log_exception(_moduleLogger) def browse_to(self, path): if self._child is None: self._child = FileSystemModel(self._model, path) else: self._child.switch_to(path) self.childChanged.emit() return self._child @qt_compat.Slot(str) @misc.log_exception(_moduleLogger) def switch_to(self, path): with scoped_model_reset(self): self._path = path self._rootIndex = self._model.index(self._path) self.backendChanged.emit() def __len__(self): return self._model.rowCount(self._rootIndex) def __getitem__(self, key): return self._model.index(key, 0, self._rootIndex) def __iter__(self): return (self[i] for i in xrange(len(self))) def rowCount(self, parent=QtCore.QModelIndex()): return len(self) def data(self, index, role): if index.isValid() and 0 <= role and role < len(self.ALLINFOS): internalIndex = self._translate_index(index) info = self._model.fileInfo(internalIndex) if role < len(self.FILEINFOS): field = self.FILEINFOS[role] value = getattr(info, field)() else: role -= len(self.FILEINFOS) field = self.EXTINFOS[role] if field == "type": return self._model.type(internalIndex) else: raise NotImplementedError("Out of range that was already checked") return value return None def _on_directory_loaded(self, path): if self._path == path: self.backendChanged.emit() self.reset() def _translate_index(self, externalIndex): internalIndex = self._model.index(externalIndex.row(), 0, self._rootIndex) return internalIndex
class QErrorLog(QtCore.QObject): messagePushed = qt_compat.Signal() messagePopped = qt_compat.Signal() currentMessageChanged = qt_compat.Signal() def __init__(self): QtCore.QObject.__init__(self) self._messages = [] self._nullMessage = QErrorMessage("", QErrorMessage.LEVEL_INFO) @qt_compat.Slot(str) def push_busy(self, message): _moduleLogger.debug("Entering state: %s" % message) self._push_message(message, QErrorMessage.LEVEL_BUSY) @qt_compat.Slot(str) def push_info(self, message): self._push_message(message, QErrorMessage.LEVEL_INFO) @qt_compat.Slot(str) def push_error(self, message): self._push_message(message, QErrorMessage.LEVEL_ERROR) @qt_compat.Slot(str, int) def push_message(self, message, level): self._push_message(message, level) def push_exception(self): userMessage = str(sys.exc_info()[1]) _moduleLogger.exception(userMessage) self.push_error(userMessage) @qt_compat.Slot() @qt_compat.Slot(str) def pop(self, message = None): if message is None: del self._messages[0] else: _moduleLogger.debug("Exiting state: %s" % message) messageIndex = [ i for (i, error) in enumerate(self._messages) if error.message == message ] # Might be removed out of order if messageIndex: del self._messages[messageIndex[0]] self.messagePopped.emit() self.currentMessageChanged.emit() def peek_message(self): if self._messages: return self._messages[0] else: return self._nullMessage currentMessage = qt_compat.Property(QtCore.QObject, lambda self: self.peek_message(), notify=currentMessageChanged) hasMessages = qt_compat.Property(bool, lambda self: bool(self._messages), notify=currentMessageChanged) def _push_message(self, message, level): self._messages.append(QErrorMessage(message, level)) # Sort is defined as stable, so this should be fine self._messages.sort(key=lambda x: x.level) self._messages.reverse() self.messagePushed.emit() self.currentMessageChanged.emit() def __len__(self): return len(self._messages)