class IconColorEditor(QDialog): """An editor to let the user select an icon and a color for an object_class. """ def __init__(self, parent): """Init class.""" super().__init__(parent) # , Qt.Popup) icon_size = QSize(32, 32) self.icon_mngr = IconListManager(icon_size) self.setWindowTitle("Select icon and color") self.icon_widget = QWidget(self) self.icon_list = QListView(self.icon_widget) self.icon_list.setViewMode(QListView.IconMode) self.icon_list.setIconSize(icon_size) self.icon_list.setResizeMode(QListView.Adjust) self.icon_list.setItemDelegate(_IconPainterDelegate(self)) self.icon_list.setMovement(QListView.Static) self.icon_list.setMinimumHeight(400) icon_widget_layout = QVBoxLayout(self.icon_widget) icon_widget_layout.addWidget(QLabel("Font Awesome icons")) self.line_edit = QLineEdit() self.line_edit.setPlaceholderText("Search icons for...") icon_widget_layout.addWidget(self.line_edit) icon_widget_layout.addWidget(self.icon_list) self.color_dialog = QColorDialog(self) self.color_dialog.setWindowFlags(Qt.Widget) self.color_dialog.setOption(QColorDialog.NoButtons, True) self.color_dialog.setOption(QColorDialog.DontUseNativeDialog, True) self.button_box = QDialogButtonBox(self) self.button_box.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) top_widget = QWidget(self) top_layout = QHBoxLayout(top_widget) top_layout.addWidget(self.icon_widget) top_layout.addWidget(self.color_dialog) layout = QVBoxLayout(self) layout.addWidget(top_widget) layout.addWidget(self.button_box) self.proxy_model = QSortFilterProxyModel(self) self.proxy_model.setSourceModel(self.icon_mngr.model) self.proxy_model.filterAcceptsRow = self._proxy_model_filter_accepts_row self.icon_list.setModel(self.proxy_model) self.setAttribute(Qt.WA_DeleteOnClose) self.connect_signals() def _proxy_model_filter_accepts_row(self, source_row, source_parent): """Overridden method to filter icons according to search terms. """ text = self.line_edit.text() if not text: return QSortFilterProxyModel.filterAcceptsRow( self.proxy_model, source_row, source_parent) searchterms = self.icon_mngr.model.index( source_row, 0, source_parent).data(Qt.UserRole + 1) return any([text in term for term in searchterms]) def connect_signals(self): """Connect signals to slots.""" self.line_edit.textEdited.connect(self.proxy_model.invalidateFilter) self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.reject) def set_data(self, data): icon_code, color_code = interpret_icon_id(data) self.icon_mngr.init_model() for i in range(self.proxy_model.rowCount()): index = self.proxy_model.index(i, 0) if index.data(Qt.UserRole) == icon_code: self.icon_list.setCurrentIndex(index) break self.color_dialog.setCurrentColor(QColor(color_code)) def data(self): icon_code = self.icon_list.currentIndex().data(Qt.UserRole) color_code = self.color_dialog.currentColor().rgb() return make_icon_id(icon_code, color_code)
class Completer(QWidget): """docstring for ClassName Attributes: delegate (CompleterDelegate): the delegate use by the view model (CompleterModel): the model proxy_model (QSortFilterProxyModel ): the proxy model used to filter model panel (QLabel): The description widget view (QListView): the view Signals: activated (str): return the keyword selected """ activated = Signal(str) def __init__(self, parent=None): super().__init__(parent) self._target = None self._completion_prefix = "" self.setWindowFlag(Qt.Popup) self.setFocusPolicy(Qt.NoFocus) # create model self.model = CompleterModel() self.proxy_model = QSortFilterProxyModel() self.proxy_model.setSourceModel(self.model) self.proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive) # create delegate self.delegate = CompleterDelegate() # create view self.view = QListView() self.view.setSelectionMode(QAbstractItemView.SingleSelection) self.view.setFocusPolicy(Qt.NoFocus) self.view.installEventFilter(self) self.view.setModel(self.proxy_model) self.view.setItemDelegate(self.delegate) self.view.setMinimumWidth(200) self.view.setUniformItemSizes(True) self.view.setSpacing(0) self.view.selectionModel().currentRowChanged.connect( self._on_row_changed) self.setFocusProxy(self.view) # create panel info self.panel = QLabel() self.panel.setAlignment(Qt.AlignTop) self.panel.setMinimumWidth(300) self.panel.setWordWrap(True) self.panel.setFrameShape(QFrame.StyledPanel) # Create layout vlayout = QHBoxLayout() vlayout.setContentsMargins(0, 0, 0, 0) vlayout.setSpacing(0) vlayout.addWidget(self.view) vlayout.addWidget(self.panel) self.setLayout(vlayout) def set_target(self, target): """Set CodeEdit Args: target (CodeEdit): The CodeEdit """ self._target = target self.installEventFilter(self._target) def eventFilter(self, obj: QObject, event: QEvent) -> bool: """Filter event from CodeEdit and QListView Args: obj (QObject): Description event (QEvent): Description Returns: bool """ # Intercept CodeEdit event if obj == self._target: if event.type() == QEvent.FocusOut: # Ignore lost focus! return True else: obj.event(event) return True # Intercept QListView event if obj == self.view: # Redirect event to QTextExit if event.type() == QEvent.KeyPress and self._target: current = self.view.selectionModel().currentIndex() # emit signal when user press return if event.key() == Qt.Key_Return: word = current.data() self.activated.emit(word) self.hide() event.ignore() return True # use tab to move down/up in the list if event.key() == Qt.Key_Tab: if current.row() < self.proxy_model.rowCount() - 1: self.view.setCurrentIndex( self.proxy_model.index(current.row() + 1, 0)) if event.key() == Qt.Key_Backtab: if current.row() > 0: self.view.setCurrentIndex( self.proxy_model.index(current.row() - 1, 0)) # Route other key event to the target ! This make possible to write text when completer is visible self._target.event(event) return super().eventFilter(obj, event) def complete(self, rect: QRect): """Show completer as popup Args: rect (QRect): the area where to display the completer """ if self.proxy_model.rowCount() == 0: self.hide() return if self._target: pos = self._target.mapToGlobal(rect.bottomRight()) self.move(pos) self.setFocus() if not self.isVisible(): width = 400 #height = self.view.sizeHintForRow(0) * self.proxy_model.rowCount() + 3 # HACK.. TODO better ! #height = min(self._target.height() / 2, height) #self.resize(width, height) self.adjustSize() self.show() def set_completion_prefix(self, prefix: str): """Set prefix and filter model Args: prefix (str): A prefix keyword used to filter model """ self.view.clearSelection() self._completion_prefix = prefix self.proxy_model.setFilterRegularExpression( QRegularExpression(f"^{prefix}.*", QRegularExpression.CaseInsensitiveOption)) if self.proxy_model.rowCount() > 0: self.select_row(0) def select_row(self, row: int): """Select a row in the model Args: row (int): a row number """ index = self.proxy_model.index(row, 0) self.view.selectionModel().setCurrentIndex(index, QItemSelectionModel.Select) def completion_prefix(self) -> str: """getter of completion_prefix TODO: use getter / setter Returns: str: Return the completion_prefix """ return self._completion_prefix def hide(self): """Override from QWidget Hide the completer """ self.set_completion_prefix("") super().hide() def _on_row_changed(self, current: QModelIndex, previous: QModelIndex): """Slot received when user select a new item in the list. This is used to update the panel Args: current (QModelIndex): the selection index previous (QModelIndex): UNUSED """ description = current.data(Qt.ToolTipRole) self.panel.setText(description)