def animate_to(t, item, x, y, angle): # The QGraphicsItemAnimation class is used to # animate an item in specific ways # FIXME QGraphicsItemAnimation class is no longer supported. animation = QGraphicsItemAnimation() # You create a timeline (in this case, it is 1 second long timeline = QTimeLine(1000) # And it has 100 steps timeline.setFrameRange(0, 100) # I want that, at time t, the item be at point x,y animation.setPosAt(t, QPointF(x, y)) # And it should be rotated at angle "angle" animation.setRotationAt(t, angle) # It should animate this specific item animation.setItem(item) # And the whole animation is this long, and has # this many steps as I set in timeline. animation.setTimeLine(timeline) # Here is the animation, use it. return animation
class CCountUp(QLabel): def __init__(self, *args, **kwargs): super(CCountUp, self).__init__(*args, **kwargs) self.isFloat = False # 是否是小数 font = self.font() or QFont() font.setBold(True) self.setFont(font) self.timeline = QTimeLine(6000, self) self.timeline.setEasingCurve(QEasingCurve.OutExpo) self.timeline.frameChanged.connect(self.onFrameChanged) def pause(self): """暂停 """ self.timeline.setPaused(True) def resume(self): """继续 """ self.timeline.resume() def isPaused(self): """是否暂停 """ return self.timeline.state() == QTimeLine.Paused def reset(self): """重置 """ self.timeline.stop() self.isFloat = False # 是否是小数 self.setText('0') def onFrameChanged(self, value): if self.isFloat: value = round(value / 100.0 + 0.00001, 2) value = str(format(value, ',')) self.setText(value + '0' if value.endswith('.0') else value) def setDuration(self, duration): """设置动画持续时间 :param duration: """ self.timeline.setDuration(duration) def setNum(self, number): """设置数字 :param number: int or float """ if isinstance(number, int): self.isFloat = False self.timeline.setFrameRange(0, number) elif isinstance(number, float): self.isFloat = True self.timeline.setFrameRange(0, number * 100) self.timeline.stop() self.setText('0') self.timeline.start()
class TimedProgressBar(QProgressBar): """A QProgressBar showing a certain time elapse.""" hideOnTimeout = True def __init__(self, parent=None): super(TimedProgressBar, self).__init__(parent, minimum=0, maximum=100) self._timeline = QTimeLine(updateInterval=100, frameChanged=self.setValue) self._timeline.setFrameRange(0, 100) self._hideTimer = QTimer(timeout=self._done, singleShot=True, interval=3000) def start(self, total, elapsed=0.0): """Starts showing progress. total is the number of seconds (maybe float) the timeline will last, elapsed (defaulting to 0) is the value to start with. """ self._hideTimer.stop() self._timeline.stop() self._timeline.setDuration(total * 1000) self._timeline.setCurrentTime(elapsed * 1000) self.setValue(self._timeline.currentFrame()) self._timeline.resume() if self.hideOnTimeout: self.show() def stop(self, showFinished=True): """Ends the progress display. If showFinished is True (the default), 100% is shown for a few seconds and then the progress is reset. The progressbar is hidden if the hideOnTimeout attribute is True. """ self._hideTimer.stop() self._timeline.stop() if showFinished: self.setValue(100) self._hideTimer.start() else: self._done() def _done(self): if self.hideOnTimeout: self.hide() self.reset()
class MyView(QGraphicsView): def __init__(self): super().__init__() self.initView() self.setupScene() self.setupAnimation() self.setGeometry(300, 150, 250, 250) def initView(self): self.setWindowTitle("Progress meter") self.setRenderHint(QPainter.Antialiasing) policy = Qt.ScrollBarAlwaysOff self.setVerticalScrollBarPolicy(policy) self.setHorizontalScrollBarPolicy(policy) self.setBackgroundBrush(self.palette().window()) self.pm = ProgressMeter(self) self.pm.setPos(55, 55) def setupScene(self): self.scene = QGraphicsScene(self) self.scene.setSceneRect(0, 0, 250, 250) self.scene.addItem(self.pm) self.setScene(self.scene) def setupAnimation(self): self.timer = QTimeLine() self.timer.setLoopCount(0) self.timer.setFrameRange(0, 100) self.timer.frameChanged[int].connect(self.doStep) self.timer.start() def doStep(self, i): if not self.pm.increment(): self.timer.stop() self.pm.update()
class AnimationView(QWidget): """docstring for AnimationView""" frameIndex = 0 frameCount = 0 def __init__(self, bmpFrames): super(AnimationView, self).__init__() self.setFixedSize(frameWidth,frameHeight) self.bmpSize =64 if bmpFrames : self.bmpFrames = bmpFrames self.frameCount = len(bmpFrames) self.bmpSize = bmpFrames[0].width() else : self.initDefaultFrames() def initDefaultFrames(self): self.bmpFrames = [] defaultImage = QImage("..\\resource\\default.bmp") imageWidth = defaultImage.width() imageHeight = defaultImage.height() #判断各幀图片是否是横向排列 isHorizontal = min(imageWidth,imageHeight) == imageHeight #计算幀数 self.frameCount = imageWidth//frameWidth if isHorizontal else imageHeight//frameHeight for i in range(0,self.frameCount): pixmap = QPixmap(defaultImage.copy(i*frameWidth if isHorizontal else 0, 0 if isHorizontal else i*frameHeight,frameWidth,frameHeight)) eliminateBackgroundColor(pixmap) self.bmpFrames.append(pixmap) def createAnimation(self): self.timeLine = QTimeLine(10*1000) self.timeLine.setFrameRange(0,60//(4/self.frameCount)) self.timeLine.frameChanged.connect(self.refreshFrameIndex) self.timeLine.setLoopCount(0) self.timeLine.setCurveShape(3) self.timeLine.start() def refreshFrameIndex(self,currFrame): self.update() self.frameIndex = (self.frameIndex+1) % self.frameCount def paintEvent(self,event): painter = QPainter(self) painter.drawPixmap((frameWidth-self.bmpSize)//2,(frameHeight-self.bmpSize)//2,self.bmpFrames[self.frameIndex])
def __init__(self, size, color): super().__init__() self._loading_angle = 0 self.width = 0 self.color = color self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_TranslucentBackground) self.setStyleSheet("background:transparent;") self.resize(size, size) self.center() self.initUI() timeline = QTimeLine(3000, self) timeline.setFrameRange(360, 0) timeline.frameChanged.connect(self.setLoadingAngle) timeline.start()
def __init__(self, size, color, side): super().__init__() self._next_step = 0 self.width = 0 self.color = color self.side = side self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_TranslucentBackground) self.setStyleSheet("background:transparent;") self.resize(size, size) self.center() self.initUI() timeline = QTimeLine(3000, self) timeline.setFrameRange(size, 0) timeline.frameChanged.connect(self.setNextStep) timeline.start()
class MainWindow(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.setWindowTitle("Screen and Gaze Capture") grid = QGridLayout() layout_frame = QFrame() layout_frame.setLayout(grid) self.setCentralWidget(layout_frame) self.tracker_label = QLabel("No eye tracker connected.") grid.addWidget(self.tracker_label, 0, 0) connect_button = QPushButton("Connect to Eye Tracker") connect_button.pressed.connect(connect_to_tracker) grid.addWidget(connect_button, 1, 0) calibrate_button = QPushButton("Calibrate Eye Tracker") calibrate_button.pressed.connect(calibrate) grid.addWidget(calibrate_button, 2, 0) self.record_button = QPushButton("Record Screen and Gaze") self.record_button.pressed.connect(capture_screen) grid.addWidget(self.record_button, 3, 0) self.author_vid_button = QPushButton("Write Author Video") self.author_vid_button.pressed.connect(create_video) grid.addWidget(self.author_vid_button, 4, 0) self.author_vid_button.setEnabled(False) self.timeline = QTimeLine() self.timeline.setCurveShape(QTimeLine.LinearCurve) self.timeline.setDuration(360000) #Totsl video lengthn in milliseconds self.timeline.setFrameRange( 0, 7500) #Maximum Frames in this video as 30 fps self.timeline.frameChanged.connect(update_frame) self.timeline.setUpdateInterval(10) #Rate of Frame Capture def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: app.closeAllWindows()
class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.resize(600, 600) self.label = QLabel('Hello PyQt5', self) self.label.move(-100, 100) self.timeline = QTimeLine(5000, self) self.timeline.setFrameRange(0, 700) self.timeline.frameChanged.connect(self.set_frame_func) self.timeline.stateChanged.connect( lambda: print(self.timeline.state())) # 1 self.timeline.setLoopCount(0) self.start_btn = QPushButton('Start', self) self.stop_btn = QPushButton('Stop', self) self.pause_btn = QPushButton('Pause', self) self.resume_btn = QPushButton('Resume', self) self.start_btn.clicked.connect(self.timeline.start) # 2 self.stop_btn.clicked.connect(self.timeline.stop) self.pause_btn.clicked.connect(lambda: self.timeline.setPaused(True)) self.resume_btn.clicked.connect(self.timeline.resume) self.h_layout = QHBoxLayout() self.v_layout = QVBoxLayout() self.h_layout.addWidget(self.start_btn) self.h_layout.addWidget(self.stop_btn) self.h_layout.addWidget(self.pause_btn) self.h_layout.addWidget(self.resume_btn) self.v_layout.addStretch(1) self.v_layout.addLayout(self.h_layout) self.setLayout(self.v_layout) def set_frame_func(self, frame): self.label.move(-100 + frame, 100)
class Widget(Ui_CollectionsWidget, QWidget, ScreenWidget): name = "collectionSelection" def __init__(self): QWidget.__init__(self) self.setupUi(self) self.collections = None self.current_item = None self.last_item = None self.collectionList.itemClicked.connect(self.openItem) self.collectionList.currentItemChanged.connect(self.itemChanged) def fillCollections(self): self.collectionList.clear() selected = None for index, collection in enumerate(self.collections): self.addItem(collection) if ctx.installData.autoCollection == collection: selected = index if not selected: selected = 0 self.current_item = self.collectionList.item(selected) self.last_item = self.current_item self.collectionList.setCurrentRow(selected) def shown(self): self.collections = ctx.collections self.fillCollections() ctx.mainScreen.disableNext() if self.current_item: self.openItem(self.current_item) else: self.openItem(self.collectionList.item(0)) self.check() def execute(self): ctx.installData.autoCollection = self.collectionList.itemWidget( self.current_item).collection return True def check(self): if self.current_item: ctx.mainScreen.enableNext() else: ctx.mainScreen.disableNext() def itemChanged(self, current, previous): self.current_item = current self.check() def addItem(self, collection): item = QListWidgetItem(self.collectionList) item.setSizeHint(QSize(36, CLOSED_SIZE)) self.collectionList.addItem(item) self.collectionList.setItemWidget( item, CollectionItem(self, collection, item)) def openItem(self, item): if item == self.last_item: return if self.last_item: self.closeItem(self.last_item) self.animation = QTimeLine(ANIMATE_TIME, self) self.animation.setFrameRange(36, EXPANDED_SIZE) self.animation.frameChanged.connect( lambda x: item.setSizeHint(QSize(32, x))) self.animation.start() self.last_item = item self.animation.finished.connect( lambda: self.collectionList.setCurrentItem(item)) def closeItem(self, item): animation = QTimeLine(ANIMATE_TIME, self) animation.setFrameRange(146, CLOSED_SIZE) animation.frameChanged.connect( lambda x: item.setSizeHint(QSize(32, x))) animation.start()
def closeItem(self, item): animation = QTimeLine(ANIMATE_TIME, self) animation.setFrameRange(146, CLOSED_SIZE) animation.frameChanged.connect( lambda x: item.setSizeHint(QSize(32, x))) animation.start()
def closeItem(self, item): animation = QTimeLine(ANIMATE_TIME, self) animation.setFrameRange(146, CLOSED_SIZE) animation.frameChanged.connect(lambda x: item.setSizeHint(QSize(32, x))) animation.start()
class Widget(Ui_CollectionsWidget, QWidget, ScreenWidget): name = "collectionSelection" def __init__(self): QWidget.__init__(self) self.setupUi(self) self.collections = None self.current_item = None self.last_item = None self.collectionList.itemClicked.connect(self.openItem) self.collectionList.currentItemChanged.connect(self.itemChanged) def fillCollections(self): self.collectionList.clear() selected = None for index, collection in enumerate(self.collections): self.addItem(collection) if ctx.installData.autoCollection == collection: selected = index if not selected: selected = 0 self.current_item = self.collectionList.item(selected) self.last_item = self.current_item self.collectionList.setCurrentRow(selected) def shown(self): self.collections = ctx.collections self.fillCollections() ctx.mainScreen.disableNext() if self.current_item: self.openItem(self.current_item) else: self.openItem(self.collectionList.item(0)) self.check() def execute(self): ctx.installData.autoCollection = self.collectionList.itemWidget(self.current_item).collection return True def check(self): if self.current_item: ctx.mainScreen.enableNext() else: ctx.mainScreen.disableNext() def itemChanged(self, current, previous): self.current_item = current self.check() def addItem(self, collection): item = QListWidgetItem(self.collectionList) item.setSizeHint(QSize(36, CLOSED_SIZE)) self.collectionList.addItem(item) self.collectionList.setItemWidget(item, CollectionItem(self, collection, item)) def openItem(self, item): if item == self.last_item: return if self.last_item: self.closeItem(self.last_item) self.animation = QTimeLine(ANIMATE_TIME, self) self.animation.setFrameRange(36, EXPANDED_SIZE) self.animation.frameChanged.connect(lambda x: item.setSizeHint(QSize(32, x))) self.animation.start() self.last_item = item self.animation.finished.connect(lambda: self.collectionList.setCurrentItem(item)) def closeItem(self, item): animation = QTimeLine(ANIMATE_TIME, self) animation.setFrameRange(146, CLOSED_SIZE) animation.frameChanged.connect(lambda x: item.setSizeHint(QSize(32, x))) animation.start()
class Window(QWidget): def __init__(self, *args, **kwargs): super(Window, self).__init__(*args, **kwargs) self.resize(600, 200) layout = QVBoxLayout(self) # 配置全局属性(也可以通过start方法里的参数配置单独的属性) CLoadingBar.config(height=2, direction=CLoadingBar.TOP, color='#2d8cf0', failedColor='#ed4014') # 子控件顶部进度 self.widget1 = QWidget(self) layout.addWidget(self.widget1) CLoadingBar.start(self.widget1, color='#19be6b', failedColor='#ff9900') widget = QWidget(self) layoutc = QHBoxLayout(widget) layoutc.addWidget(QPushButton('开始', self, clicked=self.doStart)) layoutc.addWidget(QPushButton('结束', self, clicked=self.doFinish)) layoutc.addWidget(QPushButton('错误', self, clicked=self.doError)) layout.addWidget(widget) # 子控件底部进度 self.widget2 = QWidget(self) layout.addWidget(self.widget2) CLoadingBar.start(self.widget2, direction=CLoadingBar.BOTTOM, height=6) # 模拟进度 self.updateTimer = QTimeLine(10000, self, frameChanged=self.doUpdateProgress) self.updateTimer.setFrameRange(0, 100) # 设置数字变化曲线模拟进度的不规则变化 self.updateTimer.setCurveShape(QTimeLine.EaseInOutCurve) def doStart(self): """模拟开始 """ self.updateTimer.stop() self.updateTimer.start() def doFinish(self): """模拟结束 """ self.updateTimer.stop() CLoadingBar.finish(self.widget1) CLoadingBar.finish(self.widget2) def doError(self): """模拟出错 """ self.updateTimer.stop() CLoadingBar.error(self.widget1) CLoadingBar.error(self.widget2) def doUpdateProgress(self, value): """模拟进度值变化 :param value: """ CLoadingBar.update(self.widget1, value) CLoadingBar.update(self.widget2, value) if value == 100: self.updateTimer.stop() self.doFinish()
r = 0 for i in range(self.m_digits): r = c % 10 c = c // 10 rect = QRectF(x + self.X_OFFSET, self.Y_OFFSET, w, h) self.m_svg.render(p, "d{}".format(r), rect) x -= w p.restore() def inicio(self): import analogwidgets_rc self.m_value = 0 self.m_digits = 4 self.m_svg = None self.setDigitsFile(":/default/resources/train_digits.svg") if __name__ == '__main__': import sys from PyQt5.QtCore import QTimeLine app = QApplication(sys.argv) w = PyCounter() timeline = QTimeLine(10000) timeline.setFrameRange(0, 9999) timeline.frameChanged.connect(w.setValue) timeline.start() w.show() sys.exit(app.exec_())
class E5AnimatedWidget(QWidget): """ Class implementing an animated widget. """ DirectionDown = 0 DirectionUp = 1 def __init__(self, direction=DirectionDown, duration=300, parent=None): """ Constructor @param direction direction of the animation @type int (one of DirectionDown or DirectionUp) @param duration duration of the animation @type int @param parent reference to the parent widget @type QWidget """ super(E5AnimatedWidget, self).__init__(parent) self.__direction = direction self.__stepHeight = 0.0 self.__stepY = 0.0 self.__startY = 0 self.__widget = QWidget(self) self.__timeline = QTimeLine(duration) self.__timeline.setFrameRange(0, 100) self.__timeline.frameChanged.connect(self.__animateFrame) self.setMaximumHeight(0) def widget(self): """ Public method to get a reference to the animated widget. @return reference to the animated widget @rtype QWidget """ return self.__widget @pyqtSlot() def startAnimation(self): """ Public slot to start the animation. """ if self.__timeline.state() == QTimeLine.Running: return shown = 0 hidden = 0 if self.__direction == self.DirectionDown: shown = 0 hidden = -self.__widget.height() self.__widget.move(QPoint(self.__widget.pos().x(), hidden)) self.__stepY = (hidden - shown) / 100.0 self.__startY = hidden self.__stepHeight = self.__widget.height() / 100.0 self.__timeline.setDirection(QTimeLine.Forward) self.__timeline.start() @pyqtSlot(int) def __animateFrame(self, frame): """ Private slot to animate the next frame. @param frame frame number @type int """ self.setFixedHeight(frame * self.__stepHeight) self.__widget.move(self.pos().x(), self.__startY - frame * self.__stepY) @pyqtSlot() def hide(self): """ Public slot to hide the animated widget. """ if self.__timeline.state() == QTimeLine.Running: return self.__timeline.setDirection(QTimeLine.Backward) self.__timeline.finished.connect(self.close) self.__timeline.start() p = self.parentWidget() if p is not None: p.setFocus() def resizeEvent(self, evt): """ Protected method to handle a resize event. @param evt reference to the event object @type QResizeEvent """ if evt.size().width() != self.__widget.width(): self.__widget.resize(evt.size().width(), self.__widget.height()) super(E5AnimatedWidget, self).resizeEvent(evt)
class Widget(QWidget, ScreenWidget): name = "accounts" def __init__(self): super(Widget, self).__init__() self.ui = Ui_SetupUsersWidget() self.ui.setupUi(self) self.edititemindex = None self.time_line = QTimeLine(400, self) self.time_line.setFrameRange(0, 220) self.time_line.frameChanged[int].connect(self.animate) self.ui.scrollArea.setFixedHeight(0) # User Icons self.normal_user_icon = QPixmap(":/gui/pics/users.png") self.super_user_icon = QPixmap(":/gui/pics/users.png") # Set disabled the create Button self.ui.createButton.setEnabled(False) # Connections self.ui.pass1.textChanged[str].connect(self.slotTextChanged) self.ui.pass2.textChanged[str].connect(self.slotTextChanged) self.ui.username.textChanged[str].connect(self.slotTextChanged) self.ui.realname.textChanged[str].connect(self.slotTextChanged) self.ui.username.textEdited[str].connect(self.slotUserNameChanged) self.ui.realname.textEdited[str].connect(self.slotRealNameChanged) self.ui.userID.valueChanged[int].connect(self.slotTextChanged) self.ui.userIDCheck.stateChanged[int].connect(self.slotuserIDCheck) self.ui.createButton.clicked.connect(self.slotCreateUser) self.ui.cancelButton.clicked.connect(self.resetWidgets) self.ui.deleteButton.clicked.connect(self.slotDeleteUser) self.ui.editButton.clicked.connect(self.slotEditUser) self.ui.addMoreUsers.clicked.connect(self.slotAdvanced) self.ui.userList.itemDoubleClicked[QListWidgetItem].connect(self.slotEditUser) self.ui.pass2.returnPressed.connect(self.slotReturnPressed) # focusInEvent is not a signal # self.ui.pass1.focusInEvent[QFocusEvent].connect(self.checkCapsLock) # self.ui.pass2.focusInEvent[QFocusEvent].connect(self.checkCapsLock) # self.ui.username.focusInEvent[QFocusEvent].connect(self.checkCapsLock) # self.ui.realname.focusInEvent[QFocusEvent].connect(self.checkCapsLock) ctx.installData.users = [] ctx.installData.autoLoginUser = None self.user_name_changed = False self.used_ids = [] def eventFilter(self, obj, event): if event.type() == QEvent.FocusIn: # even.type() ==... if obj == self.ui.pass1 or obj == self.ui.pass2 or \ obj == self.ui.username or obj == self.ui.realname: self.checkCapsLock() def shown(self): self.ui.cancelButton.hide() self.ui.realname.setFocus() if len(yali.users.PENDING_USERS) > 0 and self.ui.userList.count() == 0: for u in yali.users.PENDING_USERS: pix = self.normal_user_icon if "wheel" in u.groups: pix = self.super_user_icon UserItem(self.ui.userList, pix, user=u) self.ui.autoLogin.addItem(u.username) if len(yali.users.PENDING_USERS) == 1: self.slotEditUser(self.ui.userList.item(0)) elif len(yali.users.PENDING_USERS) > 1: self.ui.addMoreUsers.setChecked(True) self.checkUsers() self.checkCapsLock() def backCheck(self): self.refill() self.ui.cancelButton.hide() return True def refill(self): # reset and fill PENDING_USERS yali.users.reset_pending_users() for index in range(self.ui.userList.count()): user = self.ui.userList.item(index).getUser() ctx.installData.users.append(user) yali.users.PENDING_USERS.append(user) def execute(self): if self.checkUsers(): ctx.installData.autoLoginUser = str(self.ui.autoLogin.currentText()) if self.ui.createButton.text() == _("General", "Update"): return self.slotCreateUser() return True if not self.slotCreateUser(): ctx.mainScreen.step_increment = 0 return True self.refill() ctx.interface.informationWindow.hide() ctx.installData.autoLoginUser = str(self.ui.autoLogin.currentText()) return True def setCapsLockIcon(self, child): if type(child) == QLineEdit: if pardus.xorg.capslock.isOn(): child.setStyleSheet("""QLineEdit { background-image: url(:/gui/pics/caps.png); background-repeat: no-repeat; background-position: right; padding-right: 35px; }""") else: child.setStyleSheet("""QLineEdit { background-image: none; padding-right: 0px; }""") def checkCapsLock(self): for child in self.ui.groupBox.children(): self.setCapsLockIcon(child) for child in self.ui.groupBox_2.children(): self.setCapsLockIcon(child) def keyReleaseEvent(self, e): self.checkCapsLock() def showError(self, message): ctx.interface.informationWindow.update(message, type="error") ctx.mainScreen.disableNext() def animate(self, value): self.ui.scrollArea.setFixedHeight(int(value)) self.ui.frame.setMinimumHeight(250) if self.ui.scrollArea.height() == 0: self.ui.scrollArea.hide() else: self.ui.scrollArea.show() if self.ui.scrollArea.height() == 220: self.time_line.setDirection(1) self.ui.frame.setMinimumHeight(420) if self.ui.scrollArea.height() == 0: self.time_line.setDirection(0) def slotuserIDCheck(self, state): if state: self.ui.userID.setEnabled(True) else: self.ui.userID.setEnabled(False) def slotAdvanced(self): icon_path = None if self.ui.scrollArea.isVisible(): icon_path = ":/gui/pics/expand.png" self.time_line.start() else: self.ui.scrollArea.show() icon_path = ":/gui/pics/collapse.png" self.time_line.start() icon = QIcon() icon.addPixmap(QPixmap(icon_path), QIcon.Normal, QIcon.Off) self.ui.addMoreUsers.setIcon(icon) self.checkUsers() def slotTextChanged(self): username = str(self.ui.username.text()) realname = unicode(self.ui.realname.text()) password = unicode(self.ui.pass1.text()) password_confirm = unicode(self.ui.pass2.text()) if not password == '' and (password.lower() == username.lower() or password.lower() == realname.lower()): self.showError(_("General", 'Don\'t use your user name or name as a password')) return elif password_confirm != password and password_confirm: self.showError(_("General", 'Passwords do not match')) return elif len(password) == len(password_confirm) and len(password_confirm) < 4 and not password =='': self.showError(_("General", 'Password is too short')) return else: ctx.interface.informationWindow.hide() if self.ui.username.text() and password and password_confirm: self.ui.createButton.setEnabled(True) if not self.ui.addMoreUsers.isChecked(): ctx.mainScreen.enableNext() ctx.mainScreen.enableBack() else: self.ui.createButton.setEnabled(False) if not self.ui.addMoreUsers.isChecked(): ctx.mainScreen.disableNext() def currentUsers(self): users = [] for index in range(self.ui.userList.count()): users.append(self.ui.userList.item(index).getUser().username) return users def slotUserNameChanged(self): self.user_name_changed = True def slotRealNameChanged(self): if not self.user_name_changed: used_users = yali.users.get_users() used_users.extend(self.currentUsers()) self.ui.username.setText(yali.users.nick_guess(self.ui.realname.text(), used_users)) def slotCreateUser(self): user = yali.users.User() user.username = str(self.ui.username.text()) # ignore last character. see bug #887 user.realname = unicode(self.ui.realname.text()) user.passwd = unicode(self.ui.pass1.text()) user.groups = ["users", "pnp", "disk", "audio", "video", "power", "dialout", "lp", "lpadmin", "cdrom", "floppy"] pix = self.normal_user_icon if self.ui.admin.isChecked(): user.groups.append("wheel") pix = self.super_user_icon user.no_password = self.ui.noPass.isChecked() # check user validity if user.exists() or (user.username in self.currentUsers() and self.edititemindex == None): self.showError(_("General", "This user name is already taken, please choose another one.")) return False elif not user.usernameIsValid(): # FIXME: Mention about what are the invalid characters! self.showError(_("General", "The user name contains invalid characters.")) return False elif not user.realnameIsValid(): self.showError(_("General", "The real name contains invalid characters.")) return False # Dont check in edit mode if self.ui.addMoreUsers.isChecked() and self.ui.userIDCheck.isChecked(): uid = self.ui.userID.value() if self.edititemindex == None: if uid in self.used_ids: self.showError(_("General", 'User ID used before, choose another one!')) return False self.used_ids.append(uid) user.uid = uid self.ui.createButton.setText(_("General", "Add")) self.ui.cancelButton.hide() update_item = None try: self.ui.userList.takeItem(self.edititemindex) self.ui.autoLogin.removeItem(self.edititemindex + 1) except: update_item = self.edititemindex # nothing wrong. just adding a new user... pass item = UserItem(self.ui.userList, pix, user=user) # add user to auto-login list. self.ui.autoLogin.addItem(user.username) if update_item: self.ui.autoLogin.setCurrentIndex(self.ui.autoLogin.count()) # clear form self.resetWidgets() ctx.logger.debug("slotCreateUser :: user (%s) '%s (%s)' added/updated" % (user.uid, user.realname, user.username)) ctx.logger.debug("slotCreateUser :: user groups are %s" % str(','.join(user.groups))) # give focus to realname widget for a new user. #3280 #self.ui.realname.setFocus() self.checkUsers() self.user_name_changed = False self.refill() return True def slotDeleteUser(self): if self.ui.userList.currentRow() == self.edititemindex: self.resetWidgets() self.ui.autoLogin.setCurrentIndex(0) _cur = self.ui.userList.currentRow() item = self.ui.userList.item(_cur).getUser() if item.uid in self.used_ids: self.used_ids.remove(item.uid) self.ui.userList.takeItem(_cur) self.ui.autoLogin.removeItem(_cur + 1) self.ui.createButton.setText(_("General", "Add")) icon = QIcon() icon.addPixmap(QPixmap(":/gui/pics/user-group-new.png"), QIcon.Normal, QIcon.Off) self.ui.createButton.setIcon(icon) self.ui.cancelButton.hide() self.checkUsers() def slotEditUser(self, item=None): if not item: item = self.ui.userList.currentItem() self.ui.userList.setCurrentItem(item) user = item.getUser() if user.uid > -1: self.ui.userIDCheck.setChecked(True) self.ui.userID.setValue(user.uid) self.ui.username.setText(user.username) self.ui.realname.setText(user.realname) self.ui.pass1.setText(user.passwd) self.ui.pass2.setText(user.passwd) if "wheel" in user.groups: self.ui.admin.setChecked(True) else: self.ui.admin.setChecked(False) self.ui.noPass.setChecked(user.no_password) self.edititemindex = self.ui.userList.currentRow() self.ui.createButton.setText(_("General", "Update")) icon = QIcon() icon.addPixmap(QPixmap(":/gui/pics/tick.png"), QIcon.Normal, QIcon.Off) self.ui.createButton.setIcon(icon) self.ui.cancelButton.setVisible(self.ui.createButton.isVisible()) def checkUserFields(self): username = unicode(self.ui.username.text()) realname = unicode(self.ui.realname.text()) password = unicode(self.ui.pass1.text()) password_confirm = unicode(self.ui.pass2.text()) if username and realname and password and password_confirm and \ (password == password_confirm) and \ (password.lower() != username.lower() and password.lower() != realname.lower()): return True else: return False def checkUsers(self): if self.ui.userList.count() > 0: self.ui.userList.setCurrentRow(0) self.ui.deleteButton.setEnabled(True) self.ui.editButton.setEnabled(True) self.ui.autoLogin.setEnabled(True) ctx.mainScreen.enableNext() ctx.mainScreen.enableBack() return True else: if self.checkUserFields(): ctx.mainScreen.enableNext() else: ctx.mainScreen.disableNext() # there is no user in list so noting to delete self.ui.deleteButton.setEnabled(False) self.ui.editButton.setEnabled(False) self.ui.autoLogin.setEnabled(False) return False def resetWidgets(self): # clear all self.edititemindex = None self.ui.username.clear() self.ui.realname.clear() self.ui.pass1.clear() self.ui.pass2.clear() self.ui.admin.setChecked(False) self.ui.noPass.setChecked(False) self.ui.userIDCheck.setChecked(False) self.ui.createButton.setEnabled(False) if self.ui.cancelButton.isVisible(): self.ui.cancelButton.setHidden(self.sender() == self.ui.cancelButton) self.checkUsers() self.ui.createButton.setText(_("General", "Add")) icon = QIcon() icon.addPixmap(QPixmap(":/gui/pics/user-group-new.png"), QIcon.Normal, QIcon.Off) self.ui.createButton.setIcon(icon) def slotReturnPressed(self): if self.ui.createButton.isEnabled() and self.ui.addMoreUsers.isChecked(): self.slotCreateUser()
class QPageWidget(QScrollArea): """ The QPageWidget provides a stack widget with animated page transitions. """ #Emits currentChanged = pyqtSignal() def __init__(self, parent=None, direction="ltr", rtf=False): """ Creates a new QPageWidget on given parent object. parent: QWidget parent direction: "ltr" -> Left To Right "ttb" -> Top To Bottom rtf: Return to first, if its True it flips to the first page when next page requested at the last page """ # First initialize, QPageWidget is based on QScrollArea QScrollArea.__init__(self, parent) # Properties for QScrollArea self.setFrameShape(QFrame.NoFrame) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setWidgetResizable(True) # Main widget, which stores all Pages in it self.widget = QWidget(self) # Layout based on QBoxLayout which supports Vertical or Horizontal layout if direction == "ltr": self.layout = QBoxLayout(QBoxLayout.LeftToRight, self.widget) self.__scrollBar = self.horizontalScrollBar() self.__base_value = self.width else: self.layout = QBoxLayout(QBoxLayout.TopToBottom, self.widget) self.__scrollBar = self.verticalScrollBar() self.__base_value = self.height self.layout.setSpacing(0) #qboxlayout setmargin özelliği yok #self.layout.setMargin(0) # Return to first self.__return_to_first = rtf # TMP_PAGE, its using as last page in stack # A workaround for a QScrollArea bug self.__tmp_page = Page(QWidget(self.widget)) self.__pages = [self.__tmp_page] self.__current = 0 self.__last = 0 # Set main widget self.setWidget(self.widget) # Animation TimeLine self.__timeline = QTimeLine() self.__timeline.setUpdateInterval(2) # Updates scrollbar position when frame changed self.__timeline.frameChanged.connect( lambda x: self.__scrollBar.setValue(x)) # End of the animation self.__timeline.finished.connect(self._animateFinished) # Initialize animation self.setAnimation() self.setDuration() def _animateFinished(self): """ Its called by TimeLine when animation finished. It first runs the outMethod of last Page and then the inMethod of current Page Finally tt gives the focus to the current page and fixes the scrollBar """ # Disable other widgets for page in self.__pages: if not page == self.__pages[self.__current]: page.widget.setEnabled(False) # Run last page's outMethod if exists if self.__pages[self.__last].outMethod: self.__pages[self.__last].outMethod() # Run new page's inMethod if exists if self.__pages[self.__current].inMethod: self.__pages[self.__current].inMethod() # Give focus to the current Page self.__pages[self.__current].widget.setFocus() # Update scrollbar position for current page self.__scrollBar.setValue(self.__current * self.__base_value()) # Emit currentChanged SIGNAL self.currentChanged.emit() def event(self, event): """ Overrides the main event handler to catch resize events """ # Catch Resize event if event.type() == QEvent.Resize: # Update each page size limits to mainwidget's new size for page in self.__pages: page.widget.setMinimumSize(self.size()) page.widget.setMaximumSize(self.size()) # Update viewport size limits to mainwidget's new size # It's a workaround for QScrollArea updateGeometry bug self.viewport().setMinimumSize(self.size()) self.viewport().setMaximumSize(self.size()) # Update scrollbar position for current page self.__scrollBar.setValue(self.__current * self.__base_value()) # Return the Event return QScrollArea.event(self, event) def keyPressEvent(self, event): """ Overrides the keyPressEvent to ignore them """ pass def wheelEvent(self, event): """ Overrides the wheelEvent to ignore them """ pass def createPage(self, widget, inMethod=None, outMethod=None): """ Creates and adds new Page for given widget with given in/out methods. widget: A QWidget which is the mainwidget for this Page inMethod: (optional) QPageWidget triggers this method when the Page appear outMethod: (optional) QPageWidget triggers this method when the Page disappear """ self.addPage(Page(widget, inMethod, outMethod)) def addPage(self, page): """ Adds the given Page to the stack. page: A Page object """ # First remove the last page; its __tmp_page self.__pages.pop() # Add new page self.__pages.append(page) self.layout.addWidget(page.widget) # Add __tmp_page to end self.__pages.append(self.__tmp_page) self.layout.addWidget(self.__tmp_page.widget) # Create connections for page navigation signals from new page try: page.widget.pageNext.connect(self.next) page.widget.pagePrevious.connect(self.prev) page.widget.setCurrent[int].connect(self.setCurrent) except: pass def __setCurrent(self, pageNumber): """ Internal method to set current page index. """ self.__last = self.__current self.__current = min(max(0, pageNumber), len(self.__pages) - 2) if pageNumber == len(self.__pages) - 1 and self.__return_to_first: self.__current = 0 def setCurrent(self, pageNumber=0): """ Set and flip the page with given pageNumber. pageNumber: index number of Page (default is 0) """ self.__setCurrent(pageNumber) self.flipPage() def getCurrent(self): """ Returns current page index. """ return self.__current def getCurrentWidget(self): """ Returns current page widget. """ return self.getWidget(self.getCurrent()) def getWidget(self, pageNumber): """ Returns widget for given page index pageNumber: index number of Page """ try: return self.__pages[pageNumber].widget except: return None def count(self): """ Returns number of pages. """ return len(self.__pages) - 1 def setAnimation(self, animation=35): """ Set the transition animation with the given animation. animation: the number represents predefined QEasingCurves List of predefined QEasingCurves can be found from: http://doc.qt.nokia.com/4/qeasingcurve.html#Type-enum Default is QEasingCurve::InOutBack (35) """ self.__animation = animation self.__timeline.setEasingCurve(QEasingCurve(self.__animation)) def setDuration(self, duration=400): """ Set the transition duration. duration: duration time in ms """ self.__duration = duration self.__timeline.setDuration(self.__duration) def flipPage(self, direction=0): """ Flip the page with given direction. direction: can be -1, 0 or +1 -1: previous page (if exists) 0: just flip to current page +1: next page (if exists) """ # Enable all widgets for page in self.__pages: page.widget.setEnabled(True) # Check given direction direction = direction if direction == 0 else max(min(1, direction), -1) # If direction is equal to zero no need to re-set current if not direction == 0: self.__setCurrent(self.__current + direction) # If last page is different from new page, flip it ! if not self.__last == self.__current: self.__timeline.setFrameRange(self.__scrollBar.value(), self.__current * self.__base_value()) self.__timeline.start() def next(self): """ Helper method to flip next page. """ self.flipPage(1) def prev(self): """ Helper method to flip previous page. """ self.flipPage(-1)
class ExplorerNode(BaseNode): def __init__(self, nx_node, center_pos, nx_pos, steps, steps_max, small): """ Create node in the graph scene :param tuple nx_node: Node info :param center_pos: The position of the center node :param nx_pos: Position of the nodes in the graph :param int steps: The steps from the center identity :param int steps_max: The steps max of the graph :param bool small: Small dots for big networks """ super().__init__(nx_node, nx_pos) self.steps = steps self.steps_max = steps_max self.highlighted = False self.status_sentry = False if small: self.setRect(0, 0, 10, 10) self.text_item = None else: # text inside ellipse self.text_item = QGraphicsSimpleTextItem(self) self.text_item.setText(self.text) # center ellipse around text self.setRect(0, 0, self.text_item.boundingRect().width() * 2, self.text_item.boundingRect().height() * 2) # center text in ellipse self.text_item.setPos(self.boundingRect().width() / 4.0, self.boundingRect().height() / 4.0) # set anchor to the center self.setTransform(QTransform().translate( -self.boundingRect().width() / 2.0, -self.boundingRect().height() / 2.0)) # cursor change on hover self.setAcceptHoverEvents(True) self.setZValue(1) # animation and moves self.timeline = None self.loading_timer = QTimer() self.loading_timer.timeout.connect(self.next_tick) self.loading_counter = 0 self._refresh_colors() self.setPos(center_pos) self.move_to(nx_pos) def update_metadata(self, metadata): super().update_metadata(metadata) self.status_sentry = self.metadata[ 'is_sentry'] if 'is_sentry' in self.metadata else False self._refresh_colors() def _refresh_colors(self): """ Refresh elements in the node """ # color around ellipse outline_color = QColor('grey') outline_style = Qt.SolidLine outline_width = 1 if self.status_wallet: outline_width = 2 if not self.status_member: outline_color = QColor('red') if self.status_sentry: outline_color = QColor('black') outline_width = 3 self.setPen(QPen(outline_color, outline_width, outline_style)) if self.highlighted: text_color = QColor('grey') else: text_color = QColor('black') if self.status_wallet == NodeStatus.HIGHLIGHTED: text_color = QColor('grey') if self.text_item: self.text_item.setBrush(QBrush(text_color)) # create gradient inside the ellipse gradient = QRadialGradient( QPointF(0, self.boundingRect().height() / 4), self.boundingRect().width()) color = QColor() color.setHsv(120 - 60 / self.steps_max * self.steps, 180 + 50 / self.steps_max * self.steps, 60 + 170 / self.steps_max * self.steps) if self.highlighted: color = color.darker(200) color = color.lighter( math.fabs(math.sin(self.loading_counter / 100 * math.pi) * 100) + 100) gradient.setColorAt(0, color) gradient.setColorAt(1, color.darker(150)) self.setBrush(QBrush(gradient)) def move_to(self, nx_pos): """ Move to corresponding position :param nx_pos: :return: """ origin_x = self.x() origin_y = self.y() final_x = nx_pos[self.id][0] final_y = nx_pos[self.id][1] def frame_move(frame): value = self.timeline.valueForTime(self.timeline.currentTime()) x = origin_x + (final_x - origin_x) * value y = origin_y + (final_y - origin_y) * value self.setPos(x, y) if self.scene(): self.scene().node_moved.emit(self.id, x, y) def timeline_ends(): self.setPos(final_x, final_y) self.timeline = None # Remember to hold the references to QTimeLine and QGraphicsItemAnimation instances. # They are not kept anywhere, even if you invoke QTimeLine.start(). self.timeline = QTimeLine(1000) self.timeline.setFrameRange(0, 100) self.timeline.frameChanged.connect(frame_move) self.timeline.finished.connect(timeline_ends) self.timeline.start() def highlight(self): """ Highlight the edge in the scene """ self.highlighted = True self._refresh_colors() self.update(self.boundingRect()) def neutralize(self): """ Neutralize the edge in the scene """ self.highlighted = False self._refresh_colors() self.update(self.boundingRect()) def start_loading_animation(self): """ Neutralize the edge in the scene """ if not self.loading_timer.isActive(): self.loading_timer.start(10) def stop_loading_animation(self): """ Neutralize the edge in the scene """ self.loading_timer.stop() self.loading_counter = 100 self._refresh_colors() self.update(self.boundingRect()) def next_tick(self): """ Next tick :return: """ self.loading_counter += 1 self.loading_counter %= 100 self._refresh_colors() self.update(self.boundingRect())
class QPageWidget(QScrollArea): """ The QPageWidget provides a stack widget with animated page transitions. """ #Emits currentChanged = pyqtSignal() def __init__(self, parent = None, direction = "ltr", rtf = False): """ Creates a new QPageWidget on given parent object. parent: QWidget parent direction: "ltr" -> Left To Right "ttb" -> Top To Bottom rtf: Return to first, if its True it flips to the first page when next page requested at the last page """ # First initialize, QPageWidget is based on QScrollArea QScrollArea.__init__(self, parent) # Properties for QScrollArea self.setFrameShape(QFrame.NoFrame) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setWidgetResizable(True) # Main widget, which stores all Pages in it self.widget = QWidget(self) # Layout based on QBoxLayout which supports Vertical or Horizontal layout if direction == "ltr": self.layout = QBoxLayout(QBoxLayout.LeftToRight, self.widget) self.__scrollBar = self.horizontalScrollBar() self.__base_value = self.width else: self.layout = QBoxLayout(QBoxLayout.TopToBottom, self.widget) self.__scrollBar = self.verticalScrollBar() self.__base_value = self.height self.layout.setSpacing(0) #qboxlayout setmargin özelliği yok #self.layout.setMargin(0) # Return to first self.__return_to_first = rtf # TMP_PAGE, its using as last page in stack # A workaround for a QScrollArea bug self.__tmp_page = Page(QWidget(self.widget)) self.__pages = [self.__tmp_page] self.__current = 0 self.__last = 0 # Set main widget self.setWidget(self.widget) # Animation TimeLine self.__timeline = QTimeLine() self.__timeline.setUpdateInterval(2) # Updates scrollbar position when frame changed self.__timeline.frameChanged.connect(lambda x: self.__scrollBar.setValue(x)) # End of the animation self.__timeline.finished.connect(self._animateFinished) # Initialize animation self.setAnimation() self.setDuration() def _animateFinished(self): """ Its called by TimeLine when animation finished. It first runs the outMethod of last Page and then the inMethod of current Page Finally tt gives the focus to the current page and fixes the scrollBar """ # Disable other widgets for page in self.__pages: if not page == self.__pages[self.__current]: page.widget.setEnabled(False) # Run last page's outMethod if exists if self.__pages[self.__last].outMethod: self.__pages[self.__last].outMethod() # Run new page's inMethod if exists if self.__pages[self.__current].inMethod: self.__pages[self.__current].inMethod() # Give focus to the current Page self.__pages[self.__current].widget.setFocus() # Update scrollbar position for current page self.__scrollBar.setValue(self.__current * self.__base_value()) # Emit currentChanged SIGNAL self.currentChanged.emit() def event(self, event): """ Overrides the main event handler to catch resize events """ # Catch Resize event if event.type() == QEvent.Resize: # Update each page size limits to mainwidget's new size for page in self.__pages: page.widget.setMinimumSize(self.size()) page.widget.setMaximumSize(self.size()) # Update viewport size limits to mainwidget's new size # It's a workaround for QScrollArea updateGeometry bug self.viewport().setMinimumSize(self.size()) self.viewport().setMaximumSize(self.size()) # Update scrollbar position for current page self.__scrollBar.setValue(self.__current * self.__base_value()) # Return the Event return QScrollArea.event(self, event) def keyPressEvent(self, event): """ Overrides the keyPressEvent to ignore them """ pass def wheelEvent(self, event): """ Overrides the wheelEvent to ignore them """ pass def createPage(self, widget, inMethod = None, outMethod = None): """ Creates and adds new Page for given widget with given in/out methods. widget: A QWidget which is the mainwidget for this Page inMethod: (optional) QPageWidget triggers this method when the Page appear outMethod: (optional) QPageWidget triggers this method when the Page disappear """ self.addPage(Page(widget, inMethod, outMethod)) def addPage(self, page): """ Adds the given Page to the stack. page: A Page object """ # First remove the last page; its __tmp_page self.__pages.pop() # Add new page self.__pages.append(page) self.layout.addWidget(page.widget) # Add __tmp_page to end self.__pages.append(self.__tmp_page) self.layout.addWidget(self.__tmp_page.widget) # Create connections for page navigation signals from new page try: page.widget.pageNext.connect(self.next) page.widget.pagePrevious.connect(self.prev) page.widget.setCurrent[int].connect(self.setCurrent) except: pass def __setCurrent(self, pageNumber): """ Internal method to set current page index. """ self.__last = self.__current self.__current = min(max(0, pageNumber), len(self.__pages) - 2) if pageNumber == len(self.__pages) - 1 and self.__return_to_first: self.__current = 0 def setCurrent(self, pageNumber = 0): """ Set and flip the page with given pageNumber. pageNumber: index number of Page (default is 0) """ self.__setCurrent(pageNumber) self.flipPage() def getCurrent(self): """ Returns current page index. """ return self.__current def getCurrentWidget(self): """ Returns current page widget. """ return self.getWidget(self.getCurrent()) def getWidget(self, pageNumber): """ Returns widget for given page index pageNumber: index number of Page """ try: return self.__pages[pageNumber].widget except: return None def count(self): """ Returns number of pages. """ return len(self.__pages) - 1 def setAnimation(self, animation = 35): """ Set the transition animation with the given animation. animation: the number represents predefined QEasingCurves List of predefined QEasingCurves can be found from: http://doc.qt.nokia.com/4/qeasingcurve.html#Type-enum Default is QEasingCurve::InOutBack (35) """ self.__animation = animation self.__timeline.setEasingCurve(QEasingCurve(self.__animation)) def setDuration(self, duration = 400): """ Set the transition duration. duration: duration time in ms """ self.__duration = duration self.__timeline.setDuration(self.__duration) def flipPage(self, direction=0): """ Flip the page with given direction. direction: can be -1, 0 or +1 -1: previous page (if exists) 0: just flip to current page +1: next page (if exists) """ # Enable all widgets for page in self.__pages: page.widget.setEnabled(True) # Check given direction direction = direction if direction == 0 else max(min(1, direction), -1) # If direction is equal to zero no need to re-set current if not direction == 0: self.__setCurrent(self.__current + direction) # If last page is different from new page, flip it ! if not self.__last == self.__current: self.__timeline.setFrameRange(self.__scrollBar.value(), self.__current * self.__base_value()) self.__timeline.start() def next(self): """ Helper method to flip next page. """ self.flipPage(1) def prev(self): """ Helper method to flip previous page. """ self.flipPage(-1)
class ExplorerNode(BaseNode): def __init__(self, nx_node, center_pos, nx_pos, steps, steps_max): """ Create node in the graph scene :param tuple nx_node: Node info :param center_pos: The position of the center node :param nx_pos: Position of the nodes in the graph :param int steps: The steps from the center identity :param int steps_max: The steps max of the graph """ super().__init__(nx_node, nx_pos) self.steps = steps self.steps_max = steps_max self.highlighted = False # text inside ellipse self.text_item = QGraphicsSimpleTextItem(self) self.text_item.setText(self.text) # center ellipse around text self.setRect( 0, 0, self.text_item.boundingRect().width() * 2, self.text_item.boundingRect().height() * 2 ) # set anchor to the center self.setTransform( QTransform().translate(-self.boundingRect().width() / 2.0, -self.boundingRect().height() / 2.0)) # center text in ellipse self.text_item.setPos(self.boundingRect().width() / 4.0, self.boundingRect().height() / 4.0) # cursor change on hover self.setAcceptHoverEvents(True) self.setZValue(1) # animation and moves self.timeline = None self.loading_timer = QTimer() self.loading_timer.timeout.connect(self.next_tick) self.loading_counter = 0 self._refresh_colors() self.setPos(center_pos) self.move_to(nx_pos) def _refresh_colors(self): """ Refresh elements in the node """ # color around ellipse outline_color = QColor('black') outline_style = Qt.SolidLine outline_width = 1 if self.status_wallet: outline_color = QColor('grey') outline_width = 2 if not self.status_member: outline_color = QColor('red') outline_style = Qt.SolidLine self.setPen(QPen(outline_color, outline_width, outline_style)) if self.highlighted: text_color = QColor('grey') else: text_color = QColor('black') if self.status_wallet == NodeStatus.HIGHLIGHTED: text_color = QColor('grey') self.text_item.setBrush(QBrush(text_color)) # create gradient inside the ellipse gradient = QRadialGradient(QPointF(0, self.boundingRect().height() / 4), self.boundingRect().width()) color = QColor() color.setHsv(120 - 60 / self.steps_max * self.steps, 180 + 50 / self.steps_max * self.steps, 60 + 170 / self.steps_max * self.steps) if self.highlighted: color = color.darker(200) color = color.lighter(math.fabs(math.sin(self.loading_counter / 100 * math.pi) * 100) + 100) gradient.setColorAt(0, color) gradient.setColorAt(1, color.darker(150)) self.setBrush(QBrush(gradient)) def move_to(self, nx_pos): """ Move to corresponding position :param nx_pos: :return: """ origin_x = self.x() origin_y = self.y() final_x = nx_pos[self.id][0] final_y = nx_pos[self.id][1] def frame_move(frame): value = self.timeline.valueForTime(self.timeline.currentTime()) x = origin_x + (final_x - origin_x) * value y = origin_y + (final_y - origin_y) * value self.setPos(x, y) self.scene().node_moved.emit(self.id, x, y) def timeline_ends(): self.setPos(final_x, final_y) self.timeline = None # Remember to hold the references to QTimeLine and QGraphicsItemAnimation instances. # They are not kept anywhere, even if you invoke QTimeLine.start(). self.timeline = QTimeLine(1000) self.timeline.setFrameRange(0, 100) self.timeline.frameChanged.connect(frame_move) self.timeline.finished.connect(timeline_ends) self.timeline.start() def highlight(self): """ Highlight the edge in the scene """ self.highlighted = True self._refresh_colors() self.update(self.boundingRect()) def neutralize(self): """ Neutralize the edge in the scene """ self.highlighted = False self._refresh_colors() self.update(self.boundingRect()) def start_loading_animation(self): """ Neutralize the edge in the scene """ if not self.loading_timer.isActive(): self.loading_timer.start(10) def stop_loading_animation(self): """ Neutralize the edge in the scene """ self.loading_timer.stop() self.loading_counter = 100 self._refresh_colors() self.update(self.boundingRect()) def next_tick(self): """ Next tick :return: """ self.loading_counter += 1 self.loading_counter %= 100 self._refresh_colors() self.update(self.boundingRect())