Beispiel #1
0
class Notify(QQuickView):
    height_ = Property(int, QQuickView.height, QQuickView.setHeight)
    width_ = Property(int, QQuickView.width, QQuickView.setWidth)

    def __init__(self, parent=None):
        parent = parent or NotifyContainer()
        super(Notify, self).__init__(parent)
        layout = parent.layout()
        if layout:
            layout.addWidget(self, alignment=layout.alignment())
        self.setStyleSheet("background:transparent;")
        self.setAttribute(Qt.WA_DeleteOnClose, True)

    @classmethod
    def from_file(cls, file, **data):
        """Construct from file path.

        Args:
            file (str): qml file path.
            data (dict, optional): Data will be used in qml as `DATA`.
        """

        ret = Notify()
        context = ret.rootContext()
        context.setContextProperty('DATA', data)
        context.setContextProperty('VIEW', ret)
        ret.setSource(QUrl.fromLocalFile(file))
        QApplication.processEvents()
        return ret
Beispiel #2
0
class ClickBrowserFolderButton(buttons.BaseButton, object):
    folderChanged = Signal(str)
    foldersChanged = Signal(list)

    _on_browse_folder = browse_folder

    def __init__(self, text='', multiple=False, parent=None):
        super(ClickBrowserFolderButton, self).__init__(text=text,
                                                       parent=parent)

        self._path = None
        self._multiple = multiple

        self.setToolTip('Click to browse folder')
        self.clicked.connect(self._on_browse_folder)

    def _get_path(self):
        """
        Returns last browse file path
        :return: str
        """

        return self._path

    def _set_path(self, value):
        """
        Sets browse start path
        :param value: str
        """

        self._path = value

    def _get_multiple(self):
        """
        Returns whether or not browse can select multiple files
        :return: bool
        """

        return self._multiple

    def _set_multiple(self, flag):
        """
        Sets whether or not browse can select multiple files
        :param flag: bool
        """

        self._multiple = flag

    path = Property(str, _get_path, _set_path)
    multiple = Property(bool, _get_multiple, _set_multiple)
Beispiel #3
0
class RadioButtonGroup(BaseButtonGroup, object):
    checkedChanged = Signal(int)

    def __init__(self, orientation=Qt.Horizontal, parent=None):
        super(RadioButtonGroup, self).__init__(orientation=orientation,
                                               parent=parent)

        self._button_group.setExclusive(True)
        self.set_spacing(15)

    def setup_signals(self):
        self._button_group.buttonClicked.connect(self.checkedChanged)

    def create_button(self, data_dict):
        """
        Implements BaseButtonGroup create_button abstract function
        :param data_dict:
        :return:
        """

        return buttons.BaseRadioButton()

    def _get_checked(self):
        return self._button_group.checkedId()

    def _sert_checked(self, value):
        btn = self._button_group.button(value)
        if btn:
            btn.setChecked(True)
            self.checkedChanged.emiet(value)

    checked = Property(int, _get_checked, _sert_checked, notify=checkedChanged)
Beispiel #4
0
class DirectoryWidget(BaseWidget, object):
    """
    Widget that contains variables to store current working directory
    """
    def __init__(self, parent=None, **kwargs):
        self._directory = None
        self._last_directory = None
        super(DirectoryWidget, self).__init__(parent=parent, **kwargs)

    def _get_directory(self):
        return self._directory

    def _set_directory(self, directory):
        with qt_contexts.block_signals(self):
            self.set_directory(directory)

    directory = Property(str, _get_directory, _set_directory)

    def get_directory(self):
        return self._directory

    def set_directory(self, directory):
        """
        Set the directory used by this widget
        :param directory: str, new directory of the widget
        """

        self._last_directory = self._directory
        self._directory = directory
Beispiel #5
0
class BaseCheckBox(QCheckBox, object):
    def __init__(self, text='', parent=None):
        super(BaseCheckBox, self).__init__(text=text, parent=parent)

    def _get_checked(self):
        return self.isChecked()

    def _set_checked(self, flag):
        with qt_contexts.block_signals(self):
            self.setChecked(flag)

    check = Property(bool, _get_checked, _set_checked)
Beispiel #6
0
class MenuTabBlockButtonGroup(group.BaseButtonGroup, object):
    checkedChanged = Signal(int)

    def __init_(self, parent=None):
        super(MenuTabBlockButtonGroup, self).__init__(parent=parent)

        self.set_spacing(1)
        self._button_group.setExclusive(True)
        self._button_group.buttonClicked[int].connect(self.checkedChanged)

    # =================================================================================================================
    # PROPERTIES
    # =================================================================================================================

    def _get_checked(self):
        """
        Returns current checked button's ID
        :return:  int
        """

        return self._button_group.checkedId()

    def _set_checked(self, value):
        """
        Sets current checked button's ID
        :param value: int
        """

        btn = self._button_group.button(value)
        btn.setChecked(True)
        self.checkedChanged.emit(value)

    checked = Property(int, _get_checked, _set_checked, notify=checkedChanged)

    # =================================================================================================================
    # OVERRIDES
    # =================================================================================================================

    def create_button(self, data_dict):
        button_theme = self.theme()

        btn = MenuTabBlockButton()
        if data_dict.get('image'):
            btn.image(data_dict.get('image'))
        if data_dict.get('text'):
            if data_dict.get('image') or data_dict.get('icon'):
                btn.text_beside_icon()
            else:
                btn.text_only()
        btn.theme_size = button_theme.large if button_theme else theme.Theme.Sizes.LARGE

        return btn
Beispiel #7
0
class Document(QObject):
    textChanged = Signal(str)

    def __init__(self, parent: QObject = None):
        super().__init__(parent)
        self.m_text = ""

    def text(self):
        return self.m_text

    def setText(self, text):
        if text == self.m_text:
            return
        self.m_text = text
        self.textChanged.emit(text)

    text = Property(str, fget=text, fset=setText, notify=textChanged)
Beispiel #8
0
class MenuLineButtonGroup(group.BaseButtonGroup, object):
    checkedChanged = Signal(int)

    def __init__(self, parent=None):
        super(MenuLineButtonGroup, self).__init__(parent=parent)

        self.set_spacing(1)
        self._button_group.setExclusive(True)
        self._button_group.buttonClicked[int].connect(self.checkedChanged)

    def create_button(self, data_dict):
        new_button = MenuLineButton(parent=self)
        if data_dict.get('image'):
            new_button.image(data_dict.get('image'))
        if data_dict.get('text'):
            if data_dict.get('image') or data_dict.get('icon'):
                new_button.text_beside_icon()
            else:
                new_button.text_only()

        return new_button

    def _get_checked(self):
        """
        Returns current checked button's ID
        :return: int
        """

        return self._button_group.checkedId()

    def _set_checked(self, value):
        """
        Sets current checked button's ID
        :param value: int
        """

        btn = self._button_group.button(value)
        btn.setChecked(True)
        self.checkedChanged.emit(value)

    theme_checked = Property(int,
                             _get_checked,
                             _set_checked,
                             notify=checkedChanged)
Beispiel #9
0
class BaseProgressBar(QProgressBar, object):

    ERROR_STATUS = 'error'
    NORMAL_STATUS = 'primary'
    SUCCESS_STATUS = 'success'
    WARNING_STATUS = 'warning'

    def __init__(self, parent=None):
        super(BaseProgressBar, self).__init__(parent)

        self.setAlignment(Qt.AlignCenter)
        self._status = BaseProgressBar.NORMAL_STATUS

    def _get_status(self):
        return self._status

    def _set_status(self, value):
        self._status = value
        self.style().polish(self)

    theme_status = Property(str, _get_status, _set_status)

    def normal(self):
        self.theme_status = BaseProgressBar.NORMAL_STATUS

    def success(self):
        self.theme_status = BaseProgressBar.SUCCESS_STATUS

    def error(self):
        self.theme_status = BaseProgressBar.ERROR_STATUS

    def auto_color(self):
        self.valueChanged.connect(self._on_update_color)

        return self

    def _on_update_color(self, value):
        if value >= self.maximum():
            self.theme_status = BaseProgressBar.SUCCESS_STATUS
        else:
            self.theme_status = self._status
Beispiel #10
0
class DragFolderButton(buttons.BaseToolButton, object):
    folderChanged = Signal(str)
    foldersChanged = Signal(list)
    _on_browse_folder = browse_folder

    def __init__(self, multiple=False, parent=None):
        super(DragFolderButton, self).__init__(parent=parent)

        self._path = None
        self._multiple = multiple

        self.setAcceptDrops(True)
        self.setMouseTracking(True)
        self.text_under_icon()

        self.theme_size = 60
        self.image('folder')
        self.setText('Click or drag folder here')
        self.setIconSize(QSize(60, 60))
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.setToolTip('Click to browse folder or drag folder here')
        self.clicked.connect(self._on_browse_folder)

    # =================================================================================================================
    # PROPERTIES
    # =================================================================================================================

    def _get_path(self):
        """
        Returns last browse file path
        :return: str
        """

        return self._path

    def _set_path(self, value):
        """
        Sets browse start path
        :param value: str
        """

        self._path = value

    def _get_multiple(self):
        """
        Returns whether or not browse can select multiple files
        :return: bool
        """

        return self._multiple

    def _set_multiple(self, flag):
        """
        Sets whether or not browse can select multiple files
        :param flag: bool
        """

        self._multiple = flag

    path = Property(str, _get_path, _set_path)
    multiple = Property(bool, _get_multiple, _set_multiple)

    # =================================================================================================================
    # OVERRIDES
    # =================================================================================================================

    def dragEnterEvent(self, event):
        """
        Overrides base QToolButton dragEnterEvent to validate dragged files
        :param event: QDragEvent
        """

        if event.mimeData().hasFormat("text/uri-list"):
            folder_list = [
                url.toLocalFile() for url in event.mimeData().urls()
                if os.path.isdir(url.toLocalFile())
            ]
            count = len(folder_list)
            if count == 1 or (count > 1 and self._multiple):
                event.acceptProposedAction()
                return

    def dropEvent(self, event):
        """
        Overrides base QToolButton dropEvent Event to accept dropped files
        :param event: QDropEvent
        """

        folder_list = [
            url.toLocalFile() for url in event.mimeData().urls()
            if os.path.isdir(url.toLocalFile())
        ]
        if self._multiple:
            self.foldersChanged.emit(folder_list)
            self.set_path(folder_list)
        else:
            self.folderChanged.emit(folder_list[0])
            self.set_path(folder_list[0])

    # =================================================================================================================
    # BASE
    # =================================================================================================================

    def get_path(self):
        """
        Returns file path
        :return: str
        """

        return self._path

    def set_path(self, value):
        """
        Sets browse start path
        :param value: str
        """

        self.path = value
