Exemple #1
0
    def __init__(self, gui):
        QWidget.__init__(self, gui)
        self.setObjectName('jobs_pointer')
        self.setVisible(False)
        self.resize(100, 80)
        self.animation = QPropertyAnimation(self, "geometry", self)
        self.animation.setDuration(750)
        self.animation.setLoopCount(2)
        self.animation.setEasingCurve(QEasingCurve.Linear)
        self.animation.finished.connect(self.hide)

        taily, heady = 0, 55
        self.arrow_path = QPainterPath(QPointF(40, taily))
        self.arrow_path.lineTo(40, heady)
        self.arrow_path.lineTo(20, heady)
        self.arrow_path.lineTo(50, self.height())
        self.arrow_path.lineTo(80, heady)
        self.arrow_path.lineTo(60, heady)
        self.arrow_path.lineTo(60, taily)
        self.arrow_path.closeSubpath()

        c = self.palette().color(QPalette.Active, QPalette.WindowText)
        self.color = QColor(c)
        self.color.setAlpha(100)
        self.brush = QBrush(self.color, Qt.SolidPattern)
Exemple #2
0
 def __init__(self, parent):
     QWidget.__init__(self, parent)
     self.setVisible(False)
     self._animated_size = 1.0
     self.animation = QPropertyAnimation(self, b'animated_size', self)
     self.animation.setEasingCurve(QEasingCurve.Linear)
     self.animation.setDuration(1000), self.animation.setStartValue(0.0), self.animation.setEndValue(1.0)
     self.animation.valueChanged.connect(self.animation_value_changed)
     self.animation.finished.connect(self.animation_done)
Exemple #3
0
 def __init__(self, *args):
     QToolButton.__init__(self, *args)
     self.animation = QPropertyAnimation(self, 'iconSize', self)
     self.animation.setDuration(60/72.*1000)
     self.animation.setLoopCount(4)
     self.normal_icon_size = QSize(64, 64)
     self.animation.valueChanged.connect(self.value_changed)
     self.setCursor(Qt.PointingHandCursor)
     self.animation.finished.connect(self.animation_finished)
Exemple #4
0
 def __init__(self, parent):
     super(CoverDelegate, self).__init__(parent)
     self._animated_size = 1.0
     self.animation = QPropertyAnimation(self, 'animated_size', self)
     self.animation.setEasingCurve(QEasingCurve.OutInCirc)
     self.animation.setDuration(500)
     self.set_dimensions()
     self.cover_cache = CoverCache()
     self.render_queue = LifoQueue()
     self.animating = None
     self.highlight_color = QColor(Qt.white)
Exemple #5
0
 def __init__(self, *args):
     QToolButton.__init__(self, *args)
     self._icon_size = -1
     QToolButton.setIcon(self, QIcon(I('donate.png')))
     self.setText('\xa0')
     self.animation = QPropertyAnimation(self, b'icon_size', self)
     self.animation.setDuration(60/72.*1000)
     self.animation.setLoopCount(4)
     self.animation.valueChanged.connect(self.value_changed)
     self.setCursor(Qt.PointingHandCursor)
     self.animation.finished.connect(self.animation_finished)
Exemple #6
0
    def __init__(self, parent):
        QWidget.__init__(self, parent)

        self.setGeometry(0, 0, 1, 1)
        self._current_width = 0
        self.before_image = self.after_image = None
        self.animation = QPropertyAnimation(self, 'current_width', self)
        self.setVisible(False)
        self.animation.valueChanged.connect(self.update)
        self.animation.finished.connect(self.finished)
        self.flip_forwards = True
        self.setAttribute(Qt.WA_OpaquePaintEvent)
Exemple #7
0
 def __init__(self, *args):
     QToolButton.__init__(self, *args)
     # vertically size policy must be expanding for it to align inside a
     # toolbar
     self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
     self._icon_size = -1
     QToolButton.setIcon(self, QIcon(I('donate.png')))
     self.setText('\xa0')
     self.animation = QPropertyAnimation(self, b'icon_size', self)
     self.animation.setDuration(60/72.*1000)
     self.animation.setLoopCount(4)
     self.animation.valueChanged.connect(self.value_changed)
     self.setCursor(Qt.PointingHandCursor)
     self.animation.finished.connect(self.animation_finished)
Exemple #8
0
class RevealBar(QWidget):  # {{{

    def __init__(self, parent):
        QWidget.__init__(self, parent)
        self.setVisible(False)
        self._animated_size = 1.0
        self.animation = QPropertyAnimation(self, b'animated_size', self)
        self.animation.setEasingCurve(QEasingCurve.Linear)
        self.animation.setDuration(1000), self.animation.setStartValue(0.0), self.animation.setEndValue(1.0)
        self.animation.valueChanged.connect(self.animation_value_changed)
        self.animation.finished.connect(self.animation_done)

    @pyqtProperty(float)
    def animated_size(self):
        return self._animated_size

    @animated_size.setter
    def animated_size(self, val):
        self._animated_size = val

    def animation_value_changed(self, *args):
        self.update()

    def animation_done(self):
        self.setVisible(False)
        self.update()

    def start(self, bar):
        self.setGeometry(bar.geometry())
        self.setVisible(True)
        self.animation.start()

    def paintEvent(self, ev):
        if self._animated_size < 1.0:
            rect = self.rect()
            painter = QPainter(self)
            pal = self.palette()
            col = pal.color(pal.Button)
            rect.setLeft(rect.left() + (rect.width() * self._animated_size))
            painter.setClipRect(rect)
            painter.fillRect(self.rect(), col)
Exemple #9
0
class RevealBar(QWidget):  # {{{

    def __init__(self, parent):
        QWidget.__init__(self, parent)
        self.setVisible(False)
        self._animated_size = 1.0
        self.animation = QPropertyAnimation(self, b'animated_size', self)
        self.animation.setEasingCurve(QEasingCurve.Type.Linear)
        self.animation.setDuration(1000), self.animation.setStartValue(0.0), self.animation.setEndValue(1.0)
        self.animation.valueChanged.connect(self.animation_value_changed)
        self.animation.finished.connect(self.animation_done)

    @pyqtProperty(float)
    def animated_size(self):
        return self._animated_size

    @animated_size.setter
    def animated_size(self, val):
        self._animated_size = val

    def animation_value_changed(self, *args):
        self.update()

    def animation_done(self):
        self.setVisible(False)
        self.update()

    def start(self, bar):
        self.setGeometry(bar.geometry())
        self.setVisible(True)
        self.animation.start()

    def paintEvent(self, ev):
        if self._animated_size < 1.0:
            rect = self.rect()
            painter = QPainter(self)
            pal = self.palette()
            col = pal.color(pal.Button)
            rect.setLeft(rect.left() + (rect.width() * self._animated_size))
            painter.setClipRect(rect)
            painter.fillRect(self.rect(), col)
Exemple #10
0
    def __init__(self, vertical, parent=None):
        QWidget.__init__(self, parent)
        self._current_pixmap_size = QSize(120, 120)
        self.vertical = vertical

        self.animation = QPropertyAnimation(self, "current_pixmap_size", self)
        self.animation.setEasingCurve(QEasingCurve(QEasingCurve.OutExpo))
        self.animation.setDuration(1000)
        self.animation.setStartValue(QSize(0, 0))
        self.animation.valueChanged.connect(self.value_changed)

        self.setSizePolicy(QSizePolicy.Expanding if vertical else QSizePolicy.Minimum, QSizePolicy.Expanding)

        self.default_pixmap = QPixmap(I("book.png"))
        self.pixmap = self.default_pixmap
        self.pwidth = self.pheight = None
        self.data = {}

        self.do_layout()
Exemple #11
0
 def pressedAlarmButton(self):
     try:
         alarmButtonCurrentGeometry = QtCore.QRect(self.alarmToolButton.geometry())
         alarmButtonAnimation = QPropertyAnimation(self.alarmToolButton,'geometry'.encode(encoding='utf_8'))
         alarmButtonAnimation.setDuration(self.ANIMATIONDURATION)
         alarmButtonAnimation.setStartValue(alarmButtonCurrentGeometry)
         
         alarmWidgetCurrentGeometry = QtCore.QRect(self.alarmWidget.geometry())
         alarmWidgetAnimation = QPropertyAnimation(self.alarmWidget,'geometry'.encode(encoding='utf_8'))
         alarmWidgetAnimation.setDuration(self.ANIMATIONDURATION)
         alarmWidgetAnimation.setStartValue(alarmWidgetCurrentGeometry)
         
         if self.AlarmCurrentState == 'Closed':
             alarmButtonEndTopLeftCorner = QtCore.QPoint(self.alarmToolButton.pos() - QtCore.QPoint(275, 0))
             alarmWidgetEndTopLeftCorner = QtCore.QPoint(self.alarmWidget.pos() - QtCore.QPoint(275, 0))
             self.AlarmCurrentState = 'Open'
         else:
             alarmButtonEndTopLeftCorner = QtCore.QPoint(self.alarmToolButton.pos() + QtCore.QPoint(275, 0))
             alarmWidgetEndTopLeftCorner = QtCore.QPoint(self.alarmWidget.pos() + QtCore.QPoint(275, 0))
             self.AlarmCurrentState = 'Closed'
         
         alarmButtonFinalGeometry = QtCore.QRect(alarmButtonEndTopLeftCorner, QtCore.QSize(self.alarmToolButton.width(), self.alarmToolButton.height()))
         alarmButtonAnimation.setEndValue(alarmButtonFinalGeometry)
         
         alarmWidgetFinalGeometry = QtCore.QRect(alarmWidgetEndTopLeftCorner, QtCore.QSize(self.alarmWidget.width(), self.alarmWidget.height()))
         alarmWidgetAnimation.setEndValue(alarmWidgetFinalGeometry)
         
         alarmButtonAnimation.start()
         alarmWidgetAnimation.start()
         #self.setAlarmWidgetStyleSheet()
         self.AlarmIconAnimation = alarmButtonAnimation
         self.AlarmWidgetAnimation = alarmWidgetAnimation
     except Exception as e:
         print (e.strerror)
Exemple #12
0
    def pressedCalendarButton(self):
        try:
            calendarButtonCurrentGeometry = QtCore.QRect(
                self.calendarToolButton.geometry())
            calendarButtonAnimation = QPropertyAnimation(
                self.calendarToolButton, 'geometry'.encode(encoding='utf_8'))
            calendarButtonAnimation.setDuration(self.ANIMATIONDURATION)
            calendarButtonAnimation.setStartValue(
                calendarButtonCurrentGeometry)

            calendarWidgetCurrentGeometry = QtCore.QRect(
                self.calendarWidget.geometry())
            calendarWidgetAnimation = QPropertyAnimation(
                self.calendarWidget, 'geometry'.encode(encoding='utf_8'))
            calendarWidgetAnimation.setDuration(self.ANIMATIONDURATION)
            calendarWidgetAnimation.setStartValue(
                calendarWidgetCurrentGeometry)

            if self.CalendarCurrentState == 'Closed':
                calendarButtonEndTopLeftCorner = QtCore.QPoint(
                    self.calendarToolButton.pos() - QtCore.QPoint(275, 0))
                calendarWidgetEndTopLeftCorner = QtCore.QPoint(
                    self.calendarWidget.pos() - QtCore.QPoint(275, 0))
                self.CalendarCurrentState = 'Open'
                self.Calendar = Calendar()
                self.calendarLabel.setText(self.Calendar.EventList)
            else:
                calendarButtonEndTopLeftCorner = QtCore.QPoint(
                    self.calendarToolButton.pos() + QtCore.QPoint(275, 0))
                calendarWidgetEndTopLeftCorner = QtCore.QPoint(
                    self.calendarWidget.pos() + QtCore.QPoint(275, 0))
                self.CalendarCurrentState = 'Closed'

            calendarButtonFinalGeometry = QtCore.QRect(
                calendarButtonEndTopLeftCorner,
                QtCore.QSize(self.calendarToolButton.width(),
                             self.calendarToolButton.height()))
            calendarButtonAnimation.setEndValue(calendarButtonFinalGeometry)

            calendarWidgetFinalGeometry = QtCore.QRect(
                calendarWidgetEndTopLeftCorner,
                QtCore.QSize(self.calendarWidget.width(),
                             self.calendarWidget.height()))
            calendarWidgetAnimation.setEndValue(calendarWidgetFinalGeometry)

            calendarButtonAnimation.start()
            calendarWidgetAnimation.start()
            #self.setcalendarWidgetStyleSheet()
            self.CalendarIconAnimation = calendarButtonAnimation
            self.CalendarWidgetAnimation = calendarWidgetAnimation
        except Exception as e:
            print(e.strerror)
