示例#1
0
    def overlay_text(
        self,
        message: str,
        color: int,
        size: int,
        x: int,
        y: int,
        timeout: int,
        font_name: str,
        centered: bool,
        shadow: bool,
    ):
        gfx = QGraphicsTextItem(message)
        gfx.setDefaultTextColor(decode_color(color))

        font = QFont(font_name, min(50, size))
        font.setStyleHint(QFont.SansSerif)
        gfx.setFont(font)

        if shadow:
            effect = QGraphicsDropShadowEffect(gfx)
            effect.setBlurRadius(0)
            effect.setColor(Qt.GlobalColor.black)
            effect.setOffset(1, 1)
            gfx.setGraphicsEffect(effect)

        if centered:
            # The provided x, y is at the center of the text
            bound = gfx.boundingRect()
            gfx.setPos(x - (bound.width() / 2), y - (bound.height() / 2))
        else:
            gfx.setPos(x, y)

        self._finalize_gfx(gfx, timeout)
示例#2
0
def css_button(qtwindow, button, color=None, shadow=True):
    ''' apply style to a button widget '''

    # default bg color is gray 80
    if color == 'red':
        bg_color = 'rgb(160, 20, 20)'  # red/white
        tx_color = 'rgb(255, 255, 255)'
    elif color == 'disabled':
        bg_color = 'rgb(80, 80, 80)'  # gray/gray
        tx_color = 'rgb(180, 180, 180)'
    elif color == 'blue':
        bg_color = 'rgb(46, 134, 193)'  # blue arcane/white
        tx_color = 'rgb(230, 230, 230)'
    else:
        bg_color = 'rgb(80, 80, 80)'  # gray
        tx_color = 'rgb(230, 230, 230)'

    css = "border-radius:3px;color:{};background:{};font-size:12px;font-family:Segoe UI;".format(
        tx_color, bg_color)
    button.setStyleSheet(css)

    if shadow:
        shadow = QGraphicsDropShadowEffect(qtwindow)
        shadow.setBlurRadius(6)
        shadow.setOffset(4)
        shadow.setColor(QColor(20, 20, 20, 200))
        button.setGraphicsEffect(shadow)
示例#3
0
文件: table.py 项目: wwmm/viewpca
    def card_shadow(self):
        effect = QGraphicsDropShadowEffect(self.main_widget)

        effect.setColor(QColor(0, 0, 0, 100))
        effect.setXOffset(2)
        effect.setYOffset(2)
        effect.setBlurRadius(5)

        return effect
示例#4
0
    def button_shadow(self):
        effect = QGraphicsDropShadowEffect(self.window)

        effect.setColor(QColor(0, 0, 0, 100))
        effect.setXOffset(1)
        effect.setYOffset(1)
        effect.setBlurRadius(5)

        return effect
示例#5
0
    def __init__(self, text: str, object_name: str = "blue"):
        super().__init__(text)

        self.setCursor(Qt.PointingHandCursor)

        if object_name:
            self.setObjectName(object_name)

        effect = QGraphicsDropShadowEffect(self)
        effect.setColor(QColor(0, 0, 0, 0.25 * 255))
        effect.setOffset(2, 4)
        effect.setBlurRadius(4)
        self.setGraphicsEffect(effect)
示例#6
0
 def add_window_drop_shadow() -> None:
     """Adds a drop-shadow behind the window"""
     if self.__use_shadow:
         self.layout().setMargin(self.__style.window.SHADOW_RADIUS_PX)
         drop_shadow_effect = QGraphicsDropShadowEffect(self)
         drop_shadow_effect.setEnabled(True)
         drop_shadow_effect.setBlurRadius(
             self.__style.window.SHADOW_RADIUS_PX)
         color = QColor(self.__style.window.SHADOW_COLOR_RGB)
         color.setAlpha(self.__style.window.SHADOW_OPACITY_HEX)
         drop_shadow_effect.setColor(color)
         drop_shadow_effect.setOffset(0)
         self.setGraphicsEffect(drop_shadow_effect)
示例#7
0
class MainWindow(QtWidgets.QMainWindow):
    role_value = None
    DATA_PATH = 'D:\\SASTRA\Code\\Dean Mam Project\\assignment\\Data\\Medical_Records.csv'

    def __init__(self):
        print('Main init')
        QtWidgets.QMainWindow.__init__(self)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.ui.stackedWidget.setCurrentIndex(0)
        for i in RBACClass.permission_dict['Administrator']:
            self.ui.tableWidget.setColumnHidden(i, True)

        ## ==> MAXIMIZE RESTORE FUNCTION
        def maximize_restore():
            global GLOBAL_STATE
            status = GLOBAL_STATE

            # IF NOT MAXIMIZED
            if status == 0:
                self.showMaximized()

                # SET GLOBAL TO 1
                GLOBAL_STATE = 1

                # IF MAXIMIZED REMOVE MARGINS AND BORDER RADIUS
                self.ui.drop_shadow_frame.setContentsMargins(0, 0, 0, 0)
                self.ui.drop_shadow_frame.setStyleSheet(
                    "background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1, stop:0 rgba(42, 44, 111, 255), stop:0.521368 rgba(28, 29, 73, 255)); border-radius: 0px;"
                )
                self.ui.maximize_btn.setToolTip("Restore")
            else:
                GLOBAL_STATE = 0
                self.showNormal()
                self.resize(self.width() + 1, self.height() + 1)
                self.ui.drop_shadow_frame.setContentsMargins(10, 10, 10, 10)
                self.ui.drop_shadow_frame.setStyleSheet(
                    "background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1, stop:0 rgba(42, 44, 111, 255), stop:0.521368 rgba(28, 29, 73, 255)); border-radius: 10px;"
                )
                self.ui.maximize_btn.setToolTip("Maximize")

        ## ==> UI DEFINITIONS
        def uiDefinitions():

            # REMOVE TITLE BAR
            self.setWindowFlag(QtCore.Qt.FramelessWindowHint)
            self.setAttribute(QtCore.Qt.WA_TranslucentBackground)

            # SET DROPSHADOW WINDOW
            self.shadow = QGraphicsDropShadowEffect(self)
            self.shadow.setBlurRadius(20)
            self.shadow.setXOffset(0)
            self.shadow.setYOffset(0)
            self.shadow.setColor(QColor(0, 0, 0, 100))

            # APPLY DROPSHADOW TO FRAME
            self.ui.drop_shadow_frame.setGraphicsEffect(self.shadow)

            # MAXIMIZE / RESTORE
            self.ui.maximize_btn.clicked.connect(lambda: maximize_restore())

            # MINIMIZE
            self.ui.minimize_btn.clicked.connect(lambda: self.showMinimized())

            # CLOSE
            self.ui.close_btn.clicked.connect(lambda: self.close())

            ## ==> CREATE SIZE GRIP TO RESIZE WINDOW
            self.sizegrip = QSizeGrip(self.ui.frame_grip)
            self.sizegrip.setStyleSheet(
                "QSizeGrip { width: 10px; height: 10px; margin: 5px } QSizeGrip:hover { background-color: rgb(50, 42, 94) }"
            )
            self.sizegrip.setToolTip("Resize Window")

        def rolePermission(role):
            for i in role:
                self.ui.tableWidget.setColumnHidden(i, False)

        # MOVE WINDOW
        def moveWindowMain(event):
            # RESTORE BEFORE MOVE
            if self.returnStatus() == 1:
                self.maximize_restore(self)

            # IF LEFT CLICK MOVE WINDOW
            if event.buttons() == Qt.LeftButton:
                self.move(self.pos() + event.globalPos() - self.dragPos)
                self.dragPos = event.globalPos()
                event.accept()

        def view_details():
            patient_id = self.ui.patientIdLineEdit_2.text()
            print(patient_id)
            self.ui.stackedWidget.setCurrentIndex(1)
            with open(MainWindow.DATA_PATH, 'r') as datafile:
                print('inside file')
                readData = csv.reader(datafile)
                rowValue = 0
                for row in readData:
                    print('inside row')
                    if row[1] == patient_id:
                        self.ui.tableWidget.insertRow(rowValue)
                        print(row[1], type(row[1]))
                        for i in MainWindow.role_value:
                            print('set')
                            self.ui.tableWidget.setItem(
                                rowValue, i, QTableWidgetItem(row[i]))
                        rowValue = rowValue + 1

        def go_back():
            self.ui.stackedWidget.setCurrentIndex(0)

        ## ==> SET UI DEFINITIONS
        uiDefinitions()

        print(MainWindow.role_value, "rolePerm")
        rolePermission(MainWindow.role_value)

        self.ui.viewDetailsPushButton_2.clicked.connect(view_details)
        self.ui.back_btn.clicked.connect(go_back)

        # SET TITLE BAR
        self.ui.title_bar_2.mouseMoveEvent = moveWindowMain

        ## SHOW ==> MAIN WINDOW
        ########################################################################
        self.show()

    ## RETURN STATUS IF WINDOWS IS MAXIMIZE OR RESTAURED
    def returnStatus(self):
        return GLOBAL_STATE

    ## APP EVENTS
    ########################################################################
    def mousePressEvent(self, event):
        self.dragPos = event.globalPos()
