class MSequenceFile(QWidget, MFieldMixin): ''' 这个类必须依赖 DayuPath props: path: basestring sequence: bool ''' sig_is_sequence_changed = Signal(bool) def __init__(self, size=None, parent=None): super(MSequenceFile, self).__init__(parent) self.sequence_obj = None size = size or dayu_theme.small self._file_label = MLineEdit() self._file_label.set_dayu_size(size) self._file_label.setReadOnly(True) self._is_sequence_check_box = MCheckBox(self.tr('Sequence')) self._is_sequence_check_box.toggled.connect(functools.partial(self.setProperty, 'sequence')) self._is_sequence_check_box.toggled.connect(self.sig_is_sequence_changed) self._info_label = MLabel().secondary() self._error_label = MLabel().secondary() self._error_label.setProperty('error', True) self._error_label.setMinimumWidth(100) self._error_label.set_elide_mode(Qt.ElideMiddle) seq_lay = QHBoxLayout() seq_lay.addWidget(self._is_sequence_check_box) seq_lay.addWidget(self._info_label) seq_lay.addWidget(self._error_label) seq_lay.setStretchFactor(self._is_sequence_check_box, 0) seq_lay.setStretchFactor(self._info_label, 0) seq_lay.setStretchFactor(self._error_label, 100) self._main_lay = QVBoxLayout() self._main_lay.setContentsMargins(0, 0, 0, 0) self._main_lay.addWidget(self._file_label) self._main_lay.addLayout(seq_lay) self.setLayout(self._main_lay) self.set_sequence(True) def _set_path(self, value): path = DayuPath(value) for seq_obj in path.scan(): self.sequence_obj = seq_obj self._update_info() def set_path(self, value): self.setProperty('path', value) def set_sequence(self, value): assert isinstance(value, bool) self.setProperty('sequence', value) def _set_sequence(self, value): if value != self._is_sequence_check_box.isChecked(): # 更新来自代码 self._is_sequence_check_box.setChecked(value) self.sig_is_sequence_changed.emit(value) self._update_info() def _update_info(self): self._file_label.setProperty('text', self.sequence_obj if self.property('sequence') else self.property('path')) if self.sequence_obj: self._info_label.setText(u'Format: {ext} ' u'Total: {count} ' u'Range: {start}-{end}'.format(ext=self.sequence_obj.ext, count=len(self.sequence_obj.frames), start=self.sequence_obj.frames[ 0] if self.sequence_obj.frames else '/', end=self.sequence_obj.frames[ -1] if self.sequence_obj.frames else '/')) error_info = u'Missing: {}'.format(self.sequence_obj.missing) if self.sequence_obj.missing else '' self._error_label.setText(error_info) self._error_label.setToolTip(error_info) self._info_label.setVisible(self.property('sequence')) self._error_label.setVisible(self.property('sequence'))
class MProgressCircle(QProgressBar): """ MProgressCircle: Display the current progress of an operation flow. When you need to display the completion percentage of an operation. Property: dayu_width: int dayu_color: str """ def __init__(self, dashboard=False, parent=None): super(MProgressCircle, self).__init__(parent) self._main_lay = QHBoxLayout() self._default_label = MLabel().h3() self._default_label.setAlignment(Qt.AlignCenter) self._main_lay.addWidget(self._default_label) self.setLayout(self._main_lay) self._color = None self._width = None self._start_angle = 90 * 16 self._max_delta_angle = 360 * 16 self._height_factor = 1.0 self._width_factor = 1.0 if dashboard: self._start_angle = 225 * 16 self._max_delta_angle = 270 * 16 self._height_factor = (2 + pow(2, 0.5)) / 4 + 0.03 self.set_dayu_width(120) self.set_dayu_color(dayu_theme.primary_color) def set_widget(self, widget): """ Set a custom widget to show on the circle's inner center and replace the default percent label :param widget: QWidget :return: None """ self.setTextVisible(False) self._main_lay.addWidget(widget) def get_dayu_width(self): """ Get current circle fixed width :return: int """ return self._width def set_dayu_width(self, value): """ Set current circle fixed width :param value: int :return: None """ self._width = value self.setFixedSize( QSize(self._width * self._width_factor, self._width * self._height_factor)) def get_dayu_color(self): """ Get current circle foreground color :return: str """ return self._color def set_dayu_color(self, value): """ Set current circle's foreground color :param value: str :return: """ self._color = value self.update() dayu_color = Property(str, get_dayu_color, set_dayu_color) dayu_width = Property(int, get_dayu_width, set_dayu_width) def paintEvent(self, event): """Override QProgressBar's paintEvent.""" if self.text() != self._default_label.text(): self._default_label.setText(self.text()) if self.isTextVisible() != self._default_label.isVisible(): self._default_label.setVisible(self.isTextVisible()) percent = utils.get_percent(self.value(), self.minimum(), self.maximum()) total_width = self.get_dayu_width() pen_width = int(3 * total_width / 50.0) radius = total_width - pen_width - 1 painter = QPainter(self) painter.setRenderHints(QPainter.Antialiasing) # draw background circle pen_background = QPen() pen_background.setWidth(pen_width) pen_background.setColor(dayu_theme.background_selected_color) pen_background.setCapStyle(Qt.RoundCap) painter.setPen(pen_background) painter.drawArc(pen_width / 2.0 + 1, pen_width / 2.0 + 1, radius, radius, self._start_angle, -self._max_delta_angle) # draw foreground circle pen_foreground = QPen() pen_foreground.setWidth(pen_width) pen_foreground.setColor(self._color) pen_foreground.setCapStyle(Qt.RoundCap) painter.setPen(pen_foreground) painter.drawArc(pen_width / 2.0 + 1, pen_width / 2.0 + 1, radius, radius, self._start_angle, -percent * 0.01 * self._max_delta_angle) painter.end() @classmethod def dashboard(cls, parent=None): """Create a dashboard style MCircle""" return MProgressCircle(dashboard=True, parent=parent)
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)
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)