diff --git a/README.md b/README.md index f4e5bb7b..8f7193d3 100644 --- a/README.md +++ b/README.md @@ -207,7 +207,7 @@ name that should be used to create that icon! Once installed, run `qta-browser` from a shell to start the browser. -![QtAwesomeIconbrowser](qtawesome-browser.png) +![qta-browser](qtawesome-browser.png) ## License diff --git a/qtawesome-browser.png b/qtawesome-browser.png index 35cd3ba8..93ae1cdc 100644 Binary files a/qtawesome-browser.png and b/qtawesome-browser.png differ diff --git a/qtawesome/icon_browser.py b/qtawesome/icon_browser.py index 7d4e2331..fcff5c64 100644 --- a/qtawesome/icon_browser.py +++ b/qtawesome/icon_browser.py @@ -8,7 +8,8 @@ # TODO: Set icon colour and copy code with color kwarg -VIEW_COLUMNS = 5 +DEFAULT_VIEW_COLUMNS = 5 +VIEW_COLUMNS_OPTIONS = [5, 8, 10, 15, 20, 25, 30] AUTO_SEARCH_TIMEOUT = 500 ALL_COLLECTIONS = 'All' @@ -22,8 +23,6 @@ class IconBrowser(QtWidgets.QMainWindow): def __init__(self): super().__init__() - self.setMinimumSize(400, 300) - self.setWindowTitle('QtAwesome Icon Browser') qtawesome._instance() fontMaps = qtawesome._resource['iconic'].charmap @@ -33,6 +32,10 @@ def __init__(self): for iconName in fontData: iconNames.append('%s.%s' % (fontCollection, iconName)) + self.setMinimumSize(300, 300) + self.setWindowTitle('QtAwesome Icon Browser') + self.setWindowIcon(qtawesome.icon("fa5s.icons")) + self._filterTimer = QtCore.QTimer(self) self._filterTimer.setSingleShot(True) self._filterTimer.setInterval(AUTO_SEARCH_TIMEOUT) @@ -45,63 +48,107 @@ def __init__(self): self._proxyModel.setSourceModel(model) self._proxyModel.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) - self._listView = IconListView(self) + self._listView = IconListView(DEFAULT_VIEW_COLUMNS, parent=self) self._listView.setUniformItemSizes(True) self._listView.setViewMode(QtWidgets.QListView.IconMode) self._listView.setModel(self._proxyModel) self._listView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self._listView.doubleClicked.connect(self._copyIconText) - self._listView.selectionModel().selectionChanged.connect(self._updateNameField) - - self._lineEdit = QtWidgets.QLineEdit(self) - self._lineEdit.setAlignment(QtCore.Qt.AlignCenter) - self._lineEdit.textChanged.connect(self._triggerDelayedUpdate) - self._lineEdit.returnPressed.connect(self._triggerImmediateUpdate) - - self._comboBox = QtWidgets.QComboBox(self) - self._comboBox.setMinimumWidth(75) - self._comboBox.currentIndexChanged.connect(self._triggerImmediateUpdate) - self._comboBox.addItems([ALL_COLLECTIONS] + sorted(fontMaps.keys())) - - lyt = QtWidgets.QHBoxLayout() - lyt.setContentsMargins(0, 0, 0, 0) - lyt.addWidget(self._comboBox) - lyt.addWidget(self._lineEdit) - self._combo_style = QtWidgets.QComboBox(self) - self._combo_style.addItems([ - qtawesome.styles.DEFAULT_DARK_PALETTE, - qtawesome.styles.DEFAULT_LIGHT_PALETTE]) - self._combo_style.currentTextChanged.connect(self._updateStyle) - lyt.addWidget(self._combo_style) - - searchBarFrame = QtWidgets.QFrame(self) - searchBarFrame.setLayout(lyt) + self._listView.selectionModel().selectionChanged.connect( + self._updateNameField + ) + + toolbar = QtWidgets.QHBoxLayout() + + # Filter section + self._comboFont = QtWidgets.QComboBox(self) + self._comboFont.setToolTip( + "Select the font prefix whose icons will " + "be included in the filtering." + ) + self._comboFont.setMaximumWidth(75) + self._comboFont.addItems([ALL_COLLECTIONS] + sorted(fontMaps.keys())) + self._comboFont.currentIndexChanged.connect( + self._triggerImmediateUpdate + ) + toolbar.addWidget(self._comboFont) + + self._lineEditFilter = QtWidgets.QLineEdit(self) + self._lineEditFilter.setToolTip("Filter icons by name") + self._lineEditFilter.setMaximumWidth(200) + self._lineEditFilter.setToolTip("Filter icons by name") + self._lineEditFilter.setAlignment(QtCore.Qt.AlignLeft) + self._lineEditFilter.textChanged.connect(self._triggerDelayedUpdate) + self._lineEditFilter.returnPressed.connect( + self._triggerImmediateUpdate + ) + self._lineEditFilter.setClearButtonEnabled(True) + toolbar.addWidget(self._lineEditFilter, stretch=10) + # Icon name section self._nameField = QtWidgets.QLineEdit(self) + self._nameField.setPlaceholderText( + "Full identifier of the currently selected icon" + ) self._nameField.setAlignment(QtCore.Qt.AlignCenter) self._nameField.setReadOnly(True) + self._nameField.setMaximumWidth(250) + fnt = self._nameField.font() + fnt.setFamily("monospace") + fnt.setBold(True) + self._nameField.setFont(fnt) + toolbar.addWidget(self._nameField, stretch=10) self._copyButton = QtWidgets.QPushButton('Copy Name', self) + self._copyButton.setToolTip( + "Copy selected icon full identifier to the clipboard" + ) self._copyButton.clicked.connect(self._copyIconText) + self._copyButton.setDisabled(True) + toolbar.addWidget(self._copyButton) + toolbar.addStretch(1) + + # Style section + self._comboStyle = QtWidgets.QComboBox(self) + self._comboStyle.setToolTip( + "Select color palette for the icons and the icon browser" + ) + self._comboStyle.addItem(qtawesome.styles.DEFAULT_DARK_PALETTE, 0) + self._comboStyle.addItem(qtawesome.styles.DEFAULT_LIGHT_PALETTE, 1) + self._comboStyle.currentTextChanged.connect(self._updateStyle) + toolbar.addWidget(self._comboStyle) + + # Display (columns number) section + self._comboColumns = QtWidgets.QComboBox(self) + self._comboColumns.setToolTip( + "Select number of columns the icons list is showing" + ) + for num_columns in VIEW_COLUMNS_OPTIONS: + self._comboColumns.addItem(str(num_columns), num_columns) + self._comboColumns.setCurrentIndex( + self._comboColumns.findData(DEFAULT_VIEW_COLUMNS) + ) + self._comboColumns.currentTextChanged.connect(self._updateColumns) + toolbar.addWidget(self._comboColumns) + # Layout lyt = QtWidgets.QVBoxLayout() - lyt.addWidget(searchBarFrame) + lyt.addLayout(toolbar) lyt.addWidget(self._listView) - lyt.addWidget(self._nameField) - lyt.addWidget(self._copyButton) frame = QtWidgets.QFrame(self) frame.setLayout(lyt) self.setCentralWidget(frame) - self.setTabOrder(self._comboBox, self._lineEdit) - self.setTabOrder(self._lineEdit, self._combo_style) - self.setTabOrder(self._combo_style, self._listView) + self.setTabOrder(self._comboFont, self._lineEditFilter) + self.setTabOrder(self._lineEditFilter, self._comboStyle) + self.setTabOrder(self._comboStyle, self._listView) self.setTabOrder(self._listView, self._nameField) self.setTabOrder(self._nameField, self._copyButton) - self.setTabOrder(self._copyButton, self._comboBox) + self.setTabOrder(self._copyButton, self._comboFont) + # Shortcuts QtWidgets.QShortcut( QtGui.QKeySequence(QtCore.Qt.Key_Return), self, @@ -110,10 +157,10 @@ def __init__(self): QtWidgets.QShortcut( QtGui.QKeySequence("Ctrl+F"), self, - self._lineEdit.setFocus, + self._lineEditFilter.setFocus, ) - self._lineEdit.setFocus() + self._lineEditFilter.setFocus() geo = self.geometry() @@ -130,6 +177,7 @@ def __init__(self): geo.moveCenter(centerPoint) self.setGeometry(geo) + self._updateStyle(self._comboStyle.currentText()) def _updateStyle(self, text: str): _app = QtWidgets.QApplication.instance() @@ -140,6 +188,9 @@ def _updateStyle(self, text: str): qtawesome.reset_cache() qtawesome.light(_app) + def _updateColumns(self): + self._listView.setColumns(self._comboColumns.currentData()) + def _updateFilter(self): """ Update the string used for filtering in the proxy model with the @@ -147,11 +198,11 @@ def _updateFilter(self): """ reString = "" - group = self._comboBox.currentText() + group = self._comboFont.currentText() if group != ALL_COLLECTIONS: reString += r"^%s\." % group - searchTerm = self._lineEdit.text() + searchTerm = self._lineEditFilter.text() if searchTerm: reString += ".*%s.*$" % searchTerm @@ -196,8 +247,11 @@ def _updateNameField(self): indexes = self._listView.selectedIndexes() if not indexes: self._nameField.setText("") - else: - self._nameField.setText(indexes[0].data()) + self._copyButton.setDisabled(True) + return + + self._nameField.setText(indexes[0].data()) + self._copyButton.setDisabled(False) class IconListView(QtWidgets.QListView): @@ -206,23 +260,28 @@ class IconListView(QtWidgets.QListView): columns are always drawn. """ - def __init__(self, parent=None): + def __init__(self, columns, parent=None): super().__init__(parent) + self._columns = columns self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) - def resizeEvent(self, event): + def setColumns(self, cols): + """ + Set columns number and resize. """ - Re-implemented to re-calculate the grid size to provide scaling icons + self._columns = cols + self._resize() - Parameters - ---------- - event : QtCore.QEvent + def _resize(self): + """ + Set grid and icon size taking into account the number of columns. """ + width = self.viewport().width() - 30 # The minus 30 above ensures we don't end up with an item width that # can't be drawn the expected number of times across the view without # being wrapped. Without this, the view can flicker during resize - tileWidth = width / VIEW_COLUMNS + tileWidth = width / self._columns iconWidth = int(tileWidth * 0.8) # tileWidth needs to be an integer for setGridSize tileWidth = int(tileWidth) @@ -230,6 +289,11 @@ def resizeEvent(self, event): self.setGridSize(QtCore.QSize(tileWidth, tileWidth)) self.setIconSize(QtCore.QSize(iconWidth, iconWidth)) + def resizeEvent(self, event): + """ + Re-implemented to resize view following number of columns available. + """ + self._resize() return super().resizeEvent(event) diff --git a/qtawesome/tests/test_icon_browser.py b/qtawesome/tests/test_icon_browser.py index ae16c416..8e8c89c2 100644 --- a/qtawesome/tests/test_icon_browser.py +++ b/qtawesome/tests/test_icon_browser.py @@ -44,8 +44,8 @@ def test_copy(qtbot, browser): assert clipboard.text() == "" # Enter a search term and press enter - qtbot.keyClicks(browser._lineEdit, 'google') - qtbot.keyPress(browser._lineEdit, QtCore.Qt.Key_Enter) + qtbot.keyClicks(browser._lineEditFilter, 'penguin') + qtbot.keyPress(browser._lineEditFilter, QtCore.Qt.Key_Enter) # TODO: Figure out how to do this via a qtbot.mouseClick call # Select the first item in the list @@ -56,24 +56,39 @@ def test_copy(qtbot, browser): # Click the copy button qtbot.mouseClick(browser._copyButton, QtCore.Qt.LeftButton) - assert "google" in clipboard.text() + assert "penguin" in clipboard.text() def test_filter(qtbot, browser): """ - Ensure the filter UX works + Ensure the filter UX works when searching for `penguin` + """ + initRowCount = browser._listView.model().rowCount() + assert initRowCount > 0 + + # Enter a search term and click + qtbot.keyClicks(browser._lineEditFilter, 'penguin') + qtbot.keyPress(browser._lineEditFilter, QtCore.Qt.Key_Enter) + + filteredRowCount = browser._listView.model().rowCount() + assert initRowCount > filteredRowCount + + +def test_filter_no_results(qtbot, browser): + """ + Ensure the filter doesn't show results (the text doesn't match any icon) """ initRowCount = browser._listView.model().rowCount() assert initRowCount > 0 # Enter a search term - qtbot.keyClicks(browser._lineEdit, 'google') + qtbot.keyClicks(browser._lineEditFilter, 'I-AM-NOT-penguin-A-penguin') # Press Enter to perform the filter - qtbot.keyPress(browser._lineEdit, QtCore.Qt.Key_Enter) + qtbot.keyPress(browser._lineEditFilter, QtCore.Qt.Key_Enter) filteredRowCount = browser._listView.model().rowCount() - assert initRowCount > filteredRowCount + assert filteredRowCount == 0 if __name__ == "__main__":