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)
def _init_ui(self): self.button_grp = MRadioButtonGroup() self.button_grp.set_button_list( ['top', { 'text': 'right', 'checked': True }, 'bottom', 'left']) open_button_2 = MPushButton('Open').primary() open_button_2.clicked.connect(self.slot_open_button_2) placement_lay = QHBoxLayout() placement_lay.addWidget(self.button_grp) placement_lay.addSpacing(20) placement_lay.addWidget(open_button_2) placement_lay.addStretch() new_account_button = MPushButton(text='New account', icon=MIcon('add_line.svg', '#fff')).primary() new_account_button.clicked.connect(self.slot_new_account) new_account_lay = QHBoxLayout() new_account_lay.addWidget(MLabel('Submit form in drawer')) new_account_lay.addWidget(new_account_button) new_account_lay.addStretch() main_lay = QVBoxLayout() main_lay.addWidget(MDivider('Custom Placement')) main_lay.addLayout(placement_lay) main_lay.addWidget(MDivider('Submit form in drawer')) main_lay.addLayout(new_account_lay) main_lay.addWidget(MDivider('Preview drawer')) self.setLayout(main_lay)
def _init_ui(self): self.register_field('percent', 20) main_lay = QVBoxLayout() main_lay.addWidget(MDivider('different orientation')) for orn in [Qt.Horizontal, Qt.Vertical]: line_edit_hor = MSlider(orn) line_edit_hor.setRange(1, 100) self.bind('percent', line_edit_hor, 'value') lay = QVBoxLayout() lay.addWidget(line_edit_hor) main_lay.addLayout(lay) spin_box = MSpinBox() spin_box.setRange(1, 100) self.bind('percent', spin_box, 'value', signal='valueChanged') lay3 = QHBoxLayout() button_grp = MPushButtonGroup() button_grp.set_button_list([ { 'text': '+', 'clicked': functools.partial(self.slot_change_value, 10) }, { 'text': '-', 'clicked': functools.partial(self.slot_change_value, -10) }, ]) lay3.addWidget(spin_box) lay3.addWidget(button_grp) lay3.addStretch() main_lay.addWidget(MDivider('data bind')) main_lay.addLayout(lay3) main_lay.addStretch() self.setLayout(main_lay)
def __init__(self, view_type=None, parent=None): super(MItemViewSet, self).__init__(parent) self._main_lay = QVBoxLayout() self._main_lay.setSpacing(5) self._main_lay.setContentsMargins(0, 0, 0, 0) self.sort_filter_model = MSortFilterModel() self.source_model = MTableModel() self.sort_filter_model.setSourceModel(self.source_model) view_class = view_type or MItemViewSet.TableViewType self.item_view = view_class() self.item_view.doubleClicked.connect(self.sig_double_clicked) self.item_view.pressed.connect(self.slot_left_clicked) self.item_view.setModel(self.sort_filter_model) 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) _search_lay = QHBoxLayout() _search_lay.setContentsMargins(0, 0, 0, 0) _search_lay.addStretch() _search_lay.addWidget(self._search_line_edit) self._main_lay.addLayout(_search_lay) self._main_lay.addWidget(self.item_view) self.setLayout(self._main_lay)
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
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(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)
def __init__(self, parent=None): super(Checkable, self).__init__(parent) label = MLabel('Categories:') topic_lay = QHBoxLayout() topic_lay.addWidget(label) for i in ['Movies', 'Books', 'Music', 'Sports']: topic_lay.addWidget(MCheckableTag(text=i)) topic_lay.addStretch() main_lay = QHBoxLayout() main_lay.addLayout(topic_lay) self.setLayout(main_lay)
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)
def __init__(self, parent=None): super(Basic, self).__init__(parent) main_lay = QHBoxLayout() tag1 = MTag('Tag 1') tag2 = MTag('Clickable Tag').clickable() tag3 = MTag('Closeable Tag').closeable() tag2.sig_clicked.connect( functools.partial(self.slot_handle_signal, 'sig_clicked')) tag3.sig_closed.connect( functools.partial(self.slot_handle_signal, 'sig_closed')) main_lay.addWidget(tag1) main_lay.addWidget(tag2) main_lay.addWidget(tag3) main_lay.addStretch() self.setLayout(main_lay)
def _init_ui(self): form_lay = QFormLayout() form_lay.setLabelAlignment(Qt.AlignRight) gender_grp = MRadioButtonGroup() gender_grp.set_button_list([{ 'text': 'Female', 'icon': MIcon('female.svg') }, { 'text': 'Male', 'icon': MIcon('male.svg') }]) country_combo_box = MComboBox().small() country_combo_box.addItems(['China', 'France', 'Japan', 'US']) date_edit = MDateEdit().small() date_edit.setCalendarPopup(True) form_lay.addRow('Name:', MLineEdit().small()) form_lay.addRow('Gender:', gender_grp) form_lay.addRow('Age:', MSpinBox().small()) form_lay.addRow('Password:'******'Country:', country_combo_box) form_lay.addRow('Birthday:', date_edit) switch = MSwitch() switch.setChecked(True) form_lay.addRow('Switch:', switch) slider = MSlider() slider.setValue(30) form_lay.addRow('Slider:', slider) button_lay = QHBoxLayout() button_lay.addStretch() button_lay.addWidget(MPushButton(text='Submit').primary()) button_lay.addWidget(MPushButton(text='Cancel')) main_lay = QVBoxLayout() main_lay.addLayout(form_lay) main_lay.addWidget(MCheckBox('I accept the terms and conditions')) main_lay.addStretch() main_lay.addWidget(MDivider()) main_lay.addLayout(button_lay) self.setLayout(main_lay)
def _init_ui(self): size_lay = QVBoxLayout() sub_lay1 = QHBoxLayout() sub_lay1.addWidget(MToolButton().svg('left_line.svg').icon_only()) sub_lay1.addWidget(MToolButton().svg('right_line.svg').icon_only()) sub_lay1.addWidget(MToolButton().svg('up_line.svg').icon_only()) sub_lay1.addWidget(MToolButton().svg('down_line.svg').icon_only()) sub_lay1.addStretch() size_lay.addLayout(sub_lay1) button2 = MToolButton().svg('detail_line.svg').icon_only() button2.setEnabled(False) button7 = MToolButton().svg('trash_line.svg').icon_only() button7.setCheckable(True) state_lay = QHBoxLayout() state_lay.addWidget(button2) state_lay.addWidget(button7) state_lay.addStretch() button_trash = MToolButton().svg('trash_line.svg').text_beside_icon() button_trash.setText('Delete') button_login = MToolButton().svg('user_line.svg').text_beside_icon() button_login.setText('Login') button_lay = QHBoxLayout() button_lay.addWidget(button_trash) button_lay.addWidget(button_login) sub_lay2 = QHBoxLayout() sub_lay2.addWidget(button2) sub_lay2.addWidget(button7) main_lay = QVBoxLayout() main_lay.addWidget(MDivider('different button_size')) main_lay.addLayout(size_lay) main_lay.addWidget(MDivider('disabled & checkable')) main_lay.addLayout(state_lay) main_lay.addWidget(MDivider('type=normal')) main_lay.addLayout(button_lay) main_lay.addStretch() self.setLayout(main_lay)
def __init__(self, parent=None): super(AvatarExample, self).__init__(parent) self.setWindowTitle('Example for MAvatar') main_lay = QVBoxLayout() main_lay.addWidget(MDivider('different size')) size_list = [('Huge', MAvatar.huge), ('Large', MAvatar.large), ('Medium', MAvatar.medium), ('Small', MAvatar.small), ('Tiny', MAvatar.tiny)] self.pix_map_list = [ None, MPixmap('avatar.png'), MPixmap('app-maya.png'), MPixmap('app-nuke.png'), MPixmap('app-houdini.png') ] form_lay = QFormLayout() form_lay.setLabelAlignment(Qt.AlignRight) for label, cls in size_list: h_lay = QHBoxLayout() for image in self.pix_map_list: avatar_tmp = cls(image) h_lay.addWidget(avatar_tmp) h_lay.addStretch() form_lay.addRow(MLabel(label), h_lay) main_lay.addLayout(form_lay) self.register_field('image', None) main_lay.addWidget(MDivider('different image')) avatar = MAvatar() self.bind('image', avatar, 'dayu_image') button = MPushButton(text='Change Avatar Image').primary() button.clicked.connect(self.slot_change_image) main_lay.addWidget(avatar) main_lay.addWidget(button) main_lay.addStretch() self.setLayout(main_lay)
def _init_ui(self): standalone_lay = QHBoxLayout() standalone_lay.addWidget(MBadge.count(0)) standalone_lay.addWidget(MBadge.count(20)) standalone_lay.addWidget(MBadge.count(100)) standalone_lay.addWidget(MBadge.dot(True)) standalone_lay.addWidget(MBadge.text('new')) standalone_lay.addStretch() button = MToolButton().svg('trash_line.svg') avatar = MAvatar.large(MPixmap('avatar.png')) button_alert = MToolButton().svg('alert_fill.svg').large() badge_1 = MBadge.dot(True, widget=button) badge_2 = MBadge.dot(True, widget=avatar) badge_3 = MBadge.dot(True, widget=button_alert) button.clicked.connect(lambda: badge_1.set_dayu_dot(False)) spin_box = MSpinBox() spin_box.setRange(0, 9999) spin_box.valueChanged.connect(badge_3.set_dayu_count) spin_box.setValue(1) self.register_field('button1_selected', u'北京') menu1 = MMenu() menu1.set_data([u'北京', u'上海', u'广州', u'深圳']) select1 = MComboBox() select1.set_menu(menu1) self.bind('button1_selected', select1, 'value', signal='sig_value_changed') badge_hot = MBadge.text('hot', widget=MLabel(u'你的理想城市 ')) sub_lay1 = QHBoxLayout() sub_lay1.addWidget(badge_1) sub_lay1.addWidget(badge_2) sub_lay1.addWidget(badge_3) sub_lay1.addStretch() sub_lay2 = QHBoxLayout() sub_lay2.addWidget(badge_hot) sub_lay2.addWidget(select1) sub_lay2.addStretch() main_lay = QVBoxLayout() main_lay.addWidget(MDivider('use standalone')) main_lay.addLayout(standalone_lay) main_lay.addWidget(MDivider('different type')) main_lay.addLayout(sub_lay1) main_lay.addWidget(spin_box) main_lay.addWidget(MDivider('different type')) main_lay.addLayout(sub_lay2) main_lay.addStretch() self.setLayout(main_lay)
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)
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)
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()
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 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
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)
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
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
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
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._button_group.removeButton(sep) 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)
class MItemViewSet(QWidget): sig_double_clicked = Signal(QModelIndex) sig_left_clicked = Signal(QModelIndex) TableViewType = MTableView BigViewType = MBigView TreeViewType = MTreeView ListViewType = MListView def __init__(self, view_type=None, parent=None): super(MItemViewSet, self).__init__(parent) self.main_lay = QVBoxLayout() self.main_lay.setSpacing(5) self.main_lay.setContentsMargins(0, 0, 0, 0) self.sort_filter_model = MSortFilterModel() self.source_model = MTableModel() self.sort_filter_model.setSourceModel(self.source_model) view_class = view_type or MItemViewSet.TableViewType self.item_view = view_class() self.item_view.doubleClicked.connect(self.sig_double_clicked) self.item_view.pressed.connect(self.slot_left_clicked) self.item_view.setModel(self.sort_filter_model) 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._search_lay = QHBoxLayout() self._search_lay.setContentsMargins(0, 0, 0, 0) self._search_lay.addStretch() self._search_lay.addWidget(self._search_line_edit) self.main_lay.addLayout(self._search_lay) self.main_lay.addWidget(self.item_view) self.setLayout(self.main_lay) @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) self.item_view.set_header_list(header_list) @Slot() def setup_data(self, data_list): self.source_model.clear() if data_list: self.source_model.set_data_list(data_list) 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 def insert_widget(self, widget): """Use can insert extra widget into search layout.""" self._search_lay.insertWidget(0, widget)
def _init_ui(self): main_lay = QVBoxLayout() self.setLayout(main_lay) main_lay.addWidget(MDivider('circle')) lay1 = QHBoxLayout() circle_1 = MProgressCircle(parent=self) circle_1.setFormat(u'%p Days') circle_1.setValue(80) circle_2 = MProgressCircle(parent=self) circle_2.set_dayu_color(dayu_theme.success_color) circle_2.setValue(100) circle_3 = MProgressCircle(parent=self) circle_3.set_dayu_color(dayu_theme.error_color) circle_3.setValue(40) dashboard_1 = MProgressCircle.dashboard(parent=self) dashboard_1.setFormat(u'%p Days') dashboard_1.setValue(80) dashboard_2 = MProgressCircle.dashboard(parent=self) dashboard_2.set_dayu_color(dayu_theme.success_color) dashboard_2.setValue(100) dashboard_3 = MProgressCircle.dashboard(parent=self) dashboard_3.set_dayu_color(dayu_theme.error_color) dashboard_3.setValue(40) lay1.addWidget(circle_1) lay1.addWidget(circle_2) lay1.addWidget(circle_3) dashboard_lay = QHBoxLayout() dashboard_lay.addWidget(dashboard_1) dashboard_lay.addWidget(dashboard_2) dashboard_lay.addWidget(dashboard_3) main_lay.addLayout(lay1) main_lay.addWidget(MDivider('dashboard')) main_lay.addLayout(dashboard_lay) main_lay.addWidget(MDivider('different radius')) circle_4 = MProgressCircle(parent=self) circle_4.set_dayu_width(100) circle_4.setValue(40) circle_5 = MProgressCircle(parent=self) circle_5.setValue(40) circle_6 = MProgressCircle(parent=self) circle_6.set_dayu_width(160) circle_6.setValue(40) lay2 = QHBoxLayout() lay2.addWidget(circle_4) lay2.addWidget(circle_5) lay2.addWidget(circle_6) main_lay.addLayout(lay2) main_lay.addWidget(MDivider('data bind')) self.register_field('percent', 0) self.register_field('color', self.get_color) self.register_field('format', self.get_format) circle = MProgressCircle(parent=self) self.bind('percent', circle, 'value') self.bind('color', circle, 'dayu_color') self.bind('format', circle, 'format') lay3 = QHBoxLayout() button_grp = MPushButtonGroup() button_grp.set_dayu_type(MPushButton.DefaultType) button_grp.set_button_list([ {'text': '+', 'clicked': functools.partial(self.slot_change_percent, 10)}, {'text': '-', 'clicked': functools.partial(self.slot_change_percent, -10)}, ]) lay3.addWidget(circle) lay3.addWidget(button_grp) lay3.addStretch() main_lay.addLayout(lay3) custom_widget = QWidget() custom_layout = QVBoxLayout() custom_layout.setContentsMargins(20, 20, 20, 20) custom_layout.addStretch() custom_widget.setLayout(custom_layout) lab1 = MLabel(text='42,001,776').h3() lab2 = MLabel(text=u'消费人群规模').secondary() lab3 = MLabel(text=u'总占人数 75%').secondary() lab1.setAlignment(Qt.AlignCenter) lab2.setAlignment(Qt.AlignCenter) lab3.setAlignment(Qt.AlignCenter) custom_layout.addWidget(lab1) custom_layout.addWidget(lab2) custom_layout.addWidget(MDivider()) custom_layout.addWidget(lab3) custom_layout.addStretch() custom_circle = MProgressCircle() custom_circle.set_dayu_width(180) custom_circle.setValue(75) custom_circle.set_widget(custom_widget) main_lay.addWidget(MDivider('custom circle')) main_lay.addWidget(custom_circle) main_lay.addStretch()
class MTag(QLabel): """ Tag for categorizing or markup. """ sig_closed = Signal() sig_clicked = Signal() def __init__(self, text='', parent=None): super(MTag, self).__init__(text=text, parent=parent) self._is_pressed = False self._close_button = MToolButton().tiny().svg('close_line.svg').icon_only() self._close_button.clicked.connect(self.sig_closed) self._close_button.clicked.connect(self.close) self._close_button.setVisible(False) self._main_lay = QHBoxLayout() self._main_lay.setContentsMargins(0, 0, 0, 0) self._main_lay.addStretch() self._main_lay.addWidget(self._close_button) self.setLayout(self._main_lay) self._clickable = False self._border = True self._border_style = QssTemplate(''' MTag{ font-size: 12px; padding: 3px; color: @text_color; border-radius: @border_radius; border: 1px solid @border_color; background-color: @background_color; } MTag:hover{ color: @hover_color; } ''') self._no_border_style = QssTemplate(''' MTag{ font-size: 12px; padding: 4px; border-radius: @border_radius; color: @text_color; border: 0 solid @border_color; background-color: @background_color; } MTag:hover{ background-color:@hover_color; } ''') self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self._color = None self.set_dayu_color(dayu_theme.secondary_text_color) def minimumSizeHint(self, *args, **kwargs): """Override minimumSizeHint for expand width when the close button is visible.""" orig = super(MTag, self).minimumSizeHint(*args, **kwargs) orig.setWidth(orig.width() + (dayu_theme.tiny if self._close_button.isVisible() else 0)) return orig def get_dayu_color(self): """Get tag's color""" return self._color def set_dayu_color(self, value): """Set Tag primary color.""" self._color = value self._update_style() def _update_style(self): if self._border: self.setStyleSheet( self._border_style.substitute(background_color=utils.fade_color(self._color, '15%'), border_radius=dayu_theme.border_radius_base, border_color=utils.fade_color(self._color, '35%'), hover_color=utils.generate_color(self._color, 5), text_color=self._color)) else: self.setStyleSheet(self._no_border_style.substitute( background_color=utils.generate_color(self._color, 6), border_radius=dayu_theme.border_radius_base, border_color=utils.generate_color(self._color, 6), hover_color=utils.generate_color(self._color, 5), text_color=dayu_theme.text_color_inverse)) dayu_color = Property(str, get_dayu_color, set_dayu_color) def mousePressEvent(self, event): """Override mousePressEvent to flag _is_pressed.""" if event.button() == Qt.LeftButton: self._is_pressed = True return super(MTag, self).mousePressEvent(event) def leaveEvent(self, event): """Override leaveEvent to reset _is_pressed flag.""" self._is_pressed = False return super(MTag, self).leaveEvent(event) def mouseReleaseEvent(self, event): """Override mouseReleaseEvent to emit sig_clicked signal.""" if event.button() == Qt.LeftButton and self._is_pressed: if self._clickable: self.sig_clicked.emit() self._is_pressed = False return super(MTag, self).mouseReleaseEvent(event) def closeable(self): """Set Tag can be closed and show the close icon button.""" self._close_button.setVisible(True) return self def clickable(self): """Set Tag can be clicked and change the cursor to pointing-hand shape when enter.""" self.setCursor(Qt.PointingHandCursor) self._clickable = True return self def no_border(self): """Set Tag style is border or fill.""" self._border = False self._update_style() return self def coloring(self, color): """Same as set_dayu_color. Support chain.""" self.set_dayu_color(color) return self
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()
def __init__(self, parent=None): super(MTagTest, self).__init__(parent) self.setWindowTitle('Examples for MTag') self.default_lay = MFlowLayout() self.default_lay.addWidget(MTag(text='Tag 1')) self.default_lay.addWidget(MTag(text='Closeable Tag').closeable()) add_tag = MNewTag('New Tag') add_tag.sig_add_tag.connect(self.slot_add_tag) self.default_lay.addWidget(add_tag) color_lay = MFlowLayout() for text, color in [ ('magenta', dayu_theme.magenta), ('red', dayu_theme.red), ('volcano', dayu_theme.volcano), ('orange', dayu_theme.orange), ('gold', dayu_theme.gold), ('lime', dayu_theme.lime), ('green', dayu_theme.green), ('cyan', dayu_theme.cyan), ('blue', dayu_theme.blue), ('geekblue', dayu_theme.geekblue), ('purple', dayu_theme.purple), ]: tag = MTag(text=text).closeable() tag.set_dayu_color(color) tag.sig_closed.connect( functools.partial(MMessage.success, 'Delete "{}"'.format(text), self)) color_lay.addWidget(tag) no_border_lay = MFlowLayout() for text, color in [ ('red', dayu_theme.red), ('green', dayu_theme.green), ('blue', dayu_theme.blue), ('geekblue', dayu_theme.geekblue), ('volcano', dayu_theme.volcano), ('orange', dayu_theme.orange), ('purple', dayu_theme.purple), ('lime', dayu_theme.lime), ]: tag = MTag(text=text).border(False).clickable() tag.set_dayu_color(color) tag.sig_clicked.connect( functools.partial(MMessage.success, 'Click "{}"'.format(text), self)) no_border_lay.addWidget(tag) label = MLabel('Categories:') topic_lay = QHBoxLayout() topic_lay.addWidget(label) for i in ['Movies', 'Books', 'Music', 'Sports']: topic_lay.addWidget(MCheckableTag(text=i)) topic_lay.addStretch() main_lay = QVBoxLayout() main_lay.addWidget(MDivider('Normal')) main_lay.addLayout(self.default_lay) main_lay.addWidget(MDivider('Border')) main_lay.addLayout(color_lay) main_lay.addWidget(MDivider('No Border & Click')) main_lay.addLayout(no_border_lay) main_lay.addWidget(MDivider('Checkable')) main_lay.addLayout(topic_lay) main_lay.addStretch() self.setLayout(main_lay)