Beispiel #11
0
class DragFileButton(buttons.BaseToolButton, object):
    fileChanged = Signal(str)
    filesChanged = Signal(list)
    _on_browse_file = browse_file

    def __init__(self, text='', multiple=False, parent=None):
        super(DragFileButton, self).__init__(parent=parent)

        self._path = None
        self._multiple = multiple
        self._filters = list()

        self.setAcceptDrops(True)
        self.setMouseTracking(True)
        self.text_under_icon()
        self.setText(text)

        self.theme_size = 60
        self.image('attach')
        self.setIconSize(QSize(60, 60))
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.setToolTip('Click to browse file or drag file here')
        self.clicked.connect(self._on_browse_file)

    # =================================================================================================================
    # PROPERTIES
    # =================================================================================================================

    def _get_filters(self):
        """
        Returns browse filters
        :return: list(str)
        """

        return self._filters

    def _set_filters(self, value):
        """
        Sets browse filters
        :param value: list(str)
        """

        self._filters = value

    def _get_path(self):
        """
        Returns last browse file path
        :return: str
        """

        return self._path

    def _set_path(self, value):
        """
        Sets browse start path
        :param value: str
        """

        self._path = value

    def _get_multiple(self):
        """
        Returns whether or not browse can select multiple files
        :return: bool
        """

        return self._multiple

    def _set_multiple(self, flag):
        """
        Sets whether or not browse can select multiple files
        :param flag: bool
        """

        self._multiple = flag

    filters = Property(list, _get_filters, _set_filters)
    path = Property(str, _get_path, _set_path)
    multiple = Property(bool, _get_multiple, _set_multiple)

    # =================================================================================================================
    # OVERRIDES
    # =================================================================================================================

    def dragEnterEvent(self, event):
        """
        Overrides base QToolButton dragEnterEvent to validate dragged files
        :param event: QDragEvent
        """

        if event.mimeData().hasFormat("text/uri-list"):
            file_list = self._get_valid_file_list(event.mimeData().urls())
            count = len(file_list)
            if count == 1 or (count > 1 and self._multiple):
                event.acceptProposedAction()
                return

    def dropEvent(self, event):
        """
        Overrides base QToolButton dropEvent Event to accept dropped files
        :param event: QDropEvent
        """

        file_list = self._get_valid_file_list(event.mimeData().urls())
        if self._multiple:
            self.filesChanged.emit(file_list)
            self.set_path(file_list)
        else:
            self.fileChanged.emit(file_list[0])
            self.set_path(file_list[0])

    # =================================================================================================================
    # BASE
    # =================================================================================================================

    def get_path(self):
        """
        Returns file path
        :return: str
        """

        return self._path

    def set_path(self, value):
        """
        Sets browse start path
        :param value: str
        """

        self.path = value

    # =================================================================================================================
    # INTERNAL
    # =================================================================================================================

    def _get_valid_file_list(self, url_list):
        """
        Returns lits of valid dropped files
        :param url_list:
        :return: list(str)
        """

        file_list = list()
        for url in url_list:
            file_name = url.toLocalFile()
            if sys.platform == 'darwin':
                sub_process = subprocess.Popen(
                    'osascript -e \'get posix path of posix file \"file://{}\" -- kthxbai\''
                    .format(file_name),
                    stdout=subprocess.PIPE,
                    shell=True)
                file_name = sub_process.communicate()[0].strip()
                sub_process.wait()
            if os.path.isfile(file_name):
                if self.property('format'):
                    if os.path.splitext(file_name)[-1] in self.property(
                            'format'):
                        file_list.append(file_name)
                else:
                    file_list.append(file_name)

        return file_list