示例#8
0
class LoginWindow(QtWidgets.QWidget):
    def __init__(self):
        QtWidgets.QWidget.__init__(self)
        self.ui = Ui_Form()
        self.ui.setupUi(self)

        def uiDefinition():
            # REMOVE TITLE BAR
            self.shadow = QGraphicsDropShadowEffect(self)
            self.setWindowFlag(QtCore.Qt.FramelessWindowHint)
            self.setAttribute(QtCore.Qt.WA_TranslucentBackground)

            # SET DROPSHADOW WINDOW
            self.shadow.setBlurRadius(20)
            self.shadow.setXOffset(0)
            self.shadow.setYOffset(0)
            self.shadow.setColor(QColor(0, 0, 0, 100))

            # APPLY DROPSHADOW TO FRAME
            self.ui.login_frame.setGraphicsEffect(self.shadow)

            # CLOSE
            self.ui.closePushButton.clicked.connect(lambda: self.close())

        # MOVE WINDOW
        def moveWindowLogin(event):
            # IF LEFT CLICK MOVE WINDOW
            if event.buttons() == Qt.LeftButton():
                self.move(self.pos() + event.globalPos() - self.dragPos)
                self.dragPos = event.globalPos()
                event.accept()

        def show_new_window():
            print(self.ui.userNameLineEdit.text(),
                  self.ui.passwordLineEdit.text())
            r = RBACClass()
            MainWindow.role_value = r.logon(
                UserName=self.ui.userNameLineEdit.text(),
                Password=self.ui.passwordLineEdit.text())
            print(MainWindow.role_value, "show")
            if MainWindow.role_value != None:
                print('here')
                self.close()
                w = MainWindow()
                w.show()
            else:
                print('no')
                self.ui.userNameLineEdit.setPlaceholderText('Try Again')
                self.ui.passwordLineEdit.setPlaceholderText('Try Again')

        # SET TITLE BAR
        self.ui.login_frame.mouseMoveEvent = moveWindowLogin
        self.ui.loginPushButton.clicked.connect(show_new_window)

        ## ==> SET UI DEFINITIONS
        uiDefinition()

        ## SHOW ==> MAIN WINDOW
        ########################################################################
        self.show()

    ## APP EVENTS
    ########################################################################
    def mousePressEvent(self, event):
        self.dragPos = event.globalPos()
