コード例 #1
0
ファイル: line_tab_widget.py プロジェクト: muyr/dayu_widgets3
class MLineTabWidget(QWidget):
    """MLineTabWidget"""

    def __init__(self, alignment=Qt.AlignCenter, parent=None):
        super(MLineTabWidget, self).__init__(parent=parent)
        self.tool_button_group = MUnderlineButtonGroup()
        self.bar_layout = QHBoxLayout()
        self.bar_layout.setContentsMargins(0, 0, 0, 0)
        if alignment == Qt.AlignCenter:
            self.bar_layout.addStretch()
            self.bar_layout.addWidget(self.tool_button_group)
            self.bar_layout.addStretch()
        elif alignment == Qt.AlignLeft:
            self.bar_layout.addWidget(self.tool_button_group)
            self.bar_layout.addStretch()
        elif alignment == Qt.AlignRight:
            self.bar_layout.addStretch()
            self.bar_layout.addWidget(self.tool_button_group)
        self.stack_widget = MStackedWidget()
        self.tool_button_group.sig_checked_changed.connect(self.stack_widget.setCurrentIndex)
        main_lay = QVBoxLayout()
        main_lay.setContentsMargins(0, 0, 0, 0)
        main_lay.setSpacing(0)
        main_lay.addLayout(self.bar_layout)
        main_lay.addWidget(MDivider())
        main_lay.addSpacing(5)
        main_lay.addWidget(self.stack_widget)
        self.setLayout(main_lay)

    def add_tab(self, widget, data_dict):
        """Add a tab"""
        self.stack_widget.addWidget(widget)
        self.tool_button_group.add_button(data_dict, self.stack_widget.count() - 1)
コード例 #2
0
ファイル: menu_tab_widget.py プロジェクト: muyr/dayu_widgets3
class MMenuTabWidget(QWidget):
    """MMenuTabWidget"""
    def __init__(self, parent=None):
        super(MMenuTabWidget, self).__init__(parent=parent)
        self.tool_button_group = MBlockButtonGroup()
        self._bar_layout = QHBoxLayout()
        self._bar_layout.setContentsMargins(10, 0, 10, 0)
        self._bar_layout.addWidget(self.tool_button_group)
        self._bar_layout.addStretch()
        bar_widget = QWidget()
        bar_widget.setObjectName('bar_widget')
        bar_widget.setLayout(self._bar_layout)
        bar_widget.setAttribute(Qt.WA_StyledBackground)
        main_lay = QVBoxLayout()
        main_lay.setContentsMargins(0, 0, 0, 0)
        main_lay.setSpacing(0)
        main_lay.addWidget(bar_widget)
        main_lay.addWidget(MDivider())
        main_lay.addSpacing(5)
        self.setLayout(main_lay)
        self.setFixedHeight(dayu_theme.large + 10)

    def tool_bar_append_widget(self, widget):
        """Add the widget too menubar's right position."""
        self._bar_layout.addWidget(widget)

    def tool_bar_insert_widget(self, widget):
        """Insert the widget to menubar's left position."""
        self._bar_layout.insertWidget(0, widget)

    def add_menu(self, data_dict, index=None):
        """Add a menu"""
        self.tool_button_group.add_button(data_dict, index)
コード例 #3
0
ファイル: card.py プロジェクト: muyr/dayu_widgets3
class MCard(QWidget):
    def __init__(self,
                 title=None,
                 image=None,
                 size=None,
                 extra=None,
                 type=None,
                 parent=None):
        super(MCard, self).__init__(parent=parent)
        self.setAttribute(Qt.WA_StyledBackground)
        self.setProperty('border', False)
        size = size or dayu_theme.default_size
        map_label = {
            dayu_theme.large: (MLabel.H2Level, 20),
            dayu_theme.medium: (MLabel.H3Level, 15),
            dayu_theme.small: (MLabel.H4Level, 10),
        }
        self._title_label = MLabel(text=title)
        self._title_label.set_dayu_level(map_label.get(size)[0])

        padding = map_label.get(size)[-1]
        self._title_layout = QHBoxLayout()
        self._title_layout.setContentsMargins(padding, padding, padding,
                                              padding)
        if image:
            self._title_icon = MAvatar()
            self._title_icon.set_dayu_image(image)
            self._title_icon.set_dayu_size(size)
            self._title_layout.addWidget(self._title_icon)
        self._title_layout.addWidget(self._title_label)
        self._title_layout.addStretch()
        if extra:
            self._extra_button = MToolButton().icon_only().svg('more.svg')
            self._title_layout.addWidget(self._extra_button)

        self._content_layout = QVBoxLayout()

        self._main_lay = QVBoxLayout()
        self._main_lay.setSpacing(0)
        self._main_lay.setContentsMargins(1, 1, 1, 1)
        if title:
            self._main_lay.addLayout(self._title_layout)
            self._main_lay.addWidget(MDivider())
        self._main_lay.addLayout(self._content_layout)
        self.setLayout(self._main_lay)

    def get_more_button(self):
        return self._extra_button

    def set_widget(self, widget):
        self._content_layout.addWidget(widget)

    def border(self):
        self.setProperty('border', True)
        self.style().polish(self)
        return self