Beispiel #12
0
class BaseLineEdit(QLineEdit, object):
    """
     Basic line edit
     """

    delayTextChanged = Signal(str)

    def __init__(self, text='', input_mode=None, parent=None):
        super(BaseLineEdit, self).__init__(text, parent)

        self._prefix_widget = None
        self._suffix_widget = None
        self._size = self.theme_default_size()

        self._main_layout = layouts.HorizontalLayout()
        self._main_layout.setContentsMargins(0, 0, 0, 0)
        self._main_layout.addStretch()
        self.setLayout(self._main_layout)

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

        if input_mode == 'float':
            self.setValidator(QDoubleValidator())
        elif input_mode == 'int':
            self.setValidator(QIntValidator())

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

    # =================================================================================================================
    # PROPERTIES
    # =================================================================================================================

    def _get_text(self):
        return self.text()

    def _set_text(self, value):
        with qt_contexts.block_signals(self):
            self.setText(value)

    def _get_size(self):
        """
        Returns the spin box height size
        :return: float
        """

        return self._size

    def _set_size(self, value):
        """
        Sets spin box height size
        :param value: float
        """

        self._size = value
        if hasattr(self._prefix_widget, 'theme_size'):
            self._prefix_widget.theme_size = self._size
        if hasattr(self._suffix_widget, 'theme_size'):
            self._suffix_widget.theme_size = self._size
        self.style().polish(self)

    theme_size = Property(int, _get_size, _set_size)
    line_text = Property(str, _get_text, _set_text)

    # =================================================================================================================
    # OVERRIDES
    # =================================================================================================================

    def setText(self, text):
        """
        Overrides base QLineEdit setText base function.
        Save history
        :param text: str
        """

        self.setProperty('history', '{}\n{}'.format(self.property('history'),
                                                    text))
        return super(BaseLineEdit, self).setText(text)

    def clear(self):
        """
        Overrides base QLineEdit clear function
        :return:
        """

        self.setProperty('history', '')
        return super(BaseLineEdit, self).clear()

    def keyPressEvent(self, event):
        """
        Overrides base QLineEdit keyPressEvent function
        :param event: QKeyEvent
        """

        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(BaseLineEdit, self).keyPressEvent(event)

    # =================================================================================================================
    # BASE
    # =================================================================================================================

    def set_delay_duration(self, ms):
        """
        Sets the delay timer duration
        :param ms: float
        """

        self._delay_timer.setInterval(ms)

    def get_prefix_widget(self):
        """
        Returns prefix widget for user to edit
        :return: QWidget
        """

        return self._prefix_widget

    def set_prefix_widget(self, widget):
        """
        Sets the edit line left start widget
        :param widget: QWidget
        :return: QWidget
        """

        if self._prefix_widget:
            index = self._main_layout.indexOf(self._prefix_widget)
            self._main_layout.takeAt(index)
            self._prefix_widget.setVisible(False)
            self._prefix_widget.deleteLater()

        widget.setProperty('combine', 'horizontal')
        widget.setProperty('position', 'left')
        if hasattr(widget, 'theme_size'):
            widget.them_size = self.theme_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):
        """
        Returns suffix widget for user to edit
        :return: QWidget
        """

        return self._suffix_widget

    def set_suffix_widget(self, widget):
        """
        Sets the edit line right start widget
        :param widget: QWidget
        :return: QWidget
        """

        if self._suffix_widget:
            index = self._main_layout.indexOf(self._suffix_widget)
            self._main_layout.takeAt(index)
            self._suffix_widget.setVisible(False)
            self._suffix_widget.deleteLater()

        widget.setProperty('combine', 'horizontal')
        widget.setProperty('position', 'right')
        if hasattr(widget, 'theme_size'):
            widget.them_size = self.theme_size

        margin = self.textMargins()
        margin.setRight(margin.right() + widget.width())
        self.setTextMargins(margin)

        self._main_layout.addWidget(widget)
        self._prefix_widget = widget

        return widget

    def search(self):
        """
        Adds a search icon button for line edit
        :return: self
        """

        prefix_btn = buttons.BaseToolButton().image('search').icon_only()
        suffix_btn = buttons.BaseToolButton().image('close').icon_only()
        suffix_btn.clicked.connect(self.clear)
        self.set_prefix_widget(prefix_btn)
        self.set_suffix_widget(suffix_btn)
        self.setPlaceholderText('Enter keyword to search ...')

        return self

    def search_engine(self, text='Search'):
        """
        Adds a search push button to line edit
        :param text: str
        :return: self
        """

        _suffix_btn = buttons.BaseButton(text).primary()
        _suffix_btn.clicked.connect(self.returnPressed)
        _suffix_btn.setFixedWidth(100)
        self.set_suffix_widget(_suffix_btn)
        self.setPlaceholderText('Enter keyword to search ...')

        return self

    def file(self, filters=None):
        """
        Adds a ClickBrowserFileToolButton to line edit
        :param filters:
        :return: self
        """

        _suffix_btn = browser.ClickBrowserFileToolButton()
        _suffix_btn.fileChanged.connect(self.setText)
        _suffix_btn.filters = filters
        self.textChanged.connect(_suffix_btn.set_path)
        self.set_suffix_widget(_suffix_btn)
        self.setPlaceholderText('Click button to browse files')

        return self

    def save_file(self, filters=None):
        """
        Adds a ClickSaveFileToolButton to line edit
        :param filters:
        :return: self
        """

        _suffix_button = browser.ClickSaveFileToolButton()
        _suffix_button.fileChanged.connect(self.setText)
        _suffix_button.filters = filters or list()
        self.textChanged.connect(_suffix_button.set_path)
        self.set_suffix_widget(_suffix_button)
        self.setPlaceholderText('Click button to set save file')

        return self

    def folder(self):
        """
        Adds a ClickBrowserFileToolButton to line edit
        :return: self
        """

        _suffix_btn = browser.ClickBrowserFolderToolButton()
        _suffix_btn.folderChanged.connect(self.setText)
        self.textChanged.connect(_suffix_btn.set_path)
        self.set_suffix_widget(_suffix_btn)
        self.setPlaceholderText('Click button to browse folder')

        return self

    def error(self):
        """
        Shows error in line edit with red style
        :return: self
        """
        def _on_show_detail(self):
            dlg = QTextEdit(self)
            dlg.setReadOnly(True)
            geo = QApplication.desktop().screenGeometry()
            dlg.setGeometry(geo.width() / 2,
                            geo.height() / 2,
                            geo.width() / 4,
                            geo.height() / 4)
            dlg.setWindowTitle('Error Detail Information')
            dlg.setText(self.property('history'))
            dlg.setWindowFlags(Qt.Dialog)
            dlg.show()

        self.setProperty('theme_type', 'error')
        self.setReadOnly(True)
        _suffix_btn = buttons.BaseToolButton().image(
            'delete_message').icon_only()
        _suffix_btn.clicked.connect(partial(_on_show_detail, self))
        self.set_suffix_widget(_suffix_btn)
        self.setPlaceholderText('Error information will be here ...')

        return self

    def tiny(self):
        """
        Sets line edit to tiny size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.tiny if widget_theme else theme.Theme.Sizes.TINY

        return self

    def small(self):
        """
        Sets line edit to small size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.small if widget_theme else theme.Theme.Sizes.SMALL

        return self

    def medium(self):
        """
        Sets line edit to medium size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.medium if widget_theme else theme.Theme.Sizes.MEDIUM

        return self

    def large(self):
        """
        Sets line edit to large size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.large if widget_theme else theme.Theme.Sizes.LARGE

        return self

    def huge(self):
        """
        Sets line edit to huge size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.huge if widget_theme else theme.Theme.Sizes.HUGE

        return self

    def password(self):
        """
        Sets line edit password mode
        """

        self.setEchoMode(QLineEdit.Password)

        return self

    # =================================================================================================================
    # CALLBACKS
    # =================================================================================================================

    def _on_delay_text_changed(self):
        """
        Internal callback function that is called when delay timer is completed
        """

        self.delayTextChanged.emit(self.text())
Beispiel #13
0
class StatusWidget(QFrame, object):

    DEFAULT_DISPLAY_TIME = 10000  # milliseconds -> 10 seconds

    def __init__(self, *args):
        super(StatusWidget, self).__init__(*args)

        self._status = None
        self._blocking = False
        self._timer = QTimer(self)

        self.setObjectName('StatusWidget')
        self.setFrameShape(QFrame.NoFrame)
        self.setFixedHeight(19)
        self.setMinimumWidth(5)

        self._label = label.BaseLabel('', parent=self)
        self._label.setStyleSheet('background-color: transparent;')
        self._label.setCursor(Qt.IBeamCursor)
        self._label.setTextInteractionFlags(Qt.TextSelectableByMouse)
        self._label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)

        self.label_image = label.BaseLabel(parent=self)
        self.label_image.setMaximumSize(QSize(17, 17))
        self.label_image.hide()

        self.main_layout = QHBoxLayout(self)
        self.main_layout.setContentsMargins(1, 0, 0, 0)

        self.main_layout.addWidget(self.label_image)
        self.main_layout.addWidget(self._label)

        self.setLayout(self.main_layout)

        self._timer.timeout.connect(self._reset)

        # Force set to initialize default status Qt property
        self.status = ''

    def _get_status(self):
        return self._status

    def _set_status(self, value):
        self._status = str(value)
        self.polish()

    status = Property(str, _get_status, _set_status)

    def is_blocking(self):
        """
        Returns True if the status widget is blocking, otherwise return False
        :return: bool
        """

        return self._blocking

    def show_ok_message(self, message, msecs=None):
        """
        Set an ok message to be displayed in the status widget
        :param message: str
        :param msecs: int
        """

        if self.is_blocking():
            return

        self.status = 'ok'
        icon = resources.icon('ok')
        self._show_message(message, icon, msecs)

    def show_info_message(self, message, msecs=None):
        """
        Set an info message to be displayed in the status widget
        :param message: str
        :param msecs: int
        """

        if self.is_blocking():
            return

        self.status = 'info'
        icon = resources.icon('info')
        self._show_message(message, icon, msecs)

    def show_warning_message(self, message, msecs=None):
        """
       Set a warning message to be displayed in the status widget
       :param message: str
       :param msecs: int
       """

        if self.is_blocking():
            return

        self.status = 'warning'
        icon = resources.icon('warning')
        self._show_message(message, icon, msecs)

    def show_error_message(self, message, msecs=None):
        """
       Set an error message to be displayed in the status widget
       :param message: str
       :param msecs: int
       """

        self.status = 'error'
        icon = resources.icon('error', extension='png')
        self._show_message(message, icon, msecs, blocking=True)

    def _reset(self):
        """
        Called when the current animation has finished
        """

        self._timer.stop()
        self.label_image.setVisible(False)
        self._label.setText('')
        icon = resources.pixmap('blank')
        self.label_image.setPixmap(
            icon) if icon else self.label_image.setPixmap(QPixmap())
        self.setStyleSheet('')
        self._blocking = False
        self.status = ''

    def _show_message(self, message, icon, msecs=None, blocking=False):
        """
        Set the given text to be displayed in the status widget
        :param message: str
        :param icon: QIcon
        :param msecs: int
        :param blocking: bool
        """

        msecs = msecs or self.DEFAULT_DISPLAY_TIME
        self._blocking = blocking

        self.label_image.setStyleSheet('border: 0px;')

        if icon:
            self.label_image.setPixmap(icon.pixmap(QSize(17, 17)))
            self.label_image.show()
        else:
            self.label_image.hide()

        if message:
            self._label.setText(str(message))
            self._timer.stop()
            self._timer.start(msecs)
        else:
            self._reset()

        self.update()
Beispiel #14
0
class CircleLoading(base.BaseWidget, object):
    def __init__(self, size=None, color=None, speed=1, parent=None):
        super(CircleLoading, self).__init__(parent=parent)

        size = size or self.theme_default_size()
        self.setFixedSize(QSize(size, size))

        self._rotation = 0
        self._loading_pixmap = resources.pixmap(
            'loading', extension='svg', color=color
            or self.accent_color()).scaledToWidth(size,
                                                  Qt.SmoothTransformation)
        self._loading_anim = QPropertyAnimation()
        self._loading_anim.setTargetObject(self)
        self._loading_anim.setDuration(1000 * (1 / speed))
        self._loading_anim.setPropertyName('rotation')
        self._loading_anim.setStartValue(0)
        self._loading_anim.setEndValue(360)
        self._loading_anim.setLoopCount(-1)
        self._loading_anim.start()

    # ============================================================================================================
    # PROPERTIES
    # ============================================================================================================

    def _set_rotation(self, value):
        self._rotation = value
        self.update()

    def _get_rotation(self):
        return self._rotation

    rotation = Property(int, _get_rotation, _set_rotation)

    # ============================================================================================================
    # OVERRIDES
    # ============================================================================================================

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.SmoothPixmapTransform)
        painter.translate(self._loading_pixmap.width() / 2,
                          self._loading_pixmap.height() / 2)
        painter.rotate(self._rotation)
        painter.drawPixmap(-self._loading_pixmap.width() / 2,
                           -self._loading_pixmap.height() / 2,
                           self._loading_pixmap.width(),
                           self._loading_pixmap.height(), self._loading_pixmap)
        painter.end()

        return super(CircleLoading, self).paintEvent(event)

    # ============================================================================================================
    # BASE
    # ============================================================================================================

    def set_size(self, size):
        """
        Sets the size of the widget
        :param size: int
        """

        self.setFixedSize(QSize(size, size))
        self._loading_pixmap = self._loading_pixmap.scaledToWidth(
            size, Qt.SmoothTransformation)

    @classmethod
    def tiny(cls, color=None, parent=None):
        """
        Creates a circle loading widget with tiny size
        :param color:
        :param parent:
        :return:
        """

        loading_widget = cls(color=color, parent=parent)
        loading_theme = loading_widget.theme()
        loading_size = loading_theme.tiny if loading_theme else theme.Theme.Sizes.TINY
        loading_widget.set_size(loading_size)

        return loading_widget

    @classmethod
    def small(cls, color=None, parent=None):
        """
        Creates a circle loading widget with small size
        :param color:
        :param parent:
        :return:
        """

        loading_widget = cls(color=color)
        loading_theme = loading_widget.theme()
        loading_size = loading_theme.small if loading_theme else theme.Theme.Sizes.SMALL
        loading_widget.set_size(loading_size)

        return loading_widget

    @classmethod
    def medium(cls, color=None, parent=None):
        """
        Creates a circle loading widget with medium size
        :param color:
        :param parent:
        :return:
        """

        loading_widget = cls(color=color)
        loading_theme = loading_widget.theme()
        loading_size = loading_theme.medium if loading_theme else theme.Theme.Sizes.MEDIUM
        loading_widget.set_size(loading_size)

        return loading_widget

    @classmethod
    def large(cls, color=None, parent=None):
        """
        Creates a circle loading widget with large size
        :param color:
        :param parent:
        :return:
        """

        loading_widget = cls(color=color)
        loading_theme = loading_widget.theme()
        loading_size = loading_theme.large if loading_theme else theme.Theme.Sizes.LARGE
        loading_widget.set_size(loading_size)

        return loading_widget

    @classmethod
    def huge(cls, color=None, parent=None):
        """
        Creates a circle loading widget with huge size
        :param color:
        :param parent:
        :return:
        """

        loading_widget = cls(color=color)
        loading_theme = loading_widget.theme()
        loading_size = loading_theme.huge if loading_theme else theme.Theme.Sizes.HUGE
        loading_widget.set_size(loading_size)

        return loading_widget
class InfoMessage(base.BaseWidget, object):
    def __init__(self, name='', description='', instructions='', parent=None):
        self._name = ''
        self._description = ''
        self._instructions = instructions

        super(InfoMessage, self).__init__(parent)

        self.setAttribute(Qt.WA_StyledBackground)
        self.theme_type = message.MessageTypes.INFO
        self.style().polish(self)
        self.name = name
        self.description = description
        self.instructions = instructions

    # =================================================================================================================
    # PROPERTIES
    # =================================================================================================================

    def _get_name(self):
        return self._name

    def _set_name(self, name):
        self._name = str(name)
        self._expandable_frame.set_title(self._name)

    def _get_description(self):
        return self._description

    def _set_description(self, text):
        self._description = str(text)
        self._description_text.setPlainText(self._description)

    def _get_instructions(self):
        return self._instructions

    def _set_instructions(self, instructions):
        self._instructions = str(instructions)
        self._instructions_text.clear()
        self._instructions_text.insertHtml(instructions)
        self._instructions_widget.setVisible(bool(instructions))

    name = Property(str, _get_name, _set_name)
    description = Property(str, _get_description, _set_description)
    instructions = Property(str, _get_instructions, _set_instructions)

    # =================================================================================================================
    # OVERRIDES
    # =================================================================================================================

    def ui(self):
        super(InfoMessage, self).ui()

        self.setMaximumHeight(150)

        info_icon = resources.icon('info')
        self._expandable_frame = expandables.ExpandableFrame(icon=info_icon,
                                                             parent=self)
        self._expandable_frame.setFrameStyle(QFrame.StyledPanel
                                             | QFrame.Raised)

        expandable_layout = layouts.HorizontalLayout(margins=(2, 2, 2, 2))

        texts_layout = layouts.HorizontalLayout(spacing=0,
                                                margins=(0, 0, 0, 0))
        self._description_text = QPlainTextEdit(parent=self)
        self._description_text.setReadOnly(True)
        self._description_text.setSizePolicy(QSizePolicy.Preferred,
                                             QSizePolicy.Maximum)
        self._description_text.setFocusPolicy(Qt.NoFocus)
        self._description_text.setFrameShape(QFrame.NoFrame)
        self._instructions_widget = QWidget()
        instructions_layout = layouts.VerticalLayout(spacing=2,
                                                     margins=(0, 0, 0, 0))
        self._instructions_widget.setLayout(instructions_layout)
        self._instructions_text = QTextEdit(parent=self)
        self._instructions_text.setReadOnly(True)
        self._instructions_text.setSizePolicy(QSizePolicy.Preferred,
                                              QSizePolicy.Maximum)
        self._instructions_text.setFocusPolicy(Qt.NoFocus)
        self._instructions_text.setFrameShape(QFrame.NoFrame)
        self._instructions_widget.setVisible(False)
        # self._instructions_text.insertHtml("<ul><li>text 1</li><li>text 2</li><li>text 3</li></ul> <br />")
        instructions_layout.addWidget(dividers.Divider('Instructions'))
        instructions_layout.addWidget(self._instructions_text)
        texts_layout.addWidget(self._description_text)
        texts_layout.addWidget(self._instructions_widget)

        content_layout = layouts.VerticalLayout()
        content_layout.addLayout(texts_layout)
        expandable_layout.addLayout(content_layout)

        self._expandable_frame.addLayout(expandable_layout)

        self.main_layout.addWidget(self._expandable_frame)
Beispiel #16
0
class BaseToolButton(QToolButton, object):
    def __init__(self, parent=None):
        super(BaseToolButton, self).__init__(parent=parent)

        self._image = None
        self._image_theme = None
        self._size = self.theme_default_size()

        self.setAutoExclusive(False)
        self.setAutoRaise(True)
        self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)

        self._polish_icon()

        self.toggled.connect(self._polish_icon)

    # =================================================================================================================
    # PROPERTIES
    # =================================================================================================================

    def _get_size(self):
        """
        Returns the button height size
        :return: float
        """

        return self._size

    def _set_size(self, value):
        """
        Sets button height size
        :param value: float
        """

        self._size = value
        self.style().polish(self)
        if self.toolButtonStyle() == Qt.ToolButtonIconOnly:
            self.setFixedSize(self._size, self._size)

    theme_size = Property(int, _get_size, _set_size)

    # =================================================================================================================
    # OVERRIDES
    # =================================================================================================================

    def enterEvent(self, event):
        if self._image:
            theme = self.theme()
            if theme:
                accent_color = theme.accent_color
                if self._image_theme:
                    self.setIcon(
                        resources.icon(self._image,
                                       theme=self._image_theme,
                                       color=accent_color))
                else:
                    self.setIcon(
                        resources.icon(self._image, color=accent_color))
        return super(BaseToolButton, self).enterEvent(event)

    def leaveEvent(self, event):
        self._polish_icon()
        return super(BaseToolButton, self).leaveEvent(event)

    def _polish_icon(self, checked=None, **kwargs):
        if self._image:
            image_theme = kwargs.get('theme', self._image_theme)
            if image_theme:
                kwargs['theme'] = image_theme
            if self.isCheckable() and self.isChecked():
                self.setIcon(resources.icon(self._image, **kwargs))
            else:
                self.setIcon(resources.icon(self._image, **kwargs))

    # =================================================================================================================
    # BASE
    # =================================================================================================================

    def image(self, name, **kwargs):
        """
        Sets the name of the icon to use by the tool button
        :param str name:
        :return:
        """

        self._image = name
        self._image_theme = kwargs.get('theme', None)
        self._polish_icon(**kwargs)

        return self

    def tiny(self):
        """
        Sets tool button to tiny size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.tiny if widget_theme else theme.Theme.Sizes.TINY

        return self

    def small(self):
        """
        Sets tool button to small size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.small if widget_theme else theme.Theme.Sizes.SMALL

        return self

    def medium(self):
        """
        Sets tool button to medium size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.medium if widget_theme else theme.Theme.Sizes.MEDIUM

        return self

    def large(self):
        """
        Sets tool button to large size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.large if widget_theme else theme.Theme.Sizes.LARGE

        return self

    def huge(self):
        """
        Sets tool button to huge size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.huge if widget_theme else theme.Theme.Sizes.HUGE

        return self

    def icon_only(self):
        """
        Sets tool button style to icon only
        """

        self.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.setFixedSize(self._size, self._size)

        return self

    def text_only(self):
        """
        Sets tool button style to icon only
        """

        self.setToolButtonStyle(Qt.ToolButtonTextOnly)

        return self

    def text_beside_icon(self):
        """
        Sets tool button style to text beside icon
        """

        self.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        return self

    def text_under_icon(self):
        """
        Sets tool button style to text under icon
        """

        self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)

        return self
