Ejemplo n.º 1
0
class MColorPaletteDialog(QtWidgets.QDialog):
    def __init__(self, init_color, parent=None):
        super(MColorPaletteDialog, self).__init__(parent)
        self.setWindowTitle("DAYU Color Palette")
        self.primary_color = QtGui.QColor(init_color)
        self.color_chart = MColorChart()
        self.choose_color_button = QtWidgets.QPushButton()
        self.choose_color_button.setFixedSize(QtCore.QSize(100, 30))
        self.color_label = QtWidgets.QLabel()
        self.info_label = MLabel()
        self.info_label.setProperty("error", True)
        color_lay = QtWidgets.QHBoxLayout()
        color_lay.addWidget(MLabel("Primary Color:"))
        color_lay.addWidget(self.choose_color_button)
        color_lay.addWidget(self.color_label)
        color_lay.addWidget(self.info_label)
        color_lay.addStretch()
        dialog = QtWidgets.QColorDialog(self.primary_color, parent=self)
        dialog.setWindowFlags(QtCore.Qt.Widget)
        dialog.setOption(QtWidgets.QColorDialog.NoButtons)
        dialog.currentColorChanged.connect(self.slot_color_changed)
        setting_lay = QtWidgets.QVBoxLayout()
        setting_lay.addLayout(color_lay)
        setting_lay.addWidget(MDivider())
        setting_lay.addWidget(dialog)

        main_lay = QtWidgets.QHBoxLayout()
        main_lay.addWidget(self.color_chart)
        main_lay.addLayout(setting_lay)
        self.setLayout(main_lay)
        self.update_color()

    @QtCore.Slot(QtGui.QColor)
    def slot_color_changed(self, color):
        self.primary_color = color
        light = self.primary_color.lightness()
        saturation = self.primary_color.saturation()
        self.info_label.setText("")
        if light <= 70:
            self.info_label.setText("亮度建议不低于70(现在 {})".format(light))
        if saturation <= 70:
            self.info_label.setText("饱和度建议不低于70(现在 {})".format(saturation))

        self.update_color()

    def update_color(self):
        self.choose_color_button.setStyleSheet(
            "border-radius: 0;border: none;border:1px solid gray;"
            "background-color:{};".format(self.primary_color.name()))
        self.color_label.setText(self.primary_color.name())
        self.color_chart.set_colors([
            utils.generate_color(self.primary_color, index + 1)
            for index in range(10)
        ])
Ejemplo n.º 2
0
def test_label_elide_mode(qtbot, text, elide):
    """Test MLabel elide mode"""
    main_widget = QWidget()
    main_widget.setGeometry(0, 0, 30, 200)
    main_lay = QVBoxLayout()
    main_widget.setLayout(main_lay)

    label_left = MLabel()
    label_left.set_elide_mode(Qt.ElideLeft)
    label_left.setText(text)
    label_right = MLabel()
    label_right.set_elide_mode(Qt.ElideRight)
    label_right.setText(text)
    label_center = MLabel(text)
    label_center.set_elide_mode(Qt.ElideMiddle)
    label_center.setText(text)

    main_lay.addWidget(label_left)
    main_lay.addWidget(label_right)
    main_lay.addWidget(label_center)

    qtbot.addWidget(main_widget)

    main_widget.show()
    ellipsis = u'…'
    if elide:
        assert label_left.property('text').startswith(ellipsis)
        assert label_right.property('text').endswith(ellipsis)
        center_text = label_center.property('text')
        assert center_text.count(ellipsis) \
               and not center_text.endswith(ellipsis)
    else:
        assert label_left.property('text') == label_left.text()
        assert label_right.property('text') == label_right.text()
        assert label_center.property('text') == label_center.text()
    assert label_left.get_elide_mode() == Qt.ElideLeft
    assert label_right.get_elide_mode() == Qt.ElideRight
    assert label_center.get_elide_mode() == Qt.ElideMiddle
Ejemplo n.º 3
0
class MSequenceFile(QWidget, MFieldMixin):
    '''
    这个类必须依赖 DayuPath
    props:
        path: six.string_types
        sequence: bool
    '''
    sig_is_sequence_changed = Signal(bool)

    def __init__(self, size=None, parent=None):
        super(MSequenceFile, self).__init__(parent)
        self.sequence_obj = None
        size = size or dayu_theme.small
        self._file_label = MLineEdit()
        self._file_label.set_dayu_size(size)
        self._file_label.setReadOnly(True)
        self._is_sequence_check_box = MCheckBox(self.tr('Sequence'))
        self._is_sequence_check_box.toggled.connect(
            functools.partial(self.setProperty, 'sequence'))
        self._is_sequence_check_box.toggled.connect(
            self.sig_is_sequence_changed)

        self._info_label = MLabel().secondary()
        self._error_label = MLabel().secondary()
        self._error_label.setProperty('error', True)
        self._error_label.setMinimumWidth(100)
        self._error_label.set_elide_mode(Qt.ElideMiddle)

        seq_lay = QHBoxLayout()
        seq_lay.addWidget(self._is_sequence_check_box)
        seq_lay.addWidget(self._info_label)
        seq_lay.addWidget(self._error_label)
        seq_lay.setStretchFactor(self._is_sequence_check_box, 0)
        seq_lay.setStretchFactor(self._info_label, 0)
        seq_lay.setStretchFactor(self._error_label, 100)

        self._main_lay = QVBoxLayout()
        self._main_lay.setContentsMargins(0, 0, 0, 0)
        self._main_lay.addWidget(self._file_label)
        self._main_lay.addLayout(seq_lay)
        self.setLayout(self._main_lay)
        self.set_sequence(True)

    def _set_path(self, value):
        path = DayuPath(value)
        for seq_obj in path.scan():
            self.sequence_obj = seq_obj
        self._update_info()

    def set_path(self, value):
        self.setProperty('path', value)

    def set_sequence(self, value):
        assert isinstance(value, bool)
        self.setProperty('sequence', value)

    def _set_sequence(self, value):
        if value != self._is_sequence_check_box.isChecked():
            # 更新来自代码
            self._is_sequence_check_box.setChecked(value)
            self.sig_is_sequence_changed.emit(value)
        self._update_info()

    def _update_info(self):
        self._file_label.setProperty(
            'text', self.sequence_obj
            if self.property('sequence') else self.property('path'))
        if self.sequence_obj:
            self._info_label.setText(u'Format: {ext}  '
                                     u'Total: {count}  '
                                     u'Range: {start}-{end}'.format(
                                         ext=self.sequence_obj.ext,
                                         count=len(self.sequence_obj.frames),
                                         start=self.sequence_obj.frames[0]
                                         if self.sequence_obj.frames else '/',
                                         end=self.sequence_obj.frames[-1]
                                         if self.sequence_obj.frames else '/'))
            error_info = u'Missing: {}'.format(
                self.sequence_obj.missing) if self.sequence_obj.missing else ''
            self._error_label.setText(error_info)
            self._error_label.setToolTip(error_info)
        self._info_label.setVisible(self.property('sequence'))
        self._error_label.setVisible(self.property('sequence'))