コード例 #4
0
ファイル: page.py プロジェクト: muyr/dayu_widgets3
    def __init__(self, parent=None):
        super(MPage, self).__init__(parent)
        self.register_field('page_size_selected', 25)
        self.register_field('page_size_list', [{
            'label': '25 - Fastest',
            'value': 25
        }, {
            'label': '50 - Fast',
            'value': 50
        }, {
            'label': '75 - Medium',
            'value': 75
        }, {
            'label': '100 - Slow',
            'value': 100
        }])
        self.register_field('total', 0)
        self.register_field('current_page', 0)
        self.register_field(
            'total_page', lambda: utils.get_total_page(
                self.field('total'), self.field('page_size_selected')))
        self.register_field('total_page_text',
                            lambda: str(self.field('total_page')))
        self.register_field(
            'display_text', lambda: utils.get_page_display_string(
                self.field('current_page'), self.field('page_size_selected'),
                self.field('total')))
        self.register_field('can_pre', lambda: self.field('current_page') > 1)
        self.register_field(
            'can_next',
            lambda: self.field('current_page') < self.field('total_page'))
        page_setting_menu = MMenu(parent=self)

        self._display_label = MLabel()
        self._display_label.setAlignment(Qt.AlignCenter)
        self._change_page_size_button = MComboBox().small()
        self._change_page_size_button.setFixedWidth(110)
        self._change_page_size_button.set_menu(page_setting_menu)
        self._change_page_size_button.set_formatter(
            lambda x: u'{} per page'.format(x))
        self._change_page_size_button.sig_value_changed.connect(
            self._emit_page_changed)

        self._pre_button = MToolButton().icon_only().svg(
            'left_fill.svg').small()
        self._pre_button.clicked.connect(
            functools.partial(self._slot_change_current_page, -1))
        self._next_button = MToolButton().small().icon_only().svg(
            'right_fill.svg')
        self._next_button.clicked.connect(
            functools.partial(self._slot_change_current_page, 1))
        self._current_page_spin_box = MSpinBox()
        self._current_page_spin_box.setMinimum(1)
        self._current_page_spin_box.set_dayu_size(dayu_theme.small)
        self._current_page_spin_box.valueChanged.connect(
            self._emit_page_changed)
        self._total_page_label = MLabel()

        self.bind('page_size_list', page_setting_menu, 'data')
        self.bind('page_size_selected',
                  page_setting_menu,
                  'value',
                  signal='sig_value_changed')
        self.bind('page_size_selected',
                  self._change_page_size_button,
                  'value',
                  signal='sig_value_changed')
        self.bind('current_page',
                  self._current_page_spin_box,
                  'value',
                  signal='valueChanged')
        self.bind('total_page', self._current_page_spin_box, 'maximum')
        self.bind('total_page_text', self._total_page_label, 'dayu_text')
        self.bind('display_text', self._display_label, 'dayu_text')
        self.bind('can_pre', self._pre_button, 'enabled')
        self.bind('can_next', self._next_button, 'enabled')

        main_lay = QHBoxLayout()
        main_lay.setContentsMargins(0, 0, 0, 0)
        main_lay.setSpacing(2)
        main_lay.addStretch()
        main_lay.addWidget(self._display_label)
        main_lay.addStretch()
        main_lay.addWidget(MLabel('|').secondary())
        main_lay.addWidget(self._change_page_size_button)
        main_lay.addWidget(MLabel('|').secondary())
        main_lay.addWidget(self._pre_button)
        main_lay.addWidget(MLabel('Page'))
        main_lay.addWidget(self._current_page_spin_box)
        main_lay.addWidget(MLabel('/'))
        main_lay.addWidget(self._total_page_label)
        main_lay.addWidget(self._next_button)
        self.setLayout(main_lay)