Beispiel #17
0
class DragDoubleSpinBoxLineAxis(base.BaseWidget, object):

    textChanged = Signal(str)
    valueChanged = Signal(float)

    def __init__(self,
                 axis,
                 reset=True,
                 start=0.0,
                 max=10,
                 min=-10,
                 positive=False,
                 parent=None):
        self._axis = axis
        self._reset = reset
        self._start = start
        self._max = max
        self._min = min
        self._positive = positive
        super(DragDoubleSpinBoxLineAxis, self).__init__(parent=parent)

    def get_main_layout(self):
        main_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0))

        return main_layout

    def ui(self):
        super(DragDoubleSpinBoxLineAxis, self).ui()

        axis_widget = QFrame()
        axis_widget.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        axis_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0))
        axis_widget.setLayout(axis_layout)
        self._axis_btn = buttons.get_axis_button(axis_type=self._axis,
                                                 parent=self)
        self._line = DragDoubleSpinBoxLine(start=self._start,
                                           max=self._max,
                                           min=self._min,
                                           positive=self._positive,
                                           parent=self)
        self._reset_btn = buttons.BaseToolButton(
            parent=self).image('reset').icon_only()
        self._reset_btn.setVisible(self._reset)
        self._reset_btn.setEnabled(self._reset)
        axis_layout.addWidget(self._axis_btn)
        axis_layout.addWidget(self._line)
        axis_layout.addWidget(self._reset_btn)

        self.main_layout.addWidget(axis_widget)

    def setup_signals(self):
        self._reset_btn.clicked.connect(self._on_reset)
        self._line.valueChanged.connect(self.valueChanged.emit)
        self._line.textChanged.connect(self.textChanged.emit)

    def _get_value(self):
        return self.value()

    def _set_value(self, value):
        with qt_contexts.block_signals(self, children=True):
            self.setValue(value)

    floatValue = Property(float, _get_value, _set_value)

    def value(self):
        return self._line.value()

    def setValue(self, new_value):
        self._line.setValue(new_value)

    def set_default(self, default):
        self._line.set_default(default)

    def set_minimum(self, minimum_value):
        self._min = minimum_value
        self._line.set_minimum(self._min)

    def set_maximum(self, maximum_value):
        self._max = maximum_value
        self._line.set_maximum(self._max)

    def _on_reset(self):
        self._line.setValue(str(self._line.default))