示例#9
0
class NodeInstance(QGraphicsItem):
    def __init__(self, params):
        super(NodeInstance, self).__init__()

        self.setFlags(QGraphicsItem.ItemIsSelectable
                      | QGraphicsItem.ItemIsMovable
                      | QGraphicsItem.ItemSendsScenePositionChanges)
        self.setAcceptHoverEvents(True)

        # GENERAL ATTRIBUTES

        # the constructor parameters are stored in a tuple to make the source code of custom NIs cleaner
        parent_node, flow, config = params

        self.parent_node = parent_node
        self.flow = flow
        self.movement_state = None
        self.movement_pos_from = None
        self.painted_once = False
        self.inputs = []
        self.outputs = []
        self.color = self.parent_node.color  # manipulated by self.animator
        # self.node_instance_painter = NodeInstancePainter(self)

        self.default_actions = {
            'remove': {
                'method': self.action_remove
            },
            'update shape': {
                'method': self.update_shape
            }
        }  # for context menus
        self.special_actions = {
        }  # only gets written in custom NodeInstance-subclasses
        self.personal_logs = []

        # 'initializing' will be set to False below. It's needed for the ports setup, to prevent shape updating stuff
        self.initializing = True

        self.temp_state_data = None
        self.init_config = config

        # UI
        self.shadow_effect = None
        self.width = -1
        self.height = -1

        self.title_label = TitleLabel(self)

        self.animator = NodeInstanceAnimator(self)  # needs self.title_label

        self.main_widget = None
        self.main_widget_proxy: FlowProxyWidget = None
        if self.parent_node.has_main_widget:
            self.main_widget = self.parent_node.main_widget_class(self)
            self.main_widget_proxy = FlowProxyWidget(self.flow)
            self.main_widget_proxy.setWidget(self.main_widget)

        # LOADING UI
        self.body_layout: QGraphicsLinearLayout = None
        self.inputs_layout: QGraphicsLinearLayout = None
        self.outputs_layout: QGraphicsLinearLayout = None
        self.layout: QGraphicsLinearLayout = self.setup_ui()
        self.widget = QGraphicsWidget(self)
        self.widget.setLayout(self.layout)

        # TOOLTIP
        if self.parent_node.description != '':
            self.setToolTip('<html><head/><body><p>' +
                            self.parent_node.description +
                            '</p></body></html>')
        self.setCursor(Qt.SizeAllCursor)

        # DESIGN THEME
        Design.flow_theme_changed.connect(self.theme_changed)

    def initialized(self):
        """All ports and the main widget get finally created here."""

        # LOADING CONFIG
        if self.init_config is not None:
            # self.setPos(config['position x'], config['position y'])
            self.setup_ports(self.init_config['inputs'],
                             self.init_config['outputs'])
            if self.main_widget:
                try:
                    self.main_widget.set_data(
                        self.init_config['main widget data'])
                except Exception as e:
                    print('Exception while setting data in',
                          self.parent_node.title,
                          'NodeInstance\'s main widget:', e,
                          ' (was this intended?)')

            self.special_actions = self.set_special_actions_data(
                self.init_config['special actions'])
            self.temp_state_data = self.init_config['state data']
        else:
            self.setup_ports()

        # LOADING DATA
        if self.temp_state_data is not None:
            try:
                self.set_data(self.temp_state_data)
            except Exception as e:
                print('Exception while setting data in',
                      self.parent_node.title, 'NodeInstance:', e,
                      ' (was this intended?)')

        self.initializing = False

        # No self.update_shape() here because for some reason, the bounding rect hasn't been initialized yet, so
        # self.update_shape() gets called when the item is being drawn the first time (see paint event in NI painter)
        # TODO: change that ^ once there is a solution for this: https://forum.qt.io/topic/117179/force-qgraphicsitem-to-update-immediately-wait-for-update-event

        self.update_design()  # load current design, update QGraphicsItem

        self.update()  # and finally update the NodeInstance once

    def setup_ui(self):
        """Creates the empty layouts for the NI's widget."""

        #   main layout
        layout = QGraphicsLinearLayout(Qt.Vertical)
        layout.setSpacing(10)

        if self.parent_node.design_style == 'extended':
            layout.addItem(self.title_label)
            layout.setAlignment(self.title_label, Qt.AlignTop)

        #   inputs
        self.inputs_layout = QGraphicsLinearLayout(Qt.Vertical)
        self.inputs_layout.setSpacing(2)

        #   outputs
        self.outputs_layout = QGraphicsLinearLayout(Qt.Vertical)
        self.outputs_layout.setSpacing(2)

        #   body
        self.body_layout = QGraphicsLinearLayout(Qt.Horizontal)

        self.body_layout.setSpacing(4)
        self.body_layout.addItem(self.inputs_layout)
        self.body_layout.setAlignment(self.inputs_layout,
                                      Qt.AlignVCenter | Qt.AlignLeft)
        self.body_layout.addStretch()
        self.body_layout.addItem(self.outputs_layout)
        self.body_layout.setAlignment(self.outputs_layout,
                                      Qt.AlignVCenter | Qt.AlignRight)

        if self.main_widget is not None:
            if self.parent_node.main_widget_pos == 'between ports':
                self.body_layout.insertItem(1, self.main_widget_proxy)
                self.body_layout.insertStretch(2)
                layout.addItem(self.body_layout)

            elif self.parent_node.main_widget_pos == 'under ports':
                layout.addItem(self.body_layout)
                layout.addItem(self.main_widget_proxy)
                layout.setAlignment(self.main_widget_proxy, Qt.AlignHCenter)
        else:
            layout.addItem(self.body_layout)

        return layout

    def rebuild_ui(self):
        """Due to some really strange and annoying behaviour of these QGraphicsWidgets, they don't want to shrink
        automatically when content is removed, they just stay large, even with a Minimum SizePolicy. I didn't find a
        way around that yet, so for now I have to recreate the whole layout and make sure the widget uses the smallest
        size possible."""

        # if I don't manually remove the ports from the layouts,
        # they get deleted when setting the widget's layout to None below
        for inp in self.inputs:
            self.inputs_layout.removeAt(0)
        for out in self.outputs:
            self.outputs_layout.removeAt(0)

        self.layout = self.setup_ui()  # recreate layout

        # forcefully making the widget shrink
        self.widget.setLayout(None)
        self.widget.resize(self.widget.minimumSize())

        self.widget.setLayout(self.layout)

        # add inputs to new layout
        for inp in self.inputs:
            self.add_input_to_layout(inp)
        for out in self.outputs:
            self.add_output_to_layout(out)

    #                        __                             _    __     __
    #              ____ _   / /  ____ _   ____     _____   (_)  / /_   / /_     ____ ___
    #             / __ `/  / /  / __ `/  / __ \   / ___/  / /  / __/  / __ \   / __ `__ \
    #            / /_/ /  / /  / /_/ /  / /_/ /  / /     / /  / /_   / / / /  / / / / / /
    #            \__,_/  /_/   \__, /   \____/  /_/     /_/   \__/  /_/ /_/  /_/ /_/ /_/
    #                         /____/

    def update(self, input_called=-1, output_called=-1):
        """This is the method used to activate a NodeInstance. Note that this signature shadows the update() method from
        QGraphicsItem used to graphically update a QGraphicsItem which can be accessed via
        QGraphicsItem.update(self)."""

        if Design.animations_enabled:
            self.animator.start()

        Debugger.debug('update in', self.parent_node.title, 'on input',
                       input_called)
        try:
            self.update_event(input_called)
        except Exception as e:
            Debugger.debug('EXCEPTION IN', self.parent_node.title, 'NI:', e)

    def update_event(self, input_called=-1):
        """Gets called when an input received a signal. This is where the magic begins in subclasses."""

        pass

    def input(self, index):
        """Returns the value of a data input.
        If the input is connected, the value of the connected output is used:
        If not, the value of the widget is used."""

        Debugger.debug('input called in', self.parent_node.title, 'NI:', index)
        return self.inputs[index].get_val()

    def exec_output(self, index):
        """Executes an execution output, sending a signal to all connected execution inputs causing the connected
        NIs to update."""
        self.outputs[index].exec()

    def set_output_val(self, index, val):
        """Sets the value of a data output.
        self.data_outputs_updated() has to be called manually after all values are set."""

        if not self.flow.viewport_update_mode.sync:  # asynchronous viewport updates
            vp = self.flow.viewport()
            vp.repaint(self.flow.mapFromScene(self.sceneBoundingRect()))

        self.outputs[index].set_val(val)

    def data_outputs_updated(self):
        """(outdated!) Sends update signals to all data outputs causing connected NIs to update."""

        Debugger.debug('updating data outputs in', self.parent_node.title)
        for o in self.outputs:
            if o.type_ == 'data':
                o.updated_val()
        Debugger.debug('data outputs in', self.parent_node.title, 'updated')

    def remove_event(self):
        """Method to stop all threads in hold of the NI itself."""

        pass

    #                                 _
    #              ____ _   ____     (_)
    #             / __ `/  / __ \   / /
    #            / /_/ /  / /_/ /  / /
    #            \__,_/  / .___/  /_/
    #                   /_/
    #
    # all algorithm-unrelated api methods:

    #   LOGGING
    def new_log(self, title):
        """Requesting a new personal Log. Handy method for subclasses."""
        new_log = self.flow.parent_script.logger.new_log(self, title)
        self.personal_logs.append(new_log)
        return new_log

    def disable_personal_logs(self):
        """Disables personal Logs. They remain visible unless the user closes them via the appearing button."""
        for log in self.personal_logs:
            log.disable()

    def enable_personal_logs(self):
        """Resets personal Logs to normal state (hiding close button, changing style sheet)."""
        for log in self.personal_logs:
            log.enable()

    def log_message(self, message: str, target='global'):
        """Access to global_tools Script Logs ('global' or 'error')."""
        self.flow.parent_script.logger.log_message(message, target)

    # SHAPE
    def update_shape(self):
        """Causes recompilation of the whole shape."""
        # if not self.initializing:   # just to make sure
        #     self.rebuild_ui()       # (hopefully) temporary fix -> see rebuild_ui() docstring

        if self.main_widget is not None:  # maybe the main_widget got resized
            self.main_widget_proxy.setMaximumSize(self.main_widget.size())
            self.widget.adjustSize()
            self.widget.adjustSize()

        self.body_layout.invalidate()
        self.layout.invalidate()
        self.layout.activate()
        # very essential; repositions everything in case content has changed (inputs/outputs/widget)

        if self.parent_node.design_style == 'minimalistic':

            # making it recompute its true minimumWidth here
            self.widget.adjustSize()

            if self.layout.minimumWidth() < self.title_label.width + 15:
                self.layout.setMinimumWidth(self.title_label.width + 15)
                self.layout.activate()

        self.width = self.boundingRect().width()
        self.height = self.boundingRect().height()
        rect = QRectF(QPointF(-self.width / 2, -self.height / 2),
                      QPointF(self.width / 2, self.height / 2))
        self.widget.setPos(rect.left(), rect.top())

        if not self.parent_node.design_style == 'extended':
            self.title_label.setPos(
                QPointF(-self.title_label.boundingRect().width() / 2,
                        -self.title_label.boundingRect().height() / 2))

        self.flow.viewport().update()

    # PORTS
    def create_new_input(self,
                         type_,
                         label,
                         widget_name=None,
                         widget_pos='under',
                         pos=-1,
                         config=None):
        """Creates and adds a new input. Handy for subclasses."""
        Debugger.debug('create_new_input called')
        pi = InputPortInstance(self,
                               type_,
                               label,
                               config_data=config,
                               widget_name=widget_name,
                               widget_pos=widget_pos)
        if pos < -1:
            pos += len(self.inputs)
        if pos == -1:
            self.inputs.append(pi)
            self.add_input_to_layout(pi)
        else:
            self.inputs.insert(pos, pi)
            self.insert_input_into_layout(pos, pi)

        if not self.initializing:
            self.update_shape()
            self.update()

    def add_input_to_layout(self, i):
        if self.inputs_layout.count() > 0:
            self.inputs_layout.addStretch()
        self.inputs_layout.addItem(i)
        self.inputs_layout.setAlignment(i, Qt.AlignLeft)

    def insert_input_into_layout(self, index, i):
        self.inputs_layout.insertItem(index * 2,
                                      i)  # *2 because of the stretches
        self.inputs_layout.setAlignment(i, Qt.AlignLeft)
        if len(self.inputs) > 1:
            self.inputs_layout.insertStretch(
                index * 2 + 1)  # *2+1 because of the stretches, too

    def delete_input(self, i):
        """Disconnects and removes input. Handy for subclasses."""
        inp: InputPortInstance = None
        if type(i) == int:
            inp = self.inputs[i]
        elif type(i) == InputPortInstance:
            inp = i

        for cpi in inp.connected_port_instances:
            self.flow.connect_gates(inp.gate, cpi.gate)

        # for some reason, I have to remove all widget items manually from the scene too. setting the items to
        # ownedByLayout(True) does not work, I don't know why.
        self.scene().removeItem(inp.gate)
        self.scene().removeItem(inp.label)
        if inp.proxy is not None:
            self.scene().removeItem(inp.proxy)

        self.inputs_layout.removeItem(inp)
        self.inputs.remove(inp)

        # just a temporary workaround for the issues discussed here:
        # https://forum.qt.io/topic/116268/qgraphicslayout-not-properly-resizing-to-change-of-content
        self.rebuild_ui()

        if not self.initializing:
            self.update_shape()
            self.update()

    def create_new_output(self, type_, label, pos=-1):
        """Creates and adds a new output. Handy for subclasses."""

        pi = OutputPortInstance(self, type_, label)
        if pos < -1:
            pos += len(self.outputs)
        if pos == -1:
            self.outputs.append(pi)
            self.add_output_to_layout(pi)
        else:
            self.outputs.insert(pos, pi)
            self.insert_output_into_layout(pos, pi)

        if not self.initializing:
            self.update_shape()
            self.update()

    def add_output_to_layout(self, o):
        if self.outputs_layout.count() > 0:
            self.outputs_layout.addStretch()
        self.outputs_layout.addItem(o)
        self.outputs_layout.setAlignment(o, Qt.AlignRight)

    def insert_output_into_layout(self, index, o):
        self.outputs_layout.insertItem(index * 2,
                                       o)  # *2 because of the stretches
        self.outputs_layout.setAlignment(o, Qt.AlignRight)
        if len(self.outputs) > 1:
            self.outputs_layout.insertStretch(
                index * 2 + 1)  # *2+1 because of the stretches, too

    def delete_output(self, o):
        """Disconnects and removes output. Handy for subclasses."""
        out: OutputPortInstance = None
        if type(o) == int:
            out = self.outputs[o]
        elif type(o) == OutputPortInstance:
            out = o

        for cpi in out.connected_port_instances:
            self.flow.connect_gates(out.gate, cpi.gate)

        # see delete_input() for info!
        self.scene().removeItem(out.gate)
        self.scene().removeItem(out.label)

        self.outputs_layout.removeItem(out)
        self.outputs.remove(out)

        # just a temporary workaround for the issues discussed here:
        # https://forum.qt.io/topic/116268/qgraphicslayout-not-properly-resizing-to-change-of-content
        self.rebuild_ui()

        if not self.initializing:
            self.update_shape()
            self.update()

    # GET, SET DATA
    def get_data(self):
        """
        This method gets subclassed and specified. If the NI has states (so, the behavior depends on certain values),
        all these values must be stored in JSON-able format in a dict here. This dictionary will be used to reload the
        node's state when loading a project or pasting copied/cut nodes in the Flow (the states get copied too), see
        self.set_data(self, data) below.
        Unfortunately, I can't use pickle or something like that due to PySide2 which runs on C++, not Python.
        :return: Dictionary representing all values necessary to determine the NI's current state
        """
        return {}

    def set_data(self, data):
        """
        If the NI has states, it's state should get reloaded here according to what was previously provided by the same
        class in get_data(), see above.
        :param data: Dictionary representing all values necessary to determine the NI's current state
        """
        pass

    @staticmethod
    def get_default_stylesheet():
        """Handy method for subclasses to access the application window's stylesheet for UI content."""
        return Design.ryven_stylesheet

    # VARIABLES

    def get_vars_handler(self):
        return self.flow.parent_script.variables_handler

    def get_var_val(self, name):
        return self.get_vars_handler().get_var_val(name)

    def set_var_val(self, name, val):
        return self.get_vars_handler().set_var(name, val)

    def register_var_receiver(self, name, method):
        self.get_vars_handler().register_receiver(self, name, method)

    def unregister_var_receiver(self, name):
        self.get_vars_handler().unregister_receiver(self, name)

    # --------------------------------------------------------------------------------------
    # UI STUFF ----------------------------------------

    def theme_changed(self, new_theme):
        self.title_label.theme_changed(new_theme)
        self.update_design()

    def update_design(self):
        """Loads the shadow effect option and causes redraw with active theme."""

        if Design.node_instance_shadows_shown:
            self.shadow_effect = QGraphicsDropShadowEffect()
            self.shadow_effect.setXOffset(12)
            self.shadow_effect.setYOffset(12)
            self.shadow_effect.setBlurRadius(20)
            self.shadow_effect.setColor(QColor('#2b2b2b'))
            self.setGraphicsEffect(self.shadow_effect)
        else:
            self.setGraphicsEffect(None)

        self.title_label.update_design()
        # print(self.title_label.color)
        self.animator.reload_values()

        QGraphicsItem.update(self)

    def boundingRect(self):
        # remember: (0, 0) shall be the NI's center!
        rect = QRectF()
        w = self.layout.geometry().width()
        h = self.layout.geometry().height()
        rect.setLeft(-w / 2)
        rect.setTop(-h / 2)
        rect.setWidth(w)
        rect.setHeight(h)
        return rect

    #   PAINTING
    def paint(self, painter, option, widget=None):
        """All painting is done by NodeInstancePainter"""

        # in order to access a meaningful geometry of GraphicsWidget contents in update_shape(), the paint event
        # has to be called once. See here:
        # https://forum.qt.io/topic/117179/force-qgraphicsitem-to-update-immediately-wait-for-update-event/4
        if not self.painted_once:
            self.title_label.update_design()  # also necessary
            self.update_shape()

        # self.node_instance_painter.paint(painter, option, self.color, self.width, self.height, self.boundingRect(),
        #                                  Design.flow_theme, widget)
        Design.flow_theme.node_inst_painter.paint_NI(
            design_style=self.parent_node.design_style,
            painter=painter,
            option=option,
            c=self.color,
            w=self.width,
            h=self.height,
            bounding_rect=self.boundingRect(),
            title_rect=self.title_label.boundingRect())

        self.painted_once = True

    def get_context_menu(self):
        menu = QMenu(self.flow)

        for a in self.get_actions(self.get_extended_default_actions(),
                                  menu):  # menu needed for 'parent'
            if type(a) == NodeInstanceAction:
                menu.addAction(a)
            elif type(a) == QMenu:
                menu.addMenu(a)

        menu.addSeparator()

        actions = self.get_actions(self.special_actions, menu)
        for a in actions:  # menu needed for 'parent'
            if type(a) == NodeInstanceAction:
                menu.addAction(a)
            elif type(a) == QMenu:
                menu.addMenu(a)

        return menu

    def itemChange(self, change, value):
        """This method ensures that all connections, selection borders etc. that get drawn in the Flow are constantly
        redrawn during a NI drag. Should get disabled when running in performance mode - not implemented yet."""

        if change == QGraphicsItem.ItemPositionChange:
            if Design.performance_mode == 'pretty':
                self.flow.viewport().update()
            if self.movement_state == MovementEnum.mouse_clicked:
                self.movement_state = MovementEnum.position_changed

        return QGraphicsItem.itemChange(self, change, value)

    def hoverEnterEvent(self, event):
        self.title_label.set_NI_hover_state(hovering=True)
        QGraphicsItem.hoverEnterEvent(self, event)

    def hoverLeaveEvent(self, event):
        self.title_label.set_NI_hover_state(hovering=False)
        QGraphicsItem.hoverLeaveEvent(self, event)

    def mousePressEvent(self, event):
        """Used for Moving-Commands in Flow - may be replaced later with a nicer determination of a moving action."""
        self.movement_state = MovementEnum.mouse_clicked
        self.movement_pos_from = self.pos()
        return QGraphicsItem.mousePressEvent(self, event)

    def mouseReleaseEvent(self, event):
        """Used for Moving-Commands in Flow - may be replaced later with a nicer determination of a moving action."""
        if self.movement_state == MovementEnum.position_changed:
            self.flow.selected_components_moved(self.pos() -
                                                self.movement_pos_from)
        self.movement_state = None
        return QGraphicsItem.mouseReleaseEvent(self, event)

    # ACTIONS
    def get_extended_default_actions(self):
        actions_dict = self.default_actions.copy()
        for index in range(len(self.inputs)):
            inp = self.inputs[index]
            if inp.type_ == 'exec':
                actions_dict['exec input ' + str(index)] = {
                    'method': self.action_exec_input,
                    'data': {
                        'input index': index
                    }
                }
        return actions_dict

    def action_exec_input(self, data):
        self.update(data['input index'])

    def get_actions(self, actions_dict, menu):
        actions = []

        for k in actions_dict:
            v_dict = actions_dict[k]
            try:
                method = v_dict['method']
                data = None
                try:
                    data = v_dict['data']
                except KeyError:
                    pass
                action = NodeInstanceAction(k, menu, data)
                action.triggered_with_data.connect(
                    method)  # see NodeInstanceAction for explanation
                action.triggered_without_data.connect(
                    method)  # see NodeInstanceAction for explanation
                actions.append(action)
            except KeyError:
                action_menu = QMenu(k, menu)
                sub_actions = self.get_actions(v_dict, action_menu)
                for a in sub_actions:
                    action_menu.addAction(a)
                actions.append(action_menu)

        return actions

    def action_remove(self):
        self.flow.remove_node_instance_triggered(self)

    def get_special_actions_data(self, actions):
        cleaned_actions = actions.copy()
        for key in cleaned_actions:
            v = cleaned_actions[key]
            if type(v) == M:  # callable(v):
                cleaned_actions[key] = v.method_name
            elif callable(v):
                cleaned_actions[key] = v.__name__
            elif type(v) == dict:
                cleaned_actions[key] = self.get_special_actions_data(v)
            else:
                cleaned_actions[key] = v
        return cleaned_actions

    def set_special_actions_data(self, actions_data):
        actions = {}
        for key in actions_data:
            if type(actions_data[key]) != dict:
                if key == 'method':
                    try:
                        actions['method'] = M(getattr(self, actions_data[key]))
                    except AttributeError:  # outdated method referenced
                        pass
                elif key == 'data':
                    actions['data'] = actions_data[key]
            else:
                actions[key] = self.set_special_actions_data(actions_data[key])
        return actions

    # PORTS
    def setup_ports(self, inputs_config=None, outputs_config=None):
        if not inputs_config and not outputs_config:
            for i in range(len(self.parent_node.inputs)):
                inp = self.parent_node.inputs[i]
                self.create_new_input(
                    inp.type_,
                    inp.label,
                    widget_name=self.parent_node.inputs[i].widget_name,
                    widget_pos=self.parent_node.inputs[i].widget_pos)

            for o in range(len(self.parent_node.outputs)):
                out = self.parent_node.outputs[o]
                self.create_new_output(out.type_, out.label)
        else:  # when loading saved NIs, the port instances might not be synchronised to the parent's ports anymore
            for inp in inputs_config:
                has_widget = inp['has widget']

                self.create_new_input(
                    inp['type'],
                    inp['label'],
                    widget_name=inp['widget name'] if has_widget else None,
                    widget_pos=inp['widget position'] if has_widget else None,
                    config=inp['widget data'] if has_widget else None)

            for out in outputs_config:
                self.create_new_output(out['type'], out['label'])

    def get_input_widget_class(self, widget_name):
        """Returns a reference to the widget class of a given name for instantiation."""
        custom_node_input_widget_classes = self.flow.parent_script.main_window.custom_node_input_widget_classes
        widget_class = custom_node_input_widget_classes[
            self.parent_node][widget_name]
        return widget_class

    def add_input_to_scene(self, i):
        self.flow.scene().addItem(i.gate)
        self.flow.scene().addItem(i.label)
        if i.widget:
            self.flow.scene().addItem(i.proxy)

    def del_and_remove_input_from_scene(self, i_index):
        i = self.inputs[i_index]
        for p in self.inputs[i_index].connected_port_instances:
            self.flow.connect_gates(i.gate, p.gate)

        self.flow.scene().removeItem(i.gate)
        self.flow.scene().removeItem(i.label)
        if i.widget:
            self.flow.scene().removeItem(i.proxy)
            i.widget.remove_event()
        self.inputs.remove(i)

    def add_output_to_scene(self, o):
        self.flow.scene().addItem(o.gate)
        self.flow.scene().addItem(o.label)

    def del_and_remove_output_from_scene(self, o_index):
        o = self.outputs[o_index]
        for p in self.outputs[o_index].connected_port_instances:
            self.flow.connect_gates(o.gate, p.gate)

        self.flow.scene().removeItem(o.gate)
        self.flow.scene().removeItem(o.label)
        self.outputs.remove(o)

    # GENERAL
    def about_to_remove_from_scene(self):
        """Called from Flow when the NI gets removed from the scene
        to stop all running threads and disable personal logs."""

        if self.main_widget:
            self.main_widget.remove_event()
        self.remove_event()

        self.disable_personal_logs()

    def is_active(self):
        for i in self.inputs:
            if i.type_ == 'exec':
                return True
        for o in self.outputs:
            if o.type_ == 'exec':
                return True
        return False

    def has_main_widget(self):
        """Might be used later in CodePreview_Widget to enable not only showing the NI's class but also it's
        main_widget's class."""
        return self.main_widget is not None

    def get_input_widgets(self):
        """Might be used later in CodePreview_Widget to enable not only showing the NI's class but its input widgets'
        classes."""
        input_widgets = []
        for i in range(len(self.inputs)):
            inp = self.inputs[i]
            if inp.widget is not None:
                input_widgets.append({i: inp.widget})
        return input_widgets

    def get_json_data(self):
        """Returns all metadata of the NI including position, package etc. in a JSON-able dict format.
        Used to rebuild the Flow when loading a project."""

        # general attributes
        node_instance_dict = {
            'parent node title': self.parent_node.title,
            'parent node type': self.parent_node.type_,
            'parent node package': self.parent_node.package,
            'parent node description': self.parent_node.description,
            'position x': self.pos().x(),
            'position y': self.pos().y()
        }
        if self.main_widget:
            node_instance_dict['main widget data'] = self.main_widget.get_data(
            )

        node_instance_dict['state data'] = self.get_data()
        node_instance_dict['special actions'] = self.get_special_actions_data(
            self.special_actions)

        # inputs
        node_instance_inputs_list = []
        for i in self.inputs:
            input_dict = i.get_json_data()
            node_instance_inputs_list.append(input_dict)
        node_instance_dict['inputs'] = node_instance_inputs_list

        # outputs
        node_instance_outputs_list = []
        for o in self.outputs:
            output_dict = o.get_json_data()
            node_instance_outputs_list.append(output_dict)
        node_instance_dict['outputs'] = node_instance_outputs_list

        return node_instance_dict