Exemple #13
0
    def __init__(self, parent):
        QWidget.__init__(self, parent)
        self.setVisible(False)
        parent.installEventFilter(self)

        self._show_fraction = 0.0
        self.show_animation = a = QPropertyAnimation(self, b"show_fraction",
                                                     self)
        a.setDuration(1000), a.setEasingCurve(QEasingCurve.OutQuad)
        a.setStartValue(0.0), a.setEndValue(1.0)
        a.finished.connect(self.stop_show_animation)
        self.rendered_pixmap = None

        self.questions = []

        self.icon = ic = Icon(self)
        self.msg_label = msg = QLabel('some random filler text')
        msg.setWordWrap(True)
        self.bb = QDialogButtonBox()
        self.bb.accepted.connect(self.accept)
        self.bb.rejected.connect(self.reject)
        self.log_button = self.bb.addButton(_('View log'), self.bb.ActionRole)
        self.log_button.setIcon(QIcon(I('debug.png')))
        self.log_button.clicked.connect(self.show_log)
        self.copy_button = self.bb.addButton(_('&Copy to clipboard'),
                                             self.bb.ActionRole)
        self.copy_button.clicked.connect(self.copy_to_clipboard)
        self.action_button = self.bb.addButton('', self.bb.ActionRole)
        self.action_button.clicked.connect(self.action_clicked)
        self.show_det_msg = _('Show &details')
        self.hide_det_msg = _('Hide &details')
        self.det_msg_toggle = self.bb.addButton(self.show_det_msg,
                                                self.bb.ActionRole)
        self.det_msg_toggle.clicked.connect(self.toggle_det_msg)
        self.det_msg_toggle.setToolTip(
            _('Show detailed information about this error'))
        self.det_msg = PlainTextEdit(self)
        self.det_msg.setReadOnly(True)
        self.bb.setStandardButtons(self.bb.Yes | self.bb.No | self.bb.Ok)
        self.bb.button(self.bb.Yes).setDefault(True)
        self.title_label = title = QLabel('A dummy title')
        f = title.font()
        f.setBold(True)
        title.setFont(f)

        self.checkbox = QCheckBox('', self)

        self._l = l = QVBoxLayout(self)
        self._h = h = QHBoxLayout()
        self._v = v = QVBoxLayout()
        v.addWidget(title), v.addWidget(msg)
        h.addWidget(ic), h.addSpacing(10), h.addLayout(v), l.addLayout(h)
        l.addSpacing(5)
        l.addWidget(self.checkbox)
        l.addWidget(self.det_msg)
        l.addWidget(self.bb)

        self.ask_question.connect(self.do_ask_question,
                                  type=Qt.QueuedConnection)
        self.setFocusPolicy(Qt.NoFocus)
        for child in self.findChildren(QWidget):
            child.setFocusPolicy(Qt.NoFocus)
        self.setFocusProxy(self.parent())
        self.resize_timer = t = QTimer(self)
        t.setSingleShot(True), t.setInterval(100), t.timeout.connect(
            self.parent_resized)
Exemple #14
0
    def drawControl(self, element, option, painter, widget):
        '''
        @param: element ControlElement
        @param: option QStyleOption
        @param: painter QPainter
        @param: widget QWidget
        '''
        v_opt = option

        if element != self.CE_TabBarTab or not isinstance(
                v_opt, QStyleOptionTab):
            QProxyStyle.drawControl(element, option, painter, widget)
            return

        rect = v_opt.rect
        selected = v_opt.state & self.State_Selected
        vertical_tabs = v_opt.shape == QTabBar.RoundedWest
        text = v_opt.text

        if selected:
            # background
            painter.save()
            grad = QLinearGradient(rect.topLeft(), rect.topRight())
            grad.setColorAt(0, QColor(255, 255, 255, 140))
            grad.setColorAt(0, QColor(255, 255, 255, 210))
            painter.fillRect(rect.adjusted(0, 0, 0, -1), grad)
            painter.restore()

            # shadows
            painter.setPen(QColor(0, 0, 0, 110))
            painter.drawLine(rect.topLeft() + QPoint(1, -1),
                             rect.topRight() - QPoint(0, 1))
            painter.drawLine(rect.bottomLeft(), rect.bottomRight())
            painter.setPen(QColor(0, 0, 0, 40))
            painter.drawLine(rect.topLeft(), rect.bottomLeft())

            # highlights
            painter.setPen(QColor(255, 255, 255, 50))
            painter.drawLine(rect.topLeft() + QPoint(0, -2),
                             rect.topRight() - QPoint(0, 2))
            painter.drawLine(rect.bottomLeft() + QPoint(0, 1),
                             rect.bottomRight() + QPoint(0, 1))
            painter.setPen(QColor(255, 255, 255, 40))
            painter.drawLine(rect.topLeft() + QPoint(0, 0), rect.topRight())
            painter.drawLine(rect.topRight() + QPoint(0, 1),
                             rect.bottomRight() - QPoint(0, 1))
            painter.drawLine(rect.bottomLeft() + QPoint(0, -1),
                             rect.bottomRight() - QPoint(0, 1))

        m = QTransform()
        if vertical_tabs:
            m = QTransform.fromTranslate(rect.left(), rect.bottom())
            m.rotate(-90)
        else:
            m = QTransform.fromTranslate(rect.left(), rect.top())

        draw_rect = QRect(QPoint(0, 0), m.mapRect(rect).size())

        painter.save()
        painter.setTransform(m)

        icon_rect = QRect(QPoint(8, 0), v_opt.iconSize)
        text_rect = QRect(icon_rect.topRight() + QPoint(4, 0),
                          draw_rect.size())
        text_rect.setRight(draw_rect.width())
        icon_rect.translate(0, (draw_rect.height() - icon_rect.height()) / 2)

        boldFont = QFont(painter.font())
        boldFont.setPointSizeF(styleHelper.sidebarFontSize())
        boldFont.setBold(True)
        painter.setFont(boldFont)
        painter.setPen(selected and QColor(255, 255, 255, 160)
                       or QColor(0, 0, 0, 110))
        textFlags = Qt.AlignHCenter | Qt.AlignVCenter
        painter.drawText(text_rect, textFlags, text)
        painter.setPen(selected and QColor(60, 60, 60)
                       or styleHelper.panelTextColor())
        if widget:
            fader_key = 'tab_' + text + '_fader'
            animation_key = 'tab_' + text + '_animation'

            tab_hover = widget.property('tab_hover')
            # int
            fader = widget.property(fader_key)
            # QPropertyAnimation
            animation = widget.property(animation_key)

            if not animation:
                mut_widget = widget
                fader = 0
                mut_widget.setProperty(fader_key, fader)
                animation = QPropertyAnimation(mut_widget, fader_key,
                                               mut_widget)
                animation.valueChanged.connect(mut_widget.update)
                mut_widget.setProperty(animation_key, animation)

            if text == tab_hover:
                if animation.state(
                ) != QAbstractAnimation.Running and fader != 40:
                    animation.stop()
                    animation.setDuration(80)
                    animation.setEndValue(40)
                    animation.start()
            else:
                if animation.state(
                ) != QAbstractAnimation.Running and fader != 0:
                    animation.stop()
                    animation.setDuration(160)
                    animation.setEndValue(0)
                    animation.start()

            if not selected:
                painter.save()
                painter.fillRect(draw_rect, QColor(255, 255, 255, fader))
                painter.setPen(QPen(QColor(255, 255, 255, fader), 1.0))
                painter.drawLine(
                    draw_rect.topLeft(),
                    vertical_tabs and draw_rect.bottomLeft()
                    or draw_rect.topRight())
                painter.drawLine(
                    draw_rect.bottomRight(),
                    vertical_tabs and draw_rect.topRight()
                    or draw_rect.bottomLeft())
                painter.restore()

        if selected:
            iconMode = QIcon.Selected
        else:
            iconMode = QIcon.Normal
        styleHelper.drawIconWithShadow(v_opt.icon, icon_rect, painter,
                                       iconMode)

        painter.drawText(text_rect.translated(0, -1), textFlags, text)

        painter.restore()
Exemple #15
0
    def pressedPandoraButton(self):
        pandoraButtonCurrentGeometry = QtCore.QRect(
            self.pandoraToolButton.geometry())
        pandoraButtonAnimation = QPropertyAnimation(
            self.pandoraToolButton, 'geometry'.encode(encoding='utf_8'))
        pandoraButtonAnimation.setDuration(self.ANIMATIONDURATION)
        pandoraButtonAnimation.setStartValue(pandoraButtonCurrentGeometry)

        pandoraWidgetCurrentGeometry = QtCore.QRect(
            self.pandoraWidget.geometry())
        pandoraWidgetAnimation = QPropertyAnimation(
            self.pandoraWidget, 'geometry'.encode(encoding='utf_8'))
        pandoraWidgetAnimation.setDuration(self.ANIMATIONDURATION)
        pandoraWidgetAnimation.setStartValue(pandoraWidgetCurrentGeometry)

        if self.PandoraCurrentState == 'Closed':
            pandoraButtonEndTopLeftCorner = QtCore.QPoint(
                self.pandoraToolButton.pos() - QtCore.QPoint(275, 0))
            pandoraWidgetEndTopLeftCorner = QtCore.QPoint(
                self.pandoraWidget.pos() - QtCore.QPoint(275, 0))
            self.PandoraCurrentState = 'Open'
            if self.Pandora.started == True:
                self.playToolButton.hide()
                self.pauseToolButton.show()
        else:
            pandoraButtonEndTopLeftCorner = QtCore.QPoint(
                self.pandoraToolButton.pos() + QtCore.QPoint(275, 0))
            pandoraWidgetEndTopLeftCorner = QtCore.QPoint(
                self.pandoraWidget.pos() + QtCore.QPoint(275, 0))
            self.PandoraCurrentState = 'Closed'

        pandoraButtonFinalGeometry = QtCore.QRect(
            pandoraButtonEndTopLeftCorner,
            QtCore.QSize(self.pandoraToolButton.width(),
                         self.pandoraToolButton.height()))
        pandoraButtonAnimation.setEndValue(pandoraButtonFinalGeometry)

        pandoraWidgetFinalGeometry = QtCore.QRect(
            pandoraWidgetEndTopLeftCorner,
            QtCore.QSize(self.pandoraWidget.width(),
                         self.pandoraWidget.height()))
        pandoraWidgetAnimation.setEndValue(pandoraWidgetFinalGeometry)

        pandoraButtonAnimation.start()
        pandoraWidgetAnimation.start()
        #self.setpandoraWidgetStyleSheet()
        self.PandoraIconAnimation = pandoraButtonAnimation
        self.PandoraWidgetAnimation = pandoraWidgetAnimation
Exemple #16
0
class Pointer(QWidget):
    def __init__(self, gui):
        QWidget.__init__(self, gui)
        self.setObjectName('jobs_pointer')
        self.setVisible(False)
        self.resize(100, 80)
        self.animation = QPropertyAnimation(self, "geometry", self)
        self.animation.setDuration(750)
        self.animation.setLoopCount(2)
        self.animation.setEasingCurve(QEasingCurve.Linear)
        self.animation.finished.connect(self.hide)

        taily, heady = 0, 55
        self.arrow_path = QPainterPath(QPointF(40, taily))
        self.arrow_path.lineTo(40, heady)
        self.arrow_path.lineTo(20, heady)
        self.arrow_path.lineTo(50, self.height())
        self.arrow_path.lineTo(80, heady)
        self.arrow_path.lineTo(60, heady)
        self.arrow_path.lineTo(60, taily)
        self.arrow_path.closeSubpath()

        c = self.palette().color(QPalette.Active, QPalette.WindowText)
        self.color = QColor(c)
        self.color.setAlpha(100)
        self.brush = QBrush(self.color, Qt.SolidPattern)

        # from PyQt5.Qt import QTimer
        # QTimer.singleShot(1000, self.start)

    @property
    def gui(self):
        return self.parent()

    def point_at(self, frac):
        return (self.path.pointAtPercent(frac).toPoint() -
                QPoint(self.rect().center().x(), self.height()))

    def rect_at(self, frac):
        return QRect(self.point_at(frac), self.size())

    def abspos(self, widget):
        pos = widget.pos()
        parent = widget.parent()
        while parent is not self.gui:
            pos += parent.pos()
            parent = parent.parent()
        return pos

    def start(self):
        if config['disable_animations']:
            return
        self.setVisible(True)
        self.raise_()
        end = self.abspos(self.gui.jobs_button)
        end = QPointF(end.x() + self.gui.jobs_button.width() / 3.0,
                      end.y() + 20)
        start = QPointF(end.x(), end.y() - 0.5 * self.height())
        self.path = QPainterPath(QPointF(start))
        self.path.lineTo(end)
        self.path.closeSubpath()
        self.animation.setStartValue(self.rect_at(0.0))
        self.animation.setEndValue(self.rect_at(1.0))
        self.animation.setDirection(self.animation.Backward)
        num_keys = 100
        for i in xrange(1, num_keys):
            i /= num_keys
            self.animation.setKeyValueAt(i, self.rect_at(i))
        self.animation.start()

    def paintEvent(self, ev):
        p = QPainter(self)
        p.setRenderHints(p.Antialiasing)
        p.setBrush(self.brush)
        p.setPen(Qt.NoPen)
        p.drawPath(self.arrow_path)
        p.end()