Beispiel #18
0
class BaseSpinBox(QSpinBox, object):
    def __init__(self, parent=None):
        super(BaseSpinBox, self).__init__(parent=parent)

        self._size = self.theme_default_size()

    # =================================================================================================================
    # PROPERTIES
    # =================================================================================================================

    def _get_size(self):
        """
        Returns the spin box height size
        :return: float
        """

        return self._size

    def _set_size(self, value):
        """
        Sets spin box height size
        :param value: float
        """

        self._size = value
        self.style().polish(self)

    theme_size = Property(int, _get_size, _set_size)

    # =================================================================================================================
    # BASE
    # =================================================================================================================

    def tiny(self):
        """
        Sets spin box to tiny size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.tiny if widget_theme else theme.Theme.Sizes.TINY

        return self

    def small(self):
        """
        Sets spin box to small size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.small if widget_theme else theme.Theme.Sizes.SMALL

        return self

    def medium(self):
        """
        Sets spin box to medium size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.medium if widget_theme else theme.Theme.Sizes.MEDIUM

        return self

    def large(self):
        """
        Sets spin box to large size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.large if widget_theme else theme.Theme.Sizes.LARGE

        return self

    def huge(self):
        """
        Sets spin box to huge size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.huge if widget_theme else theme.Theme.Sizes.HUGE

        return self
Beispiel #19
0
class DoubleSpinBoxAxis(base.BaseWidget, object):

    textChanged = Signal(str)
    valueChanged = Signal(float)

    def __init__(self,
                 axis,
                 start=0.0,
                 max=10,
                 min=-10,
                 positive=False,
                 parent=None):
        self._axis = axis
        self._start = start
        self._max = max
        self._min = min
        if positive:
            self._min = 0
        super(DoubleSpinBoxAxis, self).__init__(parent=parent)

    def get_main_layout(self):
        main_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0))

        return main_layout

    def ui(self):
        super(DoubleSpinBoxAxis, self).ui()

        axis_widget = QFrame(parent=self)
        axis_widget.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        axis_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0))
        axis_widget.setLayout(axis_layout)
        self._axis_btn = buttons.get_axis_button(axis_type=self._axis,
                                                 parent=self)
        self._line = BaseDoubleSpinBox(parent=self)
        self._line.setRange(self._min, self._max)
        self._line.setValue(self._start)
        axis_layout.addWidget(self._axis_btn)
        axis_layout.addWidget(self._line)

        self.main_layout.addWidget(axis_widget)

    def setup_signals(self):
        self._line.valueChanged.connect(self.valueChanged.emit)

    def _get_value(self):
        return self.value()

    def _set_value(self, value):
        with qt_contexts.block_signals(self, children=True):
            self.setValue(value)

    floatValue = Property(float, _get_value, _set_value)

    def value(self):
        return self._line.value()

    def setValue(self, new_value):
        self._line.setValue(new_value)

    def setDecimals(self, value):
        self._line.setDecimals(value)

    def setRange(self, min_range, max_range):
        self._min = min_range
        self._max = max_range
        self._line.setMinimum(min_range)
        self._line.setMaximum(max_range)

    def set_minimum(self, minimum_value):
        self._min = minimum_value
        self._line.setMinimum(self._min)

    def set_maximum(self, maximum_value):
        self._max = maximum_value
        self._line.setMaximum(self._max)
Beispiel #20
0
class BaseMessage(base.BaseWidget, object):
    def __init__(self, text='', parent=None):

        self._type = None
        self._text = ''

        super(BaseMessage, self).__init__(parent)

        self.setAttribute(Qt.WA_StyledBackground)

        self.set_show_icon(True)
        self.set_closable(False)
        self.theme_type = MessageTypes.INFO
        self.text = text

    # =================================================================================================================
    # PROPERTIES
    # =================================================================================================================

    def _get_text(self):
        """
        Returns message text
        :return: str
        """

        return self._text

    def _set_text(self, text):
        """
        Sets message text content
        :param text: str
        """

        self._text = str(text)
        self._content_label.setText(self._text)
        self.setVisible(bool(self._text))

    def _get_type(self):
        """
        Returns message type
        :return: float
        """

        return self._type

    def _set_type(self, value):
        """
        Sets message type
        :param value: str
        """

        current_theme = self.theme()

        if value in [
                MessageTypes.INFO, MessageTypes.SUCCESS, MessageTypes.WARNING,
                MessageTypes.ERROR
        ]:
            self._type = value
        else:
            raise ValueError(
                'Given button type: "{}" is not supported. Supported types '
                'are: info, success, warning, error'.format(value))

        if current_theme:
            self._icon_label.image = resources.pixmap(
                self._type,
                color=getattr(current_theme, '{}_color'.format(self._type)))
        else:
            self._icon_label.image = resources.pixmap(self._type)
        self.style().polish(self)

    text = Property(str, _get_text, _set_text)
    theme_type = Property(str, _get_type, _set_type)

    # =================================================================================================================
    # OVERRIDES
    # =================================================================================================================

    def get_main_layout(self):
        main_layout = layouts.HorizontalLayout(spacing=2, margins=(2, 2, 2, 2))

        return main_layout

    def ui(self):
        super(BaseMessage, self).ui()

        label_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0))
        self._icon_label = avatar.Avatar()
        self._icon_label.theme_size = self.theme_size
        label_layout.addStretch()
        label_layout.addWidget(self._icon_label)
        label_layout.addStretch()
        self._content_label = label.BaseLabel().secondary()
        self._close_btn = buttons.BaseToolButton().image(
            'close', theme='window').large().icon_only()

        self.main_layout.addLayout(label_layout)
        self.main_layout.addWidget(self._content_label)
        self.main_layout.addStretch()
        self.main_layout.addWidget(self._close_btn)

    def setup_signals(self):
        self._close_btn.clicked.connect(partial(self.setVisible, False))

    # =================================================================================================================
    # BASE
    # =================================================================================================================

    def set_closable(self, flag):
        """
        Sets whether or not the message is closable
        :param flag: bool
        """

        self._close_btn.setVisible(flag)

        return self

    def set_show_icon(self, flag):
        """
        Sets whether or not the display information type icon is visible or not
        :param flag: bool
        """

        self._icon_label.setVisible(flag)

        return self

    def info(self):
        """
        Sets message to info type
        """

        self.theme_type = MessageTypes.INFO

        return self

    def success(self):
        """
        Sets message to success type
        """

        self.theme_type = MessageTypes.SUCCESS

        return self

    def warning(self):
        """
        Sets message to warning type
        """

        self.theme_type = MessageTypes.WARNING

        return self

    def error(self):
        """
        Sets message to error type
        """

        self.theme_type = MessageTypes.ERROR

        return self

    def closable(self):
        """
        Sets message to info type
        """

        self.set_closable(True)

        return self
Beispiel #21
0
class HoudiniDoubleSlider(QWidget, object):
    """
    Slider that encapsulates a DoubleSlider and Houdini draggers linked together
    """

    valueChanged = Signal(object)

    def __init__(self, parent, slider_type='float', style=0, name=None, slider_range=None, default_value=0.0,
                 dragger_steps=None, main_color=None, *args):
        if slider_range is None:
            slider_range = (-100.0, 100.0)
        if dragger_steps is None:
            dragger_steps = FLOAT_SLIDER_DRAG_STEPS
        super(HoudiniDoubleSlider, self).__init__(parent=parent, *args)

        h = 20
        self._parent = parent
        self._type = slider_type
        self._value = 0.0
        self._label = None
        self._style_type = style

        theme = self.theme()
        if theme:
            theme_color = theme.accent_color
            if core_color.string_is_hex(theme_color):
                theme_color = core_color.hex_to_rgb(theme_color)
                main_color = QColor(*theme_color).getRgb()

        self._main_color = main_color or QColor(215, 128, 26).getRgb()

        self.setMaximumHeight(h)
        self.setMinimumHeight(h)

        self._main_layout = layouts.HorizontalLayout(margins=(10, 0, 0, 0))
        self.setLayout(self._main_layout)

        self._input = DraggerSlider(slider_type=slider_type)
        self._input.setButtonSymbols(QAbstractSpinBox.NoButtons)
        self._input.setRange(slider_range[0], slider_range[1])
        self._input.setContentsMargins(0, 0, 0, 0)
        self._input.setMinimumWidth(60 if self._type == 'float' else 40)
        self._input.setMaximumWidth(60 if self._type == 'float' else 40)
        self._input.setMinimumHeight(h)
        self._input.setMaximumHeight(h)
        self._input.valueIncremented.connect(self._on_increment_value)

        if self._type == 'float':
            self._slider = DoubleSlider(parent=self, default_value=default_value, slider_range=slider_range,
                                        dragger_steps=dragger_steps)
        else:
            self._slider = Slider(parent=self, slider_range=slider_range)
            self._slider.valueIncremented.connect(self._on_increment_value)
        self._slider.setContentsMargins(0, 0, 0, 0)
        self._slider.setMinimumHeight(h)
        self._slider.setMaximumHeight(h)
        self._slider.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)

        if name:
            self._label = label.BaseLabel(name + '  ', parent=self)
            self._main_layout.addWidget(self._label)
        self._main_layout.addWidget(self._input)
        self._main_layout.addWidget(self._slider)

        style_sheet = self._get_style_sheet(self._style_type)
        if self._style_type == 0:
            self._main_layout.setSpacing(0)
        self._slider.setStyleSheet(style_sheet)

        self._slider.valueChanged.connect(self._on_slider_value_changed)
        self._input.valueChanged.connect(self._on_houdini_slider_value_changed)

    def update(self):
        style_sheet = self._get_style_sheet(self._style_type)
        if self._style_type == 0:
            self._main_layout.setSpacing(0)
        self._slider.setStyleSheet(style_sheet)

    @property
    def minimum(self):
        return self._input.minimum()

    @property
    def maximum(self):
        return self._input.maximum()

    @property
    def _value_range(self):
        return self.maximum - self.minimum

    def _get_value(self):
        return self.value()

    def _set_value(self, value):
        with qt_contexts.block_signals(self):
            self.set_value(value)

    intValue = Property(int, _get_value, _set_value, user=True)
    floatValue = Property(float, _get_value, _set_value, user=True)

    def value(self):
        self._value = self._input.value()
        if self._type == 'int':
            self._value = int(self._value)

        return self._value

    def set_value(self, value):
        self._input.setValue(value)
        self._value = self._input.value()
        self.valueChanged.emit(self.value())
        # self._on_houdini_slider_value_changed(0)

    def set_decimals(self, decimals):
        self._input.setDecimals(decimals)

    def set_single_step(self, step):
        self._input.setSingleStep(step)

    def hide_label(self):
        if self._label:
            self._label.hide()

    def show_label(self):
        if self._label:
            self._label.show()

    def hide_slider(self):
        self._slider.hide()

    def show_slider(self):
        self._slider.show()

    def set_range(self, minimum_value, maximum_value):
        self._input.setRange(minimum_value, maximum_value)

    def _on_increment_value(self, step):
        if step == 0.0:
            return
        old = self._input.value()
        new = old + step
        self._input.setValue(new)
        self.valueChanged.emit(new)

    def _on_slider_value_changed(self, value):
        out_value = mathlib.map_range_unclamped(
            value, self._slider.minimum(), self._slider.maximum(), self._input.minimum(), self._input.maximum())
        with qt_contexts.block_signals(self._input):
            self._input.setValue(out_value)
        self.valueChanged.emit(out_value)

    def _on_houdini_slider_value_changed(self, value):
        in_value = mathlib.map_range_unclamped(
            self._input.value(), self._input.minimum(), self._input.maximum(),
            self._slider.minimum(), self._slider.maximum())
        with qt_contexts.block_signals(self._slider):
            self._slider.setValue(int(in_value))
        self.valueChanged.emit(value)

    def _get_style_sheet(self, style_type):
        if style_type == 0:
            return """
            QWidget{
                border: 1.25 solid black;
            }
            QSlider::groove:horizontal,
                QSlider::sub-page:horizontal {
                background: %s;
            }
            QSlider::add-page:horizontal,
                QSlider::sub-page:horizontal:disabled {
                background: rgb(32, 32, 32);
            }
            QSlider::add-page:horizontal:disabled {
                background: grey;
            }
            QSlider::handle:horizontal {
                width: 1px;
             }
            """ % "rgba%s" % str(self._main_color)
        else:
            return """
            QSlider::groove:horizontal {
                border: 1px solid #bbb;
                background: white;
                height: 3px;
                border-radius: 2px;
            }
            QSlider::sub-page:horizontal {
                background: %s;
                border: 0px solid #777;
                height: 3px;
                border-radius: 2px;
            }
            QSlider::add-page:horizontal {
                background: #fff;
                border: 1px solid #777;
                height: 3px;
                border-radius: 2px;
            }
            QSlider::handle:horizontal {
                background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
                    stop:0 #eee, stop:1 #ccc);
                border: 1px solid #777;
                width: 4px;
                margin-top: -8px;
                margin-bottom: -8px;
                border-radius: 2px;
                height : 10px;
            }
            QSlider::handle:horizontal:hover {
                background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
                    stop:0 #fff, stop:1 #ddd);
                border: 1px solid #444;
                border-radius: 2px;
            }
            QSlider::sub-page:horizontal:disabled {
                background: #bbb;
                border-color: #999;
            }
            QSlider::add-page:horizontal:disabled {
                background: #eee;
                border-color: #999;
            }
            QSlider::handle:horizontal:disabled {
                background: #eee;
                border: 1px solid #aaa;
                border-radius: 2px;
                height : 10;
            }
            """ % "rgba%s" % str(self._main_color)
Beispiel #22
0
class ToolBar(QToolBar, object):
    """
    Class that adds functionality to expand/collapse QToolBars
    """

    DEFAULT_EXPANDED_HEIGHT = 32
    DEFAULT_COLLAPSED_HEIGHT = 10
    ICON_SIZE = 32

    def __init__(self, *args, **kwargs):
        super(ToolBar, self).__init__(*args, **kwargs)

        self._dpi = 1
        self._is_expanded = True
        self._expanded_height = self.DEFAULT_EXPANDED_HEIGHT
        self._collapsed_height = self.DEFAULT_COLLAPSED_HEIGHT

        self.setMinimumHeight(self.DEFAULT_EXPANDED_HEIGHT)

    def _get_expanded(self):
        return self._is_expanded

    def _set_expanded(self, flag):
        self._is_expanded = flag

    expanded = Property(bool, _get_expanded, _set_expanded)

    # =================================================================================================================
    # OVERRIDES
    # =================================================================================================================

    def mousePressEvent(self, event):
        if not self.is_expanded():
            self.expand()

    def setFixedHeight(self, value):
        """
        Overrides base QToolBar setFixedHeight
        Allows to also set the height for all child widgets of the menu bar
        :param value: float
        """

        self.set_children_height(value)
        super(ToolBar, self).setFixedHeight(value)
        print(self.height())

    def insertAction(self, before, action):
        """
        Overrides base QToolBar insertAction function
        Support the before argument as string
        :param before: QAction or str
        :param action: QAction
        :return: QAction
        """

        action.setParent(self)
        if python.is_string(before):
            before = self.find_action(before)

        action = super(ToolBar, self).insertAction(before, action)

        return action

    def actions(self):
        """
        Overrides base QToolBar actions function
        Returns all the widgets that are a child of the menu bar widget
        :return: list(QWidget)
        """

        actions = list()

        for child in self.children():
            if isinstance(child, QAction):
                actions.append(child)

        return actions

    # =================================================================================================================
    # DPI
    # =================================================================================================================

    def dpi(self):
        """
        Returns the zoom multiplier
        :return: float
        """

        return self._dpi

    def set_dpi(self, dpi):
        """
        Set the zoom multiplier
        :param : float
        """

        self._dpi = dpi
        if self.is_expanded():
            self.expand()
        else:
            self.collapse()

    # =================================================================================================================
    # BASE
    # =================================================================================================================

    def widgets(self):
        """
        Returns all the widgets that are a child of the menu bar widget
        :return: list(QWidget)
        """

        widgets = list()
        for i in range(self.layout().count()):
            w = self.layout().itemAt(i).widget()
            if isinstance(w, QWidget):
                widgets.append(w)

        return widgets

    def is_expanded(self):
        """
        Returns whether the menu bar is expanded or not
        :return: bool
        """

        return self._is_expanded

    def expand_height(self):
        """
        Returns the height of menu bar when is expanded
        :return: float
        """

        return int(self._expanded_height * self.dpi())

    def collapse_height(self):
        """
        Returns the height of widget when collapsed
        :return: int
        """

        return int(self._collapsed_height * self.dpi())

    def set_children_hidden(self, flag):
        """
        Hide/Show all child widgets
        :param flag: bool
        """

        for action in self.actions():
            action.setVisible(not flag)

        # for w in self.widgets():
        #     w.setHidden(flag)

    def set_children_height(self, height):
        """
        Set the height of all the child widgets to the given height
        :param height: int
        """

        for w in self.widgets():
            w.setFixedHeight(height)

    def expand(self):
        """
        Expand the menu bar to the expand height
        """

        self._is_expanded = True
        height = self.expand_height()
        self.setFixedHeight(height)
        self.set_children_hidden(False)
        icon_size = self.ICON_SIZE * self.dpi()
        self.setIconSize(QSize(icon_size, icon_size))
        self.setStyleSheet(self.styleSheet())

    def collapse(self):
        """
        Collapse the menu bar to the collapse height
        """

        self._is_expanded = False
        height = self.collapse_height()
        self.setFixedHeight(height)
        self.set_children_height(0)
        self.set_children_hidden(True)
        self.setIconSize(QSize(0, 0))
        self.setStyleSheet(self.styleSheet())

    def set_icon_color(self, color):
        """
        Set the icon colors to the current foregroundRole
        :param color: QColor
        """

        for action in self.actions():
            action_icon = action.icon()
            action_icon = icon.Icon(action_icon)
            action_icon.set_color(color)
            action.setIcon(action_icon)

    def find_action(self, text):
        """
        Find the action with the given text
        :param text: str
        :return: QAction or None
        """

        for child in self.children():
            if isinstance(child, QAction):
                if child.text() == text:
                    return child

    def find_tool_button(self, text):
        """
        Find the QToolButton with the given text
        :param text: str
        :return: QToolButton or None
        """

        for child in self.children():
            if isinstance(child, QAction):
                if child.text() == text:
                    return self.widgetForAction(child)
Beispiel #23
0
class BaseButton(QPushButton, object):
    class Types(object):
        DEFAULT = 'default'
        PRIMARY = 'primary'
        SUCCESS = 'success'
        WARNING = 'warning'
        DANGER = 'danger'

    def __init__(self, text='', icon=None, elided=False, parent=None):

        self._text = text
        self._elided = elided

        if not icon:
            super(BaseButton, self).__init__(text=text, parent=parent)
        else:
            super(BaseButton, self).__init__(text=text,
                                             parent=parent,
                                             icon=icon)

        self._type = self.Types.DEFAULT
        self._size = self.theme_default_size()

        # NOTE: Without this, button will not call focusIn/out events when pressed
        self.setFocusPolicy(Qt.StrongFocus)

    # =================================================================================================================
    # PROPERTIES
    # =================================================================================================================

    def _get_type(self):
        """
        Returns button type
        :return: float
        """

        return self._type

    def _set_type(self, value):
        """
        Sets button type
        :param value: str
        """

        if value in [
                self.Types.DEFAULT, self.Types.PRIMARY, self.Types.SUCCESS,
                self.Types.WARNING, self.Types.DANGER
        ]:
            self._type = value
        else:
            raise ValueError(
                'Given button type: "{}" is not supported. Supported types '
                'are: default, primary, success, warning and danger'.format(
                    value))
        self.style().polish(self)

    def _get_size(self):
        """
        Returns the button height size
        :return: float
        """

        return self._size

    def _set_size(self, value):
        """
        Sets button height size
        :param value: float
        """

        self._size = value
        self.style().polish(self)

    theme_type = Property(str, _get_type, _set_type)
    theme_size = Property(int, _get_size, _set_size)

    # =================================================================================================================
    # BASE
    # =================================================================================================================

    def default(self):
        """
        Sets button to default style
        """

        self.theme_type = self.Types.DEFAULT

        return self

    def primary(self):
        """
        Sets button to primary style
        """

        self.theme_type = self.Types.PRIMARY

        return self

    def success(self):
        """
        Sets button to success style
        """

        self.theme_type = self.Types.SUCCESS

        return self

    def warning(self):
        """
        Sets button to warning style
        """

        self.theme_type = self.Types.WARNING

        return self

    def danger(self):
        """
        Sets button to danger style
        """

        self.theme_type = self.Types.DANGER

        return self

    def tiny(self):
        """
        Sets button to tiny size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.tiny if widget_theme else theme.Theme.Sizes.TINY

        return self

    def small(self):
        """
        Sets button to small size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.small if widget_theme else theme.Theme.Sizes.SMALL

        return self

    def medium(self):
        """
        Sets button to medium size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.medium if widget_theme else theme.Theme.Sizes.MEDIUM

        return self

    def large(self):
        """
        Sets button to large size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.large if widget_theme else theme.Theme.Sizes.LARGE

        return self

    def huge(self):
        """
        Sets button to huge size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.huge if widget_theme else theme.Theme.Sizes.HUGE

        return self

    def setText(self, text):
        self._text = text
        super(BaseButton, self).setText(text)

    def resizeEvent(self, event):
        if self._elided:
            has_icon = self.icon() and not self.icon().isNull()
            if has_icon:
                font_metrics = QFontMetrics(self.font())
                elided = font_metrics.elidedText(self._text, Qt.ElideMiddle,
                                                 self.width() - 30)
                super(BaseButton, self).setText(elided)
        super(BaseButton, self).resizeEvent(event)
