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() # 动画开始---非阻塞
class FileManager(QDialog): def __init__(self): super().__init__() self.ui = Ui_AI() self.ui.setupUi(self) self.show() self.Loop() def Loop(self): self.light = QPropertyAnimation(self.ui.HologramLight, b"geometry") self.light.setDirection(1) self.light.setLoopCount(2) self.light.setStartValue(QRect(0, 781, 481, 20)) self.light.setEndValue(QRect(0, 340, 481, 461)) self.light.start() self.light.stop() self.anim = QPropertyAnimation(self.ui.Blur2, b"geometry") self.anim.setDirection(1) self.anim.setLoopCount(100) self.anim.setStartValue(QRect(110, 240, 231, 231)) self.anim.setEndValue(QRect(130, 240, 231, 231)) self.anim.start() mixer.init() mixer.music.load('/root/PycharmProjects/AI/good.mp3') mixer.music.play()
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()
class MoreActionsMenu(QMenu): """ 更多操作圆角菜单,flag用来指示动作的类型,flag=1有四个动作,flag=0有三个动作 """ def __init__(self, parent=None, actionFlag=1): super().__init__(parent) self.actionFlag = actionFlag # 创建动画 self.animation = QPropertyAnimation(self, b'geometry') # 实例化子窗口 self.subMenu = SubMoreActionsMenu(parent=self, actionFlag=self.actionFlag) # 实例化布局 self.all_h_layout = QHBoxLayout(self) self.initWidget() self.initLayout() self.hide() def initWidget(self): """ 初始化小部件 """ self.setAttribute(Qt.WA_TranslucentBackground) self.setWindowFlags(Qt.Popup | Qt.FramelessWindowHint | Qt.NoDropShadowWindowHint) # 初始化动画 self.animation.setDirection(300) self.animation.setEasingCurve(QEasingCurve.OutQuad) # 引用子菜单的动作 self.savePlayListAct = self.subMenu.savePlayListAct self.clearPlayListAct = self.subMenu.clearPlayListAct if self.actionFlag: self.showPlayListAct = self.subMenu.showPlayListAct self.fillScreenAct = self.subMenu.fillScreenAct else: self.showSongerCover = self.subMenu.showSongerCover def initLayout(self): """ 初始化布局 """ self.all_h_layout.addWidget(self.subMenu, 0, Qt.AlignCenter) self.all_h_layout.setContentsMargins(30, 30, 30, 30) def show(self, pos): """ 重载show() """ self.move(pos) super().show()
class MNavigationDrawer(MFrame): def __init__(self, parent): super(MNavigationDrawer, self).__init__(parent) self._parent = parent self.show_animation = None self.appear_btn = None self.set_style() def set_style(self, width=200, height=None, background_color="#FFFFFF"): if not height: height = self._parent.height() self.setGeometry(-width, 0, width, height) hide_btn = QPushButton(parent=self) hide_btn.setGeometry(int(width / 2 - 18), int(height - 60), 36, 36) hide_btn.setStyleSheet("border-image:url('assests/back.svg')") hide_btn.clicked.connect(self.disappear) self.show_animation = QPropertyAnimation(self, b"pos") self.show_animation.setStartValue(QPoint(-width, 0)) self.show_animation.setEndValue(QPoint(0, 0)) self.show_animation.setDuration(200) self.show_animation.setDirection(QAbstractAnimation.Forward) self.setStyleSheet( "background-color:{};border-right:1px solid #e0e0e0".format( background_color)) def drawerEvent(self, Qevent): if Qevent <= 0: return self.move(min(Qevent - self.width(), 0), 0) def appear(self): # 如果采用 btn trigger使得menu出现,可使用该方法 if self.appear_btn is not None: self.appear_btn.hide() self.show_animation.setStartValue(self.pos()) self.show_animation.setDirection(QAbstractAnimation.Forward) self.show_animation.start() def disappear(self): if self.appear_btn is not None: self.appear_btn.show() self.show_animation.setEndValue(self.pos()) self.show_animation.setDirection(QAbstractAnimation.Backward) self.show_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()
class StackedWidgetFadeInAnimator(AbstractAnimator): m_decorator: StackedWidgetFadeInDecorator m_animation: QPropertyAnimation def __init__(self, _container, _fadeWidget): super(StackedWidgetFadeInAnimator, self).__init__(_container) self.m_decorator = StackedWidgetFadeInDecorator(_container, _fadeWidget=_fadeWidget) self.m_animation = QPropertyAnimation(self.m_decorator, b"opacity") _container.installEventFilter(self) self.m_animation.setDuration(200) self.m_decorator.hide() def finished(): self.setAnimatedStopped() if self.m_animation.direction() == QPropertyAnimation.Forward: _container.setCurrentWidget(_fadeWidget) self.m_decorator.hide() self.m_animation.finished.connect(finished) def animationDuration(self): return self.m_animation.duration() def animateForward(self): self.fadeIn() def fadeIn(self): if self.isAnimated() and self.isAnimatedForward(): return self.setAnimatedForward() self.m_decorator.grabContainer() self.m_decorator.grabFadeWidget() startOpacity = 0 finalOpacity = 1 self.m_decorator.setOpacity(startOpacity) self.m_decorator.move(0, 0) self.m_decorator.show() self.m_decorator.raise_() if self.m_animation.state() == QPropertyAnimation.Running: self.m_animation.pause() self.m_animation.setDirection(QPropertyAnimation.Backward) self.m_animation.resume() else: self.m_animation.setEasingCurve(QEasingCurve.InQuad) self.m_animation.setDirection(QPropertyAnimation.Forward) self.m_animation.setStartValue(startOpacity) self.m_animation.setEndValue(finalOpacity) self.m_animation.start() def eventFilter(self, _object, _event): if _object == self.fadeWidget() and _event.type() == QEvent.Resize and self.m_decorator.isVisible(): self.m_decorator.grabContainer() self.m_decorator.grabFadeWidget() return QWidget.eventFilter(self, _object, _event) def fadeWidget(self): return self.parent()
class MainWindow(QMainWindow): siteLoaded = pyqtSignal(object) def __init__(self): QMainWindow.__init__(self) self.site = None self.editor = "" self.install_directory = os.getcwd() self.content_after_animation = "" self.default_path = "" self.method_after_animation = "" Generator.install_directory = self.install_directory self.initUndoRedo() self.initGui() self.readSettings() self.loadPlugins() if self.default_path: if self.loadProject(os.path.join(self.default_path, "Site.qml")): gen = Generator() gen.generateSite(self, self.site) self.dashboard.setExpanded(True) self.showDashboard() self.statusBar().showMessage( QCoreApplication.translate("MainWindow", "Ready")) def actualThemeChanged(self, themename): self.theme_settings_button.setVisible(False) for name in Plugins.themePluginNames(): tei = Plugins.getThemePlugin(name) if tei: if tei.theme_name == themename: self.theme_settings_button.setVisible(True) break def loadProject(self, filename): self.default_path = filename[0:-9] # - /Site.qml if self.reloadProject(filename): # create temp dir for undo redo tempPath = self.site.source_path[self.site.source_path.rfind("/") + 1:] temp = QDir(os.path.join(QDir.tempPath(), "FlatSiteBuilder")) temp.mkdir(tempPath) temp.cd(tempPath) temp.mkdir("pages") temp.mkdir("posts") # in case these subfolders were empty and not published to github dir = QDir(self.site.source_path) dir.mkdir("pages") dir.mkdir("posts") dir.mkdir("assets") dir.cd("assets") dir.mkdir("images") return True else: return False def initUndoRedo(self): self.undoStack = QUndoStack() temp = QDir(os.path.join(QDir.tempPath(), "FlatSiteBuilder")) if temp.exists(): temp.removeRecursively() temp.setPath(QDir.tempPath()) temp.mkdir("FlatSiteBuilder") def initGui(self): self.installEventFilter(self) self.dashboard = Expander( QCoreApplication.translate("MainWindow", "Dashboard"), ":/images/dashboard_normal.png", ":/images/dashboard_hover.png", ":/images/dashboard_selected.png") self.content = Expander( QCoreApplication.translate("MainWindow", "Content"), ":/images/pages_normal.png", ":/images/pages_hover.png", ":/images/pages_selected.png") self.appearance = Expander( QCoreApplication.translate("MainWindow", "Appearance"), ":/images/appearance_normal.png", ":/images/appearance_hover.png", ":/images/appearance_selected.png") self.settings = Expander( QCoreApplication.translate("MainWindow", "Settings"), ":/images/settings_normal.png", ":/images/settings_hover.png", ":/images/settings_selected.png") self.setWindowTitle(QCoreApplication.applicationName() + " " + QCoreApplication.applicationVersion()) vbox = QVBoxLayout() vbox.addWidget(self.dashboard) vbox.addWidget(self.content) vbox.addWidget(self.appearance) vbox.addWidget(self.settings) vbox.addStretch() content_box = QVBoxLayout() pages_button = HyperLink( QCoreApplication.translate("MainWindow", "Pages")) posts_button = HyperLink( QCoreApplication.translate("MainWindow", "Posts")) content_box.addWidget(pages_button) content_box.addWidget(posts_button) self.content.addLayout(content_box) app_box = QVBoxLayout() themes_button = HyperLink( QCoreApplication.translate("MainWindow", "Themes")) menus_button = HyperLink( QCoreApplication.translate("MainWindow", "Menus")) self.theme_settings_button = HyperLink( QCoreApplication.translate("MainWindow", "Theme Settings")) self.theme_settings_button.setVisible(False) app_box.addWidget(menus_button) app_box.addWidget(themes_button) app_box.addWidget(self.theme_settings_button) self.appearance.addLayout(app_box) scroll_content = QWidget() scroll_content.setLayout(vbox) scroll = QScrollArea() scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) scroll.setWidget(scroll_content) scroll.setWidgetResizable(True) scroll.setMaximumWidth(200) scroll.setMinimumWidth(200) self.navigationdock = QDockWidget( QCoreApplication.translate("MainWindow", "Navigation"), self) self.navigationdock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.navigationdock.setWidget(scroll) self.navigationdock.setObjectName("Navigation") self.addDockWidget(Qt.LeftDockWidgetArea, self.navigationdock) self.showDock = FlatButton(":/images/edit_normal.png", ":/images/edit_hover.png") self.showDock.setToolTip( QCoreApplication.translate("MainWindow", "Show Navigation")) self.statusBar().addPermanentWidget(self.showDock) self.dashboard.expanded.connect(self.dashboardExpanded) self.dashboard.clicked.connect(self.showDashboard) self.content.expanded.connect(self.contentExpanded) self.content.clicked.connect(self.showPages) self.appearance.expanded.connect(self.appearanceExpanded) self.appearance.clicked.connect(self.showMenus) self.settings.expanded.connect(self.settingsExpanded) self.settings.clicked.connect(self.showSettings) menus_button.clicked.connect(self.showMenus) pages_button.clicked.connect(self.showPages) posts_button.clicked.connect(self.showPosts) themes_button.clicked.connect(self.showThemes) self.theme_settings_button.clicked.connect(self.showThemesSettings) self.showDock.clicked.connect(self.showMenu) self.navigationdock.visibilityChanged.connect( self.dockVisibilityChanged) def showDashboard(self): if self.editor: self.method_after_animation = "showDashboard" self.editor.closeEditor() return db = Dashboard(self.site, self.default_path) db.loadSite.connect(self.loadProject) db.previewSite.connect(self.previewSite) db.publishSite.connect(self.publishSite) db.createSite.connect(self.createSite) db.buildSite.connect(self.buildSite) self.siteLoaded.connect(db.siteLoaded) self.setCentralWidget(db) def setCentralWidget(self, widget): # do not delete plugin editors old_widget = self.takeCentralWidget() if not isinstance(old_widget, PublisherInterface) and not isinstance( old_widget, ThemeEditorInterface): del old_widget super().setCentralWidget(widget) widget.show() def closeEvent(self, event): self.writeSettings() event.accept() def writeSettings(self): settings = QSettings(QSettings.IniFormat, QSettings.UserScope, QCoreApplication.organizationName(), QCoreApplication.applicationName()) settings.setValue("geometry", self.saveGeometry()) settings.setValue("state", self.saveState()) if self.site: settings.setValue("lastSite", self.site.source_path) def readSettings(self): settings = QSettings(QSettings.IniFormat, QSettings.UserScope, QCoreApplication.organizationName(), QCoreApplication.applicationName()) geometry = settings.value("geometry", QByteArray()) if geometry.isEmpty(): availableGeometry = QApplication.desktop().availableGeometry(self) self.resize(int(availableGeometry.width() / 2), int(availableGeometry.height() / 2)) self.move(int(((availableGeometry.width() - self.width()) / 2)), int((availableGeometry.height() - self.height()) / 2)) else: self.restoreGeometry(geometry) self.restoreState(settings.value("state")) self.default_path = settings.value("lastSite") def reloadProject(self, filename): sys.stdout.flush() engine = QQmlEngine() self.site_component = component = QQmlComponent(engine) self.site_component.loadUrl(QUrl.fromLocalFile(filename)) self.site = self.site_component.create() if self.site is not None: self.site.setFilename(filename) self.site.setWindow(self) else: for error in self.site_component.errors(): print(error.toString()) return False if self.site.output == "": self.site.output = "docs" self.site.loadMenus() self.site.loadPages() self.site.loadPosts() self.theme_settings_button.setVisible(False) Plugins.setActualThemeEditorPlugin("") for key in Plugins.themePluginNames(): tei = Plugins.getThemePlugin(key) if tei: if tei.theme_name == self.site.theme: Plugins.setActualThemeEditorPlugin(tei.class_name) self.theme_settings_button.setVisible(True) break #if not self.site.publisher: # if len(Plugins.publishPluginNames()) > 0: # self.site.publisher = Plugins.publishPluginNames[0] Plugins.setActualPublishPlugin(self.site.publisher) self.siteLoaded.emit(self.site) return True def dashboardExpanded(self, value): if value: self.content.setExpanded(False) self.appearance.setExpanded(False) self.settings.setExpanded(False) def contentExpanded(self, value): if value: self.dashboard.setExpanded(False) self.appearance.setExpanded(False) self.settings.setExpanded(False) def appearanceExpanded(self, value): if value: self.dashboard.setExpanded(False) self.content.setExpanded(False) self.settings.setExpanded(False) def settingsExpanded(self, value): if value: self.dashboard.setExpanded(False) self.content.setExpanded(False) self.appearance.setExpanded(False) def showMenus(self): if self.editor: self.method_after_animation = "showMenus" self.editor.closeEditor() return edit = MenuList(self, self.site) edit.editContent.connect(self.editMenu) self.setCentralWidget(edit) def showPages(self): if self.editor: self.method_after_animation = "showPages" self.editor.closeEditor() return list = ContentList(self.site, ContentType.PAGE) list.editContent.connect(self.editContent) self.setCentralWidget(list) def showPosts(self): if self.editor: self.method_after_animation = "showPosts" self.editor.closeEditor() return list = ContentList(self.site, ContentType.POST) list.editContent.connect(self.editContent) self.setCentralWidget(list) def showThemes(self): if self.editor: self.method_after_animation = "showThemes" self.editor.closeEditor() return tc = ThemeChooser(self, self.site) self.setCentralWidget(tc) def showThemesSettings(self): tei = Plugins.getThemePlugin(Plugins.actualThemeEditorPlugin()) if tei: if self.editor: self.method_after_animation = "showThemesSettings" self.editor.closeEditor() return path = self.site.source_path tei.setWindow(self) tei.setSourcePath(path) self.setCentralWidget(tei) else: self.statusBar().showMessage( QCoreApplication.translate("MainWindow", "Unable to load plugin") + " " + Plugins.actualThemeEditorPlugin()) def showSettings(self): if self.editor: self.method_after_animation = "showSettings" self.editor.closeEditor() return sse = SiteSettingsEditor(self, self.site) self.setCentralWidget(sse) def showMenu(self): self.navigationdock.setVisible(True) def dockVisibilityChanged(self, visible): self.showDock.setVisible(not visible) def previewSite(self, content=None): if self.editor and content: self.content_after_animation = content self.editor.closeEditor() return dir = os.path.join(self.default_path, self.site.output) if not content: if len(self.site.pages) > 0: content = self.site.pages[0] for c in self.site.pages: if c.url == "index.html": content = c break elif len(self.site.posts) > 0: content = self.site.posts()[0] if content: file = content.url self.webView = QWebEngineView() self.webView.loadFinished.connect(self.webViewLoadFinished) url = pathlib.Path(os.path.join(dir, file)).as_uri() self.webView.setUrl(QUrl(url)) self.setCursor(Qt.WaitCursor) else: self.statusBar().showMessage( QCoreApplication.translate( "MainWindow", "Site has no pages or posts to preview.")) def webViewLoadFinished(self, success): if success: self.setCentralWidget(self.webView) self.webView.loadFinished.disconnect(self.webViewLoadFinished) else: QMessageBox.warning( self, "FlatSiteBuilder", QCoreApplication.translate("MainWindow", "Unable to open webpage.")) self.setCursor(Qt.ArrowCursor) def publishSite(self): pluginName = Plugins.actualPublishPlugin() pi = Plugins.getPublishPlugin(pluginName) if pi: self.setCentralWidget(pi) pi.setSitePath(self.install_directory, self.site.source_path) else: QMessageBox.warning( self, "FlatSiteBuilder", QCoreApplication.translate( "MainWindow", "Website has no publish plugin configured.")) def createSite(self): wiz = SiteWizard(self.install_directory, parent=self) wiz.loadSite.connect(self.loadProject) wiz.buildSite.connect(self.buildSite) wiz.show() def buildSite(self): self.site.loadMenus() self.site.loadPages() self.site.loadPosts() if len(self.site.pages) == 0 and len(self.site.posts) == 0: self.statusBar().showMessage( QCoreApplication.translate( "MainWindow", "Site has no pages or posts to build.")) else: gen = Generator() gen.generateSite(self, self.site) self.statusBar().showMessage( self.site.title + " " + QCoreApplication.translate("MainWindow", "has been generated")) def editMenu(self, item): menu = item.data(Qt.UserRole) me = MenuEditor(self, menu, self.site) self.editor = me list = self.centralWidget() if list: list.registerMenuEditor(me) list.editedItemChanged.connect(self.editedItemChanged) self.editor.closes.connect(self.editorClosed) self.editor.contentChanged.connect(self.menuChanged) self.animate(item) def editContent(self, item): content = item.data(Qt.UserRole) self.editor = ContentEditor(self, self.site, content) self.siteLoaded.connect(self.editor.siteLoaded) self.editor.closes.connect(self.editorClosed) self.editor.preview.connect(self.previewSite) self.animate(item) def animate(self, item): panel = self.centralWidget() self.list = item.tableWidget() self.row = item.row() # create a cell widget to get the right position in the table self.cellWidget = QWidget() self.cellWidget.setMaximumHeight(0) self.list.setCellWidget(self.row, 1, self.cellWidget) pos = self.cellWidget.mapTo(panel, QPoint(0, 0)) self.editor.setParent(panel) self.editor.move(pos) self.editor.resize(self.cellWidget.size()) self.editor.show() self.animation = QPropertyAnimation(self.editor, "geometry".encode("utf-8")) self.animation.setDuration(300) self.animation.setStartValue( QRect(pos.x(), pos.y(), self.cellWidget.size().width(), self.cellWidget.size().height())) self.animation.setEndValue( QRect(0, 0, panel.size().width(), panel.size().height())) self.animation.start() def eventFilter(self, watched, event): if watched == self and event.type() == QEvent.Resize and self.editor: w = self.centralWidget() if w: self.editor.resize(w.size()) return False def editorClosed(self): pos = self.cellWidget.mapTo(self.centralWidget(), QPoint(0, 0)) # correct end values in case of resizing the window self.animation.setStartValue( QRect(pos.x(), pos.y(), self.cellWidget.size().width(), self.cellWidget.size().height())) self.animation.finished.connect(self.animationFineshedZoomOut) self.animation.setDirection(QAbstractAnimation.Backward) self.animation.start() def animationFineshedZoomOut(self): self.list.removeCellWidget(self.row, 1) del self.animation # in the case self.editor was a MenuEditor, we have to unregister it in the MenuList # should be refactored some day :-) list = self.centralWidget() if list is MenuEditor: list.unregisterMenuEditor() if isinstance(list, ContentList): list.reload() del self.editor self.editor = None if self.method_after_animation == "showDashboard": self.showDashboard() self.method_after_animation = "" elif self.method_after_animation == "showSettings": self.showSettings() elif self.method_after_animation == "showThemesSettings": self.showThemesSettings() elif self.method_after_animation == "showThemes": self.showThemes() elif self.method_after_animation == "showMenus": self.showMenus() elif self.method_after_animation == "showPages": self.showPages() elif self.method_after_animation == "showPosts": self.showPosts() if self.content_after_animation: self.previewSite(self.content_after_animation) self.content_after_animation = None def contentChanged(self, content): self.list.item(self.row, 1).setText(content.title()) self.list.item(self.row, 2).setText(content.source()) self.list.item(self.row, 3).setText(content.layout()) self.list.item(self.row, 4).setText(content.author()) self.list.item(self.row, 5).setText(content.date().toString("dd.MM.yyyy")) def menuChanged(self, menu): self.list.item(self.row, 1).setText(menu.name()) def editedItemChanged(self, item): # this will happen, if the MenuList.reloadMenu() has been called by the undo.command self.list = item.tableWidget() self.row = item.row() self.cellWidget = QWidget() self.list.setCellWidget(self.row, 1, self.cellWidget) def loadPlugins(self): # check if we are running in a frozen environment (pyinstaller --onefile) if getattr(sys, "frozen", False): bundle_dir = sys._MEIPASS # if we are running in a onefile environment, then copy all plugin to /tmp/... if bundle_dir != os.getcwd(): os.mkdir(os.path.join(bundle_dir, "plugins")) for root, dirs, files in os.walk( os.path.join(os.getcwd(), "plugins")): for file in files: shutil.copy(os.path.join(root, file), os.path.join(bundle_dir, "plugins")) print("copy", file) break # do not copy __pycache__ else: bundle_dir = os.getcwd() plugins_dir = os.path.join(bundle_dir, "plugins") for root, dirs, files in os.walk(plugins_dir): for file in files: modulename, ext = os.path.splitext(file) if ext == ".py": module = import_module("plugins." + modulename) for name, klass in inspect.getmembers( module, inspect.isclass): if klass.__module__ == "plugins." + modulename: instance = klass() if isinstance(instance, ElementEditorInterface): Plugins.addElementPlugin(name, instance) instance.registerContenType() elif isinstance(instance, ThemeEditorInterface): Plugins.addThemePlugin(name, instance) elif isinstance(instance, PublisherInterface): Plugins.addPublishPlugin(name, instance) elif isinstance(instance, GeneratorInterface): Plugins.addGeneratorPlugin(name, instance) break # not to list __pycache__
class CircleFillAnimator(AbstractAnimator): m_decorator: CircleFillDecorator m_animation: QPropertyAnimation m_hideAfterFinish = True def __init__(self, _widgetForFill): super(CircleFillAnimator, self).__init__(_widgetForFill) self.m_decorator = CircleFillDecorator(_widgetForFill) self.m_animation = QPropertyAnimation(self.m_decorator, b"_radius") _widgetForFill.installEventFilter(self) self.m_animation.setDuration(500) self.m_decorator.setAttribute(Qt.WA_TransparentForMouseEvents) self.m_decorator.hide() def finished(): self.setAnimatedStopped() if self.m_hideAfterFinish: self.hideDecorator() self.m_animation.finished.connect(finished) def setStartPoint(self, _point): self.m_decorator.setStartPoint(_point) def setFillColor(self, _color): self.m_decorator.setFillColor(_color) def setHideAfterFinish(self, _hide): self.m_hideAfterFinish = _hide def animationDuration(self): return self.m_animation.duration() def animateForward(self): self.fillIn() def fillIn(self): if self.isAnimated() and self.isAnimatedForward(): return self.setAnimatedForward() startRadius = 0 finalRadius = math.sqrt( self.widgetForFill().height() * self.widgetForFill().height() + self.widgetForFill().width() * self.widgetForFill().width()) self.m_decorator.resize(self.widgetForFill().size()) self.m_decorator.move(0, 0) self.m_decorator.show() self.m_decorator.raise_() if self.m_animation.state() == QPropertyAnimation.Running: self.m_animation.pause() self.m_animation.setDirection(QPropertyAnimation.Backward) self.m_animation.resume() else: self.m_animation.setEasingCurve(QEasingCurve.OutQuart) self.m_animation.setDirection(QPropertyAnimation.Forward) self.m_animation.setStartValue(startRadius) self.m_animation.setEndValue(finalRadius) self.m_animation.start() def animateBackward(self): self.fillOut() def fillOut(self): if self.isAnimated() and self.isAnimatedBackward(): return self.setAnimatedBackward() startRadius = math.sqrt( self.widgetForFill().height() * self.widgetForFill().height() + self.widgetForFill().width() * self.widgetForFill().width()) finalRadius = 0 self.m_decorator.resize(self.widgetForFill().size()) self.m_decorator.move(0, 0) self.m_decorator.show() self.m_decorator.raise_() if self.m_animation.state() == QPropertyAnimation.Running: self.m_animation.pause() self.m_animation.setDirection(QPropertyAnimation.Backward) self.m_animation.resume() else: self.m_animation.setEasingCurve(QEasingCurve.OutQuart) self.m_animation.setDirection(QPropertyAnimation.Forward) self.m_animation.setStartValue(startRadius) self.m_animation.setEndValue(finalRadius) self.m_animation.start() def hideDecorator(self): hideEffect = self.m_decorator.graphicsEffect() if hideEffect == None: hideEffect = QGraphicsOpacityEffect(self.m_decorator) self.m_decorator.setGraphicsEffect(hideEffect) hideEffect.setOpacity(1) hideAnimation = QPropertyAnimation(hideEffect, b"opacity", self.m_decorator) hideAnimation.setDuration(400) hideAnimation.setStartValue(1) hideAnimation.setEndValue(0) def finished(): self.m_decorator.hide() hideEffect.setOpacity(1) hideAnimation.finished.connect(finished) hideAnimation.start(QAbstractAnimation.DeleteWhenStopped) def widgetForFill(self): return self.parent()
class AnimBox(QGraphicsObject): """A Box that draws an outline when hover over. Attributes ---------- hoverEnter: pyqtSignal Emitted when the mouse hover into the box hoverExit: pyqtSignal Emitted when the mouse hover out of the box """ hoverEnter = pyqtSignal() hoverExit = pyqtSignal() def __init__(self, x, y, width, height, parent=None): """Prepares the box and animation Parameters ---------- x: float x position of the top-left corner of the box y: float y position of the top-left corner of the box width: float Width of the box height: float Height of the box parent: object Passed into QGraphicsObject init method """ super().__init__(parent=parent) self.x = x self.y = y self.width = width self.height = height self.circumference = 2 * (width + height) self.default_pen = QPen() self.default_pen.setColor(Qt.white) self.outline_pen = QPen() self.outline_pen.setColor(Qt.white) self.outline_pen.setWidth(5) self.detected = False # Whether the mouse hover over the box self.btn_rect = QRectF(self.x, self.y, self.width, self.height) self.left = QLineF() self.down = QLineF() self.right = QLineF() self.up = QLineF() self.line_order = [self.up, self.right, self.down, self.left] self.set_freeze(False) self.length = 0 self.anim = QPropertyAnimation(self, b'length') self.anim.setStartValue(0) for t in range(1, 10): self.anim.setKeyValueAt(t / 10, self.logistic_func(t / 10)) self.anim.setEndValue(self.circumference) def set_freeze(self, freeze): """Set whether the box should accept the mouse events Parameters ---------- freeze: bool True to stop the box from accepting mouse inputs, False otherwise """ if freeze: self.setAcceptedMouseButtons(Qt.NoButton) self.setAcceptHoverEvents(False) else: self.setAcceptedMouseButtons(Qt.LeftButton) self.setAcceptHoverEvents(True) def toggle_anim(self, toggling): """Toggle the highlight animation to be play forward or backward Parameters ---------- toggling: bool True for forward, False for backwards """ if toggling: self.anim.setDirection(QAbstractAnimation.Forward) else: self.anim.setDirection(QAbstractAnimation.Backward) self.anim.start() def logistic_func(self, x): """The logistic function that determines the animation motion Parameters ---------- x: list or numpy array Values to be feed into the function Returns ------- list or numpy array Values of the logistic function corresponding to the input range """ return self.circumference / (1 + math.exp(-(x - 0.5) * 18)) def boundingRect(self): """Reimplemented from QGraphicsObject. """ return QRectF(self.x - 5, self.y - 5, self.width + 10, self.height + 10) def paint(self, painter, style, widget=None): """Reimplemented from QGraphicsObject. Draws the Box and the highlights. """ painter.setPen(self.outline_pen) for line in self.line_order: if line.length() > 1: painter.drawLine(line) painter.setPen(self.default_pen) painter.drawRect(self.btn_rect) @pyqtProperty(float) def length(self): """float: The length of the highlight to be drawn. When set, the length of the outlines are determined """ return self._length @length.setter def length(self, value): self._length = value remaining_length = value if remaining_length >= 2 * self.width + self.height: length_to_draw = remaining_length - (2 * self.width + self.height) remaining_length -= length_to_draw else: length_to_draw = 0 self.line_order[3].setLine(self.x, self.y + self.height, self.x, self.y + self.height - length_to_draw) if remaining_length >= self.width + self.height: length_to_draw = remaining_length - (self.width + self.height) remaining_length -= length_to_draw else: length_to_draw = 0 self.line_order[2].setLine(self.x + self.width, self.y + self.height, self.x + self.width - length_to_draw, self.y + self.height) if remaining_length >= self.width: length_to_draw = remaining_length - self.width remaining_length -= length_to_draw else: length_to_draw = 0 self.line_order[1].setLine(self.x + self.width, self.y, self.x + self.width, self.y + length_to_draw) self.line_order[0].setLine(self.x, self.y, self.x + remaining_length, self.y) self.update() def hoverEnterEvent(self, event): """Reimplemented hoverEnterEvent. Detect the mouse and toggle the animation """ if ~self.detected: self.hoverEnter.emit() self.detected = True self.toggle_anim(True) super().hoverEnterEvent(event) def hoverLeaveEvent(self, event): """Reimplemented hoverLeaveEvent. Detect the mouse leaving and reverse the animation """ if self.detected: self.hoverExit.emit() self.detected = False self.toggle_anim(False) super().hoverLeaveEvent(event)
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()
class SideSlideAnimator(AbstractAnimator): m_side: ApplicationSide m_decorateBackground: bool m_decorator: SideSlideDecorator m_animation: QPropertyAnimation def __init__(self, _widgetForSlide): super(SideSlideAnimator, self).__init__(_widgetForSlide) self.m_side = ApplicationSide() self.m_decorateBackground = True self.m_decorator = SideSlideDecorator(_widgetForSlide.parentWidget()) self.m_animation = QPropertyAnimation(self.m_decorator, b"_slidePos") _widgetForSlide.parentWidget().installEventFilter(self) self.m_animation.setEasingCurve(QEasingCurve.InQuad) self.m_animation.setDuration(260) self.m_decorator.hide() def finished(): self.setAnimatedStopped() if self.isAnimatedForward(): self.widgetForSlide().move(self.m_decorator.slidePos()) else: self.m_decorator.hide() self.m_animation.finished.connect(finished) self.m_decorator.clicked.connect(self.slideOut) def setApplicationSide(self, _side): if self.m_side != _side: self.m_side = _side def setDecorateBackground(self, _decorate): if self.m_decorateBackground != _decorate: self.m_decorateBackground = _decorate def animationDuration(self): return self.m_animation.duration() def animateForward(self): self.slideIn() def slideIn(self): if self.isAnimated() and self.isAnimatedForward(): return self.setAnimatedForward() self.widgetForSlide().lower() self.widgetForSlide().move(-self.widgetForSlide().width(), -self.widgetForSlide().height()) self.widgetForSlide().show() self.widgetForSlide().resize(self.widgetForSlide().sizeHint()) _topWidget = self.widgetForSlide() topWidget = self.widgetForSlide() while _topWidget: topWidget = _topWidget _topWidget = topWidget.parentWidget() finalSize = self.widgetForSlide().size() startPosition = QPoint() finalPosition = QPoint() if self.m_side == ApplicationSide.LeftSide: finalSize.setHeight(topWidget.height()) startPosition = QPoint(-finalSize.width(), 0) finalPosition = QPoint(0, 0) if self.m_side == ApplicationSide.TopSide: finalSize.setWidth(topWidget.width()) startPosition = QPoint(0, -finalSize.height()) finalPosition = QPoint(0, 0) if self.m_side == ApplicationSide.RightSide: finalSize.setHeight(topWidget.height()) startPosition = QPoint(topWidget.width(), 0) finalPosition = QPoint(topWidget.width() - finalSize.width(), 0) if self.m_side == ApplicationSide.BottomSide: finalSize.setWidth(topWidget.width()) startPosition = QPoint(0, topWidget.height()) finalPosition = QPoint(0, topWidget.height() - finalSize.height()) if self.m_decorateBackground: self.m_decorator.setParent(topWidget) self.m_decorator.move(0, 0) self.m_decorator.grabParent() self.m_decorator.show() self.m_decorator.raise_() self.widgetForSlide().move(startPosition) self.widgetForSlide().resize(finalSize) self.widgetForSlide().raise_() self.m_decorator.grabSlideWidget(self.widgetForSlide()) if self.m_animation.state() == QPropertyAnimation.Running: self.m_animation.pause() self.m_animation.setDirection(QPropertyAnimation.Backward) self.m_animation.resume() else: self.m_animation.setEasingCurve(QEasingCurve.OutQuart) self.m_animation.setDirection(QPropertyAnimation.Forward) self.m_animation.setStartValue(startPosition) self.m_animation.setEndValue(finalPosition) self.m_animation.start() if self.m_decorateBackground: DARKER = True self.m_decorator.decorate(DARKER) def animateBackward(self): self.slideOut() def slideOut(self): if self.isAnimated() and self.isAnimatedBackward(): return self.setAnimatedBackward() if self.widgetForSlide().isVisible(): _topWidget = self.widgetForSlide() topWidget = self.widgetForSlide() while _topWidget: topWidget = _topWidget _topWidget = topWidget.parentWidget() self.widgetForSlide().hide() finalSize = self.widgetForSlide().size() startPosition = self.widgetForSlide().pos() finalPosition = QPoint() if self.m_side == ApplicationSide.LeftSide: startPosition = QPoint(0, 0) finalPosition = QPoint(-finalSize.width(), 0) if self.m_side == ApplicationSide.TopSide: startPosition = QPoint(0, 0) finalPosition = QPoint(0, -finalSize.height()) if self.m_side == ApplicationSide.RightSide: startPosition = QPoint(topWidget.width() - finalSize.width(), 0) finalPosition = QPoint(topWidget.width(), 0) if self.m_side == ApplicationSide.BottomSide: startPosition = QPoint(0, topWidget.height() - finalSize.height()) finalPosition = QPoint(0, topWidget.height()) if self.m_animation.state() == QPropertyAnimation.Running: self.m_animation.pause() self.m_animation.setDirection(QPropertyAnimation.Backward) self.m_animation.resume() else: self.m_animation.setEasingCurve(QEasingCurve.InQuart) self.m_animation.setDirection(QPropertyAnimation.Forward) self.m_animation.setStartValue(startPosition) self.m_animation.setEndValue(finalPosition) self.m_animation.start() if self.m_decorateBackground: LIGHTER = False self.m_decorator.decorate(LIGHTER) def eventFilter(self, _object, _event): if _object == self.widgetForSlide().parentWidget() and _event.type( ) == QEvent.Resize: widgetForSlideParent = self.widgetForSlide().parentWidget() if self.m_side == ApplicationSide.RightSide: self.widgetForSlide().move( widgetForSlideParent.width() - self.widgetForSlide().width(), 0) if self.m_side == ApplicationSide.LeftSide: self.widgetForSlide().resize(self.widgetForSlide().width(), widgetForSlideParent.height()) if self.m_side == ApplicationSide.BottomSide: self.widgetForSlide().move( 0, widgetForSlideParent.height() - self.widgetForSlide().height()) if self.m_side == ApplicationSide.TopSide: self.widgetForSlide().resize(widgetForSlideParent.width(), self.widgetForSlide().height()) self.m_decorator.grabSlideWidget(self.widgetForSlide()) return QObject.eventFilter(self, _object, _event) def widgetForSlide(self): return self.parent()
class SudokuGrid(BaseSudokuItem): buttonClicked = pyqtSignal(float, float, bool) finishDrawing = pyqtSignal() puzzleFinished = pyqtSignal() def __init__(self, width, height, parent=None): super().__init__(parent) self.width = width self.height = height self.thick_pen = QPen() self.thick_pen.setColor(Qt.white) self.thick_unit = 5 self.thick_pen.setWidth(self.thick_unit) self.thinlines = [] self.thicklines = [] self.cell_width = self.width / 9 self.cell_height = self.height / 9 for i in range(1, 9): delta_h = self.cell_height * i delta_w = self.cell_width * i if i % 3 == 0: self.thicklines.append(QLineF(0, delta_h, self.width, delta_h)) self.thicklines.append(QLineF(delta_w, 0, delta_w, self.height)) else: self.thinlines.append(QLineF(0, delta_h, self.width, delta_h)) self.thinlines.append(QLineF(delta_w, 0, delta_w, self.height)) self.sudoku_grid = sdk.SudokuSystem() self.grid_painter = NumberPainter(self, self.sudoku_grid) self.mouse_w = 0 self.mouse_h = 0 self.selection_unit = 8 self.selection_pen = QPen() self.selection_pen.setColor(Qt.white) self.selection_pen.setWidth(self.selection_unit) self.selection_box = QRectF(0, 0, self.cell_width, self.cell_height) self.setAcceptHoverEvents(True) self.setAcceptedMouseButtons(Qt.LeftButton) self.setFlag(QGraphicsItem.ItemIsFocusable, True) self.set_disabled(False) # Length of the box to be drawn self.length = 0 # Set up the length to be animated self.anim = QPropertyAnimation(self, b'length') self.anim.setDuration(500) # Animation speed self.anim.setStartValue(0) for t in range(1, 10): self.anim.setKeyValueAt(t / 10, self.width * t / 10) self.anim.setEndValue(self.width) self.scribbling = False self.drawn = False self.anim.finished.connect(self.finish_drawing) def set_disabled(self, state): if state: self.setAcceptedMouseButtons(Qt.NoButton) else: self.setAcceptedMouseButtons(Qt.LeftButton) self.setAcceptHoverEvents(not state) def finish_drawing(self): if self.length == self.width: self.drawn = True self.finishDrawing.emit() # Toggle the animation to be play forward or backward def toggle_anim(self, toggling): if toggling: self.anim.setDirection(QAbstractAnimation.Forward) else: self.anim.setDirection(QAbstractAnimation.Backward) self.anim.start() def generate_new_grid(self, difficulty): self.sudoku_grid.generate_random_board(difficulty) #self.sudoku_grid.generate_test_board(difficulty) # Uncomment for testing self.update() def change_cell_scribbles(self, val): if val == 0: self.sudoku_grid.clear_scribble(self.mouse_h, self.mouse_w) else: self.sudoku_grid.toggle_scribble(self.mouse_h, self.mouse_w, val) self.grid_painter.update() def replace_cell_number(self, val): self.sudoku_grid.replace_cell_number(self.mouse_h, self.mouse_w, val) self.grid_painter.update() if self.sudoku_grid.completion_check(): self.puzzleFinished.emit() def boundingRect(self): return QRectF(-5, -5, self.width + 10, self.height + 10) # Reimplemented paint def paint(self, painter, style, widget=None): painter.setPen(self.default_pen) for line in self.thinlines: painter.drawLine(line) painter.setPen(self.thick_pen) for line in self.thicklines: painter.drawLine(line) if self.drawn: painter.setPen(self.selection_pen) painter.drawRect(self.selection_box) def hoverMoveEvent(self, event): if not (self.freeze and self.drawn): box_w = bound_value(0, int(event.pos().x() / self.cell_width), 8) box_h = bound_value(0, int(event.pos().y() / self.cell_height), 8) if box_w != self.mouse_w or box_h != self.mouse_h: self.mouse_w = box_w self.mouse_h = box_h self.selection_box.moveTopLeft( QPointF(box_w * self.cell_width, box_h * self.cell_height)) self.update() def mousePressEvent(self, event): event.accept() #if self.drawn: # w = (self.mouse_w + 0.5) * self.cell_width # h = (self.mouse_h + 0.5) * self.cell_height # if not self.sudoku_grid.get_cell_status(self.mouse_h, self.mouse_w) == sdk.FIXED: # self.buttonClicked.emit(w, h, self.scribbling) #else: # self.buttonClicked.emit(0, 0, self.scribbling) def mouseReleaseEvent(self, event): if self.drawn: w = (self.mouse_w + 0.5) * self.cell_width h = (self.mouse_h + 0.5) * self.cell_height if not self.sudoku_grid.get_cell_status(self.mouse_h, self.mouse_w) == sdk.FIXED: self.buttonClicked.emit(w, h, self.scribbling) else: self.buttonClicked.emit(0, 0, self.scribbling) def focusInEvent(self, event): self.set_disabled(False) def focusOutEvent(self, event): self.set_disabled(True) def keyPressEvent(self, event): if not event.isAutoRepeat(): if (event.key() == SCRIBBLE_KEY) and not self.scribbling: self.scribbling = True def keyReleaseEvent(self, event): if not event.isAutoRepeat(): if event.key() == SCRIBBLE_KEY and self.scribbling: self.scribbling = False # Defining the length to be drawn as a pyqtProperty @pyqtProperty(float) def length(self): return self._length # Determine the length of the four lines to be drawn @length.setter def length(self, value): self._length = value for lines in self.thinlines: if lines.x1() == 0: lines.setP2(QPointF(value, lines.y2())) else: lines.setP2(QPointF(lines.x2(), value)) for lines in self.thicklines: if lines.x1() == 0: lines.setP2(QPointF(value, lines.y2())) else: lines.setP2(QPointF(lines.x2(), value)) self.update()
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 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 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 SlideAnimator(AbstractAnimator): m_direction:AnimationDirection m_isFixBackground:bool m_isFixStartSize:bool m_startMinSize = QSize() m_startMaxSize = QSize() m_startSize = QSize() m_decorator: SlideForegroundDecorator m_animation: QPropertyAnimation def __init__(self, _widgetForSlide): super(SlideAnimator, self).__init__(_widgetForSlide) self.m_direction = AnimationDirection.FromLeftToRight self.m_isFixBackground = True self.m_isFixStartSize = False self.m_decorator = SlideForegroundDecorator(_widgetForSlide) self.m_animation = QPropertyAnimation(self.m_decorator, b"maximumWidth") _widgetForSlide.installEventFilter(self) self.m_animation.setDuration(300) self.m_decorator.hide() def valueChanged(_value): if self.isWidth(): self.widgetForSlide().setMaximumWidth(_value) else: self.widgetForSlide().setMaximumHeight(_value) self.m_animation.valueChanged.connect(valueChanged) def finished(): self.setAnimatedStopped() self.m_decorator.hide() self.m_animation.finished.connect(finished) def setAnimationDirection(self, _direction): if self.m_direction != _direction: self.m_direction = _direction self.m_animation.setPropertyName(b"maximumWidth" if self.isWidth() else b"maximumHeight") def setFixBackground(self, _fix): if self.m_isFixBackground != _fix: self.m_isFixBackground = _fix def setFixStartSize(self, _fix): if self.m_isFixStartSize != _fix: self.m_isFixStartSize = _fix def animationDuration(self): return self.m_animation.duration() def animateForward(self): self.slideIn() def slideIn(self): if not self.m_startMinSize.isValid(): self.m_startMinSize = self.widgetForSlide().minimumSize() if not self.m_startMaxSize.isValid(): self.m_startMaxSize = self.widgetForSlide().maximumSize() if not self.m_startSize.isValid(): self.m_startSize = self.widgetForSlide().sizeHint() if self.isAnimated() and self.isAnimatedForward(): return self.setAnimatedForward() if self.isWidth(): self.widgetForSlide().setMaximumWidth(0) else: self.widgetForSlide().setMaximumHeight(0) self.widgetForSlide().show() currentSize = self.widgetForSlide().size() finalSize = QSize(currentSize.width(), currentSize.height()) self.fixSize(self.m_startSize, finalSize) self.widgetForSlide().hide() self.fixSizeOfWidgetForSlide(finalSize) self.m_decorator.grabParent(finalSize) self.fixSizeOfWidgetForSlide(currentSize) self.widgetForSlide().show() if self.m_isFixBackground: self.m_decorator.move(0, 0) self.m_decorator.show() self.m_decorator.raise_() if self.m_animation.state() == QPropertyAnimation.Running: self.m_animation.pause() self.m_animation.setDirection(QPropertyAnimation.Backward) self.m_animation.resume() else: self.m_animation.setEasingCurve(QEasingCurve.OutQuart) self.m_animation.setDirection(QPropertyAnimation.Forward) self.m_animation.setStartValue(self.widgetForSlide().width() if self.isWidth() else self.widgetForSlide().height()) self.m_animation.setEndValue(finalSize.width() if self.isWidth() else finalSize.height()) self.m_animation.start() def animateBackward(self): self.slideOut() def slideOut(self): if not self.m_startMinSize.isValid(): self.m_startMinSize = self.widgetForSlide().minimumSize() if not self.m_startMaxSize.isValid(): self.m_startMaxSize = self.widgetForSlide().maximumSize() if not self.m_startSize.isValid() or not self.m_isFixStartSize: self.m_startSize = self.widgetForSlide().size() if self.isAnimated() and self.isAnimatedBackward(): return self.setAnimatedBackward() finalSize = self.widgetForSlide().size() if self.isWidth(): finalSize.setWidth(0) else: finalSize.setHeight(0) self.m_decorator.grabParent(self.widgetForSlide().size()) if self.m_isFixBackground: self.m_decorator.move(0, 0) self.m_decorator.show() self.m_decorator.raise_() if self.m_animation.state() == QPropertyAnimation.Running: self.m_animation.pause() self.m_animation.setDirection(QPropertyAnimation.Backward) self.m_animation.resume() else: self.m_animation.setEasingCurve(QEasingCurve.InQuart) self.m_animation.setDirection(QPropertyAnimation.Forward) self.m_animation.setStartValue(self.widgetForSlide().width() if self.isWidth() else self.widgetForSlide().height()) self.m_animation.setEndValue(finalSize.width() if self.isWidth() else finalSize.height()) self.m_animation.start() def eventFilter(self, _object, _event): if _object == self.widgetForSlide() and _event.type() == QEvent.Resize and self.m_decorator.isVisible(): if self.m_direction == AnimationDirection.FromLeftToRight: self.m_decorator.move(self.widgetForSlide().width() - self.m_decorator.width(), 0) if self.m_direction == AnimationDirection.FromTopToBottom: self.m_decorator.move(0, self.widgetForSlide().height() - self.m_decorator.height()) return QObject.eventFilter(self, _object, _event) def isWidth(self): return self.m_direction == AnimationDirection.FromLeftToRight or self.m_direction == AnimationDirection.FromRightToLeft def fixSize(self, _sourceSize, _targetSize): if self.isWidth(): _targetSize.setWidth(_sourceSize.width()) else: _targetSize.setHeight(_sourceSize.height()) def fixSizeOfWidgetForSlide(self, _sourceSize): if self.isWidth(): self.widgetForSlide().setFixedWidth(_sourceSize.width()) else: self.widgetForSlide().setFixedHeight(_sourceSize.height()) def widgetForSlide(self): return self.parent()
class AnimatedText(QGraphicsObject): def __init__(self, text, parent=None): super().__init__(parent=parent) self.parent = parent self.actual_text = text self.shown_text = '' self.delay = 3 # Set up pens for drawing self.default_pen = QPen() self.default_pen.setColor(Qt.white) self.shown_length = 0 # Set up the length to be animated self.anim = QPropertyAnimation(self, b'shown_length') self.anim.setDuration(len(self.actual_text) * 50) # Animation speed self.anim.setStartValue(0) for t in range(1, 10): self.anim.setKeyValueAt( t / 10, (len(self.actual_text) + self.delay) * t / 10) self.anim.setEndValue(len(self.actual_text) + self.delay) self.visibleChanged.connect(self.show_text) def show_text(self): if self.isVisible(): self.toggle_anim(True) else: self.shown_length = 0 # Toggle the animation to be play forward or backward def toggle_anim(self, toggling): if toggling: self.anim.setDirection(QAbstractAnimation.Forward) else: self.anim.setDirection(QAbstractAnimation.Backward) self.anim.start() def boundingRect(self): return self.parent.boundingRect() def paint(self, painter, style, widget=None): painter.setPen(self.default_pen) painter.drawText(self.parent.boundingRect(), Qt.AlignCenter, self.shown_text) # Defining the length to be drawn as a pyqtProperty @pyqtProperty(int) def shown_length(self): return self._shown_length # Determine the length of the four lines to be drawn @shown_length.setter def shown_length(self, value): self._shown_length = value if value < self.delay: # All printed text should be garbage garbage = [ chr(num) for num in [random.choice(range(33, 127)) for _ in range(value)] ] self.shown_text = ''.join(garbage) else: # Printed text contain some actual text garbage = [ chr(num) for num in [ random.choice(range(33, 127)) for _ in range( min( len(self.actual_text) + self.delay - value, self.delay)) ] ] self.shown_text = self.actual_text[:value - self.delay] + ''.join(garbage) self.update()
class BoxBoard(QGraphicsWidget): """A generic board that draws an animated rectangular border """ def __init__(self, width, height, parent=None): """Prepare the lines to be drawn and set up the animation Parameters ---------- width: float Width of the box height: float Height of the box parent: object Pass into QGraphicsWidget init method """ super().__init__(parent) self.width = width self.height = height self.half_circumference = width + height self.freeze = False self.setMinimumSize(QSizeF(width, height)) #self.setMaximumSize(QSizeF(width, height)) self.size_policy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.size_policy.setHeightForWidth(True) self.setSizePolicy(self.size_policy) # Set up a default pen for drawing self.default_pen = QPen() self.default_pen.setColor(Qt.white) self.default_pen.setWidth(5) # The 4 lines to construct the box self.left = QLineF(0, 0, 0, self.height) self.down = QLineF(0, self.height, self.width, self.height) self.right = QLineF(self.width, self.height, self.width, 0) self.up = QLineF(self.width, 0, 0, 0) self.line_order = [self.up, self.right, self.down, self.left] self.length = 0 # Set up the length to be animated self.anim = QPropertyAnimation(self, b'length') self.anim.setDuration(800) # Animation speed self.anim.setStartValue(0) for t in range(1, 10): self.anim.setKeyValueAt(t / 10, self.half_circumference * t / 10) self.anim.setEndValue(self.half_circumference) # Defining the length to be drawn as a pyqtProperty @pyqtProperty(float) def length(self): """float: The length of the box to be drawn When the value is set, the length of the lines making up the box are calculated and updated. """ return self._length # Determine the length of the four lines to be drawn @length.setter def length(self, value): self._length = value remaining_length = value if remaining_length >= self.height: length_to_draw = remaining_length - self.height remaining_length -= length_to_draw else: length_to_draw = 0 self.down.setLine(0, self.height, length_to_draw, self.height) self.up.setLine(self.width, 0, self.width - length_to_draw, 0) self.left.setLine(0, 0, 0, remaining_length) self.right.setLine(self.width, self.height, self.width, self.height - remaining_length) self.update() def toggle_anim(self, toggling): """Toggle the animation forward and backwards Parameters ---------- toggling: bool True for forward, False for backwards """ if toggling: self.anim.setDirection(QAbstractAnimation.Forward) else: self.anim.setDirection(QAbstractAnimation.Backward) self.anim.start() def paint(self, painter, style, widget=None): """Reimplemented from QGraphicsWdiget paint function. Draws the lines making up the box. """ painter.setPen(self.default_pen) for line in self.line_order: if line.length() > 1: painter.drawLine(line)
class StackedWidgetSlideOverAnimator(AbstractAnimator): m_direction: AnimationDirection m_coveredWidget: QWidget m_decorator: StackedWidgetSlideOverDecorator m_animation: QPropertyAnimation def __init__(self, _container, _widgetForSlide): super(StackedWidgetSlideOverAnimator, self).__init__(_container) self.m_direction = AnimationDirection.FromLeftToRight self.m_coveredWidget = _container.currentWidget() self.m_decorator = StackedWidgetSlideOverDecorator( _container, _widgetForGrab=_widgetForSlide) self.m_animation = QPropertyAnimation(self.m_decorator, b"pos") _container.installEventFilter(self) self.m_animation.setDuration(400) self.m_decorator.hide() def finished(): self.setAnimatedStopped() if self.isAnimatedForward(): _container.setCurrentWidget(_widgetForSlide) self.m_decorator.hide() self.m_animation.finished.connect(finished) def updateCoveredWidget(self): self.m_coveredWidget = self.stackedWidget().currentWidget() def setAnimationDirection(self, _direction): if self.m_direction != _direction: self.m_direction = _direction def animationDuration(self): return self.m_animation.duration() def animateForward(self): self.slideOverIn() def slideOverIn(self): if self.isAnimated() and self.isAnimatedForward(): return self.setAnimatedForward() self.m_decorator.grabWidget() startPos = QPoint() finalPos = QPoint() if self.m_direction == AnimationDirection.FromLeftToRight: startPos.setX(-1 * self.stackedWidget().width()) if self.m_direction == AnimationDirection.FromRightToLeft: startPos.setX(self.stackedWidget().width()) if self.m_direction == AnimationDirection.FromTopToBottom: startPos.setY(-1 * self.stackedWidget().height()) if self.m_direction == AnimationDirection.FromBottomToTop: startPos.setY(self.stackedWidget().height()) self.m_decorator.show() self.m_decorator.raise_() if self.m_animation.state() == QPropertyAnimation.Running: self.m_animation.pause() self.m_animation.setDirection(QPropertyAnimation.Backward) self.m_animation.resume() else: self.m_animation.setEasingCurve(QEasingCurve.InOutExpo) self.m_animation.setDirection(QPropertyAnimation.Forward) self.m_animation.setStartValue(startPos) self.m_animation.setEndValue(finalPos) self.m_animation.start() def animateBackward(self): self.slideOverOut() def slideOverOut(self): if self.isAnimated() and self.isAnimatedBackward(): return self.setAnimatedBackward() self.m_decorator.grabWidget() startPos = QPoint() finalPos = QPoint() if self.m_direction == AnimationDirection.FromLeftToRight: finalPos.setX(-1 * self.stackedWidget().width()) if self.m_direction == AnimationDirection.FromRightToLeft: finalPos.setX(self.stackedWidget().width()) if self.m_direction == AnimationDirection.FromTopToBottom: finalPos.setY(-1 * self.stackedWidget().height()) if self.m_direction == AnimationDirection.FromBottomToTop: finalPos.setY(self.stackedWidget().height()) if isinstance(self.stackedWidget(), type(self)): self.stackedWidget().setCurrentWidget(self.m_coveredWidget) self.m_decorator.show() self.m_decorator.raise_() if self.m_animation.state() == QPropertyAnimation.Running: self.m_animation.pause() self.m_animation.setDirection(QPropertyAnimation.Backward) self.m_animation.resume() else: self.m_animation.setEasingCurve(QEasingCurve.InOutExpo) self.m_animation.setDirection(QPropertyAnimation.Forward) self.m_animation.setStartValue(startPos) self.m_animation.setEndValue(finalPos) self.m_animation.start() def eventFilter(self, _object, _event): if _object == self.stackedWidget() and _event.type( ) == QEvent.Resize and self.m_decorator.isVisible(): self.m_decorator.grabWidget() return QWidget.eventFilter(self, _object, _event) def stackedWidget(self): return self.parent()
class MainWindow(QMainWindow): siteLoaded = pyqtSignal(object) def __init__(self, app): QMainWindow.__init__(self) self.app = app self.initGui() self.readSettings() self.dashboard.setExpanded(True) self.showDashboard() self.loadDatabase() self.loadClients() self.statusBar().showMessage("Ready") def initGui(self): self.installEventFilter(self) self.dashboard = Expander("Dashboard", ":/images/dashboard_normal.png", ":/images/dashboard_hover.png", ":/images/dashboard_selected.png") self.content = Expander("Clients", ":/images/clients_normal.png", ":/images/clients_hover.png", ":/images/clients_selected.png") self.settings = Expander("Settings", ":/images/settings_normal.png", ":/images/settings_hover.png", ":/images/settings_selected.png") self.setWindowTitle(QCoreApplication.applicationName() + " " + QCoreApplication.applicationVersion()) vbox = QVBoxLayout() vbox.addWidget(self.dashboard) vbox.addWidget(self.content) vbox.addWidget(self.settings) vbox.addStretch() content_box = QVBoxLayout() filter_label = QLabel("Filter") self.filter = QLineEdit() self.filter.textChanged.connect(self.filterChanged) self.client_list = QListWidget() self.client_list.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed) self.client_list.currentItemChanged.connect(self.clientChanged) content_box.addWidget(filter_label) content_box.addWidget(self.filter) content_box.addWidget(self.client_list) self.content.addLayout(content_box) scroll_content = QWidget() scroll_content.setLayout(vbox) scroll = QScrollArea() scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) scroll.setWidget(scroll_content) scroll.setWidgetResizable(True) scroll.setMaximumWidth(200) scroll.setMinimumWidth(200) self.navigationdock = QDockWidget("Navigation", self) self.navigationdock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.navigationdock.setWidget(scroll) self.navigationdock.setObjectName("Navigation") self.addDockWidget(Qt.LeftDockWidgetArea, self.navigationdock) self.showDock = FlatButton(":/images/edit_normal.png", ":/images/edit_hover.png") self.showDock.setToolTip("Show Navigation") self.statusBar().addPermanentWidget(self.showDock) self.dashboard.expanded.connect(self.dashboardExpanded) self.dashboard.clicked.connect(self.showDashboard) self.content.expanded.connect(self.contentExpanded) self.content.clicked.connect(self.showClient) self.settings.expanded.connect(self.settingsExpanded) self.settings.clicked.connect(self.showSettings) self.showDock.clicked.connect(self.showMenu) self.navigationdock.visibilityChanged.connect( self.dockVisibilityChanged) self.client = None self.client_editor = ClientEditor(self) def showDashboard(self): db = Dashboard() db.createSite.connect(self.addClient) self.setCentralWidget(db) def showClient(self): self.setCentralWidget(self.client_editor) def showSettings(self): s = SettingsEditor(self) self.setCentralWidget(s) def setCentralWidget(self, widget): old_widget = self.takeCentralWidget() super().setCentralWidget(widget) widget.show() def closeEvent(self, event): self.writeSettings() event.accept() def writeSettings(self): settings = QSettings(QSettings.IniFormat, QSettings.UserScope, QCoreApplication.organizationName(), QCoreApplication.applicationName()) settings.setValue("geometry", self.saveGeometry()) settings.setValue("state", self.saveState()) settings.setValue("databaseFile", self.databaseFile) settings.setValue("fontSize", str(self.fontSize)) def readSettings(self): settings = QSettings(QSettings.IniFormat, QSettings.UserScope, QCoreApplication.organizationName(), QCoreApplication.applicationName()) geometry = settings.value("geometry", QByteArray()) if geometry.isEmpty(): availableGeometry = QApplication.desktop().availableGeometry(self) self.resize(availableGeometry.width() / 3, availableGeometry.height() / 2) self.move(int(((availableGeometry.width() - self.width()) / 2)), int((availableGeometry.height() - self.height()) / 2)) else: self.restoreGeometry(geometry) self.restoreState(settings.value("state")) self.databaseFile = settings.value("databaseFile") if not self.databaseFile: self.databaseFile = "maria.json" fs = settings.value("fontSize") if not fs: fs = 10 self.fontSize = int(fs) font = QFont("Sans Serif", self.fontSize) self.app.setFont(font) def dashboardExpanded(self, value): if value: self.content.setExpanded(False) self.settings.setExpanded(False) def contentExpanded(self, value): if value: self.dashboard.setExpanded(False) self.settings.setExpanded(False) def settingsExpanded(self, value): if value: self.dashboard.setExpanded(False) self.content.setExpanded(False) def showMenu(self): self.navigationdock.setVisible(True) def dockVisibilityChanged(self, visible): self.showDock.setVisible(not visible) def animate(self, item): panel = self.centralWidget() self.list = item.tableWidget() self.row = item.row() # create a cell widget to get the right position in the table self.cellWidget = QWidget() self.cellWidget.setMaximumHeight(0) self.list.setCellWidget(self.row, 1, self.cellWidget) pos = self.cellWidget.mapTo(panel, QPoint(0, 0)) self.editor.setParent(panel) self.editor.move(pos) self.editor.resize(self.cellWidget.size()) self.editor.show() self.animation = QPropertyAnimation(self.editor, "geometry".encode("utf-8")) self.animation.setDuration(300) self.animation.setStartValue( QRect(pos.x(), pos.y(), self.cellWidget.size().width(), self.cellWidget.size().height())) self.animation.setEndValue( QRect(0, 0, panel.size().width(), panel.size().height())) self.animation.start() def editorClosed(self): pos = self.cellWidget.mapTo(self.centralWidget(), QPoint(0, 0)) # correct end values in case of resizing the window self.animation.setStartValue( QRect(pos.x(), pos.y(), self.cellWidget.size().width(), self.cellWidget.size().height())) self.animation.finished.connect(self.animationFineshedZoomOut) self.animation.setDirection(QAbstractAnimation.Backward) self.animation.start() def animationFineshedZoomOut(self): self.list.removeCellWidget(self.row, 1) del self.animation # in the case self.editor was a MenuEditor, we have to unregister it in the MenuList # should be refactored some day :-) list = self.centralWidget() if list is MenuEditor: list.unregisterMenuEditor() del self.editor self.editor = None if self.method_after_animation == "showDashboard": self.showDashboard() self.method_after_animation = "" elif self.method_after_animation == "showSettings": self.showSettings() if self.content_after_animation: self.previewSite(self.content_after_animation) self.content_after_animation = None def filterChanged(self): self.loadClients() def loadDatabase(self): self.db = TinyDB(self.databaseFile) self.clients = self.db.table('Clients') def updateClient(self): for i in range(self.client_list.count()): item = self.client_list.item(i) c = item.data(3) if c.doc_id == self.client.doc_id: item.setData(3, self.client) item.setText(self.client["name"]) break def loadClients(self): self.client_list.clear() filter = self.filter.text() a = [] for c in self.clients: if filter in c["name"]: a.append(c) s = sorted(a, key=namesort) for c in s: item = QListWidgetItem() item.setText(c["name"]) item.setData(3, c) self.client_list.addItem(item) self.client_list.setCurrentRow(0) def addClient(self): now = datetime.now() newclient = { "number": "", "name": "", "birthday_year": 1990, "birthday_month": 1, "birthday_day": 1, "profession": "", "address": "", "mobile": "", "email": "", "reason": "", "fiscal": "", "how": "", "first_contact_year": now.year, "first_contact_month": now.month, "first_contact_day": now.day, "notes": "" } self.clients.insert(newclient) self.showClient() self.loadClients() q = Query() self.client = self.clients.get(q.name == "") self.client["name"] = "" self.client_editor.reload() def clientChanged(self, item): if item: self.client = item.data(3) self.client_editor.reload() else: self.client = None self.client_editor.reload()
class NumberRing(BaseSudokuItem): loseFocus = pyqtSignal() keyPressed = pyqtSignal(str, bool) def __init__(self, parent=None): super().__init__(parent=parent) self.setVisible(False) self.cell_width = 24 self.cell_height = 24 self.cell_buttons = [] for i in range(10): if i == 0: cell_string = 'X' else: cell_string = str(i) btn = buttons.RingButton(0, 0, self.cell_width, self.cell_height, cell_string, parent=self) btn.buttonClicked.connect(self.send_button_press) self.cell_buttons.append(btn) self.radius = 54 # Set up the radius to be animated self.anim = QPropertyAnimation(self, b'radius') self.anim.setDuration(100) # Animation speed self.anim.setStartValue(0) for t in range(1, 10): self.anim.setKeyValueAt(t / 10, self.radius * t / 10) self.anim.setEndValue(self.radius) self.anim.finished.connect(self.finish_animation) self.setFlag(QGraphicsItem.ItemIsFocusable, True) self.setAcceptHoverEvents(True) self.freeze_buttons(True) self.scribbling = False def finish_animation(self): if self.radius == 0: self.setVisible(False) self.freeze_buttons(True) self.loseFocus.emit() else: self.freeze_buttons(False) if self.isUnderMouse(): self.set_buttons_transparent(False) else: self.set_buttons_transparent(True) # Toggle the animation to be play forward or backward def toggle_anim(self, toggling): self.freeze_buttons(True) if toggling: self.anim.setDirection(QAbstractAnimation.Forward) else: self.anim.setDirection(QAbstractAnimation.Backward) self.anim.start() def boundingRect(self): return QRectF(-5 - self.radius - self.cell_width / 2, -5 - self.radius - self.cell_height / 2, self.cell_width + self.radius * 2 + 10, self.cell_height + self.radius * 2 + 10) # Reimplemented paint def paint(self, painter, style, widget=None): pass def send_button_press(self, val): self.keyPressed.emit(val, self.scribbling) self.close_menu() def freeze_buttons(self, freeze): for btn in self.cell_buttons: btn.set_freeze(freeze) def focusOutEvent(self, event): if not any(btn.isUnderMouse() for btn in self.cell_buttons): self.toggle_anim(False) else: self.setFocus() def mousePressEvent(self, event): if not any(btn.isUnderMouse() for btn in self.cell_buttons): self.toggle_anim(False) else: self.setFocus() def close_menu(self): if not self.scribbling: self.toggle_anim(False) def keyPressEvent(self, event): if not event.isAutoRepeat(): if (event.key() == SCRIBBLE_KEY) and not self.scribbling: self.scribbling = True if event.key() == 88: txt = 'X' elif 49 <= event.key() <= 57: txt = str(event.key() - 48) else: txt = '' if txt: self.keyPressed.emit(txt, self.scribbling) if not self.scribbling: self.toggle_anim(False) self.clearFocus() def keyReleaseEvent(self, event): if not event.isAutoRepeat(): if event.key() == SCRIBBLE_KEY and self.scribbling: self.scribbling = False def hoverEnterEvent(self, event): self.set_buttons_transparent(False) def hoverLeaveEvent(self, event): self.set_buttons_transparent(True) def set_buttons_transparent(self, state): for btn in self.cell_buttons: btn.set_transparent(state) # Defining the length to be drawn as a pyqtProperty @pyqtProperty(float) def radius(self): return self._radius # Determine the length of the four lines to be drawn @radius.setter def radius(self, value): self._radius = value for i, btn in enumerate(self.cell_buttons): cell_x = value * np.sin(np.deg2rad( 360 / 10 * i)) - self.cell_width / 2 cell_y = -value * np.cos(np.deg2rad( 360 / 10 * i)) - self.cell_height / 2 btn.setX(cell_x) btn.setY(cell_y) self.update()
class ExpandAnimator(AbstractAnimator): m_decorator: ExpandDecorator m_animation: QPropertyAnimation m_expandRect: QRect def __init__(self, _widgetForFill): super(ExpandAnimator, self).__init__(_widgetForFill) self.m_decorator = ExpandDecorator(_widgetForFill) self.m_animation = QPropertyAnimation(self.m_decorator, b"_expandRect") _widgetForFill.installEventFilter(self) self.m_animation.setDuration(400) self.m_decorator.hide() def finished(): self.setAnimatedStopped() if self.isAnimatedBackward(): self.m_decorator.hide() self.m_animation.finished.connect(finished) def setExpandRect(self, _rect): self.m_expandRect = _rect self.m_decorator.setExpandRect(_rect) def setFillColor(self, _color): self.m_decorator.setFillColor(_color) def animationDuration(self): return self.m_animation.duration() def animateForward(self): self.expandIn() def expandIn(self): if self.isAnimated() and self.isAnimatedForward(): return self.setAnimatedForward() startExpandRect = self.m_expandRect finalExpandRect = self.widgetForFill().rect() self.m_decorator.resize(self.widgetForFill().size()) self.m_decorator.move(0, 0) self.m_decorator.grabExpandRect() self.m_decorator.show() self.m_decorator.raise_() if self.m_animation.state() == QPropertyAnimation.Running: self.m_animation.pause() self.m_animation.setDirection(QPropertyAnimation.Backward) self.m_animation.resume() else: self.m_animation.setEasingCurve(QEasingCurve.InOutQuart) self.m_animation.setDirection(QPropertyAnimation.Forward) self.m_animation.setStartValue(startExpandRect) self.m_animation.setEndValue(finalExpandRect) self.m_animation.start() def animateBackward(self): self.expandOut() def expandOut(self): if self.isAnimated() and self.isAnimatedBackward(): return self.setAnimatedBackward() startExpandRect = self.widgetForFill().rect() finalExpandRect = self.m_expandRect self.m_decorator.resize(self.widgetForFill().size()) self.m_decorator.move(0, 0) self.m_decorator.hide() self.m_decorator.grabExpandRect() self.m_decorator.show() self.m_decorator.raise_() if self.m_animation.state() == QPropertyAnimation.Running: self.m_animation.pause() self.m_animation.setDirection(QPropertyAnimation.Backward) self.m_animation.resume() else: self.m_animation.setEasingCurve(QEasingCurve.InOutQuart) self.m_animation.setDirection(QPropertyAnimation.Forward) self.m_animation.setStartValue(startExpandRect) self.m_animation.setEndValue(finalExpandRect) self.m_animation.start() def eventFilter(self, _object, _event): if _object == self.widgetForFill() and _event.type( ) == QEvent.Resize and self.m_decorator.isVisible(): self.m_decorator.resize(self.widgetForFill().size()) self.m_animation.setEndValue(self.widgetForFill().rect()) return QObject.eventFilter(self, _object, _event) def widgetForFill(self): return self.parent()
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()
class MessageLabel(QLabel): """ QLabel that a message, which is displayed through animation. """ def __init__(self, text, speed=20, delay=-1, parent=None): """ Does some text processing, and set up the animation to display the text Parameters ---------- text: str Text to be displayed speed : int The period at which a new character is printed The total time is calculated as length of text * speed 0 means instant display, like a regular QLabel. delay : int The number of garbage to be printed before printing the actual text parent: QWidget Pass into QLabel init method """ super().__init__(text, parent) self.setWordWrap(True) self.setContentsMargins(0, 0, 0, 0) # Text processing # Make sure the garbage does not exceed the length of actual text self.actual_text = text self.shown_text = '' if delay >= 0: self.delay = min(delay, len(self.actual_text)) else: self.delay = len(self.actual_text) # Find out where the new paragraphs are so that it is retained self.splitpoints = [] current_point = 0 line_splits = self.actual_text.split('\n') for line in line_splits: current_point += len(line) self.splitpoints.append(current_point) current_point += 1 # Set up the shown text length to be animated self.shown_length = 0 self.anim = QPropertyAnimation(self, b'shown_length') self.anim.setDuration(len(self.actual_text) * speed) self.anim.setStartValue(0) self.anim.setEndValue(len(self.actual_text) + self.delay) self.setStyleSheet(""" color: rgb(0, 255, 0); """) @pyqtProperty(int) def shown_length(self): """ int : The value for the animation When the value is set, the text to be printed is generated accordingly. It determines whether actual text is to be printed, and retains the paragraphs when printing garbage. """ return self._shown_length @shown_length.setter def shown_length(self, value): self._shown_length = value if value < self.delay: # All printed text should be garbage garbage = [ chr(num) for num in [random.choice(range(33, 127)) for _ in range(value)] ] # Retain the paragraphs for num in self.splitpoints[:-1]: if num < value: garbage[num] = '\n' self.setText(''.join(garbage)) else: # Printed text contain some actual text garbage = [ chr(num) for num in [ random.choice(range(33, 127)) for _ in range( min( len(self.actual_text) + self.delay - value, self.delay)) ] ] # Retain the paragraphs, but offset by the number of actual text non_garbage = value - self.delay for num in self.splitpoints[:-1]: if num - non_garbage > 0 and num < value: garbage[num - non_garbage] = '\n' self.setText(self.actual_text[:value - self.delay] + ''.join(garbage)) def toggle_anim(self, toggling): """ Toggle the animation to be play forward or backward Parameters ---------- toggling: bool True for forward, False for backward """ if toggling: self.anim.setDirection(QAbstractAnimation.Forward) else: self.anim.setDirection(QAbstractAnimation.Backward) self.anim.start()