Esempio n. 1
0
    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
Esempio n. 2
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
Esempio n. 3
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()
Esempio n. 4
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()
Esempio n. 5
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
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
Esempio n. 7
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)