Exemple #17
0
class ThrobbingButton(QToolButton):

    @pyqtProperty(int)
    def icon_size(self):
        return self._icon_size

    @icon_size.setter
    def icon_size(self, value):
        self._icon_size = value

    def __init__(self, *args):
        QToolButton.__init__(self, *args)
        self._icon_size = -1
        QToolButton.setIcon(self, QIcon(I('donate.png')))
        self.setText('\xa0')
        self.animation = QPropertyAnimation(self, b'icon_size', self)
        self.animation.setDuration(60/72.*1000)
        self.animation.setLoopCount(4)
        self.animation.valueChanged.connect(self.value_changed)
        self.setCursor(Qt.PointingHandCursor)
        self.animation.finished.connect(self.animation_finished)

    def animation_finished(self):
        self.icon_size = self.iconSize().width()

    def enterEvent(self, ev):
        self.start_animation()

    def leaveEvent(self, ev):
        self.stop_animation()

    def value_changed(self, val):
        self.update()

    def start_animation(self):
        if config['disable_animations']:
            return
        if self.animation.state() != self.animation.Stopped or not self.isVisible():
            return
        size = self.iconSize().width()
        smaller = int(0.7 * size)
        self.animation.setStartValue(smaller)
        self.animation.setEndValue(size)
        QMetaObject.invokeMethod(self.animation, 'start', Qt.QueuedConnection)

    def stop_animation(self):
        self.animation.stop()
        self.animation_finished()

    def paintEvent(self, ev):
        size = self._icon_size if self._icon_size > 10 else self.iconSize().width()
        p = QPainter(self)
        opt = QStyleOptionToolButton()
        self.initStyleOption(opt)
        s = self.style()
        opt.iconSize = QSize(size, size)
        s.drawComplexControl(s.CC_ToolButton, opt, p, self)
Exemple #18
0
class SlideFlip(QWidget):

    # API {{{

    # In addition the isVisible() and setVisible() methods must be present

    def __init__(self, parent):
        QWidget.__init__(self, parent)

        self.setGeometry(0, 0, 1, 1)
        self._current_width = 0
        self.before_image = self.after_image = None
        self.animation = QPropertyAnimation(self, b'current_width', self)
        self.setVisible(False)
        self.animation.valueChanged.connect(self.update)
        self.animation.finished.connect(self.finished)
        self.flip_forwards = True
        self.setAttribute(Qt.WA_OpaquePaintEvent)

    @property
    def running(self):
        'True iff animation is currently running'
        return self.animation.state() == self.animation.Running

    def initialize(self, image, forwards=True):
        '''
        Initialize the flipper, causes the flipper to show itself displaying
        the full `image`.

        :param image: The image to display as background
        :param forwards: If True flipper will flip forwards, otherwise
                         backwards

        '''
        self.flip_forwards = forwards
        self.before_image = QPixmap.fromImage(image)
        self.after_image = None
        self.setGeometry(0, 0, image.width(), image.height())
        self.setVisible(True)

    def __call__(self, image, duration=0.5):
        '''
        Start the animation. You must have called :meth:`initialize` first.

        :param duration: Animation duration in seconds.

        '''
        if self.running:
            return
        self.after_image = QPixmap.fromImage(image)

        if self.flip_forwards:
            self.animation.setStartValue(image.width())
            self.animation.setEndValue(0)
            t = self.before_image
            self.before_image = self.after_image
            self.after_image = t
            self.animation.setEasingCurve(QEasingCurve(QEasingCurve.InExpo))
        else:
            self.animation.setStartValue(0)
            self.animation.setEndValue(image.width())
            self.animation.setEasingCurve(QEasingCurve(QEasingCurve.OutExpo))

        self.animation.setDuration(duration * 1000)
        self.animation.start()

    # }}}

    def finished(self):
        self.setVisible(False)
        self.before_image = self.after_image = None

    def paintEvent(self, ev):
        if self.before_image is None:
            return
        canvas_size = self.rect()
        p = QPainter(self)
        p.setRenderHints(QPainter.Antialiasing
                         | QPainter.SmoothPixmapTransform)

        p.drawPixmap(canvas_size, self.before_image, self.before_image.rect())
        if self.after_image is not None:
            width = self._current_width
            iw = self.after_image.width()
            sh = min(self.after_image.height(), canvas_size.height())

            if self.flip_forwards:
                source = QRect(max(0, iw - width), 0, width, sh)
            else:
                source = QRect(0, 0, width, sh)

            target = QRect(source)
            target.moveLeft(0)
            p.drawPixmap(target, self.after_image, source)

        p.end()

    def set_current_width(self, val):
        self._current_width = val

    current_width = pyqtProperty('int',
                                 fget=lambda self: self._current_width,
                                 fset=set_current_width)
Exemple #19
0
class ThrobbingButton(QToolButton):
    def __init__(self, *args):
        QToolButton.__init__(self, *args)
        self.animation = QPropertyAnimation(self, 'iconSize', self)
        self.animation.setDuration(60 / 72. * 1000)
        self.animation.setLoopCount(4)
        self.normal_icon_size = QSize(64, 64)
        self.animation.valueChanged.connect(self.value_changed)
        self.setCursor(Qt.PointingHandCursor)
        self.animation.finished.connect(self.animation_finished)

    def set_normal_icon_size(self, w, h):
        self.normal_icon_size = QSize(w, h)
        self.setIconSize(self.normal_icon_size)
        try:
            self.setMinimumSize(self.sizeHint())
        except:
            self.setMinimumSize(QSize(w + 5, h + 5))

    def animation_finished(self):
        self.setIconSize(self.normal_icon_size)

    def enterEvent(self, ev):
        self.start_animation()

    def leaveEvent(self, ev):
        self.stop_animation()

    def value_changed(self, val):
        self.update()

    def start_animation(self):
        if config['disable_animations']:
            return
        if self.animation.state(
        ) != self.animation.Stopped or not self.isVisible():
            return
        size = self.normal_icon_size.width()
        smaller = int(0.7 * size)
        self.animation.setStartValue(QSize(smaller, smaller))
        self.animation.setEndValue(self.normal_icon_size)
        QMetaObject.invokeMethod(self.animation, 'start', Qt.QueuedConnection)

    def stop_animation(self):
        self.animation.stop()
        self.animation_finished()
Exemple #20
0
class ThrobbingButton(QToolButton):

    def __init__(self, *args):
        QToolButton.__init__(self, *args)
        self.animation = QPropertyAnimation(self, 'iconSize', self)
        self.animation.setDuration(60/72.*1000)
        self.animation.setLoopCount(4)
        self.normal_icon_size = QSize(64, 64)
        self.animation.valueChanged.connect(self.value_changed)
        self.setCursor(Qt.PointingHandCursor)
        self.animation.finished.connect(self.animation_finished)

    def set_normal_icon_size(self, w, h):
        self.normal_icon_size = QSize(w, h)
        self.setIconSize(self.normal_icon_size)
        try:
            self.setMinimumSize(self.sizeHint())
        except:
            self.setMinimumSize(QSize(w+5, h+5))

    def animation_finished(self):
        self.setIconSize(self.normal_icon_size)

    def enterEvent(self, ev):
        self.start_animation()

    def leaveEvent(self, ev):
        self.stop_animation()

    def value_changed(self, val):
        self.update()

    def start_animation(self):
        if config['disable_animations']:
            return
        if self.animation.state() != self.animation.Stopped or not self.isVisible():
            return
        size = self.normal_icon_size.width()
        smaller = int(0.7 * size)
        self.animation.setStartValue(QSize(smaller, smaller))
        self.animation.setEndValue(self.normal_icon_size)
        QMetaObject.invokeMethod(self.animation, 'start', Qt.QueuedConnection)

    def stop_animation(self):
        self.animation.stop()
        self.animation_finished()
Exemple #21
0
class CoverView(QWidget):  # {{{

    cover_changed = pyqtSignal(object, object)
    cover_removed = pyqtSignal(object)

    def __init__(self, vertical, parent=None):
        QWidget.__init__(self, parent)
        self._current_pixmap_size = QSize(120, 120)
        self.vertical = vertical

        self.animation = QPropertyAnimation(self, 'current_pixmap_size', self)
        self.animation.setEasingCurve(QEasingCurve(QEasingCurve.OutExpo))
        self.animation.setDuration(1000)
        self.animation.setStartValue(QSize(0, 0))
        self.animation.valueChanged.connect(self.value_changed)

        self.setSizePolicy(
                QSizePolicy.Expanding if vertical else QSizePolicy.Minimum,
                QSizePolicy.Expanding)

        self.default_pixmap = QPixmap(I('book.png'))
        self.pixmap = self.default_pixmap
        self.pwidth = self.pheight = None
        self.data = {}

        self.do_layout()

    def value_changed(self, val):
        self.update()

    def setCurrentPixmapSize(self, val):
        self._current_pixmap_size = val

    def do_layout(self):
        if self.rect().width() == 0 or self.rect().height() == 0:
            return
        pixmap = self.pixmap
        pwidth, pheight = pixmap.width(), pixmap.height()
        try:
            self.pwidth, self.pheight = fit_image(pwidth, pheight,
                            self.rect().width(), self.rect().height())[1:]
        except:
            self.pwidth, self.pheight = self.rect().width()-1, \
                    self.rect().height()-1
        self.current_pixmap_size = QSize(self.pwidth, self.pheight)
        self.animation.setEndValue(self.current_pixmap_size)

    def show_data(self, data):
        self.animation.stop()
        same_item = getattr(data, 'id', True) == self.data.get('id', False)
        self.data = {'id':data.get('id', None)}
        if data.cover_data[1]:
            self.pixmap = QPixmap.fromImage(data.cover_data[1])
            if self.pixmap.isNull() or self.pixmap.width() < 5 or \
                    self.pixmap.height() < 5:
                self.pixmap = self.default_pixmap
        else:
            self.pixmap = self.default_pixmap
        self.do_layout()
        self.update()
        if (not same_item and not config['disable_animations'] and
                self.isVisible()):
            self.animation.start()

    def paintEvent(self, event):
        canvas_size = self.rect()
        width = self.current_pixmap_size.width()
        extrax = canvas_size.width() - width
        if extrax < 0:
            extrax = 0
        x = int(extrax/2.)
        height = self.current_pixmap_size.height()
        extray = canvas_size.height() - height
        if extray < 0:
            extray = 0
        y = int(extray/2.)
        target = QRect(x, y, width, height)
        p = QPainter(self)
        p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
        p.drawPixmap(target, self.pixmap.scaled(target.size(),
            Qt.KeepAspectRatio, Qt.SmoothTransformation))
        if gprefs['bd_overlay_cover_size']:
            sztgt = target.adjusted(0, 0, 0, -4)
            f = p.font()
            f.setBold(True)
            p.setFont(f)
            sz = u'\u00a0%d x %d\u00a0'%(self.pixmap.width(), self.pixmap.height())
            flags = Qt.AlignBottom|Qt.AlignRight|Qt.TextSingleLine
            szrect = p.boundingRect(sztgt, flags, sz)
            p.fillRect(szrect.adjusted(0, 0, 0, 4), QColor(0, 0, 0, 200))
            p.setPen(QPen(QColor(255,255,255)))
            p.drawText(sztgt, flags, sz)
        p.end()

    current_pixmap_size = pyqtProperty('QSize',
            fget=lambda self: self._current_pixmap_size,
            fset=setCurrentPixmapSize
            )

    def contextMenuEvent(self, ev):
        cm = QMenu(self)
        paste = cm.addAction(_('Paste Cover'))
        copy = cm.addAction(_('Copy Cover'))
        remove = cm.addAction(_('Remove Cover'))
        if not QApplication.instance().clipboard().mimeData().hasImage():
            paste.setEnabled(False)
        copy.triggered.connect(self.copy_to_clipboard)
        paste.triggered.connect(self.paste_from_clipboard)
        remove.triggered.connect(self.remove_cover)
        cm.exec_(ev.globalPos())

    def copy_to_clipboard(self):
        QApplication.instance().clipboard().setPixmap(self.pixmap)

    def paste_from_clipboard(self, pmap=None):
        if not isinstance(pmap, QPixmap):
            cb = QApplication.instance().clipboard()
            pmap = cb.pixmap()
            if pmap.isNull() and cb.supportsSelection():
                pmap = cb.pixmap(cb.Selection)
        if not pmap.isNull():
            self.pixmap = pmap
            self.do_layout()
            self.update()
            self.update_tooltip(getattr(self.parent(), 'current_path', ''))
            if not config['disable_animations']:
                self.animation.start()
            id_ = self.data.get('id', None)
            if id_ is not None:
                self.cover_changed.emit(id_,
                    pixmap_to_data(pmap))

    def remove_cover(self):
        id_ = self.data.get('id', None)
        self.pixmap = self.default_pixmap
        self.do_layout()
        self.update()
        if id_ is not None:
            self.cover_removed.emit(id_)

    def update_tooltip(self, current_path):
        try:
            sz = self.pixmap.size()
        except:
            sz = QSize(0, 0)
        self.setToolTip(
            '<p>'+_('Double-click to open Book Details window') +
            '<br><br>' + _('Path') + ': ' + current_path +
            '<br><br>' + _('Cover size: %(width)d x %(height)d')%dict(
                width=sz.width(), height=sz.height())
        )