Ejemplo n.º 4
0
    def _init_ui(self):
        title_lay = QtWidgets.QGridLayout()
        title_lay.addWidget(MLabel("一级标题").h1(), 0, 0)
        title_lay.addWidget(MLabel("二级标题").h2(), 1, 0)
        title_lay.addWidget(MLabel("三级标题").h3(), 2, 0)
        title_lay.addWidget(MLabel("四级标题").h4(), 3, 0)
        title_lay.addWidget(MLabel("h1 Level").h1(), 0, 1)
        title_lay.addWidget(MLabel("h2 Level").h2(), 1, 1)
        title_lay.addWidget(MLabel("h3 Level").h3(), 2, 1)
        title_lay.addWidget(MLabel("h4 Level").h4(), 3, 1)

        text_type_lay = QtWidgets.QHBoxLayout()
        text_type_lay.addWidget(MLabel("MLabel: Normal"))
        text_type_lay.addWidget(MLabel("MLabel: Secondary").secondary())
        text_type_lay.addWidget(MLabel("MLabel: Warning").warning())
        text_type_lay.addWidget(MLabel("MLabel: Danger").danger())
        disable_text = MLabel("MLabel: Disabled")
        disable_text.setEnabled(False)
        text_type_lay.addWidget(disable_text)

        text_attr_lay = QtWidgets.QHBoxLayout()
        text_attr_lay.addWidget(MLabel("MLabel: Mark").mark())
        text_attr_lay.addWidget(MLabel("MLabel: Code").code())
        text_attr_lay.addWidget(MLabel("MLabel: Underline").underline())
        text_attr_lay.addWidget(MLabel("MLabel: Delete").delete())
        text_attr_lay.addWidget(MLabel("MLabel: Strong").strong())

        text_mix_lay = QtWidgets.QHBoxLayout()
        text_mix_lay.addWidget(
            MLabel("MLabel: Strong & Underline").strong().underline()
        )
        text_mix_lay.addWidget(MLabel("MLabel: Danger & Delete").danger().delete())
        text_mix_lay.addWidget(MLabel("MLabel: Warning & Strong").warning().strong())
        text_mix_lay.addWidget(MLabel("MLabel: H4 & Mark").h4().mark())

        data_bind_lay = QtWidgets.QHBoxLayout()
        data_bind_label = MLabel()
        button = MPushButton(text="Random An Animal").primary()
        button.clicked.connect(self.slot_change_text)
        data_bind_lay.addWidget(data_bind_label)
        data_bind_lay.addWidget(button)
        data_bind_lay.addStretch()
        self.register_field("show_text", "Guess")
        self.bind("show_text", data_bind_label, "text")

        lay_elide = QtWidgets.QVBoxLayout()
        label_none = MLabel(
            "This is a elide NONE mode label. "
            "Ellipsis should NOT appear in the text."
        )
        label_left = MLabel(
            "This is a elide LEFT mode label. "
            "The ellipsis should appear at the beginning of the text. "
            "xiao mao xiao gou xiao ci wei"
        )
        label_left.set_elide_mode(QtCore.Qt.ElideLeft)
        label_middle = MLabel(
            "This is a elide MIDDLE mode label. "
            "The ellipsis should appear in the middle of the text. "
            "xiao mao xiao gou xiao ci wei"
        )
        label_middle.set_elide_mode(QtCore.Qt.ElideMiddle)
        label_right = MLabel()
        label_right.setText(
            "This is a elide RIGHT mode label. "
            "The ellipsis should appear at the end of the text. "
            "Some text to fill the line bala bala bala."
        )
        label_right.set_elide_mode(QtCore.Qt.ElideRight)
        lay_elide.addWidget(label_none)
        lay_elide.addWidget(label_left)
        lay_elide.addWidget(label_middle)
        lay_elide.addWidget(label_right)

        hyper_label_1 = MLabel()
        hyper_label_1.set_link("https://baidu.com", text="baidu")
        hyper_label_2 = MLabel()
        hyper_label_2.set_link("https://baidu.com")
        hyper_label_3 = MLabel()
        hyper_label_3.set_link(
            "https://github.com/phenom-films/dayu_widgets", text="Dayu Widgets"
        )

        hyperlink_lay = QtWidgets.QVBoxLayout()
        hyperlink_lay.addWidget(hyper_label_1)
        hyperlink_lay.addWidget(hyper_label_2)
        hyperlink_lay.addWidget(hyper_label_3)
        # hyperlink_lay.addWidget()

        main_lay = QtWidgets.QVBoxLayout()
        main_lay.addWidget(MDivider("different level"))
        main_lay.addLayout(title_lay)
        main_lay.addWidget(MDivider("different type"))
        main_lay.addLayout(text_type_lay)
        main_lay.addWidget(MDivider("different property"))
        main_lay.addLayout(text_attr_lay)
        main_lay.addWidget(MDivider("mix"))
        main_lay.addLayout(text_mix_lay)

        # main_lay.addWidget(MDivider('data bind'))
        # main_lay.addLayout(data_bind_lay)
        main_lay.addWidget(MDivider("elide mode"))
        main_lay.addLayout(lay_elide)
        main_lay.addWidget(MDivider("hyperlink"))
        main_lay.addLayout(hyperlink_lay)
        main_lay.addStretch()
        self.setLayout(main_lay)
