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 Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.resize(600, 600) self.btn = QPushButton('Bigger', self) self.btn.setObjectName('btn') self.btn.resize(100, 100) self.animation = QPropertyAnimation(self) # 指定动画作用的对象 self.animation.setTargetObject(self.btn) # b'size' 改变大小 b'pos' 改变位置 self.animation.setPropertyName(b'pos') # 设置动画持续时长 self.animation.setDuration(1000) # 设置位置关键帧 self.animation.setKeyValueAt(0, self.btn.pos() + QPoint(0, 0)) self.animation.setKeyValueAt(0.2, self.btn.pos() + QPoint(0, 15)) self.animation.setKeyValueAt(0.4, self.btn.pos() + QPoint(0, 0)) self.animation.setKeyValueAt(0.6, self.btn.pos() + QPoint(0, -15)) self.animation.setKeyValueAt(0.8, self.btn.pos() + QPoint(0, 0)) # self.animation.setKeyValueAt(1, self.btn.pos() + QPoint(0, 0)) # 设置动画循环次数 self.animation.setLoopCount(3) QMetaObject.connectSlotsByName(self) @pyqtSlot() def on_btn_clicked(self): # 启动动画 self.animation.start()
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 AnimationShadowEffect(QGraphicsDropShadowEffect): def __init__(self, color, *args, **kwargs): super(AnimationShadowEffect, self).__init__(*args, **kwargs) self.setColor(color) self.setOffset(0, 0) self.setBlurRadius(0) self._radius = 0 self.animation = QPropertyAnimation(self) self.animation.setTargetObject(self) self.animation.setDuration(2000) # 一次循环时间 self.animation.setLoopCount(-1) # 永久循环 self.animation.setPropertyName(b'radius') # 插入值 self.animation.setKeyValueAt(0, 1) self.animation.setKeyValueAt(0.5, 30) self.animation.setKeyValueAt(1, 1) def start(self): self.animation.start() def stop(self, r=0): # 停止动画并修改半径值 self.animation.stop() self.radius = r @pyqtProperty(int) def radius(self): return self._radius @radius.setter def radius(self, r): self._radius = r self.setBlurRadius(r)
def load_animation2(window: object, property_name: bytes = b'windowOpacity') -> None: animation = QPropertyAnimation() animation.setTargetObject(window) # 设置动画目标对象 # 设置动画属性 # 注意:字节类型 # pos---位置动画---QPoint # size---大小动画---QSize # geometry----位置+大小动画----QRect # windowOpacity---窗口的透明度(0.0是透明的 1.0是不透明)---好像只适合顶层窗口 animation.setPropertyName(property_name) # animation.setStartValue(QPoint(0, 0)) # 设置开始位置---按钮的左上角位置 # animation.setEndValue(QPoint(300, 300)) # 设置结束位置 # animation.setStartValue(QSize(0, 0)) # 设置开始大小 # animation.setEndValue(QSize(300, 300)) # 设置结束大小 # animation.setStartValue(QRect(0, 0,100,100)) # 设置开始位置和大小 # animation.setEndValue(QRect(100,100,300, 300)) # 设置结束位置和大小 # 参数1 0.0到1.0 0.0表示开始点,1.0表示结束点 # 在动画的中间插入透明度0.2 animation.setStartValue(0.2) # 设置开始不透明 animation.setKeyValueAt(0.2, 0.4) # 在动画的某时间点插入一个值 animation.setKeyValueAt(0.4, 0.6) animation.setKeyValueAt(1, 1) # 在动画的结束点是不透明的 # 0透明, 1不透明 # animation.setEndValue(1) # 设置结束透明度 animation.setEndValue(TRANSPARENT) # 设置结束透明度 animation.setDirection(3000) # 设置动画单次时长---单位毫秒 animation.setEasingCurve(QEasingCurve.InQuad) # 设置动画的节奏 animation.start() # 动画开始---非阻塞
def splash_open_init(pane): animation = QPropertyAnimation(pane) animation.setTargetObject(pane) animation.setPropertyName(b"pos") animation.setStartValue(pane.pos()) animation.setEndValue(QPoint(0, 0)) animation.setEasingCurve(QEasingCurve.OutBounce) animation.setDuration(500) animation.start(QAbstractAnimation.DeleteWhenStopped)
def splash_exit_init(pane, pane1): animation = QPropertyAnimation(pane) animation.setTargetObject(pane) animation.setPropertyName(b"pos") animation.setEndValue(QPoint(0, pane1.width())) animation.setStartValue(QPoint(0, 0)) animation.setEasingCurve(QEasingCurve.InBounce) animation.setDuration(500) animation.start(QAbstractAnimation.DeleteWhenStopped)
def moveSlider(self): animation = QPropertyAnimation(self) animation.setTargetObject(self.line_label) animation.setPropertyName(b"geometry") animation.setStartValue(self.line_label.geometry()) geo = self.sender().geometry() animation.setEndValue(QRect(geo.x(), 76, geo.width(), 4)) animation.setDuration(200) animation.start()
class SwitchPrivate(QObject): def __init__(self, q, parent=None, isOn=False): self.isOn = isOn QObject.__init__(self, parent=parent) self.mPointer = q if self.isOn: self.mPosition = 1.0 else: self.mPosition = 0.0 self.mGradient = QLinearGradient() self.mGradient.setSpread(QGradient.PadSpread) self.animation = QPropertyAnimation(self) self.animation.setTargetObject(self) self.animation.setPropertyName(b'position') self.animation.setStartValue(0.0) self.animation.setEndValue(1.0) self.animation.setDuration(200) self.animation.setEasingCurve(QEasingCurve.InOutExpo) self.animation.finished.connect(self.mPointer.update) @pyqtProperty(float) def position(self): return self.mPosition @position.setter def position(self, value): self.mPosition = value self.mPointer.update() def draw(self, painter): r = self.mPointer.rect() margin = r.height() / 10 painter.setPen(Qt.NoPen) self.background_color = QColor(118, 118, 118) painter.setBrush(self.background_color) painter.drawRoundedRect(r, r.height() / 2, r.height() / 2) self.mGradient = QColor(35, 35, 35) painter.setBrush(self.mGradient) x = r.height() / 2.0 + self.mPosition * (r.width() - r.height()) painter.drawEllipse(QPointF(x, r.height() / 2), r.height() / 2 - margin, r.height() / 2 - margin) @pyqtSlot(bool, name='animate') def animate(self, checked): self.animation.setDirection(QPropertyAnimation.Forward if checked else QPropertyAnimation.Backward) self.animation.start()
def show_error_animation(self): animation = QPropertyAnimation(self) animation.setTargetObject(self.login_widget) animation.setPropertyName(b"pos") animation.setKeyValueAt(0, self.login_widget.pos()) animation.setKeyValueAt(0.2, self.login_widget.pos() - QPoint(15, 0)) animation.setKeyValueAt(0.5, self.login_widget.pos()) animation.setKeyValueAt(0.7, self.login_widget.pos() - QPoint(-15, 0)) animation.setKeyValueAt(1, self.login_widget.pos()) animation.setDuration(200) animation.setLoopCount(5) animation.start(QAbstractAnimation.DeleteWhenStopped)
def createScene(): # Root entity rootEntity = QEntity() # Material material = QPhongMaterial(rootEntity) # Torus torusEntity = QEntity(rootEntity) # Qt3DExtras.QTorusMesh * torusMesh = QTorusMesh() torusMesh.setRadius(5) torusMesh.setMinorRadius(1) torusMesh.setRings(100) torusMesh.setSlices(20) # Qt3DCore.QTransform * torusTransform = QTransform() torusTransform.setScale3D(QVector3D(1.5, 1, 0.5)) torusTransform.setRotation( QQuaternion.fromAxisAndAngle(QVector3D(1, 0, 0), 45.0)) torusEntity.addComponent(torusMesh) torusEntity.addComponent(torusTransform) torusEntity.addComponent(material) # Sphere sphereEntity = QEntity(rootEntity) sphereMesh = QSphereMesh() sphereMesh.setRadius(3) # Qt3DCore.QTransform * sphereTransform = QTransform() # OrbitTransformController * controller = OrbitTransformController(sphereTransform) controller.setTarget(sphereTransform) controller.setRadius(20.0) # QPropertyAnimation * sphereRotateTransformAnimation = QPropertyAnimation(sphereTransform) sphereRotateTransformAnimation.setTargetObject(controller) sphereRotateTransformAnimation.setPropertyName(b"angle") sphereRotateTransformAnimation.setStartValue(0) sphereRotateTransformAnimation.setEndValue(360) sphereRotateTransformAnimation.setDuration(10000) sphereRotateTransformAnimation.setLoopCount(-1) sphereRotateTransformAnimation.start() sphereEntity.addComponent(sphereMesh) sphereEntity.addComponent(sphereTransform) sphereEntity.addComponent(material) return rootEntity
def set_shadow_animation(self, start_val=5, end_val=20, duration=300, activate=True): if getattr(self, "shadow_animation", None): # print("已存在animation") pass else: shadow_animation = QPropertyAnimation(self) shadow_animation.setTargetObject(self.shadow_effect) shadow_animation.setPropertyName(b"blurRadius") shadow_animation.setStartValue(start_val) shadow_animation.setEndValue(end_val) shadow_animation.setDuration(duration) self.shadow_animation = shadow_animation if not activate: self.shadow_animation.setDuration(1)
def show_hide_menu(self, checked): animation_group = QSequentialAnimationGroup(self) for idx, target in enumerate(self.animation_target): animation = QPropertyAnimation() animation.setTargetObject(target) animation.setPropertyName(b"pos") animation.setEndValue(self.pushButton.pos()) animation.setStartValue(self.animation_target_pos[idx]) animation.setDuration(200) animation.setEasingCurve(QEasingCurve.InOutBounce) animation_group.addAnimation(animation) animation_group.setDirection(not checked) animation_group.start(QAbstractAnimation.DeleteWhenStopped)
def animations(self, widget, starx, stary, endx, endy, time): # 创建动画对象,并且设置目标、属性 animation = QPropertyAnimation(self) animation.setTargetObject(widget) animation.setPropertyName(b"pos") # 设置属性值:开始 插值 结束 animation.setStartValue(QPoint(starx, stary)) animation.setEndValue(QPoint(endx, endy)) # 动画时长 animation.setDuration(time) # 启动动画 animation.start()
def appear(self,int_x,int_y): #动画实现 animation=QPropertyAnimation(self) animation.setTargetObject(self) animation.setPropertyName(b"pos") animation.setStartValue(QPoint(int_x+95,int_y+8)) animation.setEndValue(QPoint(int_x+95,int_y-self.geometry().height()+8)) animation.setDuration(550) animation.setEasingCurve(QEasingCurve.OutBounce) animation.start() #准备结束处理 self.timer.start() self.bool_hidden=False #设置优先显示 self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool) self.show()
def fade(self): #动画实现 animation=QPropertyAnimation(self) animation.setTargetObject(self) animation.setPropertyName(b"pos") animation.setStartValue(QPoint(self.pos().x(),self.pos().y())) animation.setEndValue(QPoint(self.pos().x(),self.pos().y()+self.geometry().height())) animation.setDuration(550) animation.setEasingCurve(QEasingCurve.OutBounce) animation.start() #准备后事 self.timer.start() self.bool_hidden=True #设置优先显示 self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool) self.show()
class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.resize(600, 600) self.btn = QPushButton('Bigger', self) self.btn.resize(100, 100) self.animation = QPropertyAnimation(self) # 1 # 指定动画作用的对象 self.animation.setTargetObject(self.btn) # b'size' 改变大小 b'pos' 改变位置 self.animation.setPropertyName(b'size') self.animation.setDuration(6000) # 2 self.animation.setStartValue(QSize(100, 100)) # 3 self.animation.setEndValue(QSize(600, 600)) # 4 self.animation.start() # 5
def load_animation(window: object, property_name: bytes = b'windowOpacity') -> None: animation = QPropertyAnimation() animation.setTargetObject(window) # 设置动画目标对象 # 设置动画属性 # 注意:字节类型 # pos---位置动画---QPoint # size---大小动画---QSize # geometry----位置+大小动画----QRect # windowOpacity---窗口的透明度(0.0是透明的 1.0是不透明)---好像只适合顶层窗口 animation.setPropertyName(property_name) animation.setEndValue(TRANSPARENT) # 设置结束透明度 animation.setEasingCurve(QEasingCurve.InQuad) # 设置动画的节奏 animation.start() # 动画开始---非阻塞
class ParentWin(QWidget): def __init__(self, parent=None, *args, **kwargs) -> None: super().__init__(parent, *args, **kwargs) self.setStyleSheet("QWidget{background-color: yellow}") self.resize(500, 500) self.move(200, 200) self.vbox = QVBoxLayout() self.btn1 = QPushButton("切换窗口") self.btn1.setObjectName('btn1') self.vbox.addWidget(self.btn1) self.setLayout(self.vbox) self.childwin = None QMetaObject.connectSlotsByName(self) @pyqtSlot() def on_btn1_clicked(self): print('切换窗口') self.childwin = ChildWin(self) # todo 设置窗口动画 self.animation = QPropertyAnimation(self) # 指定动画作用的对象 self.animation.setTargetObject(self.childwin) # b'size' 改变大小 b'pos' 改变位置 self.animation.setPropertyName(b'pos') # 设置动画持续时长 self.animation.setDuration(1000) # 设置位置关键帧 init_pos = self.childwin.pos() self.animation.setKeyValueAt(0, init_pos + QPoint(0, 0)) self.animation.setKeyValueAt(0.2, init_pos + QPoint(-200, -200)) self.animation.setKeyValueAt(0.4, init_pos + QPoint(-300, -300)) self.animation.setKeyValueAt(0.6, init_pos + QPoint(-400, -400)) self.animation.setKeyValueAt(0.8, init_pos + QPoint(-450, -450)) self.animation.setKeyValueAt(1, init_pos + QPoint(-500, -500)) # 设置动画速率曲线 缓动效果 self.animation.setEasingCurve(QEasingCurve.OutBounce) # 设置动画循环次数 # self.animation.setLoopCount(3) self.childwin.show() self.animation.start(QAbstractAnimation.DeleteWhenStopped)
class ChildWin(QWidget): def __init__(self, parent=None, *args, **kwargs) -> None: super().__init__(parent, *args, **kwargs) self.setStyleSheet("QWidget{background-color: green}") self.setAttribute(Qt.WA_StyledBackground, True) self.resize(500, 500) # 子窗口出现的位置 是相对于父窗口而言的 self.move(500, 500) self.vbox = QVBoxLayout() self.btn1 = QPushButton("提交") self.btn1.setObjectName('btn1') self.vbox.addWidget(self.btn1) self.setLayout(self.vbox) QMetaObject.connectSlotsByName(self) @pyqtSlot() def on_btn1_clicked(self): print(222) # todo 设置窗口动画 self.animation = QPropertyAnimation(self) # 指定动画作用的对象 self.animation.setTargetObject(self) # b'size' 改变大小 b'pos' 改变位置 self.animation.setPropertyName(b'pos') # 设置动画持续时长 self.animation.setDuration(1000) # 设置位置关键帧 init_pos = self.pos() self.animation.setKeyValueAt(0, init_pos + QPoint(0, 0)) self.animation.setKeyValueAt(0.2, init_pos + QPoint(-200, -200)) self.animation.setKeyValueAt(0.4, init_pos + QPoint(-300, -300)) self.animation.setKeyValueAt(0.6, init_pos + QPoint(-400, -400)) self.animation.setKeyValueAt(0.8, init_pos + QPoint(-450, -450)) self.animation.setKeyValueAt(1, init_pos + QPoint(-500, -500)) # 动画倒放 # self.animation.setDirection(QAbstractAnimation.Backward) # 设置动画速率曲线 缓动效果 self.animation.setEasingCurve(QEasingCurve.OutBounce) # 设置动画循环次数 # self.animation.setLoopCount(3) self.animation.start(QAbstractAnimation.DeleteWhenStopped)
def menu(self): """ 展示动画效果 :return: """ animation_group = QSequentialAnimationGroup(self.widget) for idx, target in enumerate(self.animation_targets): animation = QPropertyAnimation() animation.setTargetObject(target) animation.setPropertyName(b"pos") animation.setStartValue(self.menu_btn.pos()) animation.setEndValue(self.animation_targets_pos[idx]) animation.setDuration(self.duration) animation.setEasingCurve(QEasingCurve.InOutBounce) animation_group.addAnimation(animation) animation_group.setDirection(self.checked) animation_group.start(QAbstractAnimation.DeleteWhenStopped)
def table_toggle(self, table_idx): dt = self.datatables[table_idx] def resize_layout(): dt.setMaximumHeight(0) if self.expand[table_idx] == True: animation = QPropertyAnimation(self) animation.setPropertyName(QByteArray().append('size')) animation.setTargetObject(dt) animation.setDuration(250) ogWidth = dt.width() animation.setStartValue(QSize(ogWidth, 310)) animation.setEndValue(QSize(ogWidth, 0)) animation.finished.connect(resize_layout) animation.start() self.labels[table_idx].setText('+') self.a.updateGeometry() self.a.setMinimumHeight(self.a.minimumHeight() - 320) else: animation = QPropertyAnimation(self) animation.setPropertyName(QByteArray().append('size')) animation.setTargetObject(dt) animation.setDuration(250) dt.setMaximumHeight(310) ogWidth = dt.width() animation.setStartValue(QSize(ogWidth, 0)) animation.setEndValue(QSize(ogWidth, 310)) animation.start() self.labels[table_idx].setText('-') self.a.updateGeometry() self.a.setMinimumHeight(self.a.minimumHeight() + 320) self.expand[table_idx] = not self.expand[table_idx]
def setup_yd(self): if self.biaotipos == 1: self.biaotipos = 2 self.kuang.hide() self.edit2.hide() self.pushButton_18.hide() self.edit.hide() self.edit22.show() self.yuan2.show() self.pushButton_19.show() self.edit_suiji_size.show() self.yuan3.show() self.edit_suiji_num.show() animation = QPropertyAnimation(self) animation.setTargetObject(self.biaoti_one) animation.setPropertyName(b"pos") animation.setStartValue(QPoint(375, 75)) animation.setEndValue((QPoint(615, 75))) animation.setDuration(300) animation.start()
class RotatingTrefoilKnot(TrefoilKnot): def __init__(self, parent=None): super(RotatingTrefoilKnot, self).__init__(parent) self.m_thetaAnimation = QPropertyAnimation() self.m_thetaAnimation.setDuration(2000) self.m_thetaAnimation.setStartValue(0.0) self.m_thetaAnimation.setEndValue(360.0) self.m_thetaAnimation.setLoopCount(-1) self.m_thetaAnimation.setTargetObject(self) self.m_thetaAnimation.setPropertyName(b'phi') self.m_thetaAnimation.start() self.m_phiAnimation = QPropertyAnimation() self.m_phiAnimation.setDuration(2000) self.m_phiAnimation.setStartValue(0.0) self.m_phiAnimation.setEndValue(360.0) self.m_phiAnimation.setLoopCount(-1) self.m_phiAnimation.setTargetObject(self) self.m_phiAnimation.setPropertyName(b'theta') self.m_phiAnimation.start()
def options(animation: QPropertyAnimation, property_name: bytes = b'windowOpacity') -> None: """ 废弃 更改透明度 :param animation: :param property_name: :return: """ # 创建对象 # 设置动画属性 # 注意:字节类型 # pos---位置动画---QPoint # size---大小动画---QSize # geometry----位置+大小动画----QRect # windowOpacity---窗口的透明度(0.0是透明的 1.0是不透明)---好像只适合顶层窗口 animation.setPropertyName(property_name) # animation.setStartValue(QPoint(0, 0)) # 设置开始位置---按钮的左上角位置 # animation.setEndValue(QPoint(300, 300)) # 设置结束位置 # animation.setStartValue(QSize(0, 0)) # 设置开始大小 # animation.setEndValue(QSize(300, 300)) # 设置结束大小 # animation.setStartValue(QRect(0, 0,100,100)) # 设置开始位置和大小 # animation.setEndValue(QRect(100,100,300, 300)) # 设置结束位置和大小 # 参数1 0.0到1.0 0.0表示开始点,1.0表示结束点 # 在动画的中间插入透明度0.2 # animation.setStartValue(0.2) # 设置开始不透明 # animation.setKeyValueAt(0.2, 0.4) # 在动画的某时间点插入一个值 # animation.setKeyValueAt(0.4, 0.6) # animation.setKeyValueAt(1, 1) # 在动画的结束点是不透明的 # 0透明, 1不透明 # animation.setEndValue(1) # 设置结束透明度 animation.setEndValue(settings.TRANSPARENT) # 设置结束透明度 # animation.setDirection(3000) # 设置动画单次时长---单位毫秒 # animation.setEasingCurve(QEasingCurve.InQuad) # 设置动画的节奏 animation.start() # 动画开始---非阻塞
def initUi(self): self.getSystemSettings() self.setWindowTitle('我的第一个Qt程序') self.resize(500, 300) # 把label放到父类里 self就是父类 即主窗口 LoginWindow # label = QLabel() # label.setParent(self) label = QLabel(self) label.setText("社会我杰哥,人狠话不多") label.move(100, 100) self.le = QLineEdit(self) self.le.setText(self.settings.value("username")) # 透明效果 animation = QPropertyAnimation(self) animation.setTargetObject(self) animation.setPropertyName(b"windowOpacity") animation.setStartValue(1) animation.setKeyValueAt(0.5, 0) animation.setEndValue(1) animation.setLoopCount(2) animation.setDuration(2000) animation.start(QAbstractAnimation.DeleteWhenStopped)
def push_message(self, text, type='Info'): self.setText(f' {text} ') self.setObjectName(type) self.setScaledContents(True) ds_effect = QGraphicsDropShadowEffect() ds_effect.setOffset(5, 5) ds_effect.setColor(Qt.gray) ds_effect.setBlurRadius(10) self.setGraphicsEffect(ds_effect) self.show() animation = QPropertyAnimation(self.parent()) animation.setTargetObject(self) animation.setPropertyName(b'pos') animation.setDuration(500) if self.parent(): if not hasattr(self.parent(), 'off_y'): setattr(self.parent(), 'off_y', 0) if not hasattr(self.parent(), 'number'): setattr(self.parent(), 'number', 0) off_y = self.parent().off_y p_width = self.parent().width() start = QPoint(p_width, self._margin + off_y) end = QPoint(p_width - self.width() - self._margin, self._margin + off_y) timer = QTimer(self) timer.timeout.connect( lambda: self._close(timer, animation, end, start)) timer.start(self.ttl * 1000) self._show(animation, start, end) self.parent( ).off_y = self.parent().off_y + self.height() + self._margin self.parent().number += 1
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 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("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 CDrawer(QWidget): LEFT, TOP, RIGHT, BOTTOM = range(4) def __init__(self, *args, stretch=1 / 4, widget=None, **kwargs): super(CDrawer, self).__init__(*args, **kwargs) self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint | Qt.Drawer | Qt.NoDropShadowWindowHint) self.setAttribute(Qt.WA_StyledBackground, True) self.setAttribute(Qt.WA_TranslucentBackground, True) self.setStretch(stretch) ## Animation self.animIn = QPropertyAnimation(self, duration=500, easingCurve=QEasingCurve.OutCubic) self.animIn.setPropertyName(b'pos') self.animOut = QPropertyAnimation(self, duration=500, finished=self.onAnimOutEnd, easingCurve=QEasingCurve.OutCubic) self.animOut.setPropertyName(b'pos') self.animFadeIn = QPropertyAnimation(self, duration=400, easingCurve=QEasingCurve.Linear) self.animFadeIn.setPropertyName(b'opacityBG') self.animFadeOut = QPropertyAnimation(self, duration=400, easingCurve=QEasingCurve.Linear) self.animFadeOut.setPropertyName(b'opacityBG') ## Background opacity self.opacityBg = 0 self.stylesheetOpacity = '#CDrawer_alphaWidget{background:rgba(55,55,55,%i);}' self.alphaWidget = QWidget(self, objectName='CDrawer_alphaWidget') self.alphaWidget.setAttribute(Qt.WA_TransparentForMouseEvents, True) self.setWidget(widget) # 子控件 def resizeEvent(self, event): self.alphaWidget.resize(self.size()) super(CDrawer, self).resizeEvent(event) def mousePressEvent(self, event): pos = event.pos() if pos.x() >= 0 and pos.y() >= 0 and self.childAt( pos) == None and self.widget: if not self.widget.geometry().contains(pos): self.animationOut() return super(CDrawer, self).mousePressEvent(event) def show(self): super(CDrawer, self).show() parent = self.parent().window() if self.parent() else self.window() if not parent or not self.widget: return # 设置Drawer大小和主窗口一致 self.setGeometry(parent.geometry()) geometry = self.geometry() self.animationIn(geometry) def animationIn(self, geometry): """进入动画 :param geometry: """ self.widget.setGeometry(0, 0, int(geometry.width() * self.stretch), geometry.height()) self.widget.hide() self.animIn.setStartValue(QPoint(-self.widget.width(), 0)) self.animIn.setEndValue(QPoint(0, 0)) self.opacityBg = 0 self.animFadeIn.setStartValue(0) self.animFadeIn.setEndValue(100) self.animFadeIn.start() self.animIn.start() self.widget.show() def animationOut(self): """离开动画 """ self.animIn.stop() self.animFadeIn.stop() self.opacityBg = 100 self.animFadeOut.setStartValue(100) self.animFadeOut.setEndValue(0) self.animFadeOut.start() geometry = self.widget.geometry() self.animOut.setStartValue(geometry.topLeft()) self.animOut.setEndValue(QPoint(-self.widget.width(), 0)) self.animOut.start() def onAnimOutEnd(self): """离开动画结束 """ # 模拟点击外侧关闭 QApplication.sendEvent( self, QMouseEvent(QMouseEvent.MouseButtonPress, QPointF(-1, -1), Qt.LeftButton, Qt.NoButton, Qt.NoModifier)) self.close() def setWidget(self, widget): """设置子控件 :param widget: """ self.widget = widget if widget: widget.setParent(self) self.animIn.setTargetObject(widget) self.animOut.setTargetObject(widget) self.animFadeIn.setTargetObject(self) self.animFadeOut.setTargetObject(self) def setEasingCurve(self, easingCurve): """设置动画曲线 :param easingCurve: """ self.animIn.setEasingCurve(easingCurve) def getStretch(self): """获取占比 """ return self.stretch def setStretch(self, stretch): """设置占比 :param stretch: """ self.stretch = max(0.1, min(stretch, 0.9)) def getOpacityBG(self): return self.opacityBg def setOpacityBG(self, value): self.opacityBg = value self.alphaWidget.setStyleSheet(self.stylesheetOpacity % (self.opacityBg)) opacityBG = pyqtProperty(int, getOpacityBG, setOpacityBG)
class SwitchPrivate(QObject): def __init__(self, q, parent=None): QObject.__init__(self, parent=parent) self.mPointer = q self.mPosition = 0.0 self.mGradient = QLinearGradient() self.mGradient.setSpread(QGradient.PadSpread) self.animation = QPropertyAnimation(self) self.animation.setTargetObject(self) self.animation.setPropertyName(b'position') self.animation.setStartValue(0.0) self.animation.setEndValue(1.0) self.animation.setDuration(200) self.animation.setEasingCurve(QEasingCurve.InOutExpo) self.animation.finished.connect(self.mPointer.update) @pyqtProperty(float) def position(self): return self.mPosition @position.setter def position(self, value): self.mPosition = value self.mPointer.update() def draw(self, painter): r = self.mPointer.rect() margin = r.height() / 10 shadow = self.mPointer.palette().color(QPalette.Dark) light = self.mPointer.palette().color(QPalette.Light) button = self.mPointer.palette().color(QPalette.Button) painter.setPen(Qt.NoPen) self.mGradient.setColorAt(0, shadow.darker(130)) self.mGradient.setColorAt(1, light.darker(130)) self.mGradient.setStart(0, r.height()) self.mGradient.setFinalStop(0, 0) painter.setBrush(self.mGradient) painter.drawRoundedRect(r, r.height() / 2, r.height() / 2) self.mGradient.setColorAt(0, shadow.darker(140)) self.mGradient.setColorAt(1, light.darker(160)) self.mGradient.setStart(0, 0) self.mGradient.setFinalStop(0, r.height()) painter.setBrush(self.mGradient) painter.drawRoundedRect(r.adjusted(margin, margin, -margin, -margin), r.height() / 2, r.height() / 2) self.mGradient.setColorAt(0, button.darker(130)) self.mGradient.setColorAt(1, button) painter.setBrush(self.mGradient) x = r.height() / 2.0 + self.mPosition * (r.width() - r.height()) painter.drawEllipse(QPointF(x, r.height() / 2), r.height() / 2 - margin, r.height() / 2 - margin) @pyqtSlot(bool, name='animate') def animate(self, checked): self.animation.setDirection(QPropertyAnimation.Forward if checked else QPropertyAnimation.Backward) self.animation.start()
class PopupWidget(QtWidgets.QWidget): # enum PointerPosition LeftSide = 0 RightSide = 1 TopSide = 2 BottomSide = 3 NoPointer = 4 LR_MARGIN = 10.0 #8.0 #/* left / right margin */ TB_MARGIN = 8.0 #5.5 #/* top / bottom margin */ didHide = pyqtSignal() didShow = pyqtSignal() onClick = pyqtSignal() onRightClick = pyqtSignal() def __init__(self, parent=None, timeout=None, delete_on_hide=True, activation_hides=True, dark_mode=False): ''' parent should be a window or None timeout is the amount of time, in milliseconds, to show the widget before it is auto-hidden. None is no timeout. delete_on_hide, if True, will auto-delete this widget after it is hidden due to the timeout or due to calling hide(). ''' super().__init__(parent) self.layout = QtWidgets.QGridLayout(self) if sys.platform != 'darwin': self.layout.setContentsMargins(20, 20, 20, 20) self.animation = QPropertyAnimation(self) self.final_opacity = 1.0 self.popup_opacity = 1.0 self.pointerPos = self.LeftSide self._timer = None self.activation_hides = activation_hides self.dark_mode = dark_mode and sys.platform.lower() != "darwin" #self.resize(200, 50) self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool) self.setAttribute(Qt.WA_TranslucentBackground) self.setAttribute(Qt.WA_ShowWithoutActivating) self.animation.setTargetObject(self) self.animation.setPropertyName(b'popupOpacity') self.animation.setDuration(200) self.setLayout(self.layout) if parent: parent.installEventFilter(self) self.timeout = timeout self.delete_on_hide = delete_on_hide def getPointerPosition(self): return self.pointerPos def setPointerPosition(self, r): self.pointerPos = r self.update() @pyqtProperty( float ) # Property so that Qt animations work. You may set the actual attrbute directly and ingore this in client code def popupOpacity(self): return self.popup_opacity @popupOpacity.setter def popupOpacity(self, value): self.popup_opacity = value self.setWindowOpacity(value) @pyqtProperty( float ) # Property so that Qt animations work. You may set the actual attrbute directly and ingore this in client code def finalOpacity(self): return self.final_opacity @finalOpacity.setter def finalOpacity(self, value): self.final_opacity = value def paintEvent(self, e): #// Draw the popup here #// You can always pick an image and use drawPixmap to #// draw it in order to make things simpler painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.setClipRegion(e.region()) painter.fillRect(e.rect(), QColor(0, 0, 0, 0)) #// Prepare the popup dimensions roundedRectDimensions = QRectF() roundedRectDimensions.setX(self.rect().x() + self.LR_MARGIN) roundedRectDimensions.setY(self.rect().y() + self.TB_MARGIN) roundedRectDimensions.setWidth(self.rect().width() - self.LR_MARGIN * 2.0) roundedRectDimensions.setHeight(self.rect().height() - self.TB_MARGIN * 2.0) pal = QPalette(self.palette()) painter.setBrush( QBrush( pal.color( QPalette.Window if self.dark_mode else QPalette.Mid))) pen = QPen() pen.setColor( pal.color(QPalette.Light if self.dark_mode else QPalette.Button)) pen.setWidth(3) painter.setPen(pen) #// Draw the popup body painter.drawRoundedRect(roundedRectDimensions, self.LR_MARGIN * 2.0, self.TB_MARGIN * 2.0) painter.setPen(Qt.NoPen) painter.setBrush( QBrush( pal.color( QPalette.BrightText if self.dark_mode else QPalette.Dark))) #// Draw the popup pointer based on relPos self.drawPopupPointer(painter) e.accept() def drawPopupPointer(self, p): r = QRectF(self.rect()) if self.pointerPos == self.LeftSide: PPIX_X = self.LR_MARGIN PPIX_Y = PPIX_X * 2.0 points = [ QPointF(r.x() + PPIX_X, r.height() / 2.0 - PPIX_Y / 2.0), QPointF(r.x() + PPIX_X, r.height() / 2.0 + PPIX_Y / 2.0), QPointF(r.x(), r.height() / 2.0), ] p.drawPolygon(*points) if self.pointerPos == self.RightSide: PPIX_X = self.LR_MARGIN PPIX_Y = PPIX_X * 2.0 points = [ QPointF(r.right() - PPIX_X, r.height() / 2.0 - PPIX_Y / 2.0), QPointF(r.right() - PPIX_X, r.height() / 2.0 + PPIX_Y / 2.0), QPointF(r.right(), r.height() / 2.0), ] p.drawPolygon(*points) if self.pointerPos == self.TopSide: PPIX_Y = self.TB_MARGIN PPIX_X = PPIX_Y * 2.0 points = [ QPointF(r.x() + r.width() / 2.0 - PPIX_X / 2.0, r.top() + PPIX_Y), QPointF(r.x() + r.width() / 2.0 + PPIX_X / 2.0, r.top() + PPIX_Y), QPointF(r.x() + r.width() / 2.0, r.top()), ] p.drawPolygon(*points) if self.pointerPos == self.BottomSide: PPIX_Y = self.TB_MARGIN PPIX_X = PPIX_Y * 2.0 points = [ QPointF(r.x() + r.width() / 2.0 - PPIX_X / 2.0, r.bottom() - PPIX_Y), QPointF(r.x() + r.width() / 2.0 + PPIX_X / 2.0, r.bottom() - PPIX_Y), QPointF(r.x() + r.width() / 2.0, r.bottom()), ] p.drawPolygon(*points) def showRelativeTo(self, w): s = self.size() self.moveRelativeTo(w) self.hide() self.show() if self.pointerPos == self.NoPointer: self.raise_() if s != self.size(): # show caused widget resize.. recenter self.moveRelativeTo(w) def moveRelativeTo(self, w): if not w: print( "INTERNAL ERROR: PopupWidget::showRelativeTo got passed a NULL widget pointer! Ignoring.. FIXME!" ) return p = w.mapToGlobal(QPoint(0, 0)) if self.pointerPos == self.LeftSide: p.setX(p.x() + w.width()) p.setY(p.y() - self.height() // 2 + w.height() // 2) elif self.pointerPos == self.RightSide: p.setX(p.x() - self.width()) p.setY(p.y() - self.height() // 2 + w.height() // 2) elif self.pointerPos == self.BottomSide: p.setX(p.x() + w.width() // 2 - self.width() // 2) p.setY(p.y() - self.height()) elif self.pointerPos == self.TopSide: p.setX(p.x() + w.width() // 2 - self.width() // 2) p.setY(p.y() + w.height()) else: #// just center it on the widget p.setX(p.x() + w.width() // 2 - self.width() // 2) p.setY(p.y() + w.height() // 2 - self.height() // 2) if self.isVisible(): self.raise_() self.move(p) def _killTimer(self): if self._timer: self._timer.stop() self._timer.deleteLater() self._timer = None def _startTimer(self, target): self._killTimer() self._timer = QTimer(self) self._timer.setSingleShot(True) def timeout(): self._killTimer() target() self._timer.timeout.connect(timeout) self._timer.start(int(self.timeout)) def showEvent(self, e): super().showEvent(e) if not e.isAccepted(): return if self.animation.state() == QAbstractAnimation.Running: return self.setWindowOpacity(0.0) self.animation.setStartValue(0.0) self.animation.setEndValue(self.final_opacity) self.didShow.emit() self._cleanUp() self.animation.setDirection(QAbstractAnimation.Forward) self.animation.start() if isinstance(self.timeout, (float, int)) and self.timeout > 0: def autoHide(): self._cleanUp() self._startTimer(self.hideAnimated) self.animation.finished.connect(autoHide) def hideEvent(self, e): super().hideEvent(e) if e.isAccepted(): self._cleanUp() if self.delete_on_hide: self.setParent(None) self.deleteLater() def _disconnectFinished(self): try: self.animation.finished.disconnect() except: pass def hideAnimated(self): if self.animation.state() == QAbstractAnimation.Running: return self._cleanUp() self.animation.setDirection(QAbstractAnimation.Backward) self.animation.start() def doHide(): self._cleanUp() self.hide() self.didHide.emit() self.animation.finished.connect(doHide) def eventFilter(self, obj, e): evts = (QEvent.Move, QEvent.Resize, QEvent.Close, QEvent.Hide, QEvent.Show) if self.activation_hides: evts = (*evts, QEvent.WindowStateChange, QEvent.WindowDeactivate) if e.type() in evts: # if the parent window is moved or otherwise touched, make this popup go away self.hideAnimated() return False def mousePressEvent(self, e): if e.button() == Qt.LeftButton: self.onClick.emit() e.accept() elif e.button() == Qt.RightButton: self.onRightClick.emit() e.accept() def _cleanUp(self): ''' Forces animation and timer to stop. This is essential to force the object into a known consistent state ready for deletion, restart of animations, etc. ''' self._disconnectFinished() self._killTimer() self.animation.stop()