Exemple #22
0
 def pressedPandoraButton(self):
     pandoraButtonCurrentGeometry = QtCore.QRect(self.pandoraToolButton.geometry())
     pandoraButtonAnimation = QPropertyAnimation(self.pandoraToolButton,'geometry'.encode(encoding='utf_8'))
     pandoraButtonAnimation.setDuration(self.ANIMATIONDURATION)
     pandoraButtonAnimation.setStartValue(pandoraButtonCurrentGeometry)
         
     pandoraWidgetCurrentGeometry = QtCore.QRect(self.pandoraWidget.geometry())
     pandoraWidgetAnimation = QPropertyAnimation(self.pandoraWidget,'geometry'.encode(encoding='utf_8'))
     pandoraWidgetAnimation.setDuration(self.ANIMATIONDURATION)
     pandoraWidgetAnimation.setStartValue(pandoraWidgetCurrentGeometry)
         
     if self.PandoraCurrentState == 'Closed':
         pandoraButtonEndTopLeftCorner = QtCore.QPoint(self.pandoraToolButton.pos() - QtCore.QPoint(275, 0))
         pandoraWidgetEndTopLeftCorner = QtCore.QPoint(self.pandoraWidget.pos() - QtCore.QPoint(275, 0))
         self.PandoraCurrentState = 'Open'
         if self.Pandora.started == True:
             self.playToolButton.hide()
             self.pauseToolButton.show()
     else:
         pandoraButtonEndTopLeftCorner = QtCore.QPoint(self.pandoraToolButton.pos() + QtCore.QPoint(275, 0))
         pandoraWidgetEndTopLeftCorner = QtCore.QPoint(self.pandoraWidget.pos() + QtCore.QPoint(275, 0))
         self.PandoraCurrentState = 'Closed'
         
     pandoraButtonFinalGeometry = QtCore.QRect(pandoraButtonEndTopLeftCorner, QtCore.QSize(self.pandoraToolButton.width(), self.pandoraToolButton.height()))
     pandoraButtonAnimation.setEndValue(pandoraButtonFinalGeometry)
         
     pandoraWidgetFinalGeometry = QtCore.QRect(pandoraWidgetEndTopLeftCorner, QtCore.QSize(self.pandoraWidget.width(), self.pandoraWidget.height()))
     pandoraWidgetAnimation.setEndValue(pandoraWidgetFinalGeometry)
         
     pandoraButtonAnimation.start()
     pandoraWidgetAnimation.start()
         #self.setpandoraWidgetStyleSheet()
     self.PandoraIconAnimation = pandoraButtonAnimation
     self.PandoraWidgetAnimation = pandoraWidgetAnimation
Exemple #23
0
class CoverDelegate(QStyledItemDelegate):

    MARGIN = 4
    TOP, LEFT, RIGHT, BOTTOM = object(), object(), object(), object()

    @pyqtProperty(float)
    def animated_size(self):
        return self._animated_size

    @animated_size.setter
    def animated_size(self, val):
        self._animated_size = val

    def __init__(self, parent):
        super(CoverDelegate, self).__init__(parent)
        self._animated_size = 1.0
        self.animation = QPropertyAnimation(self, b'animated_size', self)
        self.animation.setEasingCurve(QEasingCurve.OutInCirc)
        self.animation.setDuration(500)
        self.set_dimensions()
        self.cover_cache = CoverCache()
        self.render_queue = LifoQueue()
        self.animating = None
        self.highlight_color = QColor(Qt.white)
        self.rating_font = QFont(rating_font())

    def set_dimensions(self):
        width = self.original_width = gprefs['cover_grid_width']
        height = self.original_height = gprefs['cover_grid_height']
        self.original_show_title = show_title = gprefs['cover_grid_show_title']
        self.original_show_emblems = gprefs['show_emblems']
        self.orginal_emblem_size = gprefs['emblem_size']
        self.orginal_emblem_position = gprefs['emblem_position']
        self.emblem_size = gprefs['emblem_size'] if self.original_show_emblems else 0
        try:
            self.gutter_position = getattr(self, self.orginal_emblem_position.upper())
        except Exception:
            self.gutter_position = self.TOP

        if height < 0.1:
            height = auto_height(self.parent())
        else:
            height *= self.parent().logicalDpiY() * CM_TO_INCH

        if width < 0.1:
            width = 0.75 * height
        else:
            width *= self.parent().logicalDpiX() * CM_TO_INCH
        self.cover_size = QSize(width, height)
        self.title_height = 0
        if show_title:
            f = self.parent().font()
            sz = f.pixelSize()
            if sz < 5:
                sz = f.pointSize() * self.parent().logicalDpiY() / 72.0
            self.title_height = max(25, sz + 10)
        self.item_size = self.cover_size + QSize(2 * self.MARGIN, (2 * self.MARGIN) + self.title_height)
        if self.emblem_size > 0:
            extra = self.emblem_size + self.MARGIN
            self.item_size += QSize(extra, 0) if self.gutter_position in (self.LEFT, self.RIGHT) else QSize(0, extra)
        self.calculate_spacing()
        self.animation.setStartValue(1.0)
        self.animation.setKeyValueAt(0.5, 0.5)
        self.animation.setEndValue(1.0)

    def calculate_spacing(self):
        spc = self.original_spacing = gprefs['cover_grid_spacing']
        if spc < 0.01:
            self.spacing = max(10, min(50, int(0.1 * self.original_width)))
        else:
            self.spacing = self.parent().logicalDpiX() * CM_TO_INCH * spc

    def sizeHint(self, option, index):
        return self.item_size

    def render_field(self, db, book_id):
        is_stars = False
        try:
            field = db.pref('field_under_covers_in_grid', 'title')
            if field == 'size':
                ans = human_readable(db.field_for(field, book_id, default_value=0))
            else:
                mi = db.get_proxy_metadata(book_id)
                display_name, ans, val, fm = mi.format_field_extended(field)
                if fm and fm['datatype'] == 'rating':
                    ans = rating_to_stars(val, fm['display'].get('allow_half_stars', False))
                    is_stars = True
            return ('' if ans is None else unicode_type(ans)), is_stars
        except Exception:
            if DEBUG:
                import traceback
                traceback.print_exc()
        return '', is_stars

    def render_emblem(self, book_id, rule, rule_index, cache, mi, db, formatter, template_cache):
        ans = cache[book_id].get(rule, False)
        if ans is not False:
            return ans, mi
        ans = None
        if mi is None:
            mi = db.get_proxy_metadata(book_id)
        ans = formatter.safe_format(rule, mi, '', mi, column_name='cover_grid%d' % rule_index, template_cache=template_cache) or None
        cache[book_id][rule] = ans
        return ans, mi

    def cached_emblem(self, cache, name, raw_icon=None):
        ans = cache.get(name, False)
        if ans is not False:
            return ans
        sz = self.emblem_size
        ans = None
        if raw_icon is not None:
            ans = raw_icon.pixmap(sz, sz)
        elif name == ':ondevice':
            ans = QIcon(I('ok.png')).pixmap(sz, sz)
        elif name:
            pmap = QIcon(os.path.join(config_dir, 'cc_icons', name)).pixmap(sz, sz)
            if not pmap.isNull():
                ans = pmap
        cache[name] = ans
        return ans

    def paint(self, painter, option, index):
        QStyledItemDelegate.paint(self, painter, option, empty_index)  # draw the hover and selection highlights
        m = index.model()
        db = m.db
        try:
            book_id = db.id(index.row())
        except (ValueError, IndexError, KeyError):
            return
        if book_id in m.ids_to_highlight_set:
            painter.save()
            try:
                painter.setPen(self.highlight_color)
                painter.setRenderHint(QPainter.Antialiasing, True)
                painter.drawRoundedRect(option.rect, 10, 10, Qt.RelativeSize)
            finally:
                painter.restore()
        marked = db.data.get_marked(book_id)
        db = db.new_api
        cdata = self.cover_cache[book_id]
        device_connected = self.parent().gui.device_connected is not None
        on_device = device_connected and db.field_for('ondevice', book_id)

        emblem_rules = db.pref('cover_grid_icon_rules', default=())
        emblems = []
        if self.emblem_size > 0:
            mi = None
            for i, (kind, column, rule) in enumerate(emblem_rules):
                icon_name, mi = self.render_emblem(book_id, rule, i, m.cover_grid_emblem_cache, mi, db, m.formatter, m.cover_grid_template_cache)
                if icon_name is not None:
                    pixmap = self.cached_emblem(m.cover_grid_bitmap_cache, icon_name)
                    if pixmap is not None:
                        emblems.append(pixmap)
            if marked:
                emblems.insert(0, self.cached_emblem(m.cover_grid_bitmap_cache, ':marked', m.marked_icon))
            if on_device:
                emblems.insert(0, self.cached_emblem(m.cover_grid_bitmap_cache, ':ondevice'))

        painter.save()
        right_adjust = 0
        try:
            rect = option.rect
            rect.adjust(self.MARGIN, self.MARGIN, -self.MARGIN, -self.MARGIN)
            if self.emblem_size > 0:
                self.paint_emblems(painter, rect, emblems)
            orect = QRect(rect)
            trect = QRect(rect)
            if self.title_height != 0:
                rect.setBottom(rect.bottom() - self.title_height)
                trect.setTop(trect.bottom() - self.title_height + 5)
            if cdata is None or cdata is False:
                title = db.field_for('title', book_id, default_value='')
                authors = ' & '.join(db.field_for('authors', book_id, default_value=()))
                painter.setRenderHint(QPainter.TextAntialiasing, True)
                painter.drawText(rect, Qt.AlignCenter|Qt.TextWordWrap, '%s\n\n%s' % (title, authors))
                if cdata is False:
                    self.render_queue.put(book_id)
                if self.title_height != 0:
                    self.paint_title(painter, trect, db, book_id)
            else:
                if self.animating is not None and self.animating.row() == index.row():
                    cdata = cdata.scaled(cdata.size() * self._animated_size)
                dpr = cdata.devicePixelRatio()
                cw, ch = int(cdata.width() / dpr), int(cdata.height() / dpr)
                dx = max(0, int((rect.width() - cw)/2.0))
                dy = max(0, rect.height() - ch)
                right_adjust = dx
                rect.adjust(dx, dy, -dx, 0)
                painter.drawPixmap(rect, cdata)
                if self.title_height != 0:
                    self.paint_title(painter, trect, db, book_id)
            if self.emblem_size > 0:
                return  # We dont draw embossed emblems as the ondevice/marked emblems are drawn in the gutter
            if marked:
                try:
                    p = self.marked_emblem
                except AttributeError:
                    p = self.marked_emblem = m.marked_icon.pixmap(48, 48)
                self.paint_embossed_emblem(p, painter, orect, right_adjust)

            if on_device:
                try:
                    p = self.on_device_emblem
                except AttributeError:
                    p = self.on_device_emblem = QIcon(I('ok.png')).pixmap(48, 48)
                self.paint_embossed_emblem(p, painter, orect, right_adjust, left=False)
        finally:
            painter.restore()

    def paint_title(self, painter, rect, db, book_id):
        painter.setRenderHint(QPainter.TextAntialiasing, True)
        title, is_stars = self.render_field(db, book_id)
        if is_stars:
            painter.setFont(self.rating_font)
        metrics = painter.fontMetrics()
        painter.setPen(self.highlight_color)
        painter.drawText(rect, Qt.AlignCenter|Qt.TextSingleLine,
                            metrics.elidedText(title, Qt.ElideRight, rect.width()))

    def paint_emblems(self, painter, rect, emblems):
        gutter = self.emblem_size + self.MARGIN
        grect = QRect(rect)
        gpos = self.gutter_position
        if gpos is self.TOP:
            grect.setBottom(grect.top() + gutter)
            rect.setTop(rect.top() + gutter)
        elif gpos is self.BOTTOM:
            grect.setTop(grect.bottom() - gutter + self.MARGIN)
            rect.setBottom(rect.bottom() - gutter)
        elif gpos is self.LEFT:
            grect.setRight(grect.left() + gutter)
            rect.setLeft(rect.left() + gutter)
        else:
            grect.setLeft(grect.right() - gutter + self.MARGIN)
            rect.setRight(rect.right() - gutter)
        horizontal = gpos in (self.TOP, self.BOTTOM)
        painter.save()
        painter.setClipRect(grect)
        try:
            for i, emblem in enumerate(emblems):
                delta = 0 if i == 0 else self.emblem_size + self.MARGIN
                grect.moveLeft(grect.left() + delta) if horizontal else grect.moveTop(grect.top() + delta)
                rect = QRect(grect)
                rect.setWidth(int(emblem.width() / emblem.devicePixelRatio())), rect.setHeight(int(emblem.height() / emblem.devicePixelRatio()))
                painter.drawPixmap(rect, emblem)
        finally:
            painter.restore()

    def paint_embossed_emblem(self, pixmap, painter, orect, right_adjust, left=True):
        drect = QRect(orect)
        pw = int(pixmap.width() / pixmap.devicePixelRatio())
        ph = int(pixmap.height() / pixmap.devicePixelRatio())
        if left:
            drect.setLeft(drect.left() + right_adjust)
            drect.setRight(drect.left() + pw)
        else:
            drect.setRight(drect.right() - right_adjust)
            drect.setLeft(drect.right() - pw + 1)
        drect.setBottom(drect.bottom() - self.title_height)
        drect.setTop(drect.bottom() - ph)
        painter.drawPixmap(drect, pixmap)

    @pyqtSlot(QHelpEvent, QAbstractItemView, QStyleOptionViewItem, QModelIndex, result=bool)
    def helpEvent(self, event, view, option, index):
        if event is not None and view is not None and event.type() == QEvent.ToolTip:
            try:
                db = index.model().db
            except AttributeError:
                return False
            try:
                book_id = db.id(index.row())
            except (ValueError, IndexError, KeyError):
                return False
            db = db.new_api
            device_connected = self.parent().gui.device_connected
            on_device = device_connected is not None and db.field_for('ondevice', book_id)
            p = prepare_string_for_xml
            title = db.field_for('title', book_id)
            authors = db.field_for('authors', book_id)
            if title and authors:
                title = '<b>%s</b>' % ('<br>'.join(wrap(p(title), 120)))
                authors = '<br>'.join(wrap(p(' & '.join(authors)), 120))
                tt = '%s<br><br>%s' % (title, authors)
                series = db.field_for('series', book_id)
                if series:
                    use_roman_numbers=config['use_roman_numerals_for_series_number']
                    val = _('Book %(sidx)s of <span class="series_name">%(series)s</span>')%dict(
                        sidx=fmt_sidx(db.field_for('series_index', book_id), use_roman=use_roman_numbers),
                        series=p(series))
                    tt += '<br><br>' + val
                if on_device:
                    val = _('This book is on the device in %s') % on_device
                    tt += '<br><br>' + val
                QToolTip.showText(event.globalPos(), tt, view)
                return True
        return False