コード例 #5
0
class MItemViewFullSet(QWidget):
    sig_double_clicked = Signal(QModelIndex)
    sig_left_clicked = Signal(QModelIndex)
    sig_current_changed = Signal(QModelIndex, QModelIndex)
    sig_current_row_changed = Signal(QModelIndex, QModelIndex)
    sig_current_column_changed = Signal(QModelIndex, QModelIndex)
    sig_selection_changed = Signal(QItemSelection, QItemSelection)
    sig_context_menu = Signal(object)

    def __init__(self, table_view=True, big_view=False, parent=None):
        super(MItemViewFullSet, self).__init__(parent)
        self.sort_filter_model = MSortFilterModel()
        self.source_model = MTableModel()
        self.sort_filter_model.setSourceModel(self.source_model)

        self.stack_widget = QStackedWidget()

        self.view_button_grp = MToolButtonGroup(exclusive=True)
        data_group = []
        if table_view:
            self.table_view = MTableView(show_row_count=True)
            self.table_view.doubleClicked.connect(self.sig_double_clicked)
            self.table_view.pressed.connect(self.slot_left_clicked)
            self.table_view.setModel(self.sort_filter_model)
            self.stack_widget.addWidget(self.table_view)
            data_group.append({
                'svg': 'table_view.svg',
                'checkable': True,
                'tooltip': u'Table View'
            })
        if big_view:
            self.big_view = MBigView()
            self.big_view.doubleClicked.connect(self.sig_double_clicked)
            self.big_view.pressed.connect(self.slot_left_clicked)
            self.big_view.setModel(self.sort_filter_model)
            self.stack_widget.addWidget(self.big_view)
            data_group.append({
                'svg': 'big_view.svg',
                'checkable': True,
                'tooltip': u'Big View'
            })

        # 设置多个view 共享 MItemSelectionModel
        leader_view = self.stack_widget.widget(0)
        self.selection_model = leader_view.selectionModel()
        for index in range(self.stack_widget.count()):
            if index == 0:
                continue
            other_view = self.stack_widget.widget(index)
            other_view.setSelectionModel(self.selection_model)

        self.selection_model.currentChanged.connect(self.sig_current_changed)
        self.selection_model.currentRowChanged.connect(
            self.sig_current_row_changed)
        self.selection_model.currentColumnChanged.connect(
            self.sig_current_column_changed)
        self.selection_model.selectionChanged.connect(
            self.sig_selection_changed)

        self.tool_bar = QWidget()
        self.top_lay = QHBoxLayout()
        self.top_lay.setContentsMargins(0, 0, 0, 0)
        if data_group and len(data_group) > 1:
            self.view_button_grp.sig_checked_changed.connect(
                self.stack_widget.setCurrentIndex)
            self.view_button_grp.set_button_list(data_group)
            self.view_button_grp.set_dayu_checked(0)
            self.top_lay.addWidget(self.view_button_grp)
        self.search_line_edit = MLineEdit().search().small()
        self.search_attr_button = MToolButton().icon_only().svg(
            'down_fill.svg').small()
        self.search_line_edit.set_prefix_widget(self.search_attr_button)
        self.search_line_edit.textChanged.connect(
            self.sort_filter_model.set_search_pattern)
        self.search_line_edit.setVisible(False)

        self.top_lay.addStretch()
        self.top_lay.addWidget(self.search_line_edit)
        self.tool_bar.setLayout(self.top_lay)

        self.page_set = MPage()
        self.main_lay = QVBoxLayout()
        self.main_lay.setSpacing(5)
        self.main_lay.setContentsMargins(0, 0, 0, 0)
        self.main_lay.addWidget(self.tool_bar)
        self.main_lay.addWidget(self.stack_widget)
        self.main_lay.addWidget(self.page_set)
        self.setLayout(self.main_lay)

    def enable_context_menu(self):
        for index in range(self.stack_widget.count()):
            view = self.stack_widget.widget(index)
            view.enable_context_menu(True)
            view.sig_context_menu.connect(self.sig_context_menu)

    def set_no_data_text(self, text):
        for index in range(self.stack_widget.count()):
            view = self.stack_widget.widget(index)
            view.set_no_data_text(text)

    def set_selection_mode(self, mode):
        for index in range(self.stack_widget.count()):
            view = self.stack_widget.widget(index)
            view.setSelectionMode(mode)

    def tool_bar_visible(self, flag):
        self.tool_bar.setVisible(flag)

    @Slot(QModelIndex)
    def slot_left_clicked(self, start_index):
        button = QApplication.mouseButtons()
        if button == Qt.LeftButton:
            real_index = self.sort_filter_model.mapToSource(start_index)
            self.sig_left_clicked.emit(real_index)

    def set_header_list(self, header_list):
        self.source_model.set_header_list(header_list)
        self.sort_filter_model.set_header_list(header_list)
        self.sort_filter_model.setSourceModel(self.source_model)
        for index in range(self.stack_widget.count()):
            view = self.stack_widget.widget(index)
            view.set_header_list(header_list)

    def tool_bar_append_widget(self, widget):
        self.top_lay.addWidget(widget)

    def tool_bar_insert_widget(self, widget):
        self.top_lay.insertWidget(0, widget)

    @Slot()
    def setup_data(self, data_list):
        self.source_model.clear()
        if data_list:
            self.source_model.set_data_list(data_list)
        self.set_record_count(len(data_list))

    @Slot(int)
    def set_record_count(self, total):
        self.page_set.set_total(total)

    def get_data(self):
        return self.source_model.get_data_list()

    def searchable(self):
        """Enable search line edit visible."""
        self.search_line_edit.setVisible(True)
        return self