示例#10
0
def set_shadow(QtWindow, button):
    shadow = QGraphicsDropShadowEffect(QtWindow)
    shadow.setBlurRadius(6)
    shadow.setOffset(3)
    shadow.setColor(QColor(0, 0, 0, 160))
    button.setGraphicsEffect(shadow)
class UIFunctions(MainWindow):

    ## ==> MAXIMIZE RESTORE FUNCTION
    def maximize_restore(self):
        global GLOBAL_STATE
        status = GLOBAL_STATE

        # IF NOT MAXIMIZED
        if status == 0:
            self.showMaximized()

            # SET GLOBAL TO 1
            GLOBAL_STATE = 1

            # IF MAXIMIZED REMOVE MARGINS AND BORDER RADIUS
            self.ui.drop_shadow_layout.setContentsMargins(0, 0, 0, 0)
            self.ui.drop_shadow_frame.setStyleSheet(
                "background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1, stop:0 rgba(42, 44, 111, 255), stop:0.521368 rgba(28, 29, 73, 255)); border-radius: 0px;")
            self.ui.maximize_btn.setToolTip("Restore")
        else:
            GLOBAL_STATE = 0
            self.showNormal()
            self.resize(self.width() + 1, self.height() + 1)
            self.ui.drop_shadow_layout.setContentsMargins(10, 10, 10, 10)
            self.ui.drop_shadow_frame.setStyleSheet(
                "background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1, stop:0 rgba(42, 44, 111, 255), stop:0.521368 rgba(28, 29, 73, 255)); border-radius: 10px;")
            self.ui.maximize_btn.setToolTip("Maximize")

    ## ==> UI DEFINITIONS
    def uiDefinitions(self):

        # REMOVE TITLE BAR
        self.setWindowFlag(QtCore.Qt.FramelessWindowHint)
        self.setAttribute(QtCore.Qt.WA_TranslucentBackground)

        # SET DROPSHADOW WINDOW
        self.shadow = QGraphicsDropShadowEffect(self)
        self.shadow.setBlurRadius(20)
        self.shadow.setXOffset(0)
        self.shadow.setYOffset(0)
        self.shadow.setColor(QColor(0, 0, 0, 100))

        # APPLY DROPSHADOW TO FRAME
        self.ui.drop_shadow_frame.setGraphicsEffect(self.shadow)

        # MAXIMIZE / RESTORE
        self.ui.maximize_btn.clicked.connect(lambda: UIFunctions.maximize_restore(self))

        # MINIMIZE
        self.ui.minimize_btn.clicked.connect(lambda: self.showMinimized())

        # CLOSE
        self.ui.close_btn.clicked.connect(lambda: self.close())

        ## ==> CREATE SIZE GRIP TO RESIZE WINDOW
        self.sizegrip = QSizeGrip(self.ui.frame_grip)
        self.sizegrip.setStyleSheet(
            "QSizeGrip { width: 10px; height: 10px; margin: 5px } QSizeGrip:hover { background-color: rgb(50, 42, 94) }")
        self.sizegrip.setToolTip("Resize Window")

    ## RETURN STATUS IF WINDOWS IS MAXIMIZE OR RESTAURED
    def returnStatus(self):
        return GLOBAL_STATE