Exemple #24
0
 def pressedCalendarButton(self):
     try:
         calendarButtonCurrentGeometry = QtCore.QRect(self.calendarToolButton.geometry())
         calendarButtonAnimation = QPropertyAnimation(self.calendarToolButton,'geometry'.encode(encoding='utf_8'))
         calendarButtonAnimation.setDuration(self.ANIMATIONDURATION)
         calendarButtonAnimation.setStartValue(calendarButtonCurrentGeometry)
         
         calendarWidgetCurrentGeometry = QtCore.QRect(self.calendarWidget.geometry())
         calendarWidgetAnimation = QPropertyAnimation(self.calendarWidget,'geometry'.encode(encoding='utf_8'))
         calendarWidgetAnimation.setDuration(self.ANIMATIONDURATION)
         calendarWidgetAnimation.setStartValue(calendarWidgetCurrentGeometry)
         
         if self.CalendarCurrentState == 'Closed':
             calendarButtonEndTopLeftCorner = QtCore.QPoint(self.calendarToolButton.pos() - QtCore.QPoint(275, 0))
             calendarWidgetEndTopLeftCorner = QtCore.QPoint(self.calendarWidget.pos() - QtCore.QPoint(275, 0))
             self.CalendarCurrentState = 'Open'
             self.Calendar = Calendar()
             self.calendarLabel.setText(self.Calendar.EventList)
         else:
             calendarButtonEndTopLeftCorner = QtCore.QPoint(self.calendarToolButton.pos() + QtCore.QPoint(275, 0))
             calendarWidgetEndTopLeftCorner = QtCore.QPoint(self.calendarWidget.pos() + QtCore.QPoint(275, 0))
             self.CalendarCurrentState = 'Closed'
         
         calendarButtonFinalGeometry = QtCore.QRect(calendarButtonEndTopLeftCorner, QtCore.QSize(self.calendarToolButton.width(), self.calendarToolButton.height()))
         calendarButtonAnimation.setEndValue(calendarButtonFinalGeometry)
         
         calendarWidgetFinalGeometry = QtCore.QRect(calendarWidgetEndTopLeftCorner, QtCore.QSize(self.calendarWidget.width(), self.calendarWidget.height()))
         calendarWidgetAnimation.setEndValue(calendarWidgetFinalGeometry)
         
         calendarButtonAnimation.start()
         calendarWidgetAnimation.start()
         #self.setcalendarWidgetStyleSheet()
         self.CalendarIconAnimation = calendarButtonAnimation
         self.CalendarWidgetAnimation = calendarWidgetAnimation
     except Exception as e:
         print (e.strerror)
Exemple #25
0
    def pressedAlarmButton(self):
        try:
            alarmButtonCurrentGeometry = QtCore.QRect(
                self.alarmToolButton.geometry())
            alarmButtonAnimation = QPropertyAnimation(
                self.alarmToolButton, 'geometry'.encode(encoding='utf_8'))
            alarmButtonAnimation.setDuration(self.ANIMATIONDURATION)
            alarmButtonAnimation.setStartValue(alarmButtonCurrentGeometry)

            alarmWidgetCurrentGeometry = QtCore.QRect(
                self.alarmWidget.geometry())
            alarmWidgetAnimation = QPropertyAnimation(
                self.alarmWidget, 'geometry'.encode(encoding='utf_8'))
            alarmWidgetAnimation.setDuration(self.ANIMATIONDURATION)
            alarmWidgetAnimation.setStartValue(alarmWidgetCurrentGeometry)

            if self.AlarmCurrentState == 'Closed':
                alarmButtonEndTopLeftCorner = QtCore.QPoint(
                    self.alarmToolButton.pos() - QtCore.QPoint(275, 0))
                alarmWidgetEndTopLeftCorner = QtCore.QPoint(
                    self.alarmWidget.pos() - QtCore.QPoint(275, 0))
                self.AlarmCurrentState = 'Open'
            else:
                alarmButtonEndTopLeftCorner = QtCore.QPoint(
                    self.alarmToolButton.pos() + QtCore.QPoint(275, 0))
                alarmWidgetEndTopLeftCorner = QtCore.QPoint(
                    self.alarmWidget.pos() + QtCore.QPoint(275, 0))
                self.AlarmCurrentState = 'Closed'

            alarmButtonFinalGeometry = QtCore.QRect(
                alarmButtonEndTopLeftCorner,
                QtCore.QSize(self.alarmToolButton.width(),
                             self.alarmToolButton.height()))
            alarmButtonAnimation.setEndValue(alarmButtonFinalGeometry)

            alarmWidgetFinalGeometry = QtCore.QRect(
                alarmWidgetEndTopLeftCorner,
                QtCore.QSize(self.alarmWidget.width(),
                             self.alarmWidget.height()))
            alarmWidgetAnimation.setEndValue(alarmWidgetFinalGeometry)

            alarmButtonAnimation.start()
            alarmWidgetAnimation.start()
            #self.setAlarmWidgetStyleSheet()
            self.AlarmIconAnimation = alarmButtonAnimation
            self.AlarmWidgetAnimation = alarmWidgetAnimation
        except Exception as e:
            print(e.strerror)
Exemple #26
0
class Pointer(QWidget):

    def __init__(self, gui):
        QWidget.__init__(self, gui)
        self.setObjectName('jobs_pointer')
        self.setVisible(False)
        self.resize(100, 80)
        self.animation = QPropertyAnimation(self, "geometry", self)
        self.animation.setDuration(750)
        self.animation.setLoopCount(2)
        self.animation.setEasingCurve(QEasingCurve.Linear)
        self.animation.finished.connect(self.hide)

        taily, heady = 0, 55
        self.arrow_path = QPainterPath(QPointF(40, taily))
        self.arrow_path.lineTo(40, heady)
        self.arrow_path.lineTo(20, heady)
        self.arrow_path.lineTo(50, self.height())
        self.arrow_path.lineTo(80, heady)
        self.arrow_path.lineTo(60, heady)
        self.arrow_path.lineTo(60, taily)
        self.arrow_path.closeSubpath()

        c = self.palette().color(QPalette.Active, QPalette.WindowText)
        self.color = QColor(c)
        self.color.setAlpha(100)
        self.brush = QBrush(self.color, Qt.SolidPattern)

        # from PyQt5.Qt import QTimer
        # QTimer.singleShot(1000, self.start)

    @property
    def gui(self):
        return self.parent()

    def point_at(self, frac):
        return (self.path.pointAtPercent(frac).toPoint() -
                QPoint(self.rect().center().x(), self.height()))

    def rect_at(self, frac):
        return QRect(self.point_at(frac), self.size())

    def abspos(self, widget):
        pos = widget.pos()
        parent = widget.parent()
        while parent is not self.gui:
            pos += parent.pos()
            parent = parent.parent()
        return pos

    def start(self):
        if config['disable_animations']:
            return
        self.setVisible(True)
        self.raise_()
        end = self.abspos(self.gui.jobs_button)
        end = QPointF( end.x() + self.gui.jobs_button.width()/3.0, end.y()+20)
        start = QPointF(end.x(), end.y() - 0.5*self.height())
        self.path = QPainterPath(QPointF(start))
        self.path.lineTo(end)
        self.path.closeSubpath()
        self.animation.setStartValue(self.rect_at(0.0))
        self.animation.setEndValue(self.rect_at(1.0))
        self.animation.setDirection(self.animation.Backward)
        num_keys = 100
        for i in xrange(1, num_keys):
            i /= num_keys
            self.animation.setKeyValueAt(i, self.rect_at(i))
        self.animation.start()

    def paintEvent(self, ev):
        p = QPainter(self)
        p.setRenderHints(p.Antialiasing)
        p.setBrush(self.brush)
        p.setPen(Qt.NoPen)
        p.drawPath(self.arrow_path)
        p.end()
Exemple #27
0
class FancyTab(QWidget):
    def __init__(self, tabbar):
        '''
        @param: tabbar QWidget
        '''
        super().__init__(tabbar)
        self.icon = QIcon()
        self.text = ''
        self._animator = QPropertyAnimation()
        self._tabbar = tabbar  # QWidget
        self._fader = 0.0

        self._animator.setPropertyName(b'fader')
        self._animator.setTargetObject(self)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)

    def fader(self):
        '''
        @return: float
        '''
        return self._fader

    def setFader(self, value):
        '''
        @param: value float
        '''
        self._fader = value
        self._tabbar.update()

    fader = pyqtProperty(float, fader, setFader)

    # override
    def sizeHint(self):
        '''
        @return: QSize
        '''
        boldFont = QFont(self.font())
        boldFont.setPointSizeF(styleHelper.sidebarFontSize())
        boldFont.setBold(True)
        fm = QFontMetrics(boldFont)
        spacing = 8
        width = 60 + spacing + 2
        iconHeight = 32
        ret = QSize(width, iconHeight + spacing + fm.height())
        return ret

    def fadeIn(self):
        self._animator.stop()
        self._animator.setDuration(80)
        self._animator.setEndValue(40)
        self._animator.start()

    def fadeOut(self):
        self._animator.stop()
        self._animator.setDuration(160)
        self._animator.setEndValue(0)
        self._animator.start()

    # protected:
    # override
    def enterEvent(self, event):
        self.fadeIn()

    # override
    def leaveEvent(self, event):
        self.fadeOut()
