def _setup_timers(self, duration): close_timer = QtCore.QTimer(self) anim_timer = QtCore.QTimer(self) close_timer.setSingleShot(True) close_timer.timeout.connect(self.close) close_timer.timeout.connect(self.closed.emit) anim_timer.timeout.connect(self._on_fade_out) close_timer.setInterval((duration or self.DEFAULT_DURATION) * 1000) anim_timer.setInterval((duration or self.DEFAULT_DURATION) * 1000 - 300) close_timer.start() anim_timer.start() self._pos_anim = QtCore.QPropertyAnimation(self) self._pos_anim.setTargetObject(self) self._pos_anim.setEasingCurve(QtCore.QEasingCurve.OutCubic) self._pos_anim.setDuration(300) self._pos_anim.setPropertyName(b'pos') self._opacity_anim = QtCore.QPropertyAnimation(self) self._opacity_anim = QtCore.QPropertyAnimation() self._opacity_anim.setTargetObject(self) self._opacity_anim.setDuration(300) self._opacity_anim.setEasingCurve(QtCore.QEasingCurve.OutCubic) self._opacity_anim.setPropertyName(b'windowOpacity') self._opacity_anim.setStartValue(0.0) self._opacity_anim.setEndValue(1.0)
def _fill_data(self): is_greater_version = self._is_greater_version() if is_greater_version: icon_pixmap = (resource.pixmap('success') or QtGui.QPixmap()).scaled( QtCore.QSize(30, 30), QtCore.Qt.KeepAspectRatio, transformMode=QtCore.Qt.SmoothTransformation) self._version_message_label.setText( 'Artella {} Plugin is updated!'.format(dcc.nice_name())) else: icon_pixmap = (resource.pixmap('info') or QtGui.QPixmap()).scaled( QtCore.QSize(30, 30), QtCore.Qt.KeepAspectRatio, transformMode=QtCore.Qt.SmoothTransformation) self._version_icon.setPixmap(icon_pixmap) self._version_message_label.setText( 'New Artella {} Plugin is available!'.format(dcc.nice_name())) self._version_icon.setPixmap(icon_pixmap) self._go_to_download_web_btn.setVisible(not is_greater_version) latest_version = self._latest_release_info.get('version', 'Undefined') self._current_version_label.setText( str(self._current_version or 'Undefined')) self._latest_version_label.setText(latest_version)
def __init__(self, parent=None, **kwargs): super(ProgressCricle, self).__init__(parent) self._infinite = False self._timer = QtCore.QTimer(self) self._timer.timeout.connect(self._on_increase_value) self._main_layout = QtWidgets.QHBoxLayout() self._default_label = QtWidgets.QLabel() self._default_label.setAlignment(QtCore.Qt.AlignCenter) self._main_layout.addWidget(self._default_label) self.setLayout(self._main_layout) self._color = QtGui.QColor(221, 235, 230) self._width = kwargs.get('width', 140) self.setTextDirection(self.Direction.BottomToTop) self._start_angle = 90 * 16 self._max_delta_angle = 360 * 16 self._height_factor = 1.0 self._width_factor = 1.0 self.setFixedSize( QtCore.QSize(self._width * self._width_factor, self._width * self._height_factor))
def __init__(self, parent=None, **kwargs): if not parent: from artella import dcc parent = dcc.get_main_window() self._use_artella_header = kwargs.pop('use_artella_header', True) super(BaseDialog, self).__init__(parent, **kwargs) self._pos_anim = QtCore.QPropertyAnimation(self) self._pos_anim.setTargetObject(self) self._pos_anim.setEasingCurve(QtCore.QEasingCurve.OutCubic) self._pos_anim.setDuration(300) self._pos_anim.setPropertyName(b'pos') self._opacity_anim = QtCore.QPropertyAnimation() self._opacity_anim.setTargetObject(self) self._opacity_anim.setEasingCurve(QtCore.QEasingCurve.OutCubic) self._opacity_anim.setDuration(300) self._opacity_anim.setPropertyName(b'windowOpacity') self._opacity_anim.setStartValue(0.0) self._opacity_anim.setEndValue(1.0) self.setup_ui() theme.theme().apply(self) self._fade_in()
class MainThreadAsyncInvoker(QtCore.QObject): """ Class that implements a mechanism to execute a function with arbitrary arguments in main thread asynchronously for DCCs that support Qt """ __signal = QtCore.Signal(object) def __init__(self): super(MainThreadAsyncInvoker, self).__init__() self.__signal.connect(self.__execute_in_main_thread) def invoke(self, fn, *args, **kwargs): """ Invoke the given function with the given arguments and keyword arguments in the main thread :param function fn: function to execute in main thread :param tuple args: args for the function :param dict kwargs: Named arguments for the function :return: Returns the result returned by the function :rtype: object """ self._signal.emit(lambda: fn(*args, **kwargs)) def __execute_in_main_thread(self, fn): """ Internal function that executes the function """ fn()
def string_is_hex(color_str): """ Returns whether or not given string is a valid hexadecimal color :param color_str: str :return: bool """ if color_str.startswith('#'): color_str = color_str[1:] hex_regex1 = QtCore.QRegExp('^[0-9A-F]{3}$', QtCore.Qt.CaseInsensitive) hex_regex2 = QtCore.QRegExp('^[0-9A-F]{6}$', QtCore.Qt.CaseInsensitive) hex_regex3 = QtCore.QRegExp('^[0-9A-F]{8}$', QtCore.Qt.CaseInsensitive) if hex_regex1.exactMatch(color_str) or hex_regex2.exactMatch(color_str) or hex_regex3.exactMatch(color_str): return True return False
def _calculate_position(self, parent=None): """ Internal function that calculates a proper position for the snack bar relative to its parent """ parent = parent or 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, SnackBarMessage) and child.isVisible(): offset = max( offset, child.y() + 10 + child.geometry().height() / 2) base_pos = pos.y() + SnackBarMessage.DEFAULT_TOP target_x = pos.x( ) + parent_geo.width() / 2 - self.size().width() / 2 target_y = (offset + 50) if offset else base_pos self._pos_anim.setStartValue(QtCore.QPoint(target_x, target_y - 40)) self._pos_anim.setEndValue(QtCore.QPoint(target_x, target_y))
def setup_ui(self): super(SplashDialog, self).setup_ui() self.setWindowFlags(QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint | QtCore.Qt.WA_DeleteOnClose) self.setAttribute(QtCore.Qt.WA_TranslucentBackground) splash_pixmap = resource.pixmap('artella_splash') splash = SplashScreen(splash_pixmap) splash.setMask(splash_pixmap.mask()) self._splash_layout = QtWidgets.QVBoxLayout() self._splash_layout.setAlignment(QtCore.Qt.AlignBottom) splash.setLayout(self._splash_layout) self.main_layout.addWidget(splash) size_width = splash_pixmap.size().width() + 20 size_height = splash_pixmap.size().height() + 20 self.setFixedSize(QtCore.QSize(size_width, size_height)) shadow_effect = QtWidgets.QGraphicsDropShadowEffect(self) shadow_effect.setBlurRadius(qtutils.dpi_scale(15)) shadow_effect.setColor(QtGui.QColor(0, 0, 0, 150)) shadow_effect.setOffset(qtutils.dpi_scale(0)) self.setGraphicsEffect(shadow_effect)
class BaseDialog(QtWidgets.QDialog, object): closed = QtCore.Signal() def __init__(self, parent=None, **kwargs): if not parent: from artella import dcc parent = dcc.get_main_window() self._use_artella_header = kwargs.pop('use_artella_header', True) super(BaseDialog, self).__init__(parent, **kwargs) self._pos_anim = QtCore.QPropertyAnimation(self) self._pos_anim.setTargetObject(self) self._pos_anim.setEasingCurve(QtCore.QEasingCurve.OutCubic) self._pos_anim.setDuration(300) self._pos_anim.setPropertyName(b'pos') self._opacity_anim = QtCore.QPropertyAnimation() self._opacity_anim.setTargetObject(self) self._opacity_anim.setEasingCurve(QtCore.QEasingCurve.OutCubic) self._opacity_anim.setDuration(300) self._opacity_anim.setPropertyName(b'windowOpacity') self._opacity_anim.setStartValue(0.0) self._opacity_anim.setEndValue(1.0) self.setup_ui() theme.theme().apply(self) self._fade_in() def get_main_layout(self): main_layout = QtWidgets.QVBoxLayout() main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) return main_layout def setup_ui(self): self.main_layout = self.get_main_layout() self.setLayout(self.main_layout) if self._use_artella_header: artella_frame = QtWidgets.QFrame() artella_frame.setObjectName('artellaFrame') artella_frame_layout = QtWidgets.QHBoxLayout() artella_frame.setLayout(artella_frame_layout) artella_header = QtWidgets.QLabel() artella_header_pixmap = resource.pixmap('artella_header') artella_header.setPixmap(artella_header_pixmap) artella_frame_layout.addStretch() artella_frame_layout.addWidget(artella_header) artella_frame_layout.addStretch() self.main_layout.addWidget(artella_frame) def fade_close(self): self._fade_out() # ================================================================================================================= # INTERNAL # ================================================================================================================= def _fade_out(self): self._opacity_anim.finished.connect(self.close) self._pos_anim.setDirection(QtCore.QAbstractAnimation.Backward) self._pos_anim.start() self._opacity_anim.setDirection(QtCore.QAbstractAnimation.Backward) self._opacity_anim.start() def _fade_in(self): self._pos_anim.start() self._opacity_anim.start()
def __init__(self, id, name, package, version, author, email, summary, latest_version, upload_date, size, url, icon_pixmap=None, parent=None): super(PluginVersionWidget, self).__init__(parent) self._id = id self._name = name self._package = package self._version = version self._author = author self._email = email self._summary = summary self._latest_version = latest_version self._upload_date = upload_date self._size = size self._url = url icon_pixmap = (icon_pixmap or resource.pixmap('artella') or QtGui.QPixmap()).scaled( QtCore.QSize(30, 30), QtCore.Qt.KeepAspectRatio, transformMode=QtCore.Qt.SmoothTransformation) self.setFrameShape(QtWidgets.QFrame.StyledPanel) self.setFrameShadow(QtWidgets.QFrame.Raised) self.setMinimumHeight(130) main_layout = QtWidgets.QHBoxLayout() main_layout.setContentsMargins(2, 2, 2, 2) main_layout.setSpacing(2) self.setLayout(main_layout) main_info_layout = QtWidgets.QVBoxLayout() main_info_layout.setContentsMargins(2, 2, 2, 2) main_info_layout.setSpacing(2) top_layout = QtWidgets.QHBoxLayout() top_layout.setContentsMargins(2, 2, 2, 2) top_layout.setSpacing(5) self._icon_label = QtWidgets.QLabel() self._icon_label.setPixmap(icon_pixmap) self._icon_label.setAlignment(QtCore.Qt.AlignTop) self._plugin_name_label = QtWidgets.QLabel(name) self._plugin_version_label = QtWidgets.QLabel( '({})'.format(version)) plugin_name_info_layout = QtWidgets.QVBoxLayout() plugin_name_info_layout.setContentsMargins(2, 2, 2, 2) plugin_name_info_layout.setSpacing(5) plugin_name_layout = QtWidgets.QHBoxLayout() plugin_name_layout.setContentsMargins(2, 2, 2, 2) plugin_name_layout.setSpacing(2) plugin_info_layout = QtWidgets.QHBoxLayout() plugin_info_layout.setContentsMargins(2, 2, 2, 2) plugin_info_layout.setSpacing(5) plugin_name_layout.addWidget(self._plugin_name_label) plugin_name_layout.addWidget(self._plugin_version_label) plugin_name_layout.addStretch() plugin_name_info_layout.addLayout(plugin_name_layout) plugin_name_info_layout.addLayout(plugin_info_layout) plugin_name_info_layout.addStretch() self._plugin_date_label = QtWidgets.QLabel(upload_date) self._plugin_size_label = QtWidgets.QLabel(size) separator_widget = QtWidgets.QWidget() separator_layout = QtWidgets.QVBoxLayout() separator_layout.setAlignment(QtCore.Qt.AlignLeft) separator_layout.setContentsMargins(0, 0, 0, 0) separator_layout.setSpacing(0) separator_widget.setLayout(separator_layout) separator_frame = QtWidgets.QFrame() separator_frame.setMaximumHeight(15) separator_frame.setFrameShape(QtWidgets.QFrame.VLine) separator_frame.setFrameShadow(QtWidgets.QFrame.Sunken) separator_layout.addWidget(separator_frame) plugin_info_layout.addWidget(self._plugin_date_label) plugin_info_layout.addWidget(separator_widget) plugin_info_layout.addWidget(self._plugin_size_label) plugin_info_layout.addStretch() top_layout.addWidget(self._icon_label) top_layout.addLayout(plugin_name_info_layout) bottom_layout = QtWidgets.QHBoxLayout() bottom_layout.setContentsMargins(2, 2, 2, 2) bottom_layout.setSpacing(5) self._summary_text = QtWidgets.QPlainTextEdit(summary) self._summary_text.setReadOnly(True) self._summary_text.setMinimumHeight(60) self._summary_text.setFocusPolicy(QtCore.Qt.NoFocus) bottom_layout.addWidget(self._summary_text) download_layout = QtWidgets.QVBoxLayout() download_layout.setContentsMargins(2, 2, 2, 2) download_layout.setSpacing(2) self._progress = splash.ProgressCricle(width=80) self._progress_text = QtWidgets.QLabel('Wait please ...') self._ok_label = QtWidgets.QLabel() self._ok_label.setPixmap(resource.pixmap('success')) self._update_button = QtWidgets.QPushButton() self._progress.setVisible(False) self._progress_text.setVisible(False) self._ok_label.setVisible(False) progress_layout = QtWidgets.QHBoxLayout() progress_layout.addStretch() progress_layout.addWidget(self._progress) progress_layout.addStretch() progress_text_layout = QtWidgets.QHBoxLayout() progress_text_layout.addStretch() progress_text_layout.addWidget(self._progress_text) progress_text_layout.addStretch() ok_layout = QtWidgets.QHBoxLayout() ok_layout.addStretch() ok_layout.addWidget(self._ok_label) ok_layout.addStretch() download_layout.addStretch() download_layout.addLayout(progress_layout) download_layout.addLayout(progress_text_layout) download_layout.addLayout(ok_layout) download_layout.addWidget(self._update_button) download_layout.addStretch() main_info_layout.addLayout(top_layout) main_info_layout.addStretch() main_info_layout.addLayout(bottom_layout) main_info_layout.addStretch() main_info_layout.addStretch() main_layout.addLayout(main_info_layout) separator_widget = QtWidgets.QWidget() separator_layout = QtWidgets.QVBoxLayout() separator_layout.setAlignment(QtCore.Qt.AlignLeft) separator_layout.setContentsMargins(0, 0, 0, 0) separator_layout.setSpacing(0) separator_widget.setLayout(separator_layout) separator_frame = QtWidgets.QFrame() separator_frame.setFrameShape(QtWidgets.QFrame.VLine) separator_frame.setFrameShadow(QtWidgets.QFrame.Sunken) separator_layout.addWidget(separator_frame) main_layout.addWidget(separator_widget) main_layout.addLayout(download_layout) main_info_layout.addStretch() self._update_plugin_thread = QtCore.QThread(self) self._update_plugin_worker = utils.UpdatePluginWorker() self._update_plugin_worker.moveToThread(self._update_plugin_thread) self._update_plugin_worker.updateStart.connect( self._on_start_update) self._update_plugin_worker.updateFinish.connect( self._on_finish_update) self._update_plugin_thread.start() self._timer = QtCore.QTimer(self) self.updatePlugin.connect(self._update_plugin_worker.run) self._update_button.clicked.connect(self._on_update) self._timer.timeout.connect(self._on_advance_progress) self.refresh()
class PluginVersionWidget(QtWidgets.QFrame, object): updatePlugin = QtCore.Signal() updated = QtCore.Signal() def __init__(self, id, name, package, version, author, email, summary, latest_version, upload_date, size, url, icon_pixmap=None, parent=None): super(PluginVersionWidget, self).__init__(parent) self._id = id self._name = name self._package = package self._version = version self._author = author self._email = email self._summary = summary self._latest_version = latest_version self._upload_date = upload_date self._size = size self._url = url icon_pixmap = (icon_pixmap or resource.pixmap('artella') or QtGui.QPixmap()).scaled( QtCore.QSize(30, 30), QtCore.Qt.KeepAspectRatio, transformMode=QtCore.Qt.SmoothTransformation) self.setFrameShape(QtWidgets.QFrame.StyledPanel) self.setFrameShadow(QtWidgets.QFrame.Raised) self.setMinimumHeight(130) main_layout = QtWidgets.QHBoxLayout() main_layout.setContentsMargins(2, 2, 2, 2) main_layout.setSpacing(2) self.setLayout(main_layout) main_info_layout = QtWidgets.QVBoxLayout() main_info_layout.setContentsMargins(2, 2, 2, 2) main_info_layout.setSpacing(2) top_layout = QtWidgets.QHBoxLayout() top_layout.setContentsMargins(2, 2, 2, 2) top_layout.setSpacing(5) self._icon_label = QtWidgets.QLabel() self._icon_label.setPixmap(icon_pixmap) self._icon_label.setAlignment(QtCore.Qt.AlignTop) self._plugin_name_label = QtWidgets.QLabel(name) self._plugin_version_label = QtWidgets.QLabel( '({})'.format(version)) plugin_name_info_layout = QtWidgets.QVBoxLayout() plugin_name_info_layout.setContentsMargins(2, 2, 2, 2) plugin_name_info_layout.setSpacing(5) plugin_name_layout = QtWidgets.QHBoxLayout() plugin_name_layout.setContentsMargins(2, 2, 2, 2) plugin_name_layout.setSpacing(2) plugin_info_layout = QtWidgets.QHBoxLayout() plugin_info_layout.setContentsMargins(2, 2, 2, 2) plugin_info_layout.setSpacing(5) plugin_name_layout.addWidget(self._plugin_name_label) plugin_name_layout.addWidget(self._plugin_version_label) plugin_name_layout.addStretch() plugin_name_info_layout.addLayout(plugin_name_layout) plugin_name_info_layout.addLayout(plugin_info_layout) plugin_name_info_layout.addStretch() self._plugin_date_label = QtWidgets.QLabel(upload_date) self._plugin_size_label = QtWidgets.QLabel(size) separator_widget = QtWidgets.QWidget() separator_layout = QtWidgets.QVBoxLayout() separator_layout.setAlignment(QtCore.Qt.AlignLeft) separator_layout.setContentsMargins(0, 0, 0, 0) separator_layout.setSpacing(0) separator_widget.setLayout(separator_layout) separator_frame = QtWidgets.QFrame() separator_frame.setMaximumHeight(15) separator_frame.setFrameShape(QtWidgets.QFrame.VLine) separator_frame.setFrameShadow(QtWidgets.QFrame.Sunken) separator_layout.addWidget(separator_frame) plugin_info_layout.addWidget(self._plugin_date_label) plugin_info_layout.addWidget(separator_widget) plugin_info_layout.addWidget(self._plugin_size_label) plugin_info_layout.addStretch() top_layout.addWidget(self._icon_label) top_layout.addLayout(plugin_name_info_layout) bottom_layout = QtWidgets.QHBoxLayout() bottom_layout.setContentsMargins(2, 2, 2, 2) bottom_layout.setSpacing(5) self._summary_text = QtWidgets.QPlainTextEdit(summary) self._summary_text.setReadOnly(True) self._summary_text.setMinimumHeight(60) self._summary_text.setFocusPolicy(QtCore.Qt.NoFocus) bottom_layout.addWidget(self._summary_text) download_layout = QtWidgets.QVBoxLayout() download_layout.setContentsMargins(2, 2, 2, 2) download_layout.setSpacing(2) self._progress = splash.ProgressCricle(width=80) self._progress_text = QtWidgets.QLabel('Wait please ...') self._ok_label = QtWidgets.QLabel() self._ok_label.setPixmap(resource.pixmap('success')) self._update_button = QtWidgets.QPushButton() self._progress.setVisible(False) self._progress_text.setVisible(False) self._ok_label.setVisible(False) progress_layout = QtWidgets.QHBoxLayout() progress_layout.addStretch() progress_layout.addWidget(self._progress) progress_layout.addStretch() progress_text_layout = QtWidgets.QHBoxLayout() progress_text_layout.addStretch() progress_text_layout.addWidget(self._progress_text) progress_text_layout.addStretch() ok_layout = QtWidgets.QHBoxLayout() ok_layout.addStretch() ok_layout.addWidget(self._ok_label) ok_layout.addStretch() download_layout.addStretch() download_layout.addLayout(progress_layout) download_layout.addLayout(progress_text_layout) download_layout.addLayout(ok_layout) download_layout.addWidget(self._update_button) download_layout.addStretch() main_info_layout.addLayout(top_layout) main_info_layout.addStretch() main_info_layout.addLayout(bottom_layout) main_info_layout.addStretch() main_info_layout.addStretch() main_layout.addLayout(main_info_layout) separator_widget = QtWidgets.QWidget() separator_layout = QtWidgets.QVBoxLayout() separator_layout.setAlignment(QtCore.Qt.AlignLeft) separator_layout.setContentsMargins(0, 0, 0, 0) separator_layout.setSpacing(0) separator_widget.setLayout(separator_layout) separator_frame = QtWidgets.QFrame() separator_frame.setFrameShape(QtWidgets.QFrame.VLine) separator_frame.setFrameShadow(QtWidgets.QFrame.Sunken) separator_layout.addWidget(separator_frame) main_layout.addWidget(separator_widget) main_layout.addLayout(download_layout) main_info_layout.addStretch() self._update_plugin_thread = QtCore.QThread(self) self._update_plugin_worker = utils.UpdatePluginWorker() self._update_plugin_worker.moveToThread(self._update_plugin_thread) self._update_plugin_worker.updateStart.connect( self._on_start_update) self._update_plugin_worker.updateFinish.connect( self._on_finish_update) self._update_plugin_thread.start() self._timer = QtCore.QTimer(self) self.updatePlugin.connect(self._update_plugin_worker.run) self._update_button.clicked.connect(self._on_update) self._timer.timeout.connect(self._on_advance_progress) self.refresh() def refresh(self): if self._latest_version == self._version: self._update_button.setText('Updated') self._update_button.setEnabled(False) else: self._update_button.setText('Update ({})'.format( self._latest_version)) def _on_update(self): install_path = r'D:\dev\artella\test_download' plugin_path = self._id.replace('-', '.') try: mod = importlib.import_module(plugin_path) except Exception: mod = None if mod: install_path = os.path.dirname(mod.__path__[0]) print(install_path) # self._update_plugin_worker.set_id(self._id) # self._update_plugin_worker.set_package(self._package) # self._update_plugin_worker.set_latest_version(self._latest_version) # self._update_plugin_worker.set_url(self._url) # self._update_plugin_worker.set_install_path(install_path) # # self.updatePlugin.emit() def _on_finish_update(self, error_msg): valid = not bool(error_msg) self._progress.setVisible(False) self._progress_text.setVisible(False) self._update_button.setVisible(not valid) self._ok_label.setVisible(valid) self.updated.emit() def _on_start_update(self): self._progress.setVisible(True) self._progress_text.setVisible(True) self._update_button.setVisible(False) self._ok_label.setVisible(False) self._timer.start(5) def _on_advance_progress(self): self._progress.setValue(self._progress.value() + 1)
def _set_artella_size(self): self.setFixedSize(QtCore.QSize(self._artella_size, self._artella_size)) self._set_artella_image()
class ArtellaLabel(QtWidgets.QLabel, object): def __init__(self, text='', parent=None): super(ArtellaLabel, self).__init__(text, parent) self._actual_text = text self._artella_type = '' self._artella_underline = False self._artella_mark = False self._artella_delete = False self._artella_strong = False self._artella_code = False self._artella_level = 0 self._elide_mode = QtCore.Qt.ElideNone self.setProperty('artella_text', text) self.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction | QtCore.Qt.LinksAccessibleByMouse) self.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) def get_artella_level(self): return self._artella_level def set_artella_level(self, value): self._artella_level = value self.style().polish(self) def set_artella_underline(self, value): self._artella_underline = value self.style().polish(self) def get_artella_underline(self): return self._artella_underline def set_artella_delete(self, value): self._artella_delete = value self.style().polish(self) def get_artella_delete(self): return self._artella_delete def set_artella_strong(self, value): self._artella_strong = value self.style().polish(self) def get_artella_strong(self): return self._artella_strong def set_artella_mark(self, value): self._artella_mark = value self.style().polish(self) def get_artella_mark(self): return self._artella_mark def set_artella_code(self, value): self._artella_code = value self.style().polish(self) def get_artella_code(self): return self._artella_code def get_elide_mode(self): return self._elide_mode def set_elide_mode(self, value): self._elide_mode = value self._update_elided_text() def get_artella_type(self): return self._artella_type def set_artella_type(self, value): self._artella_type = value self.style().polish(self) artella_level = QtCore.Property(int, get_artella_level, set_artella_level) artella_type = QtCore.Property(str, get_artella_type, set_artella_type) artella_underline = QtCore.Property(bool, get_artella_underline, set_artella_underline) artella_delete = QtCore.Property(bool, get_artella_delete, set_artella_delete) artella_strong = QtCore.Property(bool, get_artella_strong, set_artella_strong) artella_mark = QtCore.Property(bool, get_artella_mark, set_artella_mark) artella_code = QtCore.Property(bool, get_artella_code, set_artella_code) artella_elide_mod = QtCore.Property(QtCore.Qt.TextElideMode, get_artella_code, set_artella_code) def minimumSizeHint(self): return QtCore.QSize(1, self.fontMetrics().height()) def text(self): return self._actual_text def setText(self, text): self._actual_text = text self._update_elided_text() self.setToolTip(text) def _update_elided_text(self): _font_metrics = self.fontMetrics() _elided_text = _font_metrics.elidedText(self._actual_text, self._elide_mode, self.width() - 2 * 2) super(ArtellaLabel, self).setText(_elided_text) def resizeEvent(self, event): self._update_elided_text() def h1(self): self.set_artella_level(ArtellaLabelTypes.H1) return self def h2(self): self.set_artella_level(ArtellaLabelTypes.H2) return self def h3(self): self.set_artella_level(ArtellaLabelTypes.H3) return self def h4(self): self.set_artella_level(ArtellaLabelTypes.H4) return self def secondary(self): self.set_artella_type(ArtellaLabelTypes.SECONDARY) return self def warning(self): self.set_artella_type(ArtellaLabelTypes.WARNING) return self def danger(self): self.set_artella_type(ArtellaLabel.DangerType) return self def strong(self): """Set QLabel with strong style.""" self.set_artella_strong(True) return self def mark(self): self.set_artella_mark(True) return self def code(self): self.set_artella_code(True) return self def delete(self): self.set_artella_delete(True) return self def underline(self): self.set_artella_underline(True) return self
def set_artella_size(self, value): self._artella_size = value self.style().polish(self) if self.toolButtonStyle() == QtCore.Qt.ToolButtonIconOnly: self.setFixedSize( QtCore.QSize(self._artella_size, self._artella_size))
class ArtellaToolButton(QtWidgets.QToolButton): def __init__(self, parent=None): super(ArtellaToolButton, self).__init__(parent=parent) self._artella_image = None self._artella_size = theme.theme().default_size self.setAutoExclusive(False) self.setAutoRaise(True) self.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) self._polish_icon() self.toggled.connect(self._polish_icon) def enterEvent(self, event): if self._artella_image: self.setIcon( resource.icon(self._artella_image, color=theme.theme().main_color)) return super(ArtellaToolButton, self).enterEvent(event) def leaveEvent(self, event): self._polish_icon() return super(ArtellaToolButton, self).leaveEvent(event) def get_artella_size(self): return self._artella_size def set_artella_size(self, value): self._artella_size = value self.style().polish(self) if self.toolButtonStyle() == QtCore.Qt.ToolButtonIconOnly: self.setFixedSize( QtCore.QSize(self._artella_size, self._artella_size)) def get_artella_image(self): return self._artella_image def set_artella_image(self, path): self._artella_image = path self._polish_icon() artella_size = QtCore.Property(int, get_artella_size, set_artella_size) def huge(self): self.set_artella_size(theme.theme().huge) return self def large(self): self.set_artella_size(theme.theme().large) return self def medium(self): self.set_artella_size(theme.theme().medium) return self def small(self): self.set_artella_size(theme.theme().small) return self def tiny(self): self.set_artella_size(theme.theme().tiny) return self def image(self, path): self.set_artella_image(path) return self def icon_only(self): self.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly) self.setFixedSize( QtCore.QSize(self._artella_size, self._artella_size)) return self def text_only(self): self.setToolButtonStyle(QtCore.Qt.ToolButtonTextOnly) return self def text_beside_icon(self): self.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) return self def text_under_icon(self): self.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon) return self def _polish_icon(self, *args, **kwargs): if self._artella_image: if self.isCheckable() and self.isChecked(): self.setIcon( resource.icon(self._artella_image, color=theme.theme().main_color)) else: self.setIcon(resource.icon(self._artella_image))
class SnackBarMessage(QtWidgets.QWidget, object): closed = QtCore.Signal() DEFAULT_DURATION = 6 DEFAULT_TOP = 180 def __init__(self, text='', title='', duration=None, artella_type=None, closable=False, parent=None): if parent is None: parent = dcc.get_main_window() current_type = artella_type or SnackBarTypes.ARTELLA super(SnackBarMessage, self).__init__(parent) self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Dialog | QtCore.Qt.WA_DeleteOnClose) self.setAttribute(QtCore.Qt.WA_StyledBackground) self.setAttribute(QtCore.Qt.WA_TranslucentBackground) main_layout = QtWidgets.QVBoxLayout() main_layout.setContentsMargins(2, 2, 2, 2) main_layout.setSpacing(2) self.setLayout(main_layout) main_frame = QtWidgets.QFrame() main_frame.setObjectName('mainFrame') frame_layout = QtWidgets.QVBoxLayout() frame_layout.setContentsMargins(5, 5, 5, 5) frame_layout.setSpacing(5) main_frame.setLayout(frame_layout) main_layout.addWidget(main_frame) info_layout = QtWidgets.QHBoxLayout() artella_label_layout = QtWidgets.QHBoxLayout() artella_label = image.ArtellaImage.small() artella_label.set_artella_image(resource.pixmap('artella_white')) self._close_btn = button.ArtellaToolButton( parent=self).image('close').tiny().icon_only() self._close_btn.setVisible(closable or False) self._close_btn.clicked.connect(self.close) if closable: artella_label_layout.addSpacing(20) artella_label_layout.addStretch() artella_label_layout.addWidget(artella_label) artella_label_layout.addStretch() artella_label_layout.addWidget(self._close_btn) title_layout = QtWidgets.QHBoxLayout() self._title_label = label.ArtellaLabel(parent=self).strong() self._title_label.setText(title) self._title_label.setVisible(bool(text)) title_layout.addStretch() title_layout.addWidget(self._title_label) title_layout.addStretch() self._icon_label = image.ArtellaImage.small() self._icon_label.set_artella_image( resource.pixmap('{}'.format(current_type), color=vars(theme.theme()).get(current_type + '_color'))) self._content_label = label.ArtellaLabel(parent=self) self._content_label.setText(text) info_layout.addStretch() info_layout.addWidget(self._icon_label) info_layout.addWidget(self._content_label) info_layout.addStretch() frame_layout.addLayout(artella_label_layout) frame_layout.addWidget(divider.ArtellaDivider()) frame_layout.addLayout(title_layout) frame_layout.addLayout(info_layout) self._setup_timers(duration) self._on_fade_in() @property def text(self): return self._text @text.setter def text(self, text): self._text = str(text) self._label.setText(self._text) self.setVisible(bool(self._text)) @classmethod def artella(cls, text, title='', parent=None, duration=None, closable=None): inst = cls(text, title=title, artella_type=SnackBarTypes.ARTELLA, duration=duration, closable=closable, parent=parent) theme.theme().apply(inst) inst.show() return inst @classmethod def info(cls, text, title='', parent=None, duration=None, closable=None): inst = cls(text, title=title, artella_type=SnackBarTypes.INFO, duration=duration, closable=closable, parent=parent) theme.theme().apply(inst) inst.show() return inst @classmethod def success(cls, text, title='', parent=None, duration=None, closable=None): inst = cls(text, title=title, artella_type=SnackBarTypes.SUCCESS, duration=duration, closable=closable, parent=parent) theme.theme().apply(inst) inst.show() return inst @classmethod def warning(cls, text, title='', parent=None, duration=None, closable=None): inst = cls(text, title=title, artella_type=SnackBarTypes.WARNING, duration=duration, closable=closable, parent=parent) theme.theme().apply(inst) inst.show() return inst @classmethod def error(cls, text, title='', parent=None, duration=None, closable=None): inst = cls(text, title=title, artella_type=SnackBarTypes.ERROR, duration=duration, closable=closable, parent=parent) theme.theme().apply(inst) inst.show() return inst def _setup_timers(self, duration): close_timer = QtCore.QTimer(self) anim_timer = QtCore.QTimer(self) close_timer.setSingleShot(True) close_timer.timeout.connect(self.close) close_timer.timeout.connect(self.closed.emit) anim_timer.timeout.connect(self._on_fade_out) close_timer.setInterval((duration or self.DEFAULT_DURATION) * 1000) anim_timer.setInterval((duration or self.DEFAULT_DURATION) * 1000 - 300) close_timer.start() anim_timer.start() self._pos_anim = QtCore.QPropertyAnimation(self) self._pos_anim.setTargetObject(self) self._pos_anim.setEasingCurve(QtCore.QEasingCurve.OutCubic) self._pos_anim.setDuration(300) self._pos_anim.setPropertyName(b'pos') self._opacity_anim = QtCore.QPropertyAnimation(self) self._opacity_anim = QtCore.QPropertyAnimation() self._opacity_anim.setTargetObject(self) self._opacity_anim.setDuration(300) self._opacity_anim.setEasingCurve(QtCore.QEasingCurve.OutCubic) self._opacity_anim.setPropertyName(b'windowOpacity') self._opacity_anim.setStartValue(0.0) self._opacity_anim.setEndValue(1.0) def resizeEvent(self, event): self.updateGeometry() self._calculate_position() super(SnackBarMessage, self).resizeEvent(event) def _calculate_position(self, parent=None): """ Internal function that calculates a proper position for the snack bar relative to its parent """ parent = parent or 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, SnackBarMessage) and child.isVisible(): offset = max( offset, child.y() + 10 + child.geometry().height() / 2) base_pos = pos.y() + SnackBarMessage.DEFAULT_TOP target_x = pos.x( ) + parent_geo.width() / 2 - self.size().width() / 2 target_y = (offset + 50) if offset else base_pos self._pos_anim.setStartValue(QtCore.QPoint(target_x, target_y - 40)) self._pos_anim.setEndValue(QtCore.QPoint(target_x, target_y)) def _on_fade_out(self): """ Internal callback function that fades out snack bar widget """ try: self._pos_anim.setDirection(QtCore.QAbstractAnimation.Backward) self._pos_anim.start() self._opacity_anim.setDirection( QtCore.QAbstractAnimation.Backward) self._opacity_anim.start() except Exception: pass def _on_fade_in(self): """ Internal callback function that fades in snack bar widget """ try: self._pos_anim.start() self._opacity_anim.start() except Exception: pass
def minimumSizeHint(self): return QtCore.QSize(1, self.fontMetrics().height())
def icon_only(self): self.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly) self.setFixedSize( QtCore.QSize(self._artella_size, self._artella_size)) return self
class UpdatePluginWorker(QtCore.QObject, object): updateStart = QtCore.Signal() updateFinish = QtCore.Signal(str) def __init__(self): super(UpdatePluginWorker, self).__init__() self.id = None self._package = None self._latest_version = None self._url = None self._install_path = None self._max_retries = 10 def set_id(self, id): self._id = id def set_package(self, package): self._package = package def set_latest_version(self, latest_version): self._latest_version = latest_version def set_url(self, url): self._url = url def set_install_path(self, install_path): self._install_path = install_path def set_max_retries(self, value): self._max_retries = value def run(self): self.updateStart.emit() if not self._url or not self._url.endswith('.tar.gz'): error_msg = 'Plugin Package URL does not contains a .tar.gaz file ({} | {} | {}'.format( self._id, self._latest_version, self._url) self.updateFinish.emit(error_msg) return # TODO: We should download to a temporal folder and once everything is extracted we should move the info to # TODO: its proper place base_file_name = '{}_{}'.format(self._id, self._latest_version) file_name = '{}.tar.gz'.format(base_file_name) file_path = os.path.join(self._install_path, file_name) try: valid = download_and_extract_package_from_pypi( self._url, file_path, self._install_path, max_retries=self._max_retries) if not valid: error_msg = 'Impossible to download and extract plugin from PyPI server ({} | {} | {})'.format( self._id, self._latest_version, self._url) self.updateFinish.emit(error_msg) return except Exception as exc: error_msg = 'Error while downloading new plugin version from PyPI server ({} | {} | {} | {})'.format( self._id, self._latest_version, self._url, exc) self.updateFinish.emit(error_msg) return False plugin_folder = None for root, dirs, files in os.walk(self._install_path): for plugin_dir in dirs: if plugin_dir == self._package or plugin_dir == self._package.lower(): plugin_folder = os.path.join(root, plugin_dir) break if not plugin_folder or not os.path.isdir(plugin_folder): error_msg = 'No Plugin folder found ({}) in the extracted Plugin data ({} | {})'.format( self._package, self._id, self._latest_version) self.updateFinish.emit(error_msg) return self.updateFinish.emit('')
class ArtellaImage(QtWidgets.QLabel, object): def __init__(self, parent=None): super(ArtellaImage, self).__init__(parent) self._default_pixmap = resource.pixmap('artella') self._pixmap = self._default_pixmap self._artella_size = 0 self.set_artella_size(theme.theme().default_size) def set_artella_size(self, value): self._artella_size = value self._set_artella_size() def _set_artella_size(self): self.setFixedSize(QtCore.QSize(self._artella_size, self._artella_size)) self._set_artella_image() def _set_artella_image(self): self.setPixmap(self._pixmap.scaledToWidth(self.height(), QtCore.Qt.SmoothTransformation)) def set_artella_image(self, value): """ Set avatar image. :param value: QPixmap or None. :return: None """ if value is None: self._pixmap = self._default_pixmap elif isinstance(value, QtGui.QPixmap): self._pixmap = value else: raise TypeError("Input argument 'value' should be QPixmap or None, but get {}".format(type(value))) self._set_artella_image() def get_artella_image(self): return self._pixmap def get_artella_size(self): return self._artella_size artella_image = QtCore.Property(QtGui.QPixmap, get_artella_image, set_artella_image) artella_size = QtCore.Property(int, get_artella_size, set_artella_size) @classmethod def tiny(cls, image=None): inst = cls() inst.set_artella_size(theme.theme().tiny) inst.set_artella_image(image) return inst @classmethod def small(cls, image=None): inst = cls() inst.set_artella_size(theme.theme().small) inst.set_artella_image(image) return inst @classmethod def medium(cls, image=None): inst = cls() inst.set_artella_size(theme.theme().medium) inst.set_artella_image(image) return inst @classmethod def large(cls, image=None): inst = cls() inst.set_artella_size(theme.theme().large) inst.set_artella_image(image) return inst @classmethod def huge(cls, image=None): inst = cls() inst.set_artella_size(theme.theme().huge) inst.set_artella_image(image) return inst