コード例 #6
0
class MBreadcrumb(QWidget):
    """
    MBreadcrumb

    A breadcrumb displays the current location within a hierarchy.
    It allows going back to states higher up in the hierarchy.
    """
    def __init__(self, separator='/', parent=None):
        super(MBreadcrumb, self).__init__(parent)
        self._separator = separator
        self._main_layout = QHBoxLayout()
        self._main_layout.setContentsMargins(0, 0, 0, 0)
        self._main_layout.setSpacing(0)
        self._main_layout.addStretch()
        self.setLayout(self._main_layout)
        self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        self._button_group = QButtonGroup()
        self._label_list = []

    def set_item_list(self, data_list):
        """Set the whole breadcrumb items. It will clear the old widgets."""
        for button in self._button_group.buttons():
            self._button_group.removeButton(button)
            self._main_layout.removeWidget(button)
            button.setVisible(False)
        for sep in self._label_list:
            self._main_layout.removeWidget(sep)
            sep.setVisible(False)

        for index, data_dict in enumerate(data_list):
            self.add_item(data_dict, index)

    def add_item(self, data_dict, index=None):
        """Add a item"""
        button = MToolButton()
        button.setText(data_dict.get('text'))
        if data_dict.get('svg'):
            button.svg(data_dict.get('svg'))
        if data_dict.get('tooltip'):
            button.setProperty('toolTip', data_dict.get('tooltip'))
        if data_dict.get('clicked'):
            button.clicked.connect(data_dict.get('clicked'))
        if data_dict.get('text'):
            if data_dict.get('svg') or data_dict.get('icon'):
                button.text_beside_icon()
            else:
                button.text_only()
        else:
            button.icon_only()

        if self._button_group.buttons():
            separator = MLabel(self._separator).secondary()
            self._label_list.append(separator)
            self._main_layout.insertWidget(self._main_layout.count() - 1,
                                           separator)
        self._main_layout.insertWidget(self._main_layout.count() - 1, button)

        if index is None:
            self._button_group.addButton(button)
        else:
            self._button_group.addButton(button, index)