Exemple #28
0
class SlideFlip(QWidget):

    # API {{{

    # In addition the isVisible() and setVisible() methods must be present

    def __init__(self, parent):
        QWidget.__init__(self, parent)

        self.setGeometry(0, 0, 1, 1)
        self._current_width = 0
        self.before_image = self.after_image = None
        self.animation = QPropertyAnimation(self, 'current_width', self)
        self.setVisible(False)
        self.animation.valueChanged.connect(self.update)
        self.animation.finished.connect(self.finished)
        self.flip_forwards = True
        self.setAttribute(Qt.WA_OpaquePaintEvent)

    @property
    def running(self):
        'True iff animation is currently running'
        return self.animation.state() == self.animation.Running

    def initialize(self, image, forwards=True):
        '''
        Initialize the flipper, causes the flipper to show itself displaying
        the full `image`.

        :param image: The image to display as background
        :param forwards: If True flipper will flip forwards, otherwise
                         backwards

        '''
        self.flip_forwards = forwards
        self.before_image = QPixmap.fromImage(image)
        self.after_image = None
        self.setGeometry(0, 0, image.width(), image.height())
        self.setVisible(True)

    def __call__(self, image, duration=0.5):
        '''
        Start the animation. You must have called :meth:`initialize` first.

        :param duration: Animation duration in seconds.

        '''
        if self.running:
            return
        self.after_image = QPixmap.fromImage(image)

        if self.flip_forwards:
            self.animation.setStartValue(image.width())
            self.animation.setEndValue(0)
            t = self.before_image
            self.before_image = self.after_image
            self.after_image = t
            self.animation.setEasingCurve(QEasingCurve(QEasingCurve.InExpo))
        else:
            self.animation.setStartValue(0)
            self.animation.setEndValue(image.width())
            self.animation.setEasingCurve(QEasingCurve(QEasingCurve.OutExpo))

        self.animation.setDuration(duration * 1000)
        self.animation.start()

    # }}}

    def finished(self):
        self.setVisible(False)
        self.before_image = self.after_image = None

    def paintEvent(self, ev):
        if self.before_image is None:
            return
        canvas_size = self.rect()
        p = QPainter(self)
        p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)

        p.drawPixmap(canvas_size, self.before_image,
                    self.before_image.rect())
        if self.after_image is not None:
            width = self._current_width
            iw = self.after_image.width()
            sh = min(self.after_image.height(), canvas_size.height())

            if self.flip_forwards:
                source = QRect(max(0, iw - width), 0, width, sh)
            else:
                source = QRect(0, 0, width, sh)

            target = QRect(source)
            target.moveLeft(0)
            p.drawPixmap(target, self.after_image, source)

        p.end()

    def set_current_width(self, val):
        self._current_width = val

    current_width = pyqtProperty('int',
            fget=lambda self: self._current_width,
            fset=set_current_width
            )
Exemple #29
0
class ThrobbingButton(QToolButton):

    @pyqtProperty(int)
    def icon_size(self):
        return self._icon_size

    @icon_size.setter
    def icon_size(self, value):
        self._icon_size = value

    def __init__(self, *args):
        QToolButton.__init__(self, *args)
        # vertically size policy must be expanding for it to align inside a
        # toolbar
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        self._icon_size = -1
        QToolButton.setIcon(self, QIcon(I('donate.png')))
        self.setText('\xa0')
        self.animation = QPropertyAnimation(self, b'icon_size', self)
        self.animation.setDuration(60/72.*1000)
        self.animation.setLoopCount(4)
        self.animation.valueChanged.connect(self.value_changed)
        self.setCursor(Qt.PointingHandCursor)
        self.animation.finished.connect(self.animation_finished)

    def animation_finished(self):
        self.icon_size = self.iconSize().width()

    def enterEvent(self, ev):
        self.start_animation()

    def leaveEvent(self, ev):
        self.stop_animation()

    def value_changed(self, val):
        self.update()

    def start_animation(self):
        if config['disable_animations']:
            return
        if self.animation.state() != self.animation.Stopped or not self.isVisible():
            return
        size = self.iconSize().width()
        smaller = int(0.7 * size)
        self.animation.setStartValue(smaller)
        self.animation.setEndValue(size)
        QMetaObject.invokeMethod(self.animation, 'start', Qt.QueuedConnection)

    def stop_animation(self):
        self.animation.stop()
        self.animation_finished()

    def paintEvent(self, ev):
        size = self._icon_size if self._icon_size > 10 else self.iconSize().width()
        p = QPainter(self)
        opt = QStyleOptionToolButton()
        self.initStyleOption(opt)
        s = self.style()
        opt.iconSize = QSize(size, size)
        s.drawComplexControl(s.CC_ToolButton, opt, p, self)
Exemple #30
0
    def __init__(self, pathtoebook=None, debug_javascript=False, open_at=None,
                 start_in_fullscreen=False):
        MainWindow.__init__(self, debug_javascript)
        self.view.magnification_changed.connect(self.magnification_changed)
        self.show_toc_on_open = False
        self.current_book_has_toc = False
        self.iterator          = None
        self.current_page      = None
        self.pending_search    = None
        self.pending_search_dir= None
        self.pending_anchor    = None
        self.pending_reference = None
        self.pending_bookmark  = None
        self.pending_restore   = False
        self.cursor_hidden     = False
        self.existing_bookmarks= []
        self.selected_text     = None
        self.was_maximized     = False
        self.page_position_on_footnote_toggle = []
        self.read_settings()
        self.pos.value_changed.connect(self.update_pos_label)
        self.pos.setMinimumWidth(150)
        self.setFocusPolicy(Qt.StrongFocus)
        self.view.set_manager(self)
        self.pi = ProgressIndicator(self)
        self.action_quit = QAction(_('&Quit'), self)
        self.addAction(self.action_quit)
        self.view_resized_timer = QTimer(self)
        self.view_resized_timer.timeout.connect(self.viewport_resize_finished)
        self.view_resized_timer.setSingleShot(True)
        self.resize_in_progress = False
        self.action_reload = QAction(_('&Reload book'), self)
        self.action_reload.triggered.connect(self.reload_book)
        self.action_quit.triggered.connect(self.quit)
        self.action_reference_mode.triggered[bool].connect(self.view.reference_mode)
        self.action_metadata.triggered[bool].connect(self.metadata.setVisible)
        self.action_table_of_contents.toggled[bool].connect(self.set_toc_visible)
        self.action_copy.triggered[bool].connect(self.copy)
        self.action_font_size_larger.triggered.connect(self.font_size_larger)
        self.action_font_size_smaller.triggered.connect(self.font_size_smaller)
        self.action_open_ebook.triggered[bool].connect(self.open_ebook)
        self.action_next_page.triggered.connect(self.view.next_page)
        self.action_previous_page.triggered.connect(self.view.previous_page)
        self.action_find_next.triggered.connect(self.find_next)
        self.action_find_previous.triggered.connect(self.find_previous)
        self.action_full_screen.triggered[bool].connect(self.toggle_fullscreen)
        self.action_back.triggered[bool].connect(self.back)
        self.action_forward.triggered[bool].connect(self.forward)
        self.action_preferences.triggered.connect(self.do_config)
        self.pos.editingFinished.connect(self.goto_page_num)
        self.vertical_scrollbar.valueChanged[int].connect(lambda
                x:self.goto_page(x/100.))
        self.search.search.connect(self.find)
        self.search.focus_to_library.connect(lambda: self.view.setFocus(Qt.OtherFocusReason))
        self.toc.pressed[QModelIndex].connect(self.toc_clicked)
        self.toc.searched.connect(partial(self.toc_clicked, force=True))
        self.reference.goto.connect(self.goto)
        self.bookmarks.edited.connect(self.bookmarks_edited)
        self.bookmarks.activated.connect(self.goto_bookmark)
        self.bookmarks.create_requested.connect(self.bookmark)

        self.set_bookmarks([])
        self.load_theme_menu()

        if pathtoebook is not None:
            f = functools.partial(self.load_ebook, pathtoebook, open_at=open_at)
            QTimer.singleShot(50, f)
        self.window_mode_changed = None
        self.toggle_toolbar_action = QAction(_('Show/hide controls'), self)
        self.toggle_toolbar_action.setCheckable(True)
        self.toggle_toolbar_action.triggered.connect(self.toggle_toolbars)
        self.toolbar_hidden = None
        self.addAction(self.toggle_toolbar_action)
        self.full_screen_label_anim = QPropertyAnimation(
                self.full_screen_label, 'size')
        self.clock_timer = QTimer(self)
        self.clock_timer.timeout.connect(self.update_clock)

        self.action_print.triggered.connect(self.print_book)
        self.print_menu.actions()[0].triggered.connect(self.print_preview)
        self.clear_recent_history_action = QAction(
                _('Clear list of recently opened books'), self)
        self.clear_recent_history_action.triggered.connect(self.clear_recent_history)
        self.build_recent_menu()
        self.open_history_menu.triggered.connect(self.open_recent)

        for x in ('tool_bar', 'tool_bar2'):
            x = getattr(self, x)
            for action in x.actions():
                # So that the keyboard shortcuts for these actions will
                # continue to function even when the toolbars are hidden
                self.addAction(action)

        for plugin in self.view.document.all_viewer_plugins:
            plugin.customize_ui(self)
        self.view.document.settings_changed.connect(self.settings_changed)

        self.restore_state()
        self.settings_changed()
        self.action_toggle_paged_mode.toggled[bool].connect(self.toggle_paged_mode)
        if (start_in_fullscreen or self.view.document.start_in_fullscreen):
            self.action_full_screen.trigger()
        self.hide_cursor_timer = t = QTimer(self)
        t.setSingleShot(True), t.setInterval(3000)
        t.timeout.connect(self.hide_cursor)
        t.start()
 def show_error(self):
     # 窗体抖动效果
     animation = QPropertyAnimation(self)
     animation.setTargetObject(self)
     animation.setPropertyName(b'pos')
     animation.setKeyValueAt(0, self.pos())
     animation.setKeyValueAt(0.2, self.pos() + QPoint(15, 0))
     animation.setKeyValueAt(0.5, self.pos())
     animation.setKeyValueAt(0.7, self.pos() + QPoint(-15, 0))
     animation.setKeyValueAt(1, self.pos())
     animation.setDuration(100)
     animation.setLoopCount(3)
     animation.start(QAbstractAnimation.DeleteWhenStopped)
