class ExtendedComboBox(QComboBox): def __init__(self): QComboBox.__init__(self) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) self.filter_model = QSortFilterProxyModel(self) self.filter_model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.filter_model.setSourceModel(self.model()) self.completer = QCompleter(self.filter_model, self) self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) self.lineEdit().textEdited.connect( self.filter_model.setFilterFixedString) self.completer.activated.connect(self.on_completer_activated) def on_completer_activated(self, text): if text: index = self.findText(text) self.setCurrentIndex(index) self.activated[str].emit(self.itemText(index)) def setModel(self, model): self.setModel(model) self.filter_model.setSourceModel(model) self.completer.setModel(self.filter_model) def setModelColumn(self, column): self.completer.setCompletionColumn(column) self.filter_model.setFilterKeyColumn(column) self.setModelColumn(column)
class ExtendedComboBox(QComboBox): """An enhanced version of PyQt5's QComboBox widget that allows for search and filtering of the greater list. Implemented by armonge on StackOverflow, adapted for PyQt5 by Tamas Haver on StackOverflow. The Stackoverflow post can be found here: https://stackoverflow.com/questions/4827207/how-do-i-filter-the-pyqt-qcombobox-items-based-on-the-text-input """ def __init__(self, parent=None): super(ExtendedComboBox, self).__init__(parent) self.setFocusPolicy(QtCore.Qt.StrongFocus) self.setEditable(True) # add a filter model to filter matching items self.pFilterModel = QtCore.QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) self.pFilterModel.setSourceModel(self.model()) # add a completer, which uses the filter model self.completer = QCompleter(self.pFilterModel, self) # always show all (filtered) completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) # connect signals self.lineEdit().textEdited.connect( self.pFilterModel.setFilterFixedString) self.completer.activated.connect(self.on_completer_activated) def on_completer_activated(self, text): """On selection of an item from the completer, select the corresponding item from combobox. """ if text: index = self.findText(text) self.setCurrentIndex(index) self.activated[str].emit(self.itemText(index)) def setModel(self, model): """On model change, update the models of the filter and completer as well. """ super(ExtendedComboBox, self).setModel(model) self.pFilterModel.setSourceModel(model) self.completer.setModel(self.pFilterModel) def setModelColumn(self, column): """On model column change, update the model column of the filter and completer as well. """ self.completer.setCompletionColumn(column) self.pFilterModel.setFilterKeyColumn(column) super(ExtendedComboBox, self).setModelColumn(column)
class ExtendedCombo(QComboBox): def __init__(self, items=None, parent=None): super().__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) self.completer = QCompleter(self) # always show all completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.completer.setPopup(self.view()) self.setCompleter(self.completer) self.lineEdit().textEdited.connect(self.pFilterModel.setFilterFixedString) self.completer.activated.connect(self.setTextIfCompleterIsClicked) def setItems(self, items: list or set): self.items = list(items) model = QStandardItemModel() for i, student in enumerate(items): item = UserItem(student) model.setItem(i, 0, item) self.setModel(model) self.setModelColumn(0) def setModel(self, model): super(ExtendedCombo, self).setModel(model) self.pFilterModel.setSourceModel(model) self.completer.setModel(self.pFilterModel) def setModelColumn(self, column): self.completer.setCompletionColumn(column) self.pFilterModel.setFilterKeyColumn(column) super(ExtendedCombo, self).setModelColumn(column) def view(self): return self.completer.popup() def index(self): return self.currentIndex() def setTextIfCompleterIsClicked(self, text): if text: index = self.findText(text) self.setCurrentIndex(index) def currentItem(self): return self.items[self.currentIndex()]
def setup_completer(self): """ Sets up the autocomplete on the ancestor """ completer = QCompleter() completer.setModel(self.model) completer.setCompletionRole(Qt.DisplayRole) completer.setCompletionColumn(self.text_column) completer.setCaseSensitivity(Qt.CaseInsensitive) completer.setCompletionMode(QCompleter.PopupCompletion) completer.setFilterMode(Qt.MatchContains) # pylint: disable=unsubscriptable-object signal = completer.activated[QModelIndex] signal.connect(self.autocomplete_activated) self.setCompleter(completer)
class FilteringComboBox(QComboBox): """Combination of QCombobox and QLineEdit with autocompletionself. Line edit and completer model is taken from QSqlTable mod Args: table (str): db table name containing data for combobox column (str): column name containing data for combobox """ def __init__(self, table, column, placeholderText, parent=None): super(FilteringComboBox, self).__init__(parent) self.parent = parent self.setEditable(True) self.setFocusPolicy(Qt.StrongFocus) self.setInsertPolicy(QComboBox.NoInsert) self.lineEdit().setPlaceholderText(placeholderText) # setup data model self._model = QSqlTableModel(self) self._model.setTable(table) self._model.select() col_num = self._model.fieldIndex(column) self._model.sort(col_num, Qt.AscendingOrder) self.setModel(self._model) self.setModelColumn(col_num) # setup completer self._proxy = QSortFilterProxyModel(self) self._proxy.setFilterCaseSensitivity(Qt.CaseInsensitive) self._proxy.setSourceModel(self._model) self._proxy.setFilterKeyColumn(col_num) self._completer = QCompleter(self) self._completer.setModel(self._proxy) self._completer.setCompletionColumn(col_num) self._completer.activated.connect(self.onCompleterActivated) self._completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self._completer) self.lineEdit().textEdited.connect(self._proxy.setFilterFixedString) @pyqtSlot(str) def onCompleterActivated(self, text): if not text: return self.setCurrentIndex(self.findText(text)) self.activated[str].emit(self.currentText()) def updateModel(self): self._model.select()
class ExtendedComboBox(QComboBox): def __init__(self, parent=None): super(ExtendedComboBox, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) # add a filter model to filter matching items self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.pFilterModel.setSourceModel(self.model()) # add a completer, which uses the filter model self.completer = QCompleter(self.pFilterModel, self) # always show all (filtered) completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) # connect signals self.lineEdit().textEdited.connect( self.pFilterModel.setFilterFixedString) self.completer.activated.connect(self.on_completer_activated) # on selection of an item from the completer, select the corresponding item from combobox def on_completer_activated(self, text): if text: index = self.findText(text) self.setCurrentIndex(index) self.activated[str].emit(self.itemText(index)) # on model change, update the models of the filter and completer as well def setModel(self, model): super(ExtendedComboBox, self).setModel(model) self.pFilterModel.setSourceModel(model) self.completer.setModel(self.pFilterModel) # on model column change, update the model column of the filter and completer as well def setModelColumn(self, column): self.completer.setCompletionColumn(column) self.pFilterModel.setFilterKeyColumn(column) super(ExtendedComboBox, self).setModelColumn(column) def wheelEvent(self, e: QtGui.QWheelEvent) -> None: """ 当鼠标轻放在框上,禁止鼠标滚轮滚动选项 :param e: :return: """ pass
class ExtendedCombo(QComboBox): def __init__(self, parent=None): super(ExtendedCombo, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) # add a filter model to filter matching items self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.pFilterModel.setSourceModel(self.model()) # add a completer, which uses the filter model self.completer = QCompleter(self.pFilterModel, self) # always show all (filtered) completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) # connect signals self.lineEdit().textEdited.connect( self.pFilterModel.setFilterFixedString) self.completer.activated.connect(self.on_completer_activated) self.cur_index = self.currentIndex() self.text = self.currentText() # on selection of an item from the completer, select the corresponding item from combobox def on_completer_activated(self, text): if text: index = self.findText(text) self.setCurrentIndex(index) C = TextInstance C.text = text else: C = TextInstance C.text = self.currentText() def update(self): return self.text # on model change, update the models of the filter and completer as well def setModel(self, model): super(ExtendedCombo, self).setModel(model) self.pFilterModel.setSourceModel(model) self.completer.setModel(self.pFilterModel) # on model column change, update the model column of the filter and completer as well def setModelColumn(self, column): self.completer.setCompletionColumn(column) self.pFilterModel.setFilterKeyColumn(column) super(ExtendedCombo, self).setModelColumn(column)
class PostSelectionWidget(QDialog): def __init__(self, app, parent=None): super(PostSelectionWidget, self).__init__(parent=parent) self.app = app self.model = PostsListModel(app) self.filtermodel = QSortFilterProxyModel() self.filtermodel.setSourceModel(self.model) self.resize(450, 250) self.layout = QVBoxLayout() self.setLayout(self.layout) view = QListView(self) self.completer = QCompleter(parent=self) self.completer.setCompletionMode(QCompleter.InlineCompletion) self.completer.setCompletionColumn(0) self.completer.setCompletionRole(Qt.DisplayRole) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setModelSorting(QCompleter.CaseInsensitivelySortedModel) self.completer.setModel(self.model) self._edit = QLineEdit('') self._edit.setCompleter(self.completer) self._edit.textEdited.connect(self.setfilter) self.layout.addWidget(self._edit) self.layout.addWidget(view) view.setModel(self.filtermodel) view.clicked.connect(self.select) view.doubleClicked.connect(self.select_and_edit) self._button = QPushButton('Edit') self._button.clicked.connect(self.edit) self.layout.addWidget(self._button) def setfilter(self, text): regexp = QRegExp("%s*" % text, Qt.CaseInsensitive, QRegExp.Wildcard) self.filtermodel.setFilterRegExp(regexp) def select(self, index): text = self.filtermodel.data(index) self._edit.setText(text) def select_and_edit(self, index): text = self.filtermodel.data(index) self._edit.setText(text) self.app.edit_post(text) def edit(self): self.app.edit_post(self._edit.text())
class SearchableComboBox(QComboBox): def __init__(self, string_list, parent=None): super(SearchableComboBox, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) # add a filter model to filter matching items self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.pFilterModel.setSourceModel(self.model()) # add a completer, which uses the filter model self.completer = QCompleter(self.pFilterModel, self) # always show all (filtered) completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) # connect signals self.lineEdit().textEdited.connect(self.pFilterModel.setFilterFixedString) self.completer.activated.connect(self.on_completer_activated) # either fill the standard model of the combobox self.addItems(string_list) # on selection of an item from the completer, select the corresponding item from combobox def on_completer_activated(self, text): if text: index = self.findText(text) self.setCurrentIndex(index) self.activated[str].emit(self.itemText(index)) # on model change, update the models of the filter and completer as well def setModel(self, model): super(SearchableComboBox, self).setModel(model) self.pFilterModel.setSourceModel(model) self.completer.setModel(self.pFilterModel) # on model column change, update the model column of the filter and completer as well def setModelColumn(self, column): self.completer.setCompletionColumn(column) self.pFilterModel.setFilterKeyColumn(column) super(SearchableComboBox, self).setModelColumn(column)
class SearchableComboBox(QComboBox): def __init__(self, parent=None): QComboBox.__init__(self, parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) self.completer = QCompleter(self) self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.p_filter_model = QSortFilterProxyModel() self.p_filter_model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.completer.setPopup(self.completer.popup()) self.setCompleter(self.completer) self.lineEdit().textEdited.connect( self.p_filter_model.setFilterFixedString) def setModel(self, model): super().setModel(model) self.p_filter_model.setSourceModel(model) self.completer.setModel(self.p_filter_model) def setModelColumn(self, column): self.completer.setCompletionColumn(column) self.p_filter_model.setFilterKeyColumn(column) super().setModelColumn(column) def setTextIfCompleterIsClicked(self, text): if text: self.setCurrentIndex(self.findText(text)) def setItems(self, items): self.clear() model = QStandardItemModel() for i, word in enumerate(items): item = QStandardItem(word) model.setItem(i, 0, item) self.setModel(model) self.setModelColumn(0)
class ExtendedCombo(QComboBox): def __init__(self): super().__init__() self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) self.completer = QCompleter(self) # always show all completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.completer.setPopup(self.view()) self.setCompleter(self.completer) self.lineEdit().textEdited[str].connect( self.pFilterModel.setFilterFixedString) self.completer.activated.connect(self.setTextIfCompleterIsClicked) def setModel(self, model): super(ExtendedCombo, self).setModel(model) self.pFilterModel.setSourceModel(model) self.completer.setModel(self.pFilterModel) def setModelColumn(self, column): self.completer.setCompletionColumn(column) self.pFilterModel.setFilterKeyColumn(column) super(ExtendedCombo, self).setModelColumn(column) def view(self): return self.completer.popup() def index(self): return self.currentIndex() def setTextIfCompleterIsClicked(self, text): if text: index = self.findText(text) self.setCurrentIndex(index)
class FilteredComboBox(QComboBox): def __init__(self, options, **kwargs): super().__init__() self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) # self.dropEvent() self.completer = QCompleter(self) # always show all completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.completer.setPopup(self.view()) self.setCompleter(self.completer) the_style = "border-top-left-radius: 10px; border-bottom-left-radius: 10px; background-color: #f5f5f5;" self.lineEdit().setStyleSheet(the_style) if "height" in kwargs: self.setFixedHeight(kwargs["height"]) else: self.setFixedHeight(37) self.lineEdit().textEdited.connect( self.pFilterModel.setFilterFixedString) self.completer.activated.connect(self.setTextIfCompleterIsClicked) model = QStandardItemModel() for i, word in enumerate(options): item = QStandardItem(word) model.setItem(i, 0, item) self.setModel(model) self.setModelColumn(0) if "width" in kwargs: self.setFixedWidth(kwargs["width"]) else: self.setFixedWidth(260) style = """ QComboBox { border-radius: 10px; } QComboBox::drop-down:button{ width: 25px; background-color: #f5f5f5; border-image: url(./resources/assets/images/drop_down.png); border-bottom-right-radius: 10px; border-top-right-radius: 10px; } QComboBox::drop-down:button:hover{ background-color: #f5f5f5; } """ self.setStyleSheet(style) def setModel(self, model): super(FilteredComboBox, self).setModel(model) self.pFilterModel.setSourceModel(model) self.completer.setModel(self.pFilterModel) def setModelColumn(self, column): self.completer.setCompletionColumn(column) self.pFilterModel.setFilterKeyColumn(column) super(FilteredComboBox, self).setModelColumn(column) def view(self): return self.completer.popup() def index(self): return self.currentIndex() def setTextIfCompleterIsClicked(self, text): if text: index = self.findText(text) self.setCurrentIndex(index) def setMaximumWidth(self, p_int): self.setFixedWidth(p_int)
class ComboBoxAutocomplete(QComboBox): """UI dropdown element with autocomplete. Attributes: completer (QtWidgets.QCompleter): Autocomplete instance tied to the dropdown items. proxy_model (QtWidgets.QSortFilterProxyModel): Configurations of the autocomplete. """ def __init__(self, parent=None): """Creates default configurations (case insensitive) and constructs the UI element. Args: parent (App(QDialog), optional): Object corresponding to the parent UI element. """ super(ComboBoxAutocomplete, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) # Setup proxy model to sort and filter data passed between model and view self.proxy_model = QSortFilterProxyModel(self) self.proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.proxy_model.setSourceModel(self.model()) # Setup completer self.completer = QCompleter(self.proxy_model, self) self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) # Connect autocompletion signals self.lineEdit().textEdited.connect( self.proxy_model.setFilterFixedString) self.completer.activated.connect(self.on_completer_activated) # Override def on_completer_activated(self, text): """Callback event handler for a query in the autocomplete. Args: text (str): Query text. """ if not text: return index = self.findText(text) self.setCurrentIndex(index) self.activated[str].emit(self.itemText(index)) # Override def setModel(self, model): """Updates the configuration model. Args: model (QtWidgets.QSortFilterProxyModel): New configuration to update to. """ super(ComboBoxAutocomplete, self).setModel(model) self.proxy_model.setSourceModel(model) self.completer.setModel(self.proxy_model) # Override def setModelColumn(self, column): """Updates the column being autocompleted. Args: column (int): Column from the data used for checking suggestions. """ self.completer.setCompletionColumn(column) self.proxy_model.setFilterKeyColumn(column) super(ComboBoxAutocomplete, self).setModelColumn(column)
class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__() self.setGeometry(300, 300, 400, 300) fid = open('example.db', 'w') fid.close() db = QSqlDatabase.addDatabase("QSQLITE") db.setDatabaseName('example.db') db.open() query = QSqlQuery(db) query.exec_('CREATE TABLE "airports" ("city" TEXT, "code" TEXT)') airports = [('Aberdeen, SD', 'ABR'), ('Abilene, TX', 'ABI'), ('Adak Island, AK', 'ADK'), ('Akiachak, AK', 'KKI'), ('Akiak, AK', 'AKI'), ('Akron/Canton, OH', 'CAK'), ('Akuton, AK', 'KQA'), ('Alakanuk, AK', 'AUK'), ('Alamogordo, NM', 'ALM'), ('Alamosa, CO', 'ALS'), ('Albany, NY', 'ALB'), ('Albuquerque, NM', 'ABQ')] for item in airports: sql = 'INSERT INTO airports(city, code) VALUES(?, ?)' query.prepare(sql) query.addBindValue(item[0]) query.addBindValue(item[1]) query.exec_() query.exec_('SELECT * FROM airports') model = QSqlQueryModel() model.setQuery(query) self.cb = QComboBox(parent=self) self.cb.setModel(model) self.cb.setModelColumn(1) self.cb.setView(QTableView(self.cb)) self.cb.setGeometry(50, 50, 250, 50) self.cb.currentIndexChanged.connect(self.indexer) self.label = QLabel(parent=self) self.label.setGeometry(20, 200, 250, 50) self.cb.view().setMinimumWidth(500) self.cb.setEditable(True) self.completer = QCompleter() self.completer.setCaseSensitivity(False) self.cb.setCompleter(self.completer) self.completer.setModel(model) self.completer.setCompletionColumn(1) self.completer.setPopup(QTableView()) self.completer.popup().setMinimumWidth(500) self.completer.popup().setMinimumHeight(500) self.completer.activated.connect(self.activatedHandler) #@pyqtSlot(QObject) def activatedHandler(self, arg): pass def indexer(self, idx): self.label.setText('%d' % idx)
class FilteringComboBox(Widget): """Combination of QCombobox and QLineEdit with autocompletionself. Line edit and completer model is taken from QSqlTable mod Parameters: table (str): db table name containing data for combobox column (str): column name containing data for combobox color (str): 'rgb(r, g, b)' used for primary color font_size (int): default text font size in pt _model (QSqlTableModel): data model _col (int): display data model source coulumn _proxy (QSortFilterProxyModel): completer data model. _proxy.sourceModel() == _model _le (QLineEdit): QCombobox LineEdit Methods: createEditor(): (Widget): returns user input widgets value(): (str): returns user input text value setValue(value(str)): sets editor widget display value style(): (str): Returns CSS stylesheet string for input widget updateModel(): updates input widget model Args: table (str): db table name containing data for combobox column (str): column name containing data for combobox """ def __init__(self, parent, placeholderText, table, column, color='rgb(0,145,234)', image=''): self.table = table self.column = column self.color = color super().__init__(parent, placeholderText, image) self.updateModel() def createEditor(self): # setup data model self._model = QSqlTableModel() self._model.setTable(self.table) self._col = self._model.fieldIndex(self.column) # setup filter model for sorting and filtering self._proxy = QSortFilterProxyModel() self._proxy.setFilterCaseSensitivity(Qt.CaseInsensitive) self._proxy.setSourceModel(self._model) self._proxy.setFilterKeyColumn(self._col) # setup completer self._completer = QCompleter() self._completer.setModel(self._proxy) self._completer.setCompletionColumn(self._col) self._completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) # setup combobox editor = QComboBox() editor.setModel(self._proxy) editor.setModelColumn(self._col) editor.setEditable(True) editor.setFocusPolicy(Qt.StrongFocus) editor.setInsertPolicy(QComboBox.NoInsert) editor.setCompleter(self._completer) # setup connections editor.currentTextChanged[str].connect(self.onActivated) # setup editor appearence style = self.style() editor.setStyleSheet(style) editor.lineEdit().setStyleSheet(style) font = editor.font() self._completer.popup().setFont(font) return editor @pyqtSlot(str) def onActivated(self, text): print('combo_box filter text', text) if not text: # placeholder text displayed and label is not visible self._editor.setCurrentIndex(-1) if self.toggle: self._label.showLabel(False) else: # selected text is displayed and label is visible # self._editor.showPopup() # self._editor.lineEdit().setFocus() print('current copmpletion string', self._completer.currentCompletion()) self._proxy.setFilterFixedString(text) if self.toggle: self._label.showLabel(True) def style(self): """Returns stylesheet for editors Returns: style (str) """ style = """ QLineEdit {{ border: none; padding-bottom: 2px; border-bottom: 1px solid rgba(0,0,0,0.42); background-color: white; color: rgba(0,0,0,0.42); font-size: {font_size}pt;}} QLineEdit:editable {{ padding-bottom: 2px; border-bottom: 1px rgba(0,0,0,0.42); color: rgba(0,0,0,0.42);}} QLineEdit:disabled {{ border: none; padding-bottom: 2px; border-bottom: 1px rgba(0,0,0,0.42); color: rgba(0,0,0,0.38);}} QLineEdit:hover {{ padding-bottom: 2px; border-bottom: 2px solid rgba(0,0,0,0.6); color: rgba(0,0,0,0.54); }} QLineEdit:focus {{ padding-bottom: 2px; border-bottom: 2px solid {color}; color: rgba(0,0,0,0.87);}} QLineEdit:pressed {{ border-bottom: 2px {color}; font: bold; color: rgba(0,0,0,0.87)}} QComboBox {{ border: none; padding-bottom: 2px; font-size: {font_size}pt; }} QComboBox::down-arrow {{ image: url('dropdown.png'); background-color: white; border: 0px white; padding: 0px; margin: 0px; height:14px; width:14px;}} QComboBox::drop-down{{ subcontrol-position: center right; border: 0px; margin: 0px; }} QComboBox QAbstractItemView {{ font: {font_size};}} """.format(color=self.color, font_size=self._editor_font_size, dropdown=DROPDOWN_PNG) return style def updateModel(self): model = self._editor.model().sourceModel() col = self._editor.modelColumn() model.select() model.sort(col, Qt.AscendingOrder) def value(self): return self._editor.currentText() def setValue(self, value): self._editor.setCurrentText(value)
def init_toolbar(self): self.toolbar = QToolBar() self.toolbar.setFixedHeight(25) self.toolbar.setWindowTitle("Show") # text for the contextmenu # self.toolbar.setStyleSheet("QToolBar {border:0px}") # make it user defined? self.toolbar.setMovable(False) self.toolbar.setFloatable(False) # self.toolbar.setIconSize(QSize(20,20)) self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) spacer_start = QWidget() # aligns the first actions properly spacer_start.setFixedSize(QSize(10, 1)) self.toolbar.addWidget(spacer_start) favourite_view_icon = QIcon(gui_constants.STAR_BTN_PATH) favourite_view_action = QAction(favourite_view_icon, "Favorites", self) favourite_view_action.setToolTip("Show only favourite galleries") favourite_view_action.triggered.connect(self.favourite_display) # need lambda to pass extra args self.toolbar.addAction(favourite_view_action) catalog_view_icon = QIcon(gui_constants.HOME_BTN_PATH) catalog_view_action = QAction(catalog_view_icon, "Library", self) catalog_view_action.setToolTip("Show all your galleries") # catalog_view_action.setText("Catalog") catalog_view_action.triggered.connect(self.catalog_display) # need lambda to pass extra args self.toolbar.addAction(catalog_view_action) self.toolbar.addSeparator() gallery_menu = QMenu() gallery_action = QToolButton() gallery_action.setText("Gallery ") gallery_action.setPopupMode(QToolButton.InstantPopup) gallery_action.setToolTip("Contains various gallery related features") gallery_action.setMenu(gallery_menu) add_gallery_icon = QIcon(gui_constants.PLUS_PATH) gallery_action_add = QAction(add_gallery_icon, "Add gallery", self) gallery_action_add.triggered.connect(self.manga_list_view.SERIES_DIALOG.emit) gallery_action_add.setToolTip("Add a single gallery thoroughly") gallery_menu.addAction(gallery_action_add) add_more_action = QAction(add_gallery_icon, "Add galleries...", self) add_more_action.setStatusTip("Add galleries from different folders") add_more_action.triggered.connect(lambda: self.populate(True)) gallery_menu.addAction(add_more_action) populate_action = QAction(add_gallery_icon, "Populate from folder...", self) populate_action.setStatusTip("Populates the DB with galleries from a single folder") populate_action.triggered.connect(self.populate) gallery_menu.addAction(populate_action) gallery_menu.addSeparator() metadata_action = QAction("Get metadata for all galleries", self) metadata_action.triggered.connect(self.get_metadata) gallery_menu.addAction(metadata_action) self.toolbar.addWidget(gallery_action) self.toolbar.addSeparator() misc_action = QToolButton() misc_action.setText("Misc ") misc_action_menu = QMenu() misc_action.setMenu(misc_action_menu) misc_action.setPopupMode(QToolButton.InstantPopup) misc_action.setToolTip("Contains misc. features") misc_action_random = QAction("Open random gallery", misc_action_menu) misc_action_random.triggered.connect(self.manga_list_view.open_random_gallery) misc_action_menu.addAction(misc_action_random) duplicate_check_simple = QAction("Simple duplicate finder", misc_action_menu) duplicate_check_simple.triggered.connect(lambda: self.manga_list_view.duplicate_check()) misc_action_menu.addAction(duplicate_check_simple) self.toolbar.addWidget(misc_action) spacer_middle = QWidget() # aligns buttons to the right spacer_middle.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.toolbar.addWidget(spacer_middle) self.grid_toggle_g_icon = QIcon(gui_constants.GRID_PATH) self.grid_toggle_l_icon = QIcon(gui_constants.LIST_PATH) self.grid_toggle = QToolButton() if self.display.currentIndex() == self.m_l_view_index: self.grid_toggle.setIcon(self.grid_toggle_l_icon) else: self.grid_toggle.setIcon(self.grid_toggle_g_icon) self.grid_toggle.setObjectName("gridtoggle") self.grid_toggle.clicked.connect(self.toggle_view) self.toolbar.addWidget(self.grid_toggle) self.search_bar = misc.LineEdit() if gui_constants.SEARCH_AUTOCOMPLETE: completer = QCompleter(self) completer.setModel(self.manga_list_view.gallery_model) completer.setCaseSensitivity(Qt.CaseInsensitive) completer.setCompletionMode(QCompleter.PopupCompletion) completer.setCompletionRole(Qt.DisplayRole) completer.setCompletionColumn(gui_constants.TITLE) completer.setFilterMode(Qt.MatchContains) self.search_bar.setCompleter(completer) if gui_constants.SEARCH_ON_ENTER: self.search_bar.returnPressed.connect(lambda: self.search(self.search_bar.text())) else: self.search_bar.textChanged[str].connect(self.search) self.search_bar.setPlaceholderText("Search title, artist, namespace & tags") self.search_bar.setMinimumWidth(150) self.search_bar.setMaximumWidth(500) self.toolbar.addWidget(self.search_bar) self.toolbar.addSeparator() settings_icon = QIcon(gui_constants.SETTINGS_PATH) settings_action = QAction("Set&tings", self) settings_action.triggered.connect(self.settings) self.toolbar.addAction(settings_action) spacer_end = QWidget() # aligns About action properly spacer_end.setFixedSize(QSize(10, 1)) self.toolbar.addWidget(spacer_end) self.addToolBar(self.toolbar)
def init_toolbar(self): self.toolbar = QToolBar() self.toolbar.setFixedHeight(25) self.toolbar.setWindowTitle("Show") # text for the contextmenu #self.toolbar.setStyleSheet("QToolBar {border:0px}") # make it user defined? self.toolbar.setMovable(False) self.toolbar.setFloatable(False) #self.toolbar.setIconSize(QSize(20,20)) self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.toolbar.setIconSize(QSize(20,20)) spacer_start = QWidget() # aligns the first actions properly spacer_start.setFixedSize(QSize(10, 1)) self.toolbar.addWidget(spacer_start) self.favourite_btn = misc.ToolbarButton(self.toolbar, 'Favorites') self.toolbar.addWidget(self.favourite_btn) self.favourite_btn.clicked.connect(self.favourite_display) #need lambda to pass extra args self.library_btn = misc.ToolbarButton(self.toolbar, 'Library') self.toolbar.addWidget(self.library_btn) self.library_btn.clicked.connect(self.catalog_display) #need lambda to pass extra args self.library_btn.selected = True self.toolbar.addSeparator() gallery_menu = QMenu() gallery_action = QToolButton() gallery_action.setText('Gallery ') gallery_action.setPopupMode(QToolButton.InstantPopup) gallery_action.setToolTip('Contains various gallery related features') gallery_action.setMenu(gallery_menu) add_gallery_icon = QIcon(app_constants.PLUS_PATH) gallery_action_add = QAction(add_gallery_icon, "Add single gallery...", self) gallery_action_add.triggered.connect(self.manga_list_view.SERIES_DIALOG.emit) gallery_action_add.setToolTip('Add a single gallery thoroughly') gallery_menu.addAction(gallery_action_add) add_more_action = QAction(add_gallery_icon, "Add galleries...", self) add_more_action.setStatusTip('Add galleries from different folders') add_more_action.triggered.connect(lambda: self.populate(True)) gallery_menu.addAction(add_more_action) populate_action = QAction(add_gallery_icon, "Populate from directory/archive...", self) populate_action.setStatusTip('Populates the DB with galleries from a single folder or archive') populate_action.triggered.connect(self.populate) gallery_menu.addAction(populate_action) gallery_menu.addSeparator() metadata_action = QAction('Get metadata for all galleries', self) metadata_action.triggered.connect(self.get_metadata) gallery_menu.addAction(metadata_action) scan_galleries_action = QAction('Scan for new galleries', self) scan_galleries_action.triggered.connect(self.scan_for_new_galleries) scan_galleries_action.setStatusTip('Scan monitored folders for new galleries') gallery_menu.addAction(scan_galleries_action) gallery_action_random = gallery_menu.addAction("Open random gallery") gallery_action_random.triggered.connect(self.manga_list_view.open_random_gallery) self.toolbar.addWidget(gallery_action) misc_action = QToolButton() misc_action.setText('Tools ') misc_action_menu = QMenu() misc_action.setMenu(misc_action_menu) misc_action.setPopupMode(QToolButton.InstantPopup) misc_action.setToolTip("Contains misc. features") gallery_downloader = QAction("Gallery Downloader", misc_action_menu) gallery_downloader.triggered.connect(self.download_window.show) misc_action_menu.addAction(gallery_downloader) duplicate_check_simple = QAction("Simple Duplicate Finder", misc_action_menu) duplicate_check_simple.triggered.connect(lambda: self.manga_list_view.duplicate_check()) misc_action_menu.addAction(duplicate_check_simple) self.toolbar.addWidget(misc_action) spacer_middle = QWidget() # aligns buttons to the right spacer_middle.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.toolbar.addWidget(spacer_middle) sort_action = QToolButton() sort_action.setIcon(QIcon(app_constants.SORT_PATH)) sort_action.setMenu(misc.SortMenu(self.toolbar, self.manga_list_view)) sort_action.setPopupMode(QToolButton.InstantPopup) self.toolbar.addWidget(sort_action) self.grid_toggle_g_icon = QIcon(app_constants.GRID_PATH) self.grid_toggle_l_icon = QIcon(app_constants.LIST_PATH) self.grid_toggle = QToolButton() if self.display.currentIndex() == self.m_l_view_index: self.grid_toggle.setIcon(self.grid_toggle_l_icon) else: self.grid_toggle.setIcon(self.grid_toggle_g_icon) self.grid_toggle.setObjectName('gridtoggle') self.grid_toggle.clicked.connect(self.toggle_view) self.toolbar.addWidget(self.grid_toggle) spacer_mid2 = QWidget() spacer_mid2.setFixedSize(QSize(5, 1)) self.toolbar.addWidget(spacer_mid2) def set_search_case(b): app_constants.GALLERY_SEARCH_CASE = b settings.set(b, 'Application', 'gallery search case') settings.save() def set_search_strict(b): app_constants.GALLERY_SEARCH_STRICT = b settings.set(b, 'Application', 'gallery search strict') settings.save() self.search_bar = misc.LineEdit() search_options = self.search_bar.addAction(QIcon(app_constants.SEARCH_OPTIONS_PATH), QLineEdit.TrailingPosition) search_options_menu = QMenu(self) search_options.triggered.connect(lambda: search_options_menu.popup(QCursor.pos())) search_options.setMenu(search_options_menu) case_search_option = search_options_menu.addAction('Case Sensitive') case_search_option.setCheckable(True) case_search_option.setChecked(app_constants.GALLERY_SEARCH_CASE) case_search_option.toggled.connect(set_search_case) strict_search_option = search_options_menu.addAction('Match whole terms') strict_search_option.setCheckable(True) strict_search_option.setChecked(app_constants.GALLERY_SEARCH_STRICT) strict_search_option.toggled.connect(set_search_strict) self.search_bar.setObjectName('search_bar') self.search_timer = QTimer(self) self.search_timer.setSingleShot(True) self.search_timer.timeout.connect(lambda: self.search(self.search_bar.text())) self._search_cursor_pos = [0, 0] def set_cursor_pos(old, new): self._search_cursor_pos[0] = old self._search_cursor_pos[1] = new self.search_bar.cursorPositionChanged.connect(set_cursor_pos) if app_constants.SEARCH_AUTOCOMPLETE: completer = QCompleter(self) completer_view = misc.CompleterPopupView() completer.setPopup(completer_view) completer_view._setup() completer.setModel(self.manga_list_view.gallery_model) completer.setCaseSensitivity(Qt.CaseInsensitive) completer.setCompletionMode(QCompleter.PopupCompletion) completer.setCompletionRole(Qt.DisplayRole) completer.setCompletionColumn(app_constants.TITLE) completer.setFilterMode(Qt.MatchContains) self.search_bar.setCompleter(completer) self.search_bar.returnPressed.connect(lambda: self.search(self.search_bar.text())) if not app_constants.SEARCH_ON_ENTER: self.search_bar.textEdited.connect(lambda: self.search_timer.start(800)) self.search_bar.setPlaceholderText("Search title, artist, namespace & tags") self.search_bar.setMinimumWidth(150) self.search_bar.setMaximumWidth(500) self.search_bar.setFixedHeight(19) self.manga_list_view.sort_model.HISTORY_SEARCH_TERM.connect(lambda a: self.search_bar.setText(a)) self.toolbar.addWidget(self.search_bar) def search_history(_, back=True): # clicked signal passes a bool sort_model = self.manga_list_view.sort_model nav = sort_model.PREV if back else sort_model.NEXT history_term = sort_model.navigate_history(nav) if back: self.search_forward.setVisible(True) back = QShortcut(QKeySequence(QKeySequence.Back), self, lambda: search_history(None)) forward = QShortcut(QKeySequence(QKeySequence.Forward), self, lambda: search_history(None, False)) search_backbutton = QToolButton(self.toolbar) search_backbutton.setText(u'\u25C0') search_backbutton.setFixedWidth(15) search_backbutton.clicked.connect(search_history) self.search_backward = self.toolbar.addWidget(search_backbutton) self.search_backward.setVisible(False) search_forwardbutton = QToolButton(self.toolbar) search_forwardbutton.setText(u'\u25B6') search_forwardbutton.setFixedWidth(15) search_forwardbutton.clicked.connect(lambda: search_history(None, False)) self.search_forward = self.toolbar.addWidget(search_forwardbutton) self.search_forward.setVisible(False) spacer_end = QWidget() # aligns settings action properly spacer_end.setFixedSize(QSize(10, 1)) self.toolbar.addWidget(spacer_end) settings_act = QToolButton(self.toolbar) settings_act.setIcon(QIcon(app_constants.SETTINGS_PATH)) settings_act.clicked.connect(self.settings) self.toolbar.addWidget(settings_act) spacer_end2 = QWidget() # aligns About action properly spacer_end2.setFixedSize(QSize(5, 1)) self.toolbar.addWidget(spacer_end2) self.addToolBar(self.toolbar)
class ExtendedComboBox(QComboBox): def __init__(self, parent=None): super(ExtendedComboBox, self).__init__(parent) self.start = False self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) self.setStyleSheet(''' QListView {background-color:#AFEEEE} QListView::item:selected { color: red; background-color: lightgray; min-width: 1000px; } QComboBox { color:black;max-width: 500px; min-height: 40px;background-color:#AFEEEE} QComboBox QAbstractItemView::item { min-height: 150px;background-color:#AFEEEE} ''') # add a filter model to filter matching items self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.pFilterModel.setSourceModel(self.model()) # add a completer, which uses the filter model self.completer = QCompleter(self.pFilterModel, self) # always show all (filtered) completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) # connect signals self.lineEdit().textEdited.connect( self.pFilterModel.setFilterFixedString) self.completer.activated.connect(self.on_completer_activated) self.currentIndexChanged.connect(self.selectionchange) # on selection of an item from the completer, select the corresponding item from combobox def on_completer_activated(self, text): if text: index = self.findText(text) self.setCurrentIndex(index) self.activated[str].emit(self.itemText(index)) # on model change, update the models of the filter and completer as well def setModel(self, model): super(ExtendedComboBox, self).setModel(model) self.pFilterModel.setSourceModel(model) self.completer.setModel(self.pFilterModel) # on model column change, update the model column of the filter and completer as well def setModelColumn(self, column): self.completer.setCompletionColumn(column) self.pFilterModel.setFilterKeyColumn(column) super(ExtendedComboBox, self).setModelColumn(column) def selectionchange(self, i): if self.start == False: return global rows libros = [] txt = self.itemText(i) exPopup = ExamplePopup(txt, self) for i in rows: if txt == i['Título']: libros.append(i) exPopup.filltable(libros) exPopup.show() return elif txt == i['Autor']: libros.append(i) exPopup.filltable(libros) exPopup.show()
class ExtendedComboBox(QComboBox): isHidden = pyqtSignal() def __init__(self, gtoAction, parent=None): super(ExtendedComboBox, self).__init__(parent) self.gtomain = gtoAction.gtomain self.debug = gtoAction.debug self.info = gtoAction.info self.config = gtoAction.config self.tools = [] self.doQueryOnShow = False # speed up # // For performance reasons use this policy on large models # // or AdjustToMinimumContentsLengthWithIcon self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength) # // Improving Performance: It is possible to give the view hints # // about the data it is handling in order to improve its performance # // when displaying large numbers of items. One approach that can be taken # // for views that are intended to display items with equal sizes # // is to set the uniformItemSizes property to true. self.view().setUniformItemSizes(True) # // This property holds the layout mode for the items. When the mode is Batched, # // the items are laid out in batches of batchSize items, while processing events. # // This makes it possible to instantly view and interact with the visible items # // while the rest are being laid out. self.view().setLayoutMode(QListView.Batched) # // batchSize : int # // This property holds the number of items laid out in each batch # // if layoutMode is set to Batched. The default value is 100." try: self.setInsertPolicy(self.NoInsert) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) # add a filter model to filter matching items self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.pFilterModel.setSourceModel(self.model()) # add a completer, which uses the filter model self.completer = QCompleter(self.pFilterModel, self) # always show all (filtered) completions self.completer.setCompletionMode( QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) # connect signals self.lineEdit().textEdited.connect( self.pFilterModel.setFilterFixedString) self.lineEdit().textEdited.connect(self.correctInput) # corrct qt bug: style = "QWidget{font-size: " + str(self.fontInfo().pointSize( )) + "pt;font-family: " + self.fontInfo().family() + ";}" self.completer.popup().setStyleSheet(style) # stat vars self.query = None self.layer = None self.doQueryOnShow = False self.masterExpression = None self.child = None except Exception as e: self.info.err(e) def hideEvent(self, e): self.isHidden.emit() def showEvent(self, e): try: if self.doQueryOnShow: self.buildList() except: pass # on model change, update the models of the filter and completer as well def setModel(self, model): super(ExtendedComboBox, self).setModel(model) self.pFilterModel.setSourceModel(model) self.completer.setModel(self.pFilterModel) # on model column change, update the model column of the filter and completer as well def setModelColumn(self, column): self.completer.setCompletionColumn(column) self.pFilterModel.setFilterKeyColumn(column) super(ExtendedComboBox, self).setModelColumn(column) def setQuery(self, query): self.query = query self.tools = self.query.get('tools', []) self.doQueryOnShow = query.get('load', True) def Layer(self): return self.layer def setLayer(self, layer): self.layer = layer def Expression(self): return self.currentData() def Query(self): return self.query def setChild(self, cbo): try: self.child = cbo except Exception as e: self.info.err(e) # on selection of an item from the completer, select the corresponding item from combobox def on_completer_activated(self, text): try: if text is not None: index = self.findText(text, Qt.MatchContains) self.setCurrentIndex(index) if self.debug: self.info.log(self.objectName(), "index:", index) except Exception as e: self.info.err(e) def correctInput(self, text=None): try: if self.debug: self.info.log("correctInput", text) if text is not None: index = self.findText(text, Qt.MatchContains) if self.debug: self.info.log(self.objectName(), "index:", index) if index == -1: self.setCurrentText(text[:-1]) self.on_completer_activated(text[:-1]) index = self.findText(self.currentText(), Qt.MatchExactly) if index == -1: self.resetFilter(False) else: if index == self.currentIndex(): self.indexChanged(index) else: self.setCurrentIndex(index) except Exception as e: self.info.err(e) def resetFilter(self, resetSelf): try: if self.debug: self.info.log(self.objectName(), "reset") if resetSelf: self.setMasterExpression(None) if self.child: self.child.resetFilter(True) except Exception as e: self.info.err(e) def setMasterExpression(self, masterExpression): try: if self.debug: self.info.log(self.objectName(), "setMasterExpression", self.masterExpression, "to", masterExpression) self.masterExpression = masterExpression if self.doQueryOnShow: self.buildList() else: if self.masterExpression is None: self.clear() else: self.buildList() except Exception as e: self.info.err(e) def buildList(self): if self.debug: self.info.log(self.objectName(), "buildList", self.query) self.setCurrentText('Lade...') self.setEnabled(False) self.repaint() if self.debug: self.info.log(self.objectName(), "excecuteQuery") try: self.currentIndexChanged.disconnect() except: pass try: self.clear() req = qgis.core.QgsFeatureRequest() req.setFlags(qgis.core.QgsFeatureRequest.NoGeometry) req.setSubsetOfAttributes(self.query['fields'], self.layer.fields()) req.addOrderBy(self.query['fields'][0], True, True) if self.masterExpression is not None: req.setFilterExpression(self.masterExpression) values = [] features = self.layer.getFeatures(req) for feat in features: displayStr = '' expr = '' for fn in self.query['fields']: idx = self.layer.fields().indexFromName(fn) if idx != -1: value = feat[fn] if isinstance(value, str): value = "'" + value + "'" if expr == '': expr = expr + '"{0}"='.format(fn) + str(value) else: expr = expr + ' AND ' + '"{0}"='.format(fn) + str( value) displayStr = displayStr + str(feat[fn]) else: displayStr = displayStr + str(fn) if not displayStr in values and displayStr.strip() != '': values.append(displayStr) self.addItem(displayStr, expr) if self.debug: self.info.log(self.objectName(), "items:", self.count()) self.setCurrentIndex(-1) self.setSizeAdjustPolicy(self.AdjustToContents) self.setEnabled(True) self.completer.activated.connect(self.on_completer_activated) self.currentIndexChanged.connect(self.indexChanged) if self.masterExpression is not None: if self.count() == 1: if self.debug: self.info.log(self.objectName(), "self.setCurrentIndex(0)", "self.masterExpression", self.masterExpression) self.setCurrentIndex(0) else: self.setCurrentIndex(-1) except Exception as e: self.info.err(e) def indexChanged(self, index): try: expr = self.currentData() if self.debug: self.info.log(self.objectName(), "cbo Expr:", expr) if self.masterExpression is not None: if expr is not None: expr = self.masterExpression + ' AND ' + expr else: expr = self.masterExpression self.info.log(self.objectName(), "Expr:", expr) if expr is not None: self.layer.selectByExpression(expr) self.gtomain.runcmd(self.tools) if self.child is not None: if self.debug: self.child.objectName() self.child.setMasterExpression(expr) except Exception as e: self.info.err(e)
class ExtendedComboBox(QComboBox): def __init__(self, parent=None): super(ExtendedComboBox, self).__init__(parent) self.setFocusPolicy(QtCore.Qt.StrongFocus) self.setEditable(True) # add a filter model to filter matching items self.pFilterModel = QtCore.QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) self.pFilterModel.setSourceModel(self.model()) # add a completer, which uses the filter model self.completer = QCompleter(self.pFilterModel, self) # always show all (filtered) completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) # connect signals self.lineEdit().textEdited[unicode].connect( self.pFilterModel.setFilterFixedString) self.completer.activated.connect(self.on_completer_activated) # on selection of an item from the completer, select the corresponding item from combobox def on_completer_activated(self, text): if text: index = self.findText(text) self.setCurrentIndex(index) # 當lose focus時 判斷combobox輸入的text有沒有在item中 def event(self, event): if event.type() == QtCore.QEvent.FocusOut: text = self.currentText() if text: index = self.findText(text) if index == -1: self.setStyleSheet('QComboBox{border:1px solid red}') else: self.setStyleSheet('QComboBox{border:1px solid gray}') return QComboBox.event(self, event) # on model change, update the models of the filter and completer as well def setModel(self, model): super(ExtendedComboBox, self).setModel(model) self.pFilterModel.setSourceModel(model) self.completer.setModel(self.pFilterModel) # on model column change, update the model column of the filter and completer as well def setModelColumn(self, column): self.completer.setCompletionColumn(column) self.pFilterModel.setFilterKeyColumn(column) super(ExtendedComboBox, self).setModelColumn(column)
class SecuritiesSelector(QComboBox): class SingletonStorage(metaclass=ThreadSafeSingleton): def __init__(self): self.__stock_list = [] self.__lock = threading.Lock() self.__refresh_thread = None def get_stock_list(self) -> list: with self.__lock: return self.__stock_list.copy() def refresh_stock_list(self, sas_if: sasIF): with self.__lock: if self.__refresh_thread is None: self.__refresh_thread = threading.Thread( target=self.__update_stock_list, name='SecuritiesSelectorRefresh', args=(sas_if, )) self.__refresh_thread.start() def __update_stock_list(self, sas_if: sasIF): stock_list = sas_if.sas_get_stock_info_list() with self.__lock: self.__stock_list = stock_list self.__refresh_thread = None def __init__(self, sas_if: sasIF, parent=None): super(SecuritiesSelector, self).__init__(parent) self.__sas_if: sasIF = sas_if self.__timer = QTimer() self.__timer.setInterval(1000) self.__timer.timeout.connect(self.on_timer) if self.__sas_if is not None: # Timer for update stock list self.__timer.start() self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) self.addItem('Loading...') # add a filter model to filter matching items self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.pFilterModel.setSourceModel(self.model()) # add a completer, which uses the filter model self.completer = QCompleter(self.pFilterModel, self) # always show all (filtered) completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) # connect signals self.lineEdit().textEdited.connect( self.pFilterModel.setFilterFixedString) self.completer.activated.connect(self.on_completer_activated) # def keyPressEvent(self, event): # super(SecuritiesSelector, self).keyPressEvent(event) # key = event.key() # if event.key() == QtCore.Qt.Key_Return: # print('return key pressed') # else: # print('key pressed: %i' % key) def on_timer(self): # Check stock list ready and update combobox # if self.__data_utility is not None and \ # self.__data_utility.stock_cache_ready(): # stock_list = self.__data_utility.get_stock_list() stock_list = SecuritiesSelector.SingletonStorage().get_stock_list() if len(stock_list) > 0: self.clear() for stock_identity, stock_name in stock_list: self.addItem(stock_identity + ' | ' + stock_name, stock_identity) self.__timer.stop() else: SecuritiesSelector.SingletonStorage().refresh_stock_list( self.__sas_if) # on selection of an item from the completer, select the corresponding item from combobox def on_completer_activated(self, text: str): if self.__data_utility is None or \ not self.__data_utility.stock_cache_ready(): return if text is not None and text != '': self.__data_utility.guess_securities(text) index = self.findData(text) self.setCurrentIndex(index) self.activated[str].emit(self.itemText(index)) def setModel(self, model): super(QComboBox, self).setModel(model) self.pFilterModel.setSourceModel(model) self.completer.setModel(self.pFilterModel) def setModelColumn(self, column): self.completer.setCompletionColumn(column) self.pFilterModel.setFilterKeyColumn(column) super(QComboBox, self).setModelColumn(column) def select_security(self, security: str, linkage: bool): for index in range(self.count()): stock_identity = self.itemData(index) if stock_identity == security: self.setCurrentIndex(index) break def get_input_securities(self) -> str: # select_index = self.currentIndex() # if select_index >= 0: # return self.itemData(select_index) # else: input_securities = self.currentText() if '|' in input_securities: input_securities = input_securities.split('|')[0].strip() return input_securities.strip() def get_select_securities(self) -> str: select_index = self.currentIndex() return self.itemData(select_index) if select_index >= 0 else ''
class DiscoveryPlugin: def __init__(self, _iface): # Save reference to the QGIS interface self.iface = _iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # Variables to facilitate delayed queries and database connection management self.icon_loading = ':plugins/_Discovery_sqlite/icons/loading.gif' self.db_timer = QTimer() self.line_edit_timer = QTimer() self.line_edit_timer.setSingleShot(True) self.line_edit_timer.timeout.connect(self.reset_line_edit_after_move) self.next_query_time = None self.last_query_time = time.time() self.db_conn = None self.search_delay = 0.5 # s self.query_sql = '' self.query_text = '' self.query_dict = {} self.db_idle_time = 60.0 # s self.search_results = [] self.tool_bar = None self.search_line_edit = None self.completer = None self.marker = QgsVertexMarker(iface.mapCanvas()) self.marker.setIconSize(15) self.marker.setPenWidth(2) self.marker.setColor(QColor(226, 27, 28)) #51,160,44)) self.marker.setZValue(11) self.marker.setVisible(False) self.marker2 = QgsVertexMarker(iface.mapCanvas()) self.marker2.setIconSize(16) self.marker2.setPenWidth(4) self.marker2.setColor(QColor(255, 255, 255, 200)) self.marker2.setZValue(10) self.marker2.setVisible(False) def initGui(self): # Create a new toolbar self.tool_bar = self.iface.addToolBar(u'Панель поиска') self.tool_bar.setObjectName('Discovery_sqlite_Plugin') # Add search edit box self.search_line_edit = QgsFilterLineEdit() self.search_line_edit.setSelectOnFocus(True) self.search_line_edit.setShowSearchIcon(True) self.search_line_edit.setPlaceholderText( u'Поиск адреса или участка...') # self.search_line_edit.setMaximumWidth(768) self.tool_bar.addWidget(self.search_line_edit) # loading indicator self.load_movie = QMovie() self.label_load = QLabel() self.tool_bar.addWidget(self.label_load) # Set up the completer model = QStandardItemModel() self.completer = QCompleter([]) # Initialise with en empty list self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setMaxVisibleItems(30) self.completer.setModelSorting( QCompleter.UnsortedModel) # Sorting done in PostGIS self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion ) # Show all fetched possibilities self.completer.setModel(model) tableView = QTableView() tableView.verticalHeader().setVisible(False) tableView.horizontalHeader().setVisible(False) tableView.setSelectionBehavior(QTableView.SelectRows) tableView.setShowGrid(False) # tableView.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents) tableView.verticalHeader().setSectionResizeMode(QHeaderView.Fixed) fontsize = QFontMetrics( tableView.verticalHeader().font()).height() + 2 #font size tableView.verticalHeader().setDefaultSectionSize( fontsize) #font size 15 tableView.horizontalHeader().setSectionResizeMode( QHeaderView.ResizeToContents) tableView.horizontalHeader().setStretchLastSection(True) tableView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.completer.setCompletionColumn(0) self.completer.setPopup(tableView) self.completer.activated[QModelIndex].connect(self.on_result_selected) self.completer.highlighted[QModelIndex].connect( self.on_result_highlighted) self.search_line_edit.setCompleter(self.completer) # Connect any signals self.search_line_edit.textEdited.connect(self.on_search_text_changed) self.search_line_edit.returnPressed.connect(self.returnPressed) self.read_config() # Search results self.search_results = [] # Set up a timer to periodically perform db queries as required self.db_timer.timeout.connect(self.schedule_search) def read_config(self): qstring = '' self.data = self.settings_path() try: for line in self.data[1:]: words = line.split(';') postgissearchcolumn = words[2].strip() postgistable = words[0].strip() geomcolumn = words[3].strip() layername = words[4].strip() isSelect = int(words[5].strip()) connection = sqlite3.connect(os.path.join(self.dbfile.strip())) cur = connection.cursor() qstring = u'select {2} from {0} where length({1})>0 LIMIT 1'.format( postgistable, postgissearchcolumn, geomcolumn) cur.execute(qstring) connection.close() self.make_enabled(True) # assume the config is invalid first except Exception as E: print(E) self.make_enabled(False) # включить или выключить поисковую строку в зависимости от результата проверки настроек def make_enabled(self, enabled): self.search_line_edit.setEnabled(enabled) self.search_line_edit.setPlaceholderText( u"Поиск адреса или участка..." if enabled else u"Поиск отключен: проверьте конфигурацию") def settings_path(self): p = os.path.join(self.plugin_dir, 'layers.ini') f = codecs.open(p, 'r', encoding='cp1251') data = f.readlines() dbfileline = data[0] if dbfileline[:2] == u'\\\\': self.dbfile = dbfileline elif dbfileline[1] == u':': self.dbfile = dbfileline else: self.dbfile = os.path.join(self.plugin_dir, dbfileline) f.close() return data def unload(self): self.db_timer.stop() self.db_timer.timeout.disconnect(self.schedule_search) self.completer.highlighted[QModelIndex].disconnect( self.on_result_highlighted) self.completer.activated[QModelIndex].disconnect( self.on_result_selected) self.search_line_edit.textEdited.disconnect( self.on_search_text_changed) self.search_line_edit.returnPressed.disconnect(self.returnPressed) self.tool_bar.clear() # Clear all actions self.iface.mainWindow().removeToolBar(self.tool_bar) def clear_suggestions(self): model = self.completer.model() model.clear() # model.setStringList([]) def returnPressed(self): if self.completer.popup().isHidden(): self.do_search(self.search_line_edit.text()) # def setLoading(self, isLoading): # if self.label_load is None: # return # if isLoading: # load_movie = QMovie() # load_movie.setFileName(self.icon_loading) # self.label_load.setMovie(load_movie) # load_movie.start() # else: # load_movie = QMovie() # load_movie.stop() # self.label_load.setMovie(load_movie) def schedule_search(self): if self.next_query_time is not None and self.next_query_time < time.time( ): self.next_query_time = None # Prevent this query from being repeated self.last_query_time = time.time() self.do_search(self.search_line_edit.text()) self.db_timer.stop() # self.setLoading(False) self.search_line_edit.setShowSpinner(False) else: # self.setLoading(True) self.search_line_edit.setShowSpinner(True) if time.time() > self.last_query_time + self.db_idle_time: self.db_conn = None # def on_search_text_changed(self, new_search_text): def on_search_text_changed(self, new_search_text): # self.setLoading(False) self.search_line_edit.setShowSpinner(False) if len(new_search_text) < 3: self.db_timer.stop() self.clear_suggestions() return self.db_timer.start(300) self.next_query_time = time.time() + self.search_delay def do_search(self, new_search_text): if len(new_search_text) < 3: self.clear_suggestions() return self.clear_suggestions() self.query_text = new_search_text self.search_results = [] self.suggestions = [] for index, line in enumerate(self.data[1:]): curline_layer = line words = curline_layer.split(';') searchcolumn = words[2].strip() # поле со значением для поиска postgistable = words[0].strip() # таблица geomcolumn = words[3].strip() # поле с геометрией layername = words[4].strip( ) # имя слоя в легенде для соответствий и выделения isSelect = int( words[5].strip()) # выделять ли объект в слое layername descript = words[1].strip( ) # описание. Выводится в списке результатов query_text, query_dict = self.get_search_sql( new_search_text, searchcolumn, postgistable) query_sql = query_text query_dict = query_dict self.perform_search(query_sql, query_dict, descript, postgistable, layername, isSelect, searchcolumn) # QStringList - просто одна строка в выводе # if len(self.suggestions) > 0: # model = self.completer.model() # model.setStringList(self.suggestions) # print(model) # self.completer.complete() if len(self.suggestions) > 0: # model = self.completer.model() model = QStandardItemModel() font = QFont() font.setItalic(True) font.setPointSize(7) # заполняем модель for i, line in enumerate(self.suggestions): #icon pixmap = QPixmap(':plugins/_Discovery_sqlite/icons/' + line[2] + '.png') pixmap = pixmap.scaledToHeight(10) pixmap = pixmap.scaledToWidth(10) # itemImage = QStandardItem() # itemImage.setData(pixmap, Qt.DecorationRole) # model.setItem(i, 0, itemImage) itemLayer = QStandardItem(u"{1}[{0}]".format( line[1], u' ' * 50)) itemLayer.setFont(font) itemValue = QStandardItem(line[0]) itemValue.setData(pixmap, Qt.DecorationRole) model.setItem(i, 0, itemValue) model.setItem(i, 1, itemLayer) self.completer.setModel(model) self.completer.complete() else: model = self.completer.model() # self.suggestions.append(u"<Не найдено>") # для QStringList # model.setStringList(self.suggestions) # для QStringList model.setItem( 0, 0, QStandardItem('<Не найдено>')) # для QStandardItemModel self.completer.complete() def perform_search(self, query_sql, query_dict, descript, tablename, layername, isSelect, searchcolumn): cur = self.get_db_cur() cur.execute(query_sql, query_dict) for row in cur.fetchall(): geom, suggestion_text = row[0], row[1] self.search_results.append(geom) self.suggestions.append([ suggestion_text, descript, tablename, layername, isSelect, searchcolumn ]) # self.suggestions.append(suggestion_text) # для QStringList def get_search_sql(self, search_text, search_column, table): wildcarded_search_string = '' for part in search_text.split(): wildcarded_search_string += '%' + part #.lower() wildcarded_search_string += '%' wildcarded_search_string = wildcarded_search_string query_dict = {'search_text': wildcarded_search_string} # wildcarded_search_string = wildcarded_search_string.encode('cp1251') query_text = u"SELECT WKT_GEOMETRY AS geom, {0} AS suggestion_string FROM {1} WHERE ({0}) LIKE '{2}' ORDER BY {0} LIMIT 1000".format( search_column, table, wildcarded_search_string) # query_text = query_text.decode('cp1251') return query_text, query_dict def on_result_selected(self, result_index): resultIndexRow = result_index.row() if len(self.search_results) < 1: self.search_line_edit.setPlaceholderText(u'') return # What to do when the user makes a selection geometry_text = self.search_results[resultIndexRow] location_geom = QgsGeometry.fromWkt(geometry_text) canvas = self.iface.mapCanvas() # dst_srid = canvas.mapRenderer().destinationCrs().authid() # Ensure the geometry from the DB is reprojected to the same SRID as the map canvas location_centroid = location_geom.centroid().asPoint() result_text = self.completer.completionModel().index( resultIndexRow, 0).data() if self.suggestions[resultIndexRow][2] in ( u"adres_nd") and location_geom.type() == 0: # point self.show_marker(location_centroid) self.iface.mapCanvas().setExtent(location_geom.boundingBox()) self.iface.mapCanvas().zoomScale(1000) layer_build = self.find_layer(u"Здания") if layer_build != None: layer_build.selectByIds([]) for feat in layer_build.getFeatures( QgsFeatureRequest().setFilterRect( QgsRectangle(self.iface.mapCanvas().extent()))): if location_geom.intersects(feat.geometry()): # self.show_marker_feature(feat.geometry()) self.iface.setActiveLayer(layer_build) layer_build.selectByIds([feat.id()]) layer_build.triggerRepaint() return else: #not point layername = self.suggestions[resultIndexRow][3] isSelect = self.suggestions[resultIndexRow][4] searchcolumn = self.suggestions[resultIndexRow][5] box = location_geom.boundingBox() if box.height() > box.width(): max = box.height() else: max = box.width() box.grow(max * 0.10) self.iface.mapCanvas().setExtent(box) if isSelect == 1: selLayer = self.find_layer(layername) if selLayer is not None: for feat in selLayer.getFeatures( QgsFeatureRequest().setFilterRect(box)): # print(feat[searchcolumn], str(result_text).strip()) try: if str(feat[searchcolumn]) == str( result_text).strip(): self.iface.setActiveLayer(selLayer) selLayer.selectByIds([feat.id()]) selLayer.triggerRepaint() break except Exception as E: print(E) break self.show_marker_feature(location_geom) canvas.refresh() self.line_edit_timer.start(0) # self.db_timer.stop() def get_db_cur(self): # Create a new new connection if required if self.db_conn is None: self.db_conn = sqlite3.connect(os.path.join(self.dbfile.strip())) return self.db_conn.cursor() def on_result_highlighted(self, result_idx): self.line_edit_timer.start(0) def reset_line_edit_after_move(self): self.search_line_edit.setText(self.query_text) def find_layer(self, layer_name): for search_layer in self.iface.mapCanvas().layers(): if search_layer.name() == layer_name: return search_layer return None def show_marker(self, point): for m in [self.marker, self.marker2]: m.setCenter(point) m.setOpacity(1.0) m.setVisible(True) QTimer.singleShot(4000, self.hide_marker) def hide_marker(self): opacity = self.marker.opacity() if opacity > 0.: # produce a fade out effect opacity -= 0.1 self.marker.setOpacity(opacity) self.marker2.setOpacity(opacity) QTimer.singleShot(100, self.hide_marker) else: self.marker.setVisible(False) self.marker2.setVisible(False) def show_marker_feature(self, geom): if geom.type() == 2: #poly self.r = QgsRubberBand(iface.mapCanvas(), True) elif geom.type() == 1: #line self.r = QgsRubberBand(iface.mapCanvas(), False) self.r.setToGeometry(geom, None) self.r.setColor(QColor(255, 0, 0, 200)) self.r.setFillColor(QColor(255, 0, 0, 50)) self.r.setWidth(2) self.r.setZValue(9) QTimer.singleShot(4000, self.hide_marker_feature) def hide_marker_feature(self): opacity = self.r.opacity() if opacity > 0.: # produce a fade out effect opacity -= 0.1 self.r.setOpacity(opacity) QTimer.singleShot(100, self.hide_marker_feature) else: iface.mapCanvas().scene().removeItem(self.r)