def onUpdatePropertyView(self, formLayout): # name le_name = QLineEdit(self.getName()) le_name.setReadOnly(True) if self.label().IsRenamable(): le_name.setReadOnly(False) le_name.returnPressed.connect(lambda: self.setName(le_name.text())) formLayout.addRow("Name", le_name) # uid leUid = QLineEdit(str(self.uid)) leUid.setReadOnly(True) #formLayout.addRow("Uuid", leUid) # type leType = QLineEdit(self.__class__.__name__) leType.setReadOnly(True) formLayout.addRow("Type", leType) # pos #le_pos = QLineEdit("{0} x {1}".format(self.pos().x(), self.pos().y())) #formLayout.addRow("Pos", le_pos) # inputs if len([i for i in self.inputs.values()]) != 0: for inp in self.inputs.values(): dataSetter = inp.call if inp.dataType == DataTypes.Exec else inp.setData if not inp.hasConnections(): w = getInputWidget(inp.dataType, dataSetter, inp.defaultValue(), inp.getUserStruct()) if w: w.setWidgetValue(inp.currentData()) w.setObjectName(inp.getName()) formLayout.addRow(inp.name, w) if inp.hasConnections(): w.setEnabled(False) w.hide() if self.asGraphSides: # outputs if len([i for i in self.outputs.values()]) != 0: for out in self.outputs.values(): dataSetter = out.call if out.dataType == DataTypes.Exec else out.setData w = getInputWidget(out.dataType, dataSetter, out.defaultValue(), out.getUserStruct()) if w: w.setWidgetValue(out.currentData()) w.setObjectName(out.getName()) formLayout.addRow(out.name, w) if out.hasConnections(): w.setEnabled(True) doc_lb = QLabel() doc_lb.setStyleSheet("background-color: black;") doc_lb.setText("Description") #formLayout.addRow("", doc_lb) doc = QTextBrowser() doc.setOpenExternalLinks(True) doc.setHtml(self.description())
def __init__(self, settings, parent=None, description=None, pstvOnly=True): super(ConfigDialog, self).__init__(parent) self.setWindowTitle("Configure source") self.layout = QGridLayout(self) row = 0 self.widgets = {} self.settings = settings # Settings should be a dictionary for name, val in settings.items(): label = QLabel(self) label.setText(str(name)) self.layout.addWidget(label, row, 0, 1, 1) # Check the type of each setting, and create widgets accordingly if isinstance(val, str): # A string of some kind widget = QLineEdit(self) widget.setText(val) elif isinstance(val, list): # A list of alternative values, first is selected print("List: ", name) continue elif isinstance(val, bool): widget = QCheckBox(self) if val: widget.setCheckState(Qt.CheckState.Checked) else: widget.setCheckState(Qt.CheckState.Unchecked) elif isinstance(val, int): widget = QLineEdit(self) widget.setInputMask("9000000") widget.setText(str(val).strip()) elif isinstance(val, float): widget = QLineEdit(self) if pstvOnly: widget.setInputMask("0.000") widget.setText(str(val).strip()) else: print("Ignoring: " + name) continue widget.config = name self.widgets[name] = widget self.layout.addWidget(widget, row, 1, 1, 1) row += 1 # Add OK and Cancel buttons buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) buttonbox.accepted.connect(self.getValues) buttonbox.rejected.connect(self.reject) self.layout.addWidget(buttonbox, row, 1, 2, 1)
def __init__(self, parent: QWidget = None): super().__init__(parent) self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) self.server = QLocalServer(self) if not self.server.listen("fortune"): QMessageBox.critical( self, self.tr("Local Fortune Server"), self.tr("Unable to start the server: %s." % (self.server.errorString())), ) QTimer.singleShot(0, self.close) return statusLabel = QLabel() statusLabel.setWordWrap(True) statusLabel.setText( self. tr("The server is running.\nRun the Local Fortune Client example now." )) self.fortunes = ( self.tr( "You've been leading a dog's life. Stay off the furniture."), self.tr("You've got to think about tomorrow."), self.tr("You will be surprised by a loud noise."), self.tr("You will feel hungry again in another hour."), self.tr("You might have mail."), self.tr("You cannot kill time without injuring eternity."), self.tr( "Computers are not intelligent. They only think they are."), ) quitButton = QPushButton(self.tr("Quit")) quitButton.setAutoDefault(False) quitButton.clicked.connect(self.close) self.server.newConnection.connect(self.sendFortune) buttonLayout = QHBoxLayout() buttonLayout.addStretch(1) buttonLayout.addWidget(quitButton) buttonLayout.addStretch(1) mainLayout = QVBoxLayout(self) mainLayout.addWidget(statusLabel) mainLayout.addLayout(buttonLayout) self.setWindowTitle(QGuiApplication.applicationDisplayName())
class FrameProgressBar(QFrame, object): def __init__(self, *args, **kwargs): super(FrameProgressBar, self).__init__(*args, **kwargs) layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) self.setLayout(layout) self._label = QLabel('', self) self._label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) layout.addWidget(self._label) self._progress_bar = QProgressBar(self) self._progress_bar.setFormat('') self._progress_bar.setRange(0, 100) self._progress_bar.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) layout.addWidget(self._progress_bar) def reset(self): """ Reset progress bar """ self._progress_bar.reset() def set_text(self, text): """ Set the text of the progress bar :param text: str """ self._label.setText(text) def set_value(self, value): """ Set the value of the progress bar :param value: int or float """ self._progress_bar.setValue(value) def set_range(self, min_, max_): """ Set the range of the progress bar :param min_: int :param max_: int """ self._progress_bar.setRange(min_, max_)
def update_sum(self): """updates the sum of energy and thermo corrections""" self.sum_table.setRowCount(0) if not self.sp_table.rowCount(): return sp_nrg = float(self.sp_table.cellWidget(0, 1).text()) for row in range(0, self.thermo_table.rowCount()): self.sum_table.insertRow(row) label = self.thermo_table.cellWidget(row, 0) tooltip = label.toolTip() text = label.text().replace("𝛿", "") sum_label = QLabel(text) if "href=" in text: sum_label = QLabel() sum_label.setText(text) sum_label.setTextFormat(Qt.RichText) sum_label.setTextInteractionFlags(Qt.TextBrowserInteraction) sum_label.linkActivated.connect(self.open_link) sum_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) sum_label.setToolTip(tooltip) self.sum_table.setCellWidget(row, 0, sum_label) thermo = float(self.thermo_table.cellWidget(row, 1).text()) total = sp_nrg + thermo total_nrg = SmallLineEdit("%.6f" % total) total_nrg.setFrame(False) total_nrg.setReadOnly(True) total_nrg.setToolTip(tooltip) self.sum_table.setCellWidget(row, 1, total_nrg) unit_label = ReadOnlyTableItem() unit_label.setData(Qt.DisplayRole, "E\u2095") unit_label.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter) unit_label.setToolTip(tooltip) self.sum_table.setItem(row, 2, unit_label) self.sum_table.resizeRowToContents(row) self.sum_table.resizeColumnToContents(0) self.sum_table.resizeColumnToContents(1) self.sum_table.resizeColumnToContents(2)
class Widget(QTabWidget): def __init__(self): super(Widget, self).__init__() self.title = "Andrea <3" self.top = 100 self.left = 300 self.width = 200 self.height = 480 self.msg = "Nothing going on..." self.label = QLabel() self.label.setText(self.msg) self.build_button1() self.build_tab1() self.build_tab2() self.init_ui() def button1_clicked(self): self.msg = "Button 1 has been clicked." self.label.setText(self.msg) print(self.msg) def build_button1(self): self.button1 = QPushButton("Button1") self.button1.clicked.connect(self.button1_clicked) def build_tab1(self): self.tab1 = QWidget() self.tab1.layout = QVBoxLayout(self) self.tab1.layout.addWidget(self.button1) self.tab1.setLayout(self.tab1.layout) def build_tab2(self): self.tab2 = QWidget() self.tab2.layout = QVBoxLayout(self) self.tab2.layout.addWidget(self.label) self.tab2.setLayout(self.tab2.layout) def init_ui(self): self.setWindowTitle(self.title) self.addTab(self.tab1, "tab 1") self.addTab(self.tab2, "tab 2") self.setGeometry(self.left, self.top, self.width, self.height) self.show()
def _create_version(self, version, elements): version_widget = QWidget() version_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) version_layout.setContentsMargins(0, 0, 0, 0) version_layout.setSpacing(0) version_layout.setAlignment(Qt.AlignTop) version_widget.setLayout(version_layout) self.version_accordion.add_item(version, version_widget, collapsed=True) version_label = QLabel() version_layout.addWidget(version_label) version_text = '' for item in elements: version_text += '- {}\n'.format(item) version_label.setText(version_text) self.main_layout.addSpacing(5)
class HelpForm(QDialog): def __init__(self, page, parent=None): super(HelpForm, self).__init__(parent) self.setAttribute(Qt.WA_DeleteOnClose) self.setAttribute(Qt.WA_GroupLeader) backAction = QAction(QIcon(":/back.png"), "&Back", self) backAction.setShortcut(QKeySequence.Back) homeAction = QAction(QIcon(":/home.png"), "&Home", self) homeAction.setShortcut("Home") self.pageLabel = QLabel() toolBar = QToolBar() toolBar.addAction(backAction) toolBar.addAction(homeAction) toolBar.addWidget(self.pageLabel) self.textBrowser = QTextBrowser() layout = QVBoxLayout() layout.addWidget(toolBar) layout.addWidget(self.textBrowser, 1) self.setLayout(layout) backAction.triggered.connect(self.tbackward) homeAction.triggered.connect(self.thome) self.textBrowser.sourceChanged.connect(self.updatePageTitle) self.textBrowser.setSearchPaths([":/help"]) self.textBrowser.setSource(QUrl(page)) self.resize(400, 600) self.setWindowTitle("{0} Help".format(QApplication.applicationName())) def updatePageTitle(self): self.pageLabel.setText(self.textBrowser.documentTitle()) def tbackward(self): self.textBrowser.backward() def thome(self): self.textBrowser.home()
def onUpdatePropertyView(self, formLayout): # name le_name = QLineEdit(self.getName()) le_name.setReadOnly(True) if self.label().IsRenamable(): le_name.setReadOnly(False) le_name.returnPressed.connect(lambda: self.setName(le_name.text())) formLayout.addRow("Name", le_name) # uid leUid = QLineEdit(str(self.uid)) leUid.setReadOnly(True) formLayout.addRow("Uuid", leUid) # type leType = QLineEdit(self.__class__.__name__) leType.setReadOnly(True) formLayout.addRow("Type", leType) # pos le_pos = QLineEdit("{0} x {1}".format(self.pos().x(), self.pos().y())) formLayout.addRow("Pos", le_pos) pb = QPushButton("...") pb.clicked.connect(self.onChangeColor) formLayout.addRow("Color", pb) doc_lb = QLabel() doc_lb.setStyleSheet("background-color: black;") doc_lb.setText("Description") formLayout.addRow("", doc_lb) doc = QTextBrowser() doc.setOpenExternalLinks(True) doc.setHtml(self.description()) formLayout.addRow("", doc)
class MayaShelf(abstract_shelf.AbstractShelf, object): def __init__(self, name='MayaShelf', label_background=(0, 0, 0, 0), label_color=(0.9, 0.9, 0.9), category_icon=None, enable_labels=True): super(MayaShelf, self).__init__(name=name, label_background=label_background, label_color=label_color, category_icon=category_icon, enable_labels=enable_labels) @staticmethod def add_menu_item(parent, label, command='', icon=''): """ Adds a menu item with the given attributes :param parent: :param label: :param command: :param icon: :return: """ return maya.cmds.menuItem(parent=parent, label=label, command=command, image=icon or '') @staticmethod def add_sub_menu(parent, label, icon=None): """ Adds a sub menu item with the given label and icon to the given parent popup menu :param parent: :param label: :param icon: :return: """ return maya.cmds.menuItem(parent=parent, label=label, icon=icon or '', subMenu=True) def create(self, delete_if_exists=True): """ Creates a new shelf """ if delete_if_exists: if gui.shelf_exists(shelf_name=self._name): gui.delete_shelf(shelf_name=self._name) else: assert not gui.shelf_exists(self._name), 'Shelf with name {} already exists!'.format(self._name) self._name = gui.create_shelf(name=self._name) # ======================================================================================================== self._category_btn = QPushButton('') if self._category_icon: self._category_btn.setIcon(self._category_icon) self._category_btn.setIconSize(QSize(18, 18)) self._category_menu = QMenu(self._category_btn) self._category_btn.setStyleSheet( 'QPushButton::menu-indicator {image: url(myindicator.png);' 'subcontrol-position: right center;subcontrol-origin: padding;left: -2px;}') self._category_btn.setMenu(self._category_menu) self._category_lbl = QLabel('MAIN') self._category_lbl.setAlignment(Qt.AlignCenter) font = self._category_lbl.font() font.setPointSize(6) self._category_lbl.setFont(font) menu_ptr = maya.OpenMayaUI.MQtUtil.findControl(self._name) menu_widget = qtutils.wrapinstance(menu_ptr, QWidget) menu_widget.layout().addWidget(self._category_btn) menu_widget.layout().addWidget(self._category_lbl) self.add_separator() def set_as_active(self): """ Sets this shelf as active shelf in current DCC session """ main_shelf = maya.mel.eval("$_tempVar = $gShelfTopLevel") maya.cmds.tabLayout(main_shelf, edit=True, selectTab=self._name) def add_button(self, label, tooltip=None, icon='customIcon.png', command=None, double_command=None, command_type='python'): """ Adds a shelf button width the given parameters :param label: :param tooltip: :param icon: :param command: :param double_command: :param command_type: :return: """ maya.cmds.setParent(self._name) command = command or '' double_command = double_command or '' if not self._enable_labels: label = '' return maya.cmds.shelfButton(width=37, height=37, image=icon or '', label=label, command=command, doubleClickCommand=double_command, annotation=tooltip or '', imageOverlayLabel=label, overlayLabelBackColor=self._label_background, overlayLabelColor=self._label_color, sourceType=command_type) def add_separator(self): """ Adds a separator to shelf :param parent: :return: """ maya.cmds.separator( parent=self._name, manage=True, visible=True, horizontal=False, style='shelf', enableBackground=False, preventOverride=False) def build_category(self, shelf_file, category_name): self._category_lbl.setText(category_name.upper()) self.load_category(shelf_file, 'general', clear=True) if category_name != 'general': self.add_separator() self.load_category(shelf_file, category_name, clear=False) def build_categories(self, shelf_file, categories): """ Builds all categories given :param categories: list<str>, list of categories to build """ self._category_lbl.setText('ALL') self.load_category(shelf_file, 'general', clear=True) for cat in categories: if cat == 'general': continue self.add_separator() self.load_category(shelf_file, cat, clear=False) def load_category(self, shelf_file, category_name, clear=True): """ Loads into a shelf all the items of given category name, if exists :param category_name: str, name of the category """ if clear: self.clear_list() # self.add_separator() with open(shelf_file) as f: shelf_data = json.load(f, object_pairs_hook=OrderedDict) for item, item_data in shelf_data.items(): if item != category_name: continue for i in item_data: icon = i.get('icon') command = i.get('command') annotation = i.get('annotation') label = i.get('label') if annotation == 'separator': self.add_separator() else: self.add_button(label=label, command=command, icon=icon, tooltip=annotation) return def build(self, shelf_file): """ Builds shelf from JSON file :param shelf_file: str """ first_item = None all_categories = list() with open(shelf_file) as f: shelf_data = json.load(f, object_pairs_hook=OrderedDict) for i, item in enumerate(shelf_data.keys()): if i == 0: first_item = item category_action = self._category_menu.addAction(item.title()) category_action.triggered.connect(partial(self.build_category, shelf_file, item)) all_categories.append(item) category_action = self._category_menu.addAction('All') category_action.triggered.connect(partial(self.build_categories, shelf_file, all_categories)) if first_item: self.load_category(shelf_file, first_item, clear=False) def clear_list(self): """ Clears all the elements of the shelf """ if gui.shelf_exists(shelf_name=self._name): menu_items = maya.cmds.shelfLayout(self._name, query=True, childArray=True) for item in menu_items: try: maya.cmds.deleteUI(item) except Exception: pass
class DockTitleBar(QWidget, object): def __init__(self, dock_widget, renamable=False): super(DockTitleBar, self).__init__(dock_widget) self._renamable = renamable main_layout = QHBoxLayout() main_layout.setContentsMargins(0, 0, 0, 1) self.setLayout(main_layout) self._buttons_box = QGroupBox('') self._buttons_box.setObjectName('Docked') self._buttons_layout = QHBoxLayout() self._buttons_layout.setSpacing(1) self._buttons_layout.setMargin(2) self._buttons_box.setLayout(self._buttons_layout) main_layout.addWidget(self._buttons_box) self._title_label = QLabel(self) self._title_label.setStyleSheet('background: transparent') self._title_edit = QLineEdit(self) self._title_edit.setVisible(False) self._button_size = QSize(14, 14) self._dock_btn = QToolButton(self) self._dock_btn.setIcon(resources.icon('restore_window', theme='color')) self._dock_btn.setMaximumSize(self._button_size) self._dock_btn.setAutoRaise(True) self._close_btn = QToolButton(self) self._close_btn.setIcon(resources.icon('close_window', theme='color')) self._close_btn.setMaximumSize(self._button_size) self._close_btn.setAutoRaise(True) self._buttons_layout.addSpacing(2) self._buttons_layout.addWidget(self._title_label) self._buttons_layout.addWidget(self._title_edit) self._buttons_layout.addStretch() self._buttons_layout.addSpacing(5) self._buttons_layout.addWidget(self._dock_btn) self._buttons_layout.addWidget(self._close_btn) self._buttons_box.mouseDoubleClickEvent = self.mouseDoubleClickEvent self._buttons_box.mousePressEvent = self.mousePressEvent self._buttons_box.mouseMoveEvent = self.mouseMoveEvent self._buttons_box.mouseReleaseEvent = self.mouseReleaseEvent dock_widget.featuresChanged.connect(self._on_dock_features_changed) self._title_edit.editingFinished.connect(self._on_finish_edit) self._dock_btn.clicked.connect(self._on_dock_btn_clicked) self._close_btn.clicked.connect(self._on_close_btn_clicked) self._on_dock_features_changed(dock_widget.features()) self.set_title(dock_widget.windowTitle()) dock_widget.installEventFilter(self) dock_widget.topLevelChanged.connect(self._on_change_floating_style) @property def renamable(self): return self._renamable @renamable.setter def renamable(self, flag): self._renamable = flag def eventFilter(self, obj, event): if event.type() == QEvent.WindowTitleChange: self.set_title(obj.windowTitle()) return super(DockTitleBar, self).eventFilter(obj, event) def mouseMoveEvent(self, event): event.ignore() def mousePressEvent(self, event): event.ignore() def mouseReleaseEvent(self, event): event.ignore() def mouseDoubleClickEvent(self, event): if event.pos().x() <= self._title_label.width() and self._renamable: self._start_edit() else: super(DockTitleBar, self).mouseDoubleClickEvent(event) def update(self, *args, **kwargs): self._on_change_floating_style(self.parent().isFloating()) super(DockTitleBar, self).update(*args, **kwargs) def set_title(self, title): self._title_label.setText(title) self._title_edit.setText(title) def add_button(self, button): button.setAutoRaise(True) button.setMaximumSize(self._button_size) self._buttons_layout.insertWidget(5, button) def _start_edit(self): self._title_label.hide() self._title_edit.show() self._title_edit.setFocus() def _finish_edit(self): self._title_edit.hide() self._title_label.show() self.parent().setWindowTitle(self._title_edit.text()) def _on_dock_features_changed(self, features): if not features & QDockWidget.DockWidgetVerticalTitleBar: self._close_btn.setVisible(features & QDockWidget.DockWidgetClosable) self._dock_btn.setVisible(features & QDockWidget.DockWidgetFloatable) else: raise ValueError('Vertical title bar is not supported!') def _on_finish_edit(self): self._finish_edit() def _on_dock_btn_clicked(self): self.parent().setFloating(not self.parent().isFloating()) def _on_close_btn_clicked(self): self.parent().toggleViewAction().setChecked(False) self.parent().close() def _on_change_floating_style(self, state): pass
class Dialog(QDialog): def __init__(self, parent: QWidget = None) -> None: super().__init__(parent) self.tcpServer = QTcpServer() self.tcpClient = QTcpSocket() self.tcpServerConnection: QTcpSocket = None self.bytesToWrite = 0 self.bytesWritten = 0 self.bytesReceived = 0 self.clientProgressBar = QProgressBar() self.clientStatusLabel = QLabel(self.tr("Client ready")) self.serverProgressBar = QProgressBar() self.serverStatusLabel = QLabel(self.tr("Server ready")) self.startButton = QPushButton(self.tr("&Start")) self.quitButton = QPushButton(self.tr("&Quit")) self.buttonBox = QDialogButtonBox() self.buttonBox.addButton(self.startButton, QDialogButtonBox.ActionRole) self.buttonBox.addButton(self.quitButton, QDialogButtonBox.RejectRole) self.startButton.clicked.connect(self.start) self.quitButton.clicked.connect(self.close) self.tcpServer.newConnection.connect(self.acceptConnection) self.tcpClient.connected.connect(self.startTransfer) self.tcpClient.bytesWritten.connect(self.updateClientProgress) self.tcpClient.error.connect(self.displayError) mainLayout = QVBoxLayout(self) mainLayout.addWidget(self.clientProgressBar) mainLayout.addWidget(self.clientStatusLabel) mainLayout.addWidget(self.serverProgressBar) mainLayout.addWidget(self.serverStatusLabel) mainLayout.addStretch(1) mainLayout.addSpacing(10) mainLayout.addWidget(self.buttonBox) @Slot() def start(self): self.startButton.setEnabled(False) QGuiApplication.setOverrideCursor(Qt.WaitCursor) self.bytesWritten = 0 self.bytesReceived = 0 while not self.tcpServer.isListening() and not self.tcpServer.listen(): ret = QMessageBox.critical( self, self.tr("Loopback"), self.tr( "Unable to start the test: %s" % (self.tcpServer.errorString()) ), QMessageBox.Retry | QMessageBox.Cancel, ) if ret == QMessageBox.Cancel: return self.serverStatusLabel.setText(self.tr("Listening")) self.clientStatusLabel.setText(self.tr("Connecting")) self.tcpClient.connectToHost( QHostAddress.LocalHost, self.tcpServer.serverPort() ) @Slot() def acceptConnection(self): self.tcpServerConnection = self.tcpServer.nextPendingConnection() if not self.tcpServerConnection: self.serverStatusLabel.setText( self.tr("Error: got invalid pending connection!") ) return self.tcpServerConnection.readyRead.connect(self.updateServerProgress) self.tcpServerConnection.error.connect(self.displayError) self.tcpServerConnection.disconnected.connect( self.tcpServerConnection.deleteLater ) self.serverStatusLabel.setText(self.tr("Accepted connection")) self.tcpServer.close() @Slot() def startTransfer(self): # called when the TCP client connected to the loopback server self.bytesToWrite = TOTAL_BYTES - int( self.tcpClient.write(QByteArray(PAYLOAD_SIZE, "@")) ) self.clientStatusLabel.setText(self.tr("Connected")) @Slot() def updateServerProgress(self): self.bytesReceived += int(self.tcpServerConnection.bytesAvailable()) self.tcpServerConnection.readAll() self.serverProgressBar.setMaximum(TOTAL_BYTES) self.serverProgressBar.setValue(self.bytesReceived) self.serverStatusLabel.setText( self.tr("Received %dMB" % (self.bytesReceived / (1024 * 1024),)) ) if self.bytesReceived == TOTAL_BYTES: self.tcpServerConnection.close() self.startButton.setEnabled(True) QGuiApplication.restoreOverrideCursor() @Slot("qint64") def updateClientProgress(self, numBytes): self.bytesWritten += int(numBytes) if self.bytesToWrite > 0 and self.tcpClient.bytesToWrite() <= 4 * PAYLOAD_SIZE: self.bytesToWrite -= self.tcpClient.write( QByteArray(min(self.bytesToWrite, PAYLOAD_SIZE), "@") ) self.clientProgressBar.setMaximum(TOTAL_BYTES) self.clientProgressBar.setValue(self.bytesWritten) self.clientStatusLabel.setText( self.tr("Sent %dMB" % (self.bytesWritten / (1024 * 1024),)) ) @Slot(QAbstractSocket.SocketError) def displayError(self, socketError): if socketError == QTcpSocket.RemoteHostClosedError: return QMessageBox.information( self, self.tr("Network error"), self.tr( "The following error occurred: {}.".format(self.tcpClient.errorString()) ), ) self.tcpClient.close() self.tcpServer.close() self.clientProgressBar.reset() self.serverProgressBar.reset() self.clientStatusLabel.setText(self.tr("Client ready")) self.serverStatusLabel.setText(self.tr("Server ready")) self.startButton.setEnabled(True) QGuiApplication.restoreOverrideCursor()
def _build_ui(self): layout = QGridLayout() library_tabs = QTabWidget() #ligand tab ligand_tab = QWidget() ligand_layout = QFormLayout(ligand_tab) self.ligand_name = QLineEdit() self.ligand_name.setText("") self.ligand_name.setPlaceholderText("leave blank to preview") self.ligand_name.setToolTip( "name of ligand you are adding to your ligand library\nleave blank to open a new model with just the ligand" ) ligand_layout.addRow("ligand name:", self.ligand_name) ligand_key_atoms = QPushButton("set key atoms to current selection") ligand_key_atoms.clicked.connect(self.update_key_atoms) ligand_key_atoms.setToolTip( "the current selection will be the key atoms for the ligand\nleave blank to automatically determine key atoms" ) ligand_layout.addRow(ligand_key_atoms) libadd_ligand = QPushButton("add current selection to library") libadd_ligand.clicked.connect(self.libadd_ligand) ligand_layout.addRow(libadd_ligand) #substituent tab sub_tab = QWidget() sub_layout = QFormLayout(sub_tab) self.sub_name = QLineEdit() self.sub_name.setText("") self.sub_name.setPlaceholderText("leave blank to preview") self.sub_name.setToolTip( "name of substituent you are adding to your substituent library\nleave blank to open a new model with just the substituent" ) sub_layout.addRow("substituent name:", self.sub_name) self.sub_confs = QSpinBox() self.sub_confs.setMinimum(1) sub_layout.addRow("number of conformers:", self.sub_confs) self.sub_angle = QSpinBox() self.sub_angle.setRange(0, 180) self.sub_angle.setSingleStep(30) sub_layout.addRow("angle between conformers:", self.sub_angle) libadd_sub = QPushButton("add current selection to library") libadd_sub.clicked.connect(self.libadd_substituent) sub_layout.addRow(libadd_sub) #ring tab ring_tab = QWidget() ring_layout = QFormLayout(ring_tab) self.ring_name = QLineEdit() self.ring_name.setText("") self.ring_name.setPlaceholderText("leave blank to preview") self.ring_name.setToolTip( "name of ring you are adding to your ring library\nleave blank to open a new model with just the ring" ) ring_layout.addRow("ring name:", self.ring_name) libadd_ring = QPushButton("add ring with selected walk to library") libadd_ring.clicked.connect(self.libadd_ring) ring_layout.addRow(libadd_ring) library_tabs.addTab(sub_tab, "substituent") library_tabs.addTab(ring_tab, "ring") library_tabs.addTab(ligand_tab, "ligand") self.library_tabs = library_tabs layout.addWidget(library_tabs) whats_this = QLabel() whats_this.setText( "<a href=\"req\" style=\"text-decoration: none;\">what's this?</a>" ) whats_this.setTextFormat(Qt.RichText) whats_this.setTextInteractionFlags(Qt.TextBrowserInteraction) whats_this.linkActivated.connect(self.open_link) whats_this.setToolTip( "click for more information about AaronTools libraries") layout.addWidget(whats_this) self.tool_window.ui_area.setLayout(layout) self.tool_window.manage(None)
def set_thermo(self): """computes thermo corrections and sets thermo entries for when thermo model changes""" #index of combobox is -1 when combobox has no entries self.thermo_table.setRowCount(0) if self.thermo_selector.currentIndex() >= 0: fr = self.thermo_selector.currentData() if fr not in self.thermo_co: self.thermo_co[fr] = CompOutput(fr) co = self.thermo_co[fr] v0 = self.v0_edit.value() if v0 != self.settings.w0: self.settings.w0 = v0 T = self.temperature_line.value() if not T: return dZPE = co.ZPVE #compute enthalpy and entropy at this temperature #AaronTools uses Grimme's Quasi-RRHO, but this is the same as RRHO when w0=0 dE, dH, s = co.therm_corr(temperature=T, v0=0, method="RRHO") rrho_dg = dH - T * s #compute G with quasi entropy treatments qrrho_dg = co.calc_G_corr(v0=v0, temperature=T, method="QRRHO") qharm_dg = co.calc_G_corr(v0=v0, temperature=T, method="QHARM") items = [( "𝛿ZPE =", dZPE, None, "lowest energy the molecule can have\n" "no rotational or vibrational modes populated\n" "equal to enthalpy at 0 K", )] if fr.other["frequency"].anharm_data: dZPE_anh = co.calc_zpe(anharmonic=True) items.append(( "𝛿ZPE<sub>anh</sub> =", dZPE_anh, None, "lowest energy the molecule can have\n" "no rotational or vibrational modes populated\n" "includes corrections for anharmonic vibrations", )) items.extend([ ( "𝛿H<sub>RRHO</sub> =", dH, None, "enthalpy of formation", ), ( "𝛿G<sub>RRHO</sub> =", rrho_dg, None, "energy after taking into account the average\n" "population of vibrational, rotational, and translational\n" "degrees of freedom", ), ( "𝛿G<sub>Quasi-RRHO</sub> =", qrrho_dg, "Grimme's Quasi-RRHO", "vibrational entropy of each real mode is damped and complemented\n" "with rotational entropy, with the damping function being stronger for\n" "frequencies < 𝜔\u2080\n" "can mitigate error from inaccuracies in the harmonic oscillator\n" "approximation for low-frequency vibrations", ), ( "𝛿G<sub>Quasi-Harmonic</sub> =", qharm_dg, "Truhlar's Quasi-Harmonic", "real vibrational frequencies below 𝜔\u2080 are treated as 𝜔\u2080\n" "can mitigate error from inaccuracies in the harmonic oscillator\n" "approximation for low-frequency vibrations", ), ]) for i, (label_text, val, link, tooltip) in enumerate(items): self.thermo_table.insertRow(i) label = QLabel(label_text) if link: label = QLabel() label.setText( "<a href=\"%s\" style=\"text-decoration: none;\">%s</a>" % (link, label_text)) label.setTextFormat(Qt.RichText) label.setTextInteractionFlags(Qt.TextBrowserInteraction) label.linkActivated.connect(self.open_link) label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) label.setToolTip(tooltip) self.thermo_table.setCellWidget(i, 0, label) unit_label = ReadOnlyTableItem() unit_label.setData(Qt.DisplayRole, "E\u2095") unit_label.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter) unit_label.setToolTip(tooltip) self.thermo_table.setItem(i, 2, unit_label) d_nrg = SmallLineEdit("%.6f" % val) d_nrg.setReadOnly(True) d_nrg.setFrame(False) d_nrg.setToolTip(tooltip) self.thermo_table.setCellWidget(i, 1, d_nrg) self.thermo_table.resizeRowToContents(i) self.thermo_table.resizeColumnToContents(0) self.thermo_table.resizeColumnToContents(1) self.thermo_table.resizeColumnToContents(2) self.update_sum()
class Client(QDialog): def __init__(self, parent: QWidget = None): super().__init__(parent) self._in = QDataStream() self.blockSize = 0 self.currentFortune = "" self.hostLineEdit = QLineEdit("fortune") self.getFortuneButton = QPushButton(self.tr("Get Fortune")) self.statusLabel = QLabel( self.tr( "This examples requires that you run the Local Fortune Server example as well." ) ) self.socket = QLocalSocket() self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) hostLabel = QLabel(self.tr("&Server name:")) hostLabel.setBuddy(self.hostLineEdit) self.statusLabel.setWordWrap(True) self.getFortuneButton.setDefault(True) quitButton = QPushButton(self.tr("Quit")) buttonBox = QDialogButtonBox() buttonBox.addButton(self.getFortuneButton, QDialogButtonBox.ActionRole) buttonBox.addButton(quitButton, QDialogButtonBox.RejectRole) self._in.setDevice(self.socket) self._in.setVersion(QDataStream.Qt_5_10) self.hostLineEdit.textChanged.connect(self.enableGetFortuneButton) self.getFortuneButton.clicked.connect(self.requestNewFortune) quitButton.clicked.connect(self.close) self.socket.readyRead.connect(self.readFortune) self.socket.errorOccurred.connect(self.displayError) mainLayout = QGridLayout(self) mainLayout.addWidget(hostLabel, 0, 0) mainLayout.addWidget(self.hostLineEdit, 0, 1) mainLayout.addWidget(self.statusLabel, 2, 0, 1, 2) mainLayout.addWidget(buttonBox, 3, 0, 1, 2) self.setWindowTitle(QGuiApplication.applicationDisplayName()) self.hostLineEdit.setFocus() @Slot() def requestNewFortune(self): self.getFortuneButton.setEnabled(False) self.blockSize = 0 self.socket.abort() self.socket.connectToServer(self.hostLineEdit.text()) @Slot() def readFortune(self): if self.blockSize == 0: # Relies on the fact that QDataStream serializes a quint32 into # sizeof(quint32) bytes if self.socket.bytesAvailable() < 4: # (int)sizeof(quint32)) return self.blockSize = self._in.readUInt32() if self.socket.bytesAvailable() < self.blockSize or self._in.atEnd(): return nextFortune = "" nextFortune = self._in.readQString() if nextFortune == self.currentFortune: QTimer.singleShot(0, self.requestNewFortune) return currentFortune = nextFortune self.statusLabel.setText(currentFortune) self.getFortuneButton.setEnabled(True) @Slot(QLocalSocket.LocalSocketError) def displayError(self, socketError): if socketError == QLocalSocket.ServerNotFoundError: QMessageBox.information( self, self.tr("Local Fortune Client"), self.tr( "The host was not found. Please make sure " "that the server is running and that the " "server name is correct." ), ) elif socketError == QLocalSocket.ConnectionRefusedError: QMessageBox.information( self, self.tr("Local Fortune Client"), self.tr( "The connection was refused by the peer. " "Make sure the fortune server is running, " "and check that the server name is correct." ), ) elif socketError == QLocalSocket.PeerClosedError: return else: QMessageBox.information( self, self.tr("Local Fortune Client"), self.tr( "The following error occurred: %s." % (self.socket.errorString()) ), ) self.getFortuneButton.setEnabled(True) @Slot() def enableGetFortuneButton(self): self.getFortuneButton.setEnabled(bool(self.hostLineEdit.ext()))
class NotificationPopup(QWidget): def __init__(self, parent=None): super().__init__(parent) self.m_icon = QLabel() self.m_title = QLabel() self.m_message = QLabel() self.notification = None self.setWindowFlags(Qt.ToolTip) rootLayout = QHBoxLayout(self) rootLayout.addWidget(self.m_icon) bodyLayout = QVBoxLayout() rootLayout.addLayout(bodyLayout) titleLayout = QHBoxLayout() bodyLayout.addLayout(titleLayout) titleLayout.addWidget(self.m_title) titleLayout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding)) close = QPushButton(self.tr("Close")) titleLayout.addWidget(close) close.clicked.connect(self.onClosed) bodyLayout.addWidget(self.m_message) self.adjustSize() def present(self, newNotification): if self.notification: self.notification.close() sip.delete(self.notification) self.notification = None self.notification = newNotification self.m_title.setText(f"<b>{self.notification.title()}</b>") self.m_message.setText(self.notification.message()) self.m_icon.setPixmap( QPixmap.fromImage(self.notification.icon()).scaledToHeight( self.m_icon.height())) self.show() self.notification.show() self.notification.closed.connect(self.onClosed) QTimer.singleShot( 10000, lambda: self.onClosed() if self.notification is not None else None) # position our popup in the right corner of its parent widget self.move(self.parentWidget().mapToGlobal( self.parentWidget().rect().bottomRight() - QPoint(self.width() + 10, self.height() + 10))) @Slot() def onClosed(self): self.hide() self.notification.close() self.notification = None def mouseReleaseEvent(self, event): super().mouseReleaseEvent(event) if self.notification and event.button() == Qt.LeftButton: self.notification.click() self.onClosed()
class VariableBase(QWidget): ## executed when value been set valueChanged = QtCore.Signal() ## executed when name been changed nameChanged = QtCore.Signal(str) ## executed when variable been killed killed = QtCore.Signal() ## executed when variable data type been changed dataTypeChanged = QtCore.Signal(int) ## executed when variable access level been changed accessLevelChanged = QtCore.Signal(int) def __init__(self, name, value, graph, varsListWidget, dataType=DataTypes.Bool, uid=None): super(VariableBase, self).__init__() self._accessLevel = AccessLevel.public # ui self.horizontalLayout = QHBoxLayout(self) self.horizontalLayout.setSpacing(1) self.horizontalLayout.setContentsMargins(1, 1, 1, 1) self.horizontalLayout.setObjectName("horizontalLayout") self.widget = TypeWidget(Colors.Bool, self) self.widget.setObjectName("widget") self.horizontalLayout.addWidget(self.widget) self.labelName = QLabel(self) self.labelName.setObjectName("labelName") self.horizontalLayout.addWidget(self.labelName, 1) spacerItem = QSpacerItem(1, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem) QtCore.QMetaObject.connectSlotsByName(self) # body self.varsListWidget = varsListWidget self.name = None self._value = value self.dataType = dataType self._uid = uid if self._uid is None: self._uid = uuid4() self.graph = graph self.setName(name) self.types = [ v.name for v in list(DataTypes) if v not in [DataTypes.Reference, DataTypes.Exec, DataTypes.Enum] ] self.graph.vars[self.uid] = self @property def accessLevel(self): return self._accessLevel @accessLevel.setter def accessLevel(self, value): self._accessLevel = value self.accessLevelChanged.emit(value) @property def uid(self): return self._uid @uid.setter def uid(self, value): self._uid = value if self._uid in self.graph.vars: self.graph.vars.pop(self._uid) self.graph.vars[self._uid] = self @staticmethod def jsonTemplate(): template = {'name': None, 'uuid': None, 'value': None, 'type': None} return template def serialize(self): template = VariableBase.jsonTemplate() template['name'] = self.name template['uuid'] = str(self.uid) template['value'] = self.value template['type'] = int(self.dataType) template['accessLevel'] = int(self.accessLevel) return template @staticmethod def deserialize(data, graph): var = graph.parent.variablesWidget.createVariable( uuid.UUID(data['uuid'])) var.setName(data['name']) var.setDataType(data['type']) var.value = data['value'] var.accessLevel = data['accessLevel'] return var @property def value(self): return self._value @value.setter def value(self, data): self._value = data self.valueChanged.emit() ## Changes variable data type and updates [TypeWidget](@ref PyFlow.Core.Variable.TypeWidget) color # @bug in the end of this method we clear undo stack, but we should not. We do this because undo redo goes crazy def setDataType(self, dataType, _bJustSpawned=False): self.dataType = dataType #print self.dataType try: self.widget.color = Pins.findPinClassByType(self.dataType).color() self.value = Pins.findPinClassByType( self.dataType).pinDataTypeHint()[1] except: pass self.widget.update() if _bJustSpawned: return self.dataTypeChanged.emit(self.dataType) self.graph.undoStack.clear() self.graph.tryFillPropertiesView(self) def mousePressEvent(self, event): QWidget.mousePressEvent(self, event) self.graph.tryFillPropertiesView(self) def setName(self, name): self.labelName.setText(name) self.name = name self.nameChanged.emit(str(name)) def onUpdatePropertyView(self, formLayout): # name le_name = QLineEdit(self.name) le_name.returnPressed.connect(lambda: self.setName(le_name.text())) formLayout.addRow("Name", le_name) # data type cbTypes = VarTypeComboBox(self) formLayout.addRow("Type", cbTypes) # current value def valSetter(x): self.value = x w = InputWidgets.getInputWidget( self.dataType, valSetter, Pins.getPinDefaultValueByType(self.dataType), None) if w: w.setWidgetValue(self.value) w.setObjectName(self.name) formLayout.addRow(self.name, w) # access level cb = QComboBox() cb.addItem('public', 0) cb.addItem('private', 1) cb.addItem('protected', 2) def accessLevelChanged(x): self.accessLevel = x cb.currentIndexChanged[int].connect(accessLevelChanged) cb.setCurrentIndex(self.accessLevel) formLayout.addRow('Access level', cb)
class UIVariable(QWidget, IPropertiesViewSupport): def __init__(self, rawVariable, variablesWidget, parent=None): super(UIVariable, self).__init__(parent) self._rawVariable = rawVariable self.variablesWidget = variablesWidget # ui self.horizontalLayout = QHBoxLayout(self) self.horizontalLayout.setSpacing(1) self.horizontalLayout.setContentsMargins(1, 1, 1, 1) self.horizontalLayout.setObjectName("horizontalLayout") self.widget = TypeWidget( findPinClassByType(self._rawVariable.dataType).color(), self) self.widget.setObjectName("widget") self.horizontalLayout.addWidget(self.widget) self.labelName = QLabel(self) self.labelName.setObjectName("labelName") self.horizontalLayout.addWidget(self.labelName) spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem) # find refs self.pbFindRefs = QPushButton("") self.pbFindRefs.setIcon( QtGui.QIcon(RESOURCES_DIR + "/searching-magnifying-glass.png")) self.pbFindRefs.setObjectName("pbFindRefs") self.horizontalLayout.addWidget(self.pbFindRefs) self.pbFindRefs.clicked.connect(self.onFindRefsClicked) # kill variable self.pbKill = QPushButton("") self.pbKill.setIcon(QtGui.QIcon(RESOURCES_DIR + "/delete_icon.png")) self.pbKill.setObjectName("pbKill") self.horizontalLayout.addWidget(self.pbKill) self.pbKill.clicked.connect(self.onKillClicked) QtCore.QMetaObject.connectSlotsByName(self) self.setName(self._rawVariable.name) def createPropertiesWidget(self, propertiesWidget): baseCategory = CollapsibleFormWidget(headName="Base") # name le_name = QLineEdit(self._rawVariable.name) le_name.returnPressed.connect(lambda: self.setName(le_name.text())) baseCategory.addWidget("Name", le_name) # data type cbTypes = VarTypeComboBox(self) baseCategory.addWidget("Type", cbTypes) propertiesWidget.addWidget(baseCategory) valueCategory = CollapsibleFormWidget(headName="Value") # current value def valSetter(x): self._rawVariable.value = x w = createInputWidget( self._rawVariable.dataType, valSetter, getPinDefaultValueByType(self._rawVariable.dataType)) if w: w.setWidgetValue(self._rawVariable.value) w.setObjectName(self._rawVariable.name) valueCategory.addWidget(self._rawVariable.name, w) # access level cb = QComboBox() cb.addItem('public', 0) cb.addItem('private', 1) cb.addItem('protected', 2) def accessLevelChanged(x): self._rawVariable.accessLevel = AccessLevel[x] cb.currentTextChanged.connect(accessLevelChanged) cb.setCurrentIndex(self._rawVariable.accessLevel) valueCategory.addWidget('Access level', cb) propertiesWidget.addWidget(valueCategory) def onFindRefsClicked(self): refs = self._rawVariable.findRefs() print(refs) def onKillClicked(self): # check refs and ask user what to do refs = self._rawVariable.findRefs() if len(refs) > 0: item, accepted = QInputDialog.getItem( None, 'Decide!', 'What to do with getters and setters in canvas?', ['kill', 'leave'], editable=False) if accepted: if item == 'kill': self.variablesWidget.killVar(self) elif item == 'leave': # mark node as invalid # TODO: For future. Like in ue4, if variable is removed, it can be recreated from node (e.g. promote to variable) print('leave') else: self.variablesWidget.killVar(self) @property def dataType(self): return self._rawVariable.dataType @dataType.setter def dataType(self, value): self._rawVariable.dataType = value @property def packageName(self): return self._rawVariable.packageName @property def accessLevel(self): return self._rawVariable.accessLevel @accessLevel.setter def accessLevel(self, value): self._rawVariable.accessLevel = value @property def uid(self): return self._rawVariable.uid @uid.setter def uid(self, value): self._rawVariable.uid = value if self._rawVariable.uid in self.graph.vars: self.graph.vars.pop(self._rawVariable.uid) self.graph.vars[self._rawVariable.uid] = self._rawVariable @staticmethod def jsonTemplate(): template = { 'name': None, 'uuid': None, 'value': None, 'type': None, 'package': None, 'accessLevel': None } return template def serialize(self): pinClass = findPinClassByType(self._rawVariable.dataType) template = UIVariable.jsonTemplate() template['name'] = self._rawVariable.name template['uuid'] = str(self._rawVariable.uid) if self._rawVariable.dataType == "AnyPin": # don't save any variables # value will be calculated for this type of variables template['value'] = None else: template['value'] = json.dumps(self._rawVariable.value, cls=pinClass.jsonEncoderClass()) template['type'] = self._rawVariable.dataType template['package'] = self._rawVariable.packageName template['accessLevel'] = self._rawVariable.accessLevel.value return template @staticmethod def deserialize(data, graph): pinClass = findPinClassByType(data['dataType']) varUid = uuid.UUID(data['uuid']) # TODO: this is probably bad. Too long call chain var = graph.parent.variablesWidget.createVariable( dataType=data['dataType'], accessLevel=AccessLevel(data['accessLevel']), uid=varUid) var.setName(data['name']) var.setDataType(data['dataType']) if data['dataType'] == 'AnyPin': var.value = getPinDefaultValueByType('AnyPin') else: var.value = json.loads(data['value'], cls=pinClass.jsonDecoderClass()) return var @property def value(self): return self._rawVariable.value @value.setter def value(self, data): self._rawVariable.value = data # Changes variable data type and updates [TypeWidget](@ref PyFlow.Core.Variable.TypeWidget) color # @bug in the end of this method we clear undo stack, but we should not. We do this because undo redo goes crazy def setDataType(self, dataType, _bJustSpawned=False): self._rawVariable.dataType = dataType self.widget.color = findPinClassByType(self.dataType).color() self.widget.update() if _bJustSpawned: return self.variablesWidget.onUpdatePropertyView(self) def mousePressEvent(self, event): super(UIVariable, self).mousePressEvent(event) self.variablesWidget.onUpdatePropertyView(self) def setName(self, name): self._rawVariable.name = name self.labelName.setText(self._rawVariable.name)
class Second(QMainWindow): def __init__(self): super(Second, self).__init__() self.setWindowTitle("Add new device") self.nameLabel = QLabel(self) self.nameLabel.move(10, 10) self.nameLabel.setText("Device name") self.nameEntry = QLineEdit(self) self.nameEntry.move(10, 40) self.nameEntry.resize(100, 30) self.colorLabel = QLabel(self) self.colorLabel.move(120, 10) self.colorLabel.setText("Color cameras") self.colorEntry = QLineEdit(self) self.colorEntry.move(140, 40) self.colorEntry.resize(70, 30) self.colorEntry.setValidator(QtGui.QIntValidator()) self.monoLabel = QLabel(self) self.monoLabel.move(230, 10) self.monoLabel.setText("Mono cameras") self.monoEntry = QLineEdit(self) self.monoEntry.move(250, 40) self.monoEntry.resize(70, 30) self.monoEntry.setValidator(QtGui.QIntValidator()) self.depthPresent = QCheckBox("Include depth", self) self.depthPresent.move(10, 80) self.depthPresent.resize(150, 30) self.depthPresent.stateChanged.connect(self.toggle_depth) self.leftfovLabel = QLabel(self) self.leftfovLabel.move(10, 120) self.leftfovLabel.setText("Left FOV deg.") self.leftfovEntry = QLineEdit(self) self.leftfovEntry.move(180, 120) self.leftfovEntry.resize(140, 30) self.leftfovEntry.setValidator(QtGui.QDoubleValidator()) self.rightfovLabel = QLabel(self) self.rightfovLabel.move(10, 160) self.rightfovLabel.setText("Right FOV deg.") self.rightfovEntry = QLineEdit(self) self.rightfovEntry.move(180, 160) self.rightfovEntry.resize(140, 30) self.rightfovEntry.setValidator(QtGui.QDoubleValidator()) self.rgbfovLabel = QLabel(self) self.rgbfovLabel.move(10, 200) self.rgbfovLabel.setText("RGB FOV deg.") self.rgbfovEntry = QLineEdit(self) self.rgbfovEntry.move(180, 200) self.rgbfovEntry.resize(140, 30) self.rgbfovEntry.setValidator(QtGui.QDoubleValidator()) self.lrdistanceLabel = QLabel(self) self.lrdistanceLabel.move(10, 240) self.lrdistanceLabel.resize(200, 30) self.lrdistanceLabel.setText("Left - Right distance cm.") self.lrdistanceEntry = QLineEdit(self) self.lrdistanceEntry.move(180, 240) self.lrdistanceEntry.resize(140, 30) self.lrdistanceEntry.setValidator(QtGui.QDoubleValidator()) self.lrgbdistanceLabel = QLabel(self) self.lrgbdistanceLabel.move(10, 280) self.lrgbdistanceLabel.resize(200, 30) self.lrgbdistanceLabel.setText("Left - RGB distance cm.") self.lrgbdistanceEntry = QLineEdit(self) self.lrgbdistanceEntry.move(180, 280) self.lrgbdistanceEntry.resize(140, 30) self.lrgbdistanceEntry.setValidator(QtGui.QDoubleValidator()) self.saveButton = QPushButton("Save", self) self.saveButton.resize(100, 30) self.saveButton.clicked.connect(self.save) self.cancelButton = QPushButton("Cancel", self) self.cancelButton.resize(100, 30) self.cancelButton.clicked.connect(self.cancel) self.toggle_depth(False) def toggle_depth(self, checked): if checked: self.leftfovLabel.setVisible(True) self.leftfovEntry.setVisible(True) self.rightfovLabel.setVisible(True) self.rightfovEntry.setVisible(True) self.rgbfovLabel.setVisible(True) self.rgbfovEntry.setVisible(True) self.lrdistanceLabel.setVisible(True) self.lrdistanceEntry.setVisible(True) self.lrgbdistanceLabel.setVisible(True) self.lrgbdistanceEntry.setVisible(True) self.saveButton.move(200, 330) self.cancelButton.move(30, 330) self.resize(330, 380) else: self.leftfovLabel.setVisible(False) self.leftfovEntry.setVisible(False) self.rightfovLabel.setVisible(False) self.rightfovEntry.setVisible(False) self.rgbfovLabel.setVisible(False) self.rgbfovEntry.setVisible(False) self.lrdistanceLabel.setVisible(False) self.lrdistanceEntry.setVisible(False) self.lrgbdistanceLabel.setVisible(False) self.lrgbdistanceEntry.setVisible(False) self.saveButton.move(200, 120) self.cancelButton.move(30, 120) self.resize(330, 170) def save(self, *args, **kwargs): try: data = { "name": self.nameEntry.text(), "color_count": int(self.colorEntry.text()), "mono_count": int(self.monoEntry.text()), } if self.depthPresent.isChecked(): data.update({ "depth": True, "left_fov_deg": float(self.leftfovEntry.text()), "right_fov_deg": float(self.rightfovEntry.text()), "rgb_fov_deg": float(self.rgbfovEntry.text()), "left_to_right_distance_cm": float(self.lrdistanceEntry.text()), "left_to_rgb_distance_cm": float(self.lrgbdistanceEntry.text()), }) append_to_json( data, Path(__file__).parent.parent / Path('custom_devices.json')) self.close() INITIALIZE() self.instance.getRegisteredTools(['NodeBoxTool'])[0].refresh() except Exception as e: QMessageBox.warning(self, "Warning", str(e)) def cancel(self): self.close()
class CategoryWidget(base.BaseWidget, object): doRefresh = Signal(object) doPreview = Signal(list, bool) doRename = Signal() togglePreview = Signal() def __init__(self, types, nodes_to_discard=None, parent=None): self._types = types self._default_nodes_to_discard = nodes_to_discard or list() self._category_buttons = list() super(CategoryWidget, self).__init__(parent=parent) @property def types(self): return self._types @property def nodes_to_discard(self): return self._default_nodes_to_discard @nodes_to_discard.setter def nodes_to_discard(self, nodes_to_discard): self._default_nodes_to_discard = nodes_to_discard def ui(self): super(CategoryWidget, self).ui() filter_layout = layouts.HorizontalLayout(spacing=2, margins=(10, 0, 10, 0)) self.main_layout.addLayout(filter_layout) refresh_icon = resources.icon('refresh') self._refresh_list_btn = buttons.IconButton( icon=refresh_icon, icon_padding=2, button_style=buttons.ButtonStyles.FlatStyle) self._names_filter = search.SearchFindWidget() self._search_lbl = QLabel('0 found') filter_layout.addWidget(self._refresh_list_btn) filter_layout.addWidget(self._names_filter) filter_layout.addWidget(self._search_lbl) self._types_layout = layouts.HorizontalLayout(spacing=2, margins=(0, 0, 0, 0)) self.main_layout.addLayout(self._types_layout) self._names_list = QTreeWidget(self) self._names_list.setHeaderHidden(True) self._names_list.setSortingEnabled(False) self._names_list.setRootIsDecorated(False) self._names_list.setSelectionMode(QAbstractItemView.ExtendedSelection) self._names_list.sortByColumn(0, Qt.AscendingOrder) self._names_list.setUniformRowHeights(True) self._names_list.setAlternatingRowColors(True) self.main_layout.addWidget(self._names_list) bottom_buttons_layout = layouts.HorizontalLayout(spacing=2, margins=(2, 2, 2, 2)) bottom_buttons_layout.setAlignment(Qt.AlignLeft) self.main_layout.addLayout(bottom_buttons_layout) preview_icon = resources.icon('preview') self._sort_btn = buttons.BaseButton('Sort', parent=self) self._sort_btn.setMinimumWidth(40) self._all_btn = buttons.BaseButton('All', parent=self) self._all_btn.setMinimumWidth(40) self._none_btn = buttons.BaseButton('None', parent=self) self._none_btn.setMinimumWidth(40) bottom_buttons_layout.addWidget(self._sort_btn) bottom_buttons_layout.addWidget(self._all_btn) bottom_buttons_layout.addWidget(self._none_btn) bottom_buttons_layout.addWidget( dividers.get_horizontal_separator_widget()) self._hide_default_scene_nodes_cbx = checkbox.BaseCheckBox( 'Hide Default Scene Objects', parent=self) self._hide_default_scene_nodes_cbx.setChecked(True) bottom_buttons_layout.addWidget(self._hide_default_scene_nodes_cbx) self.main_layout.addLayout(dividers.DividerLayout()) preview_layout = layouts.HorizontalLayout(spacing=2, margins=(0, 0, 0, 0)) self.main_layout.addLayout(preview_layout) self._preview_btn = buttons.BaseButton('Preview', parent=self) self._preview_btn.setIcon(preview_icon) self._preview_btn.setCheckable(True) self._preview_btn.setChecked(True) self._preview_btn.setMinimumWidth(100) self._preview_btn.setMaximumWidth(100) self._rename_btn = buttons.BaseButton( 'Select objects in the list to rename ...', parent=self) self._rename_btn.setEnabled(False) preview_layout.addWidget(self._preview_btn) preview_layout.addWidget(self._rename_btn) self._setup_types() self.refresh() def setup_signals(self): self._refresh_list_btn.clicked.connect(self._on_refresh_list) self._names_filter.textChanged.connect(self._on_filter_names_changed) self._hide_default_scene_nodes_cbx.toggled.connect( self._on_toggle_hide_default_scene_nodes_cbx) self._names_list.itemSelectionChanged.connect( self._on_item_selection_changed) self._none_btn.clicked.connect(self._on_select_none_clicked) self._all_btn.clicked.connect(self._on_select_all_clicked) self._preview_btn.toggled.connect(self._on_toggle_preview) self._rename_btn.clicked.connect(self.doRename.emit) def get_names_list(self): return self._names_list def is_preview_enabled(self): return self._preview_btn.isChecked() def refresh(self, selected_objects=False, hierarchy=False): self._names_list.clear() self._names_list.setSortingEnabled(True) try: objs_names = list() if not selected_objects: objs_names.extend(dcc.all_scene_nodes(full_path=True)) else: objs_names.extend(dcc.selected_nodes(full_path=True)) if objs_names and hierarchy: children_list = list() for obj in objs_names: children = dcc.list_children(obj, all_hierarchy=True, full_path=True) if children: children_list.extend(children) children_list = list(set(children_list)) objs_names.extend(children_list) self._update_names_list(objs_names) self._on_filter_names_changed(self._names_filter.get_text()) finally: self._names_list.setSortingEnabled(False) def _setup_types(self): for i, category_type in enumerate(self._types): for type_name, type_data in category_type.items(): dcc_type = type_data.get('type', None) dcc_fn = type_data.get('fn', None) dcc_args = type_data.get('args', dict()) type_btn = buttons.BaseButton(type_name, parent=self) type_btn.setCheckable(True) type_btn.setProperty('dcc_type', dcc_type) type_btn.setProperty('dcc_fn', dcc_fn) type_btn.setProperty('dcc_args', dcc_args) if i == 0: type_btn.setChecked(True) self._types_layout.addWidget(type_btn) self._category_buttons.append(type_btn) type_btn.toggled.connect(self._on_toggle_type) self._others_btn = buttons.BaseButton('Others', parent=self) self._others_btn.setCheckable(True) self._types_layout.addWidget(self._others_btn) self._others_btn.toggled.connect(self._on_toggle_type) def _get_nodes_to_discard(self): """ Internal function that returns list of nodes that should be discarded during renaming process """ discard_nodes = self._default_nodes_to_discard[:] or list() if self._hide_default_scene_nodes_cbx and self._hide_default_scene_nodes_cbx.isChecked( ): discard_nodes.extend(dcc.default_scene_nodes(full_path=False)) # discard_nodes.extend(dcc.list_nodes(node_type='camera')) for btn in self._category_buttons: if not btn.isChecked(): dcc_type = btn.property('dcc_type') if dcc_type: discard_nodes.extend( dcc.list_nodes(node_type=btn.property('dcc_type'))) else: dcc_fn = btn.property('dcc_fn') if dcc_fn: dcc_args = btn.property('dcc_args') if dcc.is_maya(): valid_args = dict() for arg_name, arg_value in dcc_args.items(): valid_args[str(arg_name)] = arg_value nodes = getattr(maya.cmds, dcc_fn)(**valid_args) discard_nodes.extend(nodes) return list(set(discard_nodes)) def _get_node_types(self): node_types = set() for btn in self._category_buttons: dcc_type = btn.property('dcc_type') if dcc_type: node_types.add(dcc_type) else: dcc_fn = btn.property('dcc_fn') if dcc_fn: dcc_args = btn.property('dcc_args') if dcc.is_maya(): valid_args = dict() for arg_name, arg_value in dcc_args.items(): valid_args[str(arg_name)] = arg_value nodes = getattr(maya.cmds, dcc_fn)(**valid_args) for node in nodes: node_type = dcc.node_type(node) node_types.add(node_type) return list(node_types) def _update_names_list(self, nodes): """ Internal function that updates names list with given node names :param nodes: list(str) """ nodes_to_discard = self._get_nodes_to_discard() or list() nodes = list(set(nodes)) for obj in nodes: if obj in nodes_to_discard: continue node_type = dcc.node_type(obj) if node_type not in self._get_node_types( ) and not self._others_btn.isChecked(): is_valid = False for node_type in self._get_node_types(): is_valid = dcc.check_object_type(obj, node_type, check_sub_types=True) if is_valid: break if not is_valid: continue node_name = dcc.node_short_name(obj) item = QTreeWidgetItem(self._names_list, [node_name]) item.obj = node_name item.preview_name = '' item.full_name = obj if dcc.is_maya(): sel = api.SelectionList() sel.add(obj) item.handle = maya.OpenMaya.MObjectHandle( sel.get_depend_node(0)) self._names_list.addTopLevelItem(item) def _on_filter_names_changed(self, filter_text): """ Internal callback function that is called each time the user enters text in the search line widget Shows or hides elements in the list taking in account the filter_text :param filter_text: str, current text """ nodes_found = 0 for i in range(self._names_list.topLevelItemCount()): item = self._names_list.topLevelItem(i) # item.setHidden(filter_text not in item.text(0)) if filter_text not in item.text(0): item.setHidden(True) else: item.setHidden(False) nodes_found += 1 if filter_text: self._search_lbl.setText('{} found'.format(nodes_found)) else: self._search_lbl.setText('0 found') def _on_item_selection_changed(self): """ Internal callback function that is triggered when the user selects an item in the names list """ selected_items = self._names_list.selectedItems() item_names = [item.obj for item in selected_items] if len(selected_items) > 0: self._rename_btn.setText('Rename') self._rename_btn.setEnabled(True) else: self._rename_btn.setText( 'Select objects in the list to rename ...') self._rename_btn.setEnabled(False) # We reset name to original value for i in range(self._names_list.topLevelItemCount()): item = self._names_list.topLevelItem(i) if item.text(0) in item_names: continue if hasattr(item, 'obj'): item.setText(0, item.obj) self.doPreview.emit(selected_items, self._preview_btn.isChecked()) def _on_select_all_clicked(self): for i in range(self._names_list.topLevelItemCount()): item = self._names_list.topLevelItem(i) item.setSelected(True) def _on_select_none_clicked(self): for i in range(self._names_list.topLevelItemCount()): item = self._names_list.topLevelItem(i) item.setSelected(False) def _on_refresh_list(self): self.doRefresh.emit(self) def _on_toggle_type(self, flag): self.doRefresh.emit(self) def _on_toggle_hide_default_scene_nodes_cbx(self, flag): self.doRefresh.emit(self) def _on_toggle_preview(self, flag): self.togglePreview.emit()
class RowTableWidget(QFrame): double_clicked = Signal(object) def __init__(self, auto_resize=False, single_row_select=True, context_menu_callback=None, last_column_stretch=True, has_counters=False, parent=None ): QFrame.__init__(self, parent) self._has_counters = has_counters self.model = None self.table_view = RowTableView(auto_resize, single_row_select, context_menu_callback, last_column_stretch) self.table_view.doubleClicked.connect(self._double_clicked) if has_counters: self.counters = Counters() self.counters.button_clicked.connect(self._counter_clicked) self.search_bar = QLineEdit() self.search_bar.setFixedHeight(SEARCHBAR_HEIGHT) self.search_bar.textChanged.connect(self.set_search_text) self.search_bar.setToolTip("Search bar") self.auto_size_button = QPushButton('<>') self.auto_size_button.setFixedSize(SEARCHBAR_HEIGHT, SEARCHBAR_HEIGHT) self.auto_size_button.clicked.connect(self._auto_size_clicked) self.auto_size_button.setToolTip("Auto size") self.status_label = QLabel(STATUS_LABEL_MESSAGE.format(0, 0)) self.status_label.setFixedWidth(STATUS_LABEL_WIDTH) self.progress_bar = QProgressBar() self.progress_bar.setFormat('') layout = QGridLayout() layout.addWidget(self.search_bar, 0, 0, 1, 3) layout.addWidget(self.auto_size_button, 0, 3) if has_counters: layout.addWidget(self.counters, 1, 0, 1, 2) layout.addWidget(self.table_view, 1, 2, 1, 2) else: layout.addWidget(self.table_view, 1, 0, 1, 4) layout.addWidget(self.status_label, 2, 0) layout.addWidget(self.progress_bar, 2, 1, 1, 3) layout.setColumnStretch(2, 100) self.setLayout(layout) def set_model(self, model): self.model = model self.table_view.setModel(model) model.modelReset.connect(self._set_progress_maximum) model.modelReset.connect(self._update_status) if self._has_counters: model.modelReset.connect(self._update_counters) self._update_counters() model.progress_updated.connect(self._update_progress) self._set_progress_maximum() self._update_status() self.progress_bar.setVisible(model.has_background_callback) @property def selected_rows(self): return [self.model.data(index, Qt.UserRole) for index in self.table_view.selectionModel().selectedIndexes() if index.column() == 0] @property def search_text(self): return self.search_bar.text() def set_search_text(self, text): self.search_bar.blockSignals(True) self.search_bar.setText(text) self.search_bar.blockSignals(False) self.model.set_search_text(text) def _set_progress_maximum(self): self.progress_bar.setMaximum(self.model.progress_max) # do better ? def _update_progress(self, value): self.progress_bar.setValue(value) def _update_status(self): self.status_label.setText(STATUS_LABEL_MESSAGE.format( self.model.rowCount(), self.model.total_row_count )) def _counter_clicked(self, entry, checked_buttons): entries = [entry for entry, active in checked_buttons.items() if active] self.model.set_search_counters(entries) def _update_counters(self): self.counters.set(self.model.counters) def _auto_size_clicked(self): with Hourglass(): self.table_view.resizeColumnsToContents() def _double_clicked(self, index): row = self.model.data(index, Qt.UserRole) self.double_clicked.emit(row) def state(self): header_sizes = list() header = self.table_view.horizontalHeader() for section_index in range(header.count()): header_sizes.append(header.sectionSize(section_index)) return { 'header_sizes': header_sizes, 'search_text': self.search_bar.text() } def load_state(self, state): header = self.table_view.horizontalHeader() for section_index, size in enumerate(state['header_sizes']): header.resizeSection(section_index, size) self.search_bar.setText(state['search_text'])
class UIVariable(QWidget, IPropertiesViewSupport): def __init__(self, rawVariable, variablesWidget, parent=None): super(UIVariable, self).__init__(parent) self._rawVariable = rawVariable self.variablesWidget = variablesWidget # ui self.horizontalLayout = QHBoxLayout(self) self.horizontalLayout.setSpacing(1) self.horizontalLayout.setContentsMargins(1, 1, 1, 1) self.horizontalLayout.setObjectName("horizontalLayout") self.widget = TypeWidget( findPinClassByType(self._rawVariable.dataType).color(), self) self.widget.setObjectName("widget") self.horizontalLayout.addWidget(self.widget) self.labelName = QLabel(self) self.labelName.setStyleSheet("background:transparent") self.labelName.setObjectName("labelName") self.horizontalLayout.addWidget(self.labelName) spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout.addItem(spacerItem) # find refs self.pbFindRefs = QPushButton("") self.pbFindRefs.setIcon( QtGui.QIcon(":/searching-magnifying-glass.png")) self.pbFindRefs.setObjectName("pbFindRefs") self.horizontalLayout.addWidget(self.pbFindRefs) self.pbFindRefs.clicked.connect(self.onFindRefsClicked) # kill variable self.pbKill = QPushButton("") self.pbKill.setIcon(QtGui.QIcon(":/delete_icon.png")) self.pbKill.setObjectName("pbKill") self.horizontalLayout.addWidget(self.pbKill) self.pbKill.clicked.connect(self.onKillClicked) QtCore.QMetaObject.connectSlotsByName(self) self.setName(self._rawVariable.name) self._rawVariable.setWrapper(self) def onStructureChanged(self, name): self._rawVariable.structure = PinStructure[name] self.variablesWidget.pyFlowInstance.onRequestFillProperties( self.createPropertiesWidget) EditorHistory().saveState("Change variable struct", modify=True) def setDataType(self, dataType): self.dataType = dataType self._rawVariable.dataType = dataType EditorHistory().saveState("Change variable data type", modify=True) def createPropertiesWidget(self, propertiesWidget): baseCategory = CollapsibleFormWidget(headName="Base", modify=True) # name le_name = QLineEdit(self._rawVariable.name) le_name.returnPressed.connect(lambda: self.setName(le_name.text())) baseCategory.addWidget("Name", le_name) # data type cbTypes = EnumComboBox([ pin.__name__ for pin in getAllPinClasses() if pin.IsValuePin() if pin.__name__ != "AnyPin" ]) cbTypes.setCurrentIndex(cbTypes.findText(self.dataType)) cbTypes.setEditable(False) cbTypes.changeCallback.connect(self.setDataType) propertiesWidget.addWidget(baseCategory) # structure type cbStructure = EnumComboBox( [i.name for i in (PinStructure.Single, PinStructure.Array)]) cbStructure.setEditable(False) cbStructure.setCurrentIndex( cbStructure.findText(self._rawVariable.structure.name)) cbStructure.changeCallback.connect(self.onStructureChanged) propertiesWidget.addWidget(baseCategory) baseCategory.addWidget("Type", cbTypes) baseCategory.addWidget("Structure", cbStructure) valueCategory = CollapsibleFormWidget(headName="Value") # current value if self._rawVariable.structure == PinStructure.Single: if not type(self._rawVariable.value) in {list, set, dict, tuple}: def valSetter(x): self._rawVariable.value = x w = createInputWidget( self._rawVariable.dataType, valSetter, getPinDefaultValueByType(self._rawVariable.dataType)) if w: w.setWidgetValue(self._rawVariable.value) w.setObjectName(self._rawVariable.name) valueCategory.addWidget(self._rawVariable.name, w) # access level cb = QComboBox() cb.addItem('public', 0) cb.addItem('private', 1) cb.addItem('protected', 2) def accessLevelChanged(x): self._rawVariable.accessLevel = AccessLevel[x] EditorHistory().saveState("Change variable access level", modify=True) cb.currentTextChanged.connect(accessLevelChanged) cb.setCurrentIndex(self._rawVariable.accessLevel) valueCategory.addWidget('Access level', cb) propertiesWidget.addWidget(valueCategory) def onFindRefsClicked(self): from PyFlow.App import PyFlow refs = [n.getWrapper() for n in self._rawVariable.findRefs()] app = self.variablesWidget.pyFlowInstance if "Search results" not in [ t.name() for t in app.getRegisteredTools() ]: app.invokeDockToolByName("PyFlowBase", "Search results") self.variablesWidget.pyFlowInstance.getCanvas( ).requestShowSearchResults.emit(refs) def onKillClicked(self): # check refs and ask user what to do refs = self._rawVariable.findRefs() if len(refs) > 0: item, accepted = QInputDialog.getItem( None, 'Decide!', 'What to do with getters and setters in canvas?', ['kill', 'leave'], editable=False) if accepted: self.variablesWidget.killVar(self) if item == 'kill': for i in refs: i.kill() elif item == 'leave': for i in refs: i.var = None else: self.variablesWidget.killVar(self) @property def dataType(self): return self._rawVariable.dataType @dataType.setter def dataType(self, value): self._rawVariable.dataType = value self.widget.color = findPinClassByType( self._rawVariable.dataType).color() self.widget.update() self.variablesWidget.onUpdatePropertyView(self) @property def packageName(self): return self._rawVariable.packageName @property def accessLevel(self): return self._rawVariable.accessLevel @accessLevel.setter def accessLevel(self, value): self._rawVariable.accessLevel = value @property def uid(self): return self._rawVariable.uid @uid.setter def uid(self, value): self._rawVariable.uid = value if self._rawVariable.uid in self.graph.getVars(): self.graph.getVars().pop(self._rawVariable.uid) self.graph.getVars()[self._rawVariable.uid] = self._rawVariable @staticmethod def jsonTemplate(): template = { 'name': None, 'uuid': None, 'value': None, 'type': None, 'package': None, 'accessLevel': None } return template def serialize(self): pinClass = findPinClassByType(self._rawVariable.dataType) template = UIVariable.jsonTemplate() template['name'] = self._rawVariable.name template['uuid'] = str(self._rawVariable.uid) if self._rawVariable.dataType == "AnyPin": # don't save any variables # value will be calculated for this type of variables template['value'] = None else: template['value'] = json.dumps(self._rawVariable.value, cls=pinClass.jsonEncoderClass()) template['type'] = self._rawVariable.dataType template['package'] = self._rawVariable.packageName template['accessLevel'] = self._rawVariable.accessLevel.value return template @staticmethod def deserialize(data, graph): pinClass = findPinClassByType(data['dataType']) varUid = uuid.UUID(data['uuid']) var = graph.getApp().variablesWidget.createVariable( dataType=data['dataType'], accessLevel=AccessLevel(data['accessLevel']), uid=varUid) var.setName(data['name']) var.setDataType(data['dataType']) if data['dataType'] == 'AnyPin': var.value = getPinDefaultValueByType('AnyPin') else: var.value = json.loads(data['value'], cls=pinClass.jsonDecoderClass()) return var @property def value(self): return self._rawVariable.value @value.setter def value(self, data): self._rawVariable.value = data def mousePressEvent(self, event): super(UIVariable, self).mousePressEvent(event) self.variablesWidget.onUpdatePropertyView(self) def setName(self, name): self._rawVariable.name = name self.labelName.setText(self._rawVariable.name)
class BaseSaveWidget(base.BaseWidget, object): def __init__(self, item, settings, temp_path=None, parent=None): # self._item = None self._settings = settings self._temp_path = temp_path self._options_widget = None # super(BaseSaveWidget, self).__init__(parent=parent) # # self.setObjectName('LibrarySaveWidget') # self.set_item(item) def ui(self): super(BaseSaveWidget, self).ui() title_layout = layouts.HorizontalLayout() title_layout.setContentsMargins(2, 2, 0, 0) title_layout.setSpacing(2) self._icon_lbl = QLabel() self._icon_lbl.setMaximumSize(QSize(14, 14)) self._icon_lbl.setMinimumSize(QSize(14, 14)) self._icon_lbl.setScaledContents(True) self._title_lbl = QLabel() title_layout.addWidget(self._icon_lbl) title_layout.addWidget(self._title_lbl) self._folder_widget = directory.SelectFolder('Folder', use_app_browser=True) buttons_layout = layouts.HorizontalLayout() buttons_layout.setContentsMargins(4, 4, 4, 4) buttons_layout.setSpacing(4) buttons_frame = QFrame() buttons_frame.setFrameShape(QFrame.NoFrame) buttons_frame.setFrameShadow(QFrame.Plain) buttons_frame.setLayout(buttons_layout) buttons_layout.addStretch() self.save_btn = buttons.BaseButton('Save') self.cancel_btn = buttons.BaseButton('Cancel') buttons_layout.addWidget(self.save_btn, parent=self) buttons_layout.addWidget(self.cancel_btn, parent=self) buttons_layout.addStretch() self._options_layout = layouts.VerticalLayout() self._options_layout.setContentsMargins(0, 0, 0, 0) self._options_layout.setSpacing(2) self._options_frame = QFrame() self._options_frame.setFrameShape(QFrame.NoFrame) self._options_frame.setFrameShadow(QFrame.Plain) self._options_frame.setLineWidth(0) self._options_frame.setLayout(self._options_layout) self._extra_layout = layouts.VerticalLayout() self._extra_layout.setContentsMargins(0, 0, 0, 0) self._extra_layout.setSpacing(2) self.main_layout.addLayout(title_layout) self.main_layout.addWidget(self._folder_widget) self._extra_layout.addWidget(self._options_frame) self.main_layout.addWidget(dividers.Divider()) self.main_layout.addLayout(self._extra_layout) self.main_layout.addWidget(dividers.Divider()) self.main_layout.addWidget(buttons_frame) def setup_signals(self): self.save_btn.clicked.connect(self._on_save) self.cancel_btn.clicked.connect(self._on_cancel) def settings(self): """ Returns settings object :return: JSONSettings """ return self._settings def set_settings(self, settings): """ Sets save widget settings :param settings: JSONSettings """ self._settings = settings # def item(self): # """ # Returns the library item to be created # :return: LibraryItem # """ # # return self._item def set_item(self, item): """ Sets the base item to be created :param item: LibraryItem """ self._item = item self._title_lbl.setText(item.MenuName) self._icon_lbl.setPixmap(QPixmap(item.type_icon_path())) schema = item.save_schema() if schema: options_widget = formwidget.FormWidget(self) options_widget.set_schema(schema) options_widget.set_validator(item.save_validator) self._options_frame.layout().addWidget(options_widget) self._options_widget = options_widget self.load_settings() options_widget.stateChanged.connect(self._on_options_changed) options_widget.validate() else: self._options_frame.setVisible(False) def library_window(self): """ Returns library widget window for the item :return: LibraryWindow """ return self._item.library_window() def set_library_window(self, library_window): """ Sets the library widget for the item :param library_window: LibraryWindow """ self._item.set_library_window(library_window) def name(self): """ Returns the name of the field :return: str """ return self._title_lbl.text().strip() def description(self): """ Returns the string from the comment field :return: str """ return self._comment.toPlainText().strip() def folder_frame(self): """ Returns the frame that contains the folder edit, label and button :return: QFrame """ return self._folder_frame def folder_path(self): """ Returns the folder path :return: str """ return self._folder_widget.folder_line.text() def set_folder_path(self, path): """ Sets the destination folder path :param path: str """ self._folder_widget.folder_line.setText(path) def default_values(self): """ Returns all the default values for the save fields :return: dict """ values = dict() for option in self.item().save_schema(): values[option.get('name')] = option.get('default') return values def save_settings(self): """ Saves the current state of the widget to disk """ state = self._options_widget.options_state() self.settings().set(self.item().__class__.__name__, {'SaveOptions': state}) def load_settings(self): """ Returns the settings object for saving the state of the widget """ option_settings = self.settings().get(self.item().__class__.__name__, {}) options = option_settings.get('SaveOptions', dict()) values = self.default_values() if options: for option in self.item().save_schema(): name = option.get('name') persistent = option.get('persistent') if not persistent and name in options: options[name] = values[name] self._options_widget.set_state_from_options(options) def save(self, path, icon_path, objects=None): """ Saves the item with the given objects to the given disk location path :param path: list(str) :param icon_path: str :param objects: str """ item = self.item() options = self._options_widget.values() item.save(path=path, objects=objects, icon_path=icon_path, **options) self.close() def _save(self): if not self.library_window(): return try: path = self.folder_path() options = self._options_widget.values() name = options.get('name') objects = dcc.selected_nodes(full_path=True) or list() if not path: raise Exception( 'No folder selected. Please select a destination folder') if not name: raise Exception( 'No name specified. Please set a name before saving') if not os.path.exists(self.icon_path()): btn = self.show_thumbnail_capture_dialog() if btn == QDialogButtonBox.Cancel: return path += '/{}'.format(name) icon_path = self.icon_path() self.save(path=path, icon_path=icon_path, objects=objects) except Exception as e: messagebox.MessageBox.critical(self.library_window(), 'Error while saving', str(e)) LOGGER.error(traceback.format_exc()) raise self.library_window().stack.slide_in_index(0) def _on_options_changed(self): """ Internal callback function that is called when an option value changes """ self.save_settings() def _on_save(self): if not self.library_window(): return self._save() self.library_window().stack.slide_in_index(0) def _on_cancel(self): self.close() self.library_window().stack.slide_in_index(0)