def test_pixmapGeneratorCache(qtbot, validIconPath): anim = iconify.anim.Spin() pixGen = iconify.core.PixmapGenerator('delete', color=QtGui.QColor('blue'), anim=anim) size = QtCore.QSize(24, 24) altSize = QtCore.QSize(32, 32) # Ensure that the pixmap generators cache is working as expected pixmapA = pixGen.pixmap(size) pixmapB = pixGen.pixmap(size) pixmapC = pixGen.pixmap(altSize) assert pixmapA is pixmapB assert pixmapA is not pixmapC
class GlobalTick(QtCore.QObject): """ A singleton timer used to trigger all animation objects created by iconify """ timeout = QtCore.Signal() _instance = None # type: Optional[GlobalTick] def __init__(self): # type: () -> None # Note: No parent so it's owned by Qt super(GlobalTick, self).__init__() self._tick = QtCore.QTimer() self._tick.timeout.connect(self.timeout.emit) self._tick.setInterval(17) # 60fps (ish) self._tick.start() @classmethod def instance(cls): # type: () -> GlobalTick """ Return the global instance of the ticker. Returns ------- GlobalTick """ if cls._instance is None: cls._instance = cls() return cls._instance
def test_pixmapGenerator(qtbot, validIconPath): size = QtCore.QSize(24, 24) color = QtGui.QColor('blue') anim = iconify.anim.Spin() basePixmapGen = iconify.core.PixmapGenerator('delete') assert os.path.isfile(basePixmapGen.path()) assert basePixmapGen.color() is None assert basePixmapGen.anim() is None coloredPixmapGen = iconify.core.PixmapGenerator('delete', color=color) assert os.path.isfile(coloredPixmapGen.path()) assert coloredPixmapGen.color() == color assert coloredPixmapGen.anim() is None animatedPixmapGen = iconify.core.PixmapGenerator('delete', anim=anim) assert os.path.isfile(animatedPixmapGen.path()) assert animatedPixmapGen.color() is None assert animatedPixmapGen.anim() == anim baseImage = basePixmapGen.pixmap(size).toImage() coloredImage = coloredPixmapGen.pixmap(size).toImage() animatedImage = animatedPixmapGen.pixmap(size).toImage() assert baseImage.size() == size assert coloredImage.size() == size assert animatedImage.size() == size assert baseImage == baseImage assert baseImage != coloredImage assert baseImage != animatedImage
def __init__(self): # type: () -> None # Note: No parent so it's owned by Qt super(GlobalTick, self).__init__() self._tick = QtCore.QTimer() self._tick.timeout.connect(self.timeout.emit) self._tick.setInterval(17) # 60fps (ish) self._tick.start()
def paintEvent(self, event): super(IconifyLabel, self).paintEvent(event) rect = event.rect() if rect.width() > rect.height(): size = QtCore.QSize(rect.height(), rect.height()) else: size = QtCore.QSize(rect.width(), rect.width()) pixmap = self._pixmapGenerator.pixmap(size) painter = QtGui.QPainter(self) halfSize = size / 2 point = rect.center() - QtCore.QPoint(halfSize.width(), halfSize.height()) painter.drawPixmap(point, pixmap) painter.end()
def resizeEvent(self, event): # type: (QtCore.QEvent) -> bool """ Re-implemented to re-calculate the grid size to provide scaling icons Parameters ---------- event : QtCore.QEvent """ 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 iconWidth = int(tileWidth * 0.8) self.setGridSize(QtCore.QSize(tileWidth, tileWidth)) self.setIconSize(QtCore.QSize(iconWidth, iconWidth)) return super(View, self).resizeEvent(event)
def _removeUnsupportedNodes(cls, installLocation): # type: (str) -> None """ Removes unsupported nodes from svg files found under the provided installLocation and re-writes them in place. Parameters ---------- installLocation : str """ for svg in glob.glob(os.path.join(installLocation, '*.svg')): dom = QtXml.QDomDocument("initData") svgFile = QtCore.QFile(svg) svgFile.open(QtCore.QIODevice.ReadOnly) dom.setContent(svgFile) svgFile.close() defNodes = dom.elementsByTagName('defs') for i in range(defNodes.count()): node = defNodes.item(0) node.parentNode().removeChild(node) symbolNodes = dom.elementsByTagName('symbol') for i in range(symbolNodes.count()): node = symbolNodes.item(0) node.parentNode().removeChild(node) byteArray = QtCore.QByteArray() textStream = QtCore.QTextStream(byteArray) dom.save(textStream, 0) svgFile = QtCore.QFile(svg) svgFile.open(QtCore.QIODevice.WriteOnly) svgFile.write(byteArray) svgFile.close()
def data(self, index, role=QtCore.Qt.DisplayRole): if role == QtCore.Qt.DisplayRole: return self._icons[index.row()] elif role == QtCore.Qt.DecorationRole: return ico.Icon(self.data(index, role=QtCore.Qt.DisplayRole), anim=anim) return None app = QtWidgets.QApplication([]) anim = ico.anim.Spin() model = Model(anim) view = QtWidgets.QListView() view.setUniformItemSizes(True) view.setViewMode(QtWidgets.QListView.IconMode) view.setGridSize(QtCore.QSize(80, 80)) view.setIconSize(QtCore.QSize(64, 64)) view.setModel(model) anim.tick.connect(view.viewport().update) anim.start() view.show() import sys sys.exit(app.exec_())
def __init__(self, parent=None): # type: (Optional[QtWidgets.QWidget]) -> None super(Browser, self).__init__(parent=parent) self.setMinimumSize(1024, 576) self.setWindowTitle('Iconify Browser') self._currentIcon = None # type: Optional[QtGui.QIcon] self._currentAnim = None # type: Optional[ico.anim.BaseAnimation] self._currentColor = None # type: Optional[QtGui.QColor] iconNames = ico.path.listIcons() self._filterTimer = QtCore.QTimer(self) self._filterTimer.setSingleShot(True) self._filterTimer.setInterval(AUTO_SEARCH_TIMEOUT) self._filterTimer.timeout.connect(self._updateFilter) model = Model() model.setStringList(sorted(iconNames)) self._proxyModel = QtCore.QSortFilterProxyModel() self._proxyModel.setSourceModel(model) self._proxyModel.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) self._listView = View(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().currentChanged.connect( self._iconChanged ) self._lineEdit = QtWidgets.QLineEdit(self) self._lineEdit.setAlignment(QtCore.Qt.AlignCenter) self._lineEdit.textChanged.connect(self._triggerDelayedUpdate) self._lineEdit.returnPressed.connect(self._triggerImmediateUpdate) collections = [] for iconName in iconNames: if ':' not in iconName: continue collections.append(iconName.split(':', 1)[0]) collections = sorted(set(collections)) self._collectionsCombo = QtWidgets.QComboBox(self) self._collectionsCombo.addItems([ALL_COLLECTIONS] + collections) self._collectionsCombo.currentIndexChanged.connect( self._triggerImmediateUpdate ) lyt = QtWidgets.QHBoxLayout() lyt.setContentsMargins(0, 0, 0, 0) lyt.addWidget(self._collectionsCombo) lyt.addWidget(self._lineEdit) searchBarFrame = QtWidgets.QFrame(self) searchBarFrame.setLayout(lyt) self._iconName = QtWidgets.QLabel() self._iconName.setAlignment(QtCore.Qt.AlignCenter) self._copyButton = QtWidgets.QPushButton('Copy Name', self) self._copyButton.clicked.connect(self._copyIconText) lyt = QtWidgets.QVBoxLayout() lyt.addWidget(searchBarFrame) lyt.addWidget(self._listView) lyt.addWidget(self._copyButton) iconFrame = QtWidgets.QFrame(self) iconFrame.setLayout(lyt) lyt = QtWidgets.QVBoxLayout() self._previewImage = PixmapGeneratorLabel() self._previewImage.setFixedSize(QtCore.QSize(200, 200)) self._animCombo = QtWidgets.QComboBox(self) self._animCombo.addItems((NO_ANIM, 'Spin', 'Breathe')) self._animCombo.currentIndexChanged.connect(self._animChanged) self._colorCombo = QtWidgets.QComboBox(self) self._colorCombo.addItems([NO_COLOR] + sorted(QtGui.QColor.colorNames())) self._colorCombo.currentIndexChanged.connect(self._colorChanged) self._previewFrame = QtWidgets.QFrame(self) self._previewFrame.setFixedWidth(200) lyt.addWidget(self._previewImage) lyt.addWidget(self._iconName) lyt.addWidget(self._animCombo) lyt.addWidget(self._colorCombo) lyt.addSpacerItem( QtWidgets.QSpacerItem( 0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding ) ) self._previewFrame.setLayout(lyt) lyt = QtWidgets.QHBoxLayout() lyt.setContentsMargins(0, 0, 0, 0) lyt.addWidget(iconFrame) lyt.addWidget(self._previewFrame) centralFrame = QtWidgets.QFrame(self) centralFrame.setLayout(lyt) self.setCentralWidget(centralFrame) QtWidgets.QShortcut( QtGui.QKeySequence(QtCore.Qt.Key_Return), self, self._copyIconText, ) self._lineEdit.setFocus() geo = self.geometry() desktop = QtWidgets.QApplication.desktop() screen = desktop.screenNumber(desktop.cursor().pos()) centerPoint = desktop.screenGeometry(screen).center() geo.moveCenter(centerPoint) self.setGeometry(geo)
class BaseAnimation(QtCore.QObject): """ The base class that should be used for all animations. """ # Emitted when the animation steps tick = QtCore.Signal() _minFrame = 0 _maxFrame = 100 def __init__(self, parent=None): # type: (Optional[QtCore.QObject]) -> None super(BaseAnimation, self).__init__(parent=parent) self._frame = self._minFrame self._active = False def __add__(self, other): # type: (object) -> BaseAnimation if isinstance(other, BaseAnimation): concatAnim = _ConcatAnim() concatAnim.setAnimations((self, other)) return concatAnim raise ValueError("Unsupported operation!") def transform(self, rect): # type: (QtCore.QRect) -> QtGui.QTransform """ Return a QtGui.QTransform for the current frame that will be used when drawing an image in the provided QRect. Parameters ---------- rect : QtCore.QRect Returns ------- QtGui.QTransform """ return QtGui.QTransform() def start(self): # type: () -> None """ Start the animation. """ GlobalTick.instance().timeout.connect(self._tick) self._active = True def stop(self): # type: () -> None """ Stop the animation and reset the current frame back to the start. """ self.pause() self._frame = self._minFrame def pause(self): # type: () -> None """ Stop the animation and maintain the current frame """ try: GlobalTick.instance().timeout.disconnect(self._tick) except RuntimeError: pass self._active = False def toggle(self): # type: () -> None """ Start or stop the animation based on it's current state. """ if self.active(): self.pause() else: self.start() def active(self): # type: () -> bool """ Indicate if the animation is currently playing. Returns ------- bool """ return self._active def frame(self): # type: () -> int """ Return the current frame of the animation. Returns ------- int """ return self._frame def forceTick(self): # type: () -> None """ Manually increment the current frame. """ self._tick() def incrementFrame(self): # type: () -> None """ Called when the animation is ticked allowing subclasses to provide custom frame step logic e.g. single shot animations. """ if self._frame == self._maxFrame: self._frame = self._minFrame else: self._frame += 1 def _tick(self): # type: () -> None self.incrementFrame() self.tick.emit()