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 __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 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 subTimeOut(self): fontColor = self.previewSubtitle.fontColor fontSize = (self.previewSubtitle.fontSize + 5) / 2.5 fontBold = self.previewSubtitle.bold fontItalic = self.previewSubtitle.italic fontShadowOffset = self.previewSubtitle.shadowOffset for _, srtTextItem in self.srtTextItemDict.items(): srtTextItem.setDefaultTextColor(fontColor) font = QFont() font.setFamily("微软雅黑") font.setPointSize(fontSize) font.setBold(fontBold) font.setItalic(fontItalic) srtTextItem.setFont(font) srtTextShadow = QGraphicsDropShadowEffect() srtTextShadow.setOffset(fontShadowOffset) srtTextItem.setGraphicsEffect(srtTextShadow) try: selected = self.subtitle.selectionModel().selection().indexes() for x, i in enumerate(selected): if self.subtitle.item(i.row(), x): txt = self.subtitle.item(i.row(), x).text() if txt: self.srtTextItemDict[x].setPlainText('#%s:' % (x + 1) + txt) txtSize = self.srtTextItemDict[x].boundingRect().size() posY = self.playerWidget.size().height() - txtSize.height() * (x + 1) posX = (self.playerWidget.size().width() - txtSize.width()) / 2 self.srtTextItemDict[x].setPos(posX, posY) else: self.srtTextItemDict[x].setPlainText('') else: self.srtTextItemDict[x].setPlainText('') except: pass
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 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
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
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)
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)
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)
def __init__(self, base_path, file, file_config, parent=None): super().__init__(parent) board = BanBoard(file, file_config) board_area = QScrollArea() board_area.setWidget(board) board_area.setWidgetResizable(True) self.setCentralWidget(board_area) self.stbar = QStatusBar() # add a save button at the right bottom corner save_btn = BanButton( "save", objectName="appBtn_save", toolTip="save xban file", shortcut="Ctrl+S", ) shadow = QGraphicsDropShadowEffect(self, blurRadius=10, offset=5, color=QColor("lightgrey")) save_btn.setGraphicsEffect(shadow) save_btn.pressed.connect(board.save_board) self.stbar.addPermanentWidget(save_btn) self.setStatusBar(self.stbar) log_handler = QLogHandler(self) root_logger = logging.getLogger() root_logger.addHandler(log_handler) log_handler.signal.log_msg.connect( partial(self.stbar.showMessage, timeout=1500)) self.stbar.showMessage(f"Initiate {file}", 1500) self.show()
def __init__(self, parent=None): super(airWidget, self).__init__(parent) self.desktop = '' box = QGridLayout() box.setColumnStretch(0, -1) box.setColumnStretch(1, 1) self.btn_icon = QPushButton() effect = QGraphicsDropShadowEffect(blurRadius=5, xOffset=3, yOffset=3) self.btn_icon.setGraphicsEffect(effect) self.btn_icon.setIconSize(QSize(64, 64)) self.btn_icon.setMinimumHeight(72) self.btn_icon.clicked.connect(self._executeAir) box.addWidget(self.btn_icon, 0, 0, 2, 1, Qt.AlignLeft) self.lbl_name = QLabel("") self.lbl_name.setObjectName("appName") box.addWidget(self.lbl_name, 0, 1, 1, 1, Qt.AlignLeft) self.lbl_desc = QLabel("") box.addWidget(self.lbl_desc, 1, 1, 1, 2, Qt.AlignLeft) btn_remove = QPushButton(_("Remove")) btn_remove.setObjectName("btnRemove") btn_remove.clicked.connect(self._removeAir) box.addWidget(btn_remove, 0, 2, 1, 1, Qt.AlignLeft) self.setObjectName("cell") self.setLayout(box) self.setStyleSheet(self._setCss())
def __init__(self, ): ui_file = QFile("resources/ui/splashscreen.ui") ui_file.open(QFile.ReadOnly) loader = QUiLoader() self.window = loader.load(ui_file) ui_file.close() if not self.window: print(loader.errorString()) sys.exit(-1) self.window.setWindowFlag(QtCore.Qt.FramelessWindowHint) self.window.setAttribute(QtCore.Qt.WA_TranslucentBackground) ## DROP SHADOW EFFECT self.window.shadow = QGraphicsDropShadowEffect(self.window) self.window.shadow.setBlurRadius(30) self.window.shadow.setXOffset(0) self.window.shadow.setYOffset(0) self.window.shadow.setColor(QColor(0, 0, 0, 90)) self.window.dropShadowFrame.setGraphicsEffect(self.window.shadow) self.counter = 0 self.timer = QtCore.QTimer() self.timer.timeout.connect(self.progress)
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())
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 __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)
def draw_board(self, file_config): """Initiate UI The UI consists of 2 parts, top is the board title and info and button is the subbords with tiles in each ones the subboard is drawn based on file_configuration """ config, content = file_config mainlayout = QVBoxLayout() mainlayout.setContentsMargins(20, 20, 20, 20) title_edit = QLineEdit(config["xban_config"]["title"], self) title_edit.setPlaceholderText("Enter title here ...") title_edit.setStyleSheet( 'font-family: "Courier New"; font-size: 24px; font-weight: bold;') info_edit = NoteTile(config["xban_config"]["description"], self) info_edit.setPlaceholderText("Enter description here ...") info_edit.setStyleSheet( "font-size: 14px; background-color:transparent;") mainlayout.addWidget(title_edit) mainlayout.addWidget(info_edit) sublayout = QHBoxLayout() color = config["xban_config"]["board_color"] sublayout.setMargin(10) sublayout.setSpacing(20) for i, tile_contents in enumerate(content.items()): subboard = SubBoard(tile_contents, color[i], self) sublayout.addWidget(subboard) subboard.delboardSig.connect( partial(self.delete_board, board=subboard)) add_btn = QPushButton("+", clicked=self.insert_board, toolTip="add board") shadow = QGraphicsDropShadowEffect(self, blurRadius=10, offset=5, color=QColor("lightgrey")) add_btn.setGraphicsEffect(shadow) add_btn.setFixedWidth(30) add_btn.setStyleSheet("border-style: none ") sublayout.addWidget(add_btn) save_shortcut = QShortcut(QKeySequence("Ctrl+S"), self) save_shortcut.activated.connect(self.save_board) mainlayout.addLayout(sublayout) self.setLayout(mainlayout)
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")
def draw_board(self, file_config): """Initiate UI The UI consists of 2 parts, top is the board title and info and button is the subbords with tiles in each ones the subboard is drawn based on file_configuration """ config, content = file_config mainlayout = QVBoxLayout() mainlayout.setContentsMargins(20, 20, 20, 20) title_edit = QLineEdit( config["xban_config"]["title"], objectName="windowEdit_title", parent=self, ) title_edit.setPlaceholderText("Enter title here ...") info_edit = NoteTile(config["xban_config"]["description"], "windowEdit_text", self) info_edit.setPlaceholderText("Enter description here ...") mainlayout.addWidget(title_edit) mainlayout.addWidget(info_edit) self.sublayout = QHBoxLayout() color = config["xban_config"]["board_color"] self.sublayout.setMargin(10) self.sublayout.setSpacing(20) add_btn = BanButton( "+", clicked=self.insert_board, toolTip="add board", objectName="windowBtn_add", ) shadow = QGraphicsDropShadowEffect(self, blurRadius=10, offset=5, color=QColor("lightgrey")) add_btn.setGraphicsEffect(shadow) self.sublayout.addWidget(add_btn) mainlayout.addLayout(self.sublayout) self.setLayout(mainlayout) for i, tile_contents in enumerate(content.items()): # insert the boards self.insert_board(tile_contents, color[i])
def activate(self): """Adds items to scene and setup graphics effect. Called in the constructor and when re-adding the item to the project in the context of undo/redo. """ scene = self._toolbox.ui.graphicsView.scene() scene.addItem(self) shadow_effect = QGraphicsDropShadowEffect() shadow_effect.setOffset(1) shadow_effect.setEnabled(False) self.setGraphicsEffect(shadow_effect)
def __init__(self, tile_contents, color, parent=None): super().__init__(parent) title_name, tile_items = tile_contents self.color = color self.setStyleSheet("QFrame{border: 0.5px solid #dddddd; " "border-radius: 8px; background-color:white;}") shadow = QGraphicsDropShadowEffect(self, blurRadius=10, offset=5, color=QColor("lightgrey")) self.setGraphicsEffect(shadow) board = QVBoxLayout() board.setMargin(20) tile_title = NoteTile(title_name, self) tile_title.setPlaceholderText("Title here ...") tile_title.setStyleSheet( "border-style: none; font-size: 18px; font-weight: bold;") board.addWidget(tile_title) listwidget = BanListWidget(self) listwidget.setStyleSheet(TILE_STYLE.get(color, "black")) for tile in tile_items: listwidget.add_item(tile) board.addWidget(listwidget) btn_layout = QHBoxLayout() add_btn = QPushButton("+", clicked=self.add_listitem, toolTip="add tile") del_btn = QPushButton("-", clicked=self.del_listitem, toolTip="delete tile") context_btn = QPushButton("\u2261", toolTip="change color") context_btn.setMenu(self.context_menu()) # context_btn.setStyleSheet('QPushButton:hover {color:#b5b3b3;} QPushButton::menu-indicator:hover {color:black;}') destory_btn = QPushButton("\u00D7", clicked=self.delete_board, toolTip="delete board") destory_btn.setStyleSheet("QPushButton:hover {color: red;}") btn_layout.addWidget(add_btn) btn_layout.addWidget(del_btn) btn_layout.addWidget(context_btn) btn_layout.addWidget(destory_btn) board.addLayout(btn_layout) self.setLayout(board)
def __init__(self, toolbox, x, y, w, h, project_item, icon_file, icon_color, background_color): """Base class for project item icons drawn in Design View. Args: toolbox (ToolBoxUI): QMainWindow instance x (float): Icon x coordinate y (float): Icon y coordinate w (float): Icon width h (float): Icon height project_item (ProjectItem): Item icon_file (str): Path to icon resource icon_color (QColor): Icon's color background_color (QColor): Background color """ super().__init__() self._toolbox = toolbox self._project_item = project_item self._moved_on_scene = False self.renderer = QSvgRenderer() self.svg_item = QGraphicsSvgItem() self.colorizer = QGraphicsColorizeEffect() self.setRect(QRectF(x, y, w, h)) # Set ellipse coordinates and size self.text_font_size = 10 # point size # Make item name graphics item. name = project_item.name if project_item else "" self.name_item = QGraphicsSimpleTextItem(name) shadow_effect = QGraphicsDropShadowEffect() shadow_effect.setOffset(1) shadow_effect.setEnabled(False) self.setGraphicsEffect(shadow_effect) self.set_name_attributes() # Set font, size, position, etc. # Make connector buttons self.connectors = dict( bottom=ConnectorButton(self, toolbox, position="bottom"), left=ConnectorButton(self, toolbox, position="left"), right=ConnectorButton(self, toolbox, position="right"), ) # Make exclamation and rank icons self.exclamation_icon = ExclamationIcon(self) self.rank_icon = RankIcon(self) # Group the drawn items together by setting the background rectangle as the parent of other QGraphicsItems # NOTE: setting the parent item moves the items as one! self.name_item.setParentItem(self) for conn in self.connectors.values(): conn.setParentItem(self) self.svg_item.setParentItem(self) self.exclamation_icon.setParentItem(self) self.rank_icon.setParentItem(self) brush = QBrush(background_color) self._setup(brush, icon_file, icon_color) # Add items to scene scene = self._toolbox.ui.graphicsView.scene() scene.addItem(self)
def __init__(self, toolbox, icon_file, icon_color, background_color): """ Args: toolbox (ToolboxUI): QMainWindow instance icon_file (str): Path to icon resource icon_color (QColor): Icon's color background_color (QColor): Background color """ super().__init__() self._toolbox = toolbox self.icon_file = icon_file self._moved_on_scene = False self.previous_pos = QPointF() self.current_pos = QPointF() self.icon_group = {self} self.renderer = QSvgRenderer() self.svg_item = QGraphicsSvgItem(self) self.colorizer = QGraphicsColorizeEffect() self.setRect( QRectF(-self.ITEM_EXTENT / 2, -self.ITEM_EXTENT / 2, self.ITEM_EXTENT, self.ITEM_EXTENT)) self.text_font_size = 10 # point size # Make item name graphics item. self._name = "" self.name_item = QGraphicsSimpleTextItem(self._name, self) self.set_name_attributes() # Set font, size, position, etc. # Make connector buttons self.connectors = dict( bottom=ConnectorButton(self, toolbox, position="bottom"), left=ConnectorButton(self, toolbox, position="left"), right=ConnectorButton(self, toolbox, position="right"), ) # Make exclamation and rank icons self.exclamation_icon = ExclamationIcon(self) self.execution_icon = ExecutionIcon(self) self.rank_icon = RankIcon(self) brush = QBrush(background_color) self._setup(brush, icon_file, icon_color) shadow_effect = QGraphicsDropShadowEffect() shadow_effect.setOffset(1) shadow_effect.setEnabled(False) self.setGraphicsEffect(shadow_effect)
def __init__(self, tile_contents, color, parent=None): super().__init__(parent) title_name, tile_items = tile_contents self.color = color self.setObjectName("subBoardFrame") shadow = QGraphicsDropShadowEffect(self, blurRadius=10, offset=5, color=QColor("lightgrey")) self.setGraphicsEffect(shadow) board = QVBoxLayout() board.setMargin(20) tile_title = NoteTile(title_name, "boardEdit", self) tile_title.setPlaceholderText("Title here ...") board.addWidget(tile_title) self.listwidget = BanListWidget(self) self.listwidget.setStyleSheet(TILE_STYLE.get(color, "black")) for tile in tile_items: self.listwidget.add_item(tile) board.addWidget(self.listwidget) btn_layout = QHBoxLayout() add_btn = BanButton( "+", clicked=self.add_listitem, toolTip="add tile", objectName="boardBtn", ) del_btn = BanButton( "-", clicked=self.del_listitem, toolTip="delete tile", objectName="boardBtn", shortcut=QKeySequence(Qt.Key_Backspace), ) color_btn = BanButton( "\u2261", toolTip="change color", objectName="boardBtn_color", color=[("white", "#bdbdbd"), ("grey", "white")], ) self.color_menu = self.context_menu() color_btn.setMenu(self.color_menu) color_btn.sizeSig.connect(self.menu_resize) destory_btn = BanButton( "\u00D7", clicked=self.delete_board, toolTip="delete board", objectName="boardBtn_des", color=[("white", "tomato"), ("white", "red")], ) btn_layout.addWidget(add_btn) btn_layout.addWidget(del_btn) btn_layout.addWidget(color_btn) btn_layout.addWidget(destory_btn) board.addLayout(btn_layout) self.setLayout(board)
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()
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()
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
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)))
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