def __init__(self, parent=None, columns=4, buttonSize=None, iconSize=None, toolButtonStyle=Qt.ToolButtonTextUnderIcon): QFrame.__init__(self, parent) if buttonSize is not None: buttonSize = QSize(buttonSize) if iconSize is not None: iconSize = QSize(iconSize) self.__columns = columns self.__buttonSize = buttonSize or QSize(50, 50) self.__iconSize = iconSize or QSize(26, 26) self.__toolButtonStyle = toolButtonStyle self.__gridSlots = [] self.__buttonListener = ToolButtonEventListener(self) self.__buttonListener.buttonRightClicked.connect( self.__onButtonRightClick) self.__buttonListener.buttonEnter.connect(self.__onButtonEnter) self.__mapper = QSignalMapper() self.__mapper.mapped[QObject].connect(self.__onClicked) self.__setupUi()
def __init__(self): super(MainWindow, self).__init__() self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setViewMode(QMdiArea.TabbedView) self.mdiArea.setDocumentMode(True) self.mdiArea.setTabsClosable(True) self.mdiArea.setTabsMovable(True) self.setCentralWidget(self.mdiArea) self.mdiArea.subWindowActivated.connect(self.updateMenus) self.windowMapper = QSignalMapper(self) self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) self.createActions() self.createMenus() self.createToolBars() self.createStatusBar() self.updateMenus() self.readSettings() self.setWindowTitle("MDI")
def __init__(self, currentApp): ''' Constructor ''' self.app = currentApp QtWidgets.QMainWindow.__init__(self) self.setupUi(self) central.CoreCentral.__init__(self) atexit.register(self.sessionClosed) self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdiArea) self.mdiArea.subWindowActivated.connect(self.updateMenus) self.windowMapper = QSignalMapper(self) self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) self.setWindowTitle("PyOne") self.createActions() self.createMenus() self.createToolBars() self.createStatusBar() self.updateMenus() self.startupActivity() self.reloadedModules = []
def __init__(self): super(RDFNavigator, self).__init__() self.lastDir = '.' self.settingsManager = RDFNavigatorSettignsManager() self.resourceRefManager = RDFNavigatorResourceReferenceManager() self.childrenFactory = RDFNavigatorChildrenFactory(self) self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdiArea) self.mdiArea.subWindowActivated.connect(self.updateMenus) self.windowMapper = QSignalMapper(self) self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) self.createActions() self.createMenus() self.createToolBars() self.createStatusBar() self.updateMenus() self.createDockWidgets() self.readSettings() self.setWindowTitle("RDF Navigator") self.global_data = {} self.analyzeSystemData()
def updatePlotPersoButton(self): menu = QMenu(self.mw) menus = [] for i in [self.tr("Main"), self.tr("Secondary"), self.tr("Minor")]: m = QMenu(i, menu) menus.append(m) menu.addMenu(m) mpr = QSignalMapper(menu) for i in range(self.mw.mdlCharacter.rowCount()): a = QAction(self.mw.mdlCharacter.name(i), menu) a.setIcon(self.mw.mdlCharacter.icon(i)) a.triggered.connect(mpr.map) mpr.setMapping(a, int(self.mw.mdlCharacter.ID(i))) imp = toInt(self.mw.mdlCharacter.importance(i)) menus[2 - imp].addAction(a) # Disabling empty menus for m in menus: if not m.actions(): m.setEnabled(False) mpr.mapped.connect(self.addPlotPerso) self.mw.btnAddPlotPerso.setMenu(menu)
def on_view_horizontalHeader_sectionClicked(self, logicalIndex): self.logicalIndex = logicalIndex self.menuValues = QMenu(self) self.signalMapper = QSignalMapper(self) self.columnComboBox.blockSignals(True) self.columnComboBox.setCurrentIndex(self.logicalIndex) self.columnComboBox.blockSignals(True) valuesUnique = [ self.model.item(row, self.logicalIndex).text() for row in range(self.model.rowCount()) ] actionAll = QAction("All", self) actionAll.triggered.connect(self.on_actionAll_triggered) self.menuValues.addAction(actionAll) self.menuValues.addSeparator() for actionNumber, actionName in enumerate(sorted(list(set(valuesUnique)))): action = QAction(actionName, self) self.signalMapper.setMapping(action, actionNumber) action.triggered.connect(self.signalMapper.map) self.menuValues.addAction(action) self.signalMapper.mapped.connect(self.on_signalMapper_mapped) headerPos = self.tableView.mapToGlobal(self.horizontalHeader.pos()) posY = headerPos.y() + self.horizontalHeader.height() posX = headerPos.x() + self.horizontalHeader.sectionPosition(self.logicalIndex) self.menuValues.exec_(QPoint(posX, posY))
def initDialog(self): """Creates buttons for each sequence option and add them to the dialog. Maps the clicked signal of those buttons to keep track of what sequence gets selected. """ ui_dlg = Ui_AddSeqDialog() ui_dlg.setupUi(self.dialog) self.signal_mapper = QSignalMapper(self) # set up the radio buttons for i, name in enumerate(['Abstract', 'Custom'] + sorted(sequences.keys())): radio_button = QRadioButton(ui_dlg.group_box) radio_button.setObjectName(name + "Button") radio_button.setText(name) self.buttons.append(radio_button) ui_dlg.horizontalLayout.addWidget(radio_button) self.signal_mapper.setMapping(radio_button, i) radio_button.clicked.connect(self.signal_mapper.map) if name in sequences: self.sequence_radio_button_id[sequences[name]] = i self.signal_mapper.mapped.connect(self.sequenceOptionChangedSlot) # disable apply until valid option or custom sequence is chosen self.apply_button = ui_dlg.custom_button_box.button( QDialogButtonBox.Apply) self.apply_button.setEnabled(False) # watch sequence textedit box to validate custom sequences self.seq_box = ui_dlg.seq_text_edit self.seq_box.textChanged.connect(self.validateCustomSequence) self.highlighter = DNAHighlighter(self.seq_box) # finally, pre-click the first radio button self.buttons[0].click()
def __init__(self): """ Initializes the main game's window and sets its attributes to their default values. """ super(RagingSeasWindow, self).__init__() self.ui = Ui_RagingSeasWindow() self.ui.setupUi(self) self.ui.menuFile.menuAction().setStatusTip(self.tr("File")) self.ui.menuHelp.menuAction().setStatusTip(self.tr("Help")) self.ui.playerOneLabel.hide() self.ui.playerTwoLabel.hide() self.leftGridMapper = QSignalMapper(self) self.rightGridMapper = QSignalMapper(self) self.statusLabel = QLabel(self) self.statusLabel.setObjectName("StatusLabel") self.ui.statusBar.setSizeGripEnabled(False) self.ui.statusBar.addPermanentWidget(self.statusLabel) self.setStyleSheet(style.INITIAL_MAIN_WINDOW_STYLE) self.setFixedSize(self.size()) self.setWindowFlags( self.windowFlags() ^ (QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowMaximizeButtonHint) ) self._session = None self._grid_size = None self._previous_grid_size = None self._is_new_game = False self._has_game_been_started = False self._current_ship_index = 0 self._current_orientation = ShipOrientation.horizontal
def __init__(self, dataTab=None, parent=None): super(processManagerDialog, self).__init__(parent=parent) self.installDir = GENERAL.get_value('INSTALL_DIR') self.mainLayout = QVBoxLayout(self) self.upperLayout = QVBoxLayout(self) self.lowerLayout = QVBoxLayout(self) self.menuBar = QMenuBar(self) self.toolBar = QToolBar(self) self.processTable = QTableView(self) self.tableModel = functionModel(self) self.processTablePopMenu = QMenu(self) self.importMenu = QMenu(self) self.signalMapper = QSignalMapper(self) # self.MLProject = MLProject self.openedData = GENERAL.get_value('OPENED_DATA') self.MainTabWindow = GENERAL.get_value('TABWINDOW') # tool bar self.addProcessAction = None self.importProcessAction = None # data self.processList = [] self.processDict = set() self.dataTab = dataTab self.targetDataset = dataTab.filename if dataTab else None # self.curDir = GENERAL.get_value('INSTALL_DIR') self.initUI() self.initRightClickMenu()
def createUI(self): self.vblayout = QVBoxLayout(self) self.layoutWidget = pg.LayoutWidget() self.vblayout.addWidget(self.layoutWidget) self.labels = {} self.inputFields = {} for key, value in self.inputs.items(): self.labels[key] = QLabel(key) self.layoutWidget.addWidget(self.labels[key]) if type(value) == int: self.signalMapper1 = QSignalMapper(self) self.inputFields[key] = QLineEdit(str(value)) self.inputFields[key].setValidator(self.intValidator) self.inputFields[key].textChanged.connect( self.signalMapper1.map) self.signalMapper1.setMapping(self.inputFields[key], key) self.signalMapper1.mapped[str].connect(self.inputChanged) elif type(value) == float: self.signalMapper2 = QSignalMapper(self) self.inputFields[key] = QLineEdit(str(value)) self.inputFields[key].setValidator(self.floatValidator) self.inputFields[key].textChanged.connect( self.signalMapper2.map) self.signalMapper2.setMapping(self.inputFields[key], key) self.signalMapper2.mapped[str].connect(self.inputChanged) elif type(value) == bool: self.signalMapper3 = QSignalMapper(self) self.inputFields[key] = QCheckBox() self.inputFields[key].setTristate(False) self.inputFields[key].stateChanged.connect( self.signalMapper3.map) self.signalMapper3.setMapping(self.inputFields[key], key) self.signalMapper3.mapped[str].connect(self.inputStateChanged) elif type(value) == str: self.signalMapper4 = QSignalMapper(self) self.inputFields[key] = QLineEdit(value) self.inputFields[key].textChanged.connect( self.signalMapper4.map) self.signalMapper4.setMapping(self.inputFields[key], key) self.signalMapper4.mapped[str].connect(self.inputChanged) elif type(value) == list: self.signalMapper5 = QSignalMapper(self) self.inputFields[key] = QComboBox() self.inputFields[key].addItems(value) self.inputFields[key].currentTextChanged.connect( self.signalMapper5.map) self.signalMapper5.setMapping(self.inputFields[key], key) self.signalMapper5.mapped[str].connect(self.inputTextChanged) self.layoutWidget.addWidget(self.inputFields[key]) self.layoutWidget.nextRow() self.layoutWidget.nextRow() self.cancelButton = QPushButton('Cancel') self.cancelButton.clicked.connect(self.cancelandClose) self.layoutWidget.addWidget(self.cancelButton, col=0) self.okButton = QPushButton('OK') self.okButton.clicked.connect(self.okandClose) self.layoutWidget.addWidget(self.okButton, col=1) self.okButton.setDefault(True)
def __init__(self, **kwargs): super(Monitor, self).__init__(**kwargs) self.watched = WeakValueDictionary() self.delMapper = QSignalMapper(self) self.delMapper.mapped[str].connect(self.unmonitorFile) self.watcher = MonitorWithRename(parent=self) self.watcher.fileChanged.connect(self._onFileChanged)
class Monitor(QObject): """File monitor This monitor can be used to track single files """ def __init__(self, **kwargs): super(Monitor, self).__init__(**kwargs) self.watched = WeakValueDictionary() self.delMapper = QSignalMapper(self) self.delMapper.mapped[str].connect(self.unmonitorFile) self.watcher = MonitorWithRename(parent=self) self.watcher.fileChanged.connect(self._onFileChanged) def monitorFile(self, path): """Monitor a file and return an object that tracks only `path` :rtype: SingleFileWatcher :return: an object tracking `path`, the same object is returned if the method is called with the same path. """ path = os.path.abspath(path) self.watcher.addPath(path) proxy = self.watched.get(path) if not proxy: proxy = SingleFileWatcher(path) proxy.destroyed.connect(self.delMapper.map) self.delMapper.setMapping(proxy, path) self.watched[path] = proxy return proxy @Slot(str) def unmonitorFile(self, path): """Stop monitoring a file Since there is only one :any:`SingleFileWatcher` object per path, all objects monitoring `path` will not receive notifications anymore. To let only one object stop monitoring the file, simply disconnect its `modified` signal. When the :any:`SingleFileWatcher` object returned by method :any:`monitorFile` is destroyed, the file is automatically un-monitored. """ path = os.path.abspath(path) self.watcher.removePath(path) self.watched.pop(path, None) @Slot(str) def _onFileChanged(self, path): proxy = self.watched.get(path) if proxy: proxy.modified.emit()
def refresh_accounts(self): self.menu_change_account.clear() signal_mapper = QSignalMapper(self) for account_name in sorted(self.app.accounts.keys()): action = QAction(account_name, self) self.menu_change_account.addAction(action) signal_mapper.setMapping(action, account_name) action.triggered.connect(signal_mapper.map) signal_mapper.mapped[str].connect(self.action_change_account)
def __init__(self, columns: int = 9, rows: int = 9, mines: int = 10): """Initialize a game of Minesweeper This will create the game board and hide the specified number of mines on the board. :param columns: The number of columns on the game board (the width). Default is 9. :param rows: The number of rows on the game board (the height). Default is 9. :param mines: The number of mines to hide on the game board. There cannot be more mines on the game board than there are fields. Default is 10. """ super().__init__(parent=None) # These attributes will be initialized in __new_game() self.columns = 0 self.rows = 0 self.mines = 0 self.game_running = False # Initialize the GUI self.view = Ui_MainWindow() self.view.setupUi(self) # Connect the signals of the "New game" buttons self.view.new_game_easy.triggered.connect(self.easy_game) self.view.new_game_medium.triggered.connect(self.medium_game) self.view.new_game_difficult.triggered.connect(self.difficult_game) self.view.new_game_custom.triggered.connect(self.custom_game_dialog) # The model will be initialized in __new_game() self.model = None # A dialog window for starting a custom game self.dialog = CustomGameDialog() self.dialog.accepted.connect(self.__custom_game) # Initialize a QSignalMapper to enable mapping all button clicks to a single method self.mapper = QSignalMapper(self.view.centralwidget) self.mapper.mapped.connect(self.button_clicked) # Initialize another QSignalMapper to enable mapping of right-clicks self.rc_mapper = QSignalMapper(self.view.centralwidget) self.rc_mapper.mapped.connect(self.button_right_clicked) # Create a QGridLayout for the buttons self.grid = QGridLayout() self.view.main_layout.addLayout(self.grid) self.grid.setSpacing(5) # This is the only efficient way to keep a reference to all buttons on the board, since buttons are # "re-parented" to the main window after being added to a layout self.buttons = [] self.grid.setSizeConstraint(QGridLayout.SetFixedSize) self.__new_game(columns=columns, rows=rows, mines=mines)
def __init__(self, parent=None): super(MyMainWindow, self).__init__(parent) self.mgr = WordMgr(auto_shuffle=False) self.setupUi(self) self.signal_map_list_checkbox = QSignalMapper() self.signal_map_list_checkbox.mapped[int].connect( self.onListCheckboxChanged) self.map_li_checkbox = {} self.initUI()
def setup_ui(self, main_window): self.main_window = main_window main_window.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) red, green, blue = 11, 7, 11 main_window.setPalette(QPalette(QColor(red, green, blue))) self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.create_menu(main_window) self.create_tool_bars(main_window) main_window.setCentralWidget(self.mdiArea) self.windowMapper = QSignalMapper(main_window)
def init_menu(self): # menu_bar _menu_bar = self.menuBar() self.init_file_menu(_menu_bar) self.init_edditing_menu(_menu_bar) self.init_view_menu(_menu_bar) self.init_last_toolbar() self.init_file_toolbar() self.init_adding_toolbar() self.init_formattin_toolbar() # mapper for recent files self.mapper = QSignalMapper(self)
def initUI(self): """UI is composed with """ # variable for QSettings self.name_company = 'Copey' self.name_product = 'DataViz NodeEditor' self.setWindowIcon(QIcon('icons/main-icon.png')) # Load filesheets # TODO Review style # self.stylesheet_filenames = (os.path.join(os.path.dirname(__file__), 'qss/nodeeditor.qss'), # os.path.join(os.path.dirname(__file__), 'qss/nodeeditor-dark.qss')) self.stylesheet_filenames = (os.path.join(os.path.dirname(__file__), 'icons/nodeeditor.qss')) print(self.stylesheet_filenames) loadStylessheets(*self.stylesheet_filenames) # self.empty_icon = QIcon(".") if DEBUG: print('Registered Node') pp(NodeFactory.get_nodes()) # Instantiate the MultiDocument Area self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setViewMode(QMdiArea.TabbedView) self.mdiArea.setTabsClosable(True) self.setCentralWidget(self.mdiArea) # Connect subWindowActivate to updateMenu # Activate the items on the file_menu and the edit_menu self.mdiArea.subWindowActivated.connect(self.updateMenus) # from mdi example... self.windowMapper = QSignalMapper(self) self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) # instantiate various elements self.createNodesDock() self.createPropertiesDock() self.createActions() self.createMenus() self.createToolBars() self.createStatusBar() self.updateMenus() self.readSettings() self.setWindowTitle("DataViz NodeEditor")
def _add_widgets(self,vbox,apps): row=int(len(self.appsWidgets)/self.maxCol) col=(self.maxCol*(row+1))-len(self.appsWidgets) sigmap_run=QSignalMapper(self) sigmap_run.mapped[QString].connect(self._launch) for appName,data in apps.items(): appIcon=data['Icon'] appDesc=data['Name'] if QtGui.QIcon.hasThemeIcon(appIcon): icnApp=QtGui.QIcon.fromTheme(appIcon) elif os.path.isfile(appIcon): iconPixmap=QtGui.QPixmap(appIcon) scaledIcon=iconPixmap.scaled(QSize(BTN_SIZE*1.2,BTN_SIZE*1.2)) icnApp=QtGui.QIcon(scaledIcon) elif appIcon.startswith("http"): if not os.path.isdir("%s/icons"%self.cache): os.makedirs("%s/icons"%self.cache) tmpfile=os.path.join("%s/icons"%self.cache,appIcon.split("/")[2].split(".")[0]) if not os.path.isfile(tmpfile): try: urlretrieve(appIcon,tmpfile) except: tmpfile=QtGui.QIcon.fromTheme("shell") iconPixmap=QtGui.QPixmap(tmpfile) scaledIcon=iconPixmap.scaled(QSize(BTN_SIZE*1.2,BTN_SIZE*1.2)) icnApp=QtGui.QIcon(scaledIcon) appIcon=tmpfile else: continue if not appName: continue self.app_icons[appName]=appIcon self._debug("Adding %s"%appName) btnApp=navButton(self) btnApp.setIcon(icnApp) btnApp.setIconSize(QSize(BTN_SIZE,BTN_SIZE)) btnApp.setToolTip(appDesc) btnApp.setFocusPolicy(Qt.NoFocus) btnApp.keypress.connect(self._set_focus) btnApp.focusIn.connect(self._get_focus) self.focusWidgets.append(btnApp) self.appsWidgets.append(appName) sigmap_run.setMapping(btnApp,appName) btnApp.clicked.connect(sigmap_run.map) vbox.addWidget(btnApp,row,col,Qt.Alignment(-1)) col+=1 if col==self.maxCol: col=0 row+=1
def setup_board(self): gridLayout = QGridLayout() mapper = QSignalMapper(self) for row in range(3): for column in range(3): button = QPushButton() button.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) button.setText(" ") gridLayout.addWidget(button, row, column) self.board.append(button) mapper.setMapping(button, len(self.board) - 1) button.clicked.connect(mapper.map) mapper.mapped.connect(self.handle_button_click) self.setLayout(gridLayout)
def on_header_sectionClicked(self, logicalIndex): """opens a dialog to choose between all unique values for this column, or revert to 'All' """ self.log.debug("Header clicked: column {}".format(logicalIndex)) self.logicalIndex = logicalIndex menuValues = QMenu(self) self.signalMapper = QSignalMapper(self) self.filter_cb.setCurrentIndex(self.logicalIndex) self.filter_cb.blockSignals(True) self.proxy.setFilterKeyColumn(self.logicalIndex) valuesUnique = [str(self.model.index(row, self.logicalIndex).data()) for row in range(self.model.rowCount()) ] actionAll = QAction("All", self) actionAll.triggered.connect(self.on_actionAll_triggered) menuValues.addAction(actionAll) menuValues.addSeparator() for actionNumber, actionName in enumerate(sorted(list(set(valuesUnique)))): action = QAction(actionName, self) self.signalMapper.setMapping(action, actionNumber) action.triggered.connect(self.signalMapper.map) menuValues.addAction(action) self.signalMapper.mapped.connect(self.on_signalMapper_mapped) headerPos = self.table.mapToGlobal(self.header.pos()) posY = headerPos.y() + self.header.height() posX = headerPos.x() + self.header.sectionViewportPosition(self.logicalIndex) menuValues.exec_(QPoint(posX, posY))
def initDialog(self): """Creates buttons for each sequence option and add them to the dialog. Maps the clicked signal of those buttons to keep track of what sequence gets selected. """ ui_dlg = Ui_AddSeqDialog() ui_dlg.setupUi(self.dialog) self.signal_mapper = QSignalMapper(self) # set up the radio buttons for i, name in enumerate(['Abstract', 'Custom'] + sorted(sequences.keys())): radio_button = QRadioButton(ui_dlg.group_box) radio_button.setObjectName(name + "Button") radio_button.setText(name) self.buttons.append(radio_button) ui_dlg.horizontalLayout.addWidget(radio_button) self.signal_mapper.setMapping(radio_button, i) radio_button.clicked.connect(self.signal_mapper.map) if name in sequences: self.sequence_radio_button_id[sequences[name]] = i self.signal_mapper.mapped.connect(self.sequenceOptionChangedSlot) # disable apply until valid option or custom sequence is chosen self.apply_button = ui_dlg.custom_button_box.button(QDialogButtonBox.Apply) self.apply_button.setEnabled(False) # watch sequence textedit box to validate custom sequences self.seq_box = ui_dlg.seq_text_edit self.seq_box.textChanged.connect(self.validateCustomSequence) self.highlighter = DNAHighlighter(self.seq_box) # finally, pre-click the first radio button self.buttons[0].click()
def home(self): self.folderLoader = FolderLoader() self.saved = True #Parameters self.statusBar = QStatusBar() self.setStatusBar(self.statusBar) self.createMainMenu() self.canvas = Canvas(parent=self) self.scrollArea = QScrollArea() self.scrollArea.setWidget(self.canvas) self.scrollArea.setWidgetResizable(True) self.scrollArea.setStyleSheet(STYLE_SHEET) self.labelCoordinates = QLabel('') self.statusBar.addPermanentWidget(self.labelCoordinates) self.toolbar = QToolBar(TOOLBAR_NAME, self) self.toolbar.setStyleSheet(STYLE_SHEET) self.toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) self.addToolBar(Qt.LeftToolBarArea, self.toolbar) self.mapper = QSignalMapper() self.createColorComboBox() self.createToolbarActions() self.createFileDock() self.setCentralWidget(self.scrollArea) self.update() self.showMaximized()
def initUI(self): """UI is composed with """ # variable for QSettings self.name_company = 'Michelin' self.name_product = 'Calculator NodeEditor' # Load filesheets self.stylesheet_filename = os.path.join(os.path.dirname(__file__), 'qss/nodeeditor.qss') loadStylessheets( os.path.join(os.path.dirname(__file__), 'qss/nodeeditor-dark.qss'), self.stylesheet_filename) self.empty_icon = QIcon(".") if DEBUG: print('Registered Node') pp(CALC_NODES) # Instantiate the MultiDocument Area self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setViewMode(QMdiArea.TabbedView) self.mdiArea.setTabsClosable(True) self.setCentralWidget(self.mdiArea) # Connect subWindowActivate to updateMenu # Activate the items on the file_menu and the edit_menu self.mdiArea.subWindowActivated.connect(self.updateMenus) # from mdi example... self.windowMapper = QSignalMapper(self) self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) # instantiate various elements self.createNodesDock() self.createActions() self.createMenus() self.createToolBars() self.createStatusBar() self.updateMenus() self.readSettings() self.setWindowTitle("Calculator NodeEditor Example")
def _set_keymapping(self): #Disable meta association within plasma. I've no eggs to capture and block this event in kde for key,value in vars(Qt).items(): if isinstance(value, Qt.Key): self.keymap[value]=key.partition('_')[2] self.modmap={ Qt.ControlModifier: self.keymap[Qt.Key_Control], Qt.AltModifier: self.keymap[Qt.Key_Alt], Qt.ShiftModifier: self.keymap[Qt.Key_Shift], Qt.MetaModifier: self.keymap[Qt.Key_Meta], Qt.GroupSwitchModifier: self.keymap[Qt.Key_AltGr], Qt.KeypadModifier: self.keymap[Qt.Key_NumLock] } self.sigmap_tabSelect=QSignalMapper(self) self.sigmap_tabSelect.mapped[QInt].connect(self._on_tabSelect) self.sigmap_tabRemove=QSignalMapper(self) self.sigmap_tabRemove.mapped[QInt].connect(self._on_tabRemove)
def refreshTilesetMenu(self): self.mTilesetMenu.clear() if (self.mTilesetMenuMapper): self.mTabBar.disconnect(self.mTilesetMenuMapper) del self.mTilesetMenuMapper self.mTilesetMenuMapper = QSignalMapper(self) self.mTilesetMenuMapper.mapped.connect(self.mTabBar.setCurrentIndex) currentIndex = self.mTabBar.currentIndex() for i in range(self.mTabBar.count()): action = QAction(self.mTabBar.tabText(i), self) action.setCheckable(True) self.mTilesetActionGroup.addAction(action) if (i == currentIndex): action.setChecked(True) self.mTilesetMenu.addAction(action) action.triggered.connect(self.mTilesetMenuMapper.map) self.mTilesetMenuMapper.setMapping(action, i)
def onSelectionMenuClicked ( self ): signalMapper = QSignalMapper ( self ) acts = self.tabSelectionMenu.actions () activeAction = self.currentIndex () for i in range ( 0, acts.size () ): self.tabSelectionMenu.removeAction ( acts[ i ] ) for i in range ( 0, self.count () ): action = self.tabSelectionMenu.addAction ( self.tabText ( i ) ) action.triggered.connect ( signalMapper.map ) signalMapper.setMapping ( action, i ) if activeAction >= 0: acts = self.tabSelectionMenu.actions () acts[ activeAction ].setIcon ( QIcon ( "./resources/general_tick.ico" ) ) # TODO: CryIcon signalMapper.mapped.connect ( self.setCurrentIndex )
def __init__(self): super(MainWindow, self).__init__() self.ports = serial.listports() self.port = None # remove close & maximize window buttons #self.setWindowFlags(Qt.CustomizeWindowHint|Qt.WindowMinimizeButtonHint) self.setMinimumSize(850, 450) self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdiArea) self.mdiArea.subWindowActivated.connect(self.updateMenus) self.mdiArea.setViewMode(QMdiArea.TabbedView) self.windowMapper = QSignalMapper(self) self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) self.child = None self.createActions() self.createMenus() self.createStatusBar() self.updateMenus() self.readSettings() self.setWindowTitle("VISCAM") mytoolbar = QToolBar() ports_menu = QComboBox() ports_menu.addItem('Output Port') ports_menu.insertSeparator(1) for port in self.ports: ports_menu.addItem(port) self.ports_menu = ports_menu ports_menu.currentTextChanged.connect(self.setActivePort) mytoolbar.addWidget(ports_menu) mytoolbar.addSeparator() mytoolbar.setMovable(False) mytoolbar.setFixedHeight(60) self.addToolBar(Qt.TopToolBarArea, mytoolbar)
def __init__(self): super().__init__() grid = QGridLayout(self) mapper = QSignalMapper(self) btns = (('1', '2', '3', '+'), ('4', '5', '6', '-'), ('7', '8', '9', '*'), ('0', '.', '=', '/'), ) self.line = QLineEdit() self.flag = False grid.addWidget(self.line, 0, 0, 1, 4) for row in range(4): for col in range(4): button = QPushButton(btns[row][col]) grid.addWidget(button, row + 1, col) button.clicked.connect(mapper.map) mapper.setMapping(button, button.text()) mapper.mapped[str].connect(self.on_mapped)
class MyWindow(QWidget): def __init__(self): super(MyWindow, self).__init__() self.setGeometry(100, 100, 600, 400) self.setWindowTitle("Send to Player") self.list = MyListWidget(self) self.uploader = Uploader(self.list, self) layout = QHBoxLayout(self) layout.addWidget(self.list) self.browserMapper = QSignalMapper() self.openSourceButton = QPushButton('&Open Music Archive') self.openSourceButton.released.connect(self.browserMapper.map) self.browserMapper.setMapping(self.openSourceButton, config.mediaSource) self.openTargetButton = QPushButton('Open &Player') self.openTargetButton.released.connect(self.browserMapper.map) self.browserMapper.setMapping(self.openTargetButton, config.mediaTarget) self.browserMapper.mapped[str].connect( lambda b: QDesktopServices.openUrl(QUrl(b))) self.submitButton = QPushButton('&Send to Player') self.submitButton.released.connect(self.uploader.uploadMedia) self.deleteSelectedButton = QPushButton('&Delete\nSelected') self.deleteSelectedButton.released.connect(self.list.deleteSelected) controls = QVBoxLayout() controls.addWidget(QLabel('Filesystem Browsers')) controls.addWidget(self.openSourceButton) controls.addWidget(self.openTargetButton) controls.addWidget(Separator(self)) controls.addWidget(self.deleteSelectedButton) controls.addWidget(Separator(self)) controls.addStretch() controls.addWidget(self.submitButton) layout.addLayout(controls) self.setLayout(layout)
def initDialog(self): """ 1. Create buttons according to available scaffold sequences and add them to the dialog. 2. Map the clicked signal of those buttons to keep track of what sequence gets selected. 3. Watch the tab_widget change signal to determine whether a standard or custom sequence should be applied. """ ui_dlg = Ui_AddSeqDialog() ui_dlg.setupUi(self.dialog) self.signal_mapper = QSignalMapper(self) # set up the radio buttons for i, name in enumerate(sorted(sequences.keys())): radio_button = QRadioButton(ui_dlg.group_box) radio_button.setObjectName(name + "Button") radio_button.setText(name) self.buttons.append(radio_button) ui_dlg.verticalLayout.addWidget(radio_button) self.signal_mapper.setMapping(radio_button, i) radio_button.clicked.connect(self.signal_mapper.map) self.signal_mapper.mapped.connect(self.standardSequenceChangedSlot) ui_dlg.tab_widget.currentChanged.connect(self.tabWidgetChangedSlot) # disable apply until valid option or custom sequence is chosen self.apply_button = ui_dlg.custom_button_box.button( QDialogButtonBox.Apply) self.apply_button.setEnabled(False) # watch sequence textedit box to validate custom sequences self.seq_box = ui_dlg.seq_text_edit self.seq_box.textChanged.connect(self.validateCustomSequence) self.highlighter = DNAHighlighter(self.seq_box) # finally, pre-click the M13mp18 radio button self.buttons[0].click() buttons = self.buttons self.dialog.setFocusProxy(ui_dlg.group_box) self.dialog.setFocusPolicy(Qt.TabFocus) ui_dlg.group_box.setFocusPolicy(Qt.TabFocus) for i in range(len(buttons) - 1): ui_dlg.group_box.setTabOrder(buttons[i], buttons[i + 1])
def updatePlotPersoButton(self): menu = QMenu(self.mw) menus = [] for i in [self.tr("Main"), self.tr("Secondary"), self.tr("Minor")]: m = QMenu(i, menu) menus.append(m) menu.addMenu(m) mpr = QSignalMapper(menu) for i in range(self.mw.mdlPersos.rowCount()): a = QAction(self.mw.mdlPersos.name(i), menu) a.setIcon(self.mw.mdlPersos.icon(i)) a.triggered.connect(mpr.map) mpr.setMapping(a, int(self.mw.mdlPersos.ID(i))) imp = toInt(self.mw.mdlPersos.importance(i)) menus[2 - imp].addAction(a) mpr.mapped.connect(self.addPlotPerso) self.mw.btnAddPlotPerso.setMenu(menu)
def __init__(self): super(NetMainWindow, self).__init__() self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdiArea) self.windowMapper = QSignalMapper(self) self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) self.createActions() self.createMenus() self.createStatusBar() self.readSettings() self.setWindowTitle('Net')
def onHeaderClicked(self, logicalIndex): if logicalIndex != 0: return # allow filter on first column only for now self.logicalIndex = logicalIndex self.menuValues = QMenu(self) self.signalMapper = QSignalMapper(self) # get unique values from (unfiltered) model valuesUnique = set([ self.table_model.index(row, self.logicalIndex).data() for row in range( self.table_model.rowCount( self.table_model.index(-1, self.logicalIndex))) ]) if len(valuesUnique) == 1: return # no need to select anything actionAll = QAction("Show All", self) actionAll.triggered.connect(self.onShowAllRows) self.menuValues.addAction(actionAll) self.menuValues.addSeparator() """se_comment: hard-refactoring to comply to pep8""" l: enumerate = enumerate(sorted(list(set(valuesUnique)))) for actionNumber, actionName in l: action = QAction(actionName, self) self.signalMapper.setMapping(action, actionNumber) action.triggered.connect(self.signalMapper.map) self.menuValues.addAction(action) self.signalMapper.mapped.connect(self.onSignalMapper) # get screen position of table header and open menu headerPos = self.table_view.mapToGlobal(self.horizontalHeader.pos()) posY = headerPos.y() + self.horizontalHeader.height() posX = headerPos.x() + \ self.horizontalHeader.sectionPosition(self.logicalIndex) self.menuValues.exec_(QPoint(posX, posY))
def __init__(self, parent: QWidget=None, text: str="") -> None: super(NumPad, self).__init__(parent) self.setupUi(self) # type: ignore try: suffixIndex = text.index(" ") except ValueError: self.outputLineEdit.setText(text) else: self.outputLineEdit.setText(text[:suffixIndex]) self.outputLineEdit.setSelection(0, len(self.outputLineEdit.text())) self.outputLineEdit.setFocus() self.signal_mapper = QSignalMapper(self) self.signal_mapper.setMapping(self.button0, "0") self.signal_mapper.setMapping(self.button1, "1") self.signal_mapper.setMapping(self.button2, "2") self.signal_mapper.setMapping(self.button3, "3") self.signal_mapper.setMapping(self.button4, "4") self.signal_mapper.setMapping(self.button5, "5") self.signal_mapper.setMapping(self.button6, "6") self.signal_mapper.setMapping(self.button7, "7") self.signal_mapper.setMapping(self.button8, "8") self.signal_mapper.setMapping(self.button9, "9") self.signal_mapper.setMapping(self.buttonDecimal, DECIMAL_SEPARATOR) self.signal_mapper.setMapping(self.buttonDel, "DEL") self.button0.pressed.connect(self.signal_mapper.map) self.button1.pressed.connect(self.signal_mapper.map) self.button2.pressed.connect(self.signal_mapper.map) self.button3.pressed.connect(self.signal_mapper.map) self.button4.pressed.connect(self.signal_mapper.map) self.button5.pressed.connect(self.signal_mapper.map) self.button6.pressed.connect(self.signal_mapper.map) self.button7.pressed.connect(self.signal_mapper.map) self.button8.pressed.connect(self.signal_mapper.map) self.button9.pressed.connect(self.signal_mapper.map) self.buttonDecimal.pressed.connect(self.signal_mapper.map) self.buttonDel.pressed.connect(self.signal_mapper.map) self.signal_mapper.mapped[str].connect(self.button_pressed) # type: ignore self.buttonOK.pressed.connect(self.accept) self.buttonCancel.pressed.connect(self.close) self.outputLineEdit.focusOutEvent = self.focusOutEvent doubleValidator = QDoubleValidator() self.outputLineEdit.setValidator(doubleValidator)
def _connectSignals(self): self._signalMapper = QSignalMapper() for widgetName, modelAttr in self.FIELDS: widget = getattr(self, widgetName) self._widget2ModelAttr[widget] = modelAttr self._signalMapper.setMapping(widget, widget) if isinstance(widget, QComboBox): widget.currentIndexChanged.connect(self._signalMapper.map) elif isinstance(widget, QSpinBox): widget.valueChanged.connect(self._signalMapper.map) elif isinstance(widget, QLineEdit): widget.editingFinished.connect(self._signalMapper.map) elif isinstance(widget, QPlainTextEdit): widget.textChanged.connect(self._signalMapper.map) elif isinstance(widget, QCheckBox): widget.stateChanged.connect(self._signalMapper.map) self._signalMapper.mapped[QWidget].connect(self.widgetChanged)
def __init__(self): super(MainWindow, self).__init__() self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdiArea) self.mdiArea.subWindowActivated.connect(self.updateMenus) self.windowMapper = QSignalMapper(self) self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) self.createActions() self.createMenus() self.createToolBars() self.createStatusBar() self.updateMenus() self.readSettings() self.setWindowTitle("MDI")
def __init__(self): super(MainWindow, self).__init__() # remove close & maximize window buttons #self.setWindowFlags(Qt.CustomizeWindowHint|Qt.WindowMinimizeButtonHint) self.setMinimumSize(500, 666) #self.setMaximumSize(1000,666) self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdiArea) self.mdiArea.subWindowActivated.connect(self.updateMenus) self.mdiArea.setViewMode(QMdiArea.TabbedView) self.windowMapper = QSignalMapper(self) self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) self.child = None self.createActions() self.createMenus() self.createStatusBar() self.updateMenus() self.readSettings() self.setWindowTitle("LEKTURE") mytoolbar = QToolBar() #self.toolbar = self.addToolBar() mytoolbar.addAction(self.newAct) mytoolbar.addAction(self.openAct) mytoolbar.addAction(self.saveAct) mytoolbar.addAction(self.saveAsAct) mytoolbar.addSeparator() mytoolbar.addAction(self.outputsAct) mytoolbar.addAction(self.scenarioAct) self.scenarioAct.setVisible(False) mytoolbar.setMovable(False) mytoolbar.setFixedWidth(60) self.addToolBar(Qt.LeftToolBarArea, mytoolbar)
def initDialog(self): """ 1. Create buttons according to available scaffold sequences and add them to the dialog. 2. Map the clicked signal of those buttons to keep track of what sequence gets selected. 3. Watch the tabWidget change signal to determine whether a standard or custom sequence should be applied. """ uiDlg = Ui_AddSeqDialog() uiDlg.setupUi(self.dialog) self.signalMapper = QSignalMapper(self) # set up the radio buttons for i, name in enumerate(sorted(sequences.keys())): radioButton = QRadioButton(uiDlg.groupBox) radioButton.setObjectName(name + "Button") radioButton.setText(name) self.buttons.append(radioButton) uiDlg.verticalLayout.addWidget(radioButton) self.signalMapper.setMapping(radioButton, i) radioButton.clicked.connect(self.signalMapper.map) self.signalMapper.mapped.connect(self.standardSequenceChangedSlot) uiDlg.tabWidget.currentChanged.connect(self.tabWidgetChangedSlot) # disable apply until valid option or custom sequence is chosen self.applyButton = uiDlg.customButtonBox.button(QDialogButtonBox.Apply) self.applyButton.setEnabled(False) # watch sequence textedit box to validate custom sequences self.seqBox = uiDlg.seqTextEdit self.seqBox.textChanged.connect(self.validateCustomSequence) self.highlighter = DNAHighlighter(self.seqBox) # finally, pre-click the M13mp18 radio button self.buttons[0].click() buttons = self.buttons self.dialog.setFocusProxy(uiDlg.groupBox) self.dialog.setFocusPolicy(Qt.TabFocus) uiDlg.groupBox.setFocusPolicy(Qt.TabFocus) for i in range(len(buttons)-1): uiDlg.groupBox.setTabOrder(buttons[i], buttons[i+1])
def main(icon_spec): app = QApplication(sys.argv) main_window = QMainWindow() def sigint_handler(*args): main_window.close() signal.signal(signal.SIGINT, sigint_handler) # the timer enables triggering the sigint_handler signal_timer = QTimer() signal_timer.start(100) signal_timer.timeout.connect(lambda: None) tool_bar = QToolBar() main_window.addToolBar(Qt.TopToolBarArea, tool_bar) table_view = QTableView() table_view.setSelectionBehavior(QAbstractItemView.SelectRows) table_view.setSelectionMode(QAbstractItemView.SingleSelection) table_view.setSortingEnabled(True) main_window.setCentralWidget(table_view) proxy_model = QSortFilterProxyModel() proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive) proxy_model.setFilterKeyColumn(1) table_view.setModel(proxy_model) proxy_model.layoutChanged.connect(table_view.resizeRowsToContents) item_model = QStandardItemModel() proxy_model.setSourceModel(item_model) # get all icons and their available sizes icons = [] all_sizes = set([]) for context, icon_names in icon_spec: for icon_name in icon_names: icon = QIcon.fromTheme(icon_name) sizes = [] for size in icon.availableSizes(): size = (size.width(), size.height()) sizes.append(size) all_sizes.add(size) sizes.sort() icons.append({ 'context': context, 'icon_name': icon_name, 'icon': icon, 'sizes': sizes, }) all_sizes = list(all_sizes) all_sizes.sort() # input field for filter def filter_changed(value): proxy_model.setFilterRegExp(value) table_view.resizeRowsToContents() filter_line_edit = QLineEdit() filter_line_edit.setMaximumWidth(200) filter_line_edit.setPlaceholderText('Filter name') filter_line_edit.setToolTip('Filter name optionally using regular expressions (' + QKeySequence(QKeySequence.Find).toString() + ')') filter_line_edit.textChanged.connect(filter_changed) tool_bar.addWidget(filter_line_edit) # actions to toggle visibility of available sizes/columns def action_toggled(index): column = 2 + index table_view.setColumnHidden(column, not table_view.isColumnHidden(column)) table_view.resizeColumnsToContents() table_view.resizeRowsToContents() signal_mapper = QSignalMapper() for i, size in enumerate(all_sizes): action = QAction('%dx%d' % size, tool_bar) action.setCheckable(True) action.setChecked(True) tool_bar.addAction(action) action.toggled.connect(signal_mapper.map) signal_mapper.setMapping(action, i) # set tool tip and handle key sequence tool_tip = 'Toggle visibility of column' if i < 10: digit = ('%d' % (i + 1))[-1] tool_tip += ' (%s)' % QKeySequence('Ctrl+%s' % digit).toString() action.setToolTip(tool_tip) signal_mapper.mapped.connect(action_toggled) # label columns header_labels = ['context', 'name'] for width, height in all_sizes: header_labels.append('%dx%d' % (width, height)) item_model.setColumnCount(len(header_labels)) item_model.setHorizontalHeaderLabels(header_labels) # fill rows item_model.setRowCount(len(icons)) for row, icon_data in enumerate(icons): # context item = QStandardItem(icon_data['context']) item.setFlags(item.flags() ^ Qt.ItemIsEditable) item_model.setItem(row, 0, item) # icon name item = QStandardItem(icon_data['icon_name']) item.setFlags(item.flags() ^ Qt.ItemIsEditable) item_model.setItem(row, 1, item) for index_in_all_sizes, size in enumerate(all_sizes): column = 2 + index_in_all_sizes if size in icon_data['sizes']: # icon as pixmap to keep specific size item = QStandardItem('') pixmap = icon_data['icon'].pixmap(size[0], size[1]) item.setData(pixmap, Qt.DecorationRole) item.setFlags(item.flags() ^ Qt.ItemIsEditable) item_model.setItem(row, column, item) else: # single space to be sortable against icons item = QStandardItem(' ') item.setFlags(item.flags() ^ Qt.ItemIsEditable) item_model.setItem(row, column, item) table_view.resizeColumnsToContents() # manually set row heights because resizeRowsToContents is not working properly for row, icon_data in enumerate(icons): if len(icon_data['sizes']) > 0: max_size = icon_data['sizes'][-1] table_view.setRowHeight(row, max_size[1]) # enable focus find (ctrl+f) and toggle columns (ctrl+NUM) def main_window_keyPressEvent(self, event, old_keyPressEvent=QMainWindow.keyPressEvent): if event.matches(QKeySequence.Find): filter_line_edit.setFocus() return if event.modifiers() == Qt.ControlModifier and event.key() >= Qt.Key_0 and event.key() <= Qt.Key_9: index = event.key() - Qt.Key_1 if event.key() == Qt.Key_0: index += 10 action = signal_mapper.mapping(index) if action: action.toggle() return old_keyPressEvent(self, event) main_window.keyPressEvent = MethodType(main_window_keyPressEvent, table_view) # enable copy (ctrl+c) name of icon to clipboard def table_view_keyPressEvent(self, event, old_keyPressEvent=QTableView.keyPressEvent): if event.matches(QKeySequence.Copy): selection_model = self.selectionModel() if selection_model.hasSelection(): index = selection_model.selectedRows()[0] source_index = self.model().mapToSource(index) item = self.model().sourceModel().item(source_index.row(), 1) icon_name = item.data(Qt.EditRole) app.clipboard().setText(icon_name.toString()) return old_keyPressEvent(self, event) table_view.keyPressEvent = MethodType(table_view_keyPressEvent, table_view) main_window.showMaximized() return app.exec_()
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdiArea) self.mdiArea.subWindowActivated.connect(self.updateMenus) self.windowMapper = QSignalMapper(self) self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) self.createActions() self.createMenus() self.createToolBars() self.createStatusBar() self.updateMenus() self.readSettings() self.setWindowTitle("MDI") def closeEvent(self, event): self.mdiArea.closeAllSubWindows() if self.mdiArea.currentSubWindow(): event.ignore() else: self.writeSettings() event.accept() def newFile(self): child = self.createMdiChild() child.newFile() child.show() def open(self): fileName, _ = QFileDialog.getOpenFileName(self) if fileName: existing = self.findMdiChild(fileName) if existing: self.mdiArea.setActiveSubWindow(existing) return child = self.createMdiChild() if child.loadFile(fileName): self.statusBar().showMessage("File loaded", 2000) child.show() else: child.close() def save(self): if self.activeMdiChild() and self.activeMdiChild().save(): self.statusBar().showMessage("File saved", 2000) def saveAs(self): if self.activeMdiChild() and self.activeMdiChild().saveAs(): self.statusBar().showMessage("File saved", 2000) def cut(self): if self.activeMdiChild(): self.activeMdiChild().cut() def copy(self): if self.activeMdiChild(): self.activeMdiChild().copy() def paste(self): if self.activeMdiChild(): self.activeMdiChild().paste() def about(self): QMessageBox.about(self, "About MDI", "The <b>MDI</b> example demonstrates how to write multiple " "document interface applications using Qt.") def updateMenus(self): hasMdiChild = (self.activeMdiChild() is not None) self.saveAct.setEnabled(hasMdiChild) self.saveAsAct.setEnabled(hasMdiChild) self.pasteAct.setEnabled(hasMdiChild) self.closeAct.setEnabled(hasMdiChild) self.closeAllAct.setEnabled(hasMdiChild) self.tileAct.setEnabled(hasMdiChild) self.cascadeAct.setEnabled(hasMdiChild) self.nextAct.setEnabled(hasMdiChild) self.previousAct.setEnabled(hasMdiChild) self.separatorAct.setVisible(hasMdiChild) hasSelection = (self.activeMdiChild() is not None and self.activeMdiChild().textCursor().hasSelection()) self.cutAct.setEnabled(hasSelection) self.copyAct.setEnabled(hasSelection) def updateWindowMenu(self): self.windowMenu.clear() self.windowMenu.addAction(self.closeAct) self.windowMenu.addAction(self.closeAllAct) self.windowMenu.addSeparator() self.windowMenu.addAction(self.tileAct) self.windowMenu.addAction(self.cascadeAct) self.windowMenu.addSeparator() self.windowMenu.addAction(self.nextAct) self.windowMenu.addAction(self.previousAct) self.windowMenu.addAction(self.separatorAct) windows = self.mdiArea.subWindowList() self.separatorAct.setVisible(len(windows) != 0) for i, window in enumerate(windows): child = window.widget() text = "%d %s" % (i + 1, child.userFriendlyCurrentFile()) if i < 9: text = '&' + text action = self.windowMenu.addAction(text) action.setCheckable(True) action.setChecked(child is self.activeMdiChild()) action.triggered.connect(self.windowMapper.map) self.windowMapper.setMapping(action, window) def createMdiChild(self): child = MdiChild() self.mdiArea.addSubWindow(child) child.copyAvailable.connect(self.cutAct.setEnabled) child.copyAvailable.connect(self.copyAct.setEnabled) return child def createActions(self): self.newAct = QAction(QIcon(':/images/new.png'), "&New", self, shortcut=QKeySequence.New, statusTip="Create a new file", triggered=self.newFile) self.openAct = QAction(QIcon(':/images/open.png'), "&Open...", self, shortcut=QKeySequence.Open, statusTip="Open an existing file", triggered=self.open) self.saveAct = QAction(QIcon(':/images/save.png'), "&Save", self, shortcut=QKeySequence.Save, statusTip="Save the document to disk", triggered=self.save) self.saveAsAct = QAction("Save &As...", self, shortcut=QKeySequence.SaveAs, statusTip="Save the document under a new name", triggered=self.saveAs) self.exitAct = QAction("E&xit", self, shortcut=QKeySequence.Quit, statusTip="Exit the application", triggered=QApplication.instance().closeAllWindows) self.cutAct = QAction(QIcon(':/images/cut.png'), "Cu&t", self, shortcut=QKeySequence.Cut, statusTip="Cut the current selection's contents to the clipboard", triggered=self.cut) self.copyAct = QAction(QIcon(':/images/copy.png'), "&Copy", self, shortcut=QKeySequence.Copy, statusTip="Copy the current selection's contents to the clipboard", triggered=self.copy) self.pasteAct = QAction(QIcon(':/images/paste.png'), "&Paste", self, shortcut=QKeySequence.Paste, statusTip="Paste the clipboard's contents into the current selection", triggered=self.paste) self.closeAct = QAction("Cl&ose", self, statusTip="Close the active window", triggered=self.mdiArea.closeActiveSubWindow) self.closeAllAct = QAction("Close &All", self, statusTip="Close all the windows", triggered=self.mdiArea.closeAllSubWindows) self.tileAct = QAction("&Tile", self, statusTip="Tile the windows", triggered=self.mdiArea.tileSubWindows) self.cascadeAct = QAction("&Cascade", self, statusTip="Cascade the windows", triggered=self.mdiArea.cascadeSubWindows) self.nextAct = QAction("Ne&xt", self, shortcut=QKeySequence.NextChild, statusTip="Move the focus to the next window", triggered=self.mdiArea.activateNextSubWindow) self.previousAct = QAction("Pre&vious", self, shortcut=QKeySequence.PreviousChild, statusTip="Move the focus to the previous window", triggered=self.mdiArea.activatePreviousSubWindow) self.separatorAct = QAction(self) self.separatorAct.setSeparator(True) self.aboutAct = QAction("&About", self, statusTip="Show the application's About box", triggered=self.about) self.aboutQtAct = QAction("About &Qt", self, statusTip="Show the Qt library's About box", triggered=QApplication.instance().aboutQt) def createMenus(self): self.fileMenu = self.menuBar().addMenu("&File") self.fileMenu.addAction(self.newAct) self.fileMenu.addAction(self.openAct) self.fileMenu.addAction(self.saveAct) self.fileMenu.addAction(self.saveAsAct) self.fileMenu.addSeparator() action = self.fileMenu.addAction("Switch layout direction") action.triggered.connect(self.switchLayoutDirection) self.fileMenu.addAction(self.exitAct) self.editMenu = self.menuBar().addMenu("&Edit") self.editMenu.addAction(self.cutAct) self.editMenu.addAction(self.copyAct) self.editMenu.addAction(self.pasteAct) self.windowMenu = self.menuBar().addMenu("&Window") self.updateWindowMenu() self.windowMenu.aboutToShow.connect(self.updateWindowMenu) self.menuBar().addSeparator() self.helpMenu = self.menuBar().addMenu("&Help") self.helpMenu.addAction(self.aboutAct) self.helpMenu.addAction(self.aboutQtAct) def createToolBars(self): self.fileToolBar = self.addToolBar("File") self.fileToolBar.addAction(self.newAct) self.fileToolBar.addAction(self.openAct) self.fileToolBar.addAction(self.saveAct) self.editToolBar = self.addToolBar("Edit") self.editToolBar.addAction(self.cutAct) self.editToolBar.addAction(self.copyAct) self.editToolBar.addAction(self.pasteAct) def createStatusBar(self): self.statusBar().showMessage("Ready") def readSettings(self): settings = QSettings('Trolltech', 'MDI Example') pos = settings.value('pos', QPoint(200, 200)) size = settings.value('size', QSize(400, 400)) self.move(pos) self.resize(size) def writeSettings(self): settings = QSettings('Trolltech', 'MDI Example') settings.setValue('pos', self.pos()) settings.setValue('size', self.size()) def activeMdiChild(self): activeSubWindow = self.mdiArea.activeSubWindow() if activeSubWindow: return activeSubWindow.widget() return None def findMdiChild(self, fileName): canonicalFilePath = QFileInfo(fileName).canonicalFilePath() for window in self.mdiArea.subWindowList(): if window.widget().currentFile() == canonicalFilePath: return window return None def switchLayoutDirection(self): if self.layoutDirection() == Qt.LeftToRight: QApplication.setLayoutDirection(Qt.RightToLeft) else: QApplication.setLayoutDirection(Qt.LeftToRight) def setActiveSubWindow(self, window): if window: self.mdiArea.setActiveSubWindow(window)
def makePopupMenu(self): index = self.currentIndex() sel = self.getSelection() clipboard = qApp.clipboard() menu = QMenu(self) # Get index under cursor pos = self.viewport().mapFromGlobal(QCursor.pos()) mouseIndex = self.indexAt(pos) # Get index's title if mouseIndex.isValid(): title = mouseIndex.internalPointer().title() elif self.rootIndex().parent().isValid(): # mouseIndex is the background of an item, so we check the parent mouseIndex = self.rootIndex().parent() title = mouseIndex.internalPointer().title() else: title = qApp.translate("outlineBasics", "Root") if len(title) > 25: title = title[:25] + "…" # Open Item action self.actOpen = QAction(QIcon.fromTheme("go-right"), qApp.translate("outlineBasics", "Open {}".format(title)), menu) self.actOpen.triggered.connect(self.openItem) menu.addAction(self.actOpen) # Open item(s) in new tab if mouseIndex in sel and len(sel) > 1: actionTitle = qApp.translate("outlineBasics", "Open {} items in new tabs").format(len(sel)) self._indexesToOpen = sel else: actionTitle = qApp.translate("outlineBasics", "Open {} in a new tab").format(title) self._indexesToOpen = [mouseIndex] self.actNewTab = QAction(QIcon.fromTheme("go-right"), actionTitle, menu) self.actNewTab.triggered.connect(self.openItemsInNewTabs) menu.addAction(self.actNewTab) menu.addSeparator() # Add text / folder self.actAddFolder = QAction(QIcon.fromTheme("folder-new"), qApp.translate("outlineBasics", "New &Folder"), menu) self.actAddFolder.triggered.connect(self.addFolder) menu.addAction(self.actAddFolder) self.actAddText = QAction(QIcon.fromTheme("document-new"), qApp.translate("outlineBasics", "New &Text"), menu) self.actAddText.triggered.connect(self.addText) menu.addAction(self.actAddText) menu.addSeparator() # Copy, cut, paste, duplicate self.actCut = QAction(QIcon.fromTheme("edit-cut"), qApp.translate("outlineBasics", "C&ut"), menu) self.actCut.triggered.connect(self.cut) menu.addAction(self.actCut) self.actCopy = QAction(QIcon.fromTheme("edit-copy"), qApp.translate("outlineBasics", "&Copy"), menu) self.actCopy.triggered.connect(self.copy) menu.addAction(self.actCopy) self.actPaste = QAction(QIcon.fromTheme("edit-paste"), qApp.translate("outlineBasics", "&Paste"), menu) self.actPaste.triggered.connect(self.paste) menu.addAction(self.actPaste) # Rename / duplicate / remove items self.actDelete = QAction(QIcon.fromTheme("edit-delete"), qApp.translate("outlineBasics", "&Delete"), menu) self.actDelete.triggered.connect(self.delete) menu.addAction(self.actDelete) self.actRename = QAction(QIcon.fromTheme("edit-rename"), qApp.translate("outlineBasics", "&Rename"), menu) self.actRename.triggered.connect(self.rename) menu.addAction(self.actRename) menu.addSeparator() # POV self.menuPOV = QMenu(qApp.translate("outlineBasics", "Set POV"), menu) mw = mainWindow() a = QAction(QIcon.fromTheme("dialog-no"), qApp.translate("outlineBasics", "None"), self.menuPOV) a.triggered.connect(lambda: self.setPOV("")) self.menuPOV.addAction(a) self.menuPOV.addSeparator() menus = [] for i in [qApp.translate("outlineBasics", "Main"), qApp.translate("outlineBasics", "Secondary"), qApp.translate("outlineBasics", "Minor")]: m = QMenu(i, self.menuPOV) menus.append(m) self.menuPOV.addMenu(m) mpr = QSignalMapper(self.menuPOV) for i in range(mw.mdlCharacter.rowCount()): a = QAction(mw.mdlCharacter.icon(i), mw.mdlCharacter.name(i), self.menuPOV) a.triggered.connect(mpr.map) mpr.setMapping(a, int(mw.mdlCharacter.ID(i))) imp = toInt(mw.mdlCharacter.importance(i)) menus[2 - imp].addAction(a) mpr.mapped.connect(self.setPOV) menu.addMenu(self.menuPOV) # Status self.menuStatus = QMenu(qApp.translate("outlineBasics", "Set Status"), menu) # a = QAction(QIcon.fromTheme("dialog-no"), qApp.translate("outlineBasics", "None"), self.menuStatus) # a.triggered.connect(lambda: self.setStatus("")) # self.menuStatus.addAction(a) # self.menuStatus.addSeparator() mpr = QSignalMapper(self.menuStatus) for i in range(mw.mdlStatus.rowCount()): a = QAction(mw.mdlStatus.item(i, 0).text(), self.menuStatus) a.triggered.connect(mpr.map) mpr.setMapping(a, i) self.menuStatus.addAction(a) mpr.mapped.connect(self.setStatus) menu.addMenu(self.menuStatus) # Labels self.menuLabel = QMenu(qApp.translate("outlineBasics", "Set Label"), menu) mpr = QSignalMapper(self.menuLabel) for i in range(mw.mdlLabels.rowCount()): a = QAction(mw.mdlLabels.item(i, 0).icon(), mw.mdlLabels.item(i, 0).text(), self.menuLabel) a.triggered.connect(mpr.map) mpr.setMapping(a, i) self.menuLabel.addAction(a) mpr.mapped.connect(self.setLabel) menu.addMenu(self.menuLabel) menu.addSeparator() # Custom icons if self.menuCustomIcons: menu.addMenu(self.menuCustomIcons) else: self.menuCustomIcons = QMenu(qApp.translate("outlineBasics", "Set Custom Icon"), menu) a = QAction(qApp.translate("outlineBasics", "Restore to default"), self.menuCustomIcons) a.triggered.connect(lambda: self.setCustomIcon("")) self.menuCustomIcons.addAction(a) self.menuCustomIcons.addSeparator() txt = QLineEdit() txt.textChanged.connect(self.filterLstIcons) txt.setPlaceholderText("Filter icons") txt.setStyleSheet("QLineEdit { background: transparent; border: none; }") act = QWidgetAction(self.menuCustomIcons) act.setDefaultWidget(txt) self.menuCustomIcons.addAction(act) self.lstIcons = QListWidget() for i in customIcons(): item = QListWidgetItem() item.setIcon(QIcon.fromTheme(i)) item.setData(Qt.UserRole, i) item.setToolTip(i) self.lstIcons.addItem(item) self.lstIcons.itemClicked.connect(self.setCustomIconFromItem) self.lstIcons.setViewMode(self.lstIcons.IconMode) self.lstIcons.setUniformItemSizes(True) self.lstIcons.setResizeMode(self.lstIcons.Adjust) self.lstIcons.setMovement(self.lstIcons.Static) self.lstIcons.setStyleSheet("background: transparent; background: none;") self.filterLstIcons("") act = QWidgetAction(self.menuCustomIcons) act.setDefaultWidget(self.lstIcons) self.menuCustomIcons.addAction(act) menu.addMenu(self.menuCustomIcons) # Disabling stuff if not clipboard.mimeData().hasFormat("application/xml"): self.actPaste.setEnabled(False) if len(sel) == 0: self.actCopy.setEnabled(False) self.actCut.setEnabled(False) self.actRename.setEnabled(False) self.actDelete.setEnabled(False) self.menuPOV.setEnabled(False) self.menuStatus.setEnabled(False) self.menuLabel.setEnabled(False) self.menuCustomIcons.setEnabled(False) if len(sel) > 1: self.actRename.setEnabled(False) return menu
class NumPad(QDialog, Ui_numPad): """Numpad for user input. Enables the user to insert numbers on a touchscreen. """ def __init__(self, parent: QWidget=None, text: str="") -> None: super(NumPad, self).__init__(parent) self.setupUi(self) # type: ignore try: suffixIndex = text.index(" ") except ValueError: self.outputLineEdit.setText(text) else: self.outputLineEdit.setText(text[:suffixIndex]) self.outputLineEdit.setSelection(0, len(self.outputLineEdit.text())) self.outputLineEdit.setFocus() self.signal_mapper = QSignalMapper(self) self.signal_mapper.setMapping(self.button0, "0") self.signal_mapper.setMapping(self.button1, "1") self.signal_mapper.setMapping(self.button2, "2") self.signal_mapper.setMapping(self.button3, "3") self.signal_mapper.setMapping(self.button4, "4") self.signal_mapper.setMapping(self.button5, "5") self.signal_mapper.setMapping(self.button6, "6") self.signal_mapper.setMapping(self.button7, "7") self.signal_mapper.setMapping(self.button8, "8") self.signal_mapper.setMapping(self.button9, "9") self.signal_mapper.setMapping(self.buttonDecimal, DECIMAL_SEPARATOR) self.signal_mapper.setMapping(self.buttonDel, "DEL") self.button0.pressed.connect(self.signal_mapper.map) self.button1.pressed.connect(self.signal_mapper.map) self.button2.pressed.connect(self.signal_mapper.map) self.button3.pressed.connect(self.signal_mapper.map) self.button4.pressed.connect(self.signal_mapper.map) self.button5.pressed.connect(self.signal_mapper.map) self.button6.pressed.connect(self.signal_mapper.map) self.button7.pressed.connect(self.signal_mapper.map) self.button8.pressed.connect(self.signal_mapper.map) self.button9.pressed.connect(self.signal_mapper.map) self.buttonDecimal.pressed.connect(self.signal_mapper.map) self.buttonDel.pressed.connect(self.signal_mapper.map) self.signal_mapper.mapped[str].connect(self.button_pressed) # type: ignore self.buttonOK.pressed.connect(self.accept) self.buttonCancel.pressed.connect(self.close) self.outputLineEdit.focusOutEvent = self.focusOutEvent doubleValidator = QDoubleValidator() self.outputLineEdit.setValidator(doubleValidator) @pyqtSlot(str) def button_pressed(self, value: str) -> None: """Handle a pressed button.""" cursor_position = self.outputLineEdit.cursorPosition() # Bei markierten Zeichen diese löschen if len(self.outputLineEdit.selectedText()) > 0: cursor_position = self.outputLineEdit.selectionStart() selection_length = len(self.outputLineEdit.selectedText()) self.outputLineEdit.setText( string_remove_by_index( self.outputLineEdit.text(), start_index=cursor_position, length=selection_length ) ) # Bei DEL letztes Zeichen löschen elif value == "DEL": # self.outputLineEdit.setText(self.outputLineEdit.text().remove( # self.outputLineEdit.cursorPosition() - 1, 1)) self.outputLineEdit.setText( string_remove_by_index( self.outputLineEdit.text(), start_index=self.outputLineEdit.cursorPosition() - 1, length=1) ) # Wenn nicht DEL, dann Zeichen schreiben if not value == "DEL": if value == DECIMAL_SEPARATOR: if DECIMAL_SEPARATOR not in self.outputLineEdit.text(): self.outputLineEdit.setText( string_insert(self.outputLineEdit.text(), str(value), cursor_position) ) else: self.outputLineEdit.setText( string_insert(self.outputLineEdit.text(), str(value), cursor_position) )
class MainWindow(QMainWindow, Ui_MainWindow): """docstring for MainWindow.""" def __init__(self, parent=None): super(MainWindow, self).__init__() self._csvFilePath = "" self.serialport = serial.Serial() self.receiver_thread = readerThread(self) self.receiver_thread.setPort(self.serialport) self._localEcho = None self._viewMode = None self._quickSendOptRow = 1 self.setupUi(self) self.setCorner(Qt.TopLeftCorner, Qt.LeftDockWidgetArea) self.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea) font = QtGui.QFont() font.setFamily(EDITOR_FONT) font.setPointSize(9) self.txtEdtOutput.setFont(font) self.txtEdtInput.setFont(font) #self.quickSendTable.setFont(font) if UI_FONT is not None: font = QtGui.QFont() font.setFamily(UI_FONT) font.setPointSize(9) self.dockWidget_PortConfig.setFont(font) self.dockWidget_SendHex.setFont(font) self.dockWidget_QuickSend.setFont(font) self.setupMenu() self.setupFlatUi() self.onEnumPorts() icon = QtGui.QIcon(":/MyTerm.ico") self.setWindowIcon(icon) self.actionAbout.setIcon(icon) self.defaultStyleWidget = QWidget() self.defaultStyleWidget.setWindowIcon(icon) icon = QtGui.QIcon(":/qt_logo_16.ico") self.actionAbout_Qt.setIcon(icon) self._viewGroup = QActionGroup(self) self._viewGroup.addAction(self.actionAscii) self._viewGroup.addAction(self.actionHex_lowercase) self._viewGroup.addAction(self.actionHEX_UPPERCASE) self._viewGroup.setExclusive(True) # bind events self.actionOpen_Cmd_File.triggered.connect(self.openQuickSend) self.actionSave_Log.triggered.connect(self.onSaveLog) self.actionExit.triggered.connect(self.onExit) self.actionOpen.triggered.connect(self.openPort) self.actionClose.triggered.connect(self.closePort) self.actionPort_Config_Panel.triggered.connect(self.onTogglePrtCfgPnl) self.actionQuick_Send_Panel.triggered.connect(self.onToggleQckSndPnl) self.actionSend_Hex_Panel.triggered.connect(self.onToggleHexPnl) self.dockWidget_PortConfig.visibilityChanged.connect(self.onVisiblePrtCfgPnl) self.dockWidget_QuickSend.visibilityChanged.connect(self.onVisibleQckSndPnl) self.dockWidget_SendHex.visibilityChanged.connect(self.onVisibleHexPnl) self.actionLocal_Echo.triggered.connect(self.onLocalEcho) self.actionAlways_On_Top.triggered.connect(self.onAlwaysOnTop) self.actionAscii.triggered.connect(self.onViewChanged) self.actionHex_lowercase.triggered.connect(self.onViewChanged) self.actionHEX_UPPERCASE.triggered.connect(self.onViewChanged) self.actionAbout.triggered.connect(self.onAbout) self.actionAbout_Qt.triggered.connect(self.onAboutQt) self.btnOpen.clicked.connect(self.onOpen) self.btnClear.clicked.connect(self.onClear) self.btnSaveLog.clicked.connect(self.onSaveLog) self.btnEnumPorts.clicked.connect(self.onEnumPorts) self.btnSendHex.clicked.connect(self.onSend) self.receiver_thread.read.connect(self.onReceive) self.receiver_thread.exception.connect(self.onReaderExcept) self._signalMapQuickSendOpt = QSignalMapper(self) self._signalMapQuickSendOpt.mapped[int].connect(self.onQuickSendOptions) self._signalMapQuickSend = QSignalMapper(self) self._signalMapQuickSend.mapped[int].connect(self.onQuickSend) # initial action self.actionHEX_UPPERCASE.setChecked(True) self.receiver_thread.setViewMode(VIEWMODE_HEX_UPPERCASE) self.initQuickSend() self.restoreLayout() self.moveScreenCenter() self.syncMenu() if self.isMaximized(): self.setMaximizeButton("restore") else: self.setMaximizeButton("maximize") self.loadSettings() def setupMenu(self): self.menuMenu = QtWidgets.QMenu() self.menuMenu.setTitle("&File") self.menuMenu.setObjectName("menuMenu") self.menuView = QtWidgets.QMenu(self.menuMenu) self.menuView.setTitle("&View") self.menuView.setObjectName("menuView") self.menuView.addAction(self.actionAscii) self.menuView.addAction(self.actionHex_lowercase) self.menuView.addAction(self.actionHEX_UPPERCASE) self.menuMenu.addAction(self.actionOpen_Cmd_File) self.menuMenu.addAction(self.actionSave_Log) self.menuMenu.addSeparator() self.menuMenu.addAction(self.actionPort_Config_Panel) self.menuMenu.addAction(self.actionQuick_Send_Panel) self.menuMenu.addAction(self.actionSend_Hex_Panel) self.menuMenu.addAction(self.menuView.menuAction()) self.menuMenu.addAction(self.actionLocal_Echo) self.menuMenu.addAction(self.actionAlways_On_Top) self.menuMenu.addSeparator() self.menuMenu.addAction(self.actionAbout) self.menuMenu.addAction(self.actionAbout_Qt) self.menuMenu.addSeparator() self.menuMenu.addAction(self.actionExit) self.sendOptMenu = QtWidgets.QMenu() self.actionSend_Hex = QtWidgets.QAction(self) self.actionSend_Hex.setText("Send &Hex") self.actionSend_Hex.setStatusTip("Send Hex (e.g. 31 32 FF)") self.actionSend_Asc = QtWidgets.QAction(self) self.actionSend_Asc.setText("Send &Asc") self.actionSend_Asc.setStatusTip("Send Asc (e.g. abc123)") self.actionSend_TFH = QtWidgets.QAction(self) self.actionSend_TFH.setText("Send &Text file as hex") self.actionSend_TFH.setStatusTip('Send text file as hex (e.g. strings "31 32 FF" in the file)') self.actionSend_TFA = QtWidgets.QAction(self) self.actionSend_TFA.setText("Send t&Ext file as asc") self.actionSend_TFA.setStatusTip('Send text file as asc (e.g. strings "abc123" in the file)') self.actionSend_FB = QtWidgets.QAction(self) self.actionSend_FB.setText("Send &Bin/Hex file") self.actionSend_FB.setStatusTip("Send a bin file or a hex file") self.sendOptMenu.addAction(self.actionSend_Hex) self.sendOptMenu.addAction(self.actionSend_Asc) self.sendOptMenu.addAction(self.actionSend_TFH) self.sendOptMenu.addAction(self.actionSend_TFA) self.sendOptMenu.addAction(self.actionSend_FB) self.actionSend_Hex.triggered.connect(self.onSetSendHex) self.actionSend_Asc.triggered.connect(self.onSetSendAsc) self.actionSend_TFH.triggered.connect(self.onSetSendTFH) self.actionSend_TFA.triggered.connect(self.onSetSendTFA) self.actionSend_FB.triggered.connect(self.onSetSendFB) def setupFlatUi(self): self._dragPos = self.pos() self._isDragging = False self.setMouseTracking(True) self.setWindowFlags(Qt.FramelessWindowHint) self.setStyleSheet(""" QWidget { background-color: %(BackgroundColor)s; /*background-image: url(:/background.png);*/ outline: none; } QLabel { color:%(TextColor)s; font-size:12px; /*font-family:Century;*/ } QComboBox { color:%(TextColor)s; font-size:12px; /*font-family:Century;*/ } QComboBox { border: none; padding: 1px 1px 1px 3px; } QComboBox:editable { background: white; } QComboBox:!editable, QComboBox::drop-down:editable { background: #62c7e0; } QComboBox:!editable:hover, QComboBox::drop-down:editable:hover { background: #c7eaf3; } QComboBox:!editable:pressed, QComboBox::drop-down:editable:pressed { background: #35b6d7; } QComboBox:on { padding-top: 3px; padding-left: 4px; } QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 16px; border: none; } QComboBox::down-arrow { image: url(:/downarrow.png); } QComboBox::down-arrow:on { image: url(:/uparrow.png); } QGroupBox { color:%(TextColor)s; font-size:12px; /*font-family:Century;*/ border: 1px solid gray; margin-top: 15px; } QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; left:5px; top:3px; } QCheckBox { color:%(TextColor)s; spacing: 5px; font-size:12px; /*font-family:Century;*/ } QCheckBox::indicator:unchecked { image: url(:/checkbox_unchecked.png); } QCheckBox::indicator:unchecked:hover { image: url(:/checkbox_unchecked_hover.png); } QCheckBox::indicator:unchecked:pressed { image: url(:/checkbox_unchecked_pressed.png); } QCheckBox::indicator:checked { image: url(:/checkbox_checked.png); } QCheckBox::indicator:checked:hover { image: url(:/checkbox_checked_hover.png); } QCheckBox::indicator:checked:pressed { image: url(:/checkbox_checked_pressed.png); } QScrollBar:horizontal { background-color:%(BackgroundColor)s; border: none; height: 15px; margin: 0px 20px 0 20px; } QScrollBar::handle:horizontal { background: %(ScrollBar_Handle)s; min-width: 20px; } QScrollBar::add-line:horizontal { image: url(:/rightarrow.png); border: none; background: %(ScrollBar_Line)s; width: 20px; subcontrol-position: right; subcontrol-origin: margin; } QScrollBar::sub-line:horizontal { image: url(:/leftarrow.png); border: none; background: %(ScrollBar_Line)s; width: 20px; subcontrol-position: left; subcontrol-origin: margin; } QScrollBar:vertical { background-color:%(BackgroundColor)s; border: none; width: 15px; margin: 20px 0px 20px 0px; } QScrollBar::handle::vertical { background: %(ScrollBar_Handle)s; min-height: 20px; } QScrollBar::add-line::vertical { image: url(:/downarrow.png); border: none; background: %(ScrollBar_Line)s; height: 20px; subcontrol-position: bottom; subcontrol-origin: margin; } QScrollBar::sub-line::vertical { image: url(:/uparrow.png); border: none; background: %(ScrollBar_Line)s; height: 20px; subcontrol-position: top; subcontrol-origin: margin; } QTableView { background-color: white; /*selection-background-color: #FF92BB;*/ border: 1px solid %(TableView_Border)s; color: %(TextColor)s; } QTableView::focus { /*border: 1px solid #2a7fff;*/ } QTableView QTableCornerButton::section { border: none; border-right: 1px solid %(TableView_Border)s; border-bottom: 1px solid %(TableView_Border)s; background-color: %(TableView_Corner)s; } QTableView QWidget { background-color: white; } QTableView::item:focus { border: 1px red; background-color: transparent; color: %(TextColor)s; } QHeaderView::section { border: none; border-right: 1px solid %(TableView_Border)s; border-bottom: 1px solid %(TableView_Border)s; padding-left: 2px; padding-right: 2px; color: #444444; background-color: %(TableView_Header)s; } QTextEdit { background-color:white; color:%(TextColor)s; border-top: none; border-bottom: none; border-left: 2px solid %(BackgroundColor)s; border-right: 2px solid %(BackgroundColor)s; } QTextEdit::focus { } QToolButton, QPushButton { background-color:#30a7b8; border:none; color:#ffffff; font-size:12px; /*font-family:Century;*/ } QToolButton:hover, QPushButton:hover { background-color:#51c0d1; } QToolButton:pressed, QPushButton:pressed { background-color:#3a9ecc; } QMenuBar { color: %(TextColor)s; height: 24px; } QMenuBar::item { background-color: transparent; margin: 8px 0px 0px 0px; padding: 1px 8px 1px 8px; height: 15px; } QMenuBar::item:selected { background: #51c0d1; } QMenuBar::item:pressed { } /* QMenu { color: %(TextColor)s; background: #ffffff; } QMenu { margin: 2px; } QMenu::item { padding: 2px 25px 2px 21px; border: 1px solid transparent; } QMenu::item:selected { background: #51c0d1; } QMenu::icon { background: transparent; border: 2px inset transparent; }*/ QDockWidget { font-size:12px; /*font-family:Century;*/ color: %(TextColor)s; titlebar-close-icon: none; titlebar-normal-icon: none; } QDockWidget::title { margin: 0; padding: 2px; subcontrol-origin: content; subcontrol-position: right top; text-align: left; background: #67baed; } QDockWidget::float-button { max-width: 12px; max-height: 12px; background-color:transparent; border:none; image: url(:/restore_inactive.png); } QDockWidget::float-button:hover { background-color:#227582; image: url(:/restore_active.png); } QDockWidget::float-button:pressed { padding: 0; background-color:#14464e; image: url(:/restore_active.png); } QDockWidget::close-button { max-width: 12px; max-height: 12px; background-color:transparent; border:none; image: url(:/close_inactive.png); } QDockWidget::close-button:hover { background-color:#ea5e00; image: url(:/close_active.png); } QDockWidget::close-button:pressed { background-color:#994005; image: url(:/close_active.png); padding: 0; } """ % dict( BackgroundColor = '#99d9ea', TextColor = '#202020', ScrollBar_Handle = '#61b9e1', ScrollBar_Line = '#7ecfe4', TableView_Corner = '#8ae6d2', TableView_Header = '#8ae6d2', TableView_Border = '#eeeeee' )) self.dockWidgetContents.setStyleSheet(""" QPushButton { min-height:23px; } """) self.dockWidget_QuickSend.setStyleSheet(""" QToolButton, QPushButton { background-color:#27b798; /*font-family:Consolas;*/ /*font-size:12px;*/ /*min-width:46px;*/ } QToolButton:hover, QPushButton:hover { background-color:#3bd5b4; } QToolButton:pressed, QPushButton:pressed { background-color:#1d8770; } """) self.dockWidgetContents_2.setStyleSheet(""" QPushButton { min-height:23px; min-width:50px; } """) w = self.frameGeometry().width() self._minBtn = QPushButton(self) self._minBtn.setGeometry(w-103,0,28,24) self._minBtn.clicked.connect(self.onMinimize) self._minBtn.setStyleSheet(""" QPushButton { background-color:transparent; border:none; outline: none; image: url(:/minimize_inactive.png); } QPushButton:hover { background-color:#227582; image: url(:/minimize_active.png); } QPushButton:pressed { background-color:#14464e; image: url(:/minimize_active.png); } """) self._maxBtn = QPushButton(self) self._maxBtn.setGeometry(w-74,0,28,24) self._maxBtn.clicked.connect(self.onMaximize) self.setMaximizeButton("maximize") self._closeBtn = QPushButton(self) self._closeBtn.setGeometry(w-45,0,36,24) self._closeBtn.clicked.connect(self.onExit) self._closeBtn.setStyleSheet(""" QPushButton { background-color:transparent; border:none; outline: none; image: url(:/close_inactive.png); } QPushButton:hover { background-color:#ea5e00; image: url(:/close_active.png); } QPushButton:pressed { background-color:#994005; image: url(:/close_active.png); } """) self.btnMenu = QtWidgets.QToolButton(self) self.btnMenu.setEnabled(True) self.btnMenu.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) self.btnMenu.setIcon(QtGui.QIcon(':/MyTerm.ico')) self.btnMenu.setText('Myterm ') self.btnMenu.setGeometry(3,3,80,18) self.btnMenu.setMenu(self.menuMenu) self.btnMenu.setPopupMode(QtWidgets.QToolButton.InstantPopup) self.btnRefresh = QtWidgets.QToolButton(self) self.btnRefresh.setEnabled(True) self.btnRefresh.setIcon(QtGui.QIcon(':/refresh.ico')) self.btnRefresh.setGeometry(110,3,18,18) self.btnRefresh.clicked.connect(self.onEnumPorts) self.verticalLayout_1.removeWidget(self.cmbPort) self.cmbPort.setParent(self) self.cmbPort.setGeometry(128,3,60,18) self.verticalLayout_1.removeWidget(self.btnOpen) self.btnOpen.setParent(self) self.btnOpen.setGeometry(210,3,60,18) def resizeEvent(self, event): w = event.size().width() self._minBtn.move(w-103,0) self._maxBtn.move(w-74,0) self._closeBtn.move(w-45,0) def onMinimize(self): self.showMinimized() def isMaximized(self): return ((self.windowState() == Qt.WindowMaximized)) def onMaximize(self): if self.isMaximized(): self.showNormal() self.setMaximizeButton("maximize") else: self.showMaximized() self.setMaximizeButton("restore") def setMaximizeButton(self, style): if "maximize" == style: self._maxBtn.setStyleSheet(""" QPushButton { background-color:transparent; border:none; outline: none; image: url(:/maximize_inactive.png); } QPushButton:hover { background-color:#227582; image: url(:/maximize_active.png); } QPushButton:pressed { background-color:#14464e; image: url(:/maximize_active.png); } """) elif "restore" == style: self._maxBtn.setStyleSheet(""" QPushButton { background-color:transparent; border:none; outline: none; image: url(:/restore_inactive.png); } QPushButton:hover { background-color:#227582; image: url(:/restore_active.png); } QPushButton:pressed { background-color:#14464e; image: url(:/restore_active.png); } """) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self._isDragging = True self._dragPos = event.globalPos() - self.pos() event.accept() def mouseMoveEvent(self, event): if event.buttons() == Qt.LeftButton and self._isDragging and not self.isMaximized(): self.move(event.globalPos() - self._dragPos) event.accept() def mouseReleaseEvent(self, event): self._isDragging = False event.accept() def saveSettings(self): root = ET.Element("MyTerm") GUISettings = ET.SubElement(root, "GUISettings") PortCfg = ET.SubElement(GUISettings, "PortConfig") ET.SubElement(PortCfg, "port").text = self.cmbPort.currentText() ET.SubElement(PortCfg, "baudrate").text = self.cmbBaudRate.currentText() ET.SubElement(PortCfg, "databits").text = self.cmbDataBits.currentText() ET.SubElement(PortCfg, "parity").text = self.cmbParity.currentText() ET.SubElement(PortCfg, "stopbits").text = self.cmbStopBits.currentText() ET.SubElement(PortCfg, "rtscts").text = self.chkRTSCTS.isChecked() and "on" or "off" ET.SubElement(PortCfg, "xonxoff").text = self.chkXonXoff.isChecked() and "on" or "off" View = ET.SubElement(GUISettings, "View") ET.SubElement(View, "LocalEcho").text = self.actionLocal_Echo.isChecked() and "on" or "off" ET.SubElement(View, "ReceiveView").text = self._viewGroup.checkedAction().text() with open(get_config_path('MyTerm.xml'), 'w') as f: f.write('<?xml version="1.0" encoding="UTF-8"?>\n') f.write(ET.tostring(root, encoding='utf-8', pretty_print=True).decode("utf-8")) def loadSettings(self): if os.path.isfile(get_config_path("MyTerm.xml")): with open(get_config_path("MyTerm.xml"), 'r') as f: tree = safeET.parse(f) port = tree.findtext('GUISettings/PortConfig/port', default='') if port != '': self.cmbPort.setCurrentText(port) baudrate = tree.findtext('GUISettings/PortConfig/baudrate', default='38400') if baudrate != '': self.cmbBaudRate.setCurrentText(baudrate) databits = tree.findtext('GUISettings/PortConfig/databits', default='8') id = self.cmbDataBits.findText(databits) if id >= 0: self.cmbDataBits.setCurrentIndex(id) parity = tree.findtext('GUISettings/PortConfig/parity', default='None') id = self.cmbParity.findText(parity) if id >= 0: self.cmbParity.setCurrentIndex(id) stopbits = tree.findtext('GUISettings/PortConfig/stopbits', default='1') id = self.cmbStopBits.findText(stopbits) if id >= 0: self.cmbStopBits.setCurrentIndex(id) rtscts = tree.findtext('GUISettings/PortConfig/rtscts', default='off') if 'on' == rtscts: self.chkRTSCTS.setChecked(True) else: self.chkRTSCTS.setChecked(False) xonxoff = tree.findtext('GUISettings/PortConfig/xonxoff', default='off') if 'on' == xonxoff: self.chkXonXoff.setChecked(True) else: self.chkXonXoff.setChecked(False) LocalEcho = tree.findtext('GUISettings/View/LocalEcho', default='off') if 'on' == LocalEcho: self.actionLocal_Echo.setChecked(True) self._localEcho = True else: self.actionLocal_Echo.setChecked(False) self._localEcho = False ReceiveView = tree.findtext('GUISettings/View/ReceiveView', default='HEX(UPPERCASE)') if 'Ascii' in ReceiveView: self.actionAscii.setChecked(True) self._viewMode = VIEWMODE_ASCII elif 'lowercase' in ReceiveView: self.actionHex_lowercase.setChecked(True) self._viewMode = VIEWMODE_HEX_LOWERCASE elif 'UPPERCASE' in ReceiveView: self.actionHEX_UPPERCASE.setChecked(True) self._viewMode = VIEWMODE_HEX_UPPERCASE self.receiver_thread.setViewMode(self._viewMode) def closeEvent(self, event): self.saveLayout() self.saveQuickSend() self.saveSettings() event.accept() def initQuickSend(self): #self.quickSendTable.horizontalHeader().setDefaultSectionSize(40) #self.quickSendTable.horizontalHeader().setMinimumSectionSize(25) self.quickSendTable.setRowCount(50) self.quickSendTable.setColumnCount(3) self.quickSendTable.verticalHeader().setSectionsClickable(True) for row in range(50): self.initQuickSendButton(row) if os.path.isfile(get_config_path('QuickSend.csv')): self.loadQuickSend(get_config_path('QuickSend.csv')) self.quickSendTable.resizeColumnsToContents() def initQuickSendButton(self, row, cmd = 'cmd', opt = 'H', dat = ''): if self.quickSendTable.cellWidget(row, 0) is None: item = QToolButton(self) item.setText(cmd) item.clicked.connect(self._signalMapQuickSend.map) self._signalMapQuickSend.setMapping(item, row) self.quickSendTable.setCellWidget(row, 0, item) else: self.quickSendTable.cellWidget(row, 0).setText(cmd) if self.quickSendTable.cellWidget(row, 1) is None: item = QToolButton(self) item.setText(opt) #item.setMaximumSize(QtCore.QSize(16, 16)) item.clicked.connect(self._signalMapQuickSendOpt.map) self._signalMapQuickSendOpt.setMapping(item, row) self.quickSendTable.setCellWidget(row, 1, item) else: self.quickSendTable.cellWidget(row, 1).setText(opt) if self.quickSendTable.item(row, 2) is None: self.quickSendTable.setItem(row, 2, QTableWidgetItem(dat)) else: self.quickSendTable.item(row, 2).setText(dat) self.quickSendTable.setRowHeight(row, 16) def onSetSendHex(self): item = self.quickSendTable.cellWidget(self._quickSendOptRow, 1) item.setText('H') def onSetSendAsc(self): item = self.quickSendTable.cellWidget(self._quickSendOptRow, 1) item.setText('A') def onSetSendTFH(self): item = self.quickSendTable.cellWidget(self._quickSendOptRow, 1) item.setText('FH') def onSetSendTFA(self): item = self.quickSendTable.cellWidget(self._quickSendOptRow, 1) item.setText('FA') def onSetSendFB(self): item = self.quickSendTable.cellWidget(self._quickSendOptRow, 1) item.setText('FB') def onQuickSendOptions(self, row): self._quickSendOptRow = row item = self.quickSendTable.cellWidget(row, 1) self.sendOptMenu.popup(item.mapToGlobal(QPoint(item.size().width(), item.size().height()))) def openQuickSend(self): fileName = QFileDialog.getOpenFileName(self, "Select a file", os.getcwd(), "CSV Files (*.csv)")[0] if fileName: self.loadQuickSend(fileName, notifyExcept = True) def saveQuickSend(self): # scan table rows = self.quickSendTable.rowCount() #cols = self.quickSendTable.columnCount() save_data = [[self.quickSendTable.cellWidget(row, 0).text(), self.quickSendTable.cellWidget(row, 1).text(), self.quickSendTable.item(row, 2) is not None and self.quickSendTable.item(row, 2).text() or ''] for row in range(rows)] #import pprint #pprint.pprint(save_data, width=120, compact=True) # write to file with open(get_config_path('QuickSend.csv'), 'w') as csvfile: csvwriter = csv.writer(csvfile, delimiter=',', lineterminator='\n') csvwriter.writerows(save_data) def loadQuickSend(self, path, notifyExcept = False): data = [] set_rows = 0 set_cols = 0 try: with open(path) as csvfile: csvData = csv.reader(csvfile) for row in csvData: data.append(row) set_rows = set_rows + 1 if len(row) > set_cols: set_cols = len(row) except IOError as e: print("({})".format(e)) if notifyExcept: QMessageBox.critical(self.defaultStyleWidget, "Open failed", str(e), QMessageBox.Close) return rows = self.quickSendTable.rowCount() cols = self.quickSendTable.columnCount() if rows < set_rows: rows = set_rows + 10 self.quickSendTable.setRowCount(rows) for row, rowdat in enumerate(data): if len(rowdat) >= 3: cmd, opt, dat = rowdat[0:3] self.initQuickSendButton(row, cmd, opt, dat) # self.quickSendTable.cellWidget(row, 0).setText(cmd) # self.quickSendTable.cellWidget(row, 1).setText(opt) # self.quickSendTable.setItem(row, 2, QTableWidgetItem(dat)) self.quickSendTable.resizeColumnsToContents() #self.quickSendTable.resizeRowsToContents() def onQuickSend(self, row): if self.quickSendTable.item(row, 2) is not None: tablestring = self.quickSendTable.item(row, 2).text() format = self.quickSendTable.cellWidget(row, 1).text() if 'H' == format: self.transmitHex(tablestring) elif 'A' == format: self.transmitAsc(tablestring) elif 'FB' == format: try: with open(tablestring, 'rb') as f: bytes = f.read() self.transmitBytearray(bytes) except IOError as e: print("({})".format(e)) QMessageBox.critical(self.defaultStyleWidget, "Open failed", str(e), QMessageBox.Close) else: try: with open(tablestring, 'rt') as f: filestring = f.read() if 'FH' == format: self.transmitHex(filestring) elif 'FA' == format: self.transmitAsc(filestring) except IOError as e: print("({})".format(e)) QMessageBox.critical(self.defaultStyleWidget, "Open failed", str(e), QMessageBox.Close) def onSend(self): sendstring = self.txtEdtInput.toPlainText() self.transmitHex(sendstring) def transmitHex(self, hexstring): if len(hexstring) > 0: hexarray = [] _hexstring = hexstring.replace(' ', '') _hexstring = _hexstring.replace('\r', '') _hexstring = _hexstring.replace('\n', '') for i in range(0, len(_hexstring), 2): word = _hexstring[i:i+2] if is_hex(word): hexarray.append(int(word, 16)) else: QMessageBox.critical(self.defaultStyleWidget, "Error", "'%s' is not hexadecimal." % (word), QMessageBox.Close) return self.transmitBytearray(bytearray(hexarray)) def transmitAsc(self, text): if len(text) > 0: byteArray = [ord(char) for char in text] self.transmitBytearray(bytearray(byteArray)) def transmitBytearray(self, byteArray): if self.serialport.isOpen(): try: self.serialport.write(byteArray) except Exception as e: QMessageBox.critical(self.defaultStyleWidget, "Exception in transmit", str(e), QMessageBox.Close) print("Exception in transmitBytearray(%s)" % text) else: if self._viewMode == VIEWMODE_ASCII: text = byteArray.decode('unicode_escape') elif self._viewMode == VIEWMODE_HEX_LOWERCASE: text = ''.join('%02x ' % t for t in byteArray) elif self._viewMode == VIEWMODE_HEX_UPPERCASE: text = ''.join('%02X ' % t for t in byteArray) self.appendOutputText("\n%s T->:%s" % (self.timestamp(), text), Qt.blue) def onReaderExcept(self, e): self.closePort() QMessageBox.critical(self.defaultStyleWidget, "Read failed", str(e), QMessageBox.Close) def timestamp(self): return datetime.datetime.now().time().isoformat()[:-3] def onReceive(self, data): self.appendOutputText("\n%s R<-:%s" % (self.timestamp(), data)) def appendOutputText(self, data, color=Qt.black): # the qEditText's "append" methon will add a unnecessary newline. # self.txtEdtOutput.append(data.decode('utf-8')) tc=self.txtEdtOutput.textColor() self.txtEdtOutput.moveCursor(QtGui.QTextCursor.End) self.txtEdtOutput.setTextColor(QtGui.QColor(color)) self.txtEdtOutput.insertPlainText(data) self.txtEdtOutput.moveCursor(QtGui.QTextCursor.End) self.txtEdtOutput.setTextColor(tc) def getPort(self): return self.cmbPort.currentText() def getDataBits(self): return {'5':serial.FIVEBITS, '6':serial.SIXBITS, '7':serial.SEVENBITS, '8':serial.EIGHTBITS}[self.cmbDataBits.currentText()] def getParity(self): return {'None' :serial.PARITY_NONE, 'Even' :serial.PARITY_EVEN, 'Odd' :serial.PARITY_ODD, 'Mark' :serial.PARITY_MARK, 'Space':serial.PARITY_SPACE}[self.cmbParity.currentText()] def getStopBits(self): return {'1' :serial.STOPBITS_ONE, '1.5':serial.STOPBITS_ONE_POINT_FIVE, '2' :serial.STOPBITS_TWO}[self.cmbStopBits.currentText()] def openPort(self): if self.serialport.isOpen(): return _port = self.getPort() if '' == _port: QMessageBox.information(self.defaultStyleWidget, "Invalid parameters", "Port is empty.") return _baudrate = self.cmbBaudRate.currentText() if '' == _baudrate: QMessageBox.information(self.defaultStyleWidget, "Invalid parameters", "Baudrate is empty.") return self.serialport.port = _port self.serialport.baudrate = _baudrate self.serialport.bytesize = self.getDataBits() self.serialport.stopbits = self.getStopBits() self.serialport.parity = self.getParity() self.serialport.rtscts = self.chkRTSCTS.isChecked() self.serialport.xonxoff = self.chkXonXoff.isChecked() # self.serialport.timeout = THREAD_TIMEOUT # self.serialport.writeTimeout = SERIAL_WRITE_TIMEOUT try: self.serialport.open() except Exception as e: QMessageBox.critical(self.defaultStyleWidget, "Could not open serial port", str(e), QMessageBox.Close) else: self._start_reader() self.setWindowTitle("%s on %s [%s, %s%s%s%s%s]" % ( appInfo.title, self.serialport.portstr, self.serialport.baudrate, self.serialport.bytesize, self.serialport.parity, self.serialport.stopbits, self.serialport.rtscts and ' RTS/CTS' or '', self.serialport.xonxoff and ' Xon/Xoff' or '', ) ) pal = self.btnOpen.palette() pal.setColor(QtGui.QPalette.Button, QtGui.QColor(0,0xff,0x7f)) self.btnOpen.setAutoFillBackground(True) self.btnOpen.setPalette(pal) self.btnOpen.setText('Close') self.btnOpen.update() def closePort(self): if self.serialport.isOpen(): self._stop_reader() self.serialport.close() self.setWindowTitle(appInfo.title) pal = self.btnOpen.style().standardPalette() self.btnOpen.setAutoFillBackground(True) self.btnOpen.setPalette(pal) self.btnOpen.setText('Open') self.btnOpen.update() def _start_reader(self): """Start reader thread""" self.receiver_thread.start() def _stop_reader(self): """Stop reader thread only, wait for clean exit of thread""" self.receiver_thread.join() def onTogglePrtCfgPnl(self): if self.actionPort_Config_Panel.isChecked(): self.dockWidget_PortConfig.show() else: self.dockWidget_PortConfig.hide() def onToggleQckSndPnl(self): if self.actionQuick_Send_Panel.isChecked(): self.dockWidget_QuickSend.show() else: self.dockWidget_QuickSend.hide() def onToggleHexPnl(self): if self.actionSend_Hex_Panel.isChecked(): self.dockWidget_SendHex.show() else: self.dockWidget_SendHex.hide() def onVisiblePrtCfgPnl(self, visible): self.actionPort_Config_Panel.setChecked(visible) def onVisibleQckSndPnl(self, visible): self.actionQuick_Send_Panel.setChecked(visible) def onVisibleHexPnl(self, visible): self.actionSend_Hex_Panel.setChecked(visible) def onLocalEcho(self): self._localEcho = self.actionLocal_Echo.isChecked() def onAlwaysOnTop(self): if self.actionAlways_On_Top.isChecked(): style = self.windowFlags() self.setWindowFlags(style|Qt.WindowStaysOnTopHint) self.show() else: style = self.windowFlags() self.setWindowFlags(style & ~Qt.WindowStaysOnTopHint) self.show() def onOpen(self): if self.serialport.isOpen(): self.closePort() else: self.openPort() def onClear(self): self.txtEdtOutput.clear() def onSaveLog(self): fileName = QFileDialog.getSaveFileName(self, "Save as", os.getcwd(), "Log files (*.log);;Text files (*.txt);;All files (*.*)")[0] if fileName: import codecs with codecs.open(fileName, 'w', 'utf-8') as f: f.write(self.txtEdtOutput.toPlainText()) def moveScreenCenter(self): w = self.frameGeometry().width() h = self.frameGeometry().height() desktop = QDesktopWidget() screenW = desktop.screen().width() screenH = desktop.screen().height() self.setGeometry((screenW-w)/2, (screenH-h)/2, w, h) def onEnumPorts(self): self.cmbPort.clear() for p in enum_ports(): self.cmbPort.addItem(p) def onAbout(self): QMessageBox.about(self.defaultStyleWidget, "About MyTerm", appInfo.aboutme) def onAboutQt(self): QMessageBox.aboutQt(self.defaultStyleWidget) def onExit(self): if self.serialport.isOpen(): self.closePort() self.close() def restoreLayout(self): if os.path.isfile(get_config_path("UILayout.dat")): try: f=open(get_config_path("UILayout.dat"), 'rb') geometry, state=pickle.load(f) self.restoreGeometry(geometry) self.restoreState(state) except Exception as e: print("Exception on restoreLayout, {}".format(e)) else: try: f=QFile(':/default_layout_qt5.dat') f.open(QIODevice.ReadOnly) geometry, state=pickle.loads(f.readAll()) self.restoreGeometry(geometry) self.restoreState(state) except Exception as e: print("Exception on restoreLayout, {}".format(e)) def saveLayout(self): with open(get_config_path("UILayout.dat"), 'wb') as f: pickle.dump((self.saveGeometry(), self.saveState()), f) def syncMenu(self): self.actionPort_Config_Panel.setChecked(not self.dockWidget_PortConfig.isHidden()) self.actionQuick_Send_Panel.setChecked(not self.dockWidget_QuickSend.isHidden()) self.actionSend_Hex_Panel.setChecked(not self.dockWidget_SendHex.isHidden()) def onViewChanged(self): checked = self._viewGroup.checkedAction() if checked is None: self._viewMode = VIEWMODE_HEX_UPPERCASE self.actionHEX_UPPERCASE.setChecked(True) else: if 'Ascii' in checked.text(): self._viewMode = VIEWMODE_ASCII elif 'lowercase' in checked.text(): self._viewMode = VIEWMODE_HEX_LOWERCASE elif 'UPPERCASE' in checked.text(): self._viewMode = VIEWMODE_HEX_UPPERCASE self.receiver_thread.setViewMode(self._viewMode)
class AddSeqTool(AbstractPathTool): def __init__(self, controller, parent=None): AbstractPathTool.__init__(self, controller, parent) self.dialog = QDialog() self.buttons = [] self.seqBox = None self.chosenStandardSequence = None # state for tab switching self.customSequenceIsValid = False # state for tab switching self.useCustomSequence = False # for applying sequence self.validatedSequenceToApply = None self.initDialog() def __repr__(self): return "add_seq_tool" # first letter should be lowercase def methodPrefix(self): return "addSeqTool" # first letter should be lowercase def initDialog(self): """ 1. Create buttons according to available scaffold sequences and add them to the dialog. 2. Map the clicked signal of those buttons to keep track of what sequence gets selected. 3. Watch the tabWidget change signal to determine whether a standard or custom sequence should be applied. """ uiDlg = Ui_AddSeqDialog() uiDlg.setupUi(self.dialog) self.signalMapper = QSignalMapper(self) # set up the radio buttons for i, name in enumerate(sorted(sequences.keys())): radioButton = QRadioButton(uiDlg.groupBox) radioButton.setObjectName(name + "Button") radioButton.setText(name) self.buttons.append(radioButton) uiDlg.verticalLayout.addWidget(radioButton) self.signalMapper.setMapping(radioButton, i) radioButton.clicked.connect(self.signalMapper.map) self.signalMapper.mapped.connect(self.standardSequenceChangedSlot) uiDlg.tabWidget.currentChanged.connect(self.tabWidgetChangedSlot) # disable apply until valid option or custom sequence is chosen self.applyButton = uiDlg.customButtonBox.button(QDialogButtonBox.Apply) self.applyButton.setEnabled(False) # watch sequence textedit box to validate custom sequences self.seqBox = uiDlg.seqTextEdit self.seqBox.textChanged.connect(self.validateCustomSequence) self.highlighter = DNAHighlighter(self.seqBox) # finally, pre-click the M13mp18 radio button self.buttons[0].click() buttons = self.buttons self.dialog.setFocusProxy(uiDlg.groupBox) self.dialog.setFocusPolicy(Qt.TabFocus) uiDlg.groupBox.setFocusPolicy(Qt.TabFocus) for i in range(len(buttons)-1): uiDlg.groupBox.setTabOrder(buttons[i], buttons[i+1]) def tabWidgetChangedSlot(self, index): applyEnabled = False if index == 1: # Custom Sequence self.validateCustomSequence() if self.customSequenceIsValid: applyEnabled = True else: # Standard Sequence self.useCustomSequence = False if self.chosenStandardSequence != None: # Overwrite sequence in case custom has been applied activeButton = self.buttons[self.chosenStandardSequence] sequenceName = str(activeButton.text()) self.validatedSequenceToApply = sequences.get(sequenceName, None) applyEnabled = True self.applyButton.setEnabled(applyEnabled) def standardSequenceChangedSlot(self, optionChosen): """ Connected to signalMapper to receive a signal whenever user selects a different sequence in the standard tab. """ sequenceName = str(self.buttons[optionChosen].text()) self.validatedSequenceToApply = sequences.get(sequenceName, None) self.chosenStandardSequence = optionChosen self.applyButton.setEnabled(True) def validateCustomSequence(self): """ Called when: 1. User enters custom sequence (i.e. seqBox emits textChanged signal) 2. tabWidgetChangedSlot sees the user has switched to custom tab. When the sequence is valid, make the applyButton active for clicking. Otherwise """ userSequence = self.seqBox.toPlainText() if len(userSequence) == 0: self.customSequenceIsValid = False return # tabWidgetChangedSlot will disable applyButton if dnapattern.indexIn(userSequence) == -1: # no invalid characters self.useCustomSequence = True self.customSequenceIsValid = True self.applyButton.setEnabled(True) else: self.customSequenceIsValid = False self.applyButton.setEnabled(False) def applySequence(self, oligo): self.dialog.setFocus() if self.dialog.exec_(): # apply the sequence if accept was clicked if self.useCustomSequence: self.validatedSequenceToApply = str(self.seqBox.toPlainText().toUpper()) oligo.applySequence(self.validatedSequenceToApply) return oligo.length(), len(self.validatedSequenceToApply) return (None, None)
class Panel(QDialog): # A list of two-sized tuples (QWidget's name, model field name). FIELDS = [] # Name to use for serialization of persistent data about this panel (geometry). # XXX At the time of this writing (ticket #364), there's already a separate system in Cocoa # to persist dialog frames. A "clean" implementation would do like we do with the main window # and implement frame save/restore in core, but I fear that I'll needlessly complicate things # doing so, so for now, I limit myself to a qt-only solution. Later, we should re-evaluate # whether it could be a good idea to push this implementation to the core. PERSISTENT_NAME = None def __init__(self, mainwindow): # The flags we pass are that so we don't get the "What's this" button in the title bar QDialog.__init__(self, mainwindow, Qt.WindowTitleHint | Qt.WindowSystemMenuHint) self._widget2ModelAttr = {} self.mainwindow = mainwindow def _changeComboBoxItems(self, comboBox, newItems): # When a combo box's items are changed, its currentIndex changed with a currentIndexChanged # signal, and if that signal results in the model being updated, it messes the model. # We thus have to disconnect the combo box's signal before changing the items. if comboBox in self._widget2ModelAttr: comboBox.currentIndexChanged.disconnect(self.comboBoxCurrentIndexChanged) index = comboBox.currentIndex() comboBox.clear() comboBox.addItems(newItems) comboBox.setCurrentIndex(index) if comboBox in self._widget2ModelAttr: comboBox.currentIndexChanged.connect(self.comboBoxCurrentIndexChanged) def _connectSignals(self): self._signalMapper = QSignalMapper() for widgetName, modelAttr in self.FIELDS: widget = getattr(self, widgetName) self._widget2ModelAttr[widget] = modelAttr self._signalMapper.setMapping(widget, widget) if isinstance(widget, QComboBox): widget.currentIndexChanged.connect(self._signalMapper.map) elif isinstance(widget, QSpinBox): widget.valueChanged.connect(self._signalMapper.map) elif isinstance(widget, QLineEdit): widget.editingFinished.connect(self._signalMapper.map) elif isinstance(widget, QPlainTextEdit): widget.textChanged.connect(self._signalMapper.map) elif isinstance(widget, QCheckBox): widget.stateChanged.connect(self._signalMapper.map) self._signalMapper.mapped[QWidget].connect(self.widgetChanged) def _loadFields(self): for widgetName, modelAttr in self.FIELDS: widget = getattr(self, widgetName) value = getattr(self.model, modelAttr) if isinstance(widget, QComboBox): widget.setCurrentIndex(value) elif isinstance(widget, QSpinBox): widget.setValue(value) elif isinstance(widget, QLineEdit): widget.setText(value) elif isinstance(widget, QPlainTextEdit): widget.setPlainText(value) elif isinstance(widget, QCheckBox): widget.setChecked(value) def _saveFields(self): pass def _loadGeometry(self): if self.PERSISTENT_NAME: self.mainwindow.app.prefs.restoreGeometry('%sGeometry' % self.PERSISTENT_NAME, self) def _saveGeometry(self): if self.PERSISTENT_NAME: self.mainwindow.app.prefs.saveGeometry('%sGeometry' % self.PERSISTENT_NAME, self) def accept(self): # The setFocus() call is to force the last edited field to "commit". When the save button # is clicked, accept() is called before the last field to have focus has a chance to emit # its edition signal. self.setFocus() self.model.save() self._saveGeometry() QDialog.accept(self) def reject(self): self._saveGeometry() super().reject() #--- Event Handlers def widgetChanged(self, sender): modelAttr = self._widget2ModelAttr[sender] if isinstance(sender, QComboBox): newvalue = sender.currentIndex() elif isinstance(sender, QSpinBox): newvalue = sender.value() elif isinstance(sender, QLineEdit): newvalue = sender.text() elif isinstance(sender, QPlainTextEdit): newvalue = sender.toPlainText() elif isinstance(sender, QCheckBox): newvalue = sender.isChecked() setattr(self.model, modelAttr, newvalue) #--- model --> view def pre_load(self): self._loadGeometry() def pre_save(self): self._saveFields() def post_load(self): if not self._widget2ModelAttr: # signal not connected yet self._connectSignals() self._loadFields() self.show() # For initial text edits to have their text selected, we *have to* first select the dialog, # then setFocus on it with qt.TabFocusReason. Don't ask, I don't know why either... self.setFocus() focus = self.nextInFocusChain() while focus.focusPolicy() == Qt.NoFocus: focus = focus.nextInFocusChain() focus.setFocus(Qt.TabFocusReason)
class MainWindow(QMainWindow, Ui_MainWindow): dictChanged = pyqtSignal(str) # Tab indexes TabInfos = 0 TabSummary = 1 TabPersos = 2 TabPlots = 3 TabWorld = 4 TabOutline = 5 TabRedac = 6 def __init__(self): QMainWindow.__init__(self) self.setupUi(self) self.currentProject = None self.readSettings() # UI self.setupMoreUi() # Welcome self.welcome.updateValues() # self.welcome.btnCreate.clicked.connect self.stack.setCurrentIndex(0) # Word count self.mprWordCount = QSignalMapper(self) for t, i in [ (self.txtSummarySentence, 0), (self.txtSummaryPara, 1), (self.txtSummaryPage, 2), (self.txtSummaryFull, 3) ]: t.textChanged.connect(self.mprWordCount.map) self.mprWordCount.setMapping(t, i) self.mprWordCount.mapped.connect(self.wordCount) # Snowflake Method Cycle self.mapperCycle = QSignalMapper(self) for t, i in [ (self.btnStepTwo, 0), (self.btnStepThree, 1), (self.btnStepFour, 2), (self.btnStepFive, 3), (self.btnStepSix, 4), (self.btnStepSeven, 5), (self.btnStepEight, 6) ]: t.clicked.connect(self.mapperCycle.map) self.mapperCycle.setMapping(t, i) self.mapperCycle.mapped.connect(self.clickCycle) self.cmbSummary.currentIndexChanged.connect(self.summaryPageChanged) self.cmbSummary.setCurrentIndex(0) self.cmbSummary.currentIndexChanged.emit(0) # Main Menu for i in [self.actSave, self.actSaveAs, self.actCloseProject, self.menuEdit, self.menuMode, self.menuView, self.menuTools, self.menuHelp]: i.setEnabled(False) self.actOpen.triggered.connect(self.welcome.openFile) self.actSave.triggered.connect(self.saveDatas) self.actSaveAs.triggered.connect(self.welcome.saveAsFile) self.actCompile.triggered.connect(self.doCompile) self.actLabels.triggered.connect(self.settingsLabel) self.actStatus.triggered.connect(self.settingsStatus) self.actSettings.triggered.connect(self.settingsWindow) self.actCloseProject.triggered.connect(self.closeProject) self.actQuit.triggered.connect(self.close) self.actToolFrequency.triggered.connect(self.frequencyAnalyzer) self.generateViewMenu() self.makeUIConnections() # self.loadProject(os.path.join(appPath(), "test_project.zip")) ############################################################################### # SUMMARY ############################################################################### def summaryPageChanged(self, index): fractalButtons = [ self.btnStepTwo, self.btnStepThree, self.btnStepFive, self.btnStepSeven, ] for b in fractalButtons: b.setVisible(fractalButtons.index(b) == index) ############################################################################### # OUTLINE ############################################################################### def outlineRemoveItemsRedac(self): self.treeRedacOutline.delete() def outlineRemoveItemsOutline(self): self.treeOutlineOutline.delete() ############################################################################### # PERSOS ############################################################################### def changeCurrentPerso(self, trash=None): index = self.lstPersos.currentPersoIndex() if not index.isValid(): self.tabPlot.setEnabled(False) return self.tabPersos.setEnabled(True) for w in [ self.txtPersoName, self.sldPersoImportance, self.txtPersoMotivation, self.txtPersoGoal, self.txtPersoConflict, self.txtPersoEpiphany, self.txtPersoSummarySentence, self.txtPersoSummaryPara, self.txtPersoSummaryFull, self.txtPersoNotes, ]: w.setCurrentModelIndex(index) # Button color self.mdlPersos.updatePersoColor(index) # Perso Infos self.tblPersoInfos.setRootIndex(index) if self.mdlPersos.rowCount(index): self.updatePersoInfoView() def updatePersoInfoView(self): # Hide columns for i in range(self.mdlPersos.columnCount()): self.tblPersoInfos.hideColumn(i) self.tblPersoInfos.showColumn(Perso.infoName.value) self.tblPersoInfos.showColumn(Perso.infoData.value) self.tblPersoInfos.horizontalHeader().setSectionResizeMode( Perso.infoName.value, QHeaderView.ResizeToContents) self.tblPersoInfos.horizontalHeader().setSectionResizeMode( Perso.infoData.value, QHeaderView.Stretch) self.tblPersoInfos.verticalHeader().hide() ############################################################################### # PLOTS ############################################################################### def changeCurrentPlot(self): index = self.lstPlots.currentPlotIndex() if not index.isValid(): self.tabPlot.setEnabled(False) return self.tabPlot.setEnabled(True) self.txtPlotName.setCurrentModelIndex(index) self.txtPlotDescription.setCurrentModelIndex(index) self.txtPlotResult.setCurrentModelIndex(index) self.sldPlotImportance.setCurrentModelIndex(index) self.lstPlotPerso.setRootIndex(index.sibling(index.row(), Plot.persos.value)) subplotindex = index.sibling(index.row(), Plot.subplots.value) self.lstSubPlots.setRootIndex(subplotindex) if self.mdlPlots.rowCount(subplotindex): self.updateSubPlotView() # self.txtSubPlotSummary.setCurrentModelIndex(QModelIndex()) self.txtSubPlotSummary.setEnabled(False) self._updatingSubPlot = True self.txtSubPlotSummary.setPlainText("") self._updatingSubPlot = False self.lstPlotPerso.selectionModel().clear() def updateSubPlotView(self): # Hide columns for i in range(self.mdlPlots.columnCount()): self.lstSubPlots.hideColumn(i) self.lstSubPlots.showColumn(Subplot.name.value) self.lstSubPlots.showColumn(Subplot.meta.value) self.lstSubPlots.horizontalHeader().setSectionResizeMode( Subplot.name.value, QHeaderView.Stretch) self.lstSubPlots.horizontalHeader().setSectionResizeMode( Subplot.meta.value, QHeaderView.ResizeToContents) self.lstSubPlots.verticalHeader().hide() def changeCurrentSubPlot(self, index): # Got segfaults when using textEditView model system, so ad hoc stuff. index = index.sibling(index.row(), Subplot.summary.value) item = self.mdlPlots.itemFromIndex(index) if not item: self.txtSubPlotSummary.setEnabled(False) return self.txtSubPlotSummary.setEnabled(True) txt = item.text() self._updatingSubPlot = True self.txtSubPlotSummary.setPlainText(txt) self._updatingSubPlot = False def updateSubPlotSummary(self): if self._updatingSubPlot: return index = self.lstSubPlots.currentIndex() if not index.isValid(): return index = index.sibling(index.row(), Subplot.summary.value) item = self.mdlPlots.itemFromIndex(index) self._updatingSubPlot = True item.setText(self.txtSubPlotSummary.toPlainText()) self._updatingSubPlot = False def plotPersoSelectionChanged(self): "Enables or disables remove plot perso button." self.btnRmPlotPerso.setEnabled( len(self.lstPlotPerso.selectedIndexes()) != 0) ############################################################################### # WORLD ############################################################################### def changeCurrentWorld(self): index = self.mdlWorld.selectedIndex() if not index.isValid(): self.tabWorld.setEnabled(False) return self.tabWorld.setEnabled(True) self.txtWorldName.setCurrentModelIndex(index) self.txtWorldDescription.setCurrentModelIndex(index) self.txtWorldPassion.setCurrentModelIndex(index) self.txtWorldConflict.setCurrentModelIndex(index) ############################################################################### # LOAD AND SAVE ############################################################################### def loadProject(self, project, loadFromFile=True): """Loads the project ``project``. If ``loadFromFile`` is False, then it does not load datas from file. It assumes that the datas have been populated in a different way.""" if loadFromFile and not os.path.exists(project): print(self.tr("The file {} does not exist. Try again.").format(project)) self.statusBar().showMessage( self.tr("The file {} does not exist. Try again.").format(project), 5000) return if loadFromFile: # Load empty settings imp.reload(settings) # Load data self.loadEmptyDatas() self.loadDatas(project) self.makeConnections() # Load settings for i in settings.openIndexes: idx = self.mdlOutline.indexFromPath(i) self.mainEditor.setCurrentModelIndex(idx, newTab=True) self.generateViewMenu() self.mainEditor.sldCorkSizeFactor.setValue(settings.corkSizeFactor) self.actSpellcheck.setChecked(settings.spellcheck) self.toggleSpellcheck(settings.spellcheck) self.updateMenuDict() self.setDictionary() self.mainEditor.setFolderView(settings.folderView) self.mainEditor.updateFolderViewButtons(settings.folderView) self.tabMain.setCurrentIndex(settings.lastTab) # We force to emit even if it opens on the current tab self.tabMain.currentChanged.emit(settings.lastTab) self.mainEditor.updateCorkBackground() # Set autosave self.saveTimer = QTimer() self.saveTimer.setInterval(settings.autoSaveDelay * 60 * 1000) self.saveTimer.setSingleShot(False) self.saveTimer.timeout.connect(self.saveDatas) if settings.autoSave: self.saveTimer.start() # Set autosave if no changes self.saveTimerNoChanges = QTimer() self.saveTimerNoChanges.setInterval(settings.autoSaveNoChangesDelay * 1000) self.saveTimerNoChanges.setSingleShot(True) self.mdlFlatData.dataChanged.connect(self.startTimerNoChanges) self.mdlOutline.dataChanged.connect(self.startTimerNoChanges) self.mdlPersos.dataChanged.connect(self.startTimerNoChanges) self.mdlPlots.dataChanged.connect(self.startTimerNoChanges) self.mdlWorld.dataChanged.connect(self.startTimerNoChanges) # self.mdlPersosInfos.dataChanged.connect(self.startTimerNoChanges) self.mdlStatus.dataChanged.connect(self.startTimerNoChanges) self.mdlLabels.dataChanged.connect(self.startTimerNoChanges) self.saveTimerNoChanges.timeout.connect(self.saveDatas) self.saveTimerNoChanges.stop() # UI for i in [self.actSave, self.actSaveAs, self.actCloseProject, self.menuEdit, self.menuMode, self.menuView, self.menuTools, self.menuHelp]: i.setEnabled(True) # FIXME: set Window's name: project name # Stuff # self.checkPersosID() # Should'n be necessary any longer self.currentProject = project QSettings().setValue("lastProject", project) # Show main Window self.stack.setCurrentIndex(1) def closeProject(self): # Save datas self.saveDatas() self.currentProject = None QSettings().setValue("lastProject", "") # FIXME: close all opened tabs in mainEditor # Clear datas self.loadEmptyDatas() self.saveTimer.stop() # UI for i in [self.actSave, self.actSaveAs, self.actCloseProject, self.menuEdit, self.menuMode, self.menuView, self.menuTools, self.menuHelp]: i.setEnabled(False) # Reload recent files self.welcome.updateValues() # Show welcome dialog self.stack.setCurrentIndex(0) def readSettings(self): # Load State and geometry sttgns = QSettings(qApp.organizationName(), qApp.applicationName()) if sttgns.contains("geometry"): self.restoreGeometry(sttgns.value("geometry")) if sttgns.contains("windowState"): self.restoreState(sttgns.value("windowState")) else: self.dckCheatSheet.hide() self.dckSearch.hide() if sttgns.contains("metadataState"): state = [False if v == "false" else True for v in sttgns.value("metadataState")] self.redacMetadata.restoreState(state) if sttgns.contains("revisionsState"): state = [False if v == "false" else True for v in sttgns.value("revisionsState")] self.redacMetadata.revisions.restoreState(state) if sttgns.contains("splitterRedacH"): self.splitterRedacH.restoreState(sttgns.value("splitterRedacH")) if sttgns.contains("splitterRedacV"): self.splitterRedacV.restoreState(sttgns.value("splitterRedacV")) if sttgns.contains("toolbar"): # self.toolbar is not initialized yet, so we just store balue self._toolbarState = sttgns.value("toolbar") else: self._toolbarState = "" def closeEvent(self, event): # Save State and geometry and other things sttgns = QSettings(qApp.organizationName(), qApp.applicationName()) sttgns.setValue("geometry", self.saveGeometry()) sttgns.setValue("windowState", self.saveState()) sttgns.setValue("metadataState", self.redacMetadata.saveState()) sttgns.setValue("revisionsState", self.redacMetadata.revisions.saveState()) sttgns.setValue("splitterRedacH", self.splitterRedacH.saveState()) sttgns.setValue("splitterRedacV", self.splitterRedacV.saveState()) sttgns.setValue("toolbar", self.toolbar.saveState()) # Specific settings to save before quitting settings.lastTab = self.tabMain.currentIndex() if self.currentProject: # Remembering the current items sel = [] for i in range(self.mainEditor.tab.count()): sel.append(self.mdlOutline.pathToIndex(self.mainEditor.tab.widget(i).currentIndex)) settings.openIndexes = sel # Save data from models if self.currentProject and settings.saveOnQuit: self.saveDatas() # closeEvent # QMainWindow.closeEvent(self, event) # Causin segfaults? def startTimerNoChanges(self): if settings.autoSaveNoChanges: self.saveTimerNoChanges.start() def saveDatas(self, projectName=None): """Saves the current project (in self.currentProject). If ``projectName`` is given, currentProject becomes projectName. In other words, it "saves as...". """ if projectName: self.currentProject = projectName QSettings().setValue("lastProject", projectName) # Saving files = [] files.append((saveStandardItemModelXML(self.mdlFlatData), "flatModel.xml")) files.append((saveStandardItemModelXML(self.mdlPersos), "perso.xml")) files.append((saveStandardItemModelXML(self.mdlWorld), "world.xml")) files.append((saveStandardItemModelXML(self.mdlLabels), "labels.xml")) files.append((saveStandardItemModelXML(self.mdlStatus), "status.xml")) files.append((saveStandardItemModelXML(self.mdlPlots), "plots.xml")) files.append((self.mdlOutline.saveToXML(), "outline.xml")) files.append((settings.save(), "settings.pickle")) saveFilesToZip(files, self.currentProject) # Giving some feedback print(self.tr("Project {} saved.").format(self.currentProject)) self.statusBar().showMessage( self.tr("Project {} saved.").format(self.currentProject), 5000) def loadEmptyDatas(self): self.mdlFlatData = QStandardItemModel(self) self.mdlPersos = persosModel(self) # self.mdlPersosProxy = persosProxyModel(self) # self.mdlPersosInfos = QStandardItemModel(self) self.mdlLabels = QStandardItemModel(self) self.mdlStatus = QStandardItemModel(self) self.mdlPlots = plotModel(self) self.mdlOutline = outlineModel(self) self.mdlWorld = worldModel(self) def loadDatas(self, project): # Loading files = loadFilesFromZip(project) errors = [] if "flatModel.xml" in files: loadStandardItemModelXML(self.mdlFlatData, files["flatModel.xml"], fromString=True) else: errors.append("flatModel.xml") if "perso.xml" in files: loadStandardItemModelXML(self.mdlPersos, files["perso.xml"], fromString=True) else: errors.append("perso.xml") if "world.xml" in files: loadStandardItemModelXML(self.mdlWorld, files["world.xml"], fromString=True) else: errors.append("world.xml") if "labels.xml" in files: loadStandardItemModelXML(self.mdlLabels, files["labels.xml"], fromString=True) else: errors.append("perso.xml") if "status.xml" in files: loadStandardItemModelXML(self.mdlStatus, files["status.xml"], fromString=True) else: errors.append("perso.xml") if "plots.xml" in files: loadStandardItemModelXML(self.mdlPlots, files["plots.xml"], fromString=True) else: errors.append("perso.xml") if "outline.xml" in files: self.mdlOutline.loadFromXML(files["outline.xml"], fromString=True) else: errors.append("perso.xml") if "settings.pickle" in files: settings.load(files["settings.pickle"], fromString=True) else: errors.append("perso.xml") # Giving some feedback if not errors: print(self.tr("Project {} loaded.").format(project)) self.statusBar().showMessage( self.tr("Project {} loaded.").format(project), 5000) else: print(self.tr("Project {} loaded with some errors:").format(project)) for e in errors: print(self.tr(" * {} wasn't found in project file.").format(e)) self.statusBar().showMessage( self.tr("Project {} loaded with some errors.").format(project), 5000) ############################################################################### # MAIN CONNECTIONS ############################################################################### def makeUIConnections(self): "Connections that have to be made once only, event when new project is loaded." self.lstPersos.currentItemChanged.connect(self.changeCurrentPerso, AUC) self.txtPlotFilter.textChanged.connect(self.lstPlots.setFilter, AUC) self.lstPlots.currentItemChanged.connect(self.changeCurrentPlot, AUC) self.txtSubPlotSummary.document().contentsChanged.connect( self.updateSubPlotSummary, AUC) self.lstSubPlots.activated.connect(self.changeCurrentSubPlot, AUC) self.btnRedacAddFolder.clicked.connect(self.treeRedacOutline.addFolder, AUC) self.btnOutlineAddFolder.clicked.connect(self.treeOutlineOutline.addFolder, AUC) self.btnRedacAddText.clicked.connect(self.treeRedacOutline.addText, AUC) self.btnOutlineAddText.clicked.connect(self.treeOutlineOutline.addText, AUC) self.btnRedacRemoveItem.clicked.connect(self.outlineRemoveItemsRedac, AUC) self.btnOutlineRemoveItem.clicked.connect(self.outlineRemoveItemsOutline, AUC) self.tabMain.currentChanged.connect(self.toolbar.setCurrentGroup) def makeConnections(self): # Flat datas (Summary and general infos) for widget, col in [ (self.txtSummarySituation, 0), (self.txtSummarySentence, 1), (self.txtSummarySentence_2, 1), (self.txtSummaryPara, 2), (self.txtSummaryPara_2, 2), (self.txtPlotSummaryPara, 2), (self.txtSummaryPage, 3), (self.txtSummaryPage_2, 3), (self.txtPlotSummaryPage, 3), (self.txtSummaryFull, 4), (self.txtPlotSummaryFull, 4), ]: widget.setModel(self.mdlFlatData) widget.setColumn(col) widget.setCurrentModelIndex(self.mdlFlatData.index(1, col)) for widget, col in [ (self.txtGeneralTitle, 0), (self.txtGeneralSubtitle, 1), (self.txtGeneralSerie, 2), (self.txtGeneralVolume, 3), (self.txtGeneralGenre, 4), (self.txtGeneralLicense, 5), (self.txtGeneralAuthor, 6), (self.txtGeneralEmail, 7), ]: widget.setModel(self.mdlFlatData) widget.setColumn(col) widget.setCurrentModelIndex(self.mdlFlatData.index(0, col)) # Persos self.lstPersos.setPersosModel(self.mdlPersos) self.tblPersoInfos.setModel(self.mdlPersos) self.btnAddPerso.clicked.connect(self.mdlPersos.addPerso, AUC) self.btnRmPerso.clicked.connect(self.mdlPersos.removePerso, AUC) self.btnPersoColor.clicked.connect(self.mdlPersos.chosePersoColor, AUC) self.btnPersoAddInfo.clicked.connect(self.mdlPersos.addPersoInfo, AUC) self.btnPersoRmInfo.clicked.connect(self.mdlPersos.removePersoInfo, AUC) for w, c in [ (self.txtPersoName, Perso.name.value), (self.sldPersoImportance, Perso.importance.value), (self.txtPersoMotivation, Perso.motivation.value), (self.txtPersoGoal, Perso.goal.value), (self.txtPersoConflict, Perso.conflict.value), (self.txtPersoEpiphany, Perso.epiphany.value), (self.txtPersoSummarySentence, Perso.summarySentence.value), (self.txtPersoSummaryPara, Perso.summaryPara.value), (self.txtPersoSummaryFull, Perso.summaryFull.value), (self.txtPersoNotes, Perso.notes.value) ]: w.setModel(self.mdlPersos) w.setColumn(c) self.tabPersos.setEnabled(False) # Plots self.lstPlots.setPlotModel(self.mdlPlots) self.lstPlotPerso.setModel(self.mdlPlots) self.lstSubPlots.setModel(self.mdlPlots) self._updatingSubPlot = False self.btnAddPlot.clicked.connect(self.mdlPlots.addPlot, AUC) self.btnRmPlot.clicked.connect(lambda: self.mdlPlots.removePlot(self.lstPlots.currentPlotIndex()), AUC) self.btnAddSubPlot.clicked.connect(self.mdlPlots.addSubPlot, AUC) self.btnRmSubPlot.clicked.connect(self.mdlPlots.removeSubPlot, AUC) self.lstPlotPerso.selectionModel().selectionChanged.connect(self.plotPersoSelectionChanged) self.btnRmPlotPerso.clicked.connect(self.mdlPlots.removePlotPerso, AUC) for w, c in [ (self.txtPlotName, Plot.name.value), (self.txtPlotDescription, Plot.description.value), (self.txtPlotResult, Plot.result.value), (self.sldPlotImportance, Plot.importance.value), ]: w.setModel(self.mdlPlots) w.setColumn(c) self.tabPlot.setEnabled(False) self.mdlPlots.updatePlotPersoButton() self.mdlPersos.dataChanged.connect(self.mdlPlots.updatePlotPersoButton) self.lstOutlinePlots.setPlotModel(self.mdlPlots) self.lstOutlinePlots.setShowSubPlot(True) self.plotPersoDelegate = outlinePersoDelegate(self.mdlPersos, self) self.lstPlotPerso.setItemDelegate(self.plotPersoDelegate) self.plotDelegate = plotDelegate(self) self.lstSubPlots.setItemDelegateForColumn(Subplot.meta.value, self.plotDelegate) # World self.treeWorld.setModel(self.mdlWorld) for i in range(self.mdlWorld.columnCount()): self.treeWorld.hideColumn(i) self.treeWorld.showColumn(0) self.btnWorldEmptyData.setMenu(self.mdlWorld.emptyDataMenu()) self.treeWorld.selectionModel().selectionChanged.connect(self.changeCurrentWorld, AUC) self.btnAddWorld.clicked.connect(self.mdlWorld.addItem, AUC) self.btnRmWorld.clicked.connect(self.mdlWorld.removeItem, AUC) for w, c in [ (self.txtWorldName, World.name.value), (self.txtWorldDescription, World.description.value), (self.txtWorldPassion, World.passion.value), (self.txtWorldConflict, World.conflict.value), ]: w.setModel(self.mdlWorld) w.setColumn(c) self.tabWorld.setEnabled(False) self.treeWorld.expandAll() # Outline self.treeRedacOutline.setModel(self.mdlOutline) self.treeOutlineOutline.setModelPersos(self.mdlPersos) self.treeOutlineOutline.setModelLabels(self.mdlLabels) self.treeOutlineOutline.setModelStatus(self.mdlStatus) self.redacMetadata.setModels(self.mdlOutline, self.mdlPersos, self.mdlLabels, self.mdlStatus) self.outlineItemEditor.setModels(self.mdlOutline, self.mdlPersos, self.mdlLabels, self.mdlStatus) self.treeOutlineOutline.setModel(self.mdlOutline) # self.redacEditor.setModel(self.mdlOutline) self.storylineView.setModels(self.mdlOutline, self.mdlPersos, self.mdlPlots) self.treeOutlineOutline.selectionModel().selectionChanged.connect(lambda: self.outlineItemEditor.selectionChanged( self.treeOutlineOutline), AUC) self.treeOutlineOutline.clicked.connect(lambda: self.outlineItemEditor.selectionChanged(self.treeOutlineOutline), AUC) # Sync selection self.treeRedacOutline.selectionModel().selectionChanged.connect( lambda: self.redacMetadata.selectionChanged(self.treeRedacOutline), AUC) self.treeRedacOutline.clicked.connect( lambda: self.redacMetadata.selectionChanged(self.treeRedacOutline), AUC) self.treeRedacOutline.selectionModel().selectionChanged.connect(self.mainEditor.selectionChanged, AUC) # Cheat Sheet self.cheatSheet.setModels() # Debug self.mdlFlatData.setVerticalHeaderLabels(["Infos générales", "Summary"]) self.tblDebugFlatData.setModel(self.mdlFlatData) self.tblDebugPersos.setModel(self.mdlPersos) self.tblDebugPersosInfos.setModel(self.mdlPersos) self.tblDebugPersos.selectionModel().currentChanged.connect( lambda: self.tblDebugPersosInfos.setRootIndex(self.mdlPersos.index( self.tblDebugPersos.selectionModel().currentIndex().row(), Perso.name.value)), AUC) self.tblDebugPlots.setModel(self.mdlPlots) self.tblDebugPlotsPersos.setModel(self.mdlPlots) self.tblDebugSubPlots.setModel(self.mdlPlots) self.tblDebugPlots.selectionModel().currentChanged.connect( lambda: self.tblDebugPlotsPersos.setRootIndex(self.mdlPlots.index( self.tblDebugPlots.selectionModel().currentIndex().row(), Plot.persos.value)), AUC) self.tblDebugPlots.selectionModel().currentChanged.connect( lambda: self.tblDebugSubPlots.setRootIndex(self.mdlPlots.index( self.tblDebugPlots.selectionModel().currentIndex().row(), Plot.subplots.value)), AUC) self.treeDebugWorld.setModel(self.mdlWorld) self.treeDebugOutline.setModel(self.mdlOutline) self.lstDebugLabels.setModel(self.mdlLabels) self.lstDebugStatus.setModel(self.mdlStatus) ############################################################################### # GENERAL AKA UNSORTED ############################################################################### def clickCycle(self, i): if i == 0: # step 2 - paragraph summary self.tabMain.setCurrentIndex(self.TabSummary) self.tabSummary.setCurrentIndex(1) if i == 1: # step 3 - characters summary self.tabMain.setCurrentIndex(self.TabPersos) self.tabPersos.setCurrentIndex(0) if i == 2: # step 4 - page summary self.tabMain.setCurrentIndex(self.TabSummary) self.tabSummary.setCurrentIndex(2) if i == 3: # step 5 - characters description self.tabMain.setCurrentIndex(self.TabPersos) self.tabPersos.setCurrentIndex(1) if i == 4: # step 6 - four page synopsis self.tabMain.setCurrentIndex(self.TabSummary) self.tabSummary.setCurrentIndex(3) if i == 5: # step 7 - full character charts self.tabMain.setCurrentIndex(self.TabPersos) self.tabPersos.setCurrentIndex(2) if i == 6: # step 8 - scene list self.tabMain.setCurrentIndex(self.TabPlots) def wordCount(self, i): src = { 0: self.txtSummarySentence, 1: self.txtSummaryPara, 2: self.txtSummaryPage, 3: self.txtSummaryFull }[i] lbl = { 0: self.lblSummaryWCSentence, 1: self.lblSummaryWCPara, 2: self.lblSummaryWCPage, 3: self.lblSummaryWCFull }[i] wc = wordCount(src.toPlainText()) if i in [2, 3]: pages = self.tr(" (~{} pages)").format(int(wc / 25) / 10.) else: pages = "" lbl.setText(self.tr("Words: {}{}").format(wc, pages)) def setupMoreUi(self): # Tool bar on the right self.toolbar = collapsibleDockWidgets(Qt.RightDockWidgetArea, self) self.toolbar.addCustomWidget(self.tr("Book summary"), self.grpPlotSummary, self.TabPlots) self.toolbar.addCustomWidget(self.tr("Project tree"), self.treeRedacWidget, self.TabRedac) self.toolbar.addCustomWidget(self.tr("Metadata"), self.redacMetadata, self.TabRedac) self.toolbar.addCustomWidget(self.tr("Story line"), self.storylineView, self.TabRedac) if self._toolbarState: self.toolbar.restoreState(self._toolbarState) # Custom "tab" bar on the left self.lstTabs.setIconSize(QSize(48, 48)) for i in range(self.tabMain.count()): icons = ["general-128px.png", "summary-128px.png", "characters-128px.png", "plot-128px.png", "world-128px.png", "outline-128px.png", "redaction-128px.png", "" ] self.tabMain.setTabIcon(i, QIcon(appPath("icons/Custom/Tabs/{}".format(icons[i])))) item = QListWidgetItem(self.tabMain.tabIcon(i), self.tabMain.tabText(i)) item.setSizeHint(QSize(item.sizeHint().width(), 64)) item.setTextAlignment(Qt.AlignCenter) self.lstTabs.addItem(item) self.tabMain.tabBar().hide() self.lstTabs.currentRowChanged.connect(self.tabMain.setCurrentIndex) self.tabMain.currentChanged.connect(self.lstTabs.setCurrentRow) # Splitters self.splitterPersos.setStretchFactor(0, 25) self.splitterPersos.setStretchFactor(1, 75) self.splitterPlot.setStretchFactor(0, 20) self.splitterPlot.setStretchFactor(1, 60) self.splitterPlot.setStretchFactor(2, 30) self.splitterWorld.setStretchFactor(0, 25) self.splitterWorld.setStretchFactor(1, 75) self.splitterOutlineH.setStretchFactor(0, 25) self.splitterOutlineH.setStretchFactor(1, 75) self.splitterOutlineV.setStretchFactor(0, 75) self.splitterOutlineV.setStretchFactor(1, 25) self.splitterRedacV.setStretchFactor(0, 75) self.splitterRedacV.setStretchFactor(1, 25) self.splitterRedacH.setStretchFactor(0, 30) self.splitterRedacH.setStretchFactor(1, 40) self.splitterRedacH.setStretchFactor(2, 30) # QFormLayout stretch for w in [self.txtWorldDescription, self.txtWorldPassion, self.txtWorldConflict]: s = w.sizePolicy() s.setVerticalStretch(1) w.setSizePolicy(s) # Help box references = [ (self.lytTabOverview, self.tr("Enter infos about your book, and yourself."), 0), (self.lytSituation, self.tr( """The basic situation, in the form of a 'What if...?' question. Ex: 'What if the most dangerous evil wizard could wasn't abled to kill a baby?' (Harry Potter)"""), 1), (self.lytSummary, self.tr( """Take time to think about a one sentence (~50 words) summary of your book. Then expand it to a paragraph, then to a page, then to a full summary."""), 1), (self.lytTabPersos, self.tr("Create your characters."), 0), (self.lytTabPlot, self.tr("Develop plots."), 0), (self.lytTabOutline, self.tr("Create the outline of your masterpiece."), 0), (self.lytTabRedac, self.tr("Write."), 0), (self.lytTabDebug, self.tr("Debug infos. Sometimes useful."), 0) ] for widget, text, pos in references: label = helpLabel(text, self) self.actShowHelp.toggled.connect(label.setVisible, AUC) widget.layout().insertWidget(pos, label) self.actShowHelp.setChecked(False) # Spellcheck if enchant: self.menuDict = QMenu(self.tr("Dictionary")) self.menuDictGroup = QActionGroup(self) self.updateMenuDict() self.menuTools.addMenu(self.menuDict) self.actSpellcheck.toggled.connect(self.toggleSpellcheck, AUC) self.dictChanged.connect(self.mainEditor.setDict, AUC) self.dictChanged.connect(self.redacMetadata.setDict, AUC) self.dictChanged.connect(self.outlineItemEditor.setDict, AUC) else: # No Spell check support self.actSpellcheck.setVisible(False) a = QAction(self.tr("Install PyEnchant to use spellcheck"), self) a.setIcon(self.style().standardIcon(QStyle.SP_MessageBoxWarning)) a.triggered.connect(self.openPyEnchantWebPage, AUC) self.menuTools.addAction(a) ############################################################################### # SPELLCHECK ############################################################################### def updateMenuDict(self): if not enchant: return self.menuDict.clear() for i in enchant.list_dicts(): a = QAction(str(i[0]), self) a.setCheckable(True) if settings.dict is None: settings.dict = enchant.get_default_language() if str(i[0]) == settings.dict: a.setChecked(True) a.triggered.connect(self.setDictionary, AUC) self.menuDictGroup.addAction(a) self.menuDict.addAction(a) def setDictionary(self): if not enchant: return for i in self.menuDictGroup.actions(): if i.isChecked(): # self.dictChanged.emit(i.text().replace("&", "")) settings.dict = i.text().replace("&", "") # Find all textEditView from self, and toggle spellcheck for w in self.findChildren(textEditView, QRegExp(".*"), Qt.FindChildrenRecursively): w.setDict(settings.dict) def openPyEnchantWebPage(self): from PyQt5.QtGui import QDesktopServices QDesktopServices.openUrl(QUrl("http://pythonhosted.org/pyenchant/")) def toggleSpellcheck(self, val): settings.spellcheck = val # Find all textEditView from self, and toggle spellcheck for w in self.findChildren(textEditView, QRegExp(".*"), Qt.FindChildrenRecursively): w.toggleSpellcheck(val) ############################################################################### # SETTINGS ############################################################################### def settingsLabel(self): self.settingsWindow(3) def settingsStatus(self): self.settingsWindow(4) def settingsWindow(self, tab=None): self.sw = settingsWindow(self) self.sw.hide() self.sw.setWindowModality(Qt.ApplicationModal) self.sw.setWindowFlags(Qt.Dialog) r = self.sw.geometry() r2 = self.geometry() self.sw.move(r2.center() - r.center()) if tab: self.sw.setTab(tab) self.sw.show() ############################################################################### # TOOLS ############################################################################### def frequencyAnalyzer(self): self.fw = frequencyAnalyzer(self) self.fw.show() ############################################################################### # VIEW MENU ############################################################################### def generateViewMenu(self): values = [ (self.tr("Nothing"), "Nothing"), (self.tr("POV"), "POV"), (self.tr("Label"), "Label"), (self.tr("Progress"), "Progress"), (self.tr("Compile"), "Compile"), ] menus = [ (self.tr("Tree"), "Tree"), (self.tr("Index cards"), "Cork"), (self.tr("Outline"), "Outline") ] submenus = { "Tree": [ (self.tr("Icon color"), "Icon"), (self.tr("Text color"), "Text"), (self.tr("Background color"), "Background"), ], "Cork": [ (self.tr("Icon"), "Icon"), (self.tr("Text"), "Text"), (self.tr("Background"), "Background"), (self.tr("Border"), "Border"), (self.tr("Corner"), "Corner"), ], "Outline": [ (self.tr("Icon color"), "Icon"), (self.tr("Text color"), "Text"), (self.tr("Background color"), "Background"), ], } self.menuView.clear() # print("Generating menus with", settings.viewSettings) for mnu, mnud in menus: m = QMenu(mnu, self.menuView) for s, sd in submenus[mnud]: m2 = QMenu(s, m) agp = QActionGroup(m2) for v, vd in values: a = QAction(v, m) a.setCheckable(True) a.setData("{},{},{}".format(mnud, sd, vd)) if settings.viewSettings[mnud][sd] == vd: a.setChecked(True) a.triggered.connect(self.setViewSettingsAction, AUC) agp.addAction(a) m2.addAction(a) m.addMenu(m2) self.menuView.addMenu(m) def setViewSettingsAction(self): action = self.sender() item, part, element = action.data().split(",") self.setViewSettings(item, part, element) def setViewSettings(self, item, part, element): settings.viewSettings[item][part] = element if item == "Cork": self.mainEditor.updateCorkView() if item == "Outline": self.mainEditor.updateTreeView() self.treeOutlineOutline.viewport().update() if item == "Tree": self.treeRedacOutline.viewport().update() ############################################################################### # COMPILE ############################################################################### def doCompile(self): self.compileDialog = compileDialog() self.compileDialog.show()
def __init__(self): QMainWindow.__init__(self) self.setupUi(self) self.currentProject = None self.readSettings() # UI self.setupMoreUi() # Welcome self.welcome.updateValues() # self.welcome.btnCreate.clicked.connect self.stack.setCurrentIndex(0) # Word count self.mprWordCount = QSignalMapper(self) for t, i in [ (self.txtSummarySentence, 0), (self.txtSummaryPara, 1), (self.txtSummaryPage, 2), (self.txtSummaryFull, 3) ]: t.textChanged.connect(self.mprWordCount.map) self.mprWordCount.setMapping(t, i) self.mprWordCount.mapped.connect(self.wordCount) # Snowflake Method Cycle self.mapperCycle = QSignalMapper(self) for t, i in [ (self.btnStepTwo, 0), (self.btnStepThree, 1), (self.btnStepFour, 2), (self.btnStepFive, 3), (self.btnStepSix, 4), (self.btnStepSeven, 5), (self.btnStepEight, 6) ]: t.clicked.connect(self.mapperCycle.map) self.mapperCycle.setMapping(t, i) self.mapperCycle.mapped.connect(self.clickCycle) self.cmbSummary.currentIndexChanged.connect(self.summaryPageChanged) self.cmbSummary.setCurrentIndex(0) self.cmbSummary.currentIndexChanged.emit(0) # Main Menu for i in [self.actSave, self.actSaveAs, self.actCloseProject, self.menuEdit, self.menuMode, self.menuView, self.menuTools, self.menuHelp]: i.setEnabled(False) self.actOpen.triggered.connect(self.welcome.openFile) self.actSave.triggered.connect(self.saveDatas) self.actSaveAs.triggered.connect(self.welcome.saveAsFile) self.actCompile.triggered.connect(self.doCompile) self.actLabels.triggered.connect(self.settingsLabel) self.actStatus.triggered.connect(self.settingsStatus) self.actSettings.triggered.connect(self.settingsWindow) self.actCloseProject.triggered.connect(self.closeProject) self.actQuit.triggered.connect(self.close) self.actToolFrequency.triggered.connect(self.frequencyAnalyzer) self.generateViewMenu() self.makeUIConnections()
class RagingSeasWindow(QMainWindow): """A window used to play the game (contains both players' grids) """ def __init__(self): """ Initializes the main game's window and sets its attributes to their default values. """ super(RagingSeasWindow, self).__init__() self.ui = Ui_RagingSeasWindow() self.ui.setupUi(self) self.ui.menuFile.menuAction().setStatusTip(self.tr("File")) self.ui.menuHelp.menuAction().setStatusTip(self.tr("Help")) self.ui.playerOneLabel.hide() self.ui.playerTwoLabel.hide() self.leftGridMapper = QSignalMapper(self) self.rightGridMapper = QSignalMapper(self) self.statusLabel = QLabel(self) self.statusLabel.setObjectName("StatusLabel") self.ui.statusBar.setSizeGripEnabled(False) self.ui.statusBar.addPermanentWidget(self.statusLabel) self.setStyleSheet(style.INITIAL_MAIN_WINDOW_STYLE) self.setFixedSize(self.size()) self.setWindowFlags( self.windowFlags() ^ (QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowMaximizeButtonHint) ) self._session = None self._grid_size = None self._previous_grid_size = None self._is_new_game = False self._has_game_been_started = False self._current_ship_index = 0 self._current_orientation = ShipOrientation.horizontal def get_session(self): return self._session def set_session(self, value): self._session = value session = property(get_session, set_session) def get_grid_size(self): return self._grid_size def set_grid_size(self, value): self._grid_size = value grid_size = property(get_grid_size, set_grid_size) def get_is_new_game(self): return self._is_new_game def set_is_new_game(self, value): self._is_new_game = value is_new_game = property(get_is_new_game, set_is_new_game) def get_has_game_been_started(self): return self._has_game_been_started def set_has_game_been_started(self, value): self._has_game_been_started = value has_game_been_started = property(get_has_game_been_started, set_has_game_been_started) def get_current_ship_index(self): return self._current_ship_index def set_current_ship_index(self, value): self._current_ship_index = value current_ship_index = property(get_current_ship_index, set_current_ship_index) def get_current_orientation(self): return self._current_orientation def set_current_orientation(self, value): self._current_orientation = value current_orientation = property(get_current_orientation, set_current_orientation) def initialize_grids(self): """Creates the players' grids using the chose grid size. """ dimensions = self._grid_size.value # disconnect the signal mappers and clear the layouts if a game has # previously been started if self._has_game_been_started: self._disconnect_mappers() self._clear_layout(self.ui.gridLayoutLeft) self._clear_layout(self.ui.gridLayoutRight) else: self._has_game_been_started = True for i in xrange(0, dimensions): for j in xrange(0, dimensions): # creating QCustomPushButtons self._populate_grid_at_position(GridPosition.left, self.leftGridMapper, self.ui.gridLayoutLeft, (i, j)) self._populate_grid_at_position(GridPosition.right, self.rightGridMapper, self.ui.gridLayoutRight, (i, j)) # mapping QSignalMappers to the slots that they will be using self.leftGridMapper.mapped[str].connect(self.leftGridButtonClick) self.rightGridMapper.mapped[str].connect(self.rightGridButtonClick) self._adjust_main_window() self._update_status_bar() def resolve_grid_button(self, grid_layout, coordinates): """Fetches a button at the given coordinates. :param grid_layout: an instance of QGridLayout, :param coordinates: an instance of type Coordinates, location of the item :return: an instance of type QPushButton, located at the specified coordinates """ x, y = coordinates.x, coordinates.y layout_item = grid_layout.itemAtPosition(x, y) return layout_item.widget() def _adjust_main_window(self): # resizes the main window to fit the chosen size of the grid dimensions = self._grid_size.value width = dimensions * (style.FIELD_ICON_SIZE + 10) * 2 + \ style.SPACER_WIDTH height = dimensions * (style.FIELD_ICON_SIZE + 10) + \ style.PLAYER_LABEL_HEIGHT status_bar_geometry = self.ui.statusBar.geometry() self.setFixedSize(width, height + (status_bar_geometry.height() * 2)) self.setStyleSheet(style.NEW_GAME_MAIN_WINDOW_STYLE) self.ui.horizontalLayout.setGeometry(QRect(0, 0, width, height)) self.ui.playerOneLabel.show() self.ui.playerTwoLabel.show() def _populate_grid_at_position(self, grid_position, mapper, layout, coordinate_pair): # creates a button and positions it on the grid x, y = coordinate_pair[0], coordinate_pair[1] textual_coordinates = "{0}-{1}".format(x, y) button = QCustomPushButton( self, grid_position, Coordinates.parse_coordinates(textual_coordinates, self._grid_size) ) Helpers.paint_grid_button(button, style.FIELD_BLUE) button.setObjectName("GridButton") button.setFixedSize(style.FIELD_ICON_SIZE + 10, style.FIELD_ICON_SIZE + 10) button.setIconSize(QSize(style.FIELD_ICON_SIZE, style.FIELD_ICON_SIZE)) # set the QSignalMapper's mapping to work with strings mapper.setMapping(button, textual_coordinates) # connecting the button's clicked signal to the QSignalMappers # mapped slot button.clicked.connect(mapper.map) # finally, add the button to the QGridLayout layout.addWidget(button, x, y) def _clear_layout(self, layout): # recursively deletes a QLayout's items making it ready to accept new # ones if layout is not None: while layout.count(): child = layout.takeAt(0) if child.widget() is not None: child.widget().deleteLater() elif child.layout() is not None: self._clear_layout(child.layout()) def _disconnect_mappers(self): # resets the QSignalMappers so that they can be used again dimensions = self._previous_grid_size.value for i in xrange(0, dimensions): for j in xrange(0, dimensions): button_left = self.resolve_grid_button( self.ui.gridLayoutLeft, Coordinates(i, j) ) button_right = self.resolve_grid_button( self.ui.gridLayoutRight, Coordinates(i, j) ) self.leftGridMapper.removeMappings(button_left) self.rightGridMapper.removeMappings(button_right) self.leftGridMapper.disconnect() self.rightGridMapper.disconnect() def _update_status_bar(self): session_phase = self.session.session_phase message = EnumConverters.session_phase_to_string_converter( session_phase ) if session_phase == SessionPhase.fleet_layout: fleet = self.session.player_one.fleet current_ship = fleet.ships[self._current_ship_index] message += self.tr(", Current ship: {0} (size - {1})".format( EnumConverters.ship_type_to_string_converter( current_ship.ship_type ), current_ship.ship_type.value) ) self.statusLabel.setText(self.tr("Phase: " + message)) def _paint_player_grid(self, grid_layout, player, reveal=False): # marks the grid's squares according to their state grid = player.grid dimensions = grid.grid_size.value if player.player_type == PlayerType.human: reveal = True for i in xrange(0, dimensions): for j in xrange(0, dimensions): current_square = grid.squares.item((i, j)) owner = current_square.owner square_state = current_square.square_state button = self.resolve_grid_button(grid_layout, Coordinates(i, j)) if ( square_state == SquareState.vacant or square_state == SquareState.unoccupiable ): Helpers.paint_grid_button(button, style.FIELD_BLUE) elif square_state == SquareState.populated: if reveal: Helpers.paint_grid_button(button, style.FIELD_GRAY) else: Helpers.paint_grid_button(button, style.FIELD_BLUE) elif square_state == SquareState.hit: if owner is not None: if owner.ship_state == ShipState.damaged: Helpers.paint_grid_button(button, style.FIELD_RED) elif owner.ship_state == ShipState.sunk: Helpers.paint_grid_button(button, style.FIELD_BLACK) else: Helpers.paint_grid_button(button, style.FIELD_LIGHT_BLUE) def _paint_both_player_grids(self, player_one, player_two, reveal=False): self._paint_player_grid(self.ui.gridLayoutLeft, player_one, reveal) self._paint_player_grid(self.ui.gridLayoutRight, player_two, reveal) def _declare_winner(self, winner): self.session.session_phase = SessionPhase.game_over self._is_new_game = False self._update_status_bar() player_type_textual = EnumConverters.\ player_type_to_string_converter(winner.player_type) Helpers.raise_info( self, self.tr("Game over - {0} wins!".format( player_type_textual)) ) @pyqtSlot(str, name="leftGridButtonClick") def leftGridButtonClick(self, textual_coordinates): """Slot used when the left grid's QCustomPushButton is clicked. :param textual_coordinates: position of the button represented as a string """ numerical_coordinates = Coordinates.parse_coordinates( textual_coordinates, self._grid_size ) session_phase = self._session.session_phase if session_phase == SessionPhase.fleet_layout: grid = self._session.player_one.grid fleet = self._session.player_one.fleet ship = fleet.ships[self._current_ship_index] if fleet.position_ship(grid, ship, self._current_orientation, numerical_coordinates): self._current_ship_index += 1 if self._current_ship_index == len(fleet.ships): self._session.session_phase = SessionPhase.battle self._session.player_one.initialize_fleet() self._session.player_two.initialize_fleet() self._update_status_bar() self._paint_player_grid(self.ui.gridLayoutLeft, self._session.player_one) @pyqtSlot(str, name="rightGridButtonClick") def rightGridButtonClick(self, textual_coordinates): """Slot used when the right grid's QCustomPushButton is clicked. :param textual_coordinates: position of the button represented as a string """ numerical_coordinates = Coordinates.parse_coordinates( textual_coordinates, self._grid_size ) session_phase = self._session.session_phase player_one = self.session.player_one player_two = self.session.player_two if session_phase == SessionPhase.battle: if not player_one.shoot(player_two.grid, numerical_coordinates): return else: if player_two.fleet.is_sunk(): self._paint_both_player_grids(player_one, player_two, True) self._declare_winner(player_one) return if not player_two.shoot(player_one.grid): return else: if player_one.fleet.is_sunk(): self._paint_both_player_grids(player_one, player_two, True) self._declare_winner(player_two) return self._paint_both_player_grids(player_one, player_two) @pyqtSlot(name="menuNewGameClick") def menuNewGameClick(self): """Slot used when the "New Game" menu item is clicked. """ raise_dialog = True new_game_dialog = NewGameDialog(self) if self._is_new_game: if not Helpers.raise_question( self, self.tr("Are you sure you want to start a new game?") ): raise_dialog = False if raise_dialog: self._previous_grid_size = self._grid_size new_game_dialog.exec_() @pyqtSlot(name="menuAboutClick") def menuAboutClick(self): """Slot used when the "About" menu item is clicked. """ about_dialog = AboutDialog(self) about_dialog.exec_() @pyqtSlot(name="menuExitClick") def menuExitClick(self): """Slot used when the "Exit" menu item is clicked. """ self.close() def closeEvent(self, e): """Overridden close event. :param e: an instance of type QCloseEvent """ if self._is_new_game: if Helpers.raise_question( self, self.tr("Are you sure you want to quit?") ): e.accept() else: e.ignore()
def __init__(self, parent=None): super(MainWindow, self).__init__() self._csvFilePath = "" self.serialport = serial.Serial() self.receiver_thread = readerThread(self) self.receiver_thread.setPort(self.serialport) self._localEcho = None self._viewMode = None self._quickSendOptRow = 1 self.setupUi(self) self.setCorner(Qt.TopLeftCorner, Qt.LeftDockWidgetArea) self.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea) font = QtGui.QFont() font.setFamily(EDITOR_FONT) font.setPointSize(9) self.txtEdtOutput.setFont(font) self.txtEdtInput.setFont(font) #self.quickSendTable.setFont(font) if UI_FONT is not None: font = QtGui.QFont() font.setFamily(UI_FONT) font.setPointSize(9) self.dockWidget_PortConfig.setFont(font) self.dockWidget_SendHex.setFont(font) self.dockWidget_QuickSend.setFont(font) self.setupMenu() self.setupFlatUi() self.onEnumPorts() icon = QtGui.QIcon(":/MyTerm.ico") self.setWindowIcon(icon) self.actionAbout.setIcon(icon) self.defaultStyleWidget = QWidget() self.defaultStyleWidget.setWindowIcon(icon) icon = QtGui.QIcon(":/qt_logo_16.ico") self.actionAbout_Qt.setIcon(icon) self._viewGroup = QActionGroup(self) self._viewGroup.addAction(self.actionAscii) self._viewGroup.addAction(self.actionHex_lowercase) self._viewGroup.addAction(self.actionHEX_UPPERCASE) self._viewGroup.setExclusive(True) # bind events self.actionOpen_Cmd_File.triggered.connect(self.openQuickSend) self.actionSave_Log.triggered.connect(self.onSaveLog) self.actionExit.triggered.connect(self.onExit) self.actionOpen.triggered.connect(self.openPort) self.actionClose.triggered.connect(self.closePort) self.actionPort_Config_Panel.triggered.connect(self.onTogglePrtCfgPnl) self.actionQuick_Send_Panel.triggered.connect(self.onToggleQckSndPnl) self.actionSend_Hex_Panel.triggered.connect(self.onToggleHexPnl) self.dockWidget_PortConfig.visibilityChanged.connect(self.onVisiblePrtCfgPnl) self.dockWidget_QuickSend.visibilityChanged.connect(self.onVisibleQckSndPnl) self.dockWidget_SendHex.visibilityChanged.connect(self.onVisibleHexPnl) self.actionLocal_Echo.triggered.connect(self.onLocalEcho) self.actionAlways_On_Top.triggered.connect(self.onAlwaysOnTop) self.actionAscii.triggered.connect(self.onViewChanged) self.actionHex_lowercase.triggered.connect(self.onViewChanged) self.actionHEX_UPPERCASE.triggered.connect(self.onViewChanged) self.actionAbout.triggered.connect(self.onAbout) self.actionAbout_Qt.triggered.connect(self.onAboutQt) self.btnOpen.clicked.connect(self.onOpen) self.btnClear.clicked.connect(self.onClear) self.btnSaveLog.clicked.connect(self.onSaveLog) self.btnEnumPorts.clicked.connect(self.onEnumPorts) self.btnSendHex.clicked.connect(self.onSend) self.receiver_thread.read.connect(self.onReceive) self.receiver_thread.exception.connect(self.onReaderExcept) self._signalMapQuickSendOpt = QSignalMapper(self) self._signalMapQuickSendOpt.mapped[int].connect(self.onQuickSendOptions) self._signalMapQuickSend = QSignalMapper(self) self._signalMapQuickSend.mapped[int].connect(self.onQuickSend) # initial action self.actionHEX_UPPERCASE.setChecked(True) self.receiver_thread.setViewMode(VIEWMODE_HEX_UPPERCASE) self.initQuickSend() self.restoreLayout() self.moveScreenCenter() self.syncMenu() if self.isMaximized(): self.setMaximizeButton("restore") else: self.setMaximizeButton("maximize") self.loadSettings()
class MainWindow(QMainWindow): """This create the main window of the application""" def __init__(self): super(MainWindow, self).__init__() # remove close & maximize window buttons #self.setWindowFlags(Qt.CustomizeWindowHint|Qt.WindowMinimizeButtonHint) self.setMinimumSize(500, 666) #self.setMaximumSize(1000,666) self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdiArea) self.mdiArea.subWindowActivated.connect(self.updateMenus) self.mdiArea.setViewMode(QMdiArea.TabbedView) self.windowMapper = QSignalMapper(self) self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) self.child = None self.createActions() self.createMenus() self.createStatusBar() self.updateMenus() self.readSettings() self.setWindowTitle("LEKTURE") mytoolbar = QToolBar() #self.toolbar = self.addToolBar() mytoolbar.addAction(self.newAct) mytoolbar.addAction(self.openAct) mytoolbar.addAction(self.saveAct) mytoolbar.addAction(self.saveAsAct) mytoolbar.addSeparator() mytoolbar.addAction(self.outputsAct) mytoolbar.addAction(self.scenarioAct) self.scenarioAct.setVisible(False) mytoolbar.setMovable(False) mytoolbar.setFixedWidth(60) self.addToolBar(Qt.LeftToolBarArea, mytoolbar) def closeEvent(self, scenario): """method called when the main window wants to be closed""" self.mdiArea.closeAllSubWindows() if self.mdiArea.currentSubWindow(): scenario.ignore() else: self.writeSettings() scenario.accept() def newFile(self): """creates a new project""" child = self.createProjekt() child.newFile() child.show() self.child = child def open(self): """open a project""" fileName, _ = QFileDialog.getOpenFileName(self) if fileName: existing = self.findProjekt(fileName) if existing: self.mdiArea.setActiveSubWindow(existing) return child = self.createProjekt() if child.loadFile(fileName): self.statusBar().showMessage("File loaded", 2000) child.show() else: child.close() def save(self): """called when user save a project""" if self.activeProjekt() and self.activeProjekt().save(): self.statusBar().showMessage("File saved", 2000) else: self.statusBar().showMessage("Error when trying to save the file") def saveAs(self): """called when user save AS a project""" if self.activeProjekt() and self.activeProjekt().saveAs(): self.statusBar().showMessage("File saved", 2000) else: self.statusBar().showMessage("Error when trying to save the file") def openFolder(self): """called when user calls 'reveal in finder' function""" if self.activeProjekt() and self.activeProjekt().openFolder(): self.statusBar().showMessage("File revealed in Finder", 2000) def about(self): """called when user wants to know a bit more on the app""" import sys python_version = str(sys.version_info[0]) python_version_temp = sys.version_info[1:5] for item in python_version_temp: python_version = python_version + "." + str(item) QMessageBox.about(self, "About Lekture", "pylekture build " + str(pylekture.__version__ + "\n" + \ "python version " + str(python_version))) def updateMenus(self): """update menus""" hasProjekt = (self.activeProjekt() is not None) self.saveAct.setEnabled(hasProjekt) self.saveAsAct.setEnabled(hasProjekt) self.outputsAct.setEnabled(hasProjekt) self.scenarioAct.setEnabled(hasProjekt) self.openFolderAct.setEnabled(hasProjekt) self.closeAct.setEnabled(hasProjekt) self.closeAllAct.setEnabled(hasProjekt) self.nextAct.setEnabled(hasProjekt) self.previousAct.setEnabled(hasProjekt) self.separatorAct.setVisible(hasProjekt) def updateWindowMenu(self): """unpates menus on the window toolbar""" self.windowMenu.clear() self.windowMenu.addAction(self.closeAct) self.windowMenu.addAction(self.closeAllAct) self.windowMenu.addSeparator() self.windowMenu.addAction(self.nextAct) self.windowMenu.addAction(self.previousAct) self.windowMenu.addAction(self.separatorAct) windows = self.mdiArea.subWindowList() self.separatorAct.setVisible(len(windows) != 0) for i, window in enumerate(windows): child = window.widget() text = "%d %s" % (i + 1, child.userFriendlyCurrentFile()) if i < 9: text = '&' + text action = self.windowMenu.addAction(text) action.setCheckable(True) action.setChecked(child is self.activeProjekt()) action.triggered.connect(self.windowMapper.map) self.windowMapper.setMapping(action, window) def createProjekt(self): """create a new project""" child = Projekt() self.mdiArea.addSubWindow(child) self.child = child return child def createActions(self): """create all actions""" self.newAct = QAction("&New", self, shortcut=QKeySequence.New, statusTip="Create a new file", triggered=self.newFile) self.openAct = QAction("&Open...", self, shortcut=QKeySequence.Open, statusTip="Open an existing file", triggered=self.open) self.saveAct = QAction("&Save", self, shortcut=QKeySequence.Save, statusTip="Save the document to disk", triggered=self.save) self.saveAsAct = QAction("Save &As...", self, shortcut=QKeySequence.SaveAs, statusTip="Save the document under a new name", triggered=self.saveAs) self.openFolderAct = QAction("Open Project Folder", self, statusTip="Reveal Project in Finder", triggered=self.openFolder) self.exitAct = QAction("E&xit", self, shortcut=QKeySequence.Quit, statusTip="Exit the application", triggered=QApplication.instance().closeAllWindows) self.closeAct = QAction("Cl&ose", self, statusTip="Close the active window", triggered=self.mdiArea.closeActiveSubWindow) self.outputsAct = QAction("Outputs", self, statusTip="Open the outputs panel", triggered=self.openOutputsPanel) self.scenarioAct = QAction("Scenario", self, statusTip="Open the scenario panel", triggered=self.openScenarioPanel) self.closeAllAct = QAction("Close &All", self, statusTip="Close all the windows", triggered=self.mdiArea.closeAllSubWindows) self.nextAct = QAction("Ne&xt", self, shortcut=QKeySequence.NextChild, statusTip="Move the focus to the next window", triggered=self.mdiArea.activateNextSubWindow) self.previousAct = QAction("Pre&vious", self, shortcut=QKeySequence.PreviousChild, statusTip="Move the focus to the previous window", triggered=self.mdiArea.activatePreviousSubWindow) self.separatorAct = QAction(self) self.separatorAct.setSeparator(True) self.aboutAct = QAction("&About", self, statusTip="Show the application's About box", triggered=self.about) def createMenus(self): """create all menus""" self.fileMenu = self.menuBar().addMenu("&File") self.fileMenu.addAction(self.newAct) self.fileMenu.addAction(self.openAct) self.fileMenu.addAction(self.saveAct) self.fileMenu.addAction(self.saveAsAct) self.fileMenu.addSeparator() self.fileMenu.addAction(self.openFolderAct) self.fileMenu.addAction(self.exitAct) self.viewMenu = self.menuBar().addMenu("&View") self.viewMenu.addAction(self.outputsAct) self.viewMenu.addAction(self.scenarioAct) self.windowMenu = self.menuBar().addMenu("&Window") self.updateWindowMenu() self.windowMenu.aboutToShow.connect(self.updateWindowMenu) self.menuBar().addSeparator() self.helpMenu = self.menuBar().addMenu("&Help") self.helpMenu.addAction(self.aboutAct) def createStatusBar(self): """create the status bar""" self.statusBar().showMessage("Ready") def readSettings(self): """read the settings""" settings = QSettings('Pixel Stereo', 'lekture') pos = settings.value('pos', QPoint(200, 200)) size = settings.value('size', QSize(1000, 650)) self.move(pos) self.resize(size) def writeSettings(self): """write settings""" settings = QSettings('Pixel Stereo', 'lekture') settings.setValue('pos', self.pos()) settings.setValue('size', self.size()) def activeProjekt(self): """return the active project object""" activeSubWindow = self.mdiArea.activeSubWindow() if activeSubWindow: return activeSubWindow.widget() else: return None def findProjekt(self, fileName): """return the project""" canonicalFilePath = QFileInfo(fileName).canonicalFilePath() for window in self.mdiArea.subWindowList(): if window.widget().currentFile() == canonicalFilePath: return window return None def setActiveSubWindow(self, window): """set the active sub window""" if window: self.mdiArea.setActiveSubWindow(window) def openOutputsPanel(self): """switch to the outputs editor""" if self.child: project = self.activeProjekt() project.scenario_events_group.setVisible(False) project.outputs_group.setVisible(True) self.scenarioAct.setVisible(True) self.outputsAct.setVisible(False) def openScenarioPanel(self): """switch to the scenario editors""" if self.child: project = self.activeProjekt() project.outputs_group.setVisible(False) project.scenario_events_group.setVisible(True) self.scenarioAct.setVisible(False) self.outputsAct.setVisible(True)
def __initActions(self): """ Private method to initialize the view actions. """ self.alignMapper = QSignalMapper(self) self.alignMapper.mapped[int].connect(self.__alignShapes) self.deleteShapeAct = \ QAction(UI.PixmapCache.getIcon("deleteShape.png"), self.tr("Delete shapes"), self) self.deleteShapeAct.triggered.connect(self.__deleteShape) self.incWidthAct = \ QAction(UI.PixmapCache.getIcon("sceneWidthInc.png"), self.tr("Increase width by {0} points").format( self.deltaSize), self) self.incWidthAct.triggered.connect(self.__incWidth) self.incHeightAct = \ QAction(UI.PixmapCache.getIcon("sceneHeightInc.png"), self.tr("Increase height by {0} points").format( self.deltaSize), self) self.incHeightAct.triggered.connect(self.__incHeight) self.decWidthAct = \ QAction(UI.PixmapCache.getIcon("sceneWidthDec.png"), self.tr("Decrease width by {0} points").format( self.deltaSize), self) self.decWidthAct.triggered.connect(self.__decWidth) self.decHeightAct = \ QAction(UI.PixmapCache.getIcon("sceneHeightDec.png"), self.tr("Decrease height by {0} points").format( self.deltaSize), self) self.decHeightAct.triggered.connect(self.__decHeight) self.setSizeAct = \ QAction(UI.PixmapCache.getIcon("sceneSize.png"), self.tr("Set size"), self) self.setSizeAct.triggered.connect(self.__setSize) self.rescanAct = \ QAction(UI.PixmapCache.getIcon("rescan.png"), self.tr("Re-Scan"), self) self.rescanAct.triggered.connect(self.__rescan) self.relayoutAct = \ QAction(UI.PixmapCache.getIcon("relayout.png"), self.tr("Re-Layout"), self) self.relayoutAct.triggered.connect(self.__relayout) self.alignLeftAct = \ QAction(UI.PixmapCache.getIcon("shapesAlignLeft.png"), self.tr("Align Left"), self) self.alignMapper.setMapping(self.alignLeftAct, Qt.AlignLeft) self.alignLeftAct.triggered.connect(self.alignMapper.map) self.alignHCenterAct = \ QAction(UI.PixmapCache.getIcon("shapesAlignHCenter.png"), self.tr("Align Center Horizontal"), self) self.alignMapper.setMapping(self.alignHCenterAct, Qt.AlignHCenter) self.alignHCenterAct.triggered.connect(self.alignMapper.map) self.alignRightAct = \ QAction(UI.PixmapCache.getIcon("shapesAlignRight.png"), self.tr("Align Right"), self) self.alignMapper.setMapping(self.alignRightAct, Qt.AlignRight) self.alignRightAct.triggered.connect(self.alignMapper.map) self.alignTopAct = \ QAction(UI.PixmapCache.getIcon("shapesAlignTop.png"), self.tr("Align Top"), self) self.alignMapper.setMapping(self.alignTopAct, Qt.AlignTop) self.alignTopAct.triggered.connect(self.alignMapper.map) self.alignVCenterAct = \ QAction(UI.PixmapCache.getIcon("shapesAlignVCenter.png"), self.tr("Align Center Vertical"), self) self.alignMapper.setMapping(self.alignVCenterAct, Qt.AlignVCenter) self.alignVCenterAct.triggered.connect(self.alignMapper.map) self.alignBottomAct = \ QAction(UI.PixmapCache.getIcon("shapesAlignBottom.png"), self.tr("Align Bottom"), self) self.alignMapper.setMapping(self.alignBottomAct, Qt.AlignBottom) self.alignBottomAct.triggered.connect(self.alignMapper.map)
def contextMenu(self, parent, index): menu = None row = index.row() if (row >= 0 and row < self.mCommands.size()): menu = QMenu(parent) if (row > 0): action = menu.addAction(self.tr("Move Up")) mapper = QSignalMapper(action) mapper.setMapping(action, row) action.triggered.connect(mapper.map) mapper.mapped.connect(self.moveUp) if (row+1 < self.mCommands.size()): action = menu.addAction(self.tr("Move Down")) mapper = QSignalMapper(action) mapper.setMapping(action, row + 1) action.triggered.connect(mapper.map) mapper.mapped.connect(self.moveUp) menu.addSeparator() action = menu.addAction(self.tr("Execute")) mapper = QSignalMapper(action) mapper.setMapping(action, row) action.triggered.connect(mapper.map) mapper.mapped.connect(self.execute) if sys.platform in ['linux', 'darwin']: action = menu.addAction(self.tr("Execute in Terminal")) mapper = QSignalMapper(action) mapper.setMapping(action, row) action.triggered.connect(mapper.map) mapper.mapped.connect(self.executeInTerminal) menu.addSeparator() action = menu.addAction(self.tr("Delete")) mapper = QSignalMapper(action) mapper.setMapping(action, row) action.triggered.connect(mapper.map) mapper.mapped.connect(self.remove) return menu
class UMLGraphicsView(E5GraphicsView): """ Class implementing a specialized E5GraphicsView for our diagrams. @signal relayout() emitted to indicate a relayout of the diagram is requested """ relayout = pyqtSignal() def __init__(self, scene, parent=None): """ Constructor @param scene reference to the scene object (QGraphicsScene) @param parent parent widget of the view (QWidget) """ E5GraphicsView.__init__(self, scene, parent) self.setObjectName("UMLGraphicsView") self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate) self.diagramName = "Unnamed" self.__itemId = -1 self.border = 10 self.deltaSize = 100.0 self.__zoomWidget = E5ZoomWidget( UI.PixmapCache.getPixmap("zoomOut.png"), UI.PixmapCache.getPixmap("zoomIn.png"), UI.PixmapCache.getPixmap("zoomReset.png"), self) parent.statusBar().addPermanentWidget(self.__zoomWidget) self.__zoomWidget.setMapping( E5GraphicsView.ZoomLevels, E5GraphicsView.ZoomLevelDefault) self.__zoomWidget.valueChanged.connect(self.setZoom) self.zoomValueChanged.connect(self.__zoomWidget.setValue) self.__initActions() scene.changed.connect(self.__sceneChanged) self.grabGesture(Qt.PinchGesture) def __initActions(self): """ Private method to initialize the view actions. """ self.alignMapper = QSignalMapper(self) self.alignMapper.mapped[int].connect(self.__alignShapes) self.deleteShapeAct = \ QAction(UI.PixmapCache.getIcon("deleteShape.png"), self.tr("Delete shapes"), self) self.deleteShapeAct.triggered.connect(self.__deleteShape) self.incWidthAct = \ QAction(UI.PixmapCache.getIcon("sceneWidthInc.png"), self.tr("Increase width by {0} points").format( self.deltaSize), self) self.incWidthAct.triggered.connect(self.__incWidth) self.incHeightAct = \ QAction(UI.PixmapCache.getIcon("sceneHeightInc.png"), self.tr("Increase height by {0} points").format( self.deltaSize), self) self.incHeightAct.triggered.connect(self.__incHeight) self.decWidthAct = \ QAction(UI.PixmapCache.getIcon("sceneWidthDec.png"), self.tr("Decrease width by {0} points").format( self.deltaSize), self) self.decWidthAct.triggered.connect(self.__decWidth) self.decHeightAct = \ QAction(UI.PixmapCache.getIcon("sceneHeightDec.png"), self.tr("Decrease height by {0} points").format( self.deltaSize), self) self.decHeightAct.triggered.connect(self.__decHeight) self.setSizeAct = \ QAction(UI.PixmapCache.getIcon("sceneSize.png"), self.tr("Set size"), self) self.setSizeAct.triggered.connect(self.__setSize) self.rescanAct = \ QAction(UI.PixmapCache.getIcon("rescan.png"), self.tr("Re-Scan"), self) self.rescanAct.triggered.connect(self.__rescan) self.relayoutAct = \ QAction(UI.PixmapCache.getIcon("relayout.png"), self.tr("Re-Layout"), self) self.relayoutAct.triggered.connect(self.__relayout) self.alignLeftAct = \ QAction(UI.PixmapCache.getIcon("shapesAlignLeft.png"), self.tr("Align Left"), self) self.alignMapper.setMapping(self.alignLeftAct, Qt.AlignLeft) self.alignLeftAct.triggered.connect(self.alignMapper.map) self.alignHCenterAct = \ QAction(UI.PixmapCache.getIcon("shapesAlignHCenter.png"), self.tr("Align Center Horizontal"), self) self.alignMapper.setMapping(self.alignHCenterAct, Qt.AlignHCenter) self.alignHCenterAct.triggered.connect(self.alignMapper.map) self.alignRightAct = \ QAction(UI.PixmapCache.getIcon("shapesAlignRight.png"), self.tr("Align Right"), self) self.alignMapper.setMapping(self.alignRightAct, Qt.AlignRight) self.alignRightAct.triggered.connect(self.alignMapper.map) self.alignTopAct = \ QAction(UI.PixmapCache.getIcon("shapesAlignTop.png"), self.tr("Align Top"), self) self.alignMapper.setMapping(self.alignTopAct, Qt.AlignTop) self.alignTopAct.triggered.connect(self.alignMapper.map) self.alignVCenterAct = \ QAction(UI.PixmapCache.getIcon("shapesAlignVCenter.png"), self.tr("Align Center Vertical"), self) self.alignMapper.setMapping(self.alignVCenterAct, Qt.AlignVCenter) self.alignVCenterAct.triggered.connect(self.alignMapper.map) self.alignBottomAct = \ QAction(UI.PixmapCache.getIcon("shapesAlignBottom.png"), self.tr("Align Bottom"), self) self.alignMapper.setMapping(self.alignBottomAct, Qt.AlignBottom) self.alignBottomAct.triggered.connect(self.alignMapper.map) def __checkSizeActions(self): """ Private slot to set the enabled state of the size actions. """ diagramSize = self._getDiagramSize(10) sceneRect = self.scene().sceneRect() if (sceneRect.width() - self.deltaSize) < diagramSize.width(): self.decWidthAct.setEnabled(False) else: self.decWidthAct.setEnabled(True) if (sceneRect.height() - self.deltaSize) < diagramSize.height(): self.decHeightAct.setEnabled(False) else: self.decHeightAct.setEnabled(True) def __sceneChanged(self, areas): """ Private slot called when the scene changes. @param areas list of rectangles that contain changes (list of QRectF) """ if len(self.scene().selectedItems()) > 0: self.deleteShapeAct.setEnabled(True) else: self.deleteShapeAct.setEnabled(False) sceneRect = self.scene().sceneRect() newWidth = width = sceneRect.width() newHeight = height = sceneRect.height() rect = self.scene().itemsBoundingRect() # calculate with 10 pixel border on each side if sceneRect.right() - 10 < rect.right(): newWidth = rect.right() + 10 if sceneRect.bottom() - 10 < rect.bottom(): newHeight = rect.bottom() + 10 if newHeight != height or newWidth != width: self.setSceneSize(newWidth, newHeight) self.__checkSizeActions() def initToolBar(self): """ Public method to populate a toolbar with our actions. @return the populated toolBar (QToolBar) """ toolBar = QToolBar(self.tr("Graphics"), self) toolBar.setIconSize(UI.Config.ToolBarIconSize) toolBar.addAction(self.deleteShapeAct) toolBar.addSeparator() toolBar.addAction(self.alignLeftAct) toolBar.addAction(self.alignHCenterAct) toolBar.addAction(self.alignRightAct) toolBar.addAction(self.alignTopAct) toolBar.addAction(self.alignVCenterAct) toolBar.addAction(self.alignBottomAct) toolBar.addSeparator() toolBar.addAction(self.incWidthAct) toolBar.addAction(self.incHeightAct) toolBar.addAction(self.decWidthAct) toolBar.addAction(self.decHeightAct) toolBar.addAction(self.setSizeAct) toolBar.addSeparator() toolBar.addAction(self.rescanAct) toolBar.addAction(self.relayoutAct) return toolBar def filteredItems(self, items, itemType=UMLItem): """ Public method to filter a list of items. @param items list of items as returned by the scene object (QGraphicsItem) @param itemType type to be filtered (class) @return list of interesting collision items (QGraphicsItem) """ return [itm for itm in items if isinstance(itm, itemType)] def selectItems(self, items): """ Public method to select the given items. @param items list of items to be selected (list of QGraphicsItemItem) """ # step 1: deselect all items self.unselectItems() # step 2: select all given items for itm in items: if isinstance(itm, UMLItem): itm.setSelected(True) def selectItem(self, item): """ Public method to select an item. @param item item to be selected (QGraphicsItemItem) """ if isinstance(item, UMLItem): item.setSelected(not item.isSelected()) def __deleteShape(self): """ Private method to delete the selected shapes from the display. """ for item in self.scene().selectedItems(): item.removeAssociations() item.setSelected(False) self.scene().removeItem(item) del item def __incWidth(self): """ Private method to handle the increase width context menu entry. """ self.resizeScene(self.deltaSize, True) self.__checkSizeActions() def __incHeight(self): """ Private method to handle the increase height context menu entry. """ self.resizeScene(self.deltaSize, False) self.__checkSizeActions() def __decWidth(self): """ Private method to handle the decrease width context menu entry. """ self.resizeScene(-self.deltaSize, True) self.__checkSizeActions() def __decHeight(self): """ Private method to handle the decrease height context menu entry. """ self.resizeScene(-self.deltaSize, False) self.__checkSizeActions() def __setSize(self): """ Private method to handle the set size context menu entry. """ from .UMLSceneSizeDialog import UMLSceneSizeDialog rect = self._getDiagramRect(10) sceneRect = self.scene().sceneRect() dlg = UMLSceneSizeDialog(sceneRect.width(), sceneRect.height(), rect.width(), rect.height(), self) if dlg.exec_() == QDialog.Accepted: width, height = dlg.getData() self.setSceneSize(width, height) self.__checkSizeActions() def autoAdjustSceneSize(self, limit=False): """ Public method to adjust the scene size to the diagram size. @param limit flag indicating to limit the scene to the initial size (boolean) """ super(UMLGraphicsView, self).autoAdjustSceneSize(limit=limit) self.__checkSizeActions() def saveImage(self): """ Public method to handle the save context menu entry. """ fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( self, self.tr("Save Diagram"), "", self.tr("Portable Network Graphics (*.png);;" "Scalable Vector Graphics (*.svg)"), "", E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite)) if fname: ext = QFileInfo(fname).suffix() if not ext: ex = selectedFilter.split("(*")[1].split(")")[0] if ex: fname += ex if QFileInfo(fname).exists(): res = E5MessageBox.yesNo( self, self.tr("Save Diagram"), self.tr("<p>The file <b>{0}</b> already exists." " Overwrite it?</p>").format(fname), icon=E5MessageBox.Warning) if not res: return success = super(UMLGraphicsView, self).saveImage( fname, QFileInfo(fname).suffix().upper()) if not success: E5MessageBox.critical( self, self.tr("Save Diagram"), self.tr( """<p>The file <b>{0}</b> could not be saved.</p>""") .format(fname)) def __relayout(self): """ Private slot to handle the re-layout context menu entry. """ self.__itemId = -1 self.scene().clear() self.relayout.emit() def __rescan(self): """ Private slot to handle the re-scan context menu entry. """ # 1. save positions of all items and names of selected items itemPositions = {} selectedItems = [] for item in self.filteredItems(self.scene().items(), UMLItem): name = item.getName() if name: itemPositions[name] = (item.x(), item.y()) if item.isSelected(): selectedItems.append(name) # 2. save # 2. re-layout the diagram self.__relayout() # 3. move known items to the saved positions for item in self.filteredItems(self.scene().items(), UMLItem): name = item.getName() if name in itemPositions: item.setPos(*itemPositions[name]) if name in selectedItems: item.setSelected(True) def printDiagram(self): """ Public slot called to print the diagram. """ printer = QPrinter(mode=QPrinter.ScreenResolution) printer.setFullPage(True) if Preferences.getPrinter("ColorMode"): printer.setColorMode(QPrinter.Color) else: printer.setColorMode(QPrinter.GrayScale) if Preferences.getPrinter("FirstPageFirst"): printer.setPageOrder(QPrinter.FirstPageFirst) else: printer.setPageOrder(QPrinter.LastPageFirst) printer.setPageMargins( Preferences.getPrinter("LeftMargin") * 10, Preferences.getPrinter("TopMargin") * 10, Preferences.getPrinter("RightMargin") * 10, Preferences.getPrinter("BottomMargin") * 10, QPrinter.Millimeter ) printerName = Preferences.getPrinter("PrinterName") if printerName: printer.setPrinterName(printerName) printDialog = QPrintDialog(printer, self) if printDialog.exec_(): super(UMLGraphicsView, self).printDiagram( printer, self.diagramName) def printPreviewDiagram(self): """ Public slot called to show a print preview of the diagram. """ from PyQt5.QtPrintSupport import QPrintPreviewDialog printer = QPrinter(mode=QPrinter.ScreenResolution) printer.setFullPage(True) if Preferences.getPrinter("ColorMode"): printer.setColorMode(QPrinter.Color) else: printer.setColorMode(QPrinter.GrayScale) if Preferences.getPrinter("FirstPageFirst"): printer.setPageOrder(QPrinter.FirstPageFirst) else: printer.setPageOrder(QPrinter.LastPageFirst) printer.setPageMargins( Preferences.getPrinter("LeftMargin") * 10, Preferences.getPrinter("TopMargin") * 10, Preferences.getPrinter("RightMargin") * 10, Preferences.getPrinter("BottomMargin") * 10, QPrinter.Millimeter ) printerName = Preferences.getPrinter("PrinterName") if printerName: printer.setPrinterName(printerName) preview = QPrintPreviewDialog(printer, self) preview.paintRequested[QPrinter].connect(self.__printPreviewPrint) preview.exec_() def __printPreviewPrint(self, printer): """ Private slot to generate a print preview. @param printer reference to the printer object (QPrinter) """ super(UMLGraphicsView, self).printDiagram(printer, self.diagramName) def setDiagramName(self, name): """ Public slot to set the diagram name. @param name diagram name (string) """ self.diagramName = name def __alignShapes(self, alignment): """ Private slot to align the selected shapes. @param alignment alignment type (Qt.AlignmentFlag) """ # step 1: get all selected items items = self.scene().selectedItems() if len(items) <= 1: return # step 2: find the index of the item to align in relation to amount = None for i, item in enumerate(items): rect = item.sceneBoundingRect() if alignment == Qt.AlignLeft: if amount is None or rect.x() < amount: amount = rect.x() index = i elif alignment == Qt.AlignRight: if amount is None or rect.x() + rect.width() > amount: amount = rect.x() + rect.width() index = i elif alignment == Qt.AlignHCenter: if amount is None or rect.width() > amount: amount = rect.width() index = i elif alignment == Qt.AlignTop: if amount is None or rect.y() < amount: amount = rect.y() index = i elif alignment == Qt.AlignBottom: if amount is None or rect.y() + rect.height() > amount: amount = rect.y() + rect.height() index = i elif alignment == Qt.AlignVCenter: if amount is None or rect.height() > amount: amount = rect.height() index = i rect = items[index].sceneBoundingRect() # step 3: move the other items for i, item in enumerate(items): if i == index: continue itemrect = item.sceneBoundingRect() xOffset = yOffset = 0 if alignment == Qt.AlignLeft: xOffset = rect.x() - itemrect.x() elif alignment == Qt.AlignRight: xOffset = (rect.x() + rect.width()) - \ (itemrect.x() + itemrect.width()) elif alignment == Qt.AlignHCenter: xOffset = (rect.x() + rect.width() // 2) - \ (itemrect.x() + itemrect.width() // 2) elif alignment == Qt.AlignTop: yOffset = rect.y() - itemrect.y() elif alignment == Qt.AlignBottom: yOffset = (rect.y() + rect.height()) - \ (itemrect.y() + itemrect.height()) elif alignment == Qt.AlignVCenter: yOffset = (rect.y() + rect.height() // 2) - \ (itemrect.y() + itemrect.height() // 2) item.moveBy(xOffset, yOffset) self.scene().update() def __itemsBoundingRect(self, items): """ Private method to calculate the bounding rectangle of the given items. @param items list of items to operate on (list of UMLItem) @return bounding rectangle (QRectF) """ rect = self.scene().sceneRect() right = rect.left() bottom = rect.top() left = rect.right() top = rect.bottom() for item in items: rect = item.sceneBoundingRect() left = min(rect.left(), left) right = max(rect.right(), right) top = min(rect.top(), top) bottom = max(rect.bottom(), bottom) return QRectF(left, top, right - left, bottom - top) def keyPressEvent(self, evt): """ Protected method handling key press events. @param evt reference to the key event (QKeyEvent) """ key = evt.key() if key in [Qt.Key_Up, Qt.Key_Down, Qt.Key_Left, Qt.Key_Right]: items = self.filteredItems(self.scene().selectedItems()) if items: if evt.modifiers() & Qt.ControlModifier: stepSize = 50 else: stepSize = 5 if key == Qt.Key_Up: dx = 0 dy = -stepSize elif key == Qt.Key_Down: dx = 0 dy = stepSize elif key == Qt.Key_Left: dx = -stepSize dy = 0 else: dx = stepSize dy = 0 for item in items: item.moveBy(dx, dy) evt.accept() return super(UMLGraphicsView, self).keyPressEvent(evt) def wheelEvent(self, evt): """ Protected method to handle wheel events. @param evt reference to the wheel event (QWheelEvent) """ if evt.modifiers() & Qt.ControlModifier: if qVersion() >= "5.0.0": delta = evt.angleDelta().y() else: delta = evt.delta() if delta < 0: self.zoomOut() else: self.zoomIn() evt.accept() return super(UMLGraphicsView, self).wheelEvent(evt) def event(self, evt): """ Public method handling events. @param evt reference to the event (QEvent) @return flag indicating, if the event was handled (boolean) """ if evt.type() == QEvent.Gesture: self.gestureEvent(evt) return True return super(UMLGraphicsView, self).event(evt) def gestureEvent(self, evt): """ Protected method handling gesture events. @param evt reference to the gesture event (QGestureEvent """ pinch = evt.gesture(Qt.PinchGesture) if pinch: if pinch.state() == Qt.GestureStarted: pinch.setScaleFactor(self.zoom() / 100.0) else: self.setZoom(int(pinch.scaleFactor() * 100)) evt.accept() def getItemId(self): """ Public method to get the ID to be assigned to an item. @return item ID (integer) """ self.__itemId += 1 return self.__itemId def findItem(self, id): """ Public method to find an UML item based on the ID. @param id of the item to search for (integer) @return item found (UMLItem) or None """ for item in self.scene().items(): try: if item.getId() == id: return item except AttributeError: continue return None def findItemByName(self, name): """ Public method to find an UML item based on its name. @param name name to look for (string) @return item found (UMLItem) or None """ for item in self.scene().items(): try: if item.getName() == name: return item except AttributeError: continue return None def getPersistenceData(self): """ Public method to get a list of data to be persisted. @return list of data to be persisted (list of strings) """ lines = [ "diagram_name: {0}".format(self.diagramName), ] for item in self.filteredItems(self.scene().items(), UMLItem): lines.append("item: id={0}, x={1}, y={2}, item_type={3}{4}".format( item.getId(), item.x(), item.y(), item.getItemType(), item.buildItemDataString())) from .AssociationItem import AssociationItem for item in self.filteredItems(self.scene().items(), AssociationItem): lines.append("association: {0}".format( item.buildAssociationItemDataString())) return lines def parsePersistenceData(self, version, data): """ Public method to parse persisted data. @param version version of the data (string) @param data persisted data to be parsed (list of string) @return tuple of flag indicating success (boolean) and faulty line number (integer) """ umlItems = {} if not data[0].startswith("diagram_name:"): return False, 0 self.diagramName = data[0].split(": ", 1)[1].strip() from .ClassItem import ClassItem from .ModuleItem import ModuleItem from .PackageItem import PackageItem from .AssociationItem import AssociationItem linenum = 0 for line in data[1:]: linenum += 1 if not line.startswith(("item:", "association:")): return False, linenum key, value = line.split(": ", 1) if key == "item": id, x, y, itemType, itemData = value.split(", ", 4) try: id = int(id.split("=", 1)[1].strip()) x = float(x.split("=", 1)[1].strip()) y = float(y.split("=", 1)[1].strip()) itemType = itemType.split("=", 1)[1].strip() if itemType == ClassItem.ItemType: itm = ClassItem(x=x, y=y, scene=self.scene()) elif itemType == ModuleItem.ItemType: itm = ModuleItem(x=x, y=y, scene=self.scene()) elif itemType == PackageItem.ItemType: itm = PackageItem(x=x, y=y, scene=self.scene()) itm.setId(id) umlItems[id] = itm if not itm.parseItemDataString(version, itemData): return False, linenum except ValueError: return False, linenum elif key == "association": srcId, dstId, assocType, topToBottom = \ AssociationItem.parseAssociationItemDataString( value.strip()) assoc = AssociationItem(umlItems[srcId], umlItems[dstId], assocType, topToBottom) self.scene().addItem(assoc) return True, -1
class AddSeqTool(AbstractPathTool): """Summary Attributes: apply_button (TYPE): Description buttons (list): Description dialog (TYPE): Description highlighter (TYPE): Description seq_box (TYPE): Description sequence_radio_button_id (dict): Description signal_mapper (TYPE): Description use_abstract_sequence (bool): Description validated_sequence_to_apply (TYPE): Description """ def __init__(self, manager): """Summary Args: manager (TYPE): Description """ AbstractPathTool.__init__(self, manager) self.dialog = QDialog() self.buttons = [] self.seq_box = None self.sequence_radio_button_id = {} self.use_abstract_sequence = True self.validated_sequence_to_apply = None self.initDialog() def __repr__(self): """Summary Returns: TYPE: Description """ return "add_seq_tool" # first letter should be lowercase def methodPrefix(self): """Summary Returns: TYPE: Description """ return "addSeqTool" # first letter should be lowercase def initDialog(self): """Creates buttons for each sequence option and add them to the dialog. Maps the clicked signal of those buttons to keep track of what sequence gets selected. """ ui_dlg = Ui_AddSeqDialog() ui_dlg.setupUi(self.dialog) self.signal_mapper = QSignalMapper(self) # set up the radio buttons for i, name in enumerate(['Abstract', 'Custom'] + sorted(sequences.keys())): radio_button = QRadioButton(ui_dlg.group_box) radio_button.setObjectName(name + "Button") radio_button.setText(name) self.buttons.append(radio_button) ui_dlg.horizontalLayout.addWidget(radio_button) self.signal_mapper.setMapping(radio_button, i) radio_button.clicked.connect(self.signal_mapper.map) if name in sequences: self.sequence_radio_button_id[sequences[name]] = i self.signal_mapper.mapped.connect(self.sequenceOptionChangedSlot) # disable apply until valid option or custom sequence is chosen self.apply_button = ui_dlg.custom_button_box.button(QDialogButtonBox.Apply) self.apply_button.setEnabled(False) # watch sequence textedit box to validate custom sequences self.seq_box = ui_dlg.seq_text_edit self.seq_box.textChanged.connect(self.validateCustomSequence) self.highlighter = DNAHighlighter(self.seq_box) # finally, pre-click the first radio button self.buttons[0].click() def sequenceOptionChangedSlot(self, option_chosen): """ Connects to signal_mapper to receive a signal whenever user selects a sequence option. Args: option_chosen (TYPE): Description """ option_name = self.buttons[option_chosen].text() if option_name == 'Abstract': self.use_abstract_sequence = True elif option_name == 'Custom': self.use_abstract_sequence = False else: self.use_abstract_sequence = False user_sequence = sequences.get(option_name, None) if self.seq_box.toPlainText() != user_sequence: self.seq_box.setText(user_sequence) def validateCustomSequence(self): """ Called when user changes sequence (seq_box emits textChanged signal) If sequence is valid, make the apply_button active to click. Select an appropriate sequence option radio button, if necessary. """ user_sequence = self.seq_box.toPlainText() # Validate the sequence and activate the button if it checks out. if re.search(RE_DNA_PATTERN, user_sequence) is None: self.apply_button.setEnabled(True) else: self.apply_button.setEnabled(False) if len(user_sequence) == 0: # A zero-length custom sequence defaults to Abstract type. if not self.buttons[0].isChecked(): self.buttons[0].click() else: # Does this match a known sequence? if user_sequence in self.sequence_radio_button_id: # Handles case where the user might copy & paste in a known sequence i = self.sequence_radio_button_id[user_sequence] if not self.buttons[i].isChecked(): # Select the corresponding radio button for known sequence self.buttons[i].click() else: # Unrecognized, Custom type if not self.buttons[1].isChecked(): self.buttons[1].click() def applySequence(self, oligo): """Summary Args: oligo (TYPE): Description Returns: TYPE: Description """ self.dialog.setFocus() if self.dialog.exec_(): # apply the sequence if accept was clicked if self.use_abstract_sequence: oligo.applySequence(None) return (oligo.length(), None) else: self.validated_sequence_to_apply = self.seq_box.toPlainText().upper() oligo.applySequence(self.validated_sequence_to_apply) return oligo.length(), len(self.validated_sequence_to_apply) return (None, None)
def makePopupMenu(self): index = self.currentIndex() sel = self.getSelection() clipboard = qApp.clipboard() menu = QMenu(self) # Add / remove items self.actAddFolder = QAction(QIcon.fromTheme("folder-new"), qApp.translate("outlineBasics", "New Folder"), menu) self.actAddFolder.triggered.connect(self.addFolder) menu.addAction(self.actAddFolder) self.actAddText = QAction(QIcon.fromTheme("document-new"), qApp.translate("outlineBasics", "New Text"), menu) self.actAddText.triggered.connect(self.addText) menu.addAction(self.actAddText) self.actDelete = QAction(QIcon.fromTheme("edit-delete"), qApp.translate("outlineBasics", "Delete"), menu) self.actDelete.triggered.connect(self.delete) menu.addAction(self.actDelete) menu.addSeparator() # Copy, cut, paste self.actCopy = QAction(QIcon.fromTheme("edit-copy"), qApp.translate("outlineBasics", "Copy"), menu) self.actCopy.triggered.connect(self.copy) menu.addAction(self.actCopy) self.actCut = QAction(QIcon.fromTheme("edit-cut"), qApp.translate("outlineBasics", "Cut"), menu) self.actCut.triggered.connect(self.cut) menu.addAction(self.actCut) self.actPaste = QAction(QIcon.fromTheme("edit-paste"), qApp.translate("outlineBasics", "Paste"), menu) self.actPaste.triggered.connect(self.paste) menu.addAction(self.actPaste) menu.addSeparator() # POV self.menuPOV = QMenu(qApp.translate("outlineBasics", "Set POV"), menu) mw = mainWindow() a = QAction(QIcon.fromTheme("dialog-no"), qApp.translate("outlineBasics", "None"), self.menuPOV) a.triggered.connect(lambda: self.setPOV("")) self.menuPOV.addAction(a) self.menuPOV.addSeparator() menus = [] for i in [self.tr("Main"), self.tr("Secondary"), self.tr("Minor")]: m = QMenu(i, self.menuPOV) menus.append(m) self.menuPOV.addMenu(m) mpr = QSignalMapper(self.menuPOV) for i in range(mw.mdlPersos.rowCount()): a = QAction(mw.mdlPersos.icon(i), mw.mdlPersos.name(i), self.menuPOV) a.triggered.connect(mpr.map) mpr.setMapping(a, int(mw.mdlPersos.ID(i))) imp = toInt(mw.mdlPersos.importance(i)) menus[2 - imp].addAction(a) mpr.mapped.connect(self.setPOV) menu.addMenu(self.menuPOV) # Status self.menuStatus = QMenu(qApp.translate("outlineBasics", "Set Status"), menu) # a = QAction(QIcon.fromTheme("dialog-no"), qApp.translate("outlineBasics", "None"), self.menuStatus) # a.triggered.connect(lambda: self.setStatus("")) # self.menuStatus.addAction(a) # self.menuStatus.addSeparator() mpr = QSignalMapper(self.menuStatus) for i in range(mw.mdlStatus.rowCount()): a = QAction(mw.mdlStatus.item(i, 0).text(), self.menuStatus) a.triggered.connect(mpr.map) mpr.setMapping(a, i) self.menuStatus.addAction(a) mpr.mapped.connect(self.setStatus) menu.addMenu(self.menuStatus) # Labels self.menuLabel = QMenu(qApp.translate("outlineBasics", "Set Label"), menu) mpr = QSignalMapper(self.menuLabel) for i in range(mw.mdlLabels.rowCount()): a = QAction(mw.mdlLabels.item(i, 0).icon(), mw.mdlLabels.item(i, 0).text(), self.menuLabel) a.triggered.connect(mpr.map) mpr.setMapping(a, i) self.menuLabel.addAction(a) mpr.mapped.connect(self.setLabel) menu.addMenu(self.menuLabel) if len(sel) > 0 and index.isValid() and not index.internalPointer().isFolder() \ or not clipboard.mimeData().hasFormat("application/xml"): self.actPaste.setEnabled(False) if len(sel) > 0 and index.isValid() and not index.internalPointer().isFolder(): self.actAddFolder.setEnabled(False) self.actAddText.setEnabled(False) if len(sel) == 0: self.actCopy.setEnabled(False) self.actCut.setEnabled(False) self.actDelete.setEnabled(False) self.menuPOV.setEnabled(False) self.menuStatus.setEnabled(False) self.menuLabel.setEnabled(False) return menu