Ejemplo n.º 5
0
class MProgressCircle(QProgressBar):
    """
    MProgressCircle: Display the current progress of an operation flow.
    When you need to display the completion percentage of an operation.

    Property:
        dayu_width: int
        dayu_color: str
    """

    def __init__(self, dashboard=False, parent=None):
        super(MProgressCircle, self).__init__(parent)
        self._main_lay = QHBoxLayout()
        self._default_label = MLabel().h3()
        self._default_label.setAlignment(Qt.AlignCenter)
        self._main_lay.addWidget(self._default_label)
        self.setLayout(self._main_lay)
        self._color = None
        self._width = None

        self._start_angle = 90 * 16
        self._max_delta_angle = 360 * 16
        self._height_factor = 1.0
        self._width_factor = 1.0
        if dashboard:
            self._start_angle = 225 * 16
            self._max_delta_angle = 270 * 16
            self._height_factor = (2 + pow(2, 0.5)) / 4 + 0.03

        self.set_dayu_width(120)
        self.set_dayu_color(dayu_theme.primary_color)

    def set_widget(self, widget):
        """
        Set a custom widget to show on the circle's inner center
         and replace the default percent label
        :param widget: QWidget
        :return: None
        """
        self.setTextVisible(False)
        self._main_lay.addWidget(widget)

    def get_dayu_width(self):
        """
        Get current circle fixed width
        :return: int
        """
        return self._width

    def set_dayu_width(self, value):
        """
        Set current circle fixed width
        :param value: int
        :return: None
        """
        self._width = value
        self.setFixedSize(QSize(self._width * self._width_factor,
                                self._width * self._height_factor))

    def get_dayu_color(self):
        """
        Get current circle foreground color
        :return: str
        """
        return self._color

    def set_dayu_color(self, value):
        """
        Set current circle's foreground color
        :param value: str
        :return:
        """
        self._color = value
        self.update()

    dayu_color = Property(str, get_dayu_color, set_dayu_color)
    dayu_width = Property(int, get_dayu_width, set_dayu_width)

    def paintEvent(self, event):
        """Override QProgressBar's paintEvent."""
        if self.text() != self._default_label.text():
            self._default_label.setText(self.text())
        if self.isTextVisible() != self._default_label.isVisible():
            self._default_label.setVisible(self.isTextVisible())

        percent = utils.get_percent(self.value(), self.minimum(), self.maximum())
        total_width = self.get_dayu_width()
        pen_width = int(3 * total_width / 50.0)
        radius = total_width - pen_width - 1

        painter = QPainter(self)
        painter.setRenderHints(QPainter.Antialiasing)

        # draw background circle
        pen_background = QPen()
        pen_background.setWidth(pen_width)
        pen_background.setColor(dayu_theme.background_selected_color)
        pen_background.setCapStyle(Qt.RoundCap)
        painter.setPen(pen_background)
        painter.drawArc(pen_width / 2.0 + 1,
                        pen_width / 2.0 + 1,
                        radius,
                        radius,
                        self._start_angle,
                        -self._max_delta_angle)

        # draw foreground circle
        pen_foreground = QPen()
        pen_foreground.setWidth(pen_width)
        pen_foreground.setColor(self._color)
        pen_foreground.setCapStyle(Qt.RoundCap)
        painter.setPen(pen_foreground)
        painter.drawArc(pen_width / 2.0 + 1,
                        pen_width / 2.0 + 1,
                        radius,
                        radius,
                        self._start_angle,
                        -percent * 0.01 * self._max_delta_angle)
        painter.end()

    @classmethod
    def dashboard(cls, parent=None):
        """Create a dashboard style MCircle"""
        return MProgressCircle(dashboard=True, parent=parent)
Ejemplo n.º 6
0
    def __init__(self, text, duration=None, dayu_type=None, parent=None):
        super(MToast, self).__init__(parent)
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.Dialog
                            | Qt.WA_TranslucentBackground
                            | Qt.WA_DeleteOnClose)
        self.setAttribute(Qt.WA_StyledBackground)

        _icon_lay = QHBoxLayout()
        _icon_lay.addStretch()

        if dayu_type == MToast.LoadingType:
            _icon_lay.addWidget(
                MLoading(size=dayu_theme.huge,
                         color=dayu_theme.text_color_inverse))
        else:
            _icon_label = MAvatar()
            _icon_label.set_dayu_size(60)
            _icon_label.set_dayu_image(
                MPixmap('{}_line.svg'.format(dayu_type or MToast.InfoType),
                        dayu_theme.text_color_inverse))
            _icon_lay.addWidget(_icon_label)
        _icon_lay.addStretch()

        _content_label = MLabel()
        _content_label.setText(text)
        _content_label.setAlignment(Qt.AlignCenter)

        _main_lay = QVBoxLayout()
        _main_lay.setContentsMargins(0, 0, 0, 0)
        _main_lay.addStretch()
        _main_lay.addLayout(_icon_lay)
        _main_lay.addSpacing(10)
        _main_lay.addWidget(_content_label)
        _main_lay.addStretch()
        self.setLayout(_main_lay)
        self.setFixedSize(QSize(120, 120))

        _close_timer = QTimer(self)
        _close_timer.setSingleShot(True)
        _close_timer.timeout.connect(self.close)
        _close_timer.timeout.connect(self.sig_closed)
        _close_timer.setInterval(
            (duration or self.default_config.get('duration')) * 1000)

        _ani_timer = QTimer(self)
        _ani_timer.timeout.connect(self._fade_out)
        _ani_timer.setInterval(
            (duration or self.default_config.get('duration')) * 1000 - 300)

        _close_timer.start()
        _ani_timer.start()

        self._opacity_ani = QPropertyAnimation()
        self._opacity_ani.setTargetObject(self)
        self._opacity_ani.setDuration(300)
        self._opacity_ani.setEasingCurve(QEasingCurve.OutCubic)
        self._opacity_ani.setPropertyName(b'windowOpacity')
        self._opacity_ani.setStartValue(0.0)
        self._opacity_ani.setEndValue(0.9)

        self._get_center_position(parent)
        self._fade_int()