示例#12
0
class Central(QFrame):
    '''Initializes, styles, and connects the various classes'''

    def __init__(self):
        super().__init__()
        # Objects
        self.overallLayout = QVBoxLayout(self)
        self.contentLayout = QHBoxLayout()
        self.dropShadow = QGraphicsDropShadowEffect(self)
        self.boxManager = BoxManager.BoxManager()
        self.topBar = TopBar.TopBar()
        self.selectorArea = QFrame()
        self.selectorLayout = QVBoxLayout(self.selectorArea)
        self.folderArea = QFrame()
        self.folderLayout = QHBoxLayout(self.folderArea)
        self.folderList = FolderList.FolderList()
        self.folderBar = ScrollBar.ScrollBar(self.folderList)
        self.canvas = Canvas.Canvas(self.boxManager)
        self.imageArea = QFrame()
        self.imageList = ImageList.ImageList()
        self.imageLayout = QHBoxLayout(self.imageArea)
        self.imageBar = ScrollBar.ScrollBar(self.imageList)

        # Styling
        self.setStyleSheet('Central { background: transparent; }')
        self.overallLayout.setMargin(20)
        self.overallLayout.setSpacing(0)
        self.dropShadow.setOffset(QPointF(0,4))
        self.dropShadow.setColor(QColor(0,0,0,100))
        self.dropShadow.setBlurRadius(10)
        self.setGraphicsEffect(self.dropShadow)
        self.contentLayout.setAlignment(Qt.AlignCenter)
        self.contentLayout.setMargin(0)        
        self.contentLayout.setSpacing(0)
        self.selectorLayout.setMargin(0)
        self.selectorLayout.setSpacing(0)
        self.folderLayout.setMargin(0)
        self.folderLayout.setSpacing(0)
        self.imageLayout.setMargin(0)
        self.imageLayout.setSpacing(0)
        self.folderList.setVerticalScrollBar(self.folderBar)
        self.imageList.setVerticalScrollBar(self.imageBar)
        self.selectorArea.setMaximumWidth(400)
        self.selectorArea.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        # Layout
        self.folderLayout.addWidget(self.folderList)
        self.folderLayout.addSpacerItem(QSpacerItem(-7, 0))
        self.folderLayout.addWidget(self.folderBar)
        self.imageLayout.addWidget(self.imageList)
        self.imageLayout.addSpacerItem(QSpacerItem(-7, 0))
        self.imageLayout.addWidget(self.imageBar)
        self.selectorLayout.addWidget(self.folderArea, 15)
        self.selectorLayout.addWidget(self.imageArea, 85)
        self.contentLayout.addWidget(self.selectorArea, 30)
        self.contentLayout.addWidget(self.canvas, 70)
        self.overallLayout.addLayout(self.contentLayout)
        self.overallLayout.insertWidget(0, self.topBar)

        # Connections
        self.folderList.selectedFolderChanged.connect(self.handleSelectedFolderChanged)
        self.imageList.selectedImageChanged.connect(self.handleSelectedImageChanged)

    def handleSelectedFolderChanged(self, folder):
        self.imageList.populate(folder)
        self.canvas.changeImage(None)
        self.canvas.setMessage('Switching Folders - {}'.format(folder.data(role=Qt.DisplayRole)))
        self.topBar.setSelectedFolder(str(folder.data(role=Qt.UserRole+1)))
        self.topBar.setSelectedImage('')

    def handleSelectedImageChanged(self, image):
        self.canvas.changeImage(image)
        self.canvas.setMessage('Switching Images - {}'.format(image.data(role=Qt.DisplayRole)))
        self.topBar.setSelectedImage(str(image.data(role=Qt.DisplayRole)))
