def addMovementAnimation(self, animGroup): # print(">>MoveAnim") for phIdx in self.photoIdxsToMove: item = self.photosInGrid[phIdx] anim = QPropertyAnimation(item.pixmap, "pos") anim.setDuration(self.moveDuration - item.gridCol * self.moveModifier) anim.setEasingCurve(QEasingCurve.Linear) startCoords = self.getCellTLCoords(item.gridRow, item.gridCol) endCoords = self.getCellTLCoords(item.gridRow, item.gridCol + self.columnMovementDist) anim.setStartValue(startCoords) anim.setEndValue(endCoords) animGroup.addAnimation(anim)
class Tab(QObject): def __init__(self, parent): QObject.__init__(self, parent) self._toolbar = parent self.icon = None self.text = "" self.has_menu = False self.enabled = True self._fader = 0 self._animator = QPropertyAnimation(self) self._animator.setPropertyName(b"fader") self._animator.setTargetObject(self) def fade_in(self): self._animator.stop() self._animator.setDuration(80) self._animator.setEndValue(1) self._animator.start() def fade_out(self): self._animator.stop() self._animator.setDuration(160) self._animator.setEndValue(0) self._animator.start() def _set_fader(self, value): self._fader = value self._toolbar.update() def _get_fader(self): return self._fader fader = pyqtProperty(float, fget=_get_fader, fset=_set_fader)
class MoveSymbol(QUndoCommand): ''' Undo/Redo command for moving symbols ''' def __init__(self, symbol_id, old_pos, new_pos, animate=False): super(MoveSymbol, self).__init__() self.setText('Move symbol') self.symbol = symbol_id self.old_pos = old_pos self.new_pos = new_pos if animate: self.animation = QPropertyAnimation(self.symbol, "position") self.animation.setDuration(500) self.animation.setStartValue(self.old_pos) self.animation.setEndValue(self.new_pos) self.animation.setEasingCurve(QEasingCurve.OutCirc) def undo(self): ''' Undo a symbol move ''' self.symbol.position = self.old_pos try: self.symbol.decisionParent.updateConnectionPointPosition() except AttributeError: pass def redo(self): ''' Apply a symbol move ''' try: self.animation.start() except AttributeError: self.symbol.position = self.new_pos try: self.symbol.decisionParent.updateConnectionPointPosition() except AttributeError: pass
def createPositionAnimation(self, item, duration): ani = QPropertyAnimation(item, b'pos', self) ani.setDuration(duration) ani.setStartValue(item.pos()) width = self.squareWidth * 7 ani.setEndValue(QPointF(width - item.x(), width - item.y())) return ani
def createAnimation(self, target, index): '''创建动画''' # 暂停动画一 PauseAnimation1 = QPauseAnimation(target) PauseAnimation1.setDuration(150 * index) # 并行动画组一 # #透明度动画一 OpacityAnimation1 = QPropertyAnimation(target, b"opacity") OpacityAnimation1.setDuration(400) OpacityAnimation1.setStartValue(0) OpacityAnimation1.setEndValue(1) # #移动动画一 MoveAnimation1 = _MoveAnimation(target, self.parent, self.easing) MoveAnimation1.setMoveType(_MoveAnimation.MOVE1) MoveAnimation1.setDuration(400) MoveAnimation1.setStartValue(QPoint(0, 0)) MoveAnimation1.setEndValue(QPoint(self.parent.width() / 4.0, 0)) # 添加到并行动画里面 ParallelAnimation1 = QParallelAnimationGroup() ParallelAnimation1.addAnimation(OpacityAnimation1) ParallelAnimation1.addAnimation(MoveAnimation1) # 移动动画二 MoveAnimation2 = _MoveAnimation(target, self.parent, self.easing) MoveAnimation2.setMoveType(_MoveAnimation.MOVE2) MoveAnimation2.setDuration(2000) MoveAnimation2.setEndValue(QPoint((self.parent.width() / 4.0) * 3.0, 0)) # 并行动画组二 # #透明度动画二 OpacityAnimation2 = QPropertyAnimation(target, b"opacity") OpacityAnimation2.setDuration(400) OpacityAnimation2.setStartValue(1) OpacityAnimation2.setEndValue(0) # #移动动画三 MoveAnimation3 = _MoveAnimation(target, self.parent, self.easing) MoveAnimation3.setMoveType(_MoveAnimation.MOVE3) MoveAnimation3.setDuration(400) MoveAnimation3.setEndValue(QPoint(self.parent.width(), 0)) # 添加到并行动画里面 ParallelAnimation2 = QParallelAnimationGroup() ParallelAnimation2.addAnimation(OpacityAnimation2) ParallelAnimation2.addAnimation(MoveAnimation3) # 暂停动画二 PauseAnimation2 = QPauseAnimation(target) PauseAnimation2.setDuration(150 * (5 - index - 1)) # 串行动画组 self.setLoopCount(-1) # 无限循环 self.addAnimation(PauseAnimation1) self.addAnimation(ParallelAnimation1) self.addAnimation(MoveAnimation2) self.addAnimation(ParallelAnimation2) self.addAnimation(PauseAnimation2)
def __init__(self): super(Robot, self).__init__() self.setFlag(self.ItemHasNoContents) self.torsoItem = RobotTorso(self) self.headItem = RobotHead(self.torsoItem) self.upperLeftArmItem = RobotLimb(self.torsoItem) self.lowerLeftArmItem = RobotLimb(self.upperLeftArmItem) self.upperRightArmItem = RobotLimb(self.torsoItem) self.lowerRightArmItem = RobotLimb(self.upperRightArmItem) self.upperRightLegItem = RobotLimb(self.torsoItem) self.lowerRightLegItem = RobotLimb(self.upperRightLegItem) self.upperLeftLegItem = RobotLimb(self.torsoItem) self.lowerLeftLegItem = RobotLimb(self.upperLeftLegItem) settings = ( # Item Position Rotation Scale # x y start end (self.headItem, 0, -18, 20, -20, 1.1), (self.upperLeftArmItem, -15, -10, 190, 180, 0), (self.lowerLeftArmItem, 30, 0, 50, 10, 0), (self.upperRightArmItem, 15, -10, 300, 310, 0), (self.lowerRightArmItem, 30, 0, 0, -70, 0), (self.upperRightLegItem, 10, 32, 40, 120, 0), (self.lowerRightLegItem, 30, 0, 10, 50, 0), (self.upperLeftLegItem, -10, 32, 150, 80, 0), (self.lowerLeftLegItem, 30, 0, 70, 10, 0), (self.torsoItem, 0, 0, 5, -20, 0), ) animation = QParallelAnimationGroup(self) for item, pos_x, pos_y, start_rot, end_rot, scale in settings: item.setPos(pos_x, pos_y) rot_animation = QPropertyAnimation(item, b"rotation") rot_animation.setStartValue(start_rot) rot_animation.setEndValue(end_rot) rot_animation.setEasingCurve(QEasingCurve.SineCurve) rot_animation.setDuration(2000) animation.addAnimation(rot_animation) if scale > 0: scale_animation = QPropertyAnimation(item, b"scale") scale_animation.setEndValue(scale) scale_animation.setEasingCurve(QEasingCurve.SineCurve) scale_animation.setDuration(2000) animation.addAnimation(scale_animation) animation.setLoopCount(-1) animation.start()
class CircleObstruction(QColorThemedGraphicsObject): """ Useful for notifications, I...guess? """ def get_thickness(self): return self._thickness def set_thickness(self,c): self._thickness = c self.update() thickness = pyqtProperty(float, get_thickness, set_thickness) def __init__(self, sz, thickness, parent=None): super(CircleObstruction, self).__init__(parent) self._sz = sz self._thickness = thickness self._color = Qt.blue def boundingRect(self): return QRectF(-self._thickness, -self._thickness, self._sz + 2*self._thickness, self._sz + 2*self._thickness) def paint(self, painter, option, widget): # painter.setPen(QPen(self._color, # self._thickness)) painter.setBrush(self._color) painter.setPen(Qt.NoPen) painter.drawEllipse(QRectF( self._sz/2.0 - self._thickness, self._sz/2.0 - self._thickness, 2*self._thickness, 2*self._thickness, )) def show_anim(self): self.anim = QPropertyAnimation(self, "thickness") self.anim.setDuration(2000) self.anim.setStartValue(self.get_thickness()) self.anim.setEndValue(50.0) self.anim.setEasingCurve(QEasingCurve.OutElastic) self.anim.start() def hide_anim(self): self.anim = QPropertyAnimation(self, "thickness") self.anim.setDuration(500) self.anim.setStartValue(self.get_thickness()) self.anim.setEndValue(0.0) self.anim.setEasingCurve(QEasingCurve.InBack) self.anim.start()
class NotifyWidget(QWidget): def __init__(self): super().__init__() self.layout = QVBoxLayout(self) self.sub_widget = _NotifySubWidget(self) self.layout.addWidget(self.sub_widget) self.layout.setContentsMargins(0, 0, 0, 0) self._exit_shortcut = QShortcut(QKeySequence(Qt.Key_Escape), self) self._exit_shortcut.activated.connect(self.close) self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool) self.setAttribute(Qt.WA_TranslucentBackground) self.setAttribute(Qt.WA_MacAlwaysShowToolWindow) self.resize(width, height) self.move(QApplication.desktop().width() - self.width() - 20, 40) self.setLayout(self.layout) self._animation = QPropertyAnimation(self, b'windowOpacity') self._animation.setStartValue(0.8) self._animation.setKeyValueAt(0.4, 1) self._animation.setEndValue(0) self._animation.setDuration(5000) self._animation.finished.connect(self.close) def show(self): super().show() self._animation.start() def show_message(self, title, content, pixmap=None): if not self.isVisible(): self.show() self._animation.stop() self._animation.setCurrentTime(0) self._animation.start() self.sub_widget.set_title(title) self.sub_widget.set_content(content) pixmap = pixmap if pixmap else QPixmap(WINDOW_ICON) self.sub_widget.set_pixmap(pixmap) def enterEvent(self, event): self._animation.setCurrentTime(0)
def animate(self, item, property_name, duration, start_value, end_value): """Summary Args: item (TYPE): Description property_name (TYPE): Description duration (TYPE): Description start_value (TYPE): Description end_value (TYPE): Description """ if item is not None: b_name = property_name.encode('ascii') anim = QPropertyAnimation(item.adapter, b_name) anim.setDuration(duration) anim.setStartValue(start_value) anim.setEndValue(end_value) anim.start() item.adapter.saveRef(property_name, anim)
def addRemovalAnimation(self, animGroup): # print(">>RemoveAnim") for phIdx in self.photoIdxsToRemove: item = self.photosInGrid[phIdx] anim = QPropertyAnimation(item.pixmap, "scale") anim.setDuration(self.shrinkDuration) anim.setEasingCurve(QEasingCurve.Linear) anim.setStartValue(1.0) anim.setEndValue(0.0) animGroup.addAnimation(anim) anim = QPropertyAnimation(item.pixmap, "pos") anim.setDuration(self.shrinkDuration) anim.setEasingCurve(QEasingCurve.Linear) startCoords = self.getCellTLCoords(item.gridRow, item.gridCol) endCoords = QPointF(startCoords.x()+self.cellSize.width()*item.xFactor, startCoords.y() + item.pixmap.pixmapSize.height() / 2) anim.setStartValue(startCoords) anim.setEndValue(endCoords) animGroup.addAnimation(anim)
def addInsertionAnimation(self, animGroup): # Animation for added # print(">>>InsAnim") for ph in self.picItemsToAdd: # Set pixmap start location newPixmap = ph[0].pixmap row = ph[1] col = ph[2] xFact = ph[0].xFactor endCoords = self.getCellTLCoords(row, col) startCoords = QPointF(endCoords.x()-self.cellSize.width()*xFact, endCoords.y()) newPixmap.pixmap_item.setPos(startCoords) newPixmap.pixmap_item.setVisible(True) # Animate in anim = QPropertyAnimation(newPixmap, "pos") anim.setDuration(self.insertDuration) anim.setStartValue(startCoords) anim.setEasingCurve(QEasingCurve.Linear) anim.setEndValue(endCoords) animGroup.addAnimation(anim)
def animate(self, item: QGraphicsItem, property_name: str, duration: int, start_value, end_value): """ Args: item: Description property_name: Description duration: Description start_value (QVariant): Description end_value (QVariant): Description """ b_name = property_name.encode('ascii') anim = item.adapter.getRef(property_name) if anim is None: anim = QPropertyAnimation(item.adapter, b_name) item.adapter.saveRef(property_name, anim) anim.setDuration(duration) anim.setStartValue(start_value) anim.setEndValue(end_value) anim.start()
class ChannelListItem(QWidget): """ This class is responsible for managing the item in the list of channels. The list item supports a fade-in effect, which can be enabled with the should_fade parameter in the constructor. """ def __init__(self, parent, channel, fade_delay=0, should_fade=False): super(QWidget, self).__init__(parent) uic.loadUi('qt_resources/channel_list_item.ui', self) self.channel_name.setText(channel["name"]) self.channel_description_label.setText("Active 6 days ago • %d items" % channel["torrents"]) self.channel_num_subs_label.setText(str(channel["votes"])) if channel["sub"]: self.channel_subscribe_button.setText("✓ subscribed") else: self.channel_subscribe_button.setText("subscribe") if should_fade: self.opacity_effect = QGraphicsOpacityEffect(self) self.opacity_effect.setOpacity(0) self.setGraphicsEffect(self.opacity_effect) self.timer = QTimer() self.timer.setInterval(fade_delay) self.timer.timeout.connect(self.fadeIn) self.timer.start() def fadeIn(self): self.anim = QPropertyAnimation(self.opacity_effect, 'opacity') self.anim.setDuration(800) self.anim.setStartValue(0) self.anim.setEndValue(1) self.anim.start() self.timer.stop()
def __init__(self, size, parent=None): super(PadNavigator, self).__init__(parent) self.form = Ui_Form() splash = SplashItem() splash.setZValue(1) pad = FlippablePad(size) flipRotation = QGraphicsRotation(pad) xRotation = QGraphicsRotation(pad) yRotation = QGraphicsRotation(pad) flipRotation.setAxis(Qt.YAxis) xRotation.setAxis(Qt.YAxis) yRotation.setAxis(Qt.XAxis) pad.setTransformations([flipRotation, xRotation, yRotation]) backItem = QGraphicsProxyWidget(pad) widget = QWidget() self.form.setupUi(widget) self.form.hostName.setFocus() backItem.setWidget(widget) backItem.setVisible(False) backItem.setFocus() backItem.setCacheMode(QGraphicsItem.ItemCoordinateCache) r = backItem.rect() backItem.setTransform(QTransform().rotate(180, Qt.YAxis).translate(-r.width()/2, -r.height()/2)) selectionItem = RoundRectItem(QRectF(-60, -60, 120, 120), QColor(Qt.gray), pad) selectionItem.setZValue(0.5) smoothSplashMove = QPropertyAnimation(splash, b'y') smoothSplashOpacity = QPropertyAnimation(splash, b'opacity') smoothSplashMove.setEasingCurve(QEasingCurve.InQuad) smoothSplashMove.setDuration(250) smoothSplashOpacity.setDuration(250) smoothXSelection = QPropertyAnimation(selectionItem, b'x') smoothYSelection = QPropertyAnimation(selectionItem, b'y') smoothXRotation = QPropertyAnimation(xRotation, b'angle') smoothYRotation = QPropertyAnimation(yRotation, b'angle') smoothXSelection.setDuration(125) smoothYSelection.setDuration(125) smoothXRotation.setDuration(125) smoothYRotation.setDuration(125) smoothXSelection.setEasingCurve(QEasingCurve.InOutQuad) smoothYSelection.setEasingCurve(QEasingCurve.InOutQuad) smoothXRotation.setEasingCurve(QEasingCurve.InOutQuad) smoothYRotation.setEasingCurve(QEasingCurve.InOutQuad) smoothFlipRotation = QPropertyAnimation(flipRotation, b'angle') smoothFlipScale = QPropertyAnimation(pad, b'scale') smoothFlipXRotation = QPropertyAnimation(xRotation, b'angle') smoothFlipYRotation = QPropertyAnimation(yRotation, b'angle') flipAnimation = QParallelAnimationGroup(self) smoothFlipScale.setDuration(500) smoothFlipRotation.setDuration(500) smoothFlipXRotation.setDuration(500) smoothFlipYRotation.setDuration(500) smoothFlipScale.setEasingCurve(QEasingCurve.InOutQuad) smoothFlipRotation.setEasingCurve(QEasingCurve.InOutQuad) smoothFlipXRotation.setEasingCurve(QEasingCurve.InOutQuad) smoothFlipYRotation.setEasingCurve(QEasingCurve.InOutQuad) smoothFlipScale.setKeyValueAt(0, 1.0) smoothFlipScale.setKeyValueAt(0.5, 0.7) smoothFlipScale.setKeyValueAt(1, 1.0) flipAnimation.addAnimation(smoothFlipRotation) flipAnimation.addAnimation(smoothFlipScale) flipAnimation.addAnimation(smoothFlipXRotation) flipAnimation.addAnimation(smoothFlipYRotation) setVariablesSequence = QSequentialAnimationGroup() setFillAnimation = QPropertyAnimation(pad, b'fill') setBackItemVisibleAnimation = QPropertyAnimation(backItem, b'visible') setSelectionItemVisibleAnimation = QPropertyAnimation(selectionItem, b'visible') setFillAnimation.setDuration(0) setBackItemVisibleAnimation.setDuration(0) setSelectionItemVisibleAnimation.setDuration(0) setVariablesSequence.addPause(250) setVariablesSequence.addAnimation(setBackItemVisibleAnimation) setVariablesSequence.addAnimation(setSelectionItemVisibleAnimation) setVariablesSequence.addAnimation(setFillAnimation) flipAnimation.addAnimation(setVariablesSequence) stateMachine = QStateMachine(self) splashState = QState(stateMachine) frontState = QState(stateMachine) historyState = QHistoryState(frontState) backState = QState(stateMachine) frontState.assignProperty(pad, "fill", False) frontState.assignProperty(splash, "opacity", 0.0) frontState.assignProperty(backItem, "visible", False) frontState.assignProperty(flipRotation, "angle", 0.0) frontState.assignProperty(selectionItem, "visible", True) backState.assignProperty(pad, "fill", True) backState.assignProperty(backItem, "visible", True) backState.assignProperty(xRotation, "angle", 0.0) backState.assignProperty(yRotation, "angle", 0.0) backState.assignProperty(flipRotation, "angle", 180.0) backState.assignProperty(selectionItem, "visible", False) stateMachine.addDefaultAnimation(smoothXRotation) stateMachine.addDefaultAnimation(smoothYRotation) stateMachine.addDefaultAnimation(smoothXSelection) stateMachine.addDefaultAnimation(smoothYSelection) stateMachine.setInitialState(splashState) anyKeyTransition = QEventTransition(self, QEvent.KeyPress, splashState) anyKeyTransition.setTargetState(frontState) anyKeyTransition.addAnimation(smoothSplashMove) anyKeyTransition.addAnimation(smoothSplashOpacity) enterTransition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Enter, backState) returnTransition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Return, backState) backEnterTransition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Enter, frontState) backReturnTransition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Return, frontState) enterTransition.setTargetState(historyState) returnTransition.setTargetState(historyState) backEnterTransition.setTargetState(backState) backReturnTransition.setTargetState(backState) enterTransition.addAnimation(flipAnimation) returnTransition.addAnimation(flipAnimation) backEnterTransition.addAnimation(flipAnimation) backReturnTransition.addAnimation(flipAnimation) columns = size.width() rows = size.height() stateGrid = [] for y in range(rows): stateGrid.append([QState(frontState) for _ in range(columns)]) frontState.setInitialState(stateGrid[0][0]) selectionItem.setPos(pad.iconAt(0, 0).pos()) for y in range(rows): for x in range(columns): state = stateGrid[y][x] rightTransition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Right, state) leftTransition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Left, state) downTransition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Down, state) upTransition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Up, state) rightTransition.setTargetState(stateGrid[y][(x + 1) % columns]) leftTransition.setTargetState(stateGrid[y][((x - 1) + columns) % columns]) downTransition.setTargetState(stateGrid[(y + 1) % rows][x]) upTransition.setTargetState(stateGrid[((y - 1) + rows) % rows][x]) icon = pad.iconAt(x, y) state.assignProperty(xRotation, "angle", -icon.x() / 6.0) state.assignProperty(yRotation, "angle", icon.y() / 6.0) state.assignProperty(selectionItem, "x", icon.x()) state.assignProperty(selectionItem, "y", icon.y()) frontState.assignProperty(icon, "visible", True) backState.assignProperty(icon, "visible", False) setIconVisibleAnimation = QPropertyAnimation(icon, b'visible') setIconVisibleAnimation.setDuration(0) setVariablesSequence.addAnimation(setIconVisibleAnimation) scene = QGraphicsScene(self) scene.setBackgroundBrush(QBrush(QPixmap(":/images/blue_angle_swirl.jpg"))) scene.setItemIndexMethod(QGraphicsScene.NoIndex) scene.addItem(pad) scene.setSceneRect(scene.itemsBoundingRect()) self.setScene(scene) sbr = splash.boundingRect() splash.setPos(-sbr.width() / 2, scene.sceneRect().top() - 2) frontState.assignProperty(splash, "y", splash.y() - 100.0) scene.addItem(splash) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setMinimumSize(50, 50) self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate) self.setCacheMode(QGraphicsView.CacheBackground) self.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform | QPainter.TextAntialiasing) if QGLFormat.hasOpenGL(): self.setViewport(QGLWidget(QGLFormat(QGL.SampleBuffers))) stateMachine.start()
class DialogAnimation(QDialog): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 窗口透明度动画类 self.animation = QPropertyAnimation(self, b'windowOpacity') self.animation.setDuration(600) # 持续时间1秒 def doFadeShow(self): try: # 尝试先取消动画完成后关闭窗口的信号 self.animation.finished.disconnect(self.close) except Exception as e: if str(e) != "disconnect() failed between 'finished' and 'close'": print(str(e)) self.animation.stop() # 透明度范围从0逐渐增加到1 self.animation.setStartValue(0) self.animation.setEndValue(1) self.animation.start() def doFadeClose(self): self.animation.stop() self.animation.finished.connect(self.close) # 动画完成则关闭窗口 # 透明度范围从1逐渐减少到0 self.animation.setStartValue(1) self.animation.setEndValue(0) self.animation.start() def doShake(self): self.doShakeWindow(self) # 下面这个方法可以做成这样的封装给任何控件 def doShakeWindow(self, target): """窗口抖动动画 :param target: 目标控件 """ if hasattr(target, '_shake_animation'): # 如果已经有该对象则跳过 return animation = QPropertyAnimation(target, b'pos', target) target._shake_animation = animation animation.finished.connect(lambda: delattr(target, '_shake_animation')) pos = target.pos() x, y = pos.x(), pos.y() animation.setDuration(100) # 持续时间1秒 animation.setLoopCount(2) animation.setKeyValueAt(0, QPoint(x, y)) animation.setKeyValueAt(0.09, QPoint(x + 2, y - 2)) animation.setKeyValueAt(0.18, QPoint(x + 4, y - 4)) animation.setKeyValueAt(0.27, QPoint(x + 2, y - 6)) animation.setKeyValueAt(0.36, QPoint(x + 0, y - 8)) animation.setKeyValueAt(0.45, QPoint(x - 2, y - 10)) animation.setKeyValueAt(0.54, QPoint(x - 4, y - 8)) animation.setKeyValueAt(0.63, QPoint(x - 6, y - 6)) animation.setKeyValueAt(0.72, QPoint(x - 8, y - 4)) animation.setKeyValueAt(0.81, QPoint(x - 6, y - 2)) animation.setKeyValueAt(0.90, QPoint(x - 4, y - 0)) animation.setKeyValueAt(0.99, QPoint(x - 2, y + 2)) animation.setEndValue(QPoint(x, y)) animation.start(animation.DeleteWhenStopped) def exec_(self): # 执行淡入 self.doFadeShow() super().exec_()
class Preferences(QDialog): # Signal to warn that the window is closed settingsClosed = pyqtSignal() def __init__(self, parent=None): super(Preferences, self).__init__(parent) # Main container # This contains a grid main_box = QVBoxLayout(self) main_box.setContentsMargins(200, 50, 200, 100) # The grid contains two containers # left container and right container grid = QGridLayout() # Left Container left_container = QVBoxLayout() left_container.setContentsMargins(0, 0, 0, 0) # General group_gral = QGroupBox(self.tr("General")) box_gral = QVBoxLayout(group_gral) # Updates btn_updates = QPushButton(self.tr("Check for updates")) box_gral.addWidget(btn_updates) # Language group_language = QGroupBox(self.tr("Language")) box = QVBoxLayout(group_language) # Find .qm files in language path available_langs = file_manager.get_files_from_folder( settings.LANGUAGE_PATH) languages = ["English"] + available_langs self._combo_lang = QComboBox() box.addWidget(self._combo_lang) self._combo_lang.addItems(languages) self._combo_lang.currentIndexChanged[int].connect( self._change_lang) if PSetting.LANGUAGE: self._combo_lang.setCurrentText(PSetting.LANGUAGE) box.addWidget(QLabel(self.tr("(Requires restart)"))) # Add widgets left_container.addWidget(group_gral) left_container.addWidget(group_language) left_container.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Expanding)) # Right Container right_container = QVBoxLayout() right_container.setContentsMargins(0, 0, 0, 0) # Editor editor_group = QGroupBox(self.tr("Editor Configurations")) box_editor = QHBoxLayout(editor_group) # Current line self._highlight_current_line = QCheckBox( self.tr("Highlight Current Line")) self._highlight_current_line.setChecked( PSetting.HIGHLIGHT_CURRENT_LINE) self._highlight_current_line.stateChanged[int].connect( self.__current_line_value_changed) box_editor.addWidget(self._highlight_current_line) # Matching paren self._matching_paren = QCheckBox(self.tr("Matching Parenthesis")) self._matching_paren.setChecked( PSetting.MATCHING_PARENTHESIS) self._matching_paren.stateChanged[int].connect( self.__set_enabled_matching_parenthesis) box_editor.addWidget(self._matching_paren) # Font group font_group = QGroupBox(self.tr("Font")) font_grid = QGridLayout(font_group) font_grid.addWidget(QLabel(self.tr("Family")), 0, 0) self._combo_font = QFontComboBox() self._combo_font.setCurrentFont(PSetting.FONT) font_grid.addWidget(self._combo_font, 0, 1) font_grid.addWidget(QLabel(self.tr("Point Size")), 1, 0) self._combo_font_size = QComboBox() fdb = QFontDatabase() combo_sizes = fdb.pointSizes(PSetting.FONT.family()) current_size_index = combo_sizes.index( PSetting.FONT.pointSize()) self._combo_font_size.addItems([str(f) for f in combo_sizes]) self._combo_font_size.setCurrentIndex(current_size_index) font_grid.addWidget(self._combo_font_size, 1, 1) right_container.addWidget(editor_group) right_container.addWidget(font_group) right_container.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Expanding)) # Add widgets grid.addLayout(left_container, 0, 0) grid.addLayout(right_container, 0, 1) main_box.addLayout(grid) # Button close and reset hbox = QHBoxLayout() hbox.setSpacing(20) hbox.addItem(QSpacerItem(1, 0, QSizePolicy.Expanding)) btn_cancel = QPushButton(self.tr("Back")) hbox.addWidget(btn_cancel) btn_reset = QPushButton(self.tr("Reset Configurations")) hbox.addWidget(btn_reset) main_box.addLayout(hbox) # Overlay self.overlay = overlay_widget.OverlayWidget(self) self.overlay.hide() # Effect and animations self.effect = QGraphicsOpacityEffect() self.setGraphicsEffect(self.effect) duration, x = 180, 150 # Animation duration # Animation start # Opacity animation self.opacity_animation_s = QPropertyAnimation(self.effect, b"opacity") self.opacity_animation_s.setDuration(duration) self.opacity_animation_s.setStartValue(0.0) self.opacity_animation_s.setEndValue(1.0) # X animation self.x_animation_s = QPropertyAnimation(self, b"geometry") self.x_animation_s.setDuration(duration) self.x_animation_s.setStartValue(QRect(x, 0, parent.width(), parent.height())) self.x_animation_s.setEndValue(QRect(0, 0, parent.width(), parent.height())) # Animation end # Opacity animation self.opacity_animation_e = QPropertyAnimation(self.effect, b"opacity") self.opacity_animation_e.setDuration(duration) self.opacity_animation_e.setStartValue(1.0) self.opacity_animation_e.setEndValue(0.0) # X animation self.x_animation_e = QPropertyAnimation(self, b"geometry") self.x_animation_e.setDuration(duration) self.x_animation_e.setStartValue(QRect(0, 0, parent.width(), parent.height())) self.x_animation_e.setEndValue(QRect(-x, 0, parent.width(), parent.height())) # Group animation start self.group_animation_s = QParallelAnimationGroup() self.group_animation_s.addAnimation(self.opacity_animation_s) self.group_animation_s.addAnimation(self.x_animation_s) # Group animation end self.group_animation_e = QParallelAnimationGroup() self.group_animation_e.addAnimation(self.opacity_animation_e) self.group_animation_e.addAnimation(self.x_animation_e) # Connections self.group_animation_e.finished.connect( self._on_group_animation_finished) btn_cancel.clicked.connect(self.close) btn_reset.clicked.connect(self._reset_settings) btn_updates.clicked.connect(self._check_for_updates) # self.thread.finished.connect(self._on_thread_finished) self._combo_font.currentFontChanged.connect( self._change_font) self._combo_font_size.currentTextChanged.connect( self._change_font_size) def __current_line_value_changed(self, value): qs = QSettings(settings.SETTINGS_PATH, QSettings.IniFormat) qs.setValue('highlight_current_line', value) PSetting.HIGHLIGHT_CURRENT_LINE = value def __set_enabled_matching_parenthesis(self, value): qs = QSettings(settings.SETTINGS_PATH, QSettings.IniFormat) qs.setValue("matching_parenthesis", value) PSetting.MATCHING_PARENTHESIS = value def _change_font(self, font): # FIXME: un quilombo esto central = Pireal.get_service("central") mcontainer = central.get_active_db() if mcontainer is not None: query_widget = mcontainer.query_container.currentWidget() if query_widget is not None: weditor = query_widget.get_editor() if weditor is not None: qs = QSettings(settings.SETTINGS_PATH, QSettings.IniFormat) weditor.set_font(font) qs.setValue("font", font) def _change_font_size(self, size): # FIXME: un quilombo esto font = self._combo_font.currentFont() font.setPointSize(int(size)) central = Pireal.get_service("central") mcontainer = central.get_active_db() if mcontainer is not None: query_widget = mcontainer.query_container.currentWidget() if query_widget is not None: weditor = query_widget.get_editor() if weditor is not None: qs = QSettings(settings.SETTINGS_PATH, QSettings.IniFormat) weditor.set_font(font) qs.setValue("font", font) def showEvent(self, event): super(Preferences, self).showEvent(event) self.group_animation_s.start() def resizeEvent(self, event): self.overlay.resize(self.size()) event.accept() def done(self, result): self.res = result self.group_animation_e.start() def _on_group_animation_finished(self): super(Preferences, self).done(self.res) self.settingsClosed.emit() def _check_for_updates(self): # Thread self._thread = QThread() self._updater = updater.Updater() self._updater.moveToThread(self._thread) self._thread.started.connect(self._updater.check_updates) self._updater.finished.connect(self.__on_thread_update_finished) # Show overlay widget self.overlay.show() # Start thread self._thread.start() def __on_thread_update_finished(self): # Hide overlay widget self.overlay.hide() self._thread.quit() msg = QMessageBox(self) if not self._updater.error: if self._updater.version: version = self._updater.version msg.setWindowTitle(self.tr("New version available!")) msg.setText(self.tr("Check the web site to " "download <b>Pireal {}</b>".format( version))) download_btn = msg.addButton(self.tr("Download!"), QMessageBox.YesRole) msg.addButton(self.tr("Cancel"), QMessageBox.RejectRole) msg.exec_() r = msg.clickedButton() if r == download_btn: webbrowser.open_new( "http://centaurialpha.github.io/pireal") else: msg.setWindowTitle(self.tr("Information")) msg.setText(self.tr("Last version installed")) msg.addButton(self.tr("Ok"), QMessageBox.AcceptRole) msg.exec_() else: msg.critical(self, self.tr("Error"), self.tr("Connection error")) self._thread.deleteLater() self._updater.deleteLater() def _reset_settings(self): """ Remove all settings """ msg = QMessageBox(self) msg.setWindowTitle(self.tr("Reset Settings")) msg.setText(self.tr("Are you sure you want to clear all settings?")) msg.setIcon(QMessageBox.Question) msg.addButton(self.tr("No"), QMessageBox.NoRole) yes_btn = msg.addButton(self.tr("Yes"), QMessageBox.YesRole) msg.exec_() r = msg.clickedButton() if r == yes_btn: QSettings(settings.SETTINGS_PATH, QSettings.IniFormat).clear() self.close() def _change_lang(self, index): lang = self._combo_lang.itemText(index) qs = QSettings(settings.SETTINGS_PATH, QSettings.IniFormat) qs.setValue('language', lang)
view.setWindowTitle("Animated Tiles") view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate) view.setBackgroundBrush(QBrush(bgPix)) view.setCacheMode(QGraphicsView.CacheBackground) view.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) view.show() states = QStateMachine() states.addState(rootState) states.setInitialState(rootState) rootState.setInitialState(centeredState) group = QParallelAnimationGroup() for i, item in enumerate(items): anim = QPropertyAnimation(item, b'pos') anim.setDuration(750 + i * 25) anim.setEasingCurve(QEasingCurve.InOutBack) group.addAnimation(anim) trans = rootState.addTransition(ellipseButton.pressed, ellipseState) trans.addAnimation(group) trans = rootState.addTransition(figure8Button.pressed, figure8State) trans.addAnimation(group) trans = rootState.addTransition(randomButton.pressed, randomState) trans.addAnimation(group) trans = rootState.addTransition(tiledButton.pressed, tiledState) trans.addAnimation(group)
class AdvancedAppWidget: _translate = QCoreApplication.translate template = 'AdvancedAppWidget' animation: QPropertyAnimation tags: QComboBox def __init__(self, parent, container: Container) -> None: self.container = container self.ui = AdvancedAppWidgetUi(parent, container) self.drawShortcuts() containers_service.listen(self.container, 'tag_index', self.listenTagChange) containers_service.listen(self.container, SHORTCUT_CONF_CHANGED_CHANNEL, self.redrawShortcuts) if not container.expanded: self.ui.setMaximumHeight(0) self.ui.resizeEvent = self.resize def onImageTagChange(self, full_tag_name): if not full_tag_name: return tag = full_tag_name.split(':')[1] previous_val = self.container.tag self.container.tag = tag self.container.save() auditing_service.audit_update(self.container, self.container.tableName(), self.container.id, "tag", previous_val, tag) def onAdvancedConfigurationClicked(self): app_config = AppConfig(None, self.container) app_config.show() def onCollapsed(self): self.container.expanded = False self.container.save() def onExpanded(self): self.container.expanded = True self.container.save() self.ui.setMaximumHeight(99999) def toggleWindow(self): self.animation = QPropertyAnimation(self.ui, b"maximumHeight") self.animation.setDuration(200) try: self.animation.finished.disconnect() except TypeError: pass if self.ui.maximumHeight() == 0: self.animation.setStartValue(0) self.animation.setEndValue(self.ui.layout.sizeHint().height()) self.animation.finished.connect(self.onExpanded) else: self.animation.setStartValue(self.ui.layout.sizeHint().height()) self.animation.setEndValue(0) self.animation.finished.connect(self.onCollapsed) self.animation.start() def findFileOrFolder(self, shotcut: PreferencesShortcut, input_box: PathViewWidget): if shotcut.pref_type == 'File': fname = QFileDialog.getOpenFileName(self.ui, 'Open file') path = fname[0] if fname[0] else '' else: file = str( QFileDialog.getExistingDirectory(self.ui, "Select Directory")) path = file if file else '' if path: input_box.setPath(path, shotcut.pref_type.lower()) input_box.resizePaths() self.setShortcutValue(shotcut, path) def drawShortcut(self, shortcut: PreferencesShortcut, row): label = QLabel(self.ui.widget) label.setText(self._translate(self.template, shortcut.label) + ':') label.setToolTip(shortcut.description) self.ui.grid_layout.addWidget(label, row, 0, 1, 1) if shortcut.pref_type in ['File', 'Folder']: input_box = PathViewWidget(self.ui.widget) input_box.setPath(shortcut.default_value, shortcut.pref_type.lower()) self.ui.grid_layout.addWidget(input_box, row, 1, 1, 2) finder = QPushButton(self.ui.widget) finder.setText(' ... ') finder.setFlat(True) finder.setStyleSheet( "border: 1px solid #999999; padding: 0px 4px; border-radius: 2px" ) finder.setMaximumWidth(rt(25)) finder.clicked.connect( lambda x: self.findFileOrFolder(shortcut, input_box)) self.ui.grid_layout.addWidget(finder, row, 3, 1, 2) else: input_box = QLineEdit(self.ui.widget) input_box.setText(shortcut.default_value) input_box.textChanged.connect( lambda x: self.setShortcutValue(shortcut, x)) input_box.setStyleSheet( 'border: none; background-color: transparent') input_box.setReadOnly(shortcut.shortcut == 'Constant') self.ui.grid_layout.addWidget(input_box, row, 1, 1, 2) hidden_widget = QWidget(self.ui.widget) hidden_widget.setSizePolicy(BQSizePolicy(h_stretch=1)) self.ui.grid_layout.addWidget(hidden_widget, row, 3, 1, 2) def drawTagShortcut(self, row): label = QLabel(self.ui.widget) self.ui.grid_layout.addWidget(label, row, 0, 1, 1) self.tags = QComboBox(self.ui.widget) self.tags.setSizePolicy( BQSizePolicy(h_stretch=4, height=QSizePolicy.Fixed, width=QSizePolicy.Fixed)) self.ui.grid_layout.addWidget(self.tags, row, 1, 1, 2) hidden_widget = QWidget(self.ui.widget) hidden_widget.setSizePolicy(BQSizePolicy(h_stretch=1)) self.ui.grid_layout.addWidget(hidden_widget, row, 3, 1, 2) for index, tag in enumerate(tags_service.getTags(self.container)): self.tags.addItem(self.container.image_name + ":" + tag.name) if tag.name == self.container.tag: self.tags.setCurrentIndex(index) self.tags.currentTextChanged.connect(self.onImageTagChange) label.setText(self._translate(self.template, "Image tag:")) def drawShortcuts(self): shortcuts = shortcut_service.getEnabledShortcuts(self.container) for index, shortcut in enumerate(shortcuts): self.drawShortcut(shortcut, index) self.drawTagShortcut(len(shortcuts)) def cleanShortcuts(self): while self.ui.grid_layout.count(): item = self.ui.grid_layout.takeAt(0) item.widget().deleteLater() def redrawShortcuts(self): self.cleanShortcuts() self.drawShortcuts() def listenTagChange(self, index): self.tags.setCurrentIndex(index) def setShortcutValue(self, shortcut: PreferencesShortcut, value): previous_val = shortcut.default_value shortcut.default_value = value shortcut.save() auditing_service.audit_update(self.container, shortcut.tableName(), shortcut.id, "default_value", previous_val, value) def resize(self, event: QResizeEvent): if self.tags: self.tags.setFixedWidth(event.size().width() * 2 / 3) QWidget.resizeEvent(self.ui, event)
class Attr(WizardWidget): drag_label = "Attribute <attr>" acceptable_tags = ["attr"] def __init__(self, parent=None): # This changes to true when this attribute is being viewed/edited self.active = False self.ef = 0 self.nodata = None self.nodata_content = (False, None) # nodata checked, last nodata node WizardWidget.__init__(self, parent=parent) # an in memory record of all the contents that were selected self._previous_index = -1 cbo = self.ui.comboBox self._domain_content = dict.fromkeys(range(cbo.count()), None) self.parent_ui = parent self.series = None self.ui.nodata_section.hide() self.highlighter = Highlighter(self.ui.fgdc_attrdef.document()) self.nodata_matches = [ "#N/A", "#N/A N/A", "#NA", "-1.#IND", "-1.#QNAN", "-NaN", "-nan", "1.#IND", "1.#QNAN", "N/A", "NA", "NULL", "NaN", "n/a", "nan", "null", -9999, "-9999", "", "Nan", "<< empty cell >>", ] def build_ui(self): """ Build and modify this widget's GUI Returns ------- None """ self.ui = UI_attr.Ui_attribute_widget() self.ui.setupUi(self) # self.ui.fgdc_attrdef.installEventFilter(self) self.ui.fgdc_attrdef.setMouseTracking(True) self.ui.fgdc_attrdefs.installEventFilter(self) self.ui.attrdomv_contents.installEventFilter(self) self.ui.place_holder.installEventFilter(self) self.setup_dragdrop(self) self.ui.comboBox.currentIndexChanged.connect(self.change_domain) self.ui.fgdc_attr.mousePressEvent = self.mousePressEvent self.ui.fgdc_attrlabl.mousePressEvent = self.attrlabl_press self.ui.fgdc_attrdef.mousePressEvent = self.attrdef_press self.ui.fgdc_attrdefs.mousePressEvent = self.attrdefs_press self.ui.comboBox.mousePressEvent = self.combo_press self.ui.rbtn_nodata_yes.toggled.connect(self.include_nodata_change) self.domain = None self.ui.nodata_content.hide() self.ui.rbtn_nodata_no.setChecked(True) self.ui.comboBox.setCurrentIndex(3) self.ui.fgdc_attrdefs.setText(default_def_source) def include_nodata_change(self, b): """ The user has changed the nodata present selection Parameters ---------- b: qt event Returns ------- None """ if b: self.ui.nodata_section.show() else: self.ui.nodata_section.hide() def mousePressEvent(self, event): self.activate() def attrlabl_press(self, event): self.activate() return QLineEdit.mousePressEvent(self.ui.fgdc_attrlabl, event) def attrdef_press(self, event): self.activate() return QPlainTextEdit.mousePressEvent(self.ui.fgdc_attrdef, event) def attrdefs_press(self, event): self.activate() return QLineEdit.mousePressEvent(self.ui.fgdc_attrdefs, event) def combo_press(self, event): self.activate() return QComboBox.mousePressEvent(self.ui.comboBox, event) def clear_domain(self): for child in self.ui.attrdomv_contents.children(): if isinstance(child, QWidget): child.deleteLater() def clear_nodata(self): try: self.nodata_edom.deleteLater() except: pass def set_series(self, series): """ store a series with this attri Parameters ---------- series : pandas series Returns ------- None """ self.series = series def guess_domain(self): """ return the index of the domain the associated series is thought to best match. if there are less than twenty unique items in the series the guess is enumerated if it's numeric the guess is range. else it's unrepresentable Returns ------- int : index of the domain the associated series is thought to best match """ # given a series of data take a guess as to which # domain type is appropriate if self.series is not None: if self.nodata is not None: clean_series = data_io.clean_nodata(self.series, self.nodata) else: clean_series = self.series uniques = clean_series.unique() if np.issubdtype(clean_series.dtype, np.number): return 1 # range elif len(uniques) < 20: return 0 # enumerated else: return 3 # unrepresentable # without a series to introspect we're going to default to udom return 3 # unrepresentable def store_current_content(self): """ Save the current contents (xml format) into our domain contents dict Returns ------- None """ if self.domain is not None and not sip.isdeleted(self.domain): cur_xml = self.domain.to_xml() if cur_xml.tag == "udom": self._domain_content[3] = cur_xml elif cur_xml.tag == "codesetd": self._domain_content[2] = cur_xml elif cur_xml.tag == "rdom": self._domain_content[1] = cur_xml elif cur_xml.tag == "attr": self._domain_content[0] = cur_xml if self.ui.rbtn_nodata_yes.isChecked() and not sip.isdeleted(self.nodata_edom): self.nodata_content = (True, self.nodata_edom.to_xml()) else: self.nodata_content = (False, None) def populate_domain_content(self, which="guess"): """ Fill out this widget with the content from it's associated series Parameters ---------- which : str, optional, one of 'guess' or the index to force if guess introspect the series associated with this attribute and make a best guess as to which domain to use. Returns ------- None """ self.clear_domain() if which == "guess": self.sniff_nodata() index = self.guess_domain() else: index = which self.ui.comboBox.setCurrentIndex(index) if index == 0: self.domain = edom_list.EdomList(parent=self) elif index == 1: self.domain = rdom.Rdom(parent=self) elif index == 2: self.domain = codesetd.Codesetd(parent=self) else: self.domain = udom.Udom(parent=self) if self._domain_content[index] is not None: # This domain has been used before, display previous content self.domain.from_xml(self._domain_content[index]) elif self.series is not None and index == 0: clean_series = data_io.clean_nodata(self.series, self.nodata) uniques = clean_series.unique() if len(uniques) > 100: msg = "There are more than 100 unique values in this field." msg += "\n This tool cannot smoothly display that many " "entries. " msg += ( "\nTypically an enumerated domain is not used with " "that many unique entries." ) msg += "\n\nOnly the first one hundred are displayed below!" msg += ( "\nYou will likely want to change the domain to one " "of the other options." ) QMessageBox.warning(self, "Too many unique entries", msg) self.domain.populate_from_list(uniques[:101]) else: self.domain.populate_from_list(uniques) elif self.series is not None and index == 1: clean_series = data_io.clean_nodata(self.series, self.nodata) try: self.domain.ui.fgdc_rdommin.setText(str(clean_series.min())) except: self.domain.ui.fgdc_rdommin.setText("") try: self.domain.ui.fgdc_rdommax.setText(str(clean_series.max())) except: self.domain.ui.fgdc_rdommax.setText("") if not np.issubdtype(clean_series.dtype, np.number): msg = ( "Caution! The contents of this column are stored in the" ' data source as "text". The use of a range domain ' "type on text columns might give unexpected results, " "especially for columns that contain date information." ) msgbox = QMessageBox( QMessageBox.Warning, "Range domain on text field", msg ) utils.set_window_icon(msgbox) msgbox.exec_() self.ui.attrdomv_contents.layout().addWidget(self.domain) def change_domain(self): """ When changing the domain we must first store the current contents in our internal contents dictionary before loading the next. Returns ------- None """ if self.active: self.store_current_content() self.clear_domain() self.populate_domain_content(self.ui.comboBox.currentIndex()) def supersize_me(self): """ Expand this attribute and display it's contents Returns ------- None """ if not self.active: self.active = True self.animation = QPropertyAnimation(self, b"minimumSize") self.animation.setDuration(200) self.animation.setEndValue(QSize(345, self.height())) self.animation.start() self.ui.attrdomv_contents.show() self.ui.place_holder.hide() cbo = self.ui.comboBox self.populate_domain_content(cbo.currentIndex()) self.ui.nodata_content.show() self.nodata_edom = edom.Edom() self.ui.rbtn_nodata_yes.setChecked(self.nodata_content[0]) if self.nodata_content[1] is not None: self.nodata_edom.from_xml(self.nodata_content[1]) self.nodata_edom.ui.fgdc_edomv.textChanged.connect(self.nodata_changed) self.ui.nodata_section.layout().addWidget(self.nodata_edom) def regularsize_me(self): """ Collapse this attribute and hide it's content Returns ------- None """ if self.active: self.store_current_content() self.animation = QPropertyAnimation(self, b"minimumSize") self.animation.setDuration(200) self.animation.setEndValue(QSize(100, self.height())) self.animation.start() self.ui.nodata_content.hide() self.clear_domain() self.clear_nodata() self.ui.place_holder.show() self.active = False def activate(self): """ When an attribute is activated minimize all the other attributes in the parent attribute list Returns ------- None """ if self.active: # we're already big so do nothing pass else: if self.parent_ui is not None: self.parent_ui.minimize_children() self.supersize_me() def nodata_changed(self): """remove """ self.nodata = self.nodata_edom.ui.fgdc_edomv.text() if self.nodata == "<< empty cell >>": self.nodata = "" self.clean_domain_nodata() def clean_domain_nodata(self): if self.domain is not None and not sip.isdeleted(self.domain): cur_xml = self.domain.to_xml() if cur_xml.tag == "rdom": self._domain_content[1] = cur_xml elif cur_xml.tag == "attr": edoms = self.domain.edoms self._domain_content[0] = cur_xml def sniff_nodata(self): uniques = self.series.unique() self.nodata = None for nd in self.nodata_matches: if nd in list(uniques): self.nodata = nd if self.nodata is None: self.nodata_content = (False, self.nodata_content[1]) else: temp_edom = edom.Edom() if self.nodata == "": temp_edom.ui.fgdc_edomv.setText("<< empty cell >>") else: temp_edom.ui.fgdc_edomv.setText(str(self.nodata)) temp_edom.ui.fgdc_edomvd.setPlainText("No Data") self.nodata_content = (True, temp_edom.to_xml()) temp_edom.deleteLater() def contextMenuEvent(self, event): self.in_context = True clicked_widget = self.childAt(event.pos()) menu = QMenu(self) copy_action = menu.addAction(QIcon("copy.png"), "&Copy") copy_action.setStatusTip("Copy to the Clipboard") paste_action = menu.addAction(QIcon("paste.png"), "&Paste") paste_action.setStatusTip("Paste from the Clipboard") menu.addSeparator() insert_before = menu.addAction(QIcon("paste.png"), "Insert before") insert_before.setStatusTip( "insert an empty attribute (column) " "before this one" ) insert_after = menu.addAction(QIcon("paste.png"), "Insert After") insert_after.setStatusTip( "insert an empty attribute (column) after" " this one" ) delete_action = menu.addAction(QIcon("delete.png"), "&Delete") delete_action.setStatusTip("Delete this atttribute (column)") if hasattr(clicked_widget, "help_text") and clicked_widget.help_text: menu.addSeparator() help_action = menu.addAction("Help") else: help_action = None menu.addSeparator() clear_action = menu.addAction("Clear content") action = menu.exec_(self.mapToGlobal(event.pos())) if action == copy_action: if clicked_widget is None: pass elif clicked_widget.objectName() == "idinfo_button": self.idinfo.copy_mime() elif clicked_widget.objectName() == "dataquality_button": self.dataqual.copy_mime() elif clicked_widget.objectName() == "eainfo_button": self.eainfo.copy_mime() elif clicked_widget.objectName() == "distinfo_button": self.distinfo.copy_mime() elif clicked_widget.objectName() == "metainfo_button": self.metainfo.copy_mime() else: self.copy_mime() elif action == paste_action: self.paste_mime() elif action == clear_action: if clicked_widget is None: self.clear_widget() elif clicked_widget.objectName() == "idinfo_button": self.idinfo.clear_widget() elif clicked_widget.objectName() == "dataquality_button": self.dataqual.clear_widget() elif clicked_widget.objectName() == "eainfo_button": self.eainfo.clear_widget() elif clicked_widget.objectName() == "distinfo_button": self.distinfo.clear_widget() elif clicked_widget.objectName() == "metainfo_button": self.metainfo.clear_widget() else: self.clear_widget() elif action == insert_before: self.parent_ui.insert_before(self) elif action == insert_after: self.parent_ui.insert_after(self) elif action == delete_action: self.parent_ui.delete_attr(self) elif help_action is not None and action == help_action: msg = QMessageBox(self) # msg.setTextFormat(Qt.RichText) msg.setText(clicked_widget.help_text) msg.setWindowTitle("Help") msg.show() self.in_context = False def to_xml(self): """ return an XML element with the contents of this widget, augmented with any original content not displayed on the widget if applicable. Returns ------- XML Element """ cur_index = self.ui.comboBox.currentIndex() if self.active: self.store_current_content() domain = self.domain.to_xml() elif self._domain_content[cur_index] is not None: domain = self._domain_content[cur_index] else: self.populate_domain_content(cur_index) domain = self.domain.to_xml() if self.ui.comboBox.currentIndex() == 0: attr = xml_utils.XMLNode(domain) attr.clear_children(tag="attrlabl") attr.clear_children(tag="attrdef") attr.clear_children(tag="attrdefs") attr = attr.to_xml() else: attr = xml_utils.xml_node("attr") attrdomv = xml_utils.xml_node("attrdomv", parent_node=attr) attrdomv.append(domain) attrlabl = xml_utils.xml_node( "attrlabl", text=self.ui.fgdc_attrlabl.text(), parent_node=attr, index=0 ) attrdef = xml_utils.xml_node( "attrdef", text=self.ui.fgdc_attrdef.toPlainText(), parent_node=attr, index=1, ) attrdefs = xml_utils.xml_node( "attrdefs", text=self.ui.fgdc_attrdefs.text(), parent_node=attr, index=2 ) if self.nodata_content[0]: attrdomv = xml_utils.xml_node("attrdomv", parent_node=attr, index=3) attrdomv.append(self.nodata_content[1]) return attr def from_xml(self, attr): """ Populate widget with a representation of the passed XML element Parameters ---------- attr : XML Element Returns ------- None """ try: self.clear_widget() if attr.tag == "attr": utils.populate_widget(self, attr) attr_node = xml_utils.XMLNode(attr) attrdomvs = attr_node.xpath("attrdomv", as_list=True) attr_domains = [a.children[0].tag for a in attrdomvs] # pull out the first no data attribute domain for attrdomv in attrdomvs: if attrdomv.children[0].tag == "edom" and ( attrdomv.children[0].children[0].text in self.nodata_matches or attrdomv.children[0].children[1].text.lower() in ["nodata", "no data"] or (attr_domains.count("edom") == 1) and len(attr_domains) > 1 ): self.ui.rbtn_nodata_yes.setChecked(True) self.nodata_content = (1, attrdomv.children[0].to_xml()) attrdomvs.remove(attrdomv) attr_domains.remove("edom") try: edomv = attr.xpath( "attrdomv/edom/edomv[text()='{}']".format( attrdomv.children[0].children[0].text ) )[0] nd_attrdomv = edomv.getparent().getparent() nd_attrdomv.getparent().remove(nd_attrdomv) except: pass break if len(set(attr_domains)) > 1: # multiple domain types present in this attr msg = "Multiple domain types found in the attribute/column '{}'." msg += "\ni.e. more than one of Enumerated, Range, " msg += "Codeset, and Unrepresentable was used to " msg += "was used to describe a single column.\n\n" msg += "While this is valid in the FGDC schema the " msg += "MetadataWizard is not designed to handle this." msg += "\n\nOnly the first of these domains will be displayed " msg += "and retained in the output saved from this tool." msg += "\n\nIf having this structure is import please use" msg += " a different tool for editing this section." msg = msg.format(self.ui.fgdc_attrlabl.text()) msgbox = QMessageBox( QMessageBox.Warning, "Too many domain types", msg ) utils.set_window_icon(msgbox) msgbox.exec_() if len(attrdomvs) == 0: self.ui.comboBox.setCurrentIndex(3) elif attr_domains[0] == "edom": self.ui.comboBox.setCurrentIndex(0) self._domain_content[0] = attr elif attr_domains[0] == "udom": self.ui.comboBox.setCurrentIndex(3) self._domain_content[3] = attr.xpath("attrdomv/udom")[0] elif attr_domains[0] == "rdom": self.ui.comboBox.setCurrentIndex(1) self._domain_content[1] = attr.xpath("attrdomv/rdom")[0] elif attr_domains[0] == "codesetd": self.ui.comboBox.setCurrentIndex(2) self._domain_content[2] = attr.xpath("attrdomv/codesetd")[0] else: self.ui.comboBox.setCurrentIndex(3) else: print("The tag is not attr") except KeyError: pass
class ToolDialogFrame: def __init__(self, containers: QFrame, page_containers_grid_layout: QGridLayout, gui_concentrate_handler): super(ToolDialogFrame, self).__init__() self.containers = containers self.gui_concentrate_handler = gui_concentrate_handler self.page_containers_grid_layout: QGridLayout = page_containers_grid_layout self.frame_tools = QFrame(self.containers) self.frame_tools.resize(68, 78) self.frame_tools.setStyleSheet(ToolDialogFrameStyles.all_frame_style) self.frame_tools.setFrameShape(QFrame.StyledPanel) self.frame_tools.setFrameShadow(QFrame.Raised) self.frame_tools.setObjectName("frame_tools") self.frame_concat_to_frame_tools = QFrame(self.containers) self.frame_concat_to_frame_tools.resize(500, 168) self.frame_concat_to_frame_tools.setStyleSheet(ToolDialogFrameStyles.all_frame_style) self.frame_concat_to_frame_tools.setFrameShape(QFrame.StyledPanel) self.frame_concat_to_frame_tools.setFrameShadow(QFrame.Raised) self.frame_concat_to_frame_tools.setObjectName("frame_concat_to_frame_tools") self.ease_out = QEasingCurve(QEasingCurve.OutQuad) self.ease_in = QEasingCurve(QEasingCurve.InQuad) def setup_ui(self, page_containers: QFrame): self.lbl_tools = QLabel(self.frame_tools) self.lbl_tools.setText("Tools") self.lbl_tools.setGeometry(QRect(0, 27, 68, 21)) self.lbl_tools.setStyleSheet(ToolDialogFrameStyles.lbl_frames_style) self.lbl_tools.setAlignment(Qt.AlignCenter) self.lbl_tools.setObjectName("lbl_tools") self.lbl_ellipse_tools = QLabel(self.frame_tools) self.lbl_ellipse_tools.setGeometry(QRect(0, 43, 68, 21)) self.lbl_ellipse_tools.setStyleSheet(ToolDialogFrameStyles.transparent_color_style) self.lbl_ellipse_tools.setPixmap(QPixmap(AppPaths.GUI_ASSETS_ICONS_PATH + "/main_window/ellipse_logo.svg")) self.lbl_ellipse_tools.setAlignment(Qt.AlignCenter) self.lbl_ellipse_tools.setObjectName("lbl_ellipse_tools") self.lbl_close_frame_tools = QLabel(self.frame_concat_to_frame_tools) self.lbl_close_frame_tools.setGeometry(QRect(5, 5, 21, 21)) self.lbl_close_frame_tools.setCursor(QCursor(Qt.PointingHandCursor)) self.lbl_close_frame_tools.setPixmap(QPixmap(AppPaths.GUI_ASSETS_ICONS_PATH + "/main_window/close_logo.svg")) self.lbl_close_frame_tools.setObjectName("lbl_close_frame_tools") self.card_forensic = QLabel(self.frame_concat_to_frame_tools) self.card_forensic.setText("Forensic") self.card_forensic.setGeometry(QRect(25, 25, 71, 46)) self.card_forensic.setCursor(QCursor(Qt.PointingHandCursor)) self.card_forensic.setStyleSheet(ToolDialogFrameStyles.cards_in_frame_style) self.card_forensic.setAlignment(Qt.AlignBottom | Qt.AlignHCenter) self.card_forensic.setObjectName("[TOOL]-[FORENSIC]-Forensic Tools") self.pic_forensic = QLabel(self.frame_concat_to_frame_tools) self.pic_forensic.setGeometry(QRect(40, 10, 41, 31)) self.pic_forensic.setCursor(QCursor(Qt.PointingHandCursor)) self.pic_forensic.setStyleSheet(ToolDialogFrameStyles.transparent_color_style) self.pic_forensic.setPixmap(QPixmap(AppPaths.GUI_ASSETS_ICONS_PATH + "/main_window/forensic_logo.svg")) self.pic_forensic.setAlignment(Qt.AlignBottom | Qt.AlignHCenter) self.pic_forensic.setObjectName("pic_forensic") self.card_lot = QLabel(self.frame_concat_to_frame_tools) self.card_lot.setText("lot") self.card_lot.setGeometry(QRect(112, 25, 71, 46)) self.card_lot.setCursor(QCursor(Qt.PointingHandCursor)) self.card_lot.setStyleSheet(ToolDialogFrameStyles.cards_in_frame_style) self.card_lot.setAlignment(Qt.AlignBottom | Qt.AlignHCenter) self.card_lot.setObjectName("[TOOL]-[IOT]-IOT Tools") self.pic_lot_logo = QLabel(self.frame_concat_to_frame_tools) self.pic_lot_logo.setGeometry(QRect(126, 10, 41, 31)) self.pic_lot_logo.setCursor(QCursor(Qt.PointingHandCursor)) self.pic_lot_logo.setStyleSheet(ToolDialogFrameStyles.transparent_color_style) self.pic_lot_logo.setPixmap(QPixmap(AppPaths.GUI_ASSETS_ICONS_PATH + "/main_window/lot_logo.svg")) self.pic_lot_logo.setAlignment(Qt.AlignBottom | Qt.AlignHCenter) self.pic_lot_logo.setObjectName("pic_lot_logo") self.card_network = QLabel(self.frame_concat_to_frame_tools) self.card_network.setGeometry(QRect(286, 25, 71, 46)) self.card_network.setText("Network") self.card_network.setCursor(QCursor(Qt.PointingHandCursor)) self.card_network.setStyleSheet(ToolDialogFrameStyles.cards_in_frame_style) self.card_network.setAlignment(Qt.AlignBottom | Qt.AlignHCenter) self.card_network.setObjectName("[TOOL]-[NETWORK]-Network Tools") self.pic_network_logo = QLabel(self.frame_concat_to_frame_tools) self.pic_network_logo.setGeometry(QRect(300, 10, 41, 31)) self.pic_network_logo.setCursor(QCursor(Qt.PointingHandCursor)) self.pic_network_logo.setStyleSheet(ToolDialogFrameStyles.transparent_color_style) self.pic_network_logo.setPixmap(QPixmap(AppPaths.GUI_ASSETS_ICONS_PATH + "/main_window/network_logo.svg")) self.pic_network_logo.setAlignment(Qt.AlignBottom | Qt.AlignHCenter) self.pic_network_logo.setObjectName("pic_network_logo") self.card_dev_ops = QLabel(self.frame_concat_to_frame_tools) self.card_dev_ops.setText("DevOps") self.card_dev_ops.setGeometry(QRect(199, 25, 71, 46)) self.card_dev_ops.setCursor(QCursor(Qt.PointingHandCursor)) self.card_dev_ops.setStyleSheet(ToolDialogFrameStyles.cards_in_frame_style) self.card_dev_ops.setAlignment(Qt.AlignBottom | Qt.AlignHCenter) self.card_dev_ops.setObjectName("[TOOL]-[DEVOPS]-DevOps Tools") self.pic_dev_ops_logo = QLabel(self.frame_concat_to_frame_tools) self.pic_dev_ops_logo.setGeometry(QRect(215, 10, 41, 31)) self.pic_dev_ops_logo.setCursor(QCursor(Qt.PointingHandCursor)) self.pic_dev_ops_logo.setStyleSheet(ToolDialogFrameStyles.transparent_color_style) self.pic_dev_ops_logo.setPixmap(QPixmap(AppPaths.GUI_ASSETS_ICONS_PATH + "/main_window/dev_ops_logo.svg")) self.pic_dev_ops_logo.setAlignment(Qt.AlignBottom | Qt.AlignHCenter) self.pic_dev_ops_logo.setObjectName("pic_dev_ops_logo") self.card_monitoring = QLabel(self.frame_concat_to_frame_tools) self.card_monitoring.setText("Monitoring") self.card_monitoring.setGeometry(QRect(373, 105, 71, 46)) self.card_monitoring.setCursor(QCursor(Qt.PointingHandCursor)) self.card_monitoring.setStyleSheet(ToolDialogFrameStyles.cards_in_frame_style) self.card_monitoring.setAlignment(Qt.AlignBottom | Qt.AlignHCenter) self.card_monitoring.setObjectName("[TOOL]-[MONITORING]-Monitoring Tools") self.pic_monitoring_logo = QLabel(self.frame_concat_to_frame_tools) self.pic_monitoring_logo.setGeometry(QRect(388, 90, 41, 31)) self.pic_monitoring_logo.setCursor(QCursor(Qt.PointingHandCursor)) self.pic_monitoring_logo.setStyleSheet(ToolDialogFrameStyles.transparent_color_style) self.pic_monitoring_logo.setPixmap(QPixmap(AppPaths.GUI_ASSETS_ICONS_PATH + "/main_window/monitoring_logo.svg")) self.pic_monitoring_logo.setAlignment(Qt.AlignBottom | Qt.AlignHCenter) self.pic_monitoring_logo.setObjectName("pic_monitoring_logo") self.card_web = QLabel(self.frame_concat_to_frame_tools) self.card_web.setText("Web") self.card_web.setGeometry(QRect(373, 25, 71, 46)) self.card_web.setCursor(QCursor(Qt.PointingHandCursor)) self.card_web.setStyleSheet(ToolDialogFrameStyles.cards_in_frame_style) self.card_web.setAlignment(Qt.AlignBottom | Qt.AlignHCenter) self.card_web.setObjectName("[TOOL]-[WEB]-Web Tools") self.pic_web_logo = QLabel(self.frame_concat_to_frame_tools) self.pic_web_logo.setGeometry(QRect(388, 10, 41, 31)) self.pic_web_logo.setCursor(QCursor(Qt.PointingHandCursor)) self.pic_web_logo.setStyleSheet(ToolDialogFrameStyles.transparent_color_style) self.pic_web_logo.setPixmap(QPixmap(AppPaths.GUI_ASSETS_ICONS_PATH + "/main_window/web_logo.svg")) self.pic_web_logo.setAlignment(Qt.AlignBottom | Qt.AlignHCenter) self.pic_web_logo.setObjectName("pic_web_logo") self.set_events(page_containers) def set_events(self, page_containers: QFrame): """this method for Arguments: page_containers {QFrame} -- [description] """ UtilsClick.clickable(self.lbl_close_frame_tools).connect(lambda: self.set_visibility_effect(True, False, True)) UtilsClick.clickable(self.card_dev_ops).connect(partial( self.item_clicked, page_containers=page_containers, object_name=self.card_dev_ops.objectName() )) UtilsClick.clickable(self.pic_dev_ops_logo).connect(partial( self.item_clicked, page_containers=page_containers, object_name=self.card_dev_ops.objectName() )) UtilsClick.clickable(self.card_web).connect(partial( self.item_clicked, page_containers=page_containers, object_name=self.card_web.objectName() )) UtilsClick.clickable(self.pic_web_logo).connect(partial( self.item_clicked, page_containers=page_containers, object_name=self.card_web.objectName() )) UtilsClick.clickable(self.card_lot).connect(partial( self.item_clicked, page_containers=page_containers, object_name=self.card_lot.objectName() )) UtilsClick.clickable(self.pic_lot_logo).connect(partial( self.item_clicked, page_containers=page_containers, object_name=self.card_lot.objectName() )) UtilsClick.clickable(self.card_forensic).connect(partial( self.item_clicked, page_containers=page_containers, object_name=self.card_forensic.objectName() )) UtilsClick.clickable(self.pic_forensic).connect(partial( self.item_clicked, page_containers=page_containers, object_name=self.card_forensic.objectName() )) UtilsClick.clickable(self.card_monitoring).connect(partial( self.item_clicked, page_containers=page_containers, object_name=self.card_monitoring.objectName() )) UtilsClick.clickable(self.pic_monitoring_logo).connect(partial( self.item_clicked, page_containers=page_containers, object_name=self.card_monitoring.objectName() )) UtilsClick.clickable(self.card_network).connect(partial( self.item_clicked, page_containers=page_containers, object_name=self.card_network.objectName() )) UtilsClick.clickable(self.pic_network_logo).connect(partial( self.item_clicked, page_containers=page_containers, object_name=self.card_network.objectName() )) def set_visibility_effect(self, visibility: bool, is_anime: bool, is_close: bool = False): """this method for set visibility frame tools Arguments: visibility {bool} -- [this argument show status visibility frame ] is_anime {bool} -- [this argument show anime effect] Keyword Arguments: is_close {bool} -- [this argument when you want close frame] (default: {False}) """ x_location_frame_tools: int = int(self.containers.width() - 81) x_location_frame_tools_concat: int = int(x_location_frame_tools + 10) if visibility and is_anime: # start animation self.do_anim_frame_tools( self.frame_tools, QRect(x_location_frame_tools, 280, 68, 78), QRect(x_location_frame_tools, 360, 68, 78) ) self.do_anim_concat_frame_tools( self.frame_concat_to_frame_tools, QRect(x_location_frame_tools_concat-161, 280, 161, 78), QRect(x_location_frame_tools_concat - self.frame_concat_to_frame_tools.width(), 360, 500, 168) ) if is_close: # click close button self.do_anim_frame_tools( self.frame_tools, QRect(x_location_frame_tools, 360, 68, 78), QRect(x_location_frame_tools + self.containers.width(), 360, 68, 78) ) self.do_anim_concat_frame_tools( self.frame_concat_to_frame_tools, QRect(x_location_frame_tools_concat, 360, 500, 78), QRect(x_location_frame_tools_concat + self.containers.width(), 360, 500, 168) ) self.frame_tools.setVisible(visibility) self.frame_concat_to_frame_tools.setVisible(visibility) del x_location_frame_tools, x_location_frame_tools_concat def do_anim_frame_tools(self, obj: QFrame, start_location: QRect, end_location: QRect): """ this method for do animation frame tools Arguments: obj {QFrame} -- [object must be do animation effect] start_location {QRect} -- [start location for anime] end_location {QRect} -- [end location for anime] """ self.anim = QPropertyAnimation(obj, b"geometry") self.anim.setStartValue(start_location) self.anim.setEndValue(end_location) self.anim.setDuration(200) self.anim.setEasingCurve(self.ease_out) self.anim.start(QPropertyAnimation.DeleteWhenStopped) def do_anim_concat_frame_tools(self, obj: QFrame, start_location: QRect, end_location: QRect): """ this method for do animation frame concat to frame tools tools Arguments: obj {QFrame} -- [object must be do animation effect] start_location {QRect} -- [start location for anime] end_location {QRect} -- [end location for anime] """ self.anim_2 = QPropertyAnimation(obj, b"geometry") self.anim_2.setStartValue(start_location) self.anim_2.setEndValue(end_location) self.anim_2.setDuration(200) self.anim_2.setEasingCurve(self.ease_out) self.anim_2.start(QPropertyAnimation.DeleteWhenStopped) def item_clicked(self, page_containers: QFrame, object_name: str): """ this method when call client push devops item Arguments: page_containers {QFrame} -- [devops layout in this layer] """ # Delete Children in Parent from commons.constants.gui_class_names import GUIClassNames self.gui_concentrate_handler.notify(message={ "data": { "value": object_name }, "params": None, "command": { "name": "[CHANGE_NAME]" } }, to=GUIClassNames.TOP_NAVIGATION_BAR_CONTAINERS) # delete all pervious widget that created on page_containers_grid_layout from ...central_page_maker.central_page_maker import CentralPageMaker from utils.widget_helper import WidgetHelper page_containers.setVisible(False) widget_helper = WidgetHelper() widget_helper.delete_all_widget(self.page_containers_grid_layout) del widget_helper self.page_containers_grid_layout.addWidget(CentralPageMaker(). setup_ui( containers=page_containers, page_name=object_name ), 0, 1, 1, 1) page_containers.setVisible(True) self.set_visibility_effect(True, False, True)
class Expander(QWidget): expanded = pyqtSignal(object) clicked = pyqtSignal() def __init__(self, header, normal_icon="", hovered_icon="", selected_icon=""): QWidget.__init__(self) self.is_expanded = False self.text = header self.label_normal_color = self.palette().link().color().name() self.label_hovered_color = self.palette().highlight().color().name() self.label_selected_color = self.palette().highlightedText().color( ).name() self.normal_color = self.palette().base().color().name() self.selected_color = self.palette().highlight().color() self.hovered_color = self.palette().alternateBase().color() self.setCursor(Qt.PointingHandCursor) if normal_icon: self.normal_icon = QImage(normal_icon) if hovered_icon: self.hovered_icon = QImage(hovered_icon) if selected_icon: self.selected_icon = QImage(selected_icon) self.setAttribute(Qt.WA_Hover, True) self.color = self.normal_color self.setAutoFillBackground(True) vbox = QVBoxLayout() hbox = QHBoxLayout() self.icon = QLabel() self.icon.setPixmap(QPixmap.fromImage(self.normal_icon)) self.hyper = QLabel() self.hyper.setText("<a style=\"color: " + self.label_normal_color + " text-decoration: none\" href=\"#\">" + self.text + "</a>") hbox.addWidget(self.icon) hbox.addSpacing(5) hbox.addWidget(self.hyper) hbox.addStretch() hbox.setContentsMargins(8, 8, 8, 8) vbox.addLayout(hbox) self.content = QWidget() self.content.setStyleSheet("background-color: " + self.palette().base().color().name()) self.content.setMaximumHeight(0) vbox.addWidget(self.content) vbox.setContentsMargins(0, 0, 0, 0) self.setLayout(vbox) self.hyper.linkActivated.connect(self.buttonClicked) self.anim = QParallelAnimationGroup() self.height_anim = QPropertyAnimation(self.content, "maximumHeight".encode("utf-8")) self.color_anim = QPropertyAnimation(self, "color".encode("utf-8")) self.height_anim.setDuration(200) self.color_anim.setDuration(200) self.anim.addAnimation(self.height_anim) self.anim.addAnimation(self.color_anim) def setExpanded(self, value): if value == self.is_expanded: return if value: self.is_expanded = True pal = self.palette() pal.setColor(QPalette.Background, self.selected_color) self.setPalette(pal) self.icon.setPixmap(QPixmap.fromImage(self.selected_icon)) self.hyper.setText("<a style=\"color: " + self.label_selected_color + "; text-decoration: none;\" href=\"#\">" + self.text + "</a>") else: self.is_expanded = False pal = self.palette() pal.setColor(QPalette.Background, QColor(self.normal_color)) self.setPalette(pal) self.icon.setPixmap(QPixmap.fromImage(self.normal_icon)) self.hyper.setText("<a style=\"color: " + self.label_normal_color + "; text-decoration: none;\" href=\"#\">" + self.text + "</a>") if self.is_expanded: self.expandContent() else: self.collapseContent() self.expanded.emit(self.is_expanded) def addLayout(self, layout): self.content.setLayout(layout) @pyqtProperty('QColor') def color(self): return Qt.black @color.setter def color(self, color): pal = self.palette() pal.setColor(QPalette.Background, QColor(color)) self.setPalette(pal) def mouseReleaseEvent(self, me): self.setExpanded(not self.is_expanded) if self.is_expanded: self.clicked.emit() def expandContent(self): if self.content.layout(): self.height_anim.setEndValue( self.content.layout().sizeHint().height()) else: self.height_anim.setEndValue(0) self.height_anim.setStartValue(0) self.color_anim.setStartValue(self.normal_color) self.color_anim.setEndValue(self.selected_color) self.anim.start() def collapseContent(self): if self.content.layout(): self.height_anim.setStartValue( self.content.layout().sizeHint().height()) else: self.height_anim.setStartValue(0) self.height_anim.setEndValue(0) self.color_anim.setStartValue(self.selected_color) self.color_anim.setEndValue(self.normal_color) self.anim.start() def buttonClicked(self): self.setExpanded(not self.is_expanded) if self.is_expanded: self.clicked.emit() def enterEvent(self, event): if not self.is_expanded: pal = self.palette() pal.setColor(QPalette.Background, QColor(self.hovered_color)) self.setPalette(pal) self.icon.setPixmap(QPixmap.fromImage(self.hovered_icon)) self.hyper.setText("<a style=\"color: " + self.label_hovered_color + "; text-decoration: none;\" href=\"#\">" + self.text + "</a>") QWidget.enterEvent(self, event) def leaveEvent(self, event): if not self.is_expanded: pal = self.palette() pal.setColor(QPalette.Background, QColor(self.normal_color)) self.setPalette(pal) self.icon.setPixmap(QPixmap.fromImage(self.normal_icon)) self.hyper.setText("<a style=\"color: " + self.label_normal_color + "; text-decoration: none;\" href=\"#\">" + self.text + "</a>") QWidget.leaveEvent(self, event)
class Screen(QWidget): card_w = 200 card_h = 250 card_start_x = SCREEN_X / 2 - card_w / 2 card_start_y = SCREEN_Y / 2 - card_h / 2 card_pause_x = SCREEN_X / 2 - card_w / 2 card_pause_y = 0.4275 * SCREEN_Y - card_h / 2 card_pause2_x = SCREEN_X / 2 - card_w / 2 card_pause2_y = 0.4225 * SCREEN_Y - card_h / 2 card_pause3_x = SCREEN_X / 2 - card_w / 2 card_pause3_y = 0.4175 * SCREEN_Y - card_h / 2 card_end_x = SCREEN_X / 2 - card_w / 2 card_end_y = SCREEN_Y / 6 - card_h / 2 speed = 1000 def __init__(self): super().__init__() self.initUI() def initUI(self): self.initButtons() self.printerback = QLabel(self) self.printerback.setStyleSheet( "QLabel {border-image: url(./images/printer-back.png) 0 0 0 0 stretch stretch}" ) self.printerback.setGeometry( QRect(SCREEN_X / 2 - SCREEN_X / 4.2, SCREEN_Y / 2 - SCREEN_Y / 2.625, SCREEN_X / 2.1, SCREEN_Y / 1.3125)) self.label = PrintCard(self, self.card_start_x, self.card_start_y, self.card_w, self.card_h) self.label.setText(self.chooseWord()) self.printerfront = QLabel(self) self.printerfront.setStyleSheet( "QLabel {border-image: url(./images/printer-front.png) 0 0 0 0 stretch stretch}" ) self.printerfront.setGeometry( QRect(SCREEN_X / 2 - SCREEN_X / 4.2, SCREEN_Y / 2 - SCREEN_Y / 2.625, SCREEN_X / 2.1, SCREEN_Y / 1.3125)) self.gameover = QLabel(self) self.gameover.setVisible(False) self.gameover.setText("Game Over!") self.gameover.setStyleSheet("font-size: 30pt") self.gameover.setGeometry( QRect(SCREEN_X / 6 - SCREEN_X / 16.8, SCREEN_Y / 2, SCREEN_X / 8.4, SCREEN_Y / 25)) self.anim = QPropertyAnimation(self.label, b"geometry") self.anim.setDuration(self.speed) self.anim.setStartValue( QRect(self.label.x(), self.label.y(), self.label.card_w, self.label.card_h)) self.anim.setEndValue( QRect(self.card_pause_x, self.card_pause_y, self.label.card_w, self.label.card_h)) self.anim2 = QPropertyAnimation(self.label, b"geometry") self.anim2.setDuration(200) self.anim2.setEndValue( QRect(self.card_pause2_x, self.card_pause2_y, self.label.card_w, self.label.card_h)) self.anim3 = QPropertyAnimation(self.label, b"geometry") self.anim3.setDuration(200) self.anim3.setEndValue( QRect(self.card_pause3_x, self.card_pause3_y, self.label.card_w, self.label.card_h)) self.animFinish = QPropertyAnimation(self.label, b"geometry") self.animFinish.setDuration(self.speed) self.animFinish.setEndValue( QRect(self.card_end_x, self.card_end_y, self.label.card_w, self.label.card_h)) self.startbutton.clicked.connect(self.startButtonClicked) self.randombutton.clicked.connect(self.randomButtonClicked) self.printbutton.clicked.connect(self.printButtonClicked) self.print2button.clicked.connect(self.print2ButtonClicked) self.print3button.clicked.connect(self.print3ButtonClicked) self.finishprintbutton.clicked.connect(self.finishprintButtonClicked) self.reprintbutton.clicked.connect(self.reprintButtonClicked) self.nextbutton.clicked.connect(self.nextButtonClicked) self.restartbutton.clicked.connect(self.restartButtonClicked) self.exitbutton.clicked.connect(self.exit) self.animFinish.finished.connect(self.endAnimation) self.setWindowTitle("Printer") self.setGeometry(0, 0, SCREEN_X, SCREEN_Y) self.show() def initButtons(self): self.startbutton = PrinterButton("Start", self, width=SCREEN_X / 8.4, height=SCREEN_Y / 25, xPos=SCREEN_X / 6 - SCREEN_X / 16.8, yPos=SCREEN_Y / 2, font_size="20pt", background_color="#77eb34", background_color_pressed="#449615", border_radius="8px") self.randombutton = PrinterButton("Randomize Words", self, width=SCREEN_X / 8.4, height=SCREEN_Y / 25, xPos=SCREEN_X / 6 - SCREEN_X / 16.8, yPos=SCREEN_Y / 2 - SCREEN_Y / 25 - 15, font_size="20pt", background_color="#f49eff", background_color_pressed="#a843cc", border_radius="8px") self.printbutton = PrinterButton("Print", self, visible=False, width=SCREEN_X / 8.4, height=SCREEN_Y / 25, xPos=SCREEN_X / 6 - SCREEN_X / 16.8, yPos=SCREEN_Y / 2, font_size="20pt", background_color="#77eb34", background_color_pressed="#449615", border_radius="8px") self.print2button = PrinterButton("Print More", self, visible=False, width=SCREEN_X / 8.4, height=SCREEN_Y / 25, xPos=SCREEN_X / 6 - SCREEN_X / 16.8, yPos=SCREEN_Y / 2 + SCREEN_Y / 25 + 15, font_size="20pt", background_color="#77eb34", background_color_pressed="#449615", border_radius="8px") self.print3button = PrinterButton("Print More", self, visible=False, width=SCREEN_X / 8.4, height=SCREEN_Y / 25, xPos=SCREEN_X / 6 - SCREEN_X / 16.8, yPos=SCREEN_Y / 2 + SCREEN_Y / 25 + 15, font_size="20pt", background_color="#77eb34", background_color_pressed="#449615", border_radius="8px") self.finishprintbutton = PrinterButton( "Finish Printing", self, visible=False, width=SCREEN_X / 8.4, height=SCREEN_Y / 25, xPos=SCREEN_X / 6 - SCREEN_X / 16.8, yPos=SCREEN_Y / 2, font_size="20pt", background_color="#ff9717", background_color_pressed="#8f4f00", border_radius="8px") self.reprintbutton = PrinterButton("Print Again", self, visible=False, width=SCREEN_X / 8.4, height=SCREEN_Y / 25, xPos=SCREEN_X / 6 - SCREEN_X / 16.8, yPos=SCREEN_Y / 2 + SCREEN_Y / 25 + 15, font_size="20pt", background_color="#77eb34", background_color_pressed="#449615", border_radius="8px") self.nextbutton = PrinterButton("Next Page", self, visible=False, width=SCREEN_X / 8.4, height=SCREEN_Y / 25, xPos=SCREEN_X / 6 - SCREEN_X / 16.8, yPos=SCREEN_Y / 2, font_size="20pt", background_color="#f49eff", background_color_pressed="#a843cc", border_radius="8px") self.restartbutton = PrinterButton("Restart", self, visible=False, width=SCREEN_X / 8.4, height=SCREEN_Y / 25, xPos=SCREEN_X / 6 - SCREEN_X / 16.8, yPos=SCREEN_Y / 2 - SCREEN_Y / 25 - 15, font_size="20pt", background_color="#30f5ff", background_color_pressed="17a6ff", border_radius="8px") self.exitbutton = PrinterButton("Exit", self, width=SCREEN_X / 8.4, height=SCREEN_Y / 25, xPos=SCREEN_X / 6 - SCREEN_X / 16.8, yPos=8 * SCREEN_Y / 10 - SCREEN_Y / 50, font_size="20pt", background_color="#fc3003", background_color_pressed="a11216", border_radius="8px") def startButtonClicked(self): self.startbutton.setVisible(False) self.randombutton.setVisible(False) self.printbutton.setVisible(True) self.restartbutton.setVisible(True) def randomButtonClicked(self): global WORDS WORDS = read_words.shuffle(WORDS) self.restartButtonClicked() def printButtonClicked(self): self.anim.start() self.printbutton.setVisible(False) self.print2button.setVisible(True) self.finishprintbutton.setVisible(True) def print2ButtonClicked(self): if self.anim.state() == 2: return self.anim2.setStartValue( QRect(self.label.x(), self.label.y(), self.label.card_w, self.label.card_h)) self.anim2.start() self.print2button.setVisible(False) self.print3button.setVisible(True) def print3ButtonClicked(self): if self.anim2.state() == 2: return self.anim3.setStartValue( QRect(self.label.x(), self.label.y(), self.label.card_w, self.label.card_h)) self.anim3.start() self.print3button.setVisible(False) def finishprintButtonClicked(self): if (self.anim.state() == 2 or self.anim2.state() == 2 or self.anim3.state() == 2): return self.animFinish.setStartValue( QRect(self.label.x(), self.label.y(), self.label.card_w, self.label.card_h)) self.animFinish.start() self.printbutton.setVisible(False) self.print2button.setVisible(False) self.print3button.setVisible(False) self.finishprintbutton.setVisible(False) self.nextbutton.setVisible(True) self.reprintbutton.setVisible(True) def reprintButtonClicked(self): if self.animFinish.state() == 2: return global INDEX INDEX -= 1 self.nextButtonClicked() def nextButtonClicked(self): if self.animFinish.state() == 2: return if INDEX >= len(WORDS): self.restartbutton.setVisible(True) self.reprintbutton.setVisible(False) self.nextbutton.setVisible(False) self.gameover.setVisible(True) return self.resetUI() def restartButtonClicked(self): if self.anim.state() == 2: return global INDEX INDEX = 0 self.resetUI(main_menu=True) def endAnimation(self): self.printbutton.setVisible(False) self.finishprintbutton.setVisible(False) self.nextbutton.setVisible(True) self.reprintbutton.setVisible(True) def chooseWord(self): global INDEX word = WORDS[INDEX] INDEX += 1 return word def resetUI(self, main_menu=False): if main_menu: self.startbutton.setVisible(True) self.randombutton.setVisible(True) self.printbutton.setVisible(False) self.restartbutton.setVisible(False) else: self.startbutton.setVisible(False) self.randombutton.setVisible(False) self.printbutton.setVisible(True) self.restartbutton.setVisible(True) self.gameover.setVisible(False) self.anim.setCurrentTime(0) self.animFinish.setCurrentTime(0) self.reprintbutton.setVisible(False) self.nextbutton.setVisible(False) self.label.setText(self.chooseWord()) self.label.move(self.card_start_x, self.card_start_y) def exit(self): sys.exit(0)
class CoffeeFundWindow(QWidget): signal_back = pyqtSignal() signal_coffee = pyqtSignal() signal_account = pyqtSignal() def __init__(self, parent=None): super().__init__() self.cards = {} # type: Dict[GuiCards, QWidget] """ Python's GC will clean up QPropertyAnimations as soon as it leaves the button handler, therefore they will appear not to work. Use members to store the animations. see http://stackoverflow.com/a/6953965 """ self.slide_in_animation = None self.slide_out_animation = None self.animation_group = None self.setObjectName("coffeeFundWindow") """ Store the position and size of visible/hidden cards for the animation sequences """ self.hidden_geometry = None self.visible_geometry = None layout = QStackedLayout() layout.setStackingMode(QStackedLayout.StackAll) card_remove = RemoveCard() self.cards[GuiCards.RemoveCard] = card_remove layout.addWidget(card_remove) card_choose_action = ChooseActionCard() self.cards[GuiCards.ChooseAction] = card_choose_action layout.addWidget(card_choose_action) card_account = AccountCard() self.cards[GuiCards.AccountInfo] = card_account layout.addWidget(card_account) # keep this as last initialized card, the last card will be shown on startup! card_start = StartCard() self.cards[GuiCards.Start] = card_start layout.addWidget(card_start) self.setLayout(layout) self.setWindowTitle("Kaffeekasse") layout.setCurrentWidget(card_start) self.active_card = None card_choose_action.button_account.clicked.connect(self.signal_account) card_choose_action.button_coffee.clicked.connect(self.signal_coffee) card_account.button_back.clicked.connect(self.signal_back) def set_card_hidden(self, card: QWidget): card.setGeometry(self.hidden_geometry) def show_start(self): self.show_card(GuiCards.Start) def show_account(self, name, value): self.cards[GuiCards.AccountInfo].set_user_name(name) self.cards[GuiCards.AccountInfo].set_balance(value) self.show_card(GuiCards.AccountInfo) def show_choose_action(self, name: str): self.cards[GuiCards.ChooseAction].set_user_name(name) self.show_card(GuiCards.ChooseAction) def show_remove(self): self.show_card(GuiCards.RemoveCard) def show_card(self, card_id: GuiCards): if self.active_card is None: self.active_card = self.cards[GuiCards.Start] if self.active_card == self.cards[card_id]: return if self.visible_geometry is None or self.hidden_geometry is None: self.visible_geometry = self.active_card.geometry() # type: QRect self.hidden_geometry = QRect(self.visible_geometry.x(), self.visible_geometry.height() * 1.5, self.visible_geometry.width(), self.visible_geometry.height()) for key in self.cards.keys(): if key != self.active_card: self.set_card_hidden(self.cards[key]) card_to_show = self.cards[card_id] self.start_card_switch(card_to_show) self.active_card = self.cards[card_id] self.layout().setCurrentWidget(self.active_card) def start_card_switch(self, card_to_show): self.slide_out_animation = QPropertyAnimation(self.active_card, "geometry") self.slide_out_animation.setDuration(ANIMATION_DURATION) self.slide_out_animation.setEasingCurve(QEasingCurve.OutCubic) self.slide_out_animation.setStartValue(self.visible_geometry) self.slide_out_animation.setEndValue(self.hidden_geometry) self.set_card_hidden(card_to_show) self.slide_in_animation = QPropertyAnimation(card_to_show, "geometry") self.slide_in_animation.setDuration(ANIMATION_DURATION) self.slide_in_animation.setEasingCurve(QEasingCurve.InCubic) self.slide_in_animation.setStartValue(self.hidden_geometry) self.slide_in_animation.setEndValue(self.visible_geometry) self.animation_group = QParallelAnimationGroup() self.animation_group.addAnimation(self.slide_out_animation) self.animation_group.addAnimation(self.slide_in_animation) self.animation_group.start()
class MiniMap(QPlainTextEdit): def __init__(self, parent): super(MiniMap, self).__init__(parent) self.setWordWrapMode(QTextOption.NoWrap) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setReadOnly(True) self.setCenterOnScroll(True) self.setMouseTracking(True) self.viewport().setCursor(Qt.PointingHandCursor) self.setTextInteractionFlags(Qt.NoTextInteraction) self._parent = parent self.highlighter = None self.lines_count = 0 if ACTIVATE_OPACITY: self.goe = QGraphicsOpacityEffect() self.setGraphicsEffect(self.goe) self.goe.setOpacity(settings.MINIMAP_MIN_OPACITY) self.animation = QPropertyAnimation(self.goe, b"opacity") self.slider = SliderArea(self) self.slider.show() def __calculate_max(self): line_height = self._parent.cursorRect().height() if line_height > 0: self.lines_count = self._parent.viewport().height() / line_height self.slider.update_position() self.update_visible_area() def set_code(self, source): self.setPlainText(source) self.__calculate_max() def adjust_to_parent(self): self.setFixedHeight(self._parent.height()) self.setFixedWidth(self._parent.width() * settings.SIZE_PROPORTION) x = self._parent.width() - self.width() self.move(x, 0) fontsize = int(self.width() / settings.MARGIN_LINE) if fontsize < 1: fontsize = 1 font = self.document().defaultFont() font.setPointSize(fontsize) self.setFont(font) self.__calculate_max() def update_visible_area(self): if not self.slider.pressed: line_number = self._parent.firstVisibleBlock().blockNumber() block = self.document().findBlockByLineNumber(line_number) cursor = self.textCursor() cursor.setPosition(block.position()) rect = self.cursorRect(cursor) self.setTextCursor(cursor) self.slider.move_slider(rect.y()) def enterEvent(self, event): if ACTIVATE_OPACITY: self.animation.setDuration(300) self.animation.setStartValue(settings.MINIMAP_MIN_OPACITY) self.animation.setEndValue(settings.MINIMAP_MAX_OPACITY) self.animation.start() def leaveEvent(self, event): if ACTIVATE_OPACITY: self.animation.setDuration(300) self.animation.setStartValue(settings.MINIMAP_MAX_OPACITY) self.animation.setEndValue(settings.MINIMAP_MIN_OPACITY) self.animation.start() def mousePressEvent(self, event): super(MiniMap, self).mousePressEvent(event) cursor = self.cursorForPosition(event.pos()) self._parent.jump_to_line(cursor.blockNumber()) def resizeEvent(self, event): super(MiniMap, self).resizeEvent(event) self.slider.update_position() def scroll_area(self, pos_parent, pos_slider): pos_parent.setY(pos_parent.y() - pos_slider.y()) cursor = self.cursorForPosition(pos_parent) self._parent.verticalScrollBar().setValue(cursor.blockNumber()) def wheelEvent(self, event): super(MiniMap, self).wheelEvent(event) self._parent.wheelEvent(event)
class Invite(QWidget): """ An invite is a small notification being displayed in the bottom right corner of the window. It fades in and out, and is used to invite an user to jump to a certain location. Some other uses might be added later. """ def __init__(self, plugin, parent=None): super(Invite, self).__init__(parent) self._plugin = plugin self._time = 0 self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool | Qt.WindowStaysOnTopHint) self.setAttribute(Qt.WA_ShowWithoutActivating) self.setAttribute(Qt.WA_TranslucentBackground) self._icon = QLabel() self._icon.setAutoFillBackground(False) self._icon.setAttribute(Qt.WA_TranslucentBackground) self._text = QLabel() self._text.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self._layout = QHBoxLayout() self._layout.addWidget(self._text) self.setLayout(self._layout) # Fade in and out animation self._popup_opacity = 0.0 self._animation = QPropertyAnimation() self._animation.setTargetObject(self) self._animation.setPropertyName(b"popup_opacity") self._animation.finished.connect(self.hide) # Timer used to auto-close the window self._timer = QTimer() self._timer.timeout.connect(self.hide_animation) self._callback = None self._triggered = False @property def time(self): return self._time @time.setter def time(self, time): self._time = time @property def text(self): return self._text.text() @text.setter def text(self, text): self._text.setText(text) self.adjustSize() @property def icon(self): return self._icon.pixmap() @icon.setter def icon(self, pixmap): # Resize the given pixmap pixmap_height = self._text.sizeHint().height() self._icon.setPixmap( pixmap.scaled( pixmap_height, pixmap_height, Qt.KeepAspectRatio, Qt.SmoothTransformation, )) self._layout.insertWidget(0, self._icon) @property def callback(self): return self._callback @callback.setter def callback(self, callback): self._callback = callback @property def triggered(self): return self._triggered @triggered.setter def triggered(self, triggered): self._triggered = triggered def paintEvent(self, event): # noqa: N802 """We override the painting event to draw the invite ourselves.""" painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) rect = QRect(self.rect()) # Draw the border painter.setBrush(QBrush(QColor(122, 122, 122))) painter.setPen(Qt.NoPen) painter.drawRect(rect) rect.setX(rect.x() + 1) rect.setY(rect.y() + 1) rect.setWidth(rect.width() - 1) rect.setHeight(rect.height() - 1) # Draw the background painter.setBrush(QBrush(QColor(255, 255, 225))) painter.setPen(Qt.NoPen) painter.drawRect(rect) def mouseReleaseEvent(self, event): # noqa: N802 """ This function is called when the user clicks the invite. It triggers the callback function is it has been specified, and hides the invite. """ if self._callback: self._callback() self._triggered = True self._popup_opacity = 0.0 self.hide() def show(self): """Shows the invite to user. It triggers a fade in effect.""" self._plugin.logger.debug("Showing invite %s" % self.text) self.setWindowOpacity(0.0) self._animation.setDuration(500) self._animation.setStartValue(0.0) self._animation.setEndValue(1.0) # Map the notification to the bottom right corner pos = QPoint(self.parent().width() - 25, self.parent().height() - 50) pos = self.parent().mapToGlobal(pos) self.setGeometry( pos.x() - self.width(), pos.y() - self.height(), self.width(), self.height(), ) super(Invite, self).show() self._animation.start() self._timer.start(3500) def hide(self): """Hides the invite only if it is fully transparent.""" if self._popup_opacity == 0.0: self._plugin.interface.widget.refresh() super(Invite, self).hide() def hide_animation(self): """Hides the invite. It triggers the fade out animation.""" self._timer.stop() self._animation.setDuration(500) self._animation.setStartValue(1.0) self._animation.setEndValue(0.0) self._animation.start() @pyqtProperty(float) def popup_opacity(self): return self._popup_opacity @popup_opacity.setter def popup_opacity(self, opacity): self._popup_opacity = opacity self.setWindowOpacity(opacity)
class QSlideSwitchPrivate(QObject): def __init__(self, q): QObject.__init__(self) self._position = 0 self._sliderShape = QRectF() self._gradient = QLinearGradient() self._gradient.setSpread(QGradient.PadSpread) self._qPointer = q self.animation = QPropertyAnimation(self, b"position") self.animation.setTargetObject(self) # self.animation.setPropertyName() self.animation.setStartValue(0) self.animation.setEndValue(1) self.animation.setDuration(300) self.animation.setEasingCurve(QEasingCurve.InOutExpo) def __del__(self): del self.animation @pyqtProperty(float) def position(self): return self._position @position.setter def position(self, value): self._position = value self._qPointer.repaint() def drawSlider(self, painter): margin = 3 r = self._qPointer.rect().adjusted(0, 0, -1, -1) dx = (r.width() - self._sliderShape.width()) * self._position sliderRect = self._sliderShape.translated(dx, 0) painter.setPen(Qt.NoPen) # basic settings shadow = self._qPointer.palette().color(QPalette.Dark) light = self._qPointer.palette().color(QPalette.Light) button = self._qPointer.palette().color(QPalette.Button) # draw background # draw outer background self._gradient.setColorAt(0, shadow.darker(130)) self._gradient.setColorAt(1, light.darker(130)) self._gradient.setStart(0, r.height()) self._gradient.setFinalStop(0, 0) painter.setBrush(self._gradient) painter.drawRoundedRect(r, 15, 15) # draw background # draw inner background self._gradient.setColorAt(0, shadow.darker(140)) self._gradient.setColorAt(1, light.darker(160)) self._gradient.setStart(0, 0) self._gradient.setFinalStop(0, r.height()) painter.setBrush(self._gradient) painter.drawRoundedRect(r.adjusted(margin, margin, -margin, -margin), 15, 15) # draw slider self._gradient.setColorAt(0, button.darker(130)) self._gradient.setColorAt(1, button) # draw outer slider self._gradient.setStart(0, r.height()) self._gradient.setFinalStop(0, 0) painter.setBrush(self._gradient) painter.drawRoundedRect(sliderRect.adjusted(margin, margin, -margin, -margin), 10, 15) # draw inner slider self._gradient.setStart(0, 0) self._gradient.setFinalStop(0, r.height()) painter.setBrush(self._gradient) painter.drawRoundedRect(sliderRect.adjusted(2.5 * margin, 2.5 * margin, -2.5 * margin, - 2.5 * margin), 5, 15) # draw text if self.animation.state() == QPropertyAnimation.Running: return # don't draw any text while animation is running font = self._qPointer.font() self._gradient.setColorAt(0, light) self._gradient.setColorAt(1, shadow) self._gradient.setStart(0, r.height() / 2.0 + font.pointSizeF()) self._gradient.setFinalStop(0, r.height() / 2.0 - font.pointSizeF()) painter.setFont(font) painter.setPen(QPen(QBrush(self._gradient), 0)) if self._qPointer.isChecked(): painter.drawText(0, 0, r.width() / 2, r.height() - 1, Qt.AlignCenter, "ON") else: painter.drawText(r.width() / 2, 0, r.width() / 2, r.height() - 1, Qt.AlignCenter, "OFF") def updateSliderRect(self, size): self._sliderShape.setWidth(size.width() / 2.0) self._sliderShape.setHeight(size.height() - 1.0) @pyqtSlot(bool, name='animate') def animate(self, checked): self.animation.setDirection(QPropertyAnimation.Forward if checked else QPropertyAnimation.Backward) self.animation.start()
class RecoveryKeyExporter(QObject): done = pyqtSignal(str) def __init__(self, parent=None): super(RecoveryKeyExporter, self).__init__(parent) self.parent = parent self.filepath = None self.progress = None self.animation = None self.crypter = None self.crypter_thread = None self.ciphertext = None def _on_encryption_failed(self, message): self.crypter_thread.quit() error(self.parent, "Error encrypting data", message) self.crypter_thread.wait() def _on_encryption_succeeded(self, ciphertext): self.crypter_thread.quit() if self.filepath: with open(self.filepath, 'wb') as f: f.write(ciphertext) self.done.emit(self.filepath) self.filepath = None else: self.ciphertext = ciphertext self.crypter_thread.wait() def _export_encrypted_recovery(self, gateway, password): settings = gateway.get_settings(include_rootcap=True) if gateway.use_tor: settings['hide-ip'] = True data = json.dumps(settings) self.progress = QProgressDialog("Encrypting...", None, 0, 100) self.progress.show() self.animation = QPropertyAnimation(self.progress, b'value') self.animation.setDuration(6000) # XXX self.animation.setStartValue(0) self.animation.setEndValue(99) self.animation.start() self.crypter = Crypter(data.encode(), password.encode()) self.crypter_thread = QThread() self.crypter.moveToThread(self.crypter_thread) self.crypter.succeeded.connect(self.animation.stop) self.crypter.succeeded.connect(self.progress.close) self.crypter.succeeded.connect(self._on_encryption_succeeded) self.crypter.failed.connect(self.animation.stop) self.crypter.failed.connect(self.progress.close) self.crypter.failed.connect(self._on_encryption_failed) self.crypter_thread.started.connect(self.crypter.encrypt) self.crypter_thread.start() dest, _ = QFileDialog.getSaveFileName( self.parent, "Select a destination", os.path.join(os.path.expanduser('~'), gateway.name + ' Recovery Key.json.encrypted')) if not dest: return if self.ciphertext: with open(dest, 'wb') as f: f.write(self.ciphertext) self.done.emit(dest) self.ciphertext = None else: self.filepath = dest def _export_plaintext_recovery(self, gateway): dest, _ = QFileDialog.getSaveFileName( self.parent, "Select a destination", os.path.join(os.path.expanduser('~'), gateway.name + ' Recovery Key.json')) if not dest: return try: gateway.export(dest, include_rootcap=True) except Exception as e: # pylint: disable=broad-except error(self.parent, "Error exporting Recovery Key", str(e)) return self.done.emit(dest) def do_export(self, gateway): password, ok = PasswordDialog.get_password( self.parent, "Encryption passphrase (optional):", "A long passphrase will help keep your files safe in the event " "that your Recovery Key is ever compromised.") if ok and password: self._export_encrypted_recovery(gateway, password) elif ok: self._export_plaintext_recovery(gateway)
class RecoveryKeyImporter(QObject): done = pyqtSignal(dict) def __init__(self, parent=None): super(RecoveryKeyImporter, self).__init__() self.parent = parent self.filepath = None self.progress = None self.animation = None self.crypter = None self.crypter_thread = None def _on_decryption_failed(self, msg): logging.error("%s", msg) self.crypter_thread.quit() if msg == "Decryption failed. Ciphertext failed verification": msg = "The provided passphrase was incorrect. Please try again." reply = QMessageBox.critical(self.parent, "Decryption Error", msg, QMessageBox.Abort | QMessageBox.Retry) self.crypter_thread.wait() if reply == QMessageBox.Retry: self._load_from_file(self.filepath) def _on_decryption_succeeded(self, plaintext): logging.debug("Decryption of %s succeeded", self.filepath) self.crypter_thread.quit() try: settings = json.loads(plaintext.decode('utf-8')) except (UnicodeDecodeError, json.decoder.JSONDecodeError) as e: error(self, type(e).__name__, str(e)) return self.done.emit(settings) self.crypter_thread.wait() def _decrypt_content(self, data, password): logging.debug("Trying to decrypt %s...", self.filepath) self.progress = QProgressDialog( "Trying to decrypt {}...".format(os.path.basename(self.filepath)), None, 0, 100) self.progress.show() self.animation = QPropertyAnimation(self.progress, b'value') self.animation.setDuration(6000) # XXX self.animation.setStartValue(0) self.animation.setEndValue(99) self.animation.start() self.crypter = Crypter(data, password.encode()) self.crypter_thread = QThread() self.crypter.moveToThread(self.crypter_thread) self.crypter.succeeded.connect(self.animation.stop) self.crypter.succeeded.connect(self.progress.close) self.crypter.succeeded.connect(self._on_decryption_succeeded) self.crypter.failed.connect(self.animation.stop) self.crypter.failed.connect(self.progress.close) self.crypter.failed.connect(self._on_decryption_failed) self.crypter_thread.started.connect(self.crypter.decrypt) self.crypter_thread.start() def _parse_content(self, content): try: settings = json.loads(content.decode('utf-8')) except (UnicodeDecodeError, json.decoder.JSONDecodeError): logging.debug("JSON decoding failed; %s is likely encrypted", self.filepath) password, ok = PasswordDialog.get_password( self.parent, "Decryption passphrase (required):", "This Recovery Key is protected by a passphrase. Enter the " "correct passphrase to decrypt it.", show_stats=False) if ok: self._decrypt_content(content, password) return self.done.emit(settings) def _load_from_file(self, path): logging.debug("Loading %s...", self.filepath) try: with open(path, 'rb') as f: content = f.read() except Exception as e: # pylint: disable=broad-except error(self, type(e).__name__, str(e)) return self._parse_content(content) def _select_file(self): dialog = QFileDialog(self.parent, "Select a Recovery Key") dialog.setDirectory(os.path.expanduser('~')) dialog.setFileMode(QFileDialog.ExistingFile) if dialog.exec_(): return dialog.selectedFiles()[0] return None def do_import(self, filepath=None): if not filepath: filepath = self._select_file() self.filepath = filepath if self.filepath: self._load_from_file(self.filepath)
class NS_Animate(object): def __init__(self, scene, x_max, y_max, back_color): scene = QGraphicsScene(0, 0, x_max, y_max) scene.setBackgroundBrush(back_color) color = [Qt.green, Qt.lightGray, Qt.darkYellow, QtGui.QColor.fromRgb(255, 85, 0)] self.anim_butt = [ QGraphicsRectWidget(color[j]) for j in range(4) ] for j in range(4): scene.addItem(self.anim_butt[j]) self.window = QGraphicsView(scene) self.window.setFrameStyle(0) self.window.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.window.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.window.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.machine = QStateMachine() self.group = QState() self.timer = QTimer() self.timer.setInterval(1250) self.timer.setSingleShot(True) self.group.entered.connect(self.timer.start) # set states positions anim_state_rects = [ [QRect(x_max*xp/6, y_max*yp/4, 8, 8) for xp in range(4)] for yp in range(4) ] self.states = [ self.createGeometryState( self.anim_butt[0], anim_state_rects[0][j], self.anim_butt[1], anim_state_rects[1][j], self.anim_butt[2], anim_state_rects[2][j], self.anim_butt[3], anim_state_rects[3][j], self.group ) for j in range(4) ] self.group.setInitialState(self.states[0]) self.animationGroup = QParallelAnimationGroup() self.anim = QPropertyAnimation(self.anim_butt[3], 'geometry') self.anim.setDuration(1250) self.anim.setEasingCurve(QEasingCurve.InBack) self.animationGroup.addAnimation(self.anim) self.subGroup = QSequentialAnimationGroup(self.animationGroup) self.subGroup.addPause(100) self.anim = QPropertyAnimation(self.anim_butt[2], 'geometry') self.anim.setDuration(1000) self.anim.setEasingCurve(QEasingCurve.OutElastic) self.subGroup.addAnimation(self.anim) self.subGroup = QSequentialAnimationGroup(self.animationGroup) self.subGroup.addPause(500) self.anim = QPropertyAnimation(self.anim_butt[1], 'geometry') self.anim.setDuration(500) self.anim.setEasingCurve(QEasingCurve.OutElastic) self.subGroup.addAnimation(self.anim) self.subGroup = QSequentialAnimationGroup(self.animationGroup) self.subGroup.addPause(750) self.anim = QPropertyAnimation(self.anim_butt[0], 'geometry') self.anim.setDuration(250) self.anim.setEasingCurve(QEasingCurve.OutElastic) self.subGroup.addAnimation(self.anim) self.stateSwitcher = StateSwitcher(self.machine) self.group.addTransition(self.timer.timeout, self.stateSwitcher) for j in range(4): self.stateSwitcher.addState(self.states[j], self.animationGroup) self.machine.addState(self.group) self.machine.setInitialState(self.group) self.machine.start() # def createGeometryState(self, w1, rect1, w2, rect2, w3, rect3, w4, rect4, parent): result = QState(parent) result.assignProperty(w1, 'geometry', rect1) result.assignProperty(w1, 'geometry', rect1) result.assignProperty(w2, 'geometry', rect2) result.assignProperty(w3, 'geometry', rect3) result.assignProperty(w4, 'geometry', rect4) return result
class WindowWithTitleBar(QFrame): def __init__(self, mainwidget, parent=0): super(WindowWithTitleBar, self).__init__() self.setObjectName('WindowWithTitleBar') self.m_titlebar = Titlebar(self) self.initWidgetsAndPack(mainwidget, self.m_titlebar) self.initStretch() self.initTipLabel(mainwidget) def initTipLabel(self, parent): """ 消息提示标签 :param parent: :return: """ self.tipLabel = QLabel(parent) self.tipLabel.setFixedSize(200, 40) self.tipLabel.setAlignment(Qt.AlignCenter) self.tipLabel.setWordWrap(True) self.tipLabel.setGeometry(self.width() / 2 - self.tipLabel.width() / 2, self.tipLabel.y(), self.tipLabel.width(), self.tipLabel.height()) self.tipLabel.hide() def showTip(self, text, color='#20c3ff'): if self.tipLabel is not None: self.tipLabel.show() self.tipLabel.setStyleSheet( "QLabel{background: %s;border:3px; color: #FFFFFF; border-radius: 5px}" % color) self.tipLabel.setText(text) eff = QGraphicsOpacityEffect(self) self.tipLabel.setGraphicsEffect(eff) self.animate = QPropertyAnimation(eff, "opacity") self.animate.setDuration(2000) self.animate.setStartValue(0.8) self.animate.setEndValue(0) self.animate.setEasingCurve(QEasingCurve.InCubic) self.animate.finished.connect(lambda: self.tipLabel.hide()) self.animate.start() def initWidgetsAndPack(self, mainwidget, titlebar): """ 将主体Widget和titleBar拼装起来 :param mainwidget: :param titlebar: :return: """ self.mainwidget = mainwidget self.resize(mainwidget.width(), mainwidget.height() + Titlebar.TITLEBAR_HEIGHT) self.setWindowFlags(Qt.FramelessWindowHint | self.windowFlags()) self.installEventFilter(titlebar) # 布局: titlbar在上主窗体在下 pLayout = QVBoxLayout(self) pLayout.addWidget(titlebar) pLayout.addWidget(mainwidget) pLayout.setSpacing(0) # 排列的几个widget为0间隔 pLayout.setContentsMargins(0, 0, 0, 0) self.setLayout(pLayout) def initStretch(self): """ 初始化拉伸功能 :return: """ self.setSupportStretch(True) self.m_isWindowMax = False self.m_stretchRectState = NO_SELECT self.m_isMousePressed = False self.setMinimumSize( self.mainwidget.minimumWidth(), self.mainwidget.minimumHeight() + Titlebar.TITLEBAR_HEIGHT) def getTitbar(self): return self.m_titlebar def setMinimumSize(self, width, height): """ 设置拉伸的最小Size :param width: :param height: :return: """ self.m_windowMinWidth = width self.m_windowMinHeight = height super(WindowWithTitleBar, self).setMinimumSize(width, height) def setWindowRadius(self, n_px): """ 圆边 :param n_px: 弧度 :return: """ objBitmap = QBitmap(self.size()) painter = QPainter(objBitmap) painter.setBrush(QColor(0, 0, 0)) painter.drawRoundedRect(self.rect(), n_px, n_px) self.setMask(objBitmap) def setBackgroundBorderColor(self, bgdcolor, bordercolor): self.setStyleSheet( "WindowWithTitleBar{background:%s;border:3px solid %s}" % (bgdcolor, bordercolor)) def closeEvent(self, *args, **kwargs): self.mainwidget.close() def showEvent(self, event): self.calculateCurrentStrechRect() return super().showEvent(event) def calculateCurrentStrechRect(self): # 四个角Rect self.m_leftTopRect = QRect(0, 0, STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT) self.m_leftBottomRect = QRect(0, self.height() - STRETCH_RECT_HEIGHT, STRETCH_RECT_WIDTH, STRETCH_RECT_WIDTH) self.m_rightTopRect = QRect(self.width() - STRETCH_RECT_WIDTH, 0, STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT) self.m_rightBottomRect = QRect(self.width() - STRETCH_RECT_WIDTH, self.height() - STRETCH_RECT_HEIGHT, STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT) # 四条边Rect self.m_topBorderRect = QRect(STRETCH_RECT_WIDTH, 0, self.width() - STRETCH_RECT_WIDTH * 2, STRETCH_RECT_HEIGHT) self.m_rightBorderRect = QRect(self.width() - STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT, STRETCH_RECT_WIDTH, self.height() - STRETCH_RECT_HEIGHT * 2) self.m_bottomBorderRect = QRect(STRETCH_RECT_WIDTH, self.height() - STRETCH_RECT_HEIGHT, self.width() - STRETCH_RECT_WIDTH * 2, STRETCH_RECT_HEIGHT) self.m_leftBorderRect = QRect(0, STRETCH_RECT_HEIGHT, STRETCH_RECT_WIDTH, self.height() - STRETCH_RECT_HEIGHT * 2) def getCurrentStretchState(self, cursorPos): """ 根据鼠标的位置获取StretchState :param cursorPos: :return: """ if self.m_leftTopRect.contains(cursorPos): stretchState = LEFT_TOP_RECT elif self.m_rightTopRect.contains(cursorPos): stretchState = RIGHT_TOP_RECT elif self.m_rightBottomRect.contains(cursorPos): stretchState = RIGHT_BOTTOM_RECT elif self.m_leftBottomRect.contains(cursorPos): stretchState = LEFT_BOTTOM_RECT elif self.m_topBorderRect.contains(cursorPos): stretchState = TOP_BORDER elif self.m_rightBorderRect.contains(cursorPos): stretchState = RIGHT_BORDER elif self.m_bottomBorderRect.contains(cursorPos): stretchState = BOTTOM_BORDER elif self.m_leftBorderRect.contains(cursorPos): stretchState = LEFT_BORDER else: stretchState = NO_SELECT return stretchState def updateMouseStyle(self, stretchState): """ 根据stretchState刷新鼠标的样式 :param stretchState: :return: """ if stretchState == NO_SELECT: self.setCursor(Qt.ArrowCursor) elif stretchState == LEFT_TOP_RECT: self.setCursor(Qt.SizeFDiagCursor) elif stretchState == RIGHT_BOTTOM_RECT: self.setCursor(Qt.SizeFDiagCursor) elif stretchState == TOP_BORDER: self.setCursor(Qt.SizeVerCursor) elif stretchState == BOTTOM_BORDER: self.setCursor(Qt.SizeVerCursor) elif stretchState == RIGHT_TOP_RECT: self.setCursor(Qt.SizeBDiagCursor) elif stretchState == LEFT_BOTTOM_RECT: self.setCursor(Qt.SizeBDiagCursor) elif stretchState == LEFT_BORDER: self.setCursor(Qt.SizeHorCursor) elif stretchState == RIGHT_BORDER: self.setCursor(Qt.SizeHorCursor) else: self.setCursor(Qt.ArrowCursor) def mouseMoveEvent(self, event): """ 重写mouseMoveEvent事件,用于获取当前鼠标的位置,将位置传递给getCurrentStretchState方法, 得到当前鼠标的状态,然后调用updateMouseStyle对鼠标的样式进行更新 :param event: :return: """ # 如果窗口最大化是不能拉伸的 # 也不用更新鼠标样式 if (self.m_isWindowMax): return super().mouseMoveEvent(event) # 如果当前鼠标未按下,则根据当前鼠标的位置更新鼠标的状态及样式 if not self.m_isMousePressed: cursorPos = event.pos() # 根据当前鼠标的位置显示不同的样式 self.m_stretchRectState = self.getCurrentStretchState(cursorPos) self.updateMouseStyle(self.m_stretchRectState) # 如果当前鼠标左键已经按下,则记录下第二个点的位置,并更新窗口的大小 else: self.m_endPoint = self.mapToGlobal(event.pos()) self.updateWindowSize() return super().mouseMoveEvent(event) def mousePressEvent(self, event): # 当前鼠标进入了以上指定的8个区域,并且是左键按下时才开始进行窗口拉伸 if (self.m_stretchRectState != NO_SELECT and event.button() == Qt.LeftButton): self.m_isMousePressed = True # 记录下当前鼠标位置,为后面计算拉伸位置 self.m_startPoint = self.mapToGlobal(event.pos()) # 保存下拉伸前的窗口位置及大小 self.m_windowRectBeforeStretch = QRect(self.geometry().x(), self.geometry().y(), self.geometry().width(), self.geometry().height()) return super().mousePressEvent(event) def mouseReleaseEvent(self, event): """ 鼠标松开后意味之窗口拉伸结束,置标志位,并且重新计算用于拉伸的8个区域Rect :param event: :return: """ self.m_isMousePressed = False self.calculateCurrentStrechRect() return super().mouseReleaseEvent(event) def updateWindowSize(self): """ 拉伸窗口过程中,根据记录的坐标更新窗口大小 :return: """ windowRect = QRect(self.m_windowRectBeforeStretch.x(), self.m_windowRectBeforeStretch.y(), self.m_windowRectBeforeStretch.width(), self.m_windowRectBeforeStretch.height()) delValue_X = self.m_startPoint.x() - self.m_endPoint.x() delValue_Y = self.m_startPoint.y() - self.m_endPoint.y() if self.m_stretchRectState == LEFT_BORDER: topLeftPoint = windowRect.topLeft() topLeftPoint.setX(topLeftPoint.x() - delValue_X) windowRect.setTopLeft(topLeftPoint) elif self.m_stretchRectState == RIGHT_BORDER: bottomRightPoint = windowRect.bottomRight() bottomRightPoint.setX(bottomRightPoint.x() - delValue_X) windowRect.setBottomRight(bottomRightPoint) elif self.m_stretchRectState == TOP_BORDER: topLeftPoint = windowRect.topLeft() topLeftPoint.setY(topLeftPoint.y() - delValue_Y) windowRect.setTopLeft(topLeftPoint) elif self.m_stretchRectState == BOTTOM_BORDER: bottomRightPoint = windowRect.bottomRight() bottomRightPoint.setY(bottomRightPoint.y() - delValue_Y) windowRect.setBottomRight(bottomRightPoint) elif self.m_stretchRectState == LEFT_TOP_RECT: topLeftPoint = windowRect.topLeft() topLeftPoint.setX(topLeftPoint.x() - delValue_X) topLeftPoint.setY(topLeftPoint.y() - delValue_Y) windowRect.setTopLeft(topLeftPoint) elif self.m_stretchRectState == RIGHT_TOP_RECT: topRightPoint = windowRect.topRight() topRightPoint.setX(topRightPoint.x() - delValue_X) topRightPoint.setY(topRightPoint.y() - delValue_Y) windowRect.setTopRight(topRightPoint) elif self.m_stretchRectState == RIGHT_BOTTOM_RECT: bottomRightPoint = windowRect.bottomRight() bottomRightPoint.setX(bottomRightPoint.x() - delValue_X) bottomRightPoint.setY(bottomRightPoint.y() - delValue_Y) windowRect.setBottomRight(bottomRightPoint) elif self.m_stretchRectState == LEFT_BOTTOM_RECT: bottomLeftPoint = windowRect.bottomLeft() bottomLeftPoint.setX(bottomLeftPoint.x() - delValue_X) bottomLeftPoint.setY(bottomLeftPoint.y() - delValue_Y) windowRect.setBottomLeft(bottomLeftPoint) # 避免宽或高为零窗口显示有误,这里给窗口设置最小拉伸高度、宽度 if windowRect.width() < self.m_windowMinWidth: windowRect.setLeft(self.geometry().left()) windowRect.setWidth(self.m_windowMinWidth) if windowRect.height() < self.m_windowMinHeight: windowRect.setTop(self.geometry().top()) windowRect.setHeight(self.m_windowMinHeight) self.setGeometry(windowRect) def setSupportStretch(self, isSupportStretch): """ 设置当前窗口是否支持拉伸 :param isSupportStretch: :return: """ # 因为需要在鼠标未按下的情况下通过mouseMoveEvent事件捕捉鼠标位置,所以需要设置setMouseTracking为True(如果窗口支持拉伸) self.m_isSupportStretch = isSupportStretch self.setMouseTracking(isSupportStretch) # 这里对子控件也进行了设置,是因为如果不对子控件设置,当鼠标移动到子控件上时,不会发送mouseMoveEvent事件,也就获取不到当前鼠标位置,无法判断鼠标状态及显示样式了。 widgetList = self.findChildren(QWidget) for widget in widgetList: widget.setMouseTracking(isSupportStretch) if (self.m_titlebar is not None): # titleBar同理,也需要对自己及子控件进行调用setMouseTracking进行设置,见上方注释 # self.titleBar.setSupportStretch(isSupportStretch) pass def getSupportStretch(self): """ 返回当前窗口是否支持拉伸 :return: """ return self.m_isSupportStretch def setMaxEnable(self, isEnable): """ 最大化开闭 :param isEnable """ self.m_titlebar.setMaximumEnable(isEnable)
class SongInfoCard(QWidget): """ 歌曲信息卡 """ def __init__(self, songInfo: dict = None, parent=None): super().__init__(parent) self.resize(320, 55) # 创建小部件 self.songNameLabel = QLabel(self) self.songerNameLabel = QLabel(self) self.opacityEffect = QGraphicsOpacityEffect(self) self.ani = QPropertyAnimation(self.opacityEffect, b"opacity") # 初始化 self.__initWidget() # 设置窗口信息 self.updateCard(songInfo) def __initWidget(self): """ 初始化小部件 """ self.setFixedHeight(55) self.opacityEffect.setOpacity(1) self.setGraphicsEffect(self.opacityEffect) self.setAttribute(Qt.WA_TranslucentBackground) self.songNameLabel.setProperty("name", "smallestModeSongNameLabel") self.songerNameLabel.setProperty("name", "smallestModeSongerNameLabel") def __setSongInfo(self, songInfo: dict): """ 设置标签信息 """ if not songInfo: songInfo = {} self.songName = songInfo.get("songName", "未知歌曲") # type:str self.songerName = songInfo.get("songer", "未知歌手") # type:str self.songNameLabel.setText(self.songName) self.songerNameLabel.setText(self.songerName) def updateCard(self, songInfo: dict): """ 更新窗口 """ self.__setSongInfo(songInfo) self.__adjustLabel() def __adjustLabel(self): """ 根据当前窗口的宽度设置标签文本和位置 """ fontMetrics = QFontMetrics(QFont("Microsoft YaHei", 12, 75)) # 字符串的最大宽度 maxWidth = self.width() - 30 songNameWidth, songerNameWidth = 0, 0 # 调整歌名 for index, char in enumerate(self.songName): if songNameWidth + fontMetrics.width(char) > maxWidth: self.songNameLabel.setText(self.songName[:index]) break songNameWidth += fontMetrics.width(char) self.songNameLabel.setFixedWidth(songNameWidth) # 调整歌手名 fontMetrics = QFontMetrics(QFont("Microsoft YaHei", 11)) for index, char in enumerate(self.songerName): if songerNameWidth + fontMetrics.width(char) > maxWidth: self.songerNameLabel.setText(self.songerName[:index]) break songerNameWidth += fontMetrics.width(char) self.songerNameLabel.setFixedWidth(songerNameWidth) # 调整标签位置 self.songNameLabel.move(int(self.width() / 2 - songNameWidth / 2), 0) self.songerNameLabel.move(int(self.width() / 2 - songerNameWidth / 2), 30) def resizeEvent(self, e): """ 改变窗口大小时调整标签 """ super().resizeEvent(e) self.__adjustLabel() def aniHide(self): """ 淡出 """ self.ani.setStartValue(1) self.ani.setEndValue(0) self.ani.setDuration(150) self.ani.finished.connect(self.__hideAniSlot) self.ani.start() def aniShow(self): """ 淡入 """ super().show() self.ani.setStartValue(0) self.ani.setEndValue(1) self.ani.setDuration(150) self.ani.start() def __hideAniSlot(self): """ 淡出动画完成的槽函数 """ self.ani.disconnect() super().hide()
class NumberItem(SudokuItem): adj = 5 valid_input = True def __init__(self, parent, row, col, num, rect: QRectF, disabled=False): super().__init__(parent=parent) self.rect = rect.adjusted(self.adj, self.adj, -self.adj, -self.adj) self.disabled = disabled self.row = row self.col = col # Create a link to the sudoku game instance. self.game = self.parent.parent.game if num == 0: self.num = '' else: self.num = num self.animations() if not self.disabled: self.setFlags(QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemIsSelectable) self.setAcceptHoverEvents(True) self.setCacheMode(QGraphicsItem.DeviceCoordinateCache) self.setTransformOriginPoint(self.rect.center()) def paint(self, painter, option, widget): # Change font color when user input is false. # Default pen color is set to black. black_pen = QPen(Qt.black, 1) red_pen = QPen(Qt.red, 1) painter.setPen(black_pen) brush = QBrush(Qt.NoBrush) if self.disabled: brush = QBrush(QColor(200, 200, 200), Qt.SolidPattern) if self.isSelected(): width = self.rect.width() * 1.1 height = self.rect.height() * 1.1 x = self.rect.x() - ((width - self.rect.width()) / 2) y = self.rect.y() - ((height - self.rect.height()) / 2) rect = QRectF(x, y, width, height) brush = QBrush(QColor(160, 200, 255), Qt.SolidPattern) else: rect = self.rect painter.setBrush(brush) painter.setFont(QFont('Helvetica', 14, 50)) painter.setRenderHint(QPainter.Antialiasing) painter.drawEllipse(rect) if self.parent.parent.show_errors and not self.valid_input: painter.setPen(red_pen) painter.drawText(rect, Qt.AlignCenter, f'{self.num}') def boundingRect(self): return self.rect.adjusted(-self.adj, -self.adj, self.adj, self.adj) def hoverEnterEvent(self, e): if not self.disabled: self.setCursor(Qt.IBeamCursor) self.start_animations(QAbstractAnimation.Forward) def hoverMoveEvent(self, e): pass def hoverLeaveEvent(self, e): if not self.disabled: self.setCursor(Qt.ArrowCursor) self.start_animations(QAbstractAnimation.Backward) def mousePressEvent(self, e): # Clear any item that may be selected that belongs to the same parent. # Also, remove the focus from that item. for item in self.parent.childItems(): item.setSelected(False) item.setFocus(False) # Set the current item as selected, and give it focus. self.setSelected(True) self.setFocus(True) def keyPressEvent(self, e): print() # Only act on the item that currently has keyboard focus. if self.hasFocus(): # On backspace or delete, remove the current number from spot. if e.key() == 16777219 or e.key() == 16777223: self.num = '' self.game.board[self.row][self.col] = 0 elif e.key() >= 49 and e.key() <= 57: # If the number already exists in spot, do nothing. if str(e.key() - 48) == self.num: return # Otherwise, set the number to the key that was pressed. self.num = str(e.key() - 48) board_copy = deepcopy(self.game.board) board_copy[self.row][self.col] = int(self.num) # Check to make sure that there are no overlaps in columns, and rows, and the board is solveable. if self.game.check_input(int(self.num), self.row, self.col, self.game.board): self.valid_input = True else: self.valid_input = False self.game.board[self.row][self.col] = int(self.num) self.update() if self.parent.parent.game.check_game_over(): self.parent.parent.game_over.emit() def animations(self): scale_value = 1.1 self.scale_anim = QPropertyAnimation(self, b'scale') self.scale_anim.setDuration(100) self.scale_anim.setStartValue(1) self.scale_anim.setEndValue(scale_value) def start_animations(self, direction: QAbstractAnimation): self.scale_anim.setDirection(direction) self.scale_anim.start()
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() loadUi('MainWindowUi.ui', self) fontID = QFontDatabase.addApplicationFont("HelveticaNeue.ttf") self.isMirrorAwake = False self.clockThread = ClockThread() self.currentWxThread = CurrentWeatherThread() self.forecastWxThread = WxForecastThread() self.quoteThread = QuoteThread() self.trafficThread = TrafficThread() self.notificationThread = NotificationThread() self.notificationEffect = QGraphicsOpacityEffect() self.notificationAnimation = QPropertyAnimation( self.notificationEffect, b"opacity") self.mainFourEffect = QGraphicsOpacityEffect() self.notificationLabel.setGraphicsEffect(self.notificationEffect) self.mainFourFrame.setGraphicsEffect(self.mainFourEffect) self.showMainTimer = QTimer() self.notificationEffect.setOpacity(0) self.mainFourEffect.setOpacity(0) self.connectSignals() def keyPressEvent(self, event): # Did the user press the Escape key? if event.key( ) == Qt.Key_Escape: # QtCore.Qt.Key_Escape is a value that equates to what the operating system passes to python from the keyboard when the escape key is pressed. # Yes: Close the window self.close() elif event.key() == Qt.Key_F1: if self.isMirrorAwake: self.shouldMirrorWake(False) else: self.shouldMirrorWake(True) elif event.key() == Qt.Key_F2: item = QListWidgetItem(self.calendarListView) item_widget = CalendarItemWidget("Test", "Date", "End", "Start") item.setSizeHint(item_widget.size()) self.calendarListView.addItem(item) self.calendarListView.setItemWidget(item, item_widget) def connectSignals(self): # Notifications self.notificationThread.sendNotificationTextSignal.connect( self.setNotificationText) self.notificationThread.showNotificationPanelSignal.connect( self.hideShowNotificationPanel) # Clock self.clockThread.sendDateSignal.connect(self.setDateText) self.clockThread.sendTimeSignal.connect(self.setTimeText) self.clockThread.sendSecondsSignal.connect(self.setSecondsText) self.clockThread.sendAmPmSignal.connect(self.setAmPmText) # Current Weather self.currentWxThread.sendWxTempSignal.connect(self.setCurrentWxTemp) self.currentWxThread.sendWxConditionsSignal.connect( self.setCurrentWxConditions) self.currentWxThread.sendWxWindsSignal.connect(self.setCurrentWxWinds) self.currentWxThread.sendWxGraphicSignal.connect( self.setCurrentWxImage) # Forecast Weather self.forecastWxThread.sendWxForecastForecast.connect( self.setWxForecastItems) # Quote self.quoteThread.sendQuoteQuote.connect(self.setQuoteQuote) self.quoteThread.sendQuoteAuthor.connect(self.setAuthorLabel) # Traffic self.trafficThread.sendDestinationTextSignal.connect( self.setDestinationText) self.trafficThread.sendDepartureTimeTextSignal.connect( self.setDepartureTimeText) self.trafficThread.sendArrivalTimeTextSignal.connect( self.setArrivalTimeText) def startThreads(self): # Notifications self.notificationThread.setRunning(True) self.notificationThread.start() # Clock self.clockThread.setRunning(True) self.clockThread.start() # Current Weather self.currentWxThread.setRunning(True) self.currentWxThread.start() # Forecast Weather self.forecastWxThread.setRunning(True) self.forecastWxThread.start() # Quote self.quoteThread.setRunning(True) self.quoteThread.start() # Traffic self.trafficThread.setRunning(True) self.trafficThread.start() def stopThreads(self): # Notifications # self.notificationThread.setRunning(False) # self.notificationThread.quit() # Clock self.clockThread.setRunning(False) self.clockThread.quit() # Current Weather self.currentWxThread.setRunning(False) self.currentWxThread.quit() # Forecast Weather self.forecastWxThread.setRunning(False) self.forecastWxThread.quit() # Quote self.quoteThread.setRunning(False) self.quoteThread.quit() # Traffic self.trafficThread.setRunning(False) self.trafficThread.quit() # Slot functions used only by this object # Notifications def setNotificationText(self, notificationText): try: self.notificationLabel.setText(notificationText) except Exception as e: print(e) def hideShowNotificationPanel(self, showNotificationPanel): try: if showNotificationPanel: startValue = 0 endValue = 1 else: startValue = 1 endValue = 0 self.notificationLabel.setGraphicsEffect(self.notificationEffect) self.notificationAnimation.setDuration(2000) self.notificationAnimation.setStartValue(startValue) self.notificationAnimation.setEndValue(endValue) self.notificationAnimation.start() except Exception as e: print(e) # Clock def setDateText(self, dateText): self.dateLabel.setText(dateText) def setTimeText(self, timeText): self.timeLabel.setText(timeText) def setSecondsText(self, secondsText): self.timeSecondsLabel.setText(secondsText) def setAmPmText(self, amPmText): self.timeAmPmLabel.setText(amPmText) # Current Weather def setCurrentWxTemp(self, tempText): self.wxTempLabel.setText(tempText) def setCurrentWxConditions(self, conditionsText): self.wxLabel.setText(conditionsText) def setCurrentWxWinds(self, windsText): self.windLabel.setText(windsText) def setCurrentWxImage(self, wxImage): self.currentWxImage.setPixmap(wxImage) # Forecast Weather def setWxForecastItems(self, forecasts): for forecast in forecasts: item = QListWidgetItem(self.wxForecastListView) item_widget = WxForecastItemWidget(forecast.day, forecast.icon, forecast.high, forecast.low) item.setSizeHint(item_widget.size()) self.wxForecastListView.addItem(item) self.wxForecastListView.setItemWidget(item, item_widget) # Quote def setQuoteQuote(self, quote): self.quoteLabel.setText(quote) def setAuthorLabel(self, author): self.authorLabel.setText(author) # Traffic def setDestinationText(self, destination): self.trafficDestinationLabel.setText(destination) def setDepartureTimeText(self, departureTime): self.trafficDepartureTimeLabel.setText(departureTime) def setArrivalTimeText(self, arrivalTime): self.trafficArrivalTimeLabel.setText(arrivalTime) # Mirror Functions def shouldMirrorWake(self, shouldWake): self.isMirrorAwake = shouldWake if shouldWake: self.startThreads() self.showMirror() else: self.stopThreads() self.hideMirror() def showMirror(self): self.notificationThread.showNotification("Welcome back, beautiful!", 5) self.showMainTimer.timeout.connect( self.showHideMirrorAfterNotification) self.showMainTimer.start(5000) def hideMirror(self): self.notificationThread.showNotification("Goodbye...", 5) self.showMainTimer.timeout.connect( self.showHideMirrorAfterNotification) self.showMainTimer.start(5000) def showHideMirrorAfterNotification(self): try: self.showMainTimer.stop() if self.isMirrorAwake: startValue = 0 endValue = 1 else: startValue = 1 endValue = 0 self.mainFourEffect = QGraphicsOpacityEffect() self.mainFourFrame.setGraphicsEffect(self.mainFourEffect) self.showHideMirrorAnimation = QPropertyAnimation( self.mainFourEffect, b"opacity") self.showHideMirrorAnimation.setDuration(2000) self.showHideMirrorAnimation.setStartValue(startValue) self.showHideMirrorAnimation.setEndValue(endValue) self.showHideMirrorAnimation.start() except Exception as e: print(e)
class PushButton(QPushButton): clicked = pyqtSignal(str) MIN_VALOR = 1 MAX_VALOR = 101 VALOR = MAX_VALOR + MIN_VALOR def __init__(self, parent=None): super(PushButton, self).__init__(parent) self.parent = parent self.pressing = False self.inicio = QPoint() self.mover = QPoint() self.habilitado = False self.arrastrado = False self.actualizarEstado() self.actualizarPosicion() def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.ultimo = True self.inicio = event.pos() self.pressing = True self.actualizarEstado() def actualizarEstado(self): # Si la posicion x del boton mas la mitad de su ancho # es menor que la mitad del ancho del widget padre, # entonces esta apagado (NO) if (self.x() + (self.width() / 2)) < PushButton.VALOR / 2: self.habilitado = False # Si la posicion x del boton mas la mitad de su ancho # es mayor que la mitad del ancho del widget padre, # entonces esta encendido (SI) if (self.x() + (self.width() / 2)) > PushButton.VALOR / 2: self.habilitado = True if self.habilitado: self.setText("SI") color = QColor(206, 61, 59) elif not self.habilitado: self.setText("NO") color = QColor(147, 183, 89) colorFrame = self.palette() colorFrame.setColor(QPalette.Background, color) self.parent.setPalette(colorFrame) def mouseMoveEvent(self, event): if self.pressing: self.mover = self.mapToParent(event.pos() - self.inicio) self.mover.setY(1) self.move(self.mover) self.arrastrado = True self.restringirMovimiento() self.actualizarEstado() def actualizarPosicion(self): self.mover.setY(1) if self.habilitado: self.mover.setX(PushButton.MAX_VALOR - self.width()) else: self.mover.setX(PushButton.MIN_VALOR) self.animacion = QPropertyAnimation(self, b"pos") self.animacion.setDuration(150) self.animacion.setEndValue(self.mover) self.animacion.finished.connect(self.emitirEstado) self.animacion.start(QAbstractAnimation.DeleteWhenStopped) def restringirMovimiento(self): self.mover.setY(1) # Restringir lado izquierdo if self.x() < PushButton.MIN_VALOR: self.mover.setX(PushButton.MIN_VALOR) self.move(self.mover) return # Restringir lado derecho if (self.x() + self.width()) > PushButton.MAX_VALOR: self.mover.setX(PushButton.MAX_VALOR - self.width()) self.move(self.mover) return def mouseReleaseEvent(self, event): if self.pressing: self.pressing = False self.actualizarEstado() self.actualizarPosicion() if not self.arrastrado and self.ultimo: # QApplication.instance().doubleClickInterval() QTimer.singleShot(100, self.performSingleClickAction) else: self.arrastrado = False def mouseDoubleClickEvent(self, event): if event.button() == Qt.LeftButton: self.ultimo = False def performSingleClickAction(self): if self.ultimo: self.mover.setY(1) if self.habilitado: self.mover.setX(PushButton.MIN_VALOR) else: self.mover.setX(PushButton.MAX_VALOR - self.width()) self.animacion = QPropertyAnimation(self, b"pos") self.animacion.setDuration(150) self.animacion.setEndValue(self.mover) self.animacion.valueChanged.connect(self.actualizarEstado) self.animacion.finished.connect(self.emitirEstado) self.animacion.start(QAbstractAnimation.DeleteWhenStopped) def emitirEstado(self): self.clicked.emit(self.text())
view.setWindowTitle("Animated Tiles") view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate) view.setBackgroundBrush(QBrush(bgPix)) view.setCacheMode(QGraphicsView.CacheBackground) view.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) view.show() states = QStateMachine() states.addState(rootState) states.setInitialState(rootState) rootState.setInitialState(centeredState) group = QParallelAnimationGroup() for i, item in enumerate(items): anim = QPropertyAnimation(item, b'pos') anim.setDuration(750 + i * 25) anim.setEasingCurve(QEasingCurve.InOutBack) group.addAnimation(anim) trans = rootState.addTransition(ellipseButton.pressed, ellipseState) trans.addAnimation(group) trans = rootState.addTransition(figure8Button.pressed, figure8State) trans.addAnimation(group) trans = rootState.addTransition(randomButton.pressed, randomState) trans.addAnimation(group) trans = rootState.addTransition(tiledButton.pressed, tiledState) trans.addAnimation(group)
class MatplotTinderUI(QWidget, UI): def __init__(self, parent=None): super().__init__(parent) matplot_widget = FigureCanvas(Figure()) self._ax = matplot_widget.figure.subplots() self._scene = QGraphicsScene() self.matplot_graphics_widget = self._scene.addWidget(matplot_widget) self.matplot_graphics_widget.setParent(self) self.red_brush = QBrush(Qt.SolidPattern) self.red_brush.setColor(QColor(255, 0, 0, 50)) self.pen = QPen() self.pen.setColor(QColor(0, 0, 0, 0)) self._scene.addEllipse(0, self.height()/2, self.height()/2, self.height(), QPen(Qt.NoPen), self.red_brush) self._view = QGraphicsView(self._scene) self._view.setGeometry(250, 250, 500, 500) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self._view) self.setLayout(layout) id = QGestureRecognizer.registerRecognizer(PanGestureRecognizer()) self.grabGesture(id) def show_image(self, img, cmap=Defaults.cmap, interpolation='gaussian'): self._ax.clear() self._ax.axis('off') self._ax.imshow(img.data, cmap=cmap, interpolation=interpolation) self._ax.figure.canvas.draw() def connect_multi_classification_listener(self, action): pass def connect_single_classification_listener(self, action): pass def connect_skip_classification_listener(self, action): self.skip = action def event(self, event): if event.type() == QEvent.Gesture and event.gesture(Qt.PanGesture): self.pan_triggered(event.gesture(Qt.PanGesture)) else: super().event(event) return True def pan_triggered(self, pan_gesture): delta = pan_gesture.delta() self.matplot_graphics_widget.setPos(self.matplot_graphics_widget.x() + delta.x(), self.matplot_graphics_widget.y() + delta.y()) if pan_gesture.state() == Qt.GestureFinished: # self._check_if_classified() self.reset() def mouseDoubleClickEvent(self, a0): # self.reset() print('mouseclick') self.skip() def reset(self): self.animation = QPropertyAnimation(self.matplot_graphics_widget, b'pos') self.animation.setStartValue(self.matplot_graphics_widget.pos()) self.animation.setEndValue(QPointF(100, 100)) self.animation.setDuration(100) self.animation.start() def paintEvent(self, event): self.matplot_graphics_widget.setRotation(self.get_rotation()) self.update() def get_rotation(self): pic = [self.matplot_graphics_widget.x(), -self.matplot_graphics_widget.y()] pivot = [0, -500] v1 = np.subtract(pic, pivot) v2 = np.subtract([0, 0], pivot) a = np.arccos(np.divide(np.abs(np.dot(v1, v2)), (np.linalg.norm(v1) * np.linalg.norm(v2)))) if self.matplot_graphics_widget.x() < 0: return -a * 10 return a * 10
class FlipWidget(QWidget): Left = 0 # 从右往左 Right = 1 # 从左往右 Scale = 3 # 图片缩放比例 finished = pyqtSignal() def __init__(self, *args, **kwargs): super(FlipWidget, self).__init__(*args, **kwargs) # 无边框无任务栏 self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint | Qt.SubWindow) # 背景透明 self.setAttribute(Qt.WA_TranslucentBackground, True) # 翻转角度 self._angle = 0 # 属性动画针对自定义属性`angle` self._animation = QPropertyAnimation(self, b'angle', self) self._animation.setDuration(550) self._animation.setEasingCurve(QEasingCurve.OutInQuad) self._animation.finished.connect(self.finished.emit) @pyqtProperty(int) def angle(self): return self._angle @angle.setter def angle(self, angle): self._angle = angle self.update() def updateImages(self, direction, image1, image2): """设置两张切换图 :param direction: 方向 :param image1: 图片1 :param image2: 图片2 """ self.image1 = image1 self.image2 = image2 self.show() self._angle = 0 # 根据方向设置动画的初始和结束值 if direction == self.Right: self._animation.setStartValue(1) self._animation.setEndValue(-180) elif direction == self.Left: self._animation.setStartValue(1) self._animation.setEndValue(180) self._animation.start() def paintEvent(self, event): super(FlipWidget, self).paintEvent(event) if hasattr(self, 'image1') and hasattr(self, 'image2') and self.isVisible(): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.SmoothPixmapTransform, True) # 变换 transform = QTransform() # 把圆心设置为矩形中心 transform.translate(self.width() / 2, self.height() / 2) if self._angle >= -90 and self._angle <= 90: # 当翻转角度在90范围内显示第一张图,且从大图缩放到小图的过程 painter.save() # 设置翻转角度 transform.rotate(self._angle, Qt.YAxis) painter.setTransform(transform) # 缩放图片高度 width = self.image1.width() / 2 height = int(self.image1.height() * (1 - abs(self._angle / self.Scale) / 100)) image = self.image1.scaled(self.image1.width(), height, Qt.IgnoreAspectRatio, Qt.SmoothTransformation) painter.drawPixmap(QPointF(-width, -height / 2), image) painter.restore() else: # 当翻转角度在90范围内显示第二张图,且从小图缩放到原图的过程 painter.save() if self._angle > 0: angle = 180 + self._angle else: angle = self._angle - 180 # 设置翻转角度, 注意这里角度有差异 transform.rotate(angle, Qt.YAxis) painter.setTransform(transform) # 缩放图片高度 width = self.image2.width() / 2 height = int(self.image2.height() * (1 - ((360 - abs(angle)) / self.Scale / 100))) image = self.image2.scaled(self.image2.width(), height, Qt.IgnoreAspectRatio, Qt.SmoothTransformation) painter.drawPixmap(QPointF(-width, -height / 2), image) painter.restore()
class AlternatePositionAnimation(object): """ This class handles alternate position animation on widgets. It permits to move from a position to another and vice versa. """ def __init__(self, parent): try: super(AlternatePositionAnimation, self).__init__(parent) except TypeError: super(AlternatePositionAnimation, self).__init__() # Get the caller widget to apply fade effect self.widget = vars()['self'] self.show_animation = QPropertyAnimation(self.widget, b'pos') self.hide_animation = QPropertyAnimation(self.widget, b'pos') self.show_animation.setDuration(200) self.show_animation.setEasingCurve(QEasingCurve.Linear) self.hide_animation.setDuration(200) self.hide_animation.setEasingCurve(QEasingCurve.Linear) self.show_animation.finished.connect(self.show_finished_animation) self.hide_animation.finished.connect(self.hide_finished_animation) def set_animation_start_values(self): """ Set different animation values. :param start: position when starting animation. :param end: position when finishing animation. :return: """ beg_pos = self.window().width() - self.width() - 2 end_pos = self.window().width() + self.width() print(beg_pos, end_pos, self.window(), self.width()) start = QPoint(end_pos, 0) end = QPoint(beg_pos, 0) if not start or not end: return self.show_animation.setStartValue(QPoint(end_pos, 0)) self.show_animation.setEndValue(QPoint(beg_pos, 0)) def start_show(self): """ Start the showing animation. :return: """ self.set_animation_start_values() self.show_animation.start() def set_animation_hide_values(self): """ Set different animation values. :param start: position when starting animation. :param end: position when finishing animation. :return: """ beg_pos = self.window().width() - self.width() - 2 end_pos = self.window().width() + self.width() print('hide', beg_pos, end_pos, self.window(), self.width()) start = QPoint(end_pos, 0) end = QPoint(beg_pos, 0) if not start or not end: return self.hide_animation.setStartValue(QPoint(beg_pos, 0)) self.hide_animation.setEndValue(QPoint(end_pos, 0)) def start_hide(self): """ Start the hiding animation. :return: """ self.set_animation_hide_values() self.hide_animation.start() def show_finished_animation(self): """ Function called when show animation is over. :return: """ pass def hide_finished_animation(self): """ Function called when hide animation is over. :return: """ pass
class messageWidgetImpl(QWidget, Ui_messageWidget): # 初始化 def __init__(self, timeout=6000, parent=None): super(messageWidgetImpl, self).__init__(parent) self.setupUi(self) log = logger() self.message = log.getlogger('gui') self._timeout = timeout self._init() def _init(self): # 是否正在显示标志 self.isShowing = True # 超时 self._hasFocus = False # 桌面对象 self._desktop = QApplication.desktop() current_monitor = self._desktop.screenNumber(self) self._desktopGem = self._desktop.availableGeometry(current_monitor) # 窗口初始开始位置 self._startPos = QPoint( self._desktopGem.x() + self._desktopGem.width() - self.width() - 5, self._desktopGem.y() + self._desktopGem.height()) # 窗口弹出结束位置 self._endPos = QPoint( self._desktopGem.x() + self._desktopGem.width() - self.width() - 5, self._desktopGem.y() + self._desktopGem.height() - self.height() - 5) # 初始化位置到右下角 self.move(self._startPos) # 动画 self.animation = QPropertyAnimation(self, b"pos") self.animation.finished.connect(self.onAnimationEnd) self.animation.setDuration(2000) # 2s # 弹回定时器 self._timer = QTimer(self, timeout=self.closeAnimation) self.closeButton.clicked.connect(self.closeNow) def show(self, content=''): self._timer.stop() # 停止定时器 self.hide() self.move(self._startPos) self.setContent(content) super(messageWidgetImpl, self).show() return self #显示动画 def showAnimation(self): self.message.debug('showAnimation') self.isShowing = True self.animation.stop() self.animation.setStartValue(self.pos()) self.animation.setEndValue(self._endPos) self.animation.start() self._timer.start(self._timeout) # 关闭动画 def closeAnimation(self): self.message.debug('closeAnimation') # 关闭动画 if self.hasFocus(): # 如果弹出后倒计时5秒后还有焦点存在则失去焦点后需要主动触发关闭 self._hasFocus = True return # 如果有焦点则不关闭 self.isShowing = False self.animation.stop() self.animation.setStartValue(self.pos()) self.animation.setEndValue(self._startPos) self.animation.start() # 动画结束 def onAnimationEnd(self): if not self.isShowing: self.message.debug("onAnimationEnd close()") self.close() self.message.debug("onAnimationEnd stop timer") self._timer.stop() def closeNow(self): self.close() self._timer.stop() # 进入事件 def enterEvent(self, QEvent): self.message.debug("enterEvent setFocus Qt.MouseFocusReason") self.setFocus(Qt.MouseFocusReason) # 离开事件 def leaveEvent(self, QEvent): self.message.debug("leaveEvent clearFocus") self.clearFocus() if self._hasFocus: QTimer.start(1000, self.closeAnimation) # 设置内容 def setContent(self, content): if content: self.label.setText(content) def content(self): return self.label.text() #设置超时 def setTimeout(self, timeout): if isinstance(timeout, int): self._timeout = timeout return self def timeout(self): return self._timeout
class Widgets(QWidget): def __init__(self, parent=None): super(Widgets, self).__init__(parent) self.parent = parent self.initUI() def initUI(self): # ======================== WIDGETS =========================== framePrincipal = QFrame(self) framePrincipal.setFrameShape(QFrame.Box) framePrincipal.setFrameShadow(QFrame.Sunken) framePrincipal.setAutoFillBackground(True) framePrincipal.setBackgroundRole(QPalette.Light) framePrincipal.setFixedSize(662, 503) framePrincipal.move(10, 10) frame = QFrame(framePrincipal) frame.setFixedSize(640, 480) frame.move(10, 10) self.labelImagen = QLabel(frame) self.labelImagen.setAlignment(Qt.AlignCenter) self.labelImagen.setGeometry(0, 0, 640, 480) # self.labelImagen.setScaledContents(True) self.labelImagenUno = QLabel(frame) self.labelImagenUno.setAlignment(Qt.AlignCenter) self.labelImagenUno.setGeometry(-650, 0, 640, 480) # =================== BOTONES (QPUSHBUTTON) ================== self.buttonCargar = QPushButton("Cargar imagen", self) self.buttonCargar.setCursor(Qt.PointingHandCursor) self.buttonCargar.setFixedSize(325, 30) self.buttonCargar.move(10, 519) self.buttonEliminar = QPushButton("Eliminar imagen", self) self.buttonEliminar.setCursor(Qt.PointingHandCursor) self.buttonEliminar.setFixedSize(255, 30) self.buttonEliminar.move(345, 519) self.buttonAnterior = QPushButton("<", self) self.buttonAnterior.setObjectName("Anterior") self.buttonAnterior.setToolTip("Imagen anterior") self.buttonAnterior.setCursor(Qt.PointingHandCursor) self.buttonAnterior.setFixedSize(30, 30) self.buttonAnterior.move(607, 519) self.buttonSiguiente = QPushButton(">", self) self.buttonSiguiente.setObjectName("Siguiente") self.buttonSiguiente.setToolTip("Imagen siguiente") self.buttonSiguiente.setCursor(Qt.PointingHandCursor) self.buttonSiguiente.setFixedSize(30, 30) self.buttonSiguiente.move(642, 519) # ===================== CONECTAR SEÑALES ===================== self.buttonCargar.clicked.connect(self.Cargar) self.buttonEliminar.clicked.connect(self.Eliminar) self.buttonAnterior.clicked.connect(self.anteriorSiguiente) self.buttonSiguiente.clicked.connect(self.anteriorSiguiente) # Establecer los valores predeterminados self.posicion = int self.estadoAnterior, self.estadoSiguiente = False, False self.carpetaActual = QDir() self.imagenesCarpeta = [] # ======================= FUNCIONES ============================== def bloquearBotones(self, bool): self.buttonCargar.setEnabled(bool) self.buttonEliminar.setEnabled(bool) self.buttonAnterior.setEnabled(bool) self.buttonSiguiente.setEnabled(bool) def Mostrar(self, label, imagen, nombre, posicionX=650): imagen = QPixmap.fromImage(imagen) # Escalar imagen a 640x480 si el ancho es mayor a 640 o el alto mayor a 480 if imagen.width() > 640 or imagen.height() > 480: imagen = imagen.scaled(640, 480, Qt.KeepAspectRatio, Qt.SmoothTransformation) # Mostrar imagen label.setPixmap(imagen) # Animación (al finalizar la animación se muestra en la barra de estado el nombre y la extensión de la imagen # y se desbloquean los botones). self.animacionMostar = QPropertyAnimation(label, b"geometry") self.animacionMostar.finished.connect( lambda: (self.parent.statusBar.showMessage(nombre), self.bloquearBotones(True))) self.animacionMostar.setDuration(200) self.animacionMostar.setStartValue(QRect(posicionX, 0, 640, 480)) self.animacionMostar.setEndValue(QRect(0, 0, 640, 480)) self.animacionMostar.start(QAbstractAnimation.DeleteWhenStopped) def Limpiar(self, labelConImagen, labelMostrarImagen, imagen, nombre, posicionInternaX, posicionX=None): def Continuar(estado): if estado: if posicionX: self.Mostrar(labelMostrarImagen, imagen, nombre, posicionX) else: self.Mostrar(labelMostrarImagen, imagen, nombre) self.animacionLimpiar = QPropertyAnimation(labelConImagen, b"geometry") self.animacionLimpiar.finished.connect(lambda: labelConImagen.clear()) self.animacionLimpiar.setDuration(200) # self.animacionLimpiar.valueChanged.connect(lambda x: print(x)) self.animacionLimpiar.stateChanged.connect(Continuar) self.animacionLimpiar.setStartValue(QRect(0, 0, 640, 480)) self.animacionLimpiar.setEndValue(QRect(posicionInternaX, 0, 640, 480)) self.animacionLimpiar.start(QAbstractAnimation.DeleteWhenStopped) def Cargar(self): nombreImagen, _ = QFileDialog.getOpenFileName( self, "Seleccionar imagen", QDir.currentPath(), "Archivos de imagen (*.jpg *.png *.ico *.bmp)") if nombreImagen: # Verificar que QLabel tiene imagen labelConImagen = "" if self.labelImagen.pixmap(): labelConImagen = self.labelImagen elif self.labelImagenUno.pixmap(): labelConImagen = self.labelImagenUno imagen = QImage(nombreImagen) if imagen.isNull(): if labelConImagen: self.Eliminar() QMessageBox.information( self, "Visor de imágenes", "No se puede cargar %s." % nombreImagen) return # Obtener ruta de la carpeta que contiene la imagen seleccionada self.carpetaActual = QDir( QFileInfo(nombreImagen).absoluteDir().path()) # Obtener la ruta y el nombre de las imagenes que se encuentren en la carpeta de # la imagen seleccionada imagenes = self.carpetaActual.entryInfoList( ["*.jpg", "*.png", "*.ico", "*.bmp"], QDir.Files, QDir.Name) self.imagenesCarpeta = [ imagen.absoluteFilePath() for imagen in imagenes ] self.posicion = self.imagenesCarpeta.index(nombreImagen) self.estadoAnterior = True if self.posicion == 0 else False self.estadoSiguiente = True if self.posicion == len( self.imagenesCarpeta) - 1 else False # Función encargada de bloquear o desbloquear los botones self.bloquearBotones(False) # Nombre y extensión de la imagen nombre = QFileInfo(nombreImagen).fileName() if labelConImagen: posicionInternaX = -650 labelMostrarImagen = self.labelImagen if self.labelImagenUno.pixmap( ) else self.labelImagenUno self.Limpiar(labelConImagen, labelMostrarImagen, imagen, nombre, posicionInternaX) else: self.Mostrar(self.labelImagen, imagen, nombre) def Eliminar(self): def establecerValores(): labelConImagen.clear() labelConImagen.move(0, 0) # Limpiar la barra de estado self.parent.statusBar.clearMessage() # Establecer los valores predeterminados self.posicion = int self.estadoAnterior, self.estadoSiguiente = False, False self.carpetaActual = QDir() self.imagenesCarpeta.clear() self.bloquearBotones(True) # Verificar que QLabel tiene imagen labelConImagen = "" if self.labelImagen.pixmap(): labelConImagen = self.labelImagen elif self.labelImagenUno.pixmap(): labelConImagen = self.labelImagenUno if labelConImagen: self.bloquearBotones(False) self.animacionEliminar = QPropertyAnimation( labelConImagen, b"geometry") self.animacionEliminar.finished.connect(establecerValores) self.animacionEliminar.setDuration(200) self.animacionEliminar.setStartValue(QRect(0, 0, 640, 480)) self.animacionEliminar.setEndValue(QRect(-650, 0, 640, 480)) self.animacionEliminar.start(QAbstractAnimation.DeleteWhenStopped) def anteriorSiguiente(self): if self.imagenesCarpeta: widget = self.sender().objectName() if widget == "Anterior": self.estadoAnterior = True if self.posicion == 0 else False self.estadoSiguiente = False self.posicion -= 1 if self.posicion > 0 else 0 posicionInternaX, posicionX = 650, -650 else: self.estadoSiguiente = True if self.posicion == len( self.imagenesCarpeta) - 1 else False self.estadoAnterior = False self.posicion += 1 if self.posicion < len( self.imagenesCarpeta) - 1 else 0 posicionInternaX, posicionX = -650, 650 if self.estadoAnterior or self.estadoSiguiente: return else: imagen = self.imagenesCarpeta[self.posicion] # Verificar que la carpeta que contiene la imagene exista if not QDir(self.carpetaActual).exists(): self.Eliminar() return elif not QFile.exists(imagen): # Obtener la ruta y el nombre de las imagenes que se encuentren en la # carpeta de la imagen seleccionada imagenes = self.carpetaActual.entryInfoList( ["*.jpg", "*.png", "*.ico", "*.bmp"], QDir.Files, QDir.Name) if not imagenes: self.Eliminar() return self.imagenesCarpeta = [ imagen.absoluteFilePath() for imagen in imagenes ] self.posicion = randint(0, len(self.imagenesCarpeta) - 1) self.estadoAnterior = True if self.posicion == 0 else False self.estadoSiguiente = True if self.posicion == len( self.imagenesCarpeta) - 1 else False elif QImage(imagen).isNull(): del self.imagenesCarpeta[self.posicion] if not self.imagenesCarpeta: self.Eliminar() return self.posicion = randint(0, len(self.imagenesCarpeta) - 1) self.estadoAnterior = True if self.posicion == 0 else False self.estadoSiguiente = True if self.posicion == len( self.imagenesCarpeta) - 1 else False imagen = self.imagenesCarpeta[self.posicion] if self.labelImagen.pixmap(): labelConImagen = self.labelImagen elif self.labelImagenUno.pixmap(): labelConImagen = self.labelImagenUno # Función encargada de bloquear o desbloquear los botones self.bloquearBotones(False) # Nombre y extensión de la imagen nombre = QFileInfo(imagen).fileName() # Label en el que se va a mostrar la imagen labelMostrarImagen = self.labelImagen if self.labelImagenUno.pixmap( ) else self.labelImagenUno # Quitar la imagen actual y mostrar la siguiente self.Limpiar(labelConImagen, labelMostrarImagen, QImage(imagen), nombre, posicionInternaX, posicionX)
class DropdownWidget(QWidget): def __init__(self, parent=None, name="Total"): super(DropdownWidget, self).__init__(parent) self.name = name self.isOpen = False self.isButton = False layout = QVBoxLayout() layout.setSpacing(0) self.bar = MyProgressBar(parent) font = QFont() font.setFamily("Tahoma") font.setPointSize(15) self.bar.setFont(font) self.bar.setProperty("value", 0) self.bar.setValue(0) self.bar.setAlignment(Qt.AlignLeading | Qt.AlignLeft | Qt.AlignVCenter) self.bar.setTextVisible(True) self.bar.setObjectName(self.name) layout.addWidget(self.bar) self.data = QScrollArea(self) sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) self.data.setSizePolicy(sizePolicy) self.data.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.data.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.data.setMaximumHeight(0) self.data.setObjectName("DropdownSA") self.data.setFrameShape(QFrame.NoFrame) self.data.setStyleSheet( "QScrollArea {" "background-color: qlineargradient(spread:pad, " "x1:0, y1:0, x2:1, y2:1, stop:0 rgb(192, 221, 221), " "stop:1 rgb(180, 233, 197));}") self.data.setWidgetResizable(True) if name == "Total": self.dataWidget = DataWidget(self.data, mult=2) else: self.dataWidget = DataWidget(self.data) self.data.setWidget(self.dataWidget) layout.addWidget(self.data) self.setLayout(layout) self.bar.setFormat(" ▶ " + self.name) self.anim = QPropertyAnimation(self.data, b"maximumHeight") self.anim.setDuration(500) self.anim.setEasingCurve(QEasingCurve.InOutSine) self.anim.valueChanged.connect(self.animationEvent) # self.bar.mouseClicked.connect(self.onClick) def onClick(self): self.anim.stop() self.anim.setStartValue(self.data.maximumHeight()) if self.isOpen: self.isOpen = False self.anim.setDuration(250) self.bar.setFormat(" ▶ " + self.name) self.anim.setEndValue(0) else: self.isOpen = True self.anim.setDuration(500) self.bar.setFormat(" ▾ " + self.name) self.anim.setEndValue(450) self.dataWidget.startAnimation() self.anim.start() def updateResults(self, results): if results["RunTime"] > 0.1 and not self.isButton: self.toggleButton() elif results["RunTime"] < 0.1 and self.isButton: self.toggleButton() v = (results["RunTime"] / self.dataWidget.runtimeTarget[1]) * 100 if v > 99: v = 100 v = int(v) self.bar.setProperty("Value", v) self.bar.setValue(v) self.dataWidget.updateResults(results) def toggleButton(self): if self.isButton: self.isButton = False self.bar.setCursor(QCursor(Qt.ArrowCursor)) self.bar.mouseClicked.disconnect() else: self.isButton = True self.bar.setCursor(QCursor(Qt.PointingHandCursor)) self.bar.mouseClicked.connect(self.onClick) def animationEvent(self, val): self.data.setMinimumHeight(min(425, val))
class terminal(Window): def __init__(self): super( terminal, self, ).__init__() self.resize(900, 600) self.setObjectName('terminal') self.setAttribute(QtCore.Qt.WA_TranslucentBackground) self.Main_HB = QtWidgets.QHBoxLayout() Pixmap = QtGui.QPixmap() Pixmap.loadFromData( base64.b64decode(bytes(Maimwin_ico, encoding='utf-8'))) self.Window_out = False ico = QtGui.QIcon() ico.addPixmap(Pixmap) self.iclab = QLabel(self) self.tab_menu = QtWidgets.QPushButton('☰', self) self.tab_menu.setStyleSheet( 'line-height: 0.1;font: 7pt "微软雅黑";border-top-right-radius:10px;') self.tab_menu.setObjectName("tabmenu") self.tab_menu.clicked.connect(self.Title_men) self.tab_menu.setMaximumSize(30, 30) self.tab_menu.setMinimumSize(30, 30) self._MinimumButton = QHead_Button(b'\xef\x80\xb0'.decode("utf-8"), self) self._MinimumButton.setObjectName("MinMaxButton") self._MinimumButton.setMaximumSize(30, 30) self._MinimumButton.setMinimumSize(30, 30) self._MinimumButton.clicked.connect(self.showMinimized) self.iclab.setMaximumSize(30, 30) self.iclab.setMinimumSize(30, 30) self.iclab.setScaledContents(True) self.iclab.setPixmap(Pixmap) self.setWindowIcon(ico) self.Main_HB.addWidget(self.iclab) self.title_tab = table(self) self.title_tab.setMinimumSize(38, 38) self.title_tab.add_tab.clicked.connect(self.Init_lab) # self.title_tab.setGeometry(38,0,self.width() - self._ClosButton.width()-38,48) # self.title_tab.setStyleSheet('background-color:#FFFFFF') # self.title_hb.addWidget(self.title_tab) self.Main_HB.addWidget(self.title_tab) self.Main_HB.addWidget(self.tab_menu) self.Main_HB.addWidget(self._MinimumButton) self.Main_HB.addWidget(self._MaximumButton) self.Main_HB.addWidget(self._ClosButton) self.Main_HB.setContentsMargins(5, 0, 0, 0) self.verticalLayout_main.addLayout(self.Main_HB) self.window_docker = QStackedWidget(self) # spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.verticalLayout_main.addWidget(self.window_docker) # self.verticalLayout_main.addItem(spacerItem) self.verticalLayout_main.setContentsMargins(0, 0, 0, 0) self.verticalLayout_main.setSpacing(0) self._Men = QtWidgets.QMenu(self) # self._Men.setWindowFlags(QtCore.Qt.FramelessWindowHint) self._Men.setAttribute(QtCore.Qt.WA_TranslucentBackground) self._Men.setObjectName('TabMen') self.Host_often = self._Men.addAction('最近会话') self.Host_list = self._Men.addAction('远程管理') self.Host_list.triggered.connect(lambda: self.Host_win(True)) self.Theme = self._Men.addAction('切换主题') self._Men.addSeparator() self.Setting = self._Men.addAction('设置') self.Help = self._Men.addAction('帮助') self.Author = self._Men.addAction('关于') # self._ClosButton.move(self.width() - self._ClosButton.width() - 1, 5) # float_host = QtWidgets.QVBoxLayout(self) self.HostListWindow = HostWindow(self) # self.HostListWindow = HostWindow() # self.HostListWindow.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) self.HostListWindow.Send_host.connect(self.Add_lab) # self.HostListWindow. self.win_out = QPropertyAnimation(self.HostListWindow, b"geometry") self.win_out.setDuration(300) # float_host.addWidget(self.HostListWindow) # self.title_hb = QtWidgets.QHBoxLayout(self) # self.title_tab = table(self) # self.title_tab.setGeometry(38,0,self.width() - self._ClosButton.width()-38,48) # self.title_tab.setStyleSheet('background-color:#FFFFFF') # self.title_hb.addWidget(self.title_tab) # def enterEvent(self,QMouseEvent): # self.setMouseTracking(True) self.Lable_viwe = [] self.Init_lab() # self.winqss() # def winqss(self): self.setStyleSheet(''' QTableView { outline:0px; } QWidget#main_widget{ border-top-right-radius:10px; border-top-left-radius:10px; border-bottom-left-radius:4px; border-bottom-right-radius:4px; /*background-color:qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:1 rgba(0, 0, 0, 163));*/ /*background-color:qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:1 rgba(0, 0, 0, 200));*/ background-color: qlineargradient(spread:pad, x1:0.506, y1:0.567818, x2:1, y2:0, stop:1 rgba(0, 37, 56, 200)); } QWebEngineView#QWebEng{ background-color:qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:1 rgba(0, 0, 0, 163)); } QWidget#QWebEng{ background-color:qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:1 rgba(0, 0, 0, 163)); } QWidget#HostItemWidget:hover{ background-color:#111111; } QWidget#UpdateHostWindow{ background-color:#000000; color:#FFFFFF; } QWidget#HostListWindow{ background-color:#000000; } QPushButton:hover{ background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:1 rgba(47, 47, 47, 171)); } QPushButton{ color:#CCCCCC; border:none; /*font-family: "微软雅黑";*/ border-radius:3px; background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:1 rgba(37, 37, 37, 171)); } QPushButton#Left_QPush{ color:#CCCCCC; border:none; background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(255, 0, 0, 0), stop:1 rgba(255, 255, 255, 0)); } /* QPushButton{ color:#CCCCCC; border:none; background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(255, 0, 0, 0), stop:1 rgba(255, 255, 255, 0)); }*/ QPushButton#AddHost:hover{ background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:1 rgba(47, 47, 47, 171)); } QPushButton#subButton{ border:none; border-radius:3px; line-height: 1; font: 10pt "微软雅黑"; padding: 0px; color:#FFFFFF; background-color:#CC6633; outline: none; } QPushButton#subButton:hover { background-color:#AAAAAA; } QPushButton#toolbarbutton{ border:none; line-height: 1; font: 10pt "微软雅黑"; padding: 9px; text-align:left; outline: none; } QPushButton#toolbarbutton:hover { background-color:#AAAAAA; } QPushButton#toolbarbutton:focus { background-color:#CCCCCC; } QPushButton#ClosButton,#MinMaxButton,#tabmenu,#MinMaxButton{ color:#AAAAAA; border:0px; background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(255, 0, 0, 0), stop:1 rgba(255, 255, 255, 0)); } QPushButton#MinMaxButton:hover,#tabmenu:hover,#MinMaxButton:hover{ color:#FFFFFF } QPushButton#ClosButton:hover{ color:#FF0033 } QLabel{ line-height: 1.5; color:#999999; font: 6pt "微软雅黑"; font-weight: normal; opacity: 0.7; font-size: 16px; } QLabel#TitleLabel{ color: #000000; } QLabel#HostItemTitleName{ color:#CCCCCC; font: 8pt "微软雅黑"; } QLabel#HostItemHost{ color:#555555; font: 7pt "微软雅黑"; } QLineEdit { background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(255, 0, 0, 0), stop:1 rgba(255, 255, 255, 0)); padding: 5px; border-style: solid; border: 1px solid #00CCFF; border-radius: 2px; color: #999999; } QWebEngineView{ background-color: #FFFFFF; } QLineEdit:hover { border: 1px solid #99CC99; } QMenu#TabMen{ /* font: 9pt "SimSun";*/ font-weight: 400; width: 128px; color:#333333; background-color: #F7F7F7; border-radius: 5px; } QMenu::item { background-color: transparent; padding:3px 30px; margin:2px 0px; } QMenu::item:selected { background-color:#00CCFF; } QMenu::separator { height: 2px; background: lightblue; margin-left: 0px; margin-right: 5px; } QMenu::indicator { width: 18px; height: 18px; } QTableWidget#HostTabWindow{ border-top: 1px solid #76797C; border-bottom: 1px solid #76797C; background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(255, 0, 0, 0), stop:1 rgba(255, 255, 255, 0)); } QScrollBar:vertical { /* background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(255, 0, 0, 0), stop:1 rgba(255, 255, 255, 0)); */ background-color:#111111; width: 10px; margin: 15px 3px 15px 3px; border: 0px transparent #2A2929; border-radius: 4px; } QScrollBar::handle:vertical { /*background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:1 rgba(0, 0, 0, 80)); */ background-color: #222222; min-height: 5px; border-radius: 4px; } QScrollBar::sub-line:vertical { margin: 3px 0px 3px 0px; border-image: url(rc/up_arrow_disabled.png); height: 10px; width: 10px; subcontrol-position: top; subcontrol-origin: margin; } QScrollBar::add-line:vertical { margin: 3px 0px 3px 0px; border-image: url(rc/down_arrow_disabled.png); height: 10px; width: 10px; subcontrol-position: bottom; subcontrol-origin: margin; } QScrollBar::sub-line:vertical:hover,QScrollBar::sub-line:vertical:on { border-image: url(rc/up_arrow.png); height: 10px; width: 10px; subcontrol-position: top; subcontrol-origin: margin; } QScrollBar::add-line:vertical:hover, QScrollBar::add-line:vertical:on { border-image: url(rc/down_arrow.png); height: 10px; width: 10px; subcontrol-position: bottom; subcontrol-origin: margin; } QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { background: none; } QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: none; } ''') def Remove_lab(self, window): print(window) if len(self.Lable_viwe) != 1: window[1].setParent(None) self.title_tab.horizontalLayout.removeWidget(window[1]) self.Lable_viwe.remove(window[1]) self.window_docker.removeWidget(window[0]) del window[1] else: print('关闭主窗口') print(self.Lable_viwe, self.window_docker.count()) # self.window_docker.indexOf() def show_docker(self, window=None): self.window_docker.setCurrentWidget(window[0]) # self.window_docker.setCurrentIndex(1) def Init_lab(self): term = Term(data={}, open_win=False) label = ItemWidget('本地Shell', term) # itemwidget = ItemWidget(label) # itemwidget.Send_env.connect(self.Remove_lab) label.Send_env.connect(self.Remove_lab) label.Send_ck.connect(self.show_docker) self.Lable_viwe.append(label) self.window_docker.addWidget(term) cont = len(self.Lable_viwe) - 1 self.title_tab.horizontalLayout.addWidget(self.Lable_viwe[cont]) # self.title_tab.horizontalLayout.addWidget(self.Lable_viwe[len(self.Lable_viwe)-1]) def Add_lab(self, host): print(host) data = { 'host': host[2], 'port': host[3], 'username': host[4], 'ispwd': True, 'secret': host[5] } term = Term(data=data, open_win=True) label = ItemWidget(host[1], term) label.Send_env.connect(self.Remove_lab) label.Send_ck.connect(self.show_docker) self.Lable_viwe.append(label) self.window_docker.addWidget(term) self.title_tab.horizontalLayout.addWidget( self.Lable_viwe[len(self.Lable_viwe) - 1]) self.window_docker.setCurrentIndex(len(self.Lable_viwe) - 1) def Title_men(self): self._Men.exec_( self.tab_menu.mapToGlobal( QtCore.QPoint(-40, self.tab_menu.height()))) def Host_win(self, win_type=True): self.Window_out = True self.HostListWindow.setGeometry(0, 38, 360, self.height() - 38) self.win_out.setStartValue( QtCore.QRect(self.width() if win_type else self.width() - 360, 38, 360, self.height() - 38)) self.win_out.setEndValue( QRect(self.width() - 360 if win_type else self.width(), 38, 360, self.height() - 38)) # if win_type: # self.HostListWindow.show() # else: # pass # self.HostListWindow.hide() self.win_out.start() def resizeEvent(self, QResizeEvent): self._TitleLabel.setGeometry(0, 0, self.width(), 38) if self.Window_out: self.HostListWindow.setGeometry(self.width() - 360, 38, 360, self.height() - 38) else: self.HostListWindow.setGeometry(self.width(), 38, 360, self.height() - 38) self._right_rect = [ QPoint(x, y) for x in range(self.width() - self._padding, self.width() + 1) for y in range(1, self.height() - self._padding) ] self._bottom_rect = [ QPoint(x, y) for x in range(1, self.width() - self._padding) for y in range(self.height() - self._padding, self.height() + 1) ] self._corner_rect = [ QPoint(x, y) for x in range(self.width() - self._padding, self.width() + 1) for y in range(self.height() - self._padding, self.height() + 1) ] def mousePressEvent(self, event): if self.Window_out: self.Host_win(False) self.Window_out = False # 重写鼠标点击的事件 if event.y() < 42: self._MoveVarl = True self.move_DragPosition = event.globalPos() - self.pos() event.accept() elif (event.button() == Qt.LeftButton) and (event.pos() in self._corner_rect): # 鼠标左键点击右下角边界区域 self._corner_drag = True event.accept() elif (event.button() == Qt.LeftButton) and (event.pos() in self._right_rect): # 鼠标左键点击右侧边界区域 self._right_drag = True event.accept() elif (event.button() == Qt.LeftButton) and (event.pos() in self._bottom_rect): # 鼠标左键点击下侧边界区域 self._bottom_drag = True event.accept() elif (event.button() == Qt.LeftButton) and (event.y() < self._TitleLabel.height()): # 鼠标左键点击标题栏区域 self._move_drag = True self.move_DragPosition = event.globalPos() - self.pos() event.accept()
from PyQt5.QtCore import QPropertyAnimation, QEasingCurve animation = QPropertyAnimation() animation.setTargetObject() animation.setStartValue(0) animation.setEndValue(1000) animation.setDuration(1000) animation.setEasingCurve(QEasingCurve.InOutQuad) animation.start()
class FancyButton(QToolButton): def __init__(self, text="", parent=None): super().__init__(parent) self.setAttribute(Qt.WA_Hover, True) self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) if text: self.setText(text) self._fader = 0 def _set_fader(self, value): self._fader = value self.update() def _get_fader(self): return self._fader fader = pyqtProperty(float, fget=_get_fader, fset=_set_fader) def event(self, event): if event.type() == QEvent.Enter: self.anim = QPropertyAnimation(self, b"fader") self.anim.setDuration(150) self.anim.setEndValue(1.0) self.anim.start(QPropertyAnimation.DeleteWhenStopped) elif event.type() == QEvent.Leave: self.anim = QPropertyAnimation(self, b"fader") self.anim.setDuration(124) self.anim.setEndValue(0.0) self.anim.start(QPropertyAnimation.DeleteWhenStopped) else: return QToolButton.event(self, event) return False def paintEvent(self, event): painter = QPainter(self) if self.isEnabled() and not self.isDown() and not self.isChecked(): painter.save() hover_color = QColor("#424242") faded_hover_color = QColor(hover_color) faded_hover_color.setAlpha(int(self._fader * hover_color.alpha())) painter.fillRect(event.rect(), faded_hover_color) painter.restore() elif self.isDown() or self.isChecked(): painter.save() selected_color = QColor("#090909") painter.fillRect(event.rect(), selected_color) painter.restore() # fm = QFontMetrics(painter.font()) # rect = QRect(-3, 2, self.rect().width(), fm.height()) rect = event.rect() icon_rect = QRect(0, 0, 22, 22) self.icon().paint(painter, icon_rect, Qt.AlignVCenter) painter.drawText( rect.adjusted(0, 0, -3, 0), Qt.AlignRight | Qt.AlignVCenter, self.text()) def sizeHint(self): self.ensurePolished() s = self.fontMetrics().size(Qt.TextSingleLine, self.text()) s.setWidth(s.width() + 25) return s.expandedTo(QApplication.globalStrut())
class ScreensharingToolbox(base_class, ui_class): exposedPixels = 3 def __init__(self, parent): super(ScreensharingToolbox, self).__init__(parent) with Resources.directory: self.setupUi() parent.installEventFilter(self) self.animation = QPropertyAnimation(self, 'pos') self.animation.setDuration(250) self.animation.setDirection(QPropertyAnimation.Forward) self.animation.setEasingCurve(QEasingCurve.Linear) # or OutCirc with 300ms self.retract_timer = QTimer(self) self.retract_timer.setInterval(3000) self.retract_timer.setSingleShot(True) self.retract_timer.timeout.connect(self.retract) self.resize(self.size().expandedTo(self.toolbox_layout.minimumSize())) def setupUi(self): super(ScreensharingToolbox, self).setupUi(self) # fix the SVG icons, as the generated code loads them as pixmaps, losing their ability to scale -Dan scale_icon = QIcon() scale_icon.addFile(Resources.get('icons/scale.svg'), mode=QIcon.Normal, state=QIcon.Off) viewonly_icon = QIcon() viewonly_icon.addFile(Resources.get('icons/viewonly.svg'), mode=QIcon.Normal, state=QIcon.Off) screenshot_icon = QIcon() screenshot_icon.addFile(Resources.get('icons/screenshot.svg'), mode=QIcon.Normal, state=QIcon.Off) fullscreen_icon = QIcon() fullscreen_icon.addFile(Resources.get('icons/fullscreen.svg'), mode=QIcon.Normal, state=QIcon.Off) fullscreen_icon.addFile(Resources.get('icons/fullscreen-exit.svg'), mode=QIcon.Normal, state=QIcon.On) fullscreen_icon.addFile(Resources.get('icons/fullscreen-exit.svg'), mode=QIcon.Active, state=QIcon.On) fullscreen_icon.addFile(Resources.get('icons/fullscreen-exit.svg'), mode=QIcon.Disabled, state=QIcon.On) fullscreen_icon.addFile(Resources.get('icons/fullscreen-exit.svg'), mode=QIcon.Selected, state=QIcon.On) minimize_icon = QIcon() minimize_icon.addFile(Resources.get('icons/minimize.svg'), mode=QIcon.Normal, state=QIcon.Off) minimize_icon.addFile(Resources.get('icons/minimize-active.svg'), mode=QIcon.Active, state=QIcon.Off) close_icon = QIcon() close_icon.addFile(Resources.get('icons/close.svg'), mode=QIcon.Normal, state=QIcon.Off) close_icon.addFile(Resources.get('icons/close-active.svg'), mode=QIcon.Active, state=QIcon.Off) self.scale_action.setIcon(scale_icon) self.viewonly_action.setIcon(viewonly_icon) self.screenshot_action.setIcon(screenshot_icon) self.fullscreen_action.setIcon(fullscreen_icon) self.minimize_action.setIcon(minimize_icon) self.close_action.setIcon(close_icon) self.scale_button.setIcon(scale_icon) self.viewonly_button.setIcon(viewonly_icon) self.screenshot_button.setIcon(screenshot_icon) self.fullscreen_button.setIcon(fullscreen_icon) self.minimize_button.setIcon(minimize_icon) self.close_button.setIcon(close_icon) self.scale_button.setDefaultAction(self.scale_action) self.viewonly_button.setDefaultAction(self.viewonly_action) self.screenshot_button.setDefaultAction(self.screenshot_action) self.fullscreen_button.setDefaultAction(self.fullscreen_action) self.minimize_button.setDefaultAction(self.minimize_action) self.close_button.setDefaultAction(self.close_action) self.color_depth_button.clear() self.color_depth_button.addItem('Default Color Depth', ServerDefault) self.color_depth_button.addItem('TrueColor (24 bits)', TrueColor) self.color_depth_button.addItem('HighColor (16 bits)', HighColor) self.color_depth_button.addItem('LowColor (8 bits)', LowColor) def eventFilter(self, watched, event): if watched is self.parent() and event.type() == QEvent.Resize: new_x = (watched.width() - self.width()) / 2 self.move(new_x, self.y()) self.animation.setStartValue(QPoint(new_x, -self.height() + self.exposedPixels)) self.animation.setEndValue(QPoint(new_x, 0)) return False def enterEvent(self, event): super(ScreensharingToolbox, self).enterEvent(event) self.retract_timer.stop() self.expose() def leaveEvent(self, event): super(ScreensharingToolbox, self).leaveEvent(event) self.retract_timer.start() def paintEvent(self, event): # make the widget style aware option = QStyleOption() option.initFrom(self) painter = QStylePainter(self) painter.drawPrimitive(QStyle.PE_Widget, option) def expose(self): if self.animation.state() == QPropertyAnimation.Running and self.animation.direction() == QPropertyAnimation.Forward: return elif self.animation.state() == QPropertyAnimation.Stopped and self.pos() == self.animation.endValue(): return self.animation.setDirection(QPropertyAnimation.Forward) self.animation.start() def retract(self): if self.animation.state() == QPropertyAnimation.Running and self.animation.direction() == QPropertyAnimation.Backward: return elif self.animation.state() == QPropertyAnimation.Stopped and self.pos() == self.animation.startValue(): return self.animation.setDirection(QPropertyAnimation.Backward) self.animation.start()
QRect(150, 100, 50, 50), button3, QRect(100, 150, 50, 50), button4, QRect(150, 150, 50, 50), group) state6 = createGeometryState(button1, QRect(50, 50, 50, 50), button2, QRect(200, 50, 50, 50), button3, QRect(50, 200, 50, 50), button4, QRect(200, 200, 50, 50), group) state7 = createGeometryState(button1, QRect(0, 0, 50, 50), button2, QRect(250, 0, 50, 50), button3, QRect(0, 250, 50, 50), button4, QRect(250, 250, 50, 50), group) group.setInitialState(state1) animationGroup = QParallelAnimationGroup() anim = QPropertyAnimation(button4, 'geometry') anim.setDuration(1000) anim.setEasingCurve(QEasingCurve.OutElastic) animationGroup.addAnimation(anim) subGroup = QSequentialAnimationGroup(animationGroup) subGroup.addPause(100) anim = QPropertyAnimation(button3, 'geometry') anim.setDuration(1000) anim.setEasingCurve(QEasingCurve.OutElastic) subGroup.addAnimation(anim) subGroup = QSequentialAnimationGroup(animationGroup) subGroup.addPause(150) anim = QPropertyAnimation(button2, 'geometry') anim.setDuration(1000) anim.setEasingCurve(QEasingCurve.OutElastic)
class BlueApp: def __init__(self, argv): self.app = QApplication(argv) self.mainWindow = QtWidgets.QMainWindow() self.mainWindow.closeEvent = self.onClose self.ui = blueMainUi.Ui_MainWindow() self.ui.setupUi(self.mainWindow) #settings and statistics self.defaultSettings = dict(image_detect=True, text_detect=True, game_type=0, pipeRank=0, goal = 10, startDate = time.time(), avata = "res/icon.png", name = "窜天猴") self.defaultStat = dict(stat = [ [0, 0, 0, 0] for i in range(7)], achivement = 0, lastWater = 0, lastFertilize = 0, cleanHours = 0, cleanMinutes = 0) self.achivementsList = ["大淫魔", "从头开始", "欲火渐盛", "钢筋铁骨", "渐入佳境","心无杂念"] self.achivementsStd = [0, 24, 24 * 3, 24 * 7, 24 * 31, 27 * 365] self.loadSettings() self.validateSettings() self.loadStatistics() #setup the visibles self.setupUi2() self.setupWidget1() self.setupWidget2() self.setupWidget3() self.setupWidget4() self.refreshStatistics() #setup porn_detector self.devMode = False self.timeToExit = False self.pornDectector = porn_detector_final.PornDetector() self.pornDetected = False self.detectThread = threading.Thread(target = self.detectPorn) self.detectThread.start() self.alarm = False #first alarm, then take action #setup timer self.cleanMinute = 0 self.MinuteTimer = QTimer() self.MinuteTimer.setInterval(1000 * 60) self.MinuteTimer.timeout.connect(self.addCleanMinute) self.MinuteTimer.start() #lauch self.mainWindow.show() self.connections() def addCleanMinute(self): self.stat["cleanMinutes"] += 1 if self.stat["cleanMinutes"] == 60: self.self.stat["cleanMinutes"] = 0 self.stat["cleanHours"] += 1 self.saveStatistics() self.refreshStatistics() self.checkLevel() def call_zqz(self): if self.settings["game_type"] == 0: os.system("python2 easy_maze/PyMaze.py") else : os.system("python2 esay_game/play.py") QtWidgets.QMessageBox.information(None, "bluer", "你有10s的时间关掉黄黄的东西。") time.sleep(10) def detectPorn(self): while(True): if "PORN_DETECTED" == self.pornDectector.porn_detector(self.settings["image_detect"], self.settings["text_detect"], self.devMode): if self.alarm: self.call_zqz() self.stat["cleanHours"] -= 24 if self.stat["cleanHours"] < 0: self.stat["cleanHours"] = 0 l = self.stat["stat"][time.localtime(time.time())[6]] h = time.localtime(time.time())[3] if h >= 0 and h < 6: l[0] = 1 elif h >= 6 and h < 12: l[1] = 1 elif h >= 12 and h < 18: l[2] = 1; else: l[3] = 1; else: self.alarm = True else: self.alarm = True self.saveStatistics() self.refreshStatistics() time.sleep(10) def onClose(self, event): self.mainWindow.hide() event.ignore() def onTrayClicked(self, event): if event == QSystemTrayIcon.Trigger or event == QSystemTrayIcon.DoubleClick: self.mainWindow.show() def saveSettings(self, event): QtWidgets.QMessageBox.Question = QIcon("res/logo-tray.png") if self.settings["goal"] != self.ui.spin_goal.value(): ret = QtWidgets.QMessageBox.question(self.mainWindow, "Blue", "确定要将目标改为" + str(self.ui.spin_goal.value()) + "天吗?\n" "此操作会重置当前任务的进度。") if ret != QtWidgets.QMessageBox.No: self.settings["goal"] = self.ui.spin_goal.value() self.saveStatistics() self.refreshStatistics() QtWidgets.QMessageBox.information(None, "Blue", "新目标设置为" + str(self.settings["goal"]) + "天") else: QtWidgets.QMessageBox.information(None, "Blue", "目标没有被重置") try: sfile = open(PATH_TO_SETTINGS, "w") json.dump(self.settings, sfile) sfile.close() except Exception: return QtWidgets.QMessageBox.information(None, "Blue", "设置已保存:D") self.refreshStatistics() def checkLevel(self): for i in range(5, -1, -1): if self.stat["cleanHours"] >= self.achivementsStd[i] and self.stat["achivement"] < i: QtWidgets.QMessageBox.information(None, "Blue", "等级提升为Lv. " + str(i) + " :" + self.achivementsList[i]) self.stat["achivement"] = i self.saveStatistics() break def saveStatistics(self): json.dump(self.stat, open(PATH_TO_STATISTICS, "w")) def refreshStatistics(self): days = time.localtime(time.time()) delta = self.settings["goal"] - self.stat["cleanHours"] if delta == 0: QtWidgets.QMessageBox.information(None, "Blue", "目标达成!!!\n请设置新的目标!!!") self.slideClicked3(None) self.ui.lb_days.setText(str(delta)) self.ui.lb_goal.setText(str(self.settings["goal"])) self.ui.lb_achv.setText(self.achivementsList[self.stat["achivement"]]) self.ui.lb_lv.setText("Lv. " + str(self.stat["achivement"])) self.ui.lb_growth.setText(str(self.stat["cleanHours"] // 24)) #setup the water and ferilization if days[7] == time.localtime(self.stat["lastWater"])[7]: self.ui.lb_jiaoshui.setPixmap(QPixmap("res/ack.png")) self.watered = True else: self.watered = False if days[7] == time.localtime(self.stat["lastFertilize"])[7]: self.ui.lb_shifei.setPixmap(QPixmap("res/ack.png")) self.fertilized = True else: self.fertilized = False #setup the calendar pixmapA = QPixmap("res/lu.png") pixmapB = QPixmap("res/blue.png") h = days[3] if h >= 0 and h < 6: r = 0 elif h >= 6 and h < 12: r = 1 elif h >= 12 and h < 18: r = 2 else: r = 3 for i in range(days[6]): for j in range(4): if self.stat["stat"][i][j] == 0: self.statLabels[i][j].setPixmap(pixmapA) else: self.statLabels[i][j].setPixmap(pixmapB) day = days[6] for j in range(r): if self.stat["stat"][day][j] == 0: self.statLabels[day][j].setPixmap(pixmapA) else: self.statLabels[day][j].setPixmap(pixmapB) #setup the wall for i in range(6): self.achivIcons[i].setPixmap(QPixmap("res/" + str(i) * 2)) for i in range(self.stat["achivement"] + 1): self.achivIcons[i].setPixmap(QPixmap("res/" + str(i))) def loadSettings(self): try: sfile = open(PATH_TO_SETTINGS, "r") self.settings = json.load(sfile) sfile.close() except: self.settings = self.defaultSettings self.saveSettings(None) def validateSettings(self): for keys in self.defaultSettings: try: self.settings[keys] except: self.settings[keys] = self.defaultSettings[keys] def loadStatistics(self): try: sfile = open(PATH_TO_STATISTICS, "r") self.stat = json.load(sfile) except: self.stat = self.defaultStat for keys in self.defaultStat: try: self.stat[keys] except: self.stat[keys] = self.defaultStat[keys] self.saveStatistics() def refreshInfo(self): #setup avata pixmap = QPixmap() pixmap.load("res/avata_mask") pixmap.scaled(115, 115) self.ui.lb_avata.setMask(pixmap.mask()) pixmap.load(self.settings["avata"]) self.ui.lb_avata.setPixmap(pixmap) # self.ui.lb_avata2.setMask(pixmap.mask()) # pixmap.load(self.settings["avata"]) # self.ui.lb_avata2.setPixmap(pixmap) #setup the name self.ui.lb_welcomname.setText(self.settings["name"]) self.ui.lb_nick.setText(self.settings["name"]) def appExit(self, event): if self.devMode == False: QtWidgets.QMessageBox.information(None, "bluer", "开发者模式开启") self.devMode = True else: QtWidgets.QMessageBox.information(None, "bluer", "开发者模式关闭") self.devMode = False def avataEdit(self, event): openDlg = QtWidgets.QFontDialog() openDlg.open() def setupUi2(self): #setup event handling self.sideButtons = [self.ui.lb1, self.ui.lb2, self.ui.lb3, self.ui.lb4] self.ui.lb_exit.mousePressEvent = self.appExit self.setupAnimes() self.setupSideButtons() self.refreshInfo() #setup tray self.icon = QIcon("res/logo-tray.png") self.trayIcon = QSystemTrayIcon() self.trayIcon.setIcon(self.icon) self.trayIcon.activated.connect(self.onTrayClicked) self.trayIcon.show() #setup the info edit self.ui.lb_avata.mousePressEvent = self.avataEdit def setupAnimes(self): self.shiftAnime1 = QPropertyAnimation() self.shiftAnime1.setTargetObject(self.ui.widget1) self.shiftAnime1.setPropertyName("geometry".encode()) self.shiftAnime1.setDuration(400) self.shiftAnime1.setStartValue(QRect(177, 29, 0, 571)) self.shiftAnime1.setEndValue(QRect(177, 29, 623, 571)) self.shiftAnime2 = QPropertyAnimation() self.shiftAnime2.setTargetObject(self.ui.widget2) self.shiftAnime2.setPropertyName("geometry".encode()) self.shiftAnime2.setDuration(400) self.shiftAnime2.setStartValue(QRect(800, 29, 0, 571)) self.shiftAnime2.setEndValue(QRect(177, 29, 623, 571)) self.shiftAnime3 = QPropertyAnimation() self.shiftAnime3.setTargetObject(self.ui.widget3) self.shiftAnime3.setPropertyName("geometry".encode()) self.shiftAnime3.setDuration(400) self.shiftAnime3.setStartValue(QRect(800, 29, 623, 571)) self.shiftAnime3.setEndValue(QRect(177, 29, 623, 571)) self.shiftAnime4 = QPropertyAnimation() self.shiftAnime4.setTargetObject(self.ui.widget4) self.shiftAnime4.setPropertyName("geometry".encode()) self.shiftAnime4.setDuration(400) self.shiftAnime4.setStartValue(QRect(800, 29, 623, 571)) self.shiftAnime4.setEndValue(QRect(177, 29, 623, 571)) self.selectedWidget = self.ui.widget1 def setSlideMid(self, bt): if self.selectedSideButton != bt: bt.setStyleSheet(slide_bt_mid) def setSlideUp(self, bt): if self.selectedSideButton != bt: bt.setStyleSheet(slide_bt_up) def setSlideDown(self, bt): self.selectedSideButton.setStyleSheet(slide_bt_up) self.selectedSideButton = bt bt.setStyleSheet(slide_bt_down) def slideEnter1(self, event): self.setSlideMid(self.ui.lb1) def slideEnter2(self, event): self.setSlideMid(self.ui.lb2) def slideEnter3(self, event): self.setSlideMid(self.ui.lb3) def slideEnter4(self, event): self.setSlideMid(self.ui.lb4) def slideLeave1(self, event): self.setSlideUp(self.ui.lb1) def slideLeave2(self, event): self.setSlideUp(self.ui.lb2) def slideLeave3(self, event): self.setSlideUp(self.ui.lb3) def slideLeave4(self, event): self.setSlideUp(self.ui.lb4) def slideBack(self, event): self.setSlideDown(self.ui.lb1) self.ui.widget1.raise_() self.shiftAnime1.start() self.selectedWidget = self.ui.widget1 def slideClicked1(self, event): self.setSlideDown(self.ui.lb1) if self.selectedWidget != self.ui.widget1: self.ui.widget1.raise_() self.shiftAnime1.start() self.selectedWidget = self.ui.widget1 def slideClicked2(self, event): self.setSlideDown(self.ui.lb2) if self.selectedWidget != self.ui.widget2: self.ui.widget2.raise_() self.shiftAnime2.start() self.selectedWidget = self.ui.widget2 def slideClicked3(self, event): self.setSlideDown(self.ui.lb3) if self.selectedWidget != self.ui.widget3: self.ui.widget3.raise_() self.shiftAnime3.start() self.selectedWidget = self.ui.widget3 def jiaoshuiCheck(self, event): pixmap = QPixmap() pixmap.load("res/ack.png") self.ui.lb_jiaoshui.setPixmap(pixmap) self.stat["lastWater"] = time.time() self.saveStatistics() def shifeiCheck(self, event): pixmap = QPixmap() pixmap.load("res/ack.png") self.ui.lb_shifei.setPixmap(pixmap) self.stat["lastFertilize"] = time.time() self.saveStatistics() def slideClicked4(self, event): self.setSlideDown(self.ui.lb4) if self.selectedWidget != self.ui.widget4: self.ui.widget4.raise_() self.shiftAnime4.start() self.selectedWidget = self.ui.widget4 def setupWidget1(self): #setup the tree movie = QMovie() movie.setFileName("res/tree.gif") self.ui.lb_tree.setMovie(movie) self.ui.lb_tree_big.setMovie(movie) movie.start() #setup the statistics self.ui.gridLayout.setHorizontalSpacing(60) self.ui.gridLayout.setVerticalSpacing(10) self.ui.gridLayout.setGeometry(QRect(0, 51, 291, 224)) self.ui.gridLayout.setAlignment(QtCore.Qt.AlignCenter) self.statLabels = [] for i in range(7): self.statLabels.append([]) for j in range(4): self.statLabels[i].append(QLabel()) self.statLabels[i][j].setScaledContents(True) self.statLabels[i][j].setAutoFillBackground(False) self.statLabels[i][j].setAlignment(QtCore.Qt.AlignCenter) self.ui.gridLayout.addWidget(self.statLabels[i][j], i, j, 1, 1) def setupWidget2(self): self.ui.lb_jiaoshui.mousePressEvent = self.jiaoshuiCheck self.ui.lb_shifei.mousePressEvent = self.shifeiCheck def setupWidget3(self): self.ui.check_maze.mousePressEvent = self.mazeCliked self.ui.check_paper.mousePressEvent = self.paperCliked self.ui.check_pic.mousePressEvent = self.picCliked self.ui.check_text.mousePressEvent = self.textClicked self.ui.lb_save.mousePressEvent = self.saveSettings self.ui.spin_goal.setValue(self.settings["goal"]) if self.settings["game_type"] == 0: self.mazeCliked(None) else: self.paperCliked(None) self.picCliked(None) self.picCliked(None) self.textClicked(None) self.textClicked(None) def setupWidget4(self): self.achivIcons = [self.ui.lb_a0, self.ui.lb_a1, self.ui.lb_a2, self.ui.lb_a3, self.ui.lb_a4, self.ui.lb_a5] for i in range(6): self.achivIcons[i].setPixmap(QPixmap("res/" + str(i) * 2)) def mazeCliked(self, event): pixmap = QPixmap() pixmap.load("res/checked.png") self.ui.check_maze.setPixmap(pixmap) pixmap.load("res/unchecked.png") self.ui.check_paper.setPixmap(pixmap) self.settings["game_type"] = 0 def paperCliked(self, event): pixmap = QPixmap() pixmap.load("res/checked.png") self.ui.check_paper.setPixmap(pixmap) pixmap.load("res/unchecked.png") self.ui.check_maze.setPixmap(pixmap) self.settings["game_type"] = 1 def picCliked(self, event): pixmap = QPixmap() pixmap.load("res/checked.png") self.ui.check_pic.setPixmap(pixmap) pixmap.load("res/unchecked.png") self.ui.check_text.setPixmap(pixmap) self.settings["pic_detect"] = 1 self.settings["text_detect"] = 0 def textClicked(self, event): pixmap = QPixmap() pixmap.load("res/checked.png") self.ui.check_text.setPixmap(pixmap) pixmap.load("res/unchecked.png") self.ui.check_pic.setPixmap(pixmap) self.settings["pic_detect"] = 1 self.settings["text_detect"] = 1 def setupSideButtons(self): self.ui.lb1.enterEvent = self.slideEnter1 self.ui.lb1.leaveEvent = self.slideLeave1 self.ui.lb1.mousePressEvent = self.slideClicked1 self.ui.lb2.enterEvent = self.slideEnter2 self.ui.lb2.leaveEvent = self.slideLeave2 self.ui.lb2.mousePressEvent = self.slideClicked2 self.ui.lb3.enterEvent = self.slideEnter3 self.ui.lb3.leaveEvent = self.slideLeave3 self.ui.lb3.mousePressEvent = self.slideClicked3 self.ui.lb4.enterEvent = self.slideEnter4 self.ui.lb4.leaveEvent = self.slideLeave4 self.ui.lb4.mousePressEvent = self.slideClicked4 self.ui.lb4.enterEvent = self.slideEnter4 self.ui.lb4.leaveEvent = self.slideLeave4 self.ui.lb4.mousePressEvent = self.slideClicked4 self.ui.lb_back2.mousePressEvent = self.slideBack self.ui.lb_back3.mousePressEvent = self.slideBack self.ui.lb_back4.mousePressEvent = self.slideBack for lb in self.sideButtons: lb.setStyleSheet(slide_bt_up) self.selectedSideButton = self.ui.lb1 self.slideClicked1(None) def connections(self): pass
class CoolToolButton(QToolButton): def __init__(self, action=None, parent=None): super().__init__(parent) self._fader = 0 if action is not None: self.setDefaultAction(action) self.setAttribute(Qt.WA_Hover, True) self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) def _set_fader(self, value): self._fader = value self.update() def _get_fader(self): return self._fader fader = pyqtProperty(float, fget=_get_fader, fset=_set_fader) def event(self, event): if event.type() == QEvent.Enter: self.anim = QPropertyAnimation(self, b"fader") self.anim.setDuration(150) self.anim.setEndValue(1.0) self.anim.start(QPropertyAnimation.DeleteWhenStopped) elif event.type() == QEvent.Leave: self.anim = QPropertyAnimation(self, b"fader") self.anim.setDuration(124) self.anim.setEndValue(0.0) self.anim.start(QPropertyAnimation.DeleteWhenStopped) else: return QToolButton.event(self, event) return False def paintEvent(self, event): painter = QPainter(self) if self.isEnabled() and not self.isDown() and not self.isChecked(): painter.save() hover_color = QColor("#424242") faded_hover_color = QColor(hover_color) faded_hover_color.setAlpha(int(self._fader * hover_color.alpha())) painter.fillRect(event.rect(), faded_hover_color) painter.restore() elif self.isDown() or self.isChecked(): painter.save() selected_color = QColor("#161719") painter.fillRect(event.rect(), selected_color) painter.restore() is_titled = bool(self.defaultAction().property("titled")) icon_rect = QRect(0, 0, 32, 32) icon_rect.moveCenter(event.rect().center()) if is_titled: font = painter.font() center_rect = event.rect() font.setPointSizeF(6) fm = QFontMetrics(font) line_height = fm.height() text_flags = Qt.AlignHCenter | Qt.AlignTop project_name = self.defaultAction().property("heading") if project_name is not None: center_rect.adjust(0, line_height, 0, 0) icon_rect.moveTop(center_rect.top()) else: icon_rect.moveCenter(center_rect.center()) self.icon().paint(painter, icon_rect, Qt.AlignCenter) painter.setFont(font) r = QRect(0, 5, self.rect().width(), line_height) painter.setPen(Qt.white) margin = 5 available_width = r.width() - margin ellided_project_name = fm.elidedText( project_name, Qt.ElideMiddle, available_width) painter.drawText(r, text_flags, ellided_project_name) else: self.icon().paint(painter, icon_rect, Qt.AlignCenter) def sizeHint(self): button_size = self.iconSize().expandedTo(QSize(48, 48)) return button_size def minimumSizeHint(self): return QSize(8, 8)
class LineEditMenu(AeroMenu): """ 单行输入框右击菜单 """ def __init__(self, parent): super().__init__('', parent) # 不能直接改width self.animation = QPropertyAnimation(self, b'geometry') self.initWidget() def initWidget(self): """ 初始化小部件 """ self.setObjectName('lineEditMenu') self.animation.setDuration(300) self.animation.setEasingCurve(QEasingCurve.OutQuad) self.setQss() def createActions(self): # 创建动作 self.cutAct = QAction(QIcon('resource\\images\\menu\\黑色剪刀.png'), '剪切', self, shortcut='Ctrl+X', triggered=self.parent().cut) self.copyAct = QAction(QIcon('resource\\images\\menu\\黑色复制.png'), '复制', self, shortcut='Ctrl+C', triggered=self.parent().copy) self.pasteAct = QAction(QIcon('resource\\images\\menu\\黑色粘贴.png'), '粘贴', self, shortcut='Ctrl+V', triggered=self.parent().paste) self.cancelAct = QAction(QIcon('resource\\images\\menu\\黑色撤销.png'), '取消操作', self, shortcut='Ctrl+Z', triggered=self.parent().undo) self.selectAllAct = QAction('全选', self, shortcut='Ctrl+A', triggered=self.parent().selectAll) # 创建动作列表 self.action_list = [ self.cutAct, self.copyAct, self.pasteAct, self.cancelAct, self.selectAllAct ] def exec_(self, pos): # 删除所有动作 self.clear() # clear之后之前的动作已不再存在故需重新创建 self.createActions() # 初始化属性 self.setProperty('hasCancelAct', 'false') width = 176 actionNum = len(self.action_list) # 访问系统剪贴板 self.clipboard = QApplication.clipboard() # 根据剪贴板内容是否为text分两种情况讨论 if self.clipboard.mimeData().hasText(): # 再根据3种情况分类讨论 if self.parent().text(): self.setProperty('hasCancelAct', 'true') width = 213 if self.parent().selectedText(): self.addActions(self.action_list) else: self.addActions(self.action_list[2:]) actionNum -= 2 else: self.addAction(self.pasteAct) actionNum = 1 else: if self.parent().text(): self.setProperty('hasCancelAct', 'true') width = 213 if self.parent().selectedText(): self.addActions(self.action_list[:2] + self.action_list[3:]) actionNum -= 1 else: self.addActions(self.action_list[3:]) actionNum -= 3 else: return # 每个item的高度为38px,10为上下的内边距和 height = actionNum * 38 + 10 # 不能把初始的宽度设置为0px,不然会报警 self.animation.setStartValue(QRect(pos.x(), pos.y(), 1, 1)) self.animation.setEndValue(QRect(pos.x(), pos.y(), width, height)) self.setStyle(QApplication.style()) # 开始动画 self.animation.start() super().exec_(pos)
QRect(150, 100, 50, 50), button3, QRect(100, 150, 50, 50), button4, QRect(150, 150, 50, 50), group) state6 = createGeometryState(button1, QRect(50, 50, 50, 50), button2, QRect(200, 50, 50, 50), button3, QRect(50, 200, 50, 50), button4, QRect(200, 200, 50, 50), group) state7 = createGeometryState(button1, QRect(0, 0, 50, 50), button2, QRect(250, 0, 50, 50), button3, QRect(0, 250, 50, 50), button4, QRect(250, 250, 50, 50), group) group.setInitialState(state1) animationGroup = QParallelAnimationGroup() anim = QPropertyAnimation(button4, b'geometry') anim.setDuration(1000) anim.setEasingCurve(QEasingCurve.OutElastic) animationGroup.addAnimation(anim) subGroup = QSequentialAnimationGroup(animationGroup) subGroup.addPause(100) anim = QPropertyAnimation(button3, b'geometry') anim.setDuration(1000) anim.setEasingCurve(QEasingCurve.OutElastic) subGroup.addAnimation(anim) subGroup = QSequentialAnimationGroup(animationGroup) subGroup.addPause(150) anim = QPropertyAnimation(button2, b'geometry') anim.setDuration(1000) anim.setEasingCurve(QEasingCurve.OutElastic)
class SnackbarWidget(QWidget, Ui_SnackbarWidget): _dismissed = pyqtSignal(QWidget) def _set_opacity(self, o): self._opacity = o self.setWindowOpacity(o) def _get_opacity(self): return self._opacity opacity = pyqtProperty(float, fset=_set_opacity, fget=_get_opacity) def __init__(self, msg, icon=None, timeout=3000, action_text=None, action=None, animate=True): super().__init__() self.setupUi(self) self.animate = animate self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool | Qt.WindowStaysOnTopHint) self.setAttribute(Qt.WA_TranslucentBackground) self.setAttribute(Qt.WA_ShowWithoutActivating) self.setObjectName("SnackbarWidget") self.label.setText(msg) self.action = action if action_text and action: self.button_action.setText(action_text) self.button_action.clicked.connect(self._on_action_clicked) self.button_action.setCursor(QCursor(Qt.PointingHandCursor)) if icon: self.label_icon.setPixmap(icon) self.button_close.clicked.connect(self._on_timeout) self.timer = QTimer() self.timer.setInterval(timeout) self.timer.setSingleShot(True) self.timer.timeout.connect(self._on_timeout) self.index = 0 if self.animate: self._set_opacity(0.0) self.show_animation = QPropertyAnimation(self, b"opacity") self.show_animation.finished.connect(self.timer.start) self.show_animation.setDuration(500) self.show_animation.setStartValue(0.0) self.show_animation.setEndValue(1.0) self.show_animation.setEasingCurve(QEasingCurve.Linear) self.hide_animation = QPropertyAnimation(self, b"opacity") self.hide_animation.finished.connect(self.hide) self.hide_animation.setDuration(500) self.hide_animation.setStartValue(1.0) self.hide_animation.setEndValue(0.0) self.show_animation.setEasingCurve(QEasingCurve.Linear) else: self._set_opacity(1.0) def _on_action_clicked(self): self.action() self.timer.stop() if self.animate: self.show_animation.stop() self.hide_animation.start() def set_index(self, index): self.index = index self.move_popup() def paintEvent(self, _): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) rect = QRect() rect.setX(self.rect().x() + 5) rect.setY(self.rect().y() + 5) rect.setWidth(self.rect().width() - 10) rect.setHeight(self.rect().height() - 10) painter.setBrush(QBrush(QColor(0x32, 0x32, 0x32, 0xDD))) painter.setPen(Qt.NoPen) painter.drawRoundedRect(rect, 10, 10) def move_popup(self): main_window = ParsecApp.get_main_window() if not main_window: return offset = 10 height = 101 if platform.system() == "Windows" else 75 width = min(500, main_window.size().width() - 40) self.resize(QSize(width, height)) x = main_window.size().width() - width - 20 y = main_window.size().height() - ((height + offset) * (self.index + 1)) # Hide the snackbar if the main window does not have enough space to show it self.set_visible(y > 30) pos = main_window.mapToGlobal(QPoint(x, y)) self.setGeometry(pos.x(), pos.y(), width, height) def _on_timeout(self): if self.animate: self.show_animation.stop() self.hide_animation.start() else: self.hide() def set_visible(self, visible): if not visible: super().hide() else: super().show() def hide(self): if self.animate: self.show_animation.stop() self.hide_animation.stop() self.timer.stop() super().hide() self._dismissed.emit(self) def show(self): self.move_popup() super().show() if self.animate: self._set_opacity(0.0) self.show_animation.start() else: self._set_opacity(1.0) self.timer.start()
class AnimatedStackedWidget(QFrame): # Current widget has changed currentChanged = Signal(int) # Transition animation has started transitionStarted = Signal() # Transition animation has finished transitionFinished = Signal() def __init__(self, parent=None, animationEnabled=True): QFrame.__init__(self, parent) self.__animationEnabled = animationEnabled layout = StackLayout() self.__fadeWidget = CrossFadePixmapWidget(self) self.transitionAnimation = \ QPropertyAnimation(self.__fadeWidget, b"blendingFactor_", self) self.transitionAnimation.setStartValue(0.0) self.transitionAnimation.setEndValue(1.0) self.transitionAnimation.setDuration(100 if animationEnabled else 0) self.transitionAnimation.finished.connect(self.__onTransitionFinished) layout.addWidget(self.__fadeWidget) layout.currentChanged.connect(self.__onLayoutCurrentChanged) self.setLayout(layout) self.__widgets = [] self.__currentIndex = -1 self.__nextCurrentIndex = -1 def setAnimationEnabled(self, animationEnabled): """ Enable/disable transition animations. """ if self.__animationEnabled != animationEnabled: self.__animationEnabled = animationEnabled self.transitionAnimation.setDuration( 100 if animationEnabled else 0) def animationEnabled(self): """ Is the transition animation enabled. """ return self.__animationEnabled def addWidget(self, widget): """ Append the widget to the stack and return its index. """ return self.insertWidget(self.layout().count(), widget) def insertWidget(self, index, widget): """ Insert `widget` into the stack at `index`. """ index = min(index, self.count()) self.__widgets.insert(index, widget) if index <= self.__currentIndex or self.__currentIndex == -1: self.__currentIndex += 1 return self.layout().insertWidget(index, widget) def removeWidget(self, widget): """ Remove `widget` from the stack. .. note:: The widget is hidden but is not deleted. """ index = self.__widgets.index(widget) self.layout().removeWidget(widget) self.__widgets.pop(index) def widget(self, index): """ Return the widget at `index` """ return self.__widgets[index] def indexOf(self, widget): """ Return the index of `widget` in the stack. """ return self.__widgets.index(widget) def count(self): """ Return the number of widgets in the stack. """ return max(self.layout().count() - 1, 0) def setCurrentWidget(self, widget): """ Set the current shown widget. """ index = self.__widgets.index(widget) self.setCurrentIndex(index) def setCurrentIndex(self, index): """ Set the current shown widget index. """ index = max(min(index, self.count() - 1), 0) if self.__currentIndex == -1: self.layout().setCurrentIndex(index) self.__currentIndex = index return current = self.__widgets[self.__currentIndex] next_widget = self.__widgets[index] def has_pending_resize(widget): return widget.testAttribute(Qt.WA_PendingResizeEvent) or \ not widget.testAttribute(Qt.WA_WState_Created) current_pix = next_pix = None if not has_pending_resize(current): current_pix = current.grab() if not has_pending_resize(next_widget): next_pix = next_widget.grab() with updates_disabled(self): self.__fadeWidget.setPixmap(current_pix) self.__fadeWidget.setPixmap2(next_pix) self.__nextCurrentIndex = index self.__transitionStart() def currentIndex(self): """ Return the current shown widget index. """ return self.__currentIndex def sizeHint(self): hint = QFrame.sizeHint(self) if hint.isEmpty(): hint = QSize(0, 0) return hint def __transitionStart(self): """ Start the transition. """ log.debug("Stack transition start (%s)", str(self.objectName())) # Set the fade widget as the current widget self.__fadeWidget.blendingFactor_ = 0.0 self.layout().setCurrentWidget(self.__fadeWidget) self.transitionAnimation.start() self.transitionStarted.emit() def __onTransitionFinished(self): """ Transition has finished. """ log.debug("Stack transition finished (%s)" % str(self.objectName())) self.__fadeWidget.blendingFactor_ = 1.0 self.__currentIndex = self.__nextCurrentIndex with updates_disabled(self): self.layout().setCurrentIndex(self.__currentIndex) self.transitionFinished.emit() def __onLayoutCurrentChanged(self, index): # Suppress transitional __fadeWidget current widget if index != self.count(): self.currentChanged.emit(index)
class SwitchButton(QWidget): signal_on = pyqtSignal(bool) def __init__(self, parent=None, w1="Start", l1=12, w2="Stop", l2=43, width=80): super(SwitchButton, self).__init__(parent) self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_TranslucentBackground) self.__labeloff = QLabel(self) self.__labeloff.setText(w2) self.__labeloff.setStyleSheet( """color: rgb(120, 120, 120); font-weight: bold;""") self.__background = Background(self) self.__labelon = QLabel(self) self.__labelon.setText(w1) self.__labelon.setStyleSheet( """color: rgb(255, 255, 255); font-weight: bold;""") self.__circle = Circle(self) self.__circlemove = None self.__ellipsemove = None self.__enabled = True self.__duration = 100 self.setFixedSize(width, 24) self.__background.resize(20, 20) self.__background.move(2, 2) self.__labelon.move(l1, 5) self.__labeloff.move(l2, 5) self.switch_on = False if self.switch_on: self.__circle.move(self.width() - 22, 2) self.__background.resize(self.width() - 4, 20) else: self.__circle.move(2, 2) def change_state(self): if self.switch_on: self.__circle.move(2, 2) self.__background.resize(20, 20) else: self.__circle.move(self.width() - 22, 2) self.__background.resize(self.width() - 4, 20) self.switch_on = not self.switch_on def setDuration(self, time): self.__duration = time def setEnabled(self, bool): self.__enabled = bool # self.__background.setEnabled(bool) self.__circle.setEnabled(bool) def mouseReleaseEvent(self, event): if not self.__enabled: return self.__circlemove = QPropertyAnimation(self.__circle, b"pos") self.__circlemove.setDuration(self.__duration) self.__ellipsemove = QPropertyAnimation(self.__background, b"size") self.__ellipsemove.setDuration(self.__duration) xs = 2 y = 2 xf = self.width() - 22 hback = 20 isize = QSize(hback, hback) bsize = QSize(self.width() - 4, hback) if self.switch_on: xf = 2 xs = self.width() - 22 bsize = QSize(hback, hback) isize = QSize(self.width() - 4, hback) self.__circlemove.setStartValue(QPoint(xs, y)) self.__circlemove.setEndValue(QPoint(xf, y)) self.__ellipsemove.setStartValue(isize) self.__ellipsemove.setEndValue(bsize) self.__circlemove.start() self.__ellipsemove.start() self.switch_on = not self.switch_on self.signal_on.emit(self.switch_on) def paintEvent(self, event): s = self.size() qp = QPainter() qp.begin(self) qp.setRenderHint(QPainter.Antialiasing, True) pen = QPen(Qt.NoPen) qp.setPen(pen) qp.setBrush(QColor(170, 170, 170)) qp.drawRoundedRect(0, 0, s.width(), s.height(), 12, 12) # lg = QLinearGradient(35, 30, 35, 0) # lg.setColorAt(0, QColor(210, 210, 210, 255)) # lg.setColorAt(0.25, QColor(255, 255, 255, 255)) # lg.setColorAt(0.82, QColor(255, 255, 255, 255)) # lg.setColorAt(1, QColor(210, 210, 210, 255)) # qp.setBrush(lg) # qp.drawRoundedRect(1, 1, s.width()-2, s.height()-2, 10, 10) qp.setBrush(QColor(255, 255, 255)) qp.drawRoundedRect(2, 2, s.width() - 4, s.height() - 4, 10, 10) if self.__enabled: # lg = QLinearGradient(50, 30, 35, 0) # lg.setColorAt(0, QColor(230, 230, 230, 255)) # lg.setColorAt(0.25, QColor(255, 255, 255, 255)) # lg.setColorAt(0.82, QColor(255, 255, 255, 255)) # lg.setColorAt(1, QColor(230, 230, 230, 255)) qp.setBrush(QColor(255, 255, 255)) qp.drawRoundedRect(2, 2, s.width() - 4, s.height() - 4, 10, 10) self.__labelon.setStyleSheet( """color: rgb(255, 255, 255); font-weight: bold;""") else: # lg = QLinearGradient(50, 30, 35, 0) # lg.setColorAt(0, QColor(200, 200, 200, 255)) # lg.setColorAt(0.25, QColor(230, 230, 230, 255)) # lg.setColorAt(0.82, QColor(230, 230, 230, 255)) # lg.setColorAt(1, QColor(200, 200, 200, 255)) qp.setBrush(QColor(210, 210, 210)) self.__labelon.setStyleSheet( """color: rgb(210, 210, 210); font-weight: bold;""") qp.drawRoundedRect(2, 2, s.width() - 4, s.height() - 4, 10, 10) qp.end()
class Tile(QLabel): moved = pyqtSignal() def __init__(self, parent=None): super(Tile, self).__init__(parent) self.isEmpty = False self.perfectPos = None self.hasPerfectPos = True self.moveEnabled = False def mousePressEvent(self, event): if not self.moveEnabled: return if self.switch(): self.moved.emit() def switch(self, anim=True): var = False for neighbor in self.getNeighbors(): if neighbor.isEmpty: Xself = self.pos().x() Yself = self.pos().y() Xneigh = neighbor.pos().x() Yneigh = neighbor.pos().y() if self.perfectPos.x() == Xneigh and \ self.perfectPos.y() == Yneigh: self.hasPerfectPos = True else: self.hasPerfectPos = False if neighbor.perfectPos.x() == Xself and \ neighbor.perfectPos.y() == Yself: neighbor.hasPerfectPos = True else: neighbor.hasPerfectPos = False if anim: self.animation = QPropertyAnimation(self, "geometry") self.animation.setDuration(200) self.animation.setEndValue(QRect(Xneigh, Yneigh, self.width(), self.height())) self.animation.start() else: self.move(Xneigh, Yneigh) neighbor.move(Xself, Yself) var = True return var def getNeighbors(self): neighbors = [] x = self.pos().x() y = self.pos().y() if self.parent().childAt(x-1, y): neighbors.append(self.parent().childAt(x-1, y)) if self.parent().childAt(x+41, y): neighbors.append(self.parent().childAt(x+41, y)) if self.parent().childAt(x, y-1): neighbors.append(self.parent().childAt(x, y-1)) if self.parent().childAt(x, y+41): neighbors.append(self.parent().childAt(x, y+41)) return neighbors
def __init__(self, size, parent=None): super(PadNavigator, self).__init__(parent) self.form = Ui_Form() splash = SplashItem() splash.setZValue(1) pad = FlippablePad(size) flipRotation = QGraphicsRotation(pad) xRotation = QGraphicsRotation(pad) yRotation = QGraphicsRotation(pad) flipRotation.setAxis(Qt.YAxis) xRotation.setAxis(Qt.YAxis) yRotation.setAxis(Qt.XAxis) pad.setTransformations([flipRotation, xRotation, yRotation]) backItem = QGraphicsProxyWidget(pad) widget = QWidget() self.form.setupUi(widget) self.form.hostName.setFocus() backItem.setWidget(widget) backItem.setVisible(False) backItem.setFocus() backItem.setCacheMode(QGraphicsItem.ItemCoordinateCache) r = backItem.rect() backItem.setTransform(QTransform().rotate(180, Qt.YAxis).translate(-r.width()/2, -r.height()/2)) selectionItem = RoundRectItem(QRectF(-60, -60, 120, 120), QColor(Qt.gray), pad) selectionItem.setZValue(0.5) smoothSplashMove = QPropertyAnimation(splash, b'y') smoothSplashOpacity = QPropertyAnimation(splash, b'opacity') smoothSplashMove.setEasingCurve(QEasingCurve.InQuad) smoothSplashMove.setDuration(250) smoothSplashOpacity.setDuration(250) smoothXSelection = QPropertyAnimation(selectionItem, b'x') smoothYSelection = QPropertyAnimation(selectionItem, b'y') smoothXRotation = QPropertyAnimation(xRotation, b'angle') smoothYRotation = QPropertyAnimation(yRotation, b'angle') smoothXSelection.setDuration(125) smoothYSelection.setDuration(125) smoothXRotation.setDuration(125) smoothYRotation.setDuration(125) smoothXSelection.setEasingCurve(QEasingCurve.InOutQuad) smoothYSelection.setEasingCurve(QEasingCurve.InOutQuad) smoothXRotation.setEasingCurve(QEasingCurve.InOutQuad) smoothYRotation.setEasingCurve(QEasingCurve.InOutQuad) smoothFlipRotation = QPropertyAnimation(flipRotation, b'angle') smoothFlipScale = QPropertyAnimation(pad, b'scale') smoothFlipXRotation = QPropertyAnimation(xRotation, b'angle') smoothFlipYRotation = QPropertyAnimation(yRotation, b'angle') flipAnimation = QParallelAnimationGroup(self) smoothFlipScale.setDuration(500) smoothFlipRotation.setDuration(500) smoothFlipXRotation.setDuration(500) smoothFlipYRotation.setDuration(500) smoothFlipScale.setEasingCurve(QEasingCurve.InOutQuad) smoothFlipRotation.setEasingCurve(QEasingCurve.InOutQuad) smoothFlipXRotation.setEasingCurve(QEasingCurve.InOutQuad) smoothFlipYRotation.setEasingCurve(QEasingCurve.InOutQuad) smoothFlipScale.setKeyValueAt(0, 1.0) smoothFlipScale.setKeyValueAt(0.5, 0.7) smoothFlipScale.setKeyValueAt(1, 1.0) flipAnimation.addAnimation(smoothFlipRotation) flipAnimation.addAnimation(smoothFlipScale) flipAnimation.addAnimation(smoothFlipXRotation) flipAnimation.addAnimation(smoothFlipYRotation) setVariablesSequence = QSequentialAnimationGroup() setFillAnimation = QPropertyAnimation(pad, b'fill') setBackItemVisibleAnimation = QPropertyAnimation(backItem, b'visible') setSelectionItemVisibleAnimation = QPropertyAnimation(selectionItem, b'visible') setFillAnimation.setDuration(0) setBackItemVisibleAnimation.setDuration(0) setSelectionItemVisibleAnimation.setDuration(0) setVariablesSequence.addPause(250) setVariablesSequence.addAnimation(setBackItemVisibleAnimation) setVariablesSequence.addAnimation(setSelectionItemVisibleAnimation) setVariablesSequence.addAnimation(setFillAnimation) flipAnimation.addAnimation(setVariablesSequence) stateMachine = QStateMachine(self) splashState = QState(stateMachine) frontState = QState(stateMachine) historyState = QHistoryState(frontState) backState = QState(stateMachine) frontState.assignProperty(pad, "fill", False) frontState.assignProperty(splash, "opacity", 0.0) frontState.assignProperty(backItem, "visible", False) frontState.assignProperty(flipRotation, "angle", 0.0) frontState.assignProperty(selectionItem, "visible", True) backState.assignProperty(pad, "fill", True) backState.assignProperty(backItem, "visible", True) backState.assignProperty(xRotation, "angle", 0.0) backState.assignProperty(yRotation, "angle", 0.0) backState.assignProperty(flipRotation, "angle", 180.0) backState.assignProperty(selectionItem, "visible", False) stateMachine.addDefaultAnimation(smoothXRotation) stateMachine.addDefaultAnimation(smoothYRotation) stateMachine.addDefaultAnimation(smoothXSelection) stateMachine.addDefaultAnimation(smoothYSelection) stateMachine.setInitialState(splashState) anyKeyTransition = QEventTransition(self, QEvent.KeyPress, splashState) anyKeyTransition.setTargetState(frontState) anyKeyTransition.addAnimation(smoothSplashMove) anyKeyTransition.addAnimation(smoothSplashOpacity) enterTransition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Enter, backState) returnTransition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Return, backState) backEnterTransition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Enter, frontState) backReturnTransition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Return, frontState) enterTransition.setTargetState(historyState) returnTransition.setTargetState(historyState) backEnterTransition.setTargetState(backState) backReturnTransition.setTargetState(backState) enterTransition.addAnimation(flipAnimation) returnTransition.addAnimation(flipAnimation) backEnterTransition.addAnimation(flipAnimation) backReturnTransition.addAnimation(flipAnimation) columns = size.width() rows = size.height() stateGrid = [] for y in range(rows): stateGrid.append([QState(frontState) for _ in range(columns)]) frontState.setInitialState(stateGrid[0][0]) selectionItem.setPos(pad.iconAt(0, 0).pos()) for y in range(rows): for x in range(columns): state = stateGrid[y][x] rightTransition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Right, state) leftTransition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Left, state) downTransition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Down, state) upTransition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Up, state) rightTransition.setTargetState(stateGrid[y][(x + 1) % columns]) leftTransition.setTargetState(stateGrid[y][((x - 1) + columns) % columns]) downTransition.setTargetState(stateGrid[(y + 1) % rows][x]) upTransition.setTargetState(stateGrid[((y - 1) + rows) % rows][x]) icon = pad.iconAt(x, y) state.assignProperty(xRotation, "angle", -icon.x() / 6.0) state.assignProperty(yRotation, "angle", icon.y() / 6.0) state.assignProperty(selectionItem, "x", icon.x()) state.assignProperty(selectionItem, "y", icon.y()) frontState.assignProperty(icon, "visible", True) backState.assignProperty(icon, "visible", False) setIconVisibleAnimation = QPropertyAnimation(icon, b'visible') setIconVisibleAnimation.setDuration(0) setVariablesSequence.addAnimation(setIconVisibleAnimation) scene = QGraphicsScene(self) scene.setBackgroundBrush(QBrush(QPixmap(":/images/blue_angle_swirl.jpg"))) scene.setItemIndexMethod(QGraphicsScene.NoIndex) scene.addItem(pad) scene.setSceneRect(scene.itemsBoundingRect()) self.setScene(scene) sbr = splash.boundingRect() splash.setPos(-sbr.width() / 2, scene.sceneRect().top() - 2) frontState.assignProperty(splash, "y", splash.y() - 100.0) scene.addItem(splash) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setMinimumSize(50, 50) self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate) self.setCacheMode(QGraphicsView.CacheBackground) self.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform | QPainter.TextAntialiasing) if QGLFormat.hasOpenGL(): self.setViewport(QGLWidget(QGLFormat(QGL.SampleBuffers))) stateMachine.start()
class MainWindow(QMainWindow,Form): def initRandomTable(self): self.random_board=board.Board().getTable() self.Tiles=[[self.Tile_1, self.Tile_2, self.Tile_3], [self.Tile_4, self.Tile_5, self.Tile_6], [self.Tile_7, self.Tile_8, self.Tile_9]] for i in range(0,3): for j in range(0,3): if self.random_board[i][j]!=0: self.Tiles[i][j].setText( (str)(self.random_board[i][j]) ) else: self.Tiles[i][j].setText( '0' ) self.Tiles[i][j].setVisible(False) def changeTable(self,next_board,index): self.setupUi(self) self.Tiles=[[self.Tile_1, self.Tile_2, self.Tile_3], [self.Tile_4, self.Tile_5, self.Tile_6], [self.Tile_7, self.Tile_8, self.Tile_9]] self.solve_button.clicked.connect(self.solve) self.pushButton_next.clicked.connect(self.nextState) self.pushButton_prev.clicked.connect(self.prevState) self.lineEdit_Start.setInputMask("9 9 9 9 9 9 9 9 9") self.lineEdit_Goal.setInputMask("9 9 9 9 9 9 9 9 9") self.comboBox_Method.setCurrentIndex(index) def stringBoard(b): s="" for i in b: for j in i: s+=str(j) return s self.lineEdit_Start.setText(stringBoard(next_board)) self.pushButton_next.setEnabled(False) self.pushButton_prev.setEnabled(False) for i in range(0,3): for j in range(0,3): if next_board[i][j]!=0: self.Tiles[i][j].setVisible(True) self.Tiles[i][j].setText( (str)(next_board[i][j]) ) else: self.Tiles[i][j].setText( '0' ) self.Tiles[i][j].setVisible(False) def __init__(self): super(MainWindow,self).__init__() self.setupUi(self) self.initRandomTable() self.solve_button.clicked.connect(self.solve) self.pushButton_next.clicked.connect(self.nextState) self.pushButton_prev.clicked.connect(self.prevState) self.lineEdit_Start.setInputMask("9 9 9 9 9 9 9 9 9") self.lineEdit_Goal.setInputMask("9 9 9 9 9 9 9 9 9") self.pushButton_next.setEnabled(False) self.pushButton_prev.setEnabled(False) def stringBoard(b): s="" for i in b: for j in i: s+=str(j) return s self.lineEdit_Start.setText(stringBoard(self.random_board)) self.lineEdit_Level.setInputMask("90") def moveTile(self,destination,location,direction,isNext): def swapPositionsInTileList(tlist,pos1, pos2): tlist[pos1[0]][pos1[1]], tlist[pos2[0]][pos2[1]] = tlist[pos2[0]][pos2[1]], tlist[pos1[0]][pos1[1]] return tlist i=location[0] j=location[1] self.animation=QPropertyAnimation(self.Tiles[i][j],b"geometry") self.animation.setDuration(100) self.animation.setStartValue(QRect( self.Tiles[i][j].x(), self.Tiles[i][j].y(), self.Tiles[i][j].width(), self.Tiles[i][j].height() )) self.animation.setEndValue(QRect( self.Tiles[i][j].x()+direction[1]*self.Tiles[i][j].width(), self.Tiles[i][j].y()+direction[0]*self.Tiles[i][j].height(), self.Tiles[i][j].width(), self.Tiles[i][j].height() )) self.animation.start() a=destination[0] b=destination[1] self.animation2=QPropertyAnimation(self.Tiles[a][b],b"geometry") self.animation2.setDuration(100) self.animation2.setStartValue(QRect( self.Tiles[a][b].x(), self.Tiles[a][b].y(), self.Tiles[a][b].width(), self.Tiles[a][b].height() )) self.animation2.setEndValue(QRect( self.Tiles[a][b].x()-direction[1]*self.Tiles[a][b].width(), self.Tiles[a][b].y()-direction[0]*self.Tiles[a][b].height(), self.Tiles[a][b].width(), self.Tiles[a][b].height() )) self.animation2.start() self.currentState+=isNext swapPositionsInTileList(self.Tiles,destination,location) def solve(self): if(self.lineEdit_Goal.hasAcceptableInput() and self.lineEdit_Start.hasAcceptableInput() and self.lineEdit_Level.hasAcceptableInput()): #correct input user def stringToTuple(string): a=[] string=list(string) for i in string: if i != ' ': a.append(int(i)) return tuple(a) start = board.Board(stringToTuple(self.lineEdit_Start.text())) goal = board.Board(stringToTuple(self.lineEdit_Goal.text())) # self.Tiles=list(self.tempTiles) # print(start) # print(goal) if(self.comboBox_Method.currentIndex()==0): #dfsTraverse : dfs=traverse.DFSTraverseClass() self.sol=traverse.DFSTraverseClass.DFSTraverse(dfs,start,goal,(int)(self.lineEdit_Level.text())) a=([i.getTable() for i in self.sol]) a.insert(0,start.getTable()) self.sol=tuple(a) elif(self.comboBox_Method.currentIndex()==1): #bfsTraverse : self.sol=traverse.BFSTraverse(start,goal,(int)(self.lineEdit_Level.text())) b=[i.getTable() for i in self.sol] b.reverse() b.insert(0,start.getTable()) self.sol=tuple(b) if self.sol.__len__()>1: self.changeTable(start.getTable(),self.comboBox_Method.currentIndex()) self.currentState = 0 self.pushButton_next.setEnabled(True) self.pushButton_prev.setEnabled(True) else: d = QDialog(self) d.setFixedSize(300,100) b1 = QPushButton("ok",d) b1.move(math.floor(d.width()/2)-math.floor(b1.width()/3),math.floor(d.height()/2)) d.setWindowTitle("Dialog") l1 = QLabel("Please enter valid inputs ...",d) l1.move(math.floor(d.width()/2)-math.floor(l1.width()),math.floor(d.height()/3)) b1.clicked.connect(lambda : d.close()) d.setWindowModality(Qt.ApplicationModal) d.exec_() def nextState(self): def findZeroLocation(b): for i in range(0,3): for j in range(0,3): if b[i][j] == 0: return (i,j) if 0 <= self.currentState < self.sol.__len__()-1: a=findZeroLocation(self.sol[self.currentState]) b=findZeroLocation(self.sol[self.currentState+1]) def findMovementDirection(a,b): direction = (a[0]-b[0] , a[1]-b[1]) return direction self.moveTile(a,b,findMovementDirection(a,b),1) def prevState(self): def findZeroLocation(b): for i in range(0,3): for j in range(0,3): if b[i][j] == 0: return (i,j) if 1 <= self.currentState < self.sol.__len__(): a=findZeroLocation(self.sol[self.currentState]) b=findZeroLocation(self.sol[self.currentState-1]) def findMovementDirection(a,b): direction = (a[0]-b[0] , a[1]-b[1]) return direction self.moveTile(a,b,findMovementDirection(a,b),-1)