import asyncio import logging from PyQt5 import QtCore, QtWidgets def log_level_to_name(level): if level >= logging.CRITICAL: return "CRITICAL" if level >= logging.ERROR: return "ERROR" if level >= logging.WARNING: return "WARNING" if level >= logging.INFO: return "INFO" return "DEBUG" class WheelFilter(QtCore.QObject): def __init__(self, parent, ignore_with_modifier=False): super().__init__(parent) self.ignore_with_modifier = ignore_with_modifier def eventFilter(self, obj, event): if event.type() != QtCore.QEvent.Wheel: return False has_modifier = event.modifiers() != QtCore.Qt.NoModifier if has_modifier == self.ignore_with_modifier: event.ignore() return True return False def disable_scroll_wheel(widget): widget.setFocusPolicy(QtCore.Qt.StrongFocus) widget.installEventFilter(WheelFilter(widget)) class QDockWidgetCloseDetect(QtWidgets.QDockWidget): sigClosed = QtCore.pyqtSignal() def closeEvent(self, event): self.sigClosed.emit() QtWidgets.QDockWidget.closeEvent(self, event) class LayoutWidget(QtWidgets.QWidget): def __init__(self, parent=None): QtWidgets.QWidget.__init__(self, parent) self.layout = QtWidgets.QGridLayout() self.setLayout(self.layout) def addWidget(self, item, row=0, col=0, rowspan=1, colspan=1): self.layout.addWidget(item, row, col, rowspan, colspan) async def get_open_file_name(parent, caption, dir, filter): """like QtWidgets.QFileDialog.getOpenFileName(), but a coroutine""" dialog = QtWidgets.QFileDialog(parent, caption, dir, filter) dialog.setFileMode(dialog.ExistingFile) dialog.setAcceptMode(dialog.AcceptOpen) fut = asyncio.Future() def on_accept(): fut.set_result(dialog.selectedFiles()[0]) dialog.accepted.connect(on_accept) dialog.rejected.connect(fut.cancel) dialog.open() return await fut async def get_save_file_name(parent, caption, dir, filter, suffix=None): """like QtWidgets.QFileDialog.getSaveFileName(), but a coroutine""" dialog = QtWidgets.QFileDialog(parent, caption, dir, filter) dialog.setFileMode(dialog.AnyFile) dialog.setAcceptMode(dialog.AcceptSave) if suffix is not None: dialog.setDefaultSuffix(suffix) fut = asyncio.Future() def on_accept(): fut.set_result(dialog.selectedFiles()[0]) dialog.accepted.connect(on_accept) dialog.rejected.connect(fut.cancel) dialog.open() return await fut # Based on: # http://stackoverflow.com/questions/250890/using-qsortfilterproxymodel-with-a-tree-model class QRecursiveFilterProxyModel(QtCore.QSortFilterProxyModel): def filterAcceptsRow(self, source_row, source_parent): regexp = self.filterRegExp() if not regexp.isEmpty(): source_index = self.sourceModel().index( source_row, self.filterKeyColumn(), source_parent) if source_index.isValid(): for i in range(self.sourceModel().rowCount(source_index)): if self.filterAcceptsRow(i, source_index): return True key = self.sourceModel().data(source_index, self.filterRole()) return regexp.indexIn(key) != -1 return QtCore.QSortFilterProxyModel.filterAcceptsRow( self, source_row, source_parent)