Beispiel #24
0
class FilePickerWidget(QWidget):
    filenamePicked = Signal(str)
    filenameChanged = Signal(str)
    filenameEdited = Signal(str)

    def __init__(self, parent=None):
        self._correctBackground = QColor(156, 206, 156, 255)
        self._correctForeground = QColor(Qt.white)
        self._inCorrectBackground = QColor(210, 156, 156, 255)
        self._inCorrectForeground = QColor(Qt.white)
        self._defaultLocation = ''
        QWidget.__init__(self, parent)

        self.uiFilenameTXT = LineEdit(self)
        self.uiPickFileBTN = QToolButton(self)
        self.uiPickFileBTN.setText('...')
        self.uiPickFileBTN.setToolTip(
            '<html><head/><body><p>Browse to a file path.</p><p>Ctrl + LMB: Explore to current path.</p></body></html>'
        )
        # Make this widget focusable and pass the widget focus to uiFilenameTXT
        self.setFocusProxy(self.uiFilenameTXT)
        self.setFocusPolicy(Qt.StrongFocus)
        layout = QHBoxLayout(self)
        layout.addWidget(self.uiFilenameTXT)
        layout.addWidget(self.uiPickFileBTN)
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)

        self._caption = "Pick file..."
        self._filters = "All Files (*.*)"
        self._pickFolder = False
        self._openFile = False
        self._resolvePath = False
        #self._imageSequence = False
        self._resolved = False
        self._chosenPath = None
        #self._imageSequenceFormat = '{pre}[{firstNum}:{lastNum}]{post}'

        self.uiFilenameTXT.textChanged.connect(self.emitFilenameChanged)

        self.uiFilenameTXT.editingFinished.connect(self.emitFilenameEdited)
        self.uiPickFileBTN.clicked.connect(self.pickPath)
        self.resolvedStylesheet = resolvedStylesheetDefault

        self.resolve()

    def caption(self):
        return self._caption

    def emitFilenameChanged(self):
        self.resolve()
        if (not self.signalsBlocked()):
            self.filenameChanged.emit(self.uiFilenameTXT.text())

    def emitFilenameEdited(self):
        if (not self.signalsBlocked()):
            self.filenameEdited.emit(self.uiFilenameTXT.text())

    def filePath(self):
        # if it's an image sequence, return the last chosen image path
        return self._chosenPath or self.uiFilenameTXT.text()

    def filters(self):
        return self._filters

    def isResolved(self):
        return self._resolved

    def openFile(self):
        return self._openFile

    def pickFolder(self):
        return self._pickFolder

    def pickPath(self):
        initialPath = self.uiFilenameTXT.text() or self.defaultLocation
        initialPath = str(initialPath)
        while not os.path.exists(initialPath):
            if os.path.dirname(initialPath) == initialPath:
                break
            else:
                initialPath = os.path.dirname(initialPath)
        if QApplication.keyboardModifiers() == Qt.ControlModifier:
            import blurdev
            blurdev.osystem.explore(initialPath)
        else:
            if self._pickFolder:
                filepath = QFileDialog.getExistingDirectory(
                    self, self._caption, initialPath)
            elif self._openFile:
                filepath, _ = QtCompat.QFileDialog.getOpenFileName(
                    self, self._caption, initialPath, self._filters)
            else:
                filepath, _ = QtCompat.QFileDialog.getSaveFileName(
                    self, self._caption, initialPath, self._filters)
            if filepath:
                self.uiFilenameTXT.setText(filepath)
                if (not self.signalsBlocked()):
                    self.filenamePicked.emit(filepath)

    def resolve(self):
        if self.resolvePath():
            path = self.uiFilenameTXT.text()
            if self._pickFolder:
                valid = os.path.isdir(path)
            else:
                valid = os.path.isfile(path)
            if valid:
                fg = self.correctForeground
                bg = self.correctBackground
                self._resolved = True
            else:
                fg = self.inCorrectForeground
                bg = self.inCorrectBackground
                self._resolved = False

            style = self.resolvedStylesheet % {
                'bg': bg.getRgb(),
                'fg': fg.getRgb()
            }
        else:
            style = ''
            self._resolved = False

        self.uiFilenameTXT.setStyleSheet(style)

    def resolvePath(self):
        return self._resolvePath

    def setCaption(self, caption):
        self._caption = caption

    @Slot(str)
    def setFilePath(self, filePath):
        self.uiFilenameTXT.setText(filePath)
        self.resolve()

    def setFilters(self, filters):
        self._filters = filters

    def setOpenFile(self, state):
        self._openFile = state

    def setPickFolder(self, state):
        self._pickFolder = state

    @Slot(bool)
    def setNotResolvePath(self, state):
        """ Set resolvePath to the oposite of state. """
        self.setResolvePath(not state)

    @Slot(bool)
    def setResolvePath(self, state):
        self._resolvePath = state
        self.resolve()

    pyCaption = Property("QString", caption, setCaption)
    pyFilters = Property("QString", filters, setFilters)
    pyPickFolder = Property("bool", pickFolder, setPickFolder)
    pyOpenFile = Property("bool", openFile, setOpenFile)
    pyResolvePath = Property("bool", resolvePath, setResolvePath)
    #pyImageSequence	= Property( "bool", imageSequence, setImageSequence )
    pyFilePath = Property("QString", filePath, setFilePath)

    # Load the colors from the stylesheets
    @Property(QColor)
    def correctBackground(self):
        return self._correctBackground

    @correctBackground.setter
    def correctBackground(self, color):
        self._correctBackground = color
        self.resolve()

    @Property(QColor)
    def correctForeground(self):
        return self._correctForeground

    @correctForeground.setter
    def correctForeground(self, color):
        self._correctForeground = color
        self.resolve()

    @Property(QColor)
    def inCorrectBackground(self):
        return self._inCorrectBackground

    @inCorrectBackground.setter
    def inCorrectBackground(self, color):
        self._inCorrectBackground = color
        self.resolve()

    @Property(QColor)
    def inCorrectForeground(self):
        return self._inCorrectForeground

    @inCorrectForeground.setter
    def inCorrectForeground(self, color):
        self._inCorrectForeground = color
        self.resolve()

    @Property("QString")
    def defaultLocation(self):
        return self._defaultLocation

    @defaultLocation.setter
    def defaultLocation(self, value):
        self._defaultLocation = str(value)