示例#13
0
class StyleWindow(QWidget):
    def __init__(self, parent=None):
        """
        基于QWidget的窗口,作为一些窗口的基类,提供一些基础的属性
        background_color_: 背景色, 目前只支持单色
        border_width_: 边框宽度, 为0表示没有边框
        border_color_:
        opacity_: 窗口透明度
        shadow_on_: 是否开启阴影
        shadow_color_:
        shadow_blur_: 阴影半径
        border_radius_: 四个角的弧度
        分别设置4个角的radius
        left_top_border_radius:
        left_bottom_border_radius_:
        right_top_border_radius_:
        right_bottom_border_radius_
        """
        QWidget.__init__(self, parent)

        self._border_width = 0
        self._border_indicator_width = 5
        self._mouse_left_button_pressed = False
        self._rt_pre_geometry = self.geometry()
        self._mouse_move_pre_pos = QPoint()
        self._background_color = Qt.white
        self._border_color = Qt.transparent
        self._border_width = 0
        self._border_radius = 0
        self._opacity = 1.0
        self._shadow = None
        self._main_v_layout_ = None
        self._cursor_calc_type = MousePosition.CenterPos
        # border
        self._left_top_border_radius = 0
        self._left_bottom_border_radius = 0
        self._right_top_border_radius = 0
        self._right_bottom_border_radius = 0
        self._enabled_drag = False
        self.setMouseTracking(True)
        # self.setAttribute(Qt.WA_TranslucentBackground)
        self._init_default_layout()

    def paint_style_widget(self, painter):
        style_opt = QStyleOption()
        style_opt.init(self)
        self.style().drawPrimitive(QStyle.PE_Widget, style_opt, painter)

    def paintEvent(self, event):
        """ paintEvent(self, event:PySide2.QtGui.QPaintEvent) """
        painter = QPainter(self)
        self.paint_border_background(painter)

    def border_width(self):
        return self._border_width

    def set_border_width(self, width):
        self._border_width = parse_pix_width(width)
        if self._main_v_layout_ is not None:
            self._main_v_layout_.setContentsMargins(self._border_width,
                                                    self._border_width,
                                                    self._border_width,
                                                    self._border_width)

    border_width_ = Property(str, fget=border_width, fset=set_border_width)

    def border_indicator_width(self):
        return self._border_indicator_width

    def set_border_indicator_width(self, width):
        self._border_indicator_width = parse_pix_width(width)

    border_indicator_width_ = Property(str,
                                       fget=border_indicator_width,
                                       fset=set_border_indicator_width)

    def mouseMoveEvent(self, event):
        """ mouseMoveEvent(self, event:PySide2.QtGui.QMouseEvent) """
        if self._enabled_drag:
            if self.isMaximized() is not True and self.isFullScreen(
            ) is not True:
                self._set_cursor_shape(
                    self._calc_cursor_pos(event.pos(),
                                          self._calc_cursor_col(event.pos())))
            if self._mouse_left_button_pressed and MousePosition.CenterPos != self._cursor_calc_type:
                self._drag_resize()
        super(StyleWindow, self).mouseMoveEvent(event)

    def _set_cursor_shape(self, pos):
        """setCurSorShape(self, pos:StyleWindow.MousePosition)"""
        cursor_shape = Qt.ArrowCursor
        if MousePosition.TopLeftPos == pos or MousePosition.BottomRightPos == pos:
            cursor_shape = Qt.SizeFDiagCursor
        elif MousePosition.LeftPos == pos or MousePosition.RightPos == pos:
            cursor_shape = Qt.SizeHorCursor
        elif MousePosition.BottomLeftPos == pos or MousePosition.TopRightPos == pos:
            cursor_shape = Qt.SizeBDiagCursor
        elif MousePosition.BottomPos == pos or MousePosition.TopPos == pos:
            cursor_shape = Qt.SizeVerCursor
        self.setCursor(cursor_shape)

    def _calc_cursor_col(self, pt):
        """calCursorCol(self, pt:PySide2.QtCore.QPoint)"""
        res = 3
        x = pt.x()
        if x < self.border_indicator_width():
            res = 1
        elif x < self.width() - self.border_indicator_width():
            res = 2
        return res

    def _calc_cursor_pos(self, pt, bit):
        """calCursorCol(self, pt:PySide2.QtCore.QPoint, bit)"""
        result = bit
        y = pt.y()
        if y < self.border_indicator_width():
            result += 10
        elif y > self.height() - self.border_indicator_width():
            result += 30
        else:
            result += 20

        return result

    def mousePressEvent(self, event):
        """ mousePressEvent(self, event:PySide2.QtGui.QMouseEvent) """
        if self._enabled_drag:
            self.set_calc_mouse_type(
                self._calc_cursor_pos(event.pos(),
                                      self._calc_cursor_col(event.pos())))
            if Qt.LeftButton == event.button(
            ) and MousePosition.CenterPos != self._cursor_calc_type:
                self._mouse_left_button_pressed = True
            self._rt_pre_geometry = self.geometry()
            self._mouse_move_pre_pos = event.globalPos()
        super(StyleWindow, self).mousePressEvent(event)

    def mouseReleaseEvent(self, event):
        """ mousePressEvent(self, event:PySide2.QtGui.QMouseEvent) """
        self._mouse_left_button_pressed = False
        QApplication.restoreOverrideCursor()
        super(StyleWindow, self).mouseReleaseEvent(event)

    def _drag_resize(self):
        mouse_cur_pos = QCursor.pos()
        move_pos = mouse_cur_pos - self._mouse_move_pre_pos
        after_resize_geometry = self._rt_pre_geometry
        if MousePosition.TopLeftPos == self._cursor_calc_type:
            after_resize_geometry.setTopLeft(self._rt_pre_geometry.topLeft() +
                                             move_pos)
        elif MousePosition.LeftPos == self._cursor_calc_type:
            after_resize_geometry.setLeft(self._rt_pre_geometry.left() +
                                          move_pos.x())
        elif MousePosition.BottomLeftPos == self._cursor_calc_type:
            after_resize_geometry.setBottomLeft(
                self._rt_pre_geometry.bottomLeft() + move_pos)
        elif MousePosition.BottomPos == self._cursor_calc_type:
            after_resize_geometry.setBottom(self._rt_pre_geometry.bottom() +
                                            move_pos.y())
        elif MousePosition.BottomRightPos == self._cursor_calc_type:
            after_resize_geometry.setBottomRight(
                self._rt_pre_geometry.bottomRight() + move_pos)
        elif MousePosition.RightPos == self._cursor_calc_type:
            after_resize_geometry.setRight(self._rt_pre_geometry.right() +
                                           move_pos.x())
        elif MousePosition.TopRightPos == self._cursor_calc_type:
            after_resize_geometry.setTopRight(
                self._rt_pre_geometry.topRight() + move_pos)
        elif MousePosition.TopPos == self._cursor_calc_type:
            after_resize_geometry.setTop(self._rt_pre_geometry.top() +
                                         move_pos.y())

        self.setGeometry(after_resize_geometry)
        self._mouse_move_pre_pos = mouse_cur_pos
        self._rt_pre_geometry = after_resize_geometry

    def background_color(self):
        return self._background_color

    def set_background_color(self, clr):
        self._background_color = clr

    background_color_ = Property(QColor,
                                 fget=background_color,
                                 fset=set_background_color)

    def border_color(self):
        return self._border_color

    def set_border_color(self, clr):
        self._border_color = clr

    border_color_ = Property(QColor, fget=border_color, fset=set_border_color)

    def border_radius(self):
        return self._border_radius

    def set_border_radius(self, radius):
        self._border_radius = parse_pix_width(radius)

    border_radius_ = Property(str, fget=border_radius, fset=set_border_radius)

    def paint_border_background(self, painter):
        """paint_border_background(self, painter:PySide2.QtGui.QPainter)"""
        painter.save()
        pen = QPen()
        pen.setColor(self.border_color())
        pen.setWidth(self.border_width())
        painter.setPen(pen)
        brush = QBrush(self.background_color())
        painter.setBrush(brush)
        painter.setOpacity(self.opacity())
        # painter.setRenderHint(QPainter.Antialiasing)
        rc = self.rect()
        paint_path = QPainterPath()
        # adjust shadow
        if self._shadow is not None:
            rc.adjust(self.shadow_blur(), self.shadow_blur(),
                      -self.shadow_blur(), -self.shadow_blur())

        # self._calc_background_path(rc, paint_path)
        if self._calc_background_path(rc, paint_path):
            painter.setRenderHint(QPainter.Antialiasing)

        if self.border_width() > 0:
            painter.drawPath(paint_path)
        else:
            painter.fillPath(paint_path, brush)

        painter.restore()

    def set_opacity(self, opacity):
        self._opacity = opacity

    def opacity(self) -> float:
        return self._opacity

    opacity_ = Property(float, fget=opacity, fset=set_opacity)

    def resizeEvent(self, event):
        super(StyleWindow, self).resizeEvent(event)
        # self.setGeometry(5, 5, self.width() - 5, self.height() - 5)

    def set_shadow(self, on):
        if on:
            self._shadow = QGraphicsDropShadowEffect(self)
            self._shadow.setOffset(0.0, 0.0)
            self._shadow.color()
            self.setGraphicsEffect(self._shadow)
            self.setGeometry(5, 5, self.width() - 5, self.height() - 5)

    def shadow(self):
        return self._shadow

    shadow_on_ = Property(bool, fget=shadow, fset=set_shadow)

    def shadow_color(self):
        if self._shadow is not None:
            return self._shadow.color()

    def set_shadow_color(self, color):
        if self._shadow is not None:
            self._shadow.setColor(color)

    shadow_color_ = Property(QColor, fget=shadow_color, fset=set_shadow_color)

    def shadow_blur(self):
        if self._shadow is not None:
            return self._shadow.blurRadius()
        return 0

    def set_shadow_blur(self, pix_blur):
        if self._shadow is not None:
            blur = parse_pix_width(pix_blur)
            self._shadow.setBlurRadius(blur)
            self.set_border_indicator_width(blur +
                                            self.border_indicator_width())
            if self._main_v_layout_ is not None:
                self._main_v_layout_.setContentsMargins(
                    blur + self.border_width(), blur + self.border_width(),
                    blur - 1 + self.border_width(), blur + self.border_width())

    shadow_blur_ = Property(str, fget=shadow_blur, fset=set_shadow_blur)

    def _init_default_layout(self):
        """setting a default QVBoxLayout"""
        self._main_v_layout_ = QVBoxLayout(self)
        self._main_v_layout_.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self._main_v_layout_)

    def set_left_top_border_radius(self, radius):
        self._left_top_border_radius = parse_pix_width(radius)

    def left_top_border_radius(self):
        if self._left_top_border_radius:
            return self._left_top_border_radius
        return self.border_radius()

    left_top_border_radius_ = Property(str,
                                       fget=left_top_border_radius,
                                       fset=set_left_top_border_radius)

    def set_left_bottom_border_radius(self, radius):
        self._left_bottom_border_radius = parse_pix_width(radius)

    def left_bottom_border_radius(self):
        if self._left_bottom_border_radius:
            return self._left_bottom_border_radius
        return self.border_radius()

    left_bottom_border_radius_ = Property(str,
                                          fget=left_bottom_border_radius,
                                          fset=set_left_bottom_border_radius)

    def set_right_top_border_radius(self, radius):
        self._right_top_border_radius = parse_pix_width(radius)

    def right_top_border_radius(self):
        if self._right_top_border_radius:
            return self._right_top_border_radius
        return self.border_radius()

    right_top_border_radius_ = Property(str,
                                        fget=right_top_border_radius,
                                        fset=set_right_top_border_radius)

    def set_right_bottom_border_radius(self, radius):
        self._right_bottom_border_radius = parse_pix_width(radius)

    def right_bottom_border_radius(self):
        if self._right_bottom_border_radius:
            return self._right_bottom_border_radius
        return self.border_radius()

    right_bottom_border_radius_ = Property(str,
                                           fget=right_bottom_border_radius,
                                           fset=set_right_bottom_border_radius)

    def _calc_background_path(self, rc, painter_path):

        render_hint = False
        if self.border_radius():
            painter_path.addRoundedRect(rc, self.border_radius(),
                                        self.border_radius())
            render_hint = True
        else:
            if self.left_top_border_radius() or self.left_bottom_border_radius() \
                    or self.right_top_border_radius() or self.right_bottom_border_radius():
                # add radius
                render_hint = True
            radius_angle = 90.0
            rotate_angle = 90.0
            painter_path.moveTo(rc.left(), rc.top())
            painter_path.arcTo(rc.left(), rc.top(),
                               self.left_top_border_radius(),
                               self.left_top_border_radius(), rotate_angle,
                               radius_angle)
            painter_path.lineTo(
                rc.left(),
                rc.bottom() - self.left_bottom_border_radius() / 2)
            painter_path.arcTo(rc.left(),
                               rc.bottom() - self.left_bottom_border_radius(),
                               self.left_bottom_border_radius(),
                               self.left_bottom_border_radius(),
                               rotate_angle * 2, radius_angle)
            painter_path.lineTo(
                rc.right() - self.right_bottom_border_radius() / 2,
                rc.bottom())
            painter_path.arcTo(rc.right() - self.right_bottom_border_radius(),
                               rc.bottom() - self.right_bottom_border_radius(),
                               self.right_bottom_border_radius(),
                               self.right_bottom_border_radius(),
                               rotate_angle * 3, radius_angle)
            painter_path.lineTo(rc.right(),
                                rc.top() - self.right_top_border_radius() / 2)
            painter_path.arcTo(rc.right() - self.right_top_border_radius(),
                               rc.top(), self.right_top_border_radius(),
                               self.right_top_border_radius(),
                               rotate_angle * 4 % rotate_angle, radius_angle)
            painter_path.lineTo(rc.left() + self.left_top_border_radius() / 2,
                                rc.top())

        return render_hint

    def set_layout(self, layout):
        if self._main_v_layout_ is not None:
            self._main_v_layout_.addLayout(layout)

    @property
    def mouse_drag_enabled(self):
        return self._enabled_drag

    @mouse_drag_enabled.setter
    def mouse_drag_enabled(self, drag):
        self._enabled_drag = drag

    def _mouse_drag_is_center_type(self):
        if self._cursor_calc_type == MousePosition.CenterPos:
            return True
        return False

    def set_calc_mouse_type(self, mouse_type: MousePosition):
        self._cursor_calc_type = mouse_type