Exemple #32
0
class CoverView(QWidget):  # {{{

    cover_changed = pyqtSignal(object, object)
    cover_removed = pyqtSignal(object)
    open_cover_with = pyqtSignal(object, object)
    search_internet = pyqtSignal(object)

    def __init__(self, vertical, parent=None):
        QWidget.__init__(self, parent)
        self._current_pixmap_size = QSize(120, 120)
        self.vertical = vertical

        self.animation = QPropertyAnimation(self, b'current_pixmap_size', self)
        self.animation.setEasingCurve(QEasingCurve(QEasingCurve.OutExpo))
        self.animation.setDuration(1000)
        self.animation.setStartValue(QSize(0, 0))
        self.animation.valueChanged.connect(self.value_changed)

        self.setSizePolicy(
                QSizePolicy.Expanding if vertical else QSizePolicy.Minimum,
                QSizePolicy.Expanding)

        self.default_pixmap = QPixmap(I('default_cover.png'))
        self.pixmap = self.default_pixmap
        self.pwidth = self.pheight = None
        self.data = {}

        self.do_layout()

    def value_changed(self, val):
        self.update()

    def setCurrentPixmapSize(self, val):
        self._current_pixmap_size = val

    def do_layout(self):
        if self.rect().width() == 0 or self.rect().height() == 0:
            return
        pixmap = self.pixmap
        pwidth, pheight = pixmap.width(), pixmap.height()
        try:
            self.pwidth, self.pheight = fit_image(pwidth, pheight,
                            self.rect().width(), self.rect().height())[1:]
        except:
            self.pwidth, self.pheight = self.rect().width()-1, \
                    self.rect().height()-1
        self.current_pixmap_size = QSize(self.pwidth, self.pheight)
        self.animation.setEndValue(self.current_pixmap_size)

    def show_data(self, data):
        self.animation.stop()
        same_item = getattr(data, 'id', True) == self.data.get('id', False)
        self.data = {'id':data.get('id', None)}
        if data.cover_data[1]:
            self.pixmap = QPixmap.fromImage(data.cover_data[1])
            if self.pixmap.isNull() or self.pixmap.width() < 5 or \
                    self.pixmap.height() < 5:
                self.pixmap = self.default_pixmap
        else:
            self.pixmap = self.default_pixmap
        self.do_layout()
        self.update()
        if (not same_item and not config['disable_animations'] and
                self.isVisible()):
            self.animation.start()

    def paintEvent(self, event):
        canvas_size = self.rect()
        width = self.current_pixmap_size.width()
        extrax = canvas_size.width() - width
        if extrax < 0:
            extrax = 0
        x = int(extrax/2.)
        height = self.current_pixmap_size.height()
        extray = canvas_size.height() - height
        if extray < 0:
            extray = 0
        y = int(extray/2.)
        target = QRect(x, y, width, height)
        p = QPainter(self)
        p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
        try:
            dpr = self.devicePixelRatioF()
        except AttributeError:
            dpr = self.devicePixelRatio()
        spmap = self.pixmap.scaled(target.size() * dpr, Qt.KeepAspectRatio, Qt.SmoothTransformation)
        spmap.setDevicePixelRatio(dpr)
        p.drawPixmap(target, spmap)
        if gprefs['bd_overlay_cover_size']:
            sztgt = target.adjusted(0, 0, 0, -4)
            f = p.font()
            f.setBold(True)
            p.setFont(f)
            sz = u'\u00a0%d x %d\u00a0'%(self.pixmap.width(), self.pixmap.height())
            flags = Qt.AlignBottom|Qt.AlignRight|Qt.TextSingleLine
            szrect = p.boundingRect(sztgt, flags, sz)
            p.fillRect(szrect.adjusted(0, 0, 0, 4), QColor(0, 0, 0, 200))
            p.setPen(QPen(QColor(255,255,255)))
            p.drawText(sztgt, flags, sz)
        p.end()

    current_pixmap_size = pyqtProperty('QSize',
            fget=lambda self: self._current_pixmap_size,
            fset=setCurrentPixmapSize
            )

    def contextMenuEvent(self, ev):
        from calibre.gui2.open_with import populate_menu, edit_programs
        cm = QMenu(self)
        paste = cm.addAction(_('Paste cover'))
        copy = cm.addAction(_('Copy cover'))
        remove = cm.addAction(_('Remove cover'))
        gc = cm.addAction(_('Generate cover from metadata'))
        cm.addSeparator()
        if not QApplication.instance().clipboard().mimeData().hasImage():
            paste.setEnabled(False)
        copy.triggered.connect(self.copy_to_clipboard)
        paste.triggered.connect(self.paste_from_clipboard)
        remove.triggered.connect(self.remove_cover)
        gc.triggered.connect(self.generate_cover)

        m = QMenu(_('Open cover with...'))
        populate_menu(m, self.open_with, 'cover_image')
        if len(m.actions()) == 0:
            cm.addAction(_('Open cover with...'), self.choose_open_with)
        else:
            m.addSeparator()
            m.addAction(_('Add another application to open cover...'), self.choose_open_with)
            m.addAction(_('Edit Open with applications...'), partial(edit_programs, 'cover_image', self))
            cm.ocw = m
            cm.addMenu(m)
        cm.si = m = create_search_internet_menu(self.search_internet.emit)
        cm.addMenu(m)
        cm.exec_(ev.globalPos())

    def open_with(self, entry):
        id_ = self.data.get('id', None)
        if id_ is not None:
            self.open_cover_with.emit(id_, entry)

    def choose_open_with(self):
        from calibre.gui2.open_with import choose_program
        entry = choose_program('cover_image', self)
        if entry is not None:
            self.open_with(entry)

    def copy_to_clipboard(self):
        QApplication.instance().clipboard().setPixmap(self.pixmap)

    def paste_from_clipboard(self, pmap=None):
        if not isinstance(pmap, QPixmap):
            cb = QApplication.instance().clipboard()
            pmap = cb.pixmap()
            if pmap.isNull() and cb.supportsSelection():
                pmap = cb.pixmap(cb.Selection)
        if not pmap.isNull():
            self.update_cover(pmap)

    def update_cover(self, pmap=None, cdata=None):
        if pmap is None:
            pmap = QPixmap()
            pmap.loadFromData(cdata)
        if pmap.isNull():
            return
        if pmap.hasAlphaChannel():
            pmap = QPixmap.fromImage(blend_image(image_from_x(pmap)))
        self.pixmap = pmap
        self.do_layout()
        self.update()
        self.update_tooltip(getattr(self.parent(), 'current_path', ''))
        if not config['disable_animations']:
            self.animation.start()
        id_ = self.data.get('id', None)
        if id_ is not None:
            self.cover_changed.emit(id_, cdata or pixmap_to_data(pmap))

    def generate_cover(self, *args):
        book_id = self.data.get('id')
        if book_id is not None:
            from calibre.ebooks.covers import generate_cover
            from calibre.gui2.ui import get_gui
            mi = get_gui().current_db.new_api.get_metadata(book_id)
            cdata = generate_cover(mi)
            self.update_cover(cdata=cdata)

    def remove_cover(self):
        id_ = self.data.get('id', None)
        self.pixmap = self.default_pixmap
        self.do_layout()
        self.update()
        if id_ is not None:
            self.cover_removed.emit(id_)

    def update_tooltip(self, current_path):
        try:
            sz = self.pixmap.size()
        except:
            sz = QSize(0, 0)
        self.setToolTip(
            '<p>'+_('Double click to open the Book details window') +
            '<br><br>' + _('Path') + ': ' + current_path +
            '<br><br>' + _('Cover size: %(width)d x %(height)d pixels')%dict(
                width=sz.width(), height=sz.height())
        )