Ejemplo n.º 7
0
class MMessage(QWidget):
    """
    Display global messages as feedback in response to user operations.
    """
    InfoType = 'info'
    SuccessType = 'success'
    WarningType = 'warning'
    ErrorType = 'error'
    LoadingType = 'loading'

    default_config = {'duration': 2, 'top': 24}

    sig_closed = Signal()

    def __init__(self,
                 text,
                 duration=None,
                 dayu_type=None,
                 closable=False,
                 parent=None):
        super(MMessage, self).__init__(parent)
        self.setObjectName('message')
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.Dialog
                            | Qt.WA_TranslucentBackground
                            | Qt.WA_DeleteOnClose)
        self.setAttribute(Qt.WA_StyledBackground)

        if dayu_type == MMessage.LoadingType:
            _icon_label = MLoading.tiny()
        else:
            _icon_label = MAvatar.tiny()
            current_type = dayu_type or MMessage.InfoType
            _icon_label.set_dayu_image(
                MPixmap('{}_fill.svg'.format(current_type),
                        vars(dayu_theme).get(current_type + '_color')))

        self._content_label = MLabel(parent=self)
        # self._content_label.set_elide_mode(Qt.ElideMiddle)
        self._content_label.setText(text)

        self._close_button = MToolButton(
            parent=self).icon_only().svg('close_line.svg').tiny()
        self._close_button.clicked.connect(self.close)
        self._close_button.setVisible(closable or False)

        self._main_lay = QHBoxLayout()
        self._main_lay.addWidget(_icon_label)
        self._main_lay.addWidget(self._content_label)
        self._main_lay.addStretch()
        self._main_lay.addWidget(self._close_button)
        self.setLayout(self._main_lay)

        _close_timer = QTimer(self)
        _close_timer.setSingleShot(True)
        _close_timer.timeout.connect(self.close)
        _close_timer.timeout.connect(self.sig_closed)
        _close_timer.setInterval(
            (duration or self.default_config.get('duration')) * 1000)

        _ani_timer = QTimer(self)
        _ani_timer.timeout.connect(self._fade_out)
        _ani_timer.setInterval(
            (duration or self.default_config.get('duration')) * 1000 - 300)

        _close_timer.start()
        _ani_timer.start()

        self._pos_ani = QPropertyAnimation(self)
        self._pos_ani.setTargetObject(self)
        self._pos_ani.setEasingCurve(QEasingCurve.OutCubic)
        self._pos_ani.setDuration(300)
        self._pos_ani.setPropertyName(b'pos')

        self._opacity_ani = QPropertyAnimation()
        self._opacity_ani.setTargetObject(self)
        self._opacity_ani.setDuration(300)
        self._opacity_ani.setEasingCurve(QEasingCurve.OutCubic)
        self._opacity_ani.setPropertyName(b'windowOpacity')
        self._opacity_ani.setStartValue(0.0)
        self._opacity_ani.setEndValue(1.0)

        self._set_proper_position(parent)
        self._fade_int()

    def _fade_out(self):
        self._pos_ani.setDirection(QAbstractAnimation.Backward)
        self._pos_ani.start()
        self._opacity_ani.setDirection(QAbstractAnimation.Backward)
        self._opacity_ani.start()

    def _fade_int(self):
        self._pos_ani.start()
        self._opacity_ani.start()

    def _set_proper_position(self, parent):
        parent_geo = parent.geometry()
        pos = parent_geo.topLeft(
        ) if parent.parent() is None else parent.mapToGlobal(
            parent_geo.topLeft())
        offset = 0
        for child in parent.children():
            if isinstance(child, MMessage) and child.isVisible():
                offset = max(offset, child.y())
        base = pos.y() + MMessage.default_config.get('top')
        target_x = pos.x() + parent_geo.width() / 2 - 100
        target_y = (offset + 50) if offset else base
        self._pos_ani.setStartValue(QPoint(target_x, target_y - 40))
        self._pos_ani.setEndValue(QPoint(target_x, target_y))

    @classmethod
    def info(cls, text, parent, duration=None, closable=None):
        """Show a normal message"""
        inst = cls(text,
                   dayu_type=MMessage.InfoType,
                   duration=duration,
                   closable=closable,
                   parent=parent)
        inst.show()
        return inst

    @classmethod
    def success(cls, text, parent, duration=None, closable=None):
        """Show a success message"""
        inst = cls(text,
                   dayu_type=MMessage.SuccessType,
                   duration=duration,
                   closable=closable,
                   parent=parent)

        inst.show()
        return inst

    @classmethod
    def warning(cls, text, parent, duration=None, closable=None):
        """Show a warning message"""
        inst = cls(text,
                   dayu_type=MMessage.WarningType,
                   duration=duration,
                   closable=closable,
                   parent=parent)
        inst.show()
        return inst

    @classmethod
    def error(cls, text, parent, duration=None, closable=None):
        """Show an error message"""
        inst = cls(text,
                   dayu_type=MMessage.ErrorType,
                   duration=duration,
                   closable=closable,
                   parent=parent)
        inst.show()
        return inst

    @classmethod
    def loading(cls, text, parent):
        """Show a message with loading animation"""
        inst = cls(text, dayu_type=MMessage.LoadingType, parent=parent)
        inst.show()
        return inst

    @classmethod
    def config(cls, duration=None, top=None):
        """
        Config the global MMessage duration and top setting.
        :param duration: int (unit is second)
        :param top: int (unit is px)
        :return: None
        """
        if duration is not None:
            cls.default_config['duration'] = duration
        if top is not None:
            cls.default_config['top'] = top
Ejemplo n.º 8
0
class MAlert(QWidget):
    """
    Alert component for feedback.

    Property:
        dayu_type: The feedback type with different color container.
        dayu_text: The feedback string showed in container.
    """
    InfoType = 'info'
    SuccessType = 'success'
    WarningType = 'warning'
    ErrorType = 'error'

    def __init__(self, text='', parent=None, flags=0):
        super(MAlert, self).__init__(parent, flags)
        self.setAttribute(Qt.WA_StyledBackground)
        self._icon_label = MAvatar()
        self._icon_label.set_dayu_size(dayu_theme.tiny)
        self._content_label = MLabel().secondary()
        self._close_button = MToolButton().svg(
            'close_line.svg').tiny().icon_only()
        self._close_button.clicked.connect(
            functools.partial(self.setVisible, False))

        self._main_lay = QHBoxLayout()
        self._main_lay.setContentsMargins(8, 8, 8, 8)
        self._main_lay.addWidget(self._icon_label)
        self._main_lay.addWidget(self._content_label)
        self._main_lay.addStretch()
        self._main_lay.addWidget(self._close_button)

        self.setLayout(self._main_lay)

        self.set_show_icon(True)
        self.set_closeable(False)
        self._dayu_type = None
        self._dayu_text = None
        self.set_dayu_type(MAlert.InfoType)
        self.set_dayu_text(text)

    def set_closeable(self, closeable):
        """Display the close icon button or not."""
        self._close_button.setVisible(closeable)

    def set_show_icon(self, show_icon):
        """Display the information type icon or not."""
        self._icon_label.setVisible(show_icon)

    def _set_dayu_text(self):
        self._content_label.setText(self._dayu_text)
        self.setVisible(bool(self._dayu_text))

    def set_dayu_text(self, value):
        """Set the feedback content."""
        if isinstance(value, basestring):
            self._dayu_text = value
        else:
            raise TypeError("Input argument 'value' should be string type, "
                            "but get {}".format(type(value)))
        self._set_dayu_text()

    def _set_dayu_type(self):
        self._icon_label.set_dayu_image(
            MPixmap('{}_fill.svg'.format(self._dayu_type),
                    vars(dayu_theme).get(self._dayu_type + '_color')))
        self.style().polish(self)

    def set_dayu_type(self, value):
        """Set feedback type."""
        if value in [
                MAlert.InfoType, MAlert.SuccessType, MAlert.WarningType,
                MAlert.ErrorType
        ]:
            self._dayu_type = value
        else:
            raise ValueError("Input argument 'value' should be one of "
                             "info/success/warning/error string.")
        self._set_dayu_type()

    def get_dayu_type(self):
        """
        Get MAlert feedback type.
        :return: str
        """
        return self._dayu_type

    def get_dayu_text(self):
        """
        Get MAlert feedback message.
        :return: basestring
        """
        return self._dayu_text

    dayu_text = Property(unicode, get_dayu_text, set_dayu_text)
    dayu_type = Property(str, get_dayu_type, set_dayu_type)

    def info(self):
        """Set MAlert to InfoType"""
        self.set_dayu_type(MAlert.InfoType)
        return self

    def success(self):
        """Set MAlert to SuccessType"""
        self.set_dayu_type(MAlert.SuccessType)
        return self

    def warning(self):
        """Set MAlert to  WarningType"""
        self.set_dayu_type(MAlert.WarningType)
        return self

    def error(self):
        """Set MAlert to ErrorType"""
        self.set_dayu_type(MAlert.ErrorType)
        return self

    def closable(self):
        """Set MAlert closebale is True"""
        self.set_closeable(True)
        return self