示例#14
0
class Piece(QFrame):
    def __init__(self, parent, piece_type, row, col, move=False):
        super(Piece, self).__init__(parent)
        self.main_window = parent.parent().parent().parent(
        )  # CenterWidget >> AspectRatioWidget >> MainWindow
        # self.move(0, 0)
        # self.resize(50, 50)
        self.setStyleSheet("background: " + "red; border-radius: 50px")
        self.border_radius = 10
        self.offset = QPoint()
        self.crs = QCursor()
        self.table_width = 50
        self.piece_type = piece_type
        self.col = col
        self.row = row
        self.movable = move
        self.possible_jumps = []
        self.animator = QPropertyAnimation(self, b"geometry")
        self.styles = {}

        self.shadow = QGraphicsDropShadowEffect()
        self.shadow.setBlurRadius(0)
        self.shadow.setXOffset(0)
        self.shadow.setYOffset(0)
        self.setGraphicsEffect(self.shadow)
        self.confirm_jump_stop = False

    def update_size(self, w, h):
        side_width = self.main_window.aspect_ratio_widget.side_widget.width()

        if w - side_width > h:
            self.table_width = h
        else:
            self.table_width = w - side_width
        width = (self.table_width - 20) / 8
        # tile_size = grid[self.col][self.row].width()
        self.move(width * self.col + 10, width * self.row + 10)
        self.resize(width, width)
        self.border_radius = int(width / 2)
        self.paint_piece()

    # StckOverflow
    # https://stackoverflow.com/questions/18344135/why-do-stylesheets-not-work-when-subclassing-qwidget-and-using-q-object
    def paintEvent(self, pe):
        o = QStyleOption()
        o.initFrom(self)
        p = QPainter(self)
        self.style().drawPrimitive(QStyle.PE_Widget, o, p, self)

    def resizeEvent(self, event):
        super(Piece, self).resizeEvent(event)
        width = event.size().width()

        if self.styles:
            self.setStyleSheet("background: " + self.styles["background"] +
                               ";" + "border-radius: " + str(width / 2 - 2) +
                               "px;" + "border-style: outset;" +
                               "border-width: " + self.styles["border-width"] +
                               "px;" + "border-color: " +
                               self.styles["border-color"] + ";")

    def enterEvent(self, event):
        if self.movable:
            self.move(self.pos().x() - 10, self.pos().y() - 10)
            self.shadow.setBlurRadius(5)
            self.shadow.setXOffset(10)
            self.shadow.setYOffset(10)

    def leaveEvent(self, event):
        if self.movable:
            self.update_position()
            self.shadow.setBlurRadius(0)
            self.shadow.setXOffset(0)
            self.shadow.setYOffset(0)

    def mousePressEvent(self, event):

        super(Piece, self).mousePressEvent(event)
        self.main_window.remove_marks(True)
        if self.movable:
            self.setCursor(Qt.CursorShape.ClosedHandCursor)
            self.main_window.create_marks(self.possible_jumps)
            self.main_window.mark_source = [self.row, self.col]
        self.offset = event.globalPos() - self.pos()

        # Cancel all animation and replace matrix
        if self.main_window.animation_timer_list:
            for timer in self.main_window.animation_timer_list:
                if timer.objectName() == "end":
                    self.main_window.replace_matrix()
                    timer.setInterval(10)
                    return
                timer.stop()
            self.possible_jumps.clear()
            self.main_window.replace_matrix(None, self)
            self.main_window.animation_timer_list = []

    def mouseMoveEvent(self, event):
        super(Piece, self).mouseMoveEvent(event)
        self.raise_()
        # self.confirm_jump_stop = True
        if self.movable:
            self.move(event.globalPos() - self.offset)

    def mouseReleaseEvent(self, event):
        super(Piece, self).mouseReleaseEvent(event)
        self.shadow.setBlurRadius(0)
        self.shadow.setXOffset(0)
        self.shadow.setYOffset(0)
        # self.offset = event.globalPos()-self.pos()
        if self.movable:
            self.setCursor(Qt.CursorShape.OpenHandCursor)
            self.calc_position(True)

    def update_position(self):
        width = (self.table_width - 20) / 8
        self.move(width * self.col + 10, width * self.row + 10)

    def calc_position(self, user=False):
        widget_size = self.table_width - 20
        cell_size = widget_size / 8
        x_center = self.pos().x() - 10 + cell_size / 2
        y_center = self.pos().y() - 10 + cell_size / 2
        new_col = int(x_center / cell_size)
        new_row = int(y_center / cell_size)
        if user and new_row == self.row and new_col == self.col:
            if [new_row, new_col] in self.possible_jumps:
                if not self.confirm_jump_stop:
                    self.confirm_jump_stop = True
                    return
            else:
                self.update_position()
                return
        self.confirm_jump_stop = False

        if y_center >= 10 and new_row <= 7 and x_center >= 10 and new_col <= 7:
            if self.is_position_valid(new_row, new_col):
                self.main_window.remove_marks(True)

                self.movable = False
                self.set_cursor_pointing(False)
                self.main_window.pieces_matrix[self.row][self.col] = 0
                self.main_window.pieces_matrix[new_row][new_col] = self
                old = [self.row, self.col]

                self.row = new_row
                self.col = new_col
                self.main_window.lock_pieces()
                if self.row == 0:
                    if self.piece_type == 1:
                        self.piece_type = 4
                    self.paint_piece()

                QTimer.singleShot(
                    100, lambda: self.main_window.game.playerMove.stop_waiting(
                        [old, [self.row, self.col]]))

                if abs(new_row - old[0]) == 2:
                    ate_x = int((new_row + old[0]) / 2)
                    ate_y = int((new_col + old[1]) / 2)
                    self.main_window.pieces_matrix[ate_x][
                        ate_y].shrink_animation()
                    self.main_window.pl_last_eat = [new_row, new_col]
                else:
                    self.main_window.pl_last_eat = []

        self.update_position()

    def is_position_valid(self, row, col):
        for jmp in self.possible_jumps:
            if row == jmp[0] and col == jmp[1]:
                return True
        return False

    def paint_piece(self):
        color = get_piece_color(self.piece_type)
        border_width = (self.table_width - 20) / 8 / 8
        if self.piece_type == 4 or self.piece_type == 5:
            border_radius = self.border_radius - 5
            border_color = "#a32"

        else:
            border_radius = self.border_radius
            if self.piece_type == 1:
                border_color = "#444"
            else:
                border_color = "#999"

            border_width /= 2

        self.styles["background"] = str(color)
        self.styles["border-radius"] = str(border_radius)
        self.styles["border-style"] = "outset"
        self.styles["border-width"] = str(border_width)
        self.styles["border-color"] = border_color

        self.setStyleSheet("background: " + str(color) + ";" +
                           "border-radius: " + str(border_radius) + "px;" +
                           "border-style: outset;" + "border-width: " +
                           str(border_width) + "px;" + "border-color: " +
                           border_color + ";")
        self.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)

    def animate_move(self, i, j):
        self.raise_()
        self.animator.stop()
        y = (self.table_width - 20) / 8 * i + 10
        x = (self.table_width - 20) / 8 * j + 10
        old_rect = QRect(self.pos().x(),
                         self.pos().y(), self.width(), self.height())
        rect = QRect(x, y, self.height(), self.width())
        self.animator.setDuration(500)
        self.animator.setStartValue(old_rect)
        self.animator.setEndValue(rect)
        self.animator.start()
        self.main_window.pieces_matrix[i][j] = self.main_window.pieces_matrix[
            self.row][self.col]
        self.main_window.pieces_matrix[self.row][self.col] = 0

        self.row = i
        self.col = j
        if self.row == 7:
            if self.piece_type == 1:
                self.piece_type = 4
            elif self.piece_type == 2:
                self.piece_type = 5
            QTimer.singleShot(500, self.paint_piece)

    def animate_pl_move(self, i, j):
        if not self.is_position_valid(i, j):
            return
        self.raise_()
        self.animator.stop()
        self.animator = QPropertyAnimation(self, b"geometry")
        y = (self.table_width - 20) / 8 * i + 10
        x = (self.table_width - 20) / 8 * j + 10
        old_rect = QRect(self.pos().x(),
                         self.pos().y(), self.width(), self.height())
        rect = QRect(x, y, self.height(), self.width())
        self.animator.setDuration(500)
        self.animator.setStartValue(old_rect)
        self.animator.setEndValue(rect)
        self.animator.finished.connect(self.calc_position)

        self.movable = False
        self.set_cursor_pointing(False)

        self.animator.start()

    def shrink_animation(self):
        tile_width = (self.table_width - 20) / 8
        self.animator.stop()
        old_rect = QRect(self.pos().x(),
                         self.pos().y(), self.width(), self.height())
        rect = QRect(self.pos().x() + tile_width / 2,
                     self.pos().y() + tile_width / 2, 0, 0)
        self.animator.setDuration(250)
        self.animator.setStartValue(old_rect)
        self.animator.setEndValue(rect)
        self.styleSheet()
        self.animator.start()

    def set_cursor_pointing(self, clickable):
        if clickable:
            self.setCursor(Qt.CursorShape.PointingHandCursor)
            self.setCursor(Qt.CursorShape.OpenHandCursor)
        else:
            self.setCursor(Qt.CursorShape.ArrowCursor)