Beispiel #25
0
class BaseComboBox(QComboBox, object):

    valueChanged = Signal(list)

    def __init__(self, parent=None):
        super(BaseComboBox, self).__init__(parent)

        self._root_menu = None
        self._display_formatter = formatters.display_formatter
        self._has_custom_view = False
        self._size = self.theme_default_size()

        self.setEditable(True)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)

        line_edit = self.lineEdit()
        line_edit.setReadOnly(True)
        line_edit.setTextMargins(4, 0, 4, 0)
        line_edit.setStyleSheet('background-color: transparent;')
        line_edit.installEventFilter(self)

        self.set_value('')
        self.set_placeholder('Please Select')

    # =================================================================================================================
    # PROPERTIES
    # =================================================================================================================

    def _get_size(self):
        """
        Returns the button height size
        :return: float
        """

        return self._size

    def _set_size(self, value):
        """
        Sets button height size
        :param value: float
        """

        self._size = value
        self.lineEdit().setProperty('theme_size', value)
        self.style().polish(self)

    theme_size = Property(int, _get_size, _set_size)

    # =================================================================================================================
    # OVERRIDES
    # =================================================================================================================

    def eventFilter(self, widget, event):
        """
        Overrides base eventFilter function
        :param widget:
        :param event:
        :return:
        """

        if widget is self.lineEdit():
            if event.type() == QEvent.MouseButtonPress:
                self.showPopup()
        return super(BaseComboBox, self).eventFilter(widget, event)

    def setView(self, *args, **kwargs):
        """
        Overrides base setView function
        :param args:
        :param kwargs:
        """

        self._has_custom_view = True
        super(BaseComboBox, self).setView(*args, **kwargs)

    def showPopup(self):
        """
        Overrides base showPopup function.
        If we have a custom menu, we make sure that we show it.
        """

        if self._has_custom_view or self._root_menu is None:
            super(BaseComboBox, self).showPopup()
        else:
            QComboBox.hidePopup(self)
            self._root_menu.popup(self.mapToGlobal(QPoint(0, self.height())))

    # =================================================================================================================
    # BASE
    # =================================================================================================================

    def set_formatter(self, fn):
        """
        Sets the formatter used by combobox
        :param fn:
        :return:
        """

        self._display_formatter = fn

    def set_placeholder(self, text):
        """
        Sets the placeholder text that appears when no item is selected
        :param text: str
        """

        self.lineEdit().setPlaceholderText(text)

    def set_value(self, value):
        """
        Sets combo box value
        :param value:
        """

        self.setProperty('value', value)

    def set_menu(self, menu):
        """
        Sets combo box custom menu
        :param menu: QMenu
        """

        self._root_menu = menu
        self._root_menu.valueChanged.connect(self.valueChanged)
        self._root_menu.valueChanged.connect(self.set_value)

    def tiny(self):
        """
        Sets spin box to tiny size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.tiny if widget_theme else theme.Theme.Sizes.TINY

        return self

    def small(self):
        """
        Sets spin box to small size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.small if widget_theme else theme.Theme.Sizes.SMALL

        return self

    def medium(self):
        """
        Sets spin box to medium size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.medium if widget_theme else theme.Theme.Sizes.MEDIUM

        return self

    def large(self):
        """
        Sets spin box to large size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.large if widget_theme else theme.Theme.Sizes.LARGE

        return self

    def huge(self):
        """
        Sets spin box to huge size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.huge if widget_theme else theme.Theme.Sizes.HUGE

        return self

    # =================================================================================================================
    # PROPERTY MIXIN SETTERS
    # =================================================================================================================

    def _set_value(self, value):
        self.lineEdit().setProperty('text', self._display_formatter(value))
        if self._root_menu:
            self._root_menu.set_value(value)
Beispiel #26
0
class ClickBrowserFileToolButton(buttons.BaseToolButton, object):
    fileChanged = Signal(str)
    filesChanged = Signal(list)

    _on_browse_file = browse_file

    def __init__(self, multiple=False, parent=None):
        super(ClickBrowserFileToolButton, self).__init__(parent=parent)

        self._path = None
        self._multiple = multiple
        self._filters = list()

        self.image('folder')
        self.icon_only()
        self.setToolTip('Click to browse file')
        self.clicked.connect(self._on_browse_file)

    # =================================================================================================================
    # PROPERTIES
    # =================================================================================================================

    def _get_filters(self):
        """
        Returns browse filters
        :return: list(str)
        """

        return self._filters

    def _set_filters(self, value):
        """
        Sets browse filters
        :param value: list(str)
        """

        self._filters = value

    def _get_path(self):
        """
        Returns last browse file path
        :return: str
        """

        return self._path

    def _set_path(self, value):
        """
        Sets browse start path
        :param value: str
        """

        self._path = value

    def _get_multiple(self):
        """
        Returns whether or not browse can select multiple files
        :return: bool
        """

        return self._multiple

    def _set_multiple(self, flag):
        """
        Sets whether or not browse can select multiple files
        :param flag: bool
        """

        self._multiple = flag

    filters = Property(list, _get_filters, _set_filters)
    path = Property(str, _get_path, _set_path)
    multiple = Property(bool, _get_multiple, _set_multiple)

    # =================================================================================================================
    # BASE
    # =================================================================================================================

    def set_path(self, value):
        """
        Sets browse start path
        :param value: str
        """

        self.path = value
Beispiel #27
0
class BaseWidget(QWidget, object):
    """
    Base class for all QWidgets based items
    """

    def_use_scrollbar = False

    def __init__(self, parent=None, **kwargs):
        super(BaseWidget, self).__init__(parent=parent)

        self._size = self.theme_default_size()

        self._use_scrollbar = kwargs.get('use_scrollbar',
                                         self.def_use_scrollbar)

        self.ui()
        self.setup_signals()

    # =================================================================================================================
    # PROPERTIES
    # =================================================================================================================

    def _get_size(self):
        """
        Returns the spin box height size
        :return: float
        """

        return self._size

    def _set_size(self, value):
        """
        Sets spin box height size
        :param value: float
        """

        self._size = value
        self.style().polish(self)

    theme_size = Property(int, _get_size, _set_size)

    # =================================================================================================================
    # OVERRIDES
    # =================================================================================================================

    def keyPressEvent(self, event):
        return

    def mousePressEvent(self, event):
        modifiers = QApplication.keyboardModifiers()
        if modifiers == Qt.AltModifier:
            pos = self.mapToGlobal((self.rect().topLeft()))
            QWhatsThis.showText(pos, self.whatsThis())
        else:
            super(BaseWidget, self).mousePressEvent(event)

    # =================================================================================================================
    # BASE
    # =================================================================================================================

    def get_main_layout(self):
        """
        Function that generates the main layout used by the widget
        Override if necessary on new widgets
        :return: QLayout
        """

        return layouts.VerticalLayout(spacing=2, margins=(2, 2, 2, 2))

    def ui(self):
        """
        Function that sets up the ui of the widget
        Override it on new widgets (but always call super)
        """

        self.main_layout = self.get_main_layout()
        if self._use_scrollbar:
            layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0))
            self.setLayout(layout)
            central_widget = QWidget()
            central_widget.setSizePolicy(
                QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
            scroll = QScrollArea()
            scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
            scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            scroll.setWidgetResizable(True)
            scroll.setFocusPolicy(Qt.NoFocus)
            layout.addWidget(scroll)
            scroll.setWidget(central_widget)
            central_widget.setLayout(self.main_layout)
            self.setSizePolicy(
                QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
        else:
            self.setLayout(self.main_layout)

    def setup_signals(self):
        """
        Function that set up signals of the widget
        """

        pass

    def set_spacing(self, value):
        """
        Set the spacing used by widget's main layout
        :param value: float
        """

        self.main_layout.setSpacing(value)

    def tiny(self):
        """
        Sets spin box to tiny size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.tiny if widget_theme else theme.Theme.Sizes.TINY

        return self

    def small(self):
        """
        Sets spin box to small size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.small if widget_theme else theme.Theme.Sizes.SMALL

        return self

    def medium(self):
        """
        Sets spin box to medium size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.medium if widget_theme else theme.Theme.Sizes.MEDIUM

        return self

    def large(self):
        """
        Sets spin box to large size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.large if widget_theme else theme.Theme.Sizes.LARGE

        return self

    def huge(self):
        """
        Sets spin box to huge size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.huge if widget_theme else theme.Theme.Sizes.HUGE

        return self