Ejemplo n.º 9
0
class MSectionItem(QWidget):
    sig_context_menu = Signal(object)

    def __init__(self,
                 title='',
                 expand=False,
                 widget=None,
                 closeable=False,
                 parent=None):
        super(MSectionItem, self).__init__(parent)
        self._central_widget = None
        self.setAttribute(Qt.WA_StyledBackground)
        self.title_label = MLabel(parent=self)
        self.expand_icon = MLabel(parent=self)
        self.expand_icon.setSizePolicy(QSizePolicy.Minimum,
                                       QSizePolicy.Minimum)
        self._close_button = MToolButton().icon_only().tiny().svg(
            'close_line.svg')
        self._close_button.clicked.connect(self.close)

        header_lay = QHBoxLayout()
        header_lay.addWidget(self.expand_icon)
        header_lay.addWidget(self.title_label)
        header_lay.addStretch()
        header_lay.addWidget(self._close_button)
        self.header_widget = QWidget(parent=self)
        self.header_widget.setAttribute(Qt.WA_StyledBackground)
        self.header_widget.setObjectName('title')
        self.header_widget.setLayout(header_lay)
        self.header_widget.setSizePolicy(QSizePolicy.Minimum,
                                         QSizePolicy.Minimum)
        self.header_widget.setCursor(Qt.PointingHandCursor)
        self.title_label.setCursor(Qt.PointingHandCursor)
        self.header_widget.installEventFilter(self)
        self.title_label.installEventFilter(self)

        self.content_widget = QWidget(parent=self)
        self.content_layout = QHBoxLayout()
        self.content_widget.setLayout(self.content_layout)

        self.main_lay = QVBoxLayout()
        self.main_lay.setContentsMargins(0, 0, 0, 0)
        self.main_lay.setSpacing(0)
        self.main_lay.addWidget(self.header_widget)
        self.main_lay.addWidget(self.content_widget)
        self.setLayout(self.main_lay)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.setMouseTracking(True)
        self.set_title(title)
        self.set_closeable(closeable)
        if widget:
            self.set_content(widget)
        self.set_expand(expand)

    def set_content(self, widget):
        if self._central_widget:
            self.content_layout.removeWidget(self._central_widget)
            self._central_widget.close()
        self.content_layout.addWidget(widget)
        self._central_widget = widget

    def get_content(self):
        return self._central_widget

    def set_closeable(self, value):
        self.setProperty('closeable', value)

    def _set_closeable(self, value):
        self.content_widget.setVisible(value)
        self._close_button.setVisible(value)

    def set_expand(self, value):
        self.setProperty('expand', value)

    def _set_expand(self, value):
        self.content_widget.setVisible(value)
        self.expand_icon.setPixmap(
            MPixmap('down_line.svg' if value else 'right_line.svg').
            scaledToHeight(12))

    def set_title(self, value):
        self.setProperty('title', value)

    def _set_title(self, value):
        self.title_label.setText(value)

    def eventFilter(self, widget, event):
        if widget in [self.header_widget, self.title_label]:
            if event.type() == QEvent.MouseButtonRelease:
                self.set_expand(not self.property('expand'))
        return super(QWidget, self).eventFilter(widget, event)
Ejemplo n.º 10
0
class MDrawer(QWidget):
    """
    A panel which slides in from the edge of the screen.
    """
    LeftPos = 'left'
    RightPos = 'right'
    TopPos = 'top'
    BottomPos = 'bottom'

    sig_closed = Signal()

    def __init__(self, title, position='right', closable=True, parent=None):
        super(MDrawer, self).__init__(parent)
        self.setObjectName('message')
        # self.setWindowFlags(Qt.Popup )
        # self.setWindowFlags(
        #     Qt.FramelessWindowHint | Qt.Popup | Qt.WA_TranslucentBackground)
        self.setAttribute(Qt.WA_StyledBackground)

        self._title_label = MLabel(parent=self).h4()
        # self._title_label.set_elide_mode(Qt.ElideRight)
        self._title_label.setText(title)

        self._close_button = MToolButton(
            parent=self).icon_only().svg('close_line.svg').small()
        self._close_button.clicked.connect(self.close)
        self._close_button.setVisible(closable or False)

        _title_lay = QHBoxLayout()
        _title_lay.addWidget(self._title_label)
        _title_lay.addStretch()
        _title_lay.addWidget(self._close_button)
        self._button_lay = QHBoxLayout()
        self._button_lay.addStretch()

        self._scroll_area = QScrollArea()
        self._main_lay = QVBoxLayout()
        self._main_lay.addLayout(_title_lay)
        self._main_lay.addWidget(MDivider())
        self._main_lay.addWidget(self._scroll_area)
        self._main_lay.addWidget(MDivider())
        self._main_lay.addLayout(self._button_lay)
        self.setLayout(self._main_lay)

        self._position = position

        self._close_timer = QTimer(self)
        self._close_timer.setSingleShot(True)
        self._close_timer.timeout.connect(self.close)
        self._close_timer.timeout.connect(self.sig_closed)
        self._close_timer.setInterval(300)
        self._is_first_close = True

        self._pos_ani = QPropertyAnimation(self)
        self._pos_ani.setTargetObject(self)
        self._pos_ani.setEasingCurve(QEasingCurve.OutCubic)
        self._pos_ani.setDuration(300)
        self._pos_ani.setPropertyName('pos')

        self._opacity_ani = QPropertyAnimation()
        self._opacity_ani.setTargetObject(self)
        self._opacity_ani.setDuration(300)
        self._opacity_ani.setEasingCurve(QEasingCurve.OutCubic)
        self._opacity_ani.setPropertyName('windowOpacity')
        self._opacity_ani.setStartValue(0.0)
        self._opacity_ani.setEndValue(1.0)
        # self._shadow_effect = QGraphicsDropShadowEffect(self)
        # color = dayu_theme.red
        # self._shadow_effect.setColor(color)
        # self._shadow_effect.setOffset(0, 0)
        # self._shadow_effect.setBlurRadius(5)
        # self._shadow_effect.setEnabled(False)
        # self.setGraphicsEffect(self._shadow_effect)

        self.app = QApplication.instance()
        self.app.installEventFilter(self)
        self.protect_time = time.time()

    def retrieveChildren(self, parent, receiver):
        if parent is receiver:
            return True
        if not hasattr(parent, "children"):
            return

        for child in parent.children():

            ret = self.retrieveChildren(child, receiver)
            if ret:
                return ret

    def eventFilter(self, receiver, event):
        # Note QEvent.Type.MouseButtonPress 为 2
        if event.type() == 2:
            if self.retrieveChildren(self, receiver):
                self.protect_time = time.time()
            # NOTE 如果点击多次触发,通过时间进行保护
            if (time.time() - self.protect_time) > .1:
                self.close()
        elif event.type() == QEvent.Type.Resize and receiver is self.window():
            self.close()
        return False

    def set_widget(self, widget):
        self._scroll_area.setWidget(widget)

    def add_button(self, button):
        self._button_lay.addWidget(button)

    def _fade_out(self):
        self._pos_ani.setDirection(QAbstractAnimation.Backward)
        self._pos_ani.start()
        self._opacity_ani.setDirection(QAbstractAnimation.Backward)
        self._opacity_ani.start()

    def _fade_int(self):
        self._pos_ani.start()
        self._opacity_ani.start()

    def _set_proper_position(self):
        parent = self.parent()
        parent_geo = parent.geometry()
        if self._position == MDrawer.LeftPos:
            pos = parent_geo.topLeft(
            ) if parent.parent() is None else parent.mapToGlobal(
                parent_geo.topLeft())
            pos -= self.window().geometry().topLeft()
            target_x = pos.x()
            target_y = pos.y()
            self.setFixedHeight(parent_geo.height())
            self._pos_ani.setStartValue(
                QPoint(target_x - self.width(), target_y))
            self._pos_ani.setEndValue(QPoint(target_x, target_y))
        if self._position == MDrawer.RightPos:
            pos = parent_geo.topRight(
            ) if parent.parent() is None else parent.mapToGlobal(
                parent_geo.topRight())
            pos -= self.window().geometry().topLeft()
            self.setFixedHeight(parent_geo.height())
            target_x = pos.x() - self.width()
            target_y = pos.y()
            self._pos_ani.setStartValue(
                QPoint(target_x + self.width(), target_y))
            self._pos_ani.setEndValue(QPoint(target_x, target_y))
        if self._position == MDrawer.TopPos:
            pos = parent_geo.topLeft(
            ) if parent.parent() is None else parent.mapToGlobal(
                parent_geo.topLeft())
            pos -= self.window().geometry().topLeft()
            self.setFixedWidth(parent_geo.width())
            target_x = pos.x()
            target_y = pos.y()
            self._pos_ani.setStartValue(
                QPoint(target_x, target_y - self.height()))
            self._pos_ani.setEndValue(QPoint(target_x, target_y))
        if self._position == MDrawer.BottomPos:
            pos = parent_geo.bottomLeft(
            ) if parent.parent() is None else parent.mapToGlobal(
                parent_geo.bottomLeft())
            pos -= self.window().geometry().topLeft()
            self.setFixedWidth(parent_geo.width())
            target_x = pos.x()
            target_y = pos.y() - self.height()
            self._pos_ani.setStartValue(
                QPoint(target_x, target_y + self.height()))
            self._pos_ani.setEndValue(QPoint(target_x, target_y))

    def set_dayu_position(self, value):
        """
        Set the placement of the MDrawer.
        top/right/bottom/left, default is right
        :param value: str
        :return: None
        """
        self._position = value
        if value in [MDrawer.BottomPos, MDrawer.TopPos]:
            self.setFixedHeight(200)
        else:
            self.setFixedWidth(200)

    def get_dayu_position(self):
        """
        Get the placement of the MDrawer
        :return: str
        """
        return self._position

    dayu_position = Property(str, get_dayu_position, set_dayu_position)

    def left(self):
        """Set drawer's placement to left"""
        self.set_dayu_position(MDrawer.LeftPos)
        return self

    def right(self):
        """Set drawer's placement to right"""
        self.set_dayu_position(MDrawer.RightPos)
        return self

    def top(self):
        """Set drawer's placement to top"""
        self.set_dayu_position(MDrawer.TopPos)
        return self

    def bottom(self):
        """Set drawer's placement to bottom"""
        self.set_dayu_position(MDrawer.BottomPos)
        return self

    def show(self):
        self._set_proper_position()
        self._fade_int()
        return super(MDrawer, self).show()

    def closeEvent(self, event):
        self.app.removeEventFilter(self)
        if self._is_first_close:
            self._is_first_close = False
            self._close_timer.start()
            self._fade_out()
            event.ignore()
        else:
            event.accept()