示例#15
0
class PreferencesGUI(App):
    '''
    Main class for the Preferences Window
    '''
    keys = list()
    finished = Signal()

    def __init__(self, appctxt, windowInst):
        super().__init__()
        self.appctxt = appctxt
        self.initUI(windowInst)

        # self.windowInst.setWindowFlags(QtCore.Qt.WindowTitleHint | QtCore.Qt.FramelessWindowHint)

        # self.windowInst.setAttribute(QtCore.Qt.WA_DeleteOnClose)

        self.settings = QSettings(self.COMPANY_NAME, self.APPLICATION_NAME)

        self.receiversInst = Receivers(self.ui)
        self.receiversInst.confirmSignal.connect(self.handleExit)
        self.connectReceivers()

        self.guiHelper = GuiHelper()

        self.loadKeys()

        self.loadSettings()

    def terminate(self):
        self.storeSettings()
        del self.settings

    def initUI(self, windowInst):
        # self.app = QtWidgets.QApplication(sys.argv)
        self.ui = Ui_PreferencesDialog()
        self.ui.windowInst = windowInst
        self.ui.windowInst.hide()
        self.ui.setupUi(self.ui.windowInst)

        windowInst.setAttribute(Qt.WA_TranslucentBackground)
        self.backgroundEffect = QGraphicsDropShadowEffect(windowInst)
        self.backgroundEffect.setBlurRadius(30)
        self.backgroundEffect.setOffset(0, 0)
        self.backgroundEffect.setEnabled(True)

        self.ui.centralwidget.setGraphicsEffect(self.backgroundEffect)

        self.ui.comboBoxAutosaveMode.addItem("Don't Autosave")
        self.ui.comboBoxAutosaveMode.addItem("5 min Autosave")
        self.ui.comboBoxAutosaveMode.addItem("10 min Autosave")
        self.ui.comboBoxAutosaveMode.addItem("15 min Autosave")
        self.ui.comboBoxAutosaveMode.addItem("20 min Autosave")

        self.ui.comboBoxThemeSelect.addItem("Dark Theme")
        self.ui.comboBoxThemeSelect.addItem("Light Theme")
        # self.ui.comboBoxThemeSelect.addItem("Ambient Theme")

    def run(self):
        '''
        Starts the Preferences Window
        '''
        self.loadSettings()
        self.ui.windowInst.show()

    @Slot(bool)
    def handleExit(self, confirmed):
        if confirmed:
            self.storeLooseEntries()
            self.storeSettings()

            print('Settings saved')
        else:
            self.loadSettings()

            print('Settings discarded')

        self.finished.emit()

    def connectReceivers(self):
        '''
        Connects all the buttons to the right receivers
        '''
        self.ui.radioButtonAffectsPDF.clicked.connect(
            self.receiversInst.setRadioButtonAffectsPDF)
        self.ui.comboBoxThemeSelect.currentIndexChanged.connect(
            self.receiversInst.setComboBoxThemeSelect)

        self.ui.radioButtonSmoothLines.clicked.connect(
            self.receiversInst.setradioButtonSmoothLines)

        self.ui.radioButtonSaveOnExit.clicked.connect(
            self.receiversInst.setRadioButtonSaveOnExit)
        self.ui.comboBoxAutosaveMode.currentIndexChanged.connect(
            self.receiversInst.setComboBoxAutosaveMode)

        self.ui.pushButtonOk.clicked.connect(
            lambda: self.receiversInst.confirmReceiver())
        self.ui.pushButtonCancel.clicked.connect(
            lambda: self.receiversInst.rejectReceiver())

        self.receiversInst.confirmSignal.connect(self.onClose)

    def loadKeys(self):
        '''
        Load the preferences keys
        '''

        scriptPath = os.path.dirname(os.path.abspath(__file__)) + '\\'
        # absKeysFilePath = os.path.normpath(scriptPath + KEYSFILEPATH)
        absKeysFilePath = self.appctxt.get_resource('preferences.keys')

        keysFileContent = readFile(absKeysFilePath)

        for key in keysFileContent['lines']:
            self.keys.append(key.replace('\n', ''))

    def storeSettings(self):
        '''
        Store the settings from the gui to the local dict and then to the settings instance
        '''
        for key in self.keys:
            self.settings.setValue(key, str(Preferences.data[key]))

        self.settings.sync()

    def storeLooseEntries(self):
        '''
        Saves all entries, which have been entered without explicit confirmation
        '''
        Preferences.updateKeyValue(
            "radioButtonAffectsPDF",
            str(self.ui.radioButtonAffectsPDF.isChecked()))
        Preferences.updateKeyValue(
            "comboBoxThemeSelect",
            str(self.ui.comboBoxThemeSelect.currentIndex()))

        Preferences.updateKeyValue(
            "radioButtonSmoothLines",
            str(self.ui.radioButtonSmoothLines.isChecked()))
        Preferences.updateKeyValue(
            "radioButtonUsePenAsDefault",
            str(self.ui.radioButtonSmoothLines.isChecked()))

        Preferences.updateKeyValue(
            "radioButtonSaveOnExit",
            int(self.ui.radioButtonSaveOnExit.isChecked()))
        Preferences.updateKeyValue(
            "comboBoxAutosaveMode",
            int(self.ui.comboBoxAutosaveMode.currentIndex()))

        Preferences.updateKeyValue(
            "radioButtonNoInteractionWhileEditing",
            str(self.ui.radioButtonNoInteractionWhileEditing.isChecked()))

    @Slot(bool)
    def onClose(self, store):
        if store:
            self.saveSettings()
        else:
            self.discardSettings()

    def saveSettings(self):
        self.storeLooseEntries()
        self.storeSettings()

    def discardSettings(self):
        self.loadSettings()

    def loadSettings(self):
        '''
        Load the settings from the settings instance to the local dict
        '''
        for key in self.keys:
            Preferences.updateKeyValue(
                key, self.settings.value(key, defaultValue=None, type=str))

        self.ensureValidData()

        self.ui.radioButtonAffectsPDF.setChecked(
            toBool(Preferences.data["radioButtonAffectsPDF"]))
        self.ui.comboBoxThemeSelect.setCurrentIndex(
            int(Preferences.data["comboBoxThemeSelect"]))
        self.receiversInst.setComboBoxThemeSelect(
            int(Preferences.data["comboBoxThemeSelect"]))

        self.ui.radioButtonUsePenAsDefault.setChecked(
            toBool(Preferences.data["radioButtonUsePenAsDefault"]))
        self.ui.radioButtonSmoothLines.setChecked(
            toBool(Preferences.data["radioButtonSmoothLines"]))

        self.ui.radioButtonSaveOnExit.setChecked(
            toBool(Preferences.data["radioButtonSaveOnExit"]))
        self.ui.comboBoxAutosaveMode.setCurrentIndex(
            int(Preferences.data["comboBoxAutosaveMode"]))

        self.ui.radioButtonNoInteractionWhileEditing.setChecked(
            toBool(Preferences.data["radioButtonNoInteractionWhileEditing"]))

    def ensureValidData(self):
        # Apply all default preferences if necessary

        if Preferences.data['radioButtonAffectsPDF'] == "":
            Preferences.updateKeyValue('radioButtonAffectsPDF', str(True))
        if Preferences.data['comboBoxThemeSelect'] == "":
            Preferences.updateKeyValue('comboBoxThemeSelect', 1)

        if Preferences.data['radioButtonSmoothLines'] == "":
            Preferences.updateKeyValue('radioButtonSmoothLines', str(True))
        if Preferences.data['radioButtonUsePenAsDefault'] == "":
            Preferences.updateKeyValue('radioButtonUsePenAsDefault', str(True))
        if Preferences.data['comboBoxDrawingMode'] == "":
            Preferences.updateKeyValue('comboBoxDrawingMode', 0)

        if Preferences.data['radioButtonSaveOnExit'] == "":
            Preferences.updateKeyValue('radioButtonSaveOnExit', str(True))
        if Preferences.data['comboBoxAutosaveMode'] == "":
            Preferences.updateKeyValue('comboBoxAutosaveMode', 0)

        if Preferences.data['radioButtonNoInteractionWhileEditing'] == "":
            Preferences.updateKeyValue('radioButtonNoInteractionWhileEditing',
                                       str(True))

        if Preferences.data['textSize'] == "":
            Preferences.updateKeyValue('textSize', "('0', '0', '0')")
        if Preferences.data['markerSize'] == "":
            Preferences.updateKeyValue('markerSize', "70")
        if Preferences.data['markerColor'] == "":
            Preferences.updateKeyValue('markerColor', "('0', '0', '0')")
        if Preferences.data['freehandSize'] == "":
            Preferences.updateKeyValue('freehandSize', "70")
        if Preferences.data['freehandColor'] == "":
            Preferences.updateKeyValue('freehandColor', "('0', '0', '0')")
        if Preferences.data['formSize'] == "":
            Preferences.updateKeyValue('formSize', "70")
        if Preferences.data['formColor'] == "":
            Preferences.updateKeyValue('formColor', "('0', '0', '0')")