コード例 #7
0
ファイル: alert.py プロジェクト: muyr/dayu_widgets3
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
コード例 #8
0
class MDivider(QWidget):
    '''
    A divider line separates different content.

    Property:
        dayu_text: basestring
    '''
    _alignment_map = {
        Qt.AlignCenter: 50,
        Qt.AlignLeft: 20,
        Qt.AlignRight: 80,
    }

    def __init__(self,
                 text='',
                 orientation=Qt.Horizontal,
                 alignment=Qt.AlignCenter,
                 parent=None):
        super(MDivider, self).__init__(parent)
        self._orient = orientation
        self._text_label = MLabel().secondary()
        self._left_frame = QFrame()
        self._right_frame = QFrame()
        self._main_lay = 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 == Qt.Horizontal:
            self._left_frame.setFrameShape(QFrame.HLine)
            self._left_frame.setFrameShadow(QFrame.Sunken)
            self._right_frame.setFrameShape(QFrame.HLine)
            self._right_frame.setFrameShadow(QFrame.Sunken)
        else:
            self._text_label.setVisible(False)
            self._right_frame.setVisible(False)
            self._left_frame.setFrameShape(QFrame.VLine)
            self._left_frame.setFrameShadow(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: basestring
        :return: None
        """
        self._text = value
        self._text_label.setText(value)
        if self._orient == Qt.Horizontal:
            self._text_label.setVisible(bool(value))
            self._right_frame.setVisible(bool(value))

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

    dayu_text = Property(basestring, get_dayu_text, set_dayu_text)

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

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

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

    @classmethod
    def vertical(cls):
        """Create a vertical divider"""
        return cls(orientation=Qt.Vertical)
コード例 #9
0
ファイル: line_edit.py プロジェクト: muyr/dayu_widgets3
class MLineEdit(QLineEdit):
    """MLineEdit"""
    sig_delay_text_changed = Signal(basestring)

    def __init__(self, text='', parent=None):
        super(MLineEdit, self).__init__(text, parent)
        self._main_layout = QHBoxLayout()
        self._main_layout.setContentsMargins(0, 0, 0, 0)
        self._main_layout.addStretch()

        self._prefix_widget = None
        self._suffix_widget = None

        self.setLayout(self._main_layout)
        self.setProperty('history', self.property('text'))
        self.setTextMargins(2, 0, 2, 0)

        self._delay_timer = QTimer()
        self._delay_timer.setInterval(500)
        self._delay_timer.setSingleShot(True)
        self._delay_timer.timeout.connect(self._slot_delay_text_changed)

        self._dayu_size = dayu_theme.default_size

    def get_dayu_size(self):
        """
        Get the push button height
        :return: integer
        """
        return self._dayu_size

    def set_dayu_size(self, value):
        """
        Set the avatar size.
        :param value: integer
        :return: None
        """
        self._dayu_size = value
        if hasattr(self._prefix_widget, 'set_dayu_size'):
            self._prefix_widget.set_dayu_size(self._dayu_size)
        if hasattr(self._suffix_widget, 'set_dayu_size'):
            self._suffix_widget.set_dayu_size(self._dayu_size)
        self.style().polish(self)

    dayu_size = Property(int, get_dayu_size, set_dayu_size)

    def set_delay_duration(self, millisecond):
        """Set delay timer's timeout duration."""
        self._delay_timer.setInterval(millisecond)

    @Slot()
    def _slot_delay_text_changed(self):
        self.sig_delay_text_changed.emit(self.text())

    def get_prefix_widget(self):
        """Get the prefix widget for user to edit"""
        return self._prefix_widget

    def set_prefix_widget(self, widget):
        """Set the line edit left start widget"""
        if self._prefix_widget:
            index = self._main_layout.indexOf(self._prefix_widget)
            self._main_layout.takeAt(index)
            self._prefix_widget.setVisible(False)
        # if isinstance(widget, MPushButton):
        widget.setProperty('combine', 'horizontal')
        widget.setProperty('position', 'left')
        if hasattr(widget, 'set_dayu_size'):
            widget.set_dayu_size(self._dayu_size)

        margin = self.textMargins()
        margin.setLeft(margin.left() + widget.width())
        self.setTextMargins(margin)

        self._main_layout.insertWidget(0, widget)
        self._prefix_widget = widget
        return widget

    def get_suffix_widget(self):
        """Get the suffix widget for user to edit"""
        return self._suffix_widget

    def set_suffix_widget(self, widget):
        """Set the line edit right end widget"""
        if self._suffix_widget:
            index = self._main_layout.indexOf(self._suffix_widget)
            self._main_layout.takeAt(index)
            self._suffix_widget.setVisible(False)
        # if isinstance(widget, MPushButton):
        widget.setProperty('combine', 'horizontal')
        widget.setProperty('position', 'right')
        if hasattr(widget, 'set_dayu_size'):
            widget.set_dayu_size(self._dayu_size)

        margin = self.textMargins()
        margin.setRight(margin.right() + widget.width())
        self.setTextMargins(margin)
        self._main_layout.addWidget(widget)
        self._suffix_widget = widget
        return widget

    def setText(self, text):
        """Override setText save text to history"""
        self.setProperty('history', u'{}\n{}'.format(self.property('history'),
                                                     text))
        return super(MLineEdit, self).setText(text)

    def clear(self):
        """Override clear to clear history"""
        self.setProperty('history', '')
        return super(MLineEdit, self).clear()

    def keyPressEvent(self, event):
        """Override keyPressEvent to start delay timer"""
        if event.key() not in [Qt.Key_Enter, Qt.Key_Tab]:
            if self._delay_timer.isActive():
                self._delay_timer.stop()
            self._delay_timer.start()
        super(MLineEdit, self).keyPressEvent(event)

    def search(self):
        """Add a search icon button for MLineEdit."""
        suffix_button = MToolButton().icon_only().svg('close_line.svg')
        suffix_button.clicked.connect(self.clear)
        self.set_suffix_widget(suffix_button)
        self.setPlaceholderText(self.tr('Enter key word to search...'))
        return self

    def error(self):
        """A a toolset to MLineEdit to store error info with red style"""
        @Slot()
        def _slot_show_detail(self):
            dialog = QTextEdit(self)
            dialog.setReadOnly(True)
            geo = QApplication.desktop().screenGeometry()
            dialog.setGeometry(geo.width() / 2,
                               geo.height() / 2,
                               geo.width() / 4,
                               geo.height() / 4)
            dialog.setWindowTitle(self.tr('Error Detail Information'))
            dialog.setText(self.property('history'))
            dialog.setWindowFlags(Qt.Dialog)
            dialog.show()

        self.setProperty('dayu_type', 'error')
        self.setReadOnly(True)
        _suffix_button = MToolButton().icon_only().svg('detail_line.svg')
        _suffix_button.clicked.connect(
            functools.partial(_slot_show_detail, self))
        self.set_suffix_widget(_suffix_button)
        self.setPlaceholderText(self.tr('Error information will be here...'))
        return self

    def search_engine(self, text='Search'):
        """Add a MPushButton to suffix for MLineEdit"""
        _suffix_button = MPushButton(text=text).primary()
        _suffix_button.clicked.connect(self.returnPressed)
        _suffix_button.setFixedWidth(100)
        self.set_suffix_widget(_suffix_button)
        self.setPlaceholderText(self.tr('Enter key word to search...'))
        return self

    def file(self, filters=None):
        """Add a MClickBrowserFileToolButton for MLineEdit to select file"""
        _suffix_button = MClickBrowserFileToolButton()
        _suffix_button.sig_file_changed.connect(self.setText)
        _suffix_button.set_dayu_filters(filters or [])
        self.textChanged.connect(_suffix_button.set_dayu_path)
        self.set_suffix_widget(_suffix_button)
        self.setPlaceholderText(self.tr('Click button to browser files'))
        return self

    def save_file(self, filters=None):
        """Add a MClickSaveFileToolButton for MLineEdit to set save file"""
        _suffix_button = MClickSaveFileToolButton()
        _suffix_button.sig_file_changed.connect(self.setText)
        _suffix_button.set_dayu_filters(filters or [])
        self.textChanged.connect(_suffix_button.set_dayu_path)
        self.set_suffix_widget(_suffix_button)
        self.setPlaceholderText(self.tr('Click button to set save file'))
        return self

    def folder(self):
        """Add a MClickBrowserFolderToolButton for MLineEdit to select folder"""
        _suffix_button = MClickBrowserFolderToolButton()
        _suffix_button.sig_folder_changed.connect(self.setText)
        self.textChanged.connect(_suffix_button.set_dayu_path)
        self.set_suffix_widget(_suffix_button)
        self.setPlaceholderText(self.tr('Click button to browser folder'))
        return self

    def huge(self):
        """Set MLineEdit to huge size"""
        self.set_dayu_size(dayu_theme.huge)
        return self

    def large(self):
        """Set MLineEdit to large size"""
        self.set_dayu_size(dayu_theme.large)
        return self

    def medium(self):
        """Set MLineEdit to  medium"""
        self.set_dayu_size(dayu_theme.medium)
        return self

    def small(self):
        """Set MLineEdit to small size"""
        self.set_dayu_size(dayu_theme.small)
        return self

    def tiny(self):
        """Set MLineEdit to tiny size"""
        self.set_dayu_size(dayu_theme.tiny)
        return self

    def password(self):
        """Set MLineEdit to password echo mode"""
        self.setEchoMode(QLineEdit.Password)
        return self