Exemple #33
0
class CoverView(QWidget):  # {{{

    cover_changed = pyqtSignal(object, object)
    cover_removed = pyqtSignal(object)

    def __init__(self, vertical, parent=None):
        QWidget.__init__(self, parent)
        self._current_pixmap_size = QSize(120, 120)
        self.vertical = vertical

        self.animation = QPropertyAnimation(self, "current_pixmap_size", self)
        self.animation.setEasingCurve(QEasingCurve(QEasingCurve.OutExpo))
        self.animation.setDuration(1000)
        self.animation.setStartValue(QSize(0, 0))
        self.animation.valueChanged.connect(self.value_changed)

        self.setSizePolicy(QSizePolicy.Expanding if vertical else QSizePolicy.Minimum, QSizePolicy.Expanding)

        self.default_pixmap = QPixmap(I("book.png"))
        self.pixmap = self.default_pixmap
        self.pwidth = self.pheight = None
        self.data = {}

        self.do_layout()

    def value_changed(self, val):
        self.update()

    def setCurrentPixmapSize(self, val):
        self._current_pixmap_size = val

    def do_layout(self):
        if self.rect().width() == 0 or self.rect().height() == 0:
            return
        pixmap = self.pixmap
        pwidth, pheight = pixmap.width(), pixmap.height()
        try:
            self.pwidth, self.pheight = fit_image(pwidth, pheight, self.rect().width(), self.rect().height())[1:]
        except:
            self.pwidth, self.pheight = self.rect().width() - 1, self.rect().height() - 1
        self.current_pixmap_size = QSize(self.pwidth, self.pheight)
        self.animation.setEndValue(self.current_pixmap_size)

    def show_data(self, data):
        self.animation.stop()
        same_item = getattr(data, "id", True) == self.data.get("id", False)
        self.data = {"id": data.get("id", None)}
        if data.cover_data[1]:
            self.pixmap = QPixmap.fromImage(data.cover_data[1])
            if self.pixmap.isNull() or self.pixmap.width() < 5 or self.pixmap.height() < 5:
                self.pixmap = self.default_pixmap
        else:
            self.pixmap = self.default_pixmap
        self.do_layout()
        self.update()
        if not same_item and not config["disable_animations"] and self.isVisible():
            self.animation.start()

    def paintEvent(self, event):
        canvas_size = self.rect()
        width = self.current_pixmap_size.width()
        extrax = canvas_size.width() - width
        if extrax < 0:
            extrax = 0
        x = int(extrax / 2.0)
        height = self.current_pixmap_size.height()
        extray = canvas_size.height() - height
        if extray < 0:
            extray = 0
        y = int(extray / 2.0)
        target = QRect(x, y, width, height)
        p = QPainter(self)
        p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
        p.drawPixmap(target, self.pixmap.scaled(target.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
        if gprefs["bd_overlay_cover_size"]:
            sztgt = target.adjusted(0, 0, 0, -4)
            f = p.font()
            f.setBold(True)
            p.setFont(f)
            sz = u"\u00a0%d x %d\u00a0" % (self.pixmap.width(), self.pixmap.height())
            flags = Qt.AlignBottom | Qt.AlignRight | Qt.TextSingleLine
            szrect = p.boundingRect(sztgt, flags, sz)
            p.fillRect(szrect.adjusted(0, 0, 0, 4), QColor(0, 0, 0, 200))
            p.setPen(QPen(QColor(255, 255, 255)))
            p.drawText(sztgt, flags, sz)
        p.end()

    current_pixmap_size = pyqtProperty("QSize", fget=lambda self: self._current_pixmap_size, fset=setCurrentPixmapSize)

    def contextMenuEvent(self, ev):
        cm = QMenu(self)
        paste = cm.addAction(_("Paste Cover"))
        copy = cm.addAction(_("Copy Cover"))
        remove = cm.addAction(_("Remove Cover"))
        if not QApplication.instance().clipboard().mimeData().hasImage():
            paste.setEnabled(False)
        copy.triggered.connect(self.copy_to_clipboard)
        paste.triggered.connect(self.paste_from_clipboard)
        remove.triggered.connect(self.remove_cover)
        cm.exec_(ev.globalPos())

    def copy_to_clipboard(self):
        QApplication.instance().clipboard().setPixmap(self.pixmap)

    def paste_from_clipboard(self, pmap=None):
        if not isinstance(pmap, QPixmap):
            cb = QApplication.instance().clipboard()
            pmap = cb.pixmap()
            if pmap.isNull() and cb.supportsSelection():
                pmap = cb.pixmap(cb.Selection)
        if not pmap.isNull():
            self.pixmap = pmap
            self.do_layout()
            self.update()
            self.update_tooltip(getattr(self.parent(), "current_path", ""))
            if not config["disable_animations"]:
                self.animation.start()
            id_ = self.data.get("id", None)
            if id_ is not None:
                self.cover_changed.emit(id_, pixmap_to_data(pmap))

    def remove_cover(self):
        id_ = self.data.get("id", None)
        self.pixmap = self.default_pixmap
        self.do_layout()
        self.update()
        if id_ is not None:
            self.cover_removed.emit(id_)

    def update_tooltip(self, current_path):
        try:
            sz = self.pixmap.size()
        except:
            sz = QSize(0, 0)
        self.setToolTip(
            "<p>"
            + _("Double-click to open Book Details window")
            + "<br><br>"
            + _("Path")
            + ": "
            + current_path
            + "<br><br>"
            + _("Cover size: %(width)d x %(height)d") % dict(width=sz.width(), height=sz.height())
        )
Exemple #34
0
class CoverDelegate(QStyledItemDelegate):

    MARGIN = 4
    TOP, LEFT, RIGHT, BOTTOM = object(), object(), object(), object()

    @pyqtProperty(float)
    def animated_size(self):
        return self._animated_size

    @animated_size.setter
    def animated_size(self, val):
        self._animated_size = val

    def __init__(self, parent):
        super(CoverDelegate, self).__init__(parent)
        self._animated_size = 1.0
        self.animation = QPropertyAnimation(self, b'animated_size', self)
        self.animation.setEasingCurve(QEasingCurve.OutInCirc)
        self.animation.setDuration(500)
        self.set_dimensions()
        self.cover_cache = CoverCache()
        self.render_queue = LifoQueue()
        self.animating = None
        self.highlight_color = QColor(Qt.white)
        self.rating_font = QFont(rating_font())

    def set_dimensions(self):
        width = self.original_width = gprefs['cover_grid_width']
        height = self.original_height = gprefs['cover_grid_height']
        self.original_show_title = show_title = gprefs['cover_grid_show_title']
        self.original_show_emblems = gprefs['show_emblems']
        self.orginal_emblem_size = gprefs['emblem_size']
        self.orginal_emblem_position = gprefs['emblem_position']
        self.emblem_size = gprefs['emblem_size'] if self.original_show_emblems else 0
        try:
            self.gutter_position = getattr(self, self.orginal_emblem_position.upper())
        except Exception:
            self.gutter_position = self.TOP

        if height < 0.1:
            height = auto_height(self.parent())
        else:
            height *= self.parent().logicalDpiY() * CM_TO_INCH

        if width < 0.1:
            width = 0.75 * height
        else:
            width *= self.parent().logicalDpiX() * CM_TO_INCH
        self.cover_size = QSize(width, height)
        self.title_height = 0
        if show_title:
            f = self.parent().font()
            sz = f.pixelSize()
            if sz < 5:
                sz = f.pointSize() * self.parent().logicalDpiY() / 72.0
            self.title_height = max(25, sz + 10)
        self.item_size = self.cover_size + QSize(2 * self.MARGIN, (2 * self.MARGIN) + self.title_height)
        if self.emblem_size > 0:
            extra = self.emblem_size + self.MARGIN
            self.item_size += QSize(extra, 0) if self.gutter_position in (self.LEFT, self.RIGHT) else QSize(0, extra)
        self.calculate_spacing()
        self.animation.setStartValue(1.0)
        self.animation.setKeyValueAt(0.5, 0.5)
        self.animation.setEndValue(1.0)

    def calculate_spacing(self):
        spc = self.original_spacing = gprefs['cover_grid_spacing']
        if spc < 0.01:
            self.spacing = max(10, min(50, int(0.1 * self.original_width)))
        else:
            self.spacing = self.parent().logicalDpiX() * CM_TO_INCH * spc

    def sizeHint(self, option, index):
        return self.item_size

    def render_field(self, db, book_id):
        is_stars = False
        try:
            field = db.pref('field_under_covers_in_grid', 'title')
            if field == 'size':
                ans = human_readable(db.field_for(field, book_id, default_value=0))
            else:
                mi = db.get_proxy_metadata(book_id)
                display_name, ans, val, fm = mi.format_field_extended(field)
                if fm and fm['datatype'] == 'rating':
                    ans = rating_to_stars(val, fm['display'].get('allow_half_stars', False))
                    is_stars = True
            return ('' if ans is None else unicode_type(ans)), is_stars
        except Exception:
            if DEBUG:
                import traceback
                traceback.print_exc()
        return '', is_stars

    def render_emblem(self, book_id, rule, rule_index, cache, mi, db, formatter, template_cache):
        ans = cache[book_id].get(rule, False)
        if ans is not False:
            return ans, mi
        ans = None
        if mi is None:
            mi = db.get_proxy_metadata(book_id)
        ans = formatter.safe_format(rule, mi, '', mi, column_name='cover_grid%d' % rule_index, template_cache=template_cache) or None
        cache[book_id][rule] = ans
        return ans, mi

    def cached_emblem(self, cache, name, raw_icon=None):
        ans = cache.get(name, False)
        if ans is not False:
            return ans
        sz = self.emblem_size
        ans = None
        if raw_icon is not None:
            ans = raw_icon.pixmap(sz, sz)
        elif name == ':ondevice':
            ans = QIcon(I('ok.png')).pixmap(sz, sz)
        elif name:
            pmap = QIcon(os.path.join(config_dir, 'cc_icons', name)).pixmap(sz, sz)
            if not pmap.isNull():
                ans = pmap
        cache[name] = ans
        return ans

    def paint(self, painter, option, index):
        QStyledItemDelegate.paint(self, painter, option, empty_index)  # draw the hover and selection highlights
        m = index.model()
        db = m.db
        try:
            book_id = db.id(index.row())
        except (ValueError, IndexError, KeyError):
            return
        if book_id in m.ids_to_highlight_set:
            painter.save()
            try:
                painter.setPen(self.highlight_color)
                painter.setRenderHint(QPainter.Antialiasing, True)
                painter.drawRoundedRect(option.rect, 10, 10, Qt.RelativeSize)
            finally:
                painter.restore()
        marked = db.data.get_marked(book_id)
        db = db.new_api
        cdata = self.cover_cache[book_id]
        device_connected = self.parent().gui.device_connected is not None
        on_device = device_connected and db.field_for('ondevice', book_id)

        emblem_rules = db.pref('cover_grid_icon_rules', default=())
        emblems = []
        if self.emblem_size > 0:
            mi = None
            for i, (kind, column, rule) in enumerate(emblem_rules):
                icon_name, mi = self.render_emblem(book_id, rule, i, m.cover_grid_emblem_cache, mi, db, m.formatter, m.cover_grid_template_cache)
                if icon_name is not None:
                    pixmap = self.cached_emblem(m.cover_grid_bitmap_cache, icon_name)
                    if pixmap is not None:
                        emblems.append(pixmap)
            if marked:
                emblems.insert(0, self.cached_emblem(m.cover_grid_bitmap_cache, ':marked', m.marked_icon))
            if on_device:
                emblems.insert(0, self.cached_emblem(m.cover_grid_bitmap_cache, ':ondevice'))

        painter.save()
        right_adjust = 0
        try:
            rect = option.rect
            rect.adjust(self.MARGIN, self.MARGIN, -self.MARGIN, -self.MARGIN)
            if self.emblem_size > 0:
                self.paint_emblems(painter, rect, emblems)
            orect = QRect(rect)
            if cdata is None or cdata is False:
                title = db.field_for('title', book_id, default_value='')
                authors = ' & '.join(db.field_for('authors', book_id, default_value=()))
                painter.setRenderHint(QPainter.TextAntialiasing, True)
                painter.drawText(rect, Qt.AlignCenter|Qt.TextWordWrap, '%s\n\n%s' % (title, authors))
                if cdata is False:
                    self.render_queue.put(book_id)
            else:
                if self.title_height != 0:
                    trect = QRect(rect)
                    rect.setBottom(rect.bottom() - self.title_height)
                if self.animating is not None and self.animating.row() == index.row():
                    cdata = cdata.scaled(cdata.size() * self._animated_size)
                dpr = cdata.devicePixelRatio()
                cw, ch = int(cdata.width() / dpr), int(cdata.height() / dpr)
                dx = max(0, int((rect.width() - cw)/2.0))
                dy = max(0, rect.height() - ch)
                right_adjust = dx
                rect.adjust(dx, dy, -dx, 0)
                painter.drawPixmap(rect, cdata)
                if self.title_height != 0:
                    rect = trect
                    rect.setTop(rect.bottom() - self.title_height + 5)
                    painter.setRenderHint(QPainter.TextAntialiasing, True)
                    title, is_stars = self.render_field(db, book_id)
                    if is_stars:
                        painter.setFont(self.rating_font)
                    metrics = painter.fontMetrics()
                    painter.setPen(self.highlight_color)
                    painter.drawText(rect, Qt.AlignCenter|Qt.TextSingleLine,
                                     metrics.elidedText(title, Qt.ElideRight, rect.width()))
            if self.emblem_size > 0:
                return  # We dont draw embossed emblems as the ondevice/marked emblems are drawn in the gutter
            if marked:
                try:
                    p = self.marked_emblem
                except AttributeError:
                    p = self.marked_emblem = m.marked_icon.pixmap(48, 48)
                self.paint_embossed_emblem(p, painter, orect, right_adjust)

            if on_device:
                try:
                    p = self.on_device_emblem
                except AttributeError:
                    p = self.on_device_emblem = QIcon(I('ok.png')).pixmap(48, 48)
                self.paint_embossed_emblem(p, painter, orect, right_adjust, left=False)
        finally:
            painter.restore()

    def paint_emblems(self, painter, rect, emblems):
        gutter = self.emblem_size + self.MARGIN
        grect = QRect(rect)
        gpos = self.gutter_position
        if gpos is self.TOP:
            grect.setBottom(grect.top() + gutter)
            rect.setTop(rect.top() + gutter)
        elif gpos is self.BOTTOM:
            grect.setTop(grect.bottom() - gutter + self.MARGIN)
            rect.setBottom(rect.bottom() - gutter)
        elif gpos is self.LEFT:
            grect.setRight(grect.left() + gutter)
            rect.setLeft(rect.left() + gutter)
        else:
            grect.setLeft(grect.right() - gutter + self.MARGIN)
            rect.setRight(rect.right() - gutter)
        horizontal = gpos in (self.TOP, self.BOTTOM)
        painter.save()
        painter.setClipRect(grect)
        try:
            for i, emblem in enumerate(emblems):
                delta = 0 if i == 0 else self.emblem_size + self.MARGIN
                grect.moveLeft(grect.left() + delta) if horizontal else grect.moveTop(grect.top() + delta)
                rect = QRect(grect)
                rect.setWidth(int(emblem.width() / emblem.devicePixelRatio())), rect.setHeight(int(emblem.height() / emblem.devicePixelRatio()))
                painter.drawPixmap(rect, emblem)
        finally:
            painter.restore()

    def paint_embossed_emblem(self, pixmap, painter, orect, right_adjust, left=True):
        drect = QRect(orect)
        pw = int(pixmap.width() / pixmap.devicePixelRatio())
        ph = int(pixmap.height() / pixmap.devicePixelRatio())
        if left:
            drect.setLeft(drect.left() + right_adjust)
            drect.setRight(drect.left() + pw)
        else:
            drect.setRight(drect.right() - right_adjust)
            drect.setLeft(drect.right() - pw + 1)
        drect.setBottom(drect.bottom() - self.title_height)
        drect.setTop(drect.bottom() - ph)
        painter.drawPixmap(drect, pixmap)

    @pyqtSlot(QHelpEvent, QAbstractItemView, QStyleOptionViewItem, QModelIndex, result=bool)
    def helpEvent(self, event, view, option, index):
        if event is not None and view is not None and event.type() == QEvent.ToolTip:
            try:
                db = index.model().db
            except AttributeError:
                return False
            try:
                book_id = db.id(index.row())
            except (ValueError, IndexError, KeyError):
                return False
            db = db.new_api
            device_connected = self.parent().gui.device_connected
            on_device = device_connected is not None and db.field_for('ondevice', book_id)
            p = prepare_string_for_xml
            title = db.field_for('title', book_id)
            authors = db.field_for('authors', book_id)
            if title and authors:
                title = '<b>%s</b>' % ('<br>'.join(wrap(p(title), 120)))
                authors = '<br>'.join(wrap(p(' & '.join(authors)), 120))
                tt = '%s<br><br>%s' % (title, authors)
                series = db.field_for('series', book_id)
                if series:
                    use_roman_numbers=config['use_roman_numerals_for_series_number']
                    val = _('Book %(sidx)s of <span class="series_name">%(series)s</span>')%dict(
                        sidx=fmt_sidx(db.field_for('series_index', book_id), use_roman=use_roman_numbers),
                        series=p(series))
                    tt += '<br><br>' + val
                if on_device:
                    val = _('This book is on the device in %s') % on_device
                    tt += '<br><br>' + val
                QToolTip.showText(event.globalPos(), tt, view)
                return True
        return False
class IconButton(QPushButton):
    def __init__(self,
                 icon,
                 title,
                 parent,
                 size=None,
                 padding=None,
                 color=None,
                 hover_color=None):
        QPushButton.__init__(self, parent)
        self._theme_manager = ThemeManager.get(self)
        self._background_color = hover_color or self._theme_manager.get_color(
            'button_background_hover')
        self.setToolTip(title)
        self._icon = Icon(self, icon, size, padding, color)
        self._iconname = icon

        self._layout = QHBoxLayout(self)
        self._layout.addWidget(self._icon)
        self._layout.setContentsMargins(0, 0, 0, 0)
        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self._size = size
        self.setFlat(True)
        self._background_opacity = 0.0
        self._background_opacity_stop = 0.0
        self._update_style_sheet()
        self._animation = QPropertyAnimation(self, b'backgroundOpacity')
        self.clicked.connect(self._update_background_opacity_stop)

    def setColor(self, color=None):
        self._icon.setColor(color)

    def setIcon(self, icon):
        self._icon.loadIcon(icon)

    @pyqtProperty(float)
    def backgroundOpacity(self):
        return self._background_opacity

    @backgroundOpacity.setter
    def backgroundOpacity(self, value):
        self._background_opacity = value
        self._update_style_sheet()

    def sizeHint(self):
        return self._size or self._icon.sizeHint()

    def enterEvent(self, event):
        if self.isEnabled():
            self._start_opacity_animation(350, 0, 1)
        return QPushButton.enterEvent(self, event)

    def leaveEvent(self, event):
        if self.isEnabled():
            self._start_opacity_animation(350, 1, 0)
        return QPushButton.leaveEvent(self, event)

    def mousePressEvent(self, event):
        if self.isEnabled():
            self._start_opacity_animation(50, 1, 0.6)
        return QPushButton.mousePressEvent(self, event)

    def mouseReleaseEvent(self, event):
        if self.isEnabled():
            self._start_opacity_animation(50, self._background_opacity, 1)
        return QPushButton.mouseReleaseEvent(self, event)

    def _start_opacity_animation(self, duration, start, end):
        self._animation.stop()
        self._animation.setDuration(duration)
        self._animation.setStartValue(start)
        self._animation.setEndValue(end)
        self._animation.setEasingCurve(QEasingCurve.BezierSpline)
        self._animation.start()

    def setEnabled(self, value):
        QPushButton.setEnabled(self, value)
        self._icon.setEnabled(value)

    def _update_background_opacity_stop(self):
        self._background_opacity_stop = 0.0 if not self.isChecked() else 0.8
        self._update_style_sheet()

    def setChecked(self, value):
        QPushButton.setChecked(self, value)
        self._update_background_opacity_stop()

    def _update_style_sheet(self):
        stylesheet = """
Icon {{
    background-color: {color};
    border-radius: 2px;
}}
QPushButton {{
    background-color: transparent;
    border: none;
}}
""".format(color=format_color(
            self._background_color, ColorFormat.rgba_string_256,
            max(self._background_opacity, self._background_opacity_stop)))
        self.setStyleSheet(stylesheet)