Ejemplo n.º 11
0
class MMeta(QWidget):
    def __init__(self,
                 cover=None,
                 avatar=None,
                 title=None,
                 description=None,
                 extra=False,
                 parent=None):
        super(MMeta, self).__init__(parent)
        self.setAttribute(Qt.WA_StyledBackground)
        self._cover_label = QLabel()
        self._avatar = MAvatar()
        self._title_label = MLabel().h4()
        self._description_label = MLabel().secondary()
        self._description_label.setWordWrap(True)
        self._description_label.set_elide_mode(Qt.ElideRight)
        self._title_layout = QHBoxLayout()
        self._title_layout.addWidget(self._title_label)
        self._title_layout.addStretch()
        self._extra_button = MToolButton(
            parent=self).icon_only().svg('more.svg')
        self._title_layout.addWidget(self._extra_button)
        self._extra_button.setVisible(extra)

        content_lay = QFormLayout()
        content_lay.setContentsMargins(5, 5, 5, 5)
        content_lay.addRow(self._avatar, self._title_layout)
        content_lay.addRow(self._description_label)

        self._button_layout = QHBoxLayout()

        main_lay = QVBoxLayout()
        main_lay.setSpacing(0)
        main_lay.setContentsMargins(1, 1, 1, 1)
        main_lay.addWidget(self._cover_label)
        main_lay.addLayout(content_lay)
        main_lay.addLayout(self._button_layout)
        main_lay.addStretch()
        self.setLayout(main_lay)
        self._cover_label.setFixedSize(QSize(200, 200))
        # self.setFixedWidth(200)

    def get_more_button(self):
        return self._extra_button

    def setup_data(self, data_dict):
        if data_dict.get('title'):
            self._title_label.setText(data_dict.get('title'))
            self._title_label.setVisible(True)
        else:
            self._title_label.setVisible(False)

        if data_dict.get('description'):
            self._description_label.setText(data_dict.get('description'))
            self._description_label.setVisible(True)
        else:
            self._description_label.setVisible(False)

        if data_dict.get('avatar'):
            self._avatar.set_dayu_image(data_dict.get('avatar'))
            self._avatar.setVisible(True)
        else:
            self._avatar.setVisible(False)

        if data_dict.get('cover'):
            fixed_height = self._cover_label.width()
            self._cover_label.setPixmap(
                data_dict.get('cover').scaledToWidth(fixed_height,
                                                     Qt.SmoothTransformation))
            self._cover_label.setVisible(True)
        else:
            self._cover_label.setVisible(False)
