Source code for gaitutils.gui.qt_widgets

# -*- coding: utf-8 -*-
"""
gaitutils custom Qt widgets

@author: Jussi (jnu@iki.fi)
"""

from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import QObject, pyqtSignal
import logging


[docs]class NiceListWidgetItem(QtWidgets.QListWidgetItem): """Make list items more pythonic""" def __init__(self, *args, **kwargs): # don't pass this arg to superclass __init__ checkable = kwargs.pop('checkable') super().__init__(*args, **kwargs) if checkable: self.setFlags(self.flags() | QtCore.Qt.ItemIsUserCheckable) @property def text(self): return super().text() @property def userdata(self): return super().data(QtCore.Qt.UserRole) @userdata.setter def userdata(self, _data): if _data is not None: super().setData(QtCore.Qt.UserRole, _data) @property def checkstate(self): return super().checkState() @checkstate.setter def checkstate(self, checked): state = QtCore.Qt.Checked if checked else QtCore.Qt.Unchecked super().setCheckState(state)
[docs]class NiceListWidget(QtWidgets.QListWidget): """Adds some conveniences to QListWidget""" def __init__(self, parent=None): super().__init__(parent) @property def items(self): """Yield all list items. NB: be careful when modifying list items in a loop with the generator - count() is evaluated only once, so the generator may return items that have already been deleted""" for i in range(self.count()): yield self.item(i) @property def checked_items(self): """Yield checked items""" return (item for item in self.items if item.checkstate)
[docs] def check_all(self): """Check all items""" for item in self.items: item.checkstate = True
[docs] def check_none(self): """Uncheck all items""" for item in self.items: item.checkstate = False
[docs] def add_item(self, txt, data=None, checkable=False, checked=False): """Add checkable item with data. Select new item.""" item = NiceListWidgetItem(txt, self, checkable=checkable) item.userdata = data if checkable: item.checkstate = checked self.setCurrentItem(item)
[docs] def rm_current_item(self): """Remove currently selected item""" return self.takeItem(self.row(self.currentItem()))
[docs]class QtHandler(logging.Handler): """Qt logging handler""" def __init__(self): logging.Handler.__init__(self)
[docs] def emit(self, record): record = self.format(record) if record: XStream.stdout().write(f'{record}\n')
[docs]class XStream(QtCore.QObject): """Stream for Qt logging handler""" _stdout = None _stderr = None messageWritten = QtCore.pyqtSignal(str)
[docs] def flush(self): pass
[docs] def fileno(self): return -1
[docs] def write(self, msg): if not self.signalsBlocked(): self.messageWritten.emit(msg)
[docs] @staticmethod def stdout(): if not XStream._stdout: XStream._stdout = XStream() # sys.stdout = XStream._stdout # also capture stdout return XStream._stdout
[docs] @staticmethod def stderr(): if not XStream._stderr: XStream._stderr = XStream() # sys.stderr = XStream._stderr # ... and stderr return XStream._stderr
[docs]class ProgressBar(QtWidgets.QProgressDialog): """Qt progress bar with reasonable defaults""" # custom signal to indicate that the operation was canceled _canceled = pyqtSignal() def __init__(self, title): super().__init__() self.setWindowTitle(title) self.cancelbutton = QtWidgets.QPushButton('Cancel') self.setCancelButton(self.cancelbutton) # set a custom cancel signal, since the default one immediately # close the progress bar self.cancelbutton.disconnect() self.cancelbutton.clicked.connect(self._cancel) self.setMinimum(0) self.setMaximum(100) self.setGeometry(500, 300, 500, 100) # self.setAutoClose(False) self.setAutoReset(False) self.show() def _cancel(self): """Custom cancel handler""" self.cancelbutton.setText('Wait...') self.cancelbutton.setEnabled(False) QtWidgets.QApplication.processEvents() self._canceled.emit()
[docs] def update(self, text, p): """Update bar, showing text and bar at p%""" self.setLabelText(text) self.setValue(int(p)) # update right away in case that thread is blocked QtWidgets.QApplication.processEvents()
[docs]class ProgressSignals(QObject): """Used to emit progress signals across threads""" progress = pyqtSignal(object, object) def __init__(self): super().__init__() # this flag can be checked to see whether the operation was canceled self.canceled = False
[docs] def cancel(self): """Raise the cancel flag""" self.canceled = True