Beispiel #28
0
class ClickSaveFileToolButton(buttons.BaseToolButton, object):
    fileChanged = Signal(str)

    _on_browse_file = browse_file

    def __init__(self, multiple=False, parent=None):
        super(ClickSaveFileToolButton, self).__init__(parent=parent)

        self._path = None
        self._multiple = multiple
        self._filters = list()

        self.image('save')
        self.icon_only()
        self.setToolTip('Click to save file')
        self.clicked.connect(self._on_browse_file)

    # =================================================================================================================
    # PROPERTIES
    # =================================================================================================================

    def _get_filters(self):
        """
        Returns browse filters
        :return: list(str)
        """

        return self._filters

    def _set_filters(self, value):
        """
        Sets browse filters
        :param value: list(str)
        """

        self._filters = value

    def _get_path(self):
        """
        Returns last browse file path
        :return: str
        """

        return self._path

    def _set_path(self, value):
        """
        Sets browse start path
        :param value: str
        """

        self._path = value

    filters = Property(list, _get_filters, _set_filters)
    path = Property(str, _get_path, _set_path)

    # =================================================================================================================
    # BASE
    # =================================================================================================================

    def set_path(self, value):
        """
        Sets browse start path
        :param value: str
        """

        self.path = value
Beispiel #29
0
class SwitchWidget(QRadioButton, object):
    def __init__(self, parent=None):
        super(SwitchWidget, self).__init__(parent)

        self._size = self.theme_default_size()

        self.setAutoExclusive(False)

    # =================================================================================================================
    # PROPERTIES
    # =================================================================================================================

    def _get_size(self):
        """
        Returns switch size
        :return: float
        """

        return self._size

    def _set_size(self, value):
        """
        Sets switch size
        :param value: float
        """

        self._size = value
        self.style().polish(self)

    theme_size = Property(int, _get_size, _set_size)

    # =================================================================================================================
    # OVERRIDES
    # =================================================================================================================

    def minimumSizeHint(self):
        """
        Overrides base QRadioButton minimumSizeHint functino
        We do not need text space
        :return: QSize
        """

        height = self._size * 1.2
        return QSize(height, height / 2)

    # =================================================================================================================
    # BASE
    # =================================================================================================================

    def tiny(self):
        """
        Sets button to tiny size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.tiny if widget_theme else theme.Theme.Sizes.TINY

        return self

    def small(self):
        """
        Sets button to small size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.small if widget_theme else theme.Theme.Sizes.SMALL

        return self

    def medium(self):
        """
        Sets button to medium size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.medium if widget_theme else theme.Theme.Sizes.MEDIUM

        return self

    def large(self):
        """
        Sets button to large size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.large if widget_theme else theme.Theme.Sizes.LARGE

        return self

    def huge(self):
        """
        Sets button to large size
        """

        widget_theme = self.theme()
        self.theme_size = widget_theme.huge if widget_theme else theme.Theme.Sizes.HUGE

        return self
Beispiel #30
0
class Badge(base.BaseWidget, object):
    """
    Widget that can be located near notification or user avatars to display unread messages count
    We support 3 types of styles:
        1. dof: show a dot
        2. count: show a number
        3. text: show a string
    """
    def __init__(self, widget=None, parent=None):

        self._dot = None
        self._text = None
        self._count = None
        self._widget = widget
        self._overflow_count = 99

        super(Badge, self).__init__(parent=parent)

    # =================================================================================================================
    # PROPERTIES
    # =================================================================================================================

    @property
    def overflow(self):
        """
        Returns current overflow number
        :return: int
        """

        return self._overflow_count

    @overflow.setter
    def overflow(self, value):
        """
        Sets overflow number
        :param value: int
        """

        self._overflow_count = value
        self._update_number()

    @property
    def count(self):
        """
        Returns current badge count number
        :return: int
        """

        return self._count

    @count.setter
    def count(self, value):
        """
        Sets current badge count number
        :param value: int
        """

        self._count = value
        self._update_number()

    @property
    def text(self):
        """
        Returns current badge text
        :return: str
        """

        return self._text

    @text.setter
    def text(self, value):
        """
        Sets current badge text
        :param value: str
        """

        self._text = value
        self._badge_btn.setText(self._text)
        self._badge_btn.setVisible(bool(self._text))
        self._dot = None
        self.style().polish(self)

    def _get_dot(self):
        """
        Returns whether or not current badge style is dot
        :return: bool
        """

        return self._dot

    def _set_dot(self, flag):
        """
        Sets whether or not current badge style is dot
        :param flag: bool
        """

        self._dot = flag
        self._badge_btn.setText('')
        self._badge_btn.setVisible(flag)
        self.style().polish(self)

    dot = Property(bool, _get_dot, _set_dot)

    # =================================================================================================================
    # OVERRIDES
    # =================================================================================================================

    def get_main_layout(self):
        main_layout = layouts.GridLayout(margins=(0, 0, 0, 0))

        return main_layout

    def ui(self):
        super(Badge, self).ui()

        self._badge_btn = QPushButton()
        self._badge_btn.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)

        if self._widget is not None:
            self.main_layout.addWidget(self._widget, 0, 0)
        self.main_layout.addWidget(self._badge_btn, 0, 0,
                                   Qt.AlignTop | Qt.AlignRight)

    # =================================================================================================================
    # BASE
    # =================================================================================================================

    @classmethod
    def create_dot(cls, show=False, widget=None, parent=None):
        """
        Creates a new badge with dot style
        :param show: bool
        :param widget: QWidget
        :param parent: QWidget
        :return: Badge
        """

        inst = cls(widget=widget, parent=parent)
        inst.dot = show

        return inst

    @classmethod
    def create_count(cls, count=0, widget=None, parent=None):
        """
        Creates a new badge with count style
        :param count: int
        :param widget: QWidget
        :param parent: QWidget
        :return: Badge
        """

        inst = cls(widget=widget, parent=parent)
        inst.count = count

        return inst

    @classmethod
    def create_text(cls, text='', widget=None, parent=None):
        """
        Creates a new badge with dot style
        :param text: str
        :param widget: QWidget
        :param parent: QWidget
        :return: Badge
        """

        inst = cls(widget=widget, parent=parent)
        inst.text = text

        return inst

    # =================================================================================================================
    # INTERNAL
    # =================================================================================================================

    def _update_number(self):
        """
        Internal function that updates overflow number
        """

        self._badge_btn.setText(
            formatters.overflow_format(self._count, self._overflow_count))
        self._badge_btn.setVisible(self._count > 0)
        self._dot = None
        self.style().polish(self)