Ejemplo n.º 12
0
class MDivider(QtWidgets.QWidget):
    """
    A divider line separates different content.

    Property:
        dayu_text: six.string_types
    """

    _alignment_map = {
        QtCore.Qt.AlignCenter: 50,
        QtCore.Qt.AlignLeft: 20,
        QtCore.Qt.AlignRight: 80,
    }

    def __init__(
        self,
        text="",
        orientation=QtCore.Qt.Horizontal,
        alignment=QtCore.Qt.AlignCenter,
        parent=None,
    ):
        super(MDivider, self).__init__(parent)
        self._orient = orientation
        self._text_label = MLabel().secondary()
        self._left_frame = QtWidgets.QFrame()
        self._right_frame = QtWidgets.QFrame()
        self._main_lay = QtWidgets.QHBoxLayout()
        self._main_lay.setContentsMargins(0, 0, 0, 0)
        self._main_lay.setSpacing(0)
        self._main_lay.addWidget(self._left_frame)
        self._main_lay.addWidget(self._text_label)
        self._main_lay.addWidget(self._right_frame)
        self.setLayout(self._main_lay)

        if orientation == QtCore.Qt.Horizontal:
            self._left_frame.setFrameShape(QtWidgets.QFrame.HLine)
            self._left_frame.setFrameShadow(QtWidgets.QFrame.Sunken)
            self._right_frame.setFrameShape(QtWidgets.QFrame.HLine)
            self._right_frame.setFrameShadow(QtWidgets.QFrame.Sunken)
        else:
            self._text_label.setVisible(False)
            self._right_frame.setVisible(False)
            self._left_frame.setFrameShape(QtWidgets.QFrame.VLine)
            self._left_frame.setFrameShadow(QtWidgets.QFrame.Plain)
            self.setFixedWidth(2)
        self._main_lay.setStretchFactor(self._left_frame,
                                        self._alignment_map.get(alignment, 50))
        self._main_lay.setStretchFactor(
            self._right_frame, 100 - self._alignment_map.get(alignment, 50))
        self._text = None
        self.set_dayu_text(text)

    def set_dayu_text(self, value):
        """
        Set the divider's text.
        When text is empty, hide the text_label and right_frame to ensure the divider not has a gap.

        :param value: six.string_types
        :return: None
        """
        self._text = value
        self._text_label.setText(value)
        if self._orient == QtCore.Qt.Horizontal:
            self._text_label.setVisible(bool(value))
            self._right_frame.setVisible(bool(value))

    def get_dayu_text(self):
        """
        Get current text
        :return: six.string_types
        """
        return self._text

    dayu_text = QtCore.Property(six.string_types[0], get_dayu_text,
                                set_dayu_text)

    @classmethod
    def left(cls, text=""):
        """Create a horizontal divider with text at left."""
        return cls(text, alignment=QtCore.Qt.AlignLeft)

    @classmethod
    def right(cls, text=""):
        """Create a horizontal divider with text at right."""
        return cls(text, alignment=QtCore.Qt.AlignRight)

    @classmethod
    def center(cls, text=""):
        """Create a horizontal divider with text at center."""
        return cls(text, alignment=QtCore.Qt.AlignCenter)

    @classmethod
    def vertical(cls):
        """Create a vertical divider"""
        return cls(orientation=QtCore.Qt.Vertical)
Ejemplo n.º 13
0
    def _init_ui(self):
        title_lay = QGridLayout()
        title_lay.addWidget(MLabel(u'一级标题').h1(), 0, 0)
        title_lay.addWidget(MLabel(u'二级标题').h2(), 1, 0)
        title_lay.addWidget(MLabel(u'三级标题').h3(), 2, 0)
        title_lay.addWidget(MLabel(u'四级标题').h4(), 3, 0)
        title_lay.addWidget(MLabel('h1 Level').h1(), 0, 1)
        title_lay.addWidget(MLabel('h2 Level').h2(), 1, 1)
        title_lay.addWidget(MLabel('h3 Level').h3(), 2, 1)
        title_lay.addWidget(MLabel('h4 Level').h4(), 3, 1)

        text_type_lay = QHBoxLayout()
        text_type_lay.addWidget(MLabel('MLabel: Normal'))
        text_type_lay.addWidget(MLabel('MLabel: Secondary').secondary())
        text_type_lay.addWidget(MLabel('MLabel: Warning').warning())
        text_type_lay.addWidget(MLabel('MLabel: Danger').danger())
        disable_text = MLabel('MLabel: Disabled')
        disable_text.setEnabled(False)
        text_type_lay.addWidget(disable_text)

        text_attr_lay = QHBoxLayout()
        text_attr_lay.addWidget(MLabel('MLabel: Mark').mark())
        text_attr_lay.addWidget(MLabel('MLabel: Code').code())
        text_attr_lay.addWidget(MLabel('MLabel: Underline').underline())
        text_attr_lay.addWidget(MLabel('MLabel: Delete').delete())
        text_attr_lay.addWidget(MLabel('MLabel: Strong').strong())

        text_mix_lay = QHBoxLayout()
        text_mix_lay.addWidget(
            MLabel('MLabel: Strong & Underline').strong().underline())
        text_mix_lay.addWidget(
            MLabel('MLabel: Danger & Delete').danger().delete())
        text_mix_lay.addWidget(
            MLabel('MLabel: Warning & Strong').warning().strong())
        text_mix_lay.addWidget(MLabel('MLabel: H4 & Mark').h4().mark())

        data_bind_lay = QHBoxLayout()
        data_bind_label = MLabel()
        button = MPushButton(text='Random An Animal').primary()
        button.clicked.connect(self.slot_change_text)
        data_bind_lay.addWidget(data_bind_label)
        data_bind_lay.addWidget(button)
        data_bind_lay.addStretch()
        self.register_field('show_text', 'Guess')
        self.bind('show_text', data_bind_label, 'text')

        lay_elide = QVBoxLayout()
        label_none = MLabel('This is a elide NONE mode label. '
                            'Ellipsis should NOT appear in the text.')
        label_left = MLabel(
            'This is a elide LEFT mode label. '
            'The ellipsis should appear at the beginning of the text. '
            'xiao mao xiao gou xiao ci wei')
        label_left.set_elide_mode(Qt.ElideLeft)
        label_middle = MLabel(
            'This is a elide MIDDLE mode label. '
            'The ellipsis should appear in the middle of the text. '
            'xiao mao xiao gou xiao ci wei')
        label_middle.set_elide_mode(Qt.ElideMiddle)
        label_right = MLabel()
        label_right.setText(
            'This is a elide RIGHT mode label. '
            'The ellipsis should appear at the end of the text. '
            'Some text to fill the line bala bala bala.')
        label_right.set_elide_mode(Qt.ElideRight)
        lay_elide.addWidget(label_none)
        lay_elide.addWidget(label_left)
        lay_elide.addWidget(label_middle)
        lay_elide.addWidget(label_right)

        main_lay = QVBoxLayout()
        main_lay.addWidget(MDivider('different level'))
        main_lay.addLayout(title_lay)
        main_lay.addWidget(MDivider('different type'))
        main_lay.addLayout(text_type_lay)
        main_lay.addWidget(MDivider('different property'))
        main_lay.addLayout(text_attr_lay)
        main_lay.addWidget(MDivider('mix'))
        main_lay.addLayout(text_mix_lay)

        # main_lay.addWidget(MDivider('data bind'))
        # main_lay.addLayout(data_bind_lay)
        main_lay.addWidget(MDivider('elide mode'))
        main_lay.addLayout(lay_elide)
        main_lay.addStretch()
        self.setLayout(main_lay)
Ejemplo n.º 14
0
class MDrawer(QtWidgets.QWidget):
    """
    A panel which slides in from the edge of the screen.
    """

    LeftPos = "left"
    RightPos = "right"
    TopPos = "top"
    BottomPos = "bottom"

    sig_closed = QtCore.Signal()

    def __init__(self, title, position="right", closable=True, parent=None):
        super(MDrawer, self).__init__(parent)
        self.setObjectName("message")
        self.setWindowFlags(QtCore.Qt.Popup)
        # self.setWindowFlags(
        #     Qt.FramelessWindowHint | Qt.Popup | Qt.WA_TranslucentBackground)
        self.setAttribute(QtCore.Qt.WA_StyledBackground)

        self._title_label = MLabel(parent=self).h4()
        # self._title_label.set_elide_mode(Qt.ElideRight)
        self._title_label.setText(title)

        self._close_button = (MToolButton(
            parent=self).icon_only().svg("close_line.svg").small())
        self._close_button.clicked.connect(self.close)
        self._close_button.setVisible(closable or False)

        self._title_extra_lay = QtWidgets.QHBoxLayout()
        _title_lay = QtWidgets.QHBoxLayout()
        _title_lay.addWidget(self._title_label)
        _title_lay.addStretch()
        _title_lay.addLayout(self._title_extra_lay)
        _title_lay.addWidget(self._close_button)
        self._bottom_lay = QtWidgets.QHBoxLayout()
        self._bottom_lay.addStretch()

        self._scroll_area = QtWidgets.QScrollArea()
        self._scroll_area.setWidgetResizable(True)
        self._main_lay = QtWidgets.QVBoxLayout()
        self._main_lay.addLayout(_title_lay)
        self._main_lay.addWidget(MDivider())
        self._main_lay.addWidget(self._scroll_area)
        self._main_lay.addWidget(MDivider())
        self._main_lay.addLayout(self._bottom_lay)
        self.setLayout(self._main_lay)

        self._position = position

        self._close_timer = QtCore.QTimer(self)
        self._close_timer.setSingleShot(True)
        self._close_timer.timeout.connect(self.close)
        self._close_timer.timeout.connect(self.sig_closed)
        self._close_timer.setInterval(300)
        self._is_first_close = True

        self._pos_ani = QtCore.QPropertyAnimation(self)
        self._pos_ani.setTargetObject(self)
        self._pos_ani.setEasingCurve(QtCore.QEasingCurve.OutCubic)
        self._pos_ani.setDuration(300)
        self._pos_ani.setPropertyName(b"pos")

        self._opacity_ani = QtCore.QPropertyAnimation()
        self._opacity_ani.setTargetObject(self)
        self._opacity_ani.setDuration(300)
        self._opacity_ani.setEasingCurve(QtCore.QEasingCurve.OutCubic)
        self._opacity_ani.setPropertyName(b"windowOpacity")
        self._opacity_ani.setStartValue(0.0)
        self._opacity_ani.setEndValue(1.0)

    def set_widget(self, widget):
        self._scroll_area.setWidget(widget)

    def add_widget_to_bottom(self, button):
        self._bottom_lay.addWidget(button)

    def add_widget_to_top(self, button):
        self._title_extra_lay.addWidget(button)

    def _fade_out(self):
        self._pos_ani.setDirection(QtCore.QAbstractAnimation.Backward)
        self._pos_ani.start()
        self._opacity_ani.setDirection(QtCore.QAbstractAnimation.Backward)
        self._opacity_ani.start()

    def _fade_int(self):
        self._pos_ani.start()
        self._opacity_ani.start()

    def _set_proper_position(self):
        parent = self.parent()
        parent_geo = parent.geometry()
        if self._position == MDrawer.LeftPos:
            pos = (parent_geo.topLeft() if parent.parent() is None else
                   parent.mapToGlobal(parent_geo.topLeft()))
            target_x = pos.x()
            target_y = pos.y()
            self.setFixedHeight(parent_geo.height())
            self._pos_ani.setStartValue(
                QtCore.QPoint(target_x - self.width(), target_y))
            self._pos_ani.setEndValue(QtCore.QPoint(target_x, target_y))
        if self._position == MDrawer.RightPos:
            pos = (parent_geo.topRight() if parent.parent() is None else
                   parent.mapToGlobal(parent_geo.topRight()))
            self.setFixedHeight(parent_geo.height())
            target_x = pos.x() - self.width()
            target_y = pos.y()
            self._pos_ani.setStartValue(
                QtCore.QPoint(target_x + self.width(), target_y))
            self._pos_ani.setEndValue(QtCore.QPoint(target_x, target_y))
        if self._position == MDrawer.TopPos:
            pos = (parent_geo.topLeft() if parent.parent() is None else
                   parent.mapToGlobal(parent_geo.topLeft()))
            self.setFixedWidth(parent_geo.width())
            target_x = pos.x()
            target_y = pos.y()
            self._pos_ani.setStartValue(
                QtCore.QPoint(target_x, target_y - self.height()))
            self._pos_ani.setEndValue(QtCore.QPoint(target_x, target_y))
        if self._position == MDrawer.BottomPos:
            pos = (parent_geo.bottomLeft() if parent.parent() is None else
                   parent.mapToGlobal(parent_geo.bottomLeft()))
            self.setFixedWidth(parent_geo.width())
            target_x = pos.x()
            target_y = pos.y() - self.height()
            self._pos_ani.setStartValue(
                QtCore.QPoint(target_x, target_y + self.height()))
            self._pos_ani.setEndValue(QtCore.QPoint(target_x, target_y))

    def set_dayu_position(self, value):
        """
        Set the placement of the MDrawer.
        top/right/bottom/left, default is right
        :param value: str
        :return: None
        """
        self._position = value
        scale_x, _ = get_scale_factor()
        if value in [MDrawer.BottomPos, MDrawer.TopPos]:
            self.setFixedHeight(200 * scale_x)
        else:
            self.setFixedWidth(200 * scale_x)

    def get_dayu_position(self):
        """
        Get the placement of the MDrawer
        :return: str
        """
        return self._position

    dayu_position = QtCore.Property(str, get_dayu_position, set_dayu_position)

    def left(self):
        """Set drawer's placement to left"""
        self.set_dayu_position(MDrawer.LeftPos)
        return self

    def right(self):
        """Set drawer's placement to right"""
        self.set_dayu_position(MDrawer.RightPos)
        return self

    def top(self):
        """Set drawer's placement to top"""
        self.set_dayu_position(MDrawer.TopPos)
        return self

    def bottom(self):
        """Set drawer's placement to bottom"""
        self.set_dayu_position(MDrawer.BottomPos)
        return self

    def show(self):
        self._set_proper_position()
        self._fade_int()
        super(MDrawer, self).show()
        # NOTES(timmyliang): for chinese input
        self.activateWindow()

    def closeEvent(self, event):
        if self._is_first_close:
            self._is_first_close = False
            self._close_timer.start()
            self._fade_out()
            event.ignore()
        else:
            event.accept()