def testSignal(self): o = QWidget() act = QAction(o) self._called = False act.triggered.connect(self._cb) act.trigger() self.assert_(self._called)
def __init__(self,parent = None): QLabel.__init__(self, parent) self.headers = [] self.position = 0 self.viewWidth = 800 self.viewHeight = 100 self.hideFlag = False self.w = 180 self.h = 40 self.t = 10 self.setMouseTracking(True) self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.createHeaderMenus) self.deleteAct = QAction('Hide', self) self.deleteAct.setStatusTip('Hide this life-line') self.deleteAct.triggered.connect(self.hideLifeLine) self.groupAct = QAction('Make a cluster', self) self.groupAct.setStatusTip('Make a cluster of multiple life-lines') self.groupAct.triggered.connect(self.showClusterDialog) self.scatterAct = QAction('Scatter', self) self.scatterAct.setStatusTip('Scatter this cluster') self.scatterAct.triggered.connect(self.scatterCluster)
def testPythonStringAsQKeySequence(self): '''Passes a Python string to an argument expecting a QKeySequence.''' keyseq = py3k.unicode_('Ctrl+A') action = QAction(None) action.setShortcut(keyseq) shortcut = action.shortcut() self.assert_(isinstance(shortcut, QKeySequence)) self.assertEqual(shortcut.toString(), keyseq)
def testSetShortcut(self): # Somehow an exception was leaking from the constructor # and appearing in setShortcut. o = QWidget() action = QAction('aaaa', o) shortcut = 'Ctrl+N' action.setShortcut(shortcut) s2 = action.shortcut() self.assertEqual(s2, shortcut)
def __init__(self,diagramView): QtCore.QObject.__init__(self) self.actHide = QAction('Hide message', self) self.actHide.setStatusTip('Hide this message line') self.actHide.triggered.connect(self.hideOneMessage) self.actHideAll = QAction('Hide all', self) self.actHideAll.setStatusTip('Hide all the messages') self.actHideAll.triggered.connect(self.hideAllMessage) self.actViewCode = QAction('View code', self) self.actViewCode.setStatusTip('View source code of the message') self.actViewCode.triggered.connect(self.openCodeViewer) self.actCloseBody = QAction('Close body', self) self.actCloseBody.setStatusTip('View source code of the message') self.actCloseBody.triggered.connect(self.closeBody) self.diagramView = diagramView
def __init__(self): super(MainWindow, self).__init__() self.cameraInfo = QCameraInfo.defaultCamera() self.camera = QCamera(self.cameraInfo) self.camera.setCaptureMode(QCamera.CaptureStillImage) self.imageCapture = QCameraImageCapture(self.camera) self.imageCapture.imageCaptured.connect(self.imageCaptured) self.imageCapture.imageSaved.connect(self.imageSaved) self.currentPreview = QImage() toolBar = QToolBar() self.addToolBar(toolBar) fileMenu = self.menuBar().addMenu("&File") shutterIcon = QIcon(os.path.join(os.path.dirname(__file__), "shutter.svg")) self.takePictureAction = QAction(shutterIcon, "&Take Picture", self, shortcut="Ctrl+T", triggered=self.takePicture) self.takePictureAction.setToolTip("Take Picture") fileMenu.addAction(self.takePictureAction) toolBar.addAction(self.takePictureAction) exitAction = QAction(QIcon.fromTheme("application-exit"), "E&xit", self, shortcut="Ctrl+Q", triggered=self.close) fileMenu.addAction(exitAction) aboutMenu = self.menuBar().addMenu("&About") aboutQtAction = QAction("About &Qt", self, triggered=qApp.aboutQt) aboutMenu.addAction(aboutQtAction) self.tabWidget = QTabWidget() self.setCentralWidget(self.tabWidget) self.cameraViewfinder = QCameraViewfinder() self.camera.setViewfinder(self.cameraViewfinder) self.tabWidget.addTab(self.cameraViewfinder, "Viewfinder") if self.camera.status() != QCamera.UnavailableStatus: name = self.cameraInfo.description() self.setWindowTitle("PySide2 Camera Example (" + name + ")") self.statusBar().showMessage("Starting: '" + name + "'", 5000) self.camera.start() else: self.setWindowTitle("PySide2 Camera Example") self.takePictureAction.setEnabled(False) self.statusBar().showMessage("Camera unavailable", 5000)
class MessageMenu(QtCore.QObject): actHide = None actHideAll = None actViewCode = None actCloseBody = None diagramView = None def __init__(self,diagramView): QtCore.QObject.__init__(self) self.actHide = QAction('Hide message', self) self.actHide.setStatusTip('Hide this message line') self.actHide.triggered.connect(self.hideOneMessage) self.actHideAll = QAction('Hide all', self) self.actHideAll.setStatusTip('Hide all the messages') self.actHideAll.triggered.connect(self.hideAllMessage) self.actViewCode = QAction('View code', self) self.actViewCode.setStatusTip('View source code of the message') self.actViewCode.triggered.connect(self.openCodeViewer) self.actCloseBody = QAction('Close body', self) self.actCloseBody.setStatusTip('View source code of the message') self.actCloseBody.triggered.connect(self.closeBody) self.diagramView = diagramView def hideOneMessage(self): self.diagramView.activateHide(True) def hideAllMessage(self): self.diagramView.hideAllMessageSelected() def openCodeViewer(self): self.diagramView.openCodeViewer() def closeBody(self): self.diagramView.closeCurrentHighLightedBody() def createHeaderMenus(self): pass """
class MVCPlaybackControlGUI(PlaybackControlConsole): """ GUI implementation of MVCPlaybackControlBase """ nameFiltersChanged = Signal("QStringList") def __init__(self, config): assertMainThread() super().__init__(config) # state self.preventSeek = False self.beginTime = None self.timeRatio = 1.0 # gui srv = Services.getService("MainWindow") config.configLoaded.connect(self.restoreState) config.configAboutToSave.connect(self.saveState) self.config = config playbackMenu = srv.menuBar().addMenu("&Playback") style = QApplication.style() self.actStart = QAction( QIcon.fromTheme("media-playback-start", style.standardIcon(QStyle.SP_MediaPlay)), "Start Playback", self) self.actPause = QAction( QIcon.fromTheme("media-playback-pause", style.standardIcon(QStyle.SP_MediaPause)), "Pause Playback", self) self.actPause.setEnabled(False) self.actStepFwd = QAction( QIcon.fromTheme("media-seek-forward", style.standardIcon(QStyle.SP_MediaSeekForward)), "Step Forward", self) self.actStepBwd = QAction( QIcon.fromTheme("media-seek-backward", style.standardIcon(QStyle.SP_MediaSeekBackward)), "Step Backward", self) self.actSeekEnd = QAction( QIcon.fromTheme("media-skip-forward", style.standardIcon(QStyle.SP_MediaSkipForward)), "Seek End", self) self.actSeekBegin = QAction( QIcon.fromTheme("media-skip-backward", style.standardIcon(QStyle.SP_MediaSkipBackward)), "Seek Begin", self) self.actSetTimeFactor = { r: QAction("x 1/%d" % (1 / r), self) if r < 1 else QAction("x %d" % r, self) for r in (1 / 8, 1 / 4, 1 / 2, 1, 2, 4, 8) } # pylint: disable=unnecessary-lambda # let's stay on the safe side and do not use emit as a slot... self.actStart.triggered.connect(self.startPlayback) self.actPause.triggered.connect(self.pausePlayback) self.actStepFwd.triggered.connect( lambda: self.stepForward(self.selectedStream())) self.actStepBwd.triggered.connect( lambda: self.stepBackward(self.selectedStream())) self.actSeekEnd.triggered.connect(self.seekEnd) self.actSeekBegin.triggered.connect(self.seekBeginning) # pylint: enable=unnecessary-lambda def setTimeFactor(newFactor): logger.debug("new time factor %f", newFactor) self.setTimeFactor(newFactor) for r in self.actSetTimeFactor: logger.debug("adding action for time factor %f", r) self.actSetTimeFactor[r].triggered.connect( functools.partial(setTimeFactor, r)) self.dockWidget = srv.newDockWidget("PlaybackControl", None, Qt.LeftDockWidgetArea) self.dockWidgetContents = QWidget(self.dockWidget) self.dockWidget.setWidget(self.dockWidgetContents) toolLayout = QBoxLayout(QBoxLayout.TopToBottom, self.dockWidgetContents) toolLayout.setContentsMargins(0, 0, 0, 0) toolBar = QToolBar() toolLayout.addWidget(toolBar) toolBar.addAction(self.actSeekBegin) toolBar.addAction(self.actStepBwd) toolBar.addAction(self.actStart) toolBar.addAction(self.actPause) toolBar.addAction(self.actStepFwd) toolBar.addAction(self.actSeekEnd) playbackMenu.addAction(self.actSeekBegin) playbackMenu.addAction(self.actStepBwd) playbackMenu.addAction(self.actStart) playbackMenu.addAction(self.actPause) playbackMenu.addAction(self.actStepFwd) playbackMenu.addAction(self.actSeekEnd) playbackMenu.addSeparator() for r in self.actSetTimeFactor: playbackMenu.addAction(self.actSetTimeFactor[r]) self.timeRatioLabel = QLabel("x 1") self.timeRatioLabel.addActions(list(self.actSetTimeFactor.values())) self.timeRatioLabel.setContextMenuPolicy(Qt.ActionsContextMenu) toolBar.addSeparator() toolBar.addWidget(self.timeRatioLabel) contentsLayout = QGridLayout() toolLayout.addLayout(contentsLayout, 10) # now we add a position view self.positionSlider = QSlider(Qt.Horizontal, self.dockWidgetContents) self.beginLabel = QLabel(parent=self.dockWidgetContents) self.beginLabel.setAlignment(Qt.AlignLeft | Qt.AlignCenter) self.currentLabel = QLabel(parent=self.dockWidgetContents) self.currentLabel.setAlignment(Qt.AlignHCenter | Qt.AlignCenter) self.endLabel = QLabel(parent=self.dockWidgetContents) self.endLabel.setAlignment(Qt.AlignRight | Qt.AlignCenter) contentsLayout.addWidget(self.beginLabel, 0, 0, alignment=Qt.AlignLeft) contentsLayout.addWidget(self.currentLabel, 0, 1, alignment=Qt.AlignHCenter) contentsLayout.addWidget(self.endLabel, 0, 2, alignment=Qt.AlignRight) contentsLayout.addWidget(self.positionSlider, 1, 0, 1, 3) self.positionSlider.setTracking(False) self.positionSlider.valueChanged.connect(self.onSliderValueChanged, Qt.DirectConnection) self.positionSlider.sliderMoved.connect(self.displayPosition) # file browser self.browser = BrowserWidget(self.dockWidget) self.nameFiltersChanged.connect(self._onNameFiltersChanged, Qt.QueuedConnection) contentsLayout.addWidget(self.browser, 3, 0, 1, 3) contentsLayout.setRowStretch(3, 100) self.browser.activated.connect(self.browserActivated) self.actShowAllFiles = QAction("Show all files") self.actShowAllFiles.setCheckable(True) self.actShowAllFiles.setChecked(False) self.actShowAllFiles.toggled.connect(self._onShowAllFiles) playbackMenu.addSeparator() playbackMenu.addAction(self.actShowAllFiles) self.actGroupStream = QActionGroup(self) self.actGroupStream.setExclusionPolicy( QActionGroup.ExclusionPolicy.ExclusiveOptional) playbackMenu.addSeparator() self.actGroupStreamMenu = playbackMenu.addMenu("Step Stream") self._selectedStream = None self.recentSeqs = [QAction() for i in range(10)] playbackMenu.addSeparator() recentMenu = playbackMenu.addMenu("Recent") for a in self.recentSeqs: a.setVisible(False) a.triggered.connect(self.openRecent) recentMenu.addAction(a) self._supportedFeaturesChanged(set(), set()) def __del__(self): logger.internal("deleting playback control") def _onNameFiltersChanged(self, nameFilt): self.browser.setFilter(nameFilt) if isinstance(nameFilt, str): nameFilt = [nameFilt] for a in self.recentSeqs: if a.isVisible(): m = QDir.match(nameFilt, Path(a.data()).name) a.setEnabled(m) def _onShowAllFiles(self, enabled): self.fileSystemModel.setNameFilterDisables(enabled) def _supportedFeaturesChanged(self, featureset, nameFilters): """ overwritten from MVCPlaybackControlBase. This function is called from multiple threads, but not at the same time. :param featureset: the current featureset :return: """ assertMainThread() self.featureset = featureset self.actStepFwd.setEnabled("stepForward" in featureset) self.actStepBwd.setEnabled("stepBackward" in featureset) self.actSeekBegin.setEnabled("seekBeginning" in featureset) self.actSeekEnd.setEnabled("seekEnd" in featureset) self.positionSlider.setEnabled("seekTime" in featureset) self.browser.setEnabled("setSequence" in featureset) self.timeRatioLabel.setEnabled("setTimeFactor" in featureset) for f in self.actSetTimeFactor: self.actSetTimeFactor[f].setEnabled("setTimeFactor" in featureset) self.timeRatioLabel.setEnabled("setTimeFactor" in featureset) self.timeRatioLabel.setEnabled("setTimeFactor" in featureset) self.timeRatioLabel.setEnabled("setTimeFactor" in featureset) self.timeRatioLabel.setEnabled("setTimeFactor" in featureset) if "startPlayback" not in featureset: self.actStart.setEnabled(False) if "pausePlayback" not in featureset: self.actPause.setEnabled(False) logger.debug("current feature set: %s", featureset) logger.debug("Setting name filters of browser: %s", list(nameFilters)) self.nameFiltersChanged.emit(list(nameFilters)) super()._supportedFeaturesChanged(featureset, nameFilters) def scrollToCurrent(self): """ Scrolls to the current item in the browser :return: """ assertMainThread() c = self.browser.current() if c is not None: self.browser.scrollTo(c) def _sequenceOpened(self, filename, begin, end, streams): """ Notifies about an opened sequence. :param filename: the filename which has been opened :param begin: timestamp of sequence's first sample :param end: timestamp of sequence's last sample :param streams: list of streams in the sequence :return: None """ assertMainThread() self.beginTime = begin self.preventSeek = True self.positionSlider.setRange(0, (end - begin) // 1000000) self.preventSeek = False self.beginLabel.setText(self._timeToString(0)) self.endLabel.setText(self._timeToString((end - begin) // 1000000)) self._currentTimestampChanged(begin) try: self.browser.blockSignals(True) self.browser.setActive(filename) self.browser.scrollTo(filename) finally: self.browser.blockSignals(False) self._selectedStream = None for a in self.actGroupStream.actions(): logger.debug("Remove stream group action: %s", a.data()) self.actGroupStream.removeAction(a) for stream in streams: act = QAction(stream, self.actGroupStream) act.triggered.connect( lambda cstream=stream: self.setSelectedStream(cstream)) act.setCheckable(True) act.setChecked(False) logger.debug("Add stream group action: %s", act.data()) self.actGroupStreamMenu.addAction(act) QTimer.singleShot(250, self.scrollToCurrent) super()._sequenceOpened(filename, begin, end, streams) @staticmethod def _splitTime(milliseconds): hours = milliseconds // (60 * 60 * 1000) milliseconds -= hours * (60 * 60 * 1000) minutes = milliseconds // (60 * 1000) milliseconds -= minutes * (60 * 1000) seconds = milliseconds // 1000 milliseconds -= seconds * 1000 return hours, minutes, seconds, milliseconds @staticmethod def _timeToString(milliseconds): return "%02d:%02d:%02d.%03d" % ( MVCPlaybackControlGUI._splitTime(milliseconds)) def _currentTimestampChanged(self, currentTime): """ Notifies about a changed timestamp :param currentTime: the new current timestamp :return: None """ assertMainThread() if self.beginTime is None: self.currentLabel.setText("") else: sliderVal = (currentTime - self.beginTime ) // 1000000 # nanoseconds to milliseconds self.preventSeek = True self.positionSlider.setValue(sliderVal) self.preventSeek = False self.positionSlider.blockSignals(False) self.currentLabel.setEnabled(True) self.currentLabel.setText(self._timeToString(sliderVal)) super()._currentTimestampChanged(currentTime) def onSliderValueChanged(self, value): """ Slot called whenever the slider value is changed. :param value: the new slider value :return: """ assertMainThread() if self.beginTime is None or self.preventSeek: return if self.actStart.isEnabled(): ts = self.beginTime + value * 1000000 self.seekTime(ts) else: logger.warning("Can't seek while playing.") def displayPosition(self, value): """ Slot called when the slider is moved. Displays the position without actually seeking to it. :param value: the new slider value. :return: """ assertMainThread() if self.beginTime is None: return if self.positionSlider.isSliderDown(): ts = self.beginTime // 1000000 + value self.currentLabel.setEnabled(False) self.currentLabel.setText(self._timeToString(ts)) def _playbackStarted(self): """ Notifies about starting playback :return: None """ assertMainThread() self.actStart.setEnabled(False) if "pausePlayback" in self.featureset: self.actPause.setEnabled(True) super()._playbackStarted() def _playbackPaused(self): """ Notifies about pause playback :return: None """ assertMainThread() logger.debug("playbackPaused received") if "startPlayback" in self.featureset: self.actStart.setEnabled(True) self.actPause.setEnabled(False) super()._playbackPaused() def openRecent(self): """ Called when the user clicks on a recent sequence. :return: """ assertMainThread() action = self.sender() self.browser.setActive(action.data()) def browserActivated(self, filename): """ Called when the user activated a file. :param filename: the new filename :return: """ assertMainThread() if filename is not None and Path(filename).is_file(): foundIdx = None for i, a in enumerate(self.recentSeqs): if a.data() == filename: foundIdx = i if foundIdx is None: foundIdx = len(self.recentSeqs) - 1 for i in range(foundIdx, 0, -1): self.recentSeqs[i].setText(self.recentSeqs[i - 1].text()) self.recentSeqs[i].setData(self.recentSeqs[i - 1].data()) logger.debug("%d data: %s", i, self.recentSeqs[i - 1].data()) self.recentSeqs[i].setVisible( self.recentSeqs[i - 1].data() is not None) self.recentSeqs[0].setText(self.compressFileName(filename)) self.recentSeqs[0].setData(filename) self.recentSeqs[0].setVisible(True) self.setSequence(filename) def _timeRatioChanged(self, newRatio): """ Notifies about a changed playback time ratio, :param newRatio the new playback ratio as a float :return: None """ assertMainThread() self.timeRatio = newRatio logger.debug("new timeRatio: %f", newRatio) for r in [1 / 8, 1 / 4, 1 / 2, 1, 2, 4, 8]: if abs(newRatio / r - 1) < 0.01: self.timeRatioLabel.setText(("x 1/%d" % (1 / r)) if r < 1 else ("x %d" % r)) return self.timeRatioLabel.setText("%.2f" % newRatio) super()._timeRatioChanged(newRatio) def selectedStream(self): """ Returns the user-selected stream (for forward/backward stepping) :return: """ return self._selectedStream def setSelectedStream(self, stream): """ Sets the user-selected stream (for forward/backward stepping) :param stream the stream name. :return: """ self._selectedStream = stream def _defineProperties(self): propertyCollection = self.config.guiState() propertyCollection.defineProperty("PlaybackControl_showAllFiles", 0, "show all files setting") propertyCollection.defineProperty("PlaybackControl_folder", "", "current folder name") propertyCollection.defineProperty("PlaybackControl_recent", "", "recent opened sequences") def saveState(self): """ Saves the state of the playback control :return: """ assertMainThread() self._defineProperties() propertyCollection = self.config.guiState() showAllFiles = self.actShowAllFiles.isChecked() folder = self.browser.folder() logger.debug("Storing current folder: %s", folder) try: propertyCollection.setProperty("PlaybackControl_showAllFiles", int(showAllFiles)) propertyCollection.setProperty("PlaybackControl_folder", folder) recentFiles = [ a.data() for a in self.recentSeqs if a.data() is not None ] propertyCollection.setProperty("PlaybackControl_recent", "|".join(recentFiles)) except PropertyCollectionPropertyNotFound: pass def restoreState(self): """ Restores the state of the playback control from the given property collection :param propertyCollection: a PropertyCollection instance :return: """ assertMainThread() self._defineProperties() propertyCollection = self.config.guiState() showAllFiles = propertyCollection.getProperty( "PlaybackControl_showAllFiles") self.actShowAllFiles.setChecked(bool(showAllFiles)) folder = propertyCollection.getProperty("PlaybackControl_folder") if Path(folder).is_dir(): logger.debug("Setting current file: %s", folder) self.browser.setFolder(folder) recentFiles = propertyCollection.getProperty("PlaybackControl_recent") idx = 0 for f in recentFiles.split("|"): if f != "" and Path(f).is_file(): self.recentSeqs[idx].setData(f) self.recentSeqs[idx].setText(self.compressFileName(f)) self.recentSeqs[idx].setVisible(True) self.recentSeqs[idx].setEnabled(False) idx += 1 if idx >= len(self.recentSeqs): break for a in self.recentSeqs[idx:]: a.setData(None) a.setText("") a.setVisible(False) @staticmethod def compressFileName(filename): """ Compresses long path names with an ellipsis (...) :param filename: the original path name as a Path or string instance :return: the compressed path name as a string instance """ p = Path(filename) parts = tuple(p.parts) if len(parts) >= 6: p = Path(*parts[:2]) / "..." / Path(*parts[-2:]) return str(p)
def settings_action(self): act = QAction(QIcon('exit.png'), 'Settings', self) act.setStatusTip('Set connecting and other properties') act.triggered.connect(self.settings) return act
def __init__(self): super(MainWindow, self).__init__() self.playlist = QMediaPlaylist() self.player = QMediaPlayer() # Setting QToolBar toolBar = QToolBar() self.addToolBar(toolBar) fileMenu = self.menuBar().addMenu("&File") openAction = QAction(QIcon.fromTheme("document-open"), "&Open...", self, shortcut=QKeySequence.Open, triggered=self.open) fileMenu.addAction(openAction) exitAction = QAction(QIcon.fromTheme("application-exit"), "E&xit", self, shortcut="Ctrl+Q", triggered=self.close) fileMenu.addAction(exitAction) playMenu = self.menuBar().addMenu("&Play") playIcon = self.style().standardIcon(QStyle.SP_MediaPlay) self.playAction = toolBar.addAction(playIcon, "Play") self.playAction.triggered.connect(self.player.play) playMenu.addAction(self.playAction) previousIcon = self.style().standardIcon(QStyle.SP_MediaSkipBackward) self.previousAction = toolBar.addAction(previousIcon, "Previous") self.previousAction.triggered.connect(self.previousClicked) playMenu.addAction(self.previousAction) pauseIcon = self.style().standardIcon(QStyle.SP_MediaPause) self.pauseAction = toolBar.addAction(pauseIcon, "Pause") self.pauseAction.triggered.connect(self.player.pause) playMenu.addAction(self.pauseAction) nextIcon = self.style().standardIcon(QStyle.SP_MediaSkipForward) self.nextAction = toolBar.addAction(nextIcon, "Next") self.nextAction.triggered.connect(self.playlist.next) playMenu.addAction(self.nextAction) stopIcon = self.style().standardIcon(QStyle.SP_MediaStop) self.stopAction = toolBar.addAction(stopIcon, "Stop") self.stopAction.triggered.connect(self.player.stop) playMenu.addAction(self.stopAction) # Setting the QSlider self.volSlider = QSlider() self.volSlider.setOrientation(Qt.Horizontal) self.volSlider.setMinimum(0) self.volSlider.setMaximum(100) self.volSlider.setFixedWidth(120) self.volSlider.setValue(self.player.volume()) self.volSlider.setTickInterval(10) self.volSlider.setTickPosition(QSlider.TicksBelow) self.volSlider.setToolTip("Volume") self.volSlider.valueChanged.connect(self.player.setVolume) toolBar.addWidget(self.volSlider) aboutMenu = self.menuBar().addMenu("&About") aboutQtAct = QAction("About &Qt", self, triggered=QApplication.aboutQt) aboutMenu.addAction(aboutQtAct) self.videoWidget = QVideoWidget() self.setCentralWidget(self.videoWidget) self.player.setPlaylist(self.playlist) self.player.stateChanged.connect(self.updateButtons) self.player.setVideoOutput(self.videoWidget) self.updateButtons(self.player.state())
class QLiberationWindow(QMainWindow): def __init__(self): super(QLiberationWindow, self).__init__() self.info_panel = None self.setGame(persistency.restore_game()) self.setGeometry(300, 100, 270, 100) self.setWindowTitle("DCS Liberation - v" + CONST.VERSION_STRING) self.setWindowIcon(QIcon("./resources/icon.png")) self.statusBar().showMessage('Ready') self.initUi() self.initActions() self.initMenuBar() self.initToolbar() self.connectSignals() self.onGameGenerated(self.game) screen = QDesktopWidget().screenGeometry() self.setGeometry(0, 0, screen.width(), screen.height()) self.setWindowState(Qt.WindowMaximized) def initUi(self): self.liberation_map = QLiberationMap(self.game) self.info_panel = QInfoPanel(self.game) hbox = QSplitter(Qt.Horizontal) hbox.addWidget(self.info_panel) hbox.addWidget(self.liberation_map) hbox.setSizes([2, 8]) vbox = QVBoxLayout() vbox.setMargin(0) vbox.addWidget(QTopPanel(self.game)) vbox.addWidget(hbox) central_widget = QWidget() central_widget.setLayout(vbox) self.setCentralWidget(central_widget) def connectSignals(self): GameUpdateSignal.get_instance().gameupdated.connect(self.setGame) GameUpdateSignal.get_instance().debriefingReceived.connect( self.onDebriefing) def initActions(self): self.newGameAction = QAction("&New Game", self) self.newGameAction.setIcon(QIcon(CONST.ICONS["New"])) self.newGameAction.triggered.connect(self.newGame) self.newGameAction.setShortcut('CTRL+N') self.openAction = QAction("&Open", self) self.openAction.setIcon(QIcon(CONST.ICONS["Open"])) self.openAction.triggered.connect(self.openFile) self.openAction.setShortcut('CTRL+O') self.saveGameAction = QAction("&Save", self) self.saveGameAction.setIcon(QIcon(CONST.ICONS["Save"])) self.saveGameAction.triggered.connect(self.saveGame) self.saveGameAction.setShortcut('CTRL+S') self.saveAsAction = QAction("Save &As", self) self.saveAsAction.setIcon(QIcon(CONST.ICONS["Save"])) self.saveAsAction.triggered.connect(self.saveGameAs) self.saveAsAction.setShortcut('CTRL+A') self.showAboutDialogAction = QAction("&About DCS Liberation", self) self.showAboutDialogAction.setIcon(QIcon.fromTheme("help-about")) self.showAboutDialogAction.triggered.connect(self.showAboutDialog) self.showLiberationPrefDialogAction = QAction("&Preferences", self) self.showLiberationPrefDialogAction.setIcon( QIcon.fromTheme("help-about")) self.showLiberationPrefDialogAction.triggered.connect( self.showLiberationDialog) def initToolbar(self): self.tool_bar = self.addToolBar("File") self.tool_bar.addAction(self.newGameAction) self.tool_bar.addAction(self.openAction) self.tool_bar.addAction(self.saveGameAction) def initMenuBar(self): self.menu = self.menuBar() file_menu = self.menu.addMenu("&File") file_menu.addAction(self.newGameAction) file_menu.addAction(self.openAction) file_menu.addSeparator() file_menu.addAction(self.saveGameAction) file_menu.addAction(self.saveAsAction) file_menu.addSeparator() file_menu.addAction(self.showLiberationPrefDialogAction) file_menu.addSeparator() #file_menu.addAction("Close Current Game", lambda: self.closeGame()) # Not working file_menu.addAction("E&xit", lambda: self.exit()) displayMenu = self.menu.addMenu("&Display") tg_cp_visibility = QAction('&Control Point', displayMenu) tg_cp_visibility.setCheckable(True) tg_cp_visibility.setChecked(True) tg_cp_visibility.toggled.connect( lambda: QLiberationMap.set_display_rule( "cp", tg_cp_visibility.isChecked())) tg_go_visibility = QAction('&Ground Objects', displayMenu) tg_go_visibility.setCheckable(True) tg_go_visibility.setChecked(True) tg_go_visibility.toggled.connect( lambda: QLiberationMap.set_display_rule( "go", tg_go_visibility.isChecked())) tg_line_visibility = QAction('&Lines', displayMenu) tg_line_visibility.setCheckable(True) tg_line_visibility.setChecked(True) tg_line_visibility.toggled.connect( lambda: QLiberationMap.set_display_rule( "lines", tg_line_visibility.isChecked())) tg_event_visibility = QAction('&Events', displayMenu) tg_event_visibility.setCheckable(True) tg_event_visibility.setChecked(True) tg_event_visibility.toggled.connect( lambda: QLiberationMap.set_display_rule( "events", tg_event_visibility.isChecked())) tg_sam_visibility = QAction('&SAM Range', displayMenu) tg_sam_visibility.setCheckable(True) tg_sam_visibility.setChecked(True) tg_sam_visibility.toggled.connect( lambda: QLiberationMap.set_display_rule( "sam", tg_sam_visibility.isChecked())) tg_flight_path_visibility = QAction('&Flight Paths', displayMenu) tg_flight_path_visibility.setCheckable(True) tg_flight_path_visibility.setChecked(False) tg_flight_path_visibility.toggled.connect( lambda: QLiberationMap.set_display_rule( "flight_paths", tg_flight_path_visibility.isChecked())) displayMenu.addAction(tg_go_visibility) displayMenu.addAction(tg_cp_visibility) displayMenu.addAction(tg_line_visibility) displayMenu.addAction(tg_event_visibility) displayMenu.addAction(tg_sam_visibility) displayMenu.addAction(tg_flight_path_visibility) help_menu = self.menu.addMenu("&Help") help_menu.addAction( "&Discord Server", lambda: webbrowser.open_new_tab( "https://" + "discord.gg" + "/" + "bKrt" + "rkJ")) help_menu.addAction( "&Github Repository", lambda: webbrowser.open_new_tab( "https://github.com/khopa/dcs_liberation")) help_menu.addAction( "&Releases", lambda: webbrowser.open_new_tab( "https://github.com/Khopa/dcs_liberation/releases")) help_menu.addAction("&Online Manual", lambda: webbrowser.open_new_tab(URLS["Manual"])) help_menu.addAction( "&ED Forum Thread", lambda: webbrowser.open_new_tab(URLS["ForumThread"])) help_menu.addAction("Report an &issue", lambda: webbrowser.open_new_tab(URLS["Issues"])) help_menu.addSeparator() help_menu.addAction(self.showAboutDialogAction) def newGame(self): wizard = NewGameWizard(self) wizard.show() wizard.accepted.connect( lambda: self.onGameGenerated(wizard.generatedGame)) def openFile(self): file = QFileDialog.getOpenFileName( self, "Select game file to open", dir=persistency._dcs_saved_game_folder, filter="*.liberation") if file is not None: game = persistency.load_game(file[0]) self.setGame(game) GameUpdateSignal.get_instance().updateGame(self.game) def saveGame(self): logging.info("Saving game") if self.game.savepath: persistency.save_game(self.game) GameUpdateSignal.get_instance().updateGame(self.game) else: self.saveGameAs() def saveGameAs(self): file = QFileDialog.getSaveFileName( self, "Save As", dir=persistency._dcs_saved_game_folder, filter="*.liberation") if file is not None: self.game.savepath = file[0] persistency.save_game(self.game) def onGameGenerated(self, game: Game): logging.info("On Game generated") self.game = game GameUpdateSignal.get_instance().updateGame(self.game) def closeGame(self): self.game = None GameUpdateSignal.get_instance().updateGame(self.game) def exit(self): sys.exit(0) def setGame(self, game: Game): self.game = game if self.info_panel: self.info_panel.setGame(game) def showAboutDialog(self): text = "<h3>DCS Liberation " + CONST.VERSION_STRING + "</h3>" + \ "<b>Source code :</b> https://github.com/khopa/dcs_liberation" + \ "<h4>Authors</h4>" + \ "<p>DCS Liberation was originally developed by <b>shdwp</b>, DCS Liberation 2.0 is a partial rewrite based on this work by <b>Khopa</b>." \ "<h4>Contributors</h4>" + \ "shdwp, Khopa, Wrycu, calvinmorrow, JohanAberg, Deus, root0fall, Captain Cody" + \ "<h4>Special Thanks :</h4>" \ "<b>rp-</b> <i>for the pydcs framework</i><br/>"\ "<b>Grimes (mrSkortch)</b> & <b>Speed</b> <i>for the MIST framework</i><br/>"\ "<b>Ciribob </b> <i>for the JTACAutoLase.lua script</i><br/>" about = QMessageBox() about.setWindowTitle("About DCS Liberation") about.setIcon(QMessageBox.Icon.Information) about.setText(text) logging.info(about.textFormat()) about.exec_() def showLiberationDialog(self): self.subwindow = QLiberationPreferencesWindow() self.subwindow.show() def onDebriefing(self, debrief: DebriefingSignal): logging.info("On Debriefing") self.debriefing = QDebriefingWindow(debrief.debriefing, debrief.gameEvent, debrief.game) self.debriefing.show()
def toolbar(self): toolbar = QToolBar("My main toolbar") toolbar.setIconSize(QSize(16, 16)) self.addToolBar(toolbar) mouse = QAction(QIcon('./icons/icons-01.png'), 'mouse', self, checkable=True) mouse.setStatusTip('mouse') mouse.triggered.connect(lambda: self.set_pointer('mouse')) square = QAction(QIcon('./icons/icons_Square.png'), 'square', self, checkable=True) square.setStatusTip('square') square.triggered.connect(lambda: self.set_pointer('square')) circle = QAction(QIcon('./icons_Circle.png'), 'circle', self, checkable=True) circle.setStatusTip('circle') circle.triggered.connect(lambda: self.set_pointer('circle')) crosshair = QAction(QIcon('./icons/icons_Crosshair.png'), 'crosshair', self, checkable=True) crosshair.setStatusTip('crosshair') crosshair.triggered.connect(lambda: self.set_pointer('cross')) brush = QAction(QIcon('./icons/icons_Brush.png'), 'brush', self, checkable=True) brush.setStatusTip('crosshair') brush.triggered.connect(lambda: self.set_pointer('brush')) group = QActionGroup(self, exclusive=True) for action in (mouse, square, circle, crosshair, brush): toolbar.addAction(action) group.addAction(action) annotations = QAction(QIcon('./icons/icons_Circle.png'), 'Annot', self, checkable=True) annotations.setStatusTip('Toggle annotations') annotations.triggered.connect(self.toggel_annot) toolbar.addAction(annotations) clear = QAction(QIcon('./icons/icons_Square.png'), 'Clear', self) clear.setStatusTip('Clear annotations') clear.triggered.connect(self.clear_annot) toolbar.addAction(clear) self.setStatusBar(QStatusBar(self))
def toolbar_menus(self): """ Affiche la barre d'icônes permettant de diriger les robots et de les sélectionner""" # Toolbar toolbar = QToolBar("Game toolbar") self.addToolBar(toolbar) # Flèche gauche button_West = QAction(QIcon(ICON_PATH + "arrow-180.png"), "West", self) button_West.setStatusTip("Aller à gauche") button_West.triggered.connect(self.onButtonWestClick) button_West.setCheckable(False) button_West.setShortcut(QKeySequence("Left")) toolbar.addAction(button_West) # Flèche droite button_East = QAction(QIcon(ICON_PATH + "arrow.png"), "Est", self) button_East.setStatusTip("Aller à droite") button_East.triggered.connect(self.onButtonEastClick) button_East.setCheckable(False) button_East.setShortcut(QKeySequence("Right")) toolbar.addAction(button_East) # Flèche Haut button_North = QAction(QIcon(ICON_PATH + "arrow-090.png"), "North", self) button_North.setStatusTip("Aller vers le haut") button_North.triggered.connect(self.onButtonNorthClick) button_North.setCheckable(False) button_North.setShortcut(QKeySequence("Up")) toolbar.addAction(button_North) # Flèche Bas button_South = QAction(QIcon(ICON_PATH + "arrow-270.png"), "South", self) button_South.setStatusTip("Aller vers le Bas") button_South.triggered.connect(self.onButtonSouthClick) button_South.setCheckable(False) button_South.setShortcut(QKeySequence("Down")) toolbar.addAction(button_South) # Selection robot actif button_Red = QPushButton("&Red") button_Red.setIcon(QIcon(ICON_PATH + "icon_R.png")) button_Red.setAutoExclusive(True) button_Red.setCheckable(True) button_Red.setShortcut(QKeySequence("R")) button_Red.toggled.connect(self.onButtonRedClick) button_Green = QPushButton("&Green") button_Green.setIcon(QIcon(ICON_PATH + "icon_G.png")) button_Green.setAutoExclusive(True) button_Green.setCheckable(True) button_Green.setShortcut(QKeySequence("G")) button_Green.toggled.connect(self.onButtonGreenClick) button_Blue = QPushButton("&Blue") button_Blue.setIcon(QIcon(ICON_PATH + "icon_B.png")) button_Blue.setAutoExclusive(True) button_Blue.setCheckable(True) button_Blue.setShortcut(QKeySequence("B")) button_Blue.toggled.connect(self.onButtonBlueClick) button_Yellow = QPushButton("&Yellow") button_Yellow.setIcon(QIcon(ICON_PATH + "icon_Y.png")) button_Yellow.setAutoExclusive(True) button_Yellow.setCheckable(True) button_Yellow.setShortcut(QKeySequence("Y")) button_Yellow.toggled.connect(self.onButtonYellowClick) # Boutton d'annulation (revient en arrière d'un coup) button_undo = QPushButton("&Undo") button_undo.setIcon(QIcon(ICON_PATH + "undo.jpg")) button_undo.setAutoExclusive(False) button_undo.setCheckable(False) button_undo.setShortcut(QKeySequence("U")) button_undo.clicked.connect(self.onButtonUndoClick) # Boutton d'indice : lance le solveur pour donner l'indice du prochain coup à effectuer button_tip = QPushButton("&Tip") button_tip.setIcon(QIcon(ICON_PATH + "icon_tip.png")) button_tip.setAutoExclusive(False) button_tip.setCheckable(False) button_tip.setShortcut(QKeySequence("T")) button_tip.clicked.connect(self.onButtonTipClick) # Boutton Solution : lance le solveur pour afficher une liste d'actions à effectuer pour résoudre le jeu button_solution = QPushButton("&Solution") button_solution.setIcon(QIcon(ICON_PATH + "icon_solution.png")) button_solution.setAutoExclusive(False) button_solution.setCheckable(False) button_solution.setShortcut(QKeySequence("S")) button_solution.clicked.connect(self.onButtonSolutionClick) toolbar.addWidget(button_Red) toolbar.addWidget(button_Green) toolbar.addWidget(button_Blue) toolbar.addWidget(button_Yellow) toolbar.addWidget(button_undo) toolbar.addWidget(button_tip) toolbar.addWidget(button_solution)
def testNewCtor(self): o = QWidget() self._called = False myAction = QAction("&Quit", o, triggered=self._cb) myAction.trigger() self.assert_(self._called)
def __init__(self, qt_application=None): self.qt_application = qt_application # 实例化配置管理类 self.settings_manage = SettingsManage() self.__setting = self.settings_manage.get() # 实例化windows命令处理类 self.wsl2 = WinCmd() # 初始化启动脚本 if not isfile(self.wsl2.WSL_VBS_PATH): copyfile(self.wsl2.WSL_VBS_PATH_TEMP, self.wsl2.WSL_VBS_PATH) if not isfile(self.wsl2.WSL_BAT_PATH): self.settings_manage.save_file_content( self.wsl2.WSL_BAT_PATH, self.__setting.get('wsl_bat_content', '')) # 加载UI文件 self.ui = QUiLoader().load(ResourcePath.resource_path('lib/wsl2.ui')) # 设置界面图标 app_icon = QIcon(ResourcePath.resource_path("lib/logo.ico")) self.ui.setWindowIcon(app_icon) # 设置选中状态 self.ui.auto_start_wsl.setChecked( self.__setting.get('auto_start_wsl', False)) self.ui.fire_wall_open.setChecked( self.__setting.get('fire_wall_open', False)) self.ui.fire_wall_close.setChecked( self.__setting.get('fire_wall_close', False)) # 设置文本框的值 self.ui.port_text.appendPlainText('\n'.join( self.__setting.get('ports', []))) self.ui.bat_text.appendPlainText(self.wsl2.get_bat_script()) # 按钮监听 self.ui.get_wsl2_ip.clicked.connect(self.__get_wsl2_ip) self.ui.port_add.clicked.connect(self.__port_add) self.ui.port_del.clicked.connect(self.__port_del) self.ui.port_info.clicked.connect(self.__port_info) self.ui.wsl_l_v.clicked.connect(self.__wsl_l_v) self.ui.port_reset.clicked.connect(self.__port_reset) self.ui.end_wsl.clicked.connect(self.__end_wsl) self.ui.start_wsl.clicked.connect(self.__start_wsl) self.ui.start_wsl_all.clicked.connect(self.start_wsl_all) self.ui.save_settings.clicked.connect(self.__save_settings) self.ui.save_settings_ports.clicked.connect(self.__save_settings) # 设置系统托盘图标的菜单 tp_icon = QIcon(ResourcePath.resource_path("lib/logo.ico")) self.tp = QSystemTrayIcon(self.ui) self.tp.setIcon(tp_icon) self.ui_hide = QAction(icon=tp_icon, text='隐藏(Hide)', triggered=self.ui.hide) self.ui_show = QAction(icon=tp_icon, text='显示(Show)', triggered=self.ui.show) self.ui_exit = QAction(icon=tp_icon, text='退出(Exit)', triggered=self.__quit_app) self.tp_menu = QMenu() self.tp_menu.addAction(self.ui_hide) self.tp_menu.addAction(self.ui_show) self.tp_menu.addAction(self.ui_exit) self.tp.setContextMenu(self.tp_menu) self.tp.activated.connect(self.__tp_connect_action) self.tp.show() self.tp.showMessage('WSL2AutoPortForward', 'WSL2端口自动转发工具已启动', QSystemTrayIcon.MessageIcon.Information)
def setUpMenus(self) -> None: menuBar = QMenuBar() fileMenu = menuBar.addMenu('File') importMenu = fileMenu.addMenu('Import') exportMenu = fileMenu.addMenu('Export') flowMenu = menuBar.addMenu('Flow') viewMenu = menuBar.addMenu('View') helpMenu = menuBar.addMenu('Help') aAppendEmpty = QAction('Add frame', fileMenu) aAppendEmpty.setStatusTip('Create an empty dataframe in the workbench') aQuit = QAction('Quit', fileMenu) aLoadCsv = OperationAction(CsvLoader, fileMenu, 'From csv', self.rect().center(), self.centralWidget().workbenchModel) self.aWriteCsv = OperationAction(CsvWriter, fileMenu, 'To csv', self.rect().center(), w=self.centralWidget().workbenchModel) aLoadPickle = OperationAction(PickleLoader, fileMenu, 'From pickle', self.rect().center(), self.centralWidget().workbenchModel) self.aWritePickle = OperationAction( PickleWriter, fileMenu, 'To pickle', self.mapToGlobal(self.rect().center()), w=self.centralWidget().workbenchModel) aCompareFrames = QAction('Compare dataframes', viewMenu) aLogDir = QAction('Open log directory', helpMenu) aClearLogs = QAction('Delete old logs', helpMenu) fileMenu.addActions([aAppendEmpty, aQuit]) exportMenu.addActions([self.aWriteCsv, self.aWritePickle]) importMenu.addActions([aLoadCsv, aLoadPickle]) self._aStartFlow = QAction('Execute', flowMenu) self._aResetFlow = QAction('Reset', flowMenu) aSaveFlow = QAction('Save', flowMenu) aLoadFlow = QAction('Load', flowMenu) flowMenu.addActions( [self._aStartFlow, self._aResetFlow, aSaveFlow, aLoadFlow]) viewMenu.addAction(aCompareFrames) helpMenu.addActions([aLogDir, aClearLogs]) self.setMenuBar(menuBar) # Tips aLoadCsv.setStatusTip('Load a csv file in the workbench') aLoadPickle.setStatusTip('Load a Pickle file in the workbench') self.aWriteCsv.setStatusTip('Write a dataframe to a csv file') self.aWritePickle.setStatusTip( 'Serializes a dataframe into a pickle file') aCompareFrames.setStatusTip('Open two dataframes side by side') self._aStartFlow.setStatusTip('Start flow-graph execution') self._aResetFlow.setStatusTip('Reset the node status in flow-graph') aLogDir.setStatusTip('Open the folder containing all logs') aClearLogs.setStatusTip('Delete older logs and keep the last 5') # Connect aAppendEmpty.triggered.connect( self.centralWidget().workbenchModel.appendEmptyRow) aQuit.triggered.connect(self.close) self._aStartFlow.triggered.connect( self.centralWidget().controller.executeFlow) self._aResetFlow.triggered.connect( self.centralWidget().controller.resetFlowStatus) aCompareFrames.triggered.connect(self.openComparePanel) aLogDir.triggered.connect(self.openLogDirectory) aClearLogs.triggered.connect(self.clearLogDir) aSaveFlow.triggered.connect(self.saveFlow) aLoadFlow.triggered.connect(self.readFlow) aLoadCsv.stateChanged.connect(self.operationStateChanged) aLoadPickle.stateChanged.connect(self.operationStateChanged) self.aWriteCsv.stateChanged.connect(self.operationStateChanged) self.aWritePickle.stateChanged.connect(self.operationStateChanged)
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.fromTheme("document-new", QIcon(':/images/new.png')), "&New", self, shortcut=QKeySequence.New, statusTip="Create a new file", triggered=self.newFile) self.openAct = QAction(QIcon.fromTheme("document-open", QIcon(':/images/open.png')), "&Open...", self, shortcut=QKeySequence.Open, statusTip="Open an existing file", triggered=self.open) self.saveAct = QAction(QIcon.fromTheme("document-save", 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.fromTheme("edit-cut", 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.fromTheme("edit-copy", 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.fromTheme("edit-paste", 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 do_add_action(self, name): action = QAction(name, self.menu) self.menu.insertAction(self.sep, action) action.setCheckable(True) self.actGroup.addAction(action) return action
def createActions(self): self.newAct = QAction(QIcon.fromTheme("document-new", QIcon(':/images/new.png')), "&New", self, shortcut=QKeySequence.New, statusTip="Create a new file", triggered=self.newFile) self.openAct = QAction(QIcon.fromTheme("document-open", QIcon(':/images/open.png')), "&Open...", self, shortcut=QKeySequence.Open, statusTip="Open an existing file", triggered=self.open) self.saveAct = QAction(QIcon.fromTheme("document-save", 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.fromTheme("edit-cut", 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.fromTheme("edit-copy", 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.fromTheme("edit-paste", 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)
class MainWindow(QMainWindow): def __init__(self, url): super(MainWindow, self).__init__() self.progress = 0 fd = QFile(":/jquery.min.js") if fd.open(QIODevice.ReadOnly | QFile.Text): self.jQuery = QTextStream(fd).readAll() fd.close() else: self.jQuery = '' QNetworkProxyFactory.setUseSystemConfiguration(True) self.view = QWebView(self) self.view.load(url) self.view.loadFinished.connect(self.adjustLocation) self.view.titleChanged.connect(self.adjustTitle) self.view.loadProgress.connect(self.setProgress) self.view.loadFinished.connect(self.finishLoading) self.locationEdit = QLineEdit(self) self.locationEdit.setSizePolicy(QSizePolicy.Expanding, self.locationEdit.sizePolicy().verticalPolicy()) self.locationEdit.returnPressed.connect(self.changeLocation) toolBar = self.addToolBar("Navigation") toolBar.addAction(self.view.pageAction(QWebPage.Back)) toolBar.addAction(self.view.pageAction(QWebPage.Forward)) toolBar.addAction(self.view.pageAction(QWebPage.Reload)) toolBar.addAction(self.view.pageAction(QWebPage.Stop)) toolBar.addWidget(self.locationEdit) viewMenu = self.menuBar().addMenu("&View") viewSourceAction = QAction("Page Source", self) viewSourceAction.triggered.connect(self.viewSource) viewMenu.addAction(viewSourceAction) effectMenu = self.menuBar().addMenu("&Effect") effectMenu.addAction("Highlight all links", self.highlightAllLinks) self.rotateAction = QAction( self.style().standardIcon(QStyle.SP_FileDialogDetailedView), "Turn images upside down", self, checkable=True) self.rotateAction.toggled.connect(self.rotateImages) effectMenu.addAction(self.rotateAction) toolsMenu = self.menuBar().addMenu("&Tools") toolsMenu.addAction("Remove GIF images", self.removeGifImages) toolsMenu.addAction("Remove all inline frames", self.removeInlineFrames) toolsMenu.addAction("Remove all object elements", self.removeObjectElements) toolsMenu.addAction("Remove all embedded elements", self.removeEmbeddedElements) self.setCentralWidget(self.view) def viewSource(self): accessManager = self.view.page().networkAccessManager() request = QNetworkRequest(self.view.url()) reply = accessManager.get(request) reply.finished.connect(self.slotSourceDownloaded) def slotSourceDownloaded(self): reply = self.sender() self.textEdit = QTextEdit() self.textEdit.setAttribute(Qt.WA_DeleteOnClose) self.textEdit.show() self.textEdit.setPlainText(QTextStream(reply).readAll()) self.textEdit.resize(600, 400) reply.deleteLater() def adjustLocation(self): self.locationEdit.setText(self.view.url().toString()) def changeLocation(self): url = QUrl.fromUserInput(self.locationEdit.text()) self.view.load(url) self.view.setFocus() def adjustTitle(self): if 0 < self.progress < 100: self.setWindowTitle("%s (%s%%)" % (self.view.title(), self.progress)) else: self.setWindowTitle(self.view.title()) def setProgress(self, p): self.progress = p self.adjustTitle() def finishLoading(self): self.progress = 100 self.adjustTitle() self.view.page().mainFrame().evaluateJavaScript(self.jQuery) self.rotateImages(self.rotateAction.isChecked()) def highlightAllLinks(self): code = """$('a').each( function () { $(this).css('background-color', 'yellow') } )""" self.view.page().mainFrame().evaluateJavaScript(code) def rotateImages(self, invert): if invert: code = """ $('img').each( function () { $(this).css('-webkit-transition', '-webkit-transform 2s'); $(this).css('-webkit-transform', 'rotate(180deg)') } )""" else: code = """ $('img').each( function () { $(this).css('-webkit-transition', '-webkit-transform 2s'); $(this).css('-webkit-transform', 'rotate(0deg)') } )""" self.view.page().mainFrame().evaluateJavaScript(code) def removeGifImages(self): code = "$('[src*=gif]').remove()" self.view.page().mainFrame().evaluateJavaScript(code) def removeInlineFrames(self): code = "$('iframe').remove()" self.view.page().mainFrame().evaluateJavaScript(code) def removeObjectElements(self): code = "$('object').remove()" self.view.page().mainFrame().evaluateJavaScript(code) def removeEmbeddedElements(self): code = "$('embed').remove()" self.view.page().mainFrame().evaluateJavaScript(code)
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.cameraInfo = QCameraInfo.defaultCamera() self.camera = QCamera(self.cameraInfo) self.camera.setCaptureMode(QCamera.CaptureStillImage) self.imageCapture = QCameraImageCapture(self.camera) self.imageCapture.imageCaptured.connect(self.imageCaptured) self.imageCapture.imageSaved.connect(self.imageSaved) self.currentPreview = QImage() toolBar = QToolBar() self.addToolBar(toolBar) fileMenu = self.menuBar().addMenu("&File") shutterIcon = QIcon(os.path.join(os.path.dirname(__file__), "shutter.svg")) self.takePictureAction = QAction(shutterIcon, "&Take Picture", self, shortcut="Ctrl+T", triggered=self.takePicture) self.takePictureAction.setToolTip("Take Picture") fileMenu.addAction(self.takePictureAction) toolBar.addAction(self.takePictureAction) exitAction = QAction(QIcon.fromTheme("application-exit"), "E&xit", self, shortcut="Ctrl+Q", triggered=self.close) fileMenu.addAction(exitAction) aboutMenu = self.menuBar().addMenu("&About") aboutQtAction = QAction("About &Qt", self, triggered=qApp.aboutQt) aboutMenu.addAction(aboutQtAction) self.tabWidget = QTabWidget() self.setCentralWidget(self.tabWidget) self.cameraViewfinder = QCameraViewfinder() self.camera.setViewfinder(self.cameraViewfinder) self.tabWidget.addTab(self.cameraViewfinder, "Viewfinder") if self.camera.status() != QCamera.UnavailableStatus: name = self.cameraInfo.description() self.setWindowTitle("PySide2 Camera Example (" + name + ")") self.statusBar().showMessage("Starting: '" + name + "'", 5000) self.camera.start() else: self.setWindowTitle("PySide2 Camera Example") self.takePictureAction.setEnabled(False) self.statusBar().showMessage("Camera unavailable", 5000) def nextImageFileName(self): picturesLocation = QStandardPaths.writableLocation(QStandardPaths.PicturesLocation) dateString = QDate.currentDate().toString("yyyyMMdd") pattern = picturesLocation + "/pyside2_camera_" + dateString + "_{:03d}.jpg" n = 1 while True: result = pattern.format(n) if not os.path.exists(result): return result n = n + 1 return None def takePicture(self): self.currentPreview = QImage() self.camera.searchAndLock() self.imageCapture.capture(self.nextImageFileName()) self.camera.unlock() def imageCaptured(self, id, previewImage): self.currentPreview = previewImage def imageSaved(self, id, fileName): index = self.tabWidget.count() imageView = ImageView(self.currentPreview, fileName) self.tabWidget.addTab(imageView, "Capture #{}".format(index)) self.tabWidget.setCurrentIndex(index)
def initMenuBar(self): self.menu = self.menuBar() file_menu = self.menu.addMenu("&File") file_menu.addAction(self.newGameAction) file_menu.addAction(self.openAction) file_menu.addSeparator() file_menu.addAction(self.saveGameAction) file_menu.addAction(self.saveAsAction) file_menu.addSeparator() file_menu.addAction(self.showLiberationPrefDialogAction) file_menu.addSeparator() #file_menu.addAction("Close Current Game", lambda: self.closeGame()) # Not working file_menu.addAction("E&xit", lambda: self.exit()) displayMenu = self.menu.addMenu("&Display") tg_cp_visibility = QAction('&Control Point', displayMenu) tg_cp_visibility.setCheckable(True) tg_cp_visibility.setChecked(True) tg_cp_visibility.toggled.connect( lambda: QLiberationMap.set_display_rule( "cp", tg_cp_visibility.isChecked())) tg_go_visibility = QAction('&Ground Objects', displayMenu) tg_go_visibility.setCheckable(True) tg_go_visibility.setChecked(True) tg_go_visibility.toggled.connect( lambda: QLiberationMap.set_display_rule( "go", tg_go_visibility.isChecked())) tg_line_visibility = QAction('&Lines', displayMenu) tg_line_visibility.setCheckable(True) tg_line_visibility.setChecked(True) tg_line_visibility.toggled.connect( lambda: QLiberationMap.set_display_rule( "lines", tg_line_visibility.isChecked())) tg_event_visibility = QAction('&Events', displayMenu) tg_event_visibility.setCheckable(True) tg_event_visibility.setChecked(True) tg_event_visibility.toggled.connect( lambda: QLiberationMap.set_display_rule( "events", tg_event_visibility.isChecked())) tg_sam_visibility = QAction('&SAM Range', displayMenu) tg_sam_visibility.setCheckable(True) tg_sam_visibility.setChecked(True) tg_sam_visibility.toggled.connect( lambda: QLiberationMap.set_display_rule( "sam", tg_sam_visibility.isChecked())) tg_flight_path_visibility = QAction('&Flight Paths', displayMenu) tg_flight_path_visibility.setCheckable(True) tg_flight_path_visibility.setChecked(False) tg_flight_path_visibility.toggled.connect( lambda: QLiberationMap.set_display_rule( "flight_paths", tg_flight_path_visibility.isChecked())) displayMenu.addAction(tg_go_visibility) displayMenu.addAction(tg_cp_visibility) displayMenu.addAction(tg_line_visibility) displayMenu.addAction(tg_event_visibility) displayMenu.addAction(tg_sam_visibility) displayMenu.addAction(tg_flight_path_visibility) help_menu = self.menu.addMenu("&Help") help_menu.addAction( "&Discord Server", lambda: webbrowser.open_new_tab( "https://" + "discord.gg" + "/" + "bKrt" + "rkJ")) help_menu.addAction( "&Github Repository", lambda: webbrowser.open_new_tab( "https://github.com/khopa/dcs_liberation")) help_menu.addAction( "&Releases", lambda: webbrowser.open_new_tab( "https://github.com/Khopa/dcs_liberation/releases")) help_menu.addAction("&Online Manual", lambda: webbrowser.open_new_tab(URLS["Manual"])) help_menu.addAction( "&ED Forum Thread", lambda: webbrowser.open_new_tab(URLS["ForumThread"])) help_menu.addAction("Report an &issue", lambda: webbrowser.open_new_tab(URLS["Issues"])) help_menu.addSeparator() help_menu.addAction(self.showAboutDialogAction)
class OpenProjectDialog(QDialog): """A dialog that let's user select a project to open either by choosing an old .proj file or by choosing a project directory.""" def __init__(self, toolbox): """ Args: toolbox (ToolboxUI): QMainWindow instance """ from ..ui import open_project_dialog # pylint: disable=import-outside-toplevel super().__init__( parent=toolbox, f=Qt.Dialog) # Setting the parent inherits the stylesheet self._toolbox = toolbox # Set up the user interface from Designer file self.ui = open_project_dialog.Ui_Dialog() self.ui.setupUi(self) self.combobox_context_menu = None # Ensure this dialog is garbage-collected when closed self.setAttribute(Qt.WA_DeleteOnClose) # QActions for keyboard shortcuts self.go_root_action = QAction(self) self.go_home_action = QAction(self) self.go_documents_action = QAction(self) self.go_desktop_action = QAction(self) self.set_keyboard_shortcuts() self.selected_path = "" self.cb_ss = self.ui.comboBox_current_path.styleSheet() self.file_model = CustomQFileSystemModel() self.file_model.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot) self.icon_provider = ProjectDirectoryIconProvider() self.file_model.setIconProvider(self.icon_provider) self.file_model.setRootPath(QDir.rootPath()) self.ui.treeView_file_system.setModel(self.file_model) self.file_model.sort(0, Qt.AscendingOrder) # Enable validator (experimental, not very useful here) # Validator prevents typing Invalid strings to combobox. (not in use) # When text in combobox is Intermediate, the validator prevents emitting # currentIndexChanged signal when enter is pressed. # Pressing enter still triggers the done() slot of the QDialog. self.validator = DirValidator() self.ui.comboBox_current_path.setValidator(self.validator) self.ui.comboBox_current_path.setInsertPolicy(QComboBox.NoInsert) # Override QCombobox keyPressEvent to catch the Enter key press self.ui.comboBox_current_path.keyPressEvent = self.combobox_key_press_event # Read recent project directories and populate combobox recents = self._toolbox.qsettings().value( "appSettings/recentProjectStorages", defaultValue=None) if recents: recents_lst = str(recents).split("\n") self.ui.comboBox_current_path.insertItems(0, recents_lst) # Set start index to most recent project storage or to root if it does not exist p = self.ui.comboBox_current_path.itemText(0) if os.path.isdir(p): start_index = self.file_model.index(p) else: start_index = self.file_model.index(QDir.rootPath()) else: start_index = self.file_model.index(QDir.rootPath()) self.ui.comboBox_current_path.setCurrentIndex(-1) self.file_model.directoryLoaded.connect(self.expand_and_resize) # Start browsing to start index immediately when dialog is shown self.ui.treeView_file_system.setCurrentIndex(start_index) self.connect_signals() def set_keyboard_shortcuts(self): """Creates keyboard shortcuts for the 'Root', 'Home', etc. buttons.""" self.go_root_action.setShortcut(QKeySequence(Qt.Key_F1)) self.addAction(self.go_root_action) self.go_home_action.setShortcut(QKeySequence(Qt.Key_F2)) self.addAction(self.go_home_action) self.go_documents_action.setShortcut(QKeySequence(Qt.Key_F3)) self.addAction(self.go_documents_action) self.go_desktop_action.setShortcut(QKeySequence(Qt.Key_F4)) self.addAction(self.go_desktop_action) def connect_signals(self): """Connects signals to slots.""" self.ui.toolButton_root.clicked.connect(self.go_root) self.ui.toolButton_home.clicked.connect(self.go_home) self.ui.toolButton_documents.clicked.connect(self.go_documents) self.ui.toolButton_desktop.clicked.connect(self.go_desktop) self.ui.comboBox_current_path.editTextChanged.connect( self.combobox_text_edited) self.ui.comboBox_current_path.currentIndexChanged.connect( self.current_index_changed) self.ui.comboBox_current_path.customContextMenuRequested.connect( self.show_context_menu) self.validator.changed.connect(self.validator_state_changed) self.ui.treeView_file_system.clicked.connect(self.set_selected_path) self.ui.treeView_file_system.selectionModel().currentChanged.connect( self.current_changed) self.go_root_action.triggered.connect(self.go_root) self.go_home_action.triggered.connect(self.go_home) self.go_documents_action.triggered.connect(self.go_documents) self.go_desktop_action.triggered.connect(self.go_desktop) @Slot(str) def expand_and_resize(self, p): """Expands, resizes, and scrolls the tree view to the current directory when the file model has finished loading the path. Slot for the file model's directoryLoaded signal. The directoryLoaded signal is emitted only if the directory has not been cached already. Args: p (str): Directory that has been loaded """ current_index = self.ui.treeView_file_system.currentIndex() self.ui.treeView_file_system.expand(current_index) self.ui.treeView_file_system.scrollTo( current_index, hint=QAbstractItemView.PositionAtTop) self.ui.treeView_file_system.resizeColumnToContents(0) self.set_selected_path(current_index) def combobox_key_press_event(self, e): """Interrupts Enter and Return key presses when QComboBox is in focus. This is needed to prevent showing the 'Not a valid Spine Toolbox project' Notifier every time Enter is pressed. Args: e (QKeyEvent): Received key press event. """ if e.key() == Qt.Key_Enter or e.key() == Qt.Key_Return: state = self.ui.comboBox_current_path.validator().state fm_current_index = self.ui.treeView_file_system.currentIndex() if state == QValidator.Intermediate: # Remove path from qsettings self.remove_directory_from_recents( os.path.abspath(self.selection()), self._toolbox.qsettings()) # Remove path from combobox as well cb_index = self.ui.comboBox_current_path.findText( os.path.abspath(self.selection())) if cb_index == -1: pass # logging.error("{0} not found in combobox") else: self.ui.comboBox_current_path.removeItem(cb_index) notification = Notification(self, "Path does not exist") notification.show() elif state == QValidator.Acceptable: p = self.ui.comboBox_current_path.currentText() fm_index = self.file_model.index(p) if not fm_current_index == fm_index: self.ui.treeView_file_system.collapseAll() self.ui.treeView_file_system.setCurrentIndex(fm_index) self.ui.treeView_file_system.expand(fm_index) self.ui.treeView_file_system.scrollTo( fm_index, hint=QAbstractItemView.PositionAtTop) else: project_json_fp = os.path.abspath( os.path.join(self.selection(), ".spinetoolbox", "project.json")) if os.path.isfile(project_json_fp): self.done(QDialog.Accepted) else: # INVALID (or None). Happens if Enter key is pressed and the combobox text has not been edited yet. pass e.accept() else: QComboBox.keyPressEvent(self.ui.comboBox_current_path, e) @Slot() def validator_state_changed(self): """Changes the combobox border color according to the current state of the validator.""" state = self.ui.comboBox_current_path.validator().state if state == QValidator.Acceptable: self.ui.comboBox_current_path.setStyleSheet(self.cb_ss) elif state == QValidator.Intermediate: ss = "QComboBox {border: 1px solid #ff704d}" self.ui.comboBox_current_path.setStyleSheet(ss) else: # Invalid. This is never returned (on purpose). ss = "QComboBox {border: 1px solid #ff3300}" self.ui.comboBox_current_path.setStyleSheet(ss) @Slot(int) def current_index_changed(self, i): """Combobox selection changed. This slot is processed when a new item is selected from the drop-down list. This is not processed when new item txt is QValidotor.Intermediate. Args: i (int): Selected row in combobox """ p = self.ui.comboBox_current_path.itemText(i) if not os.path.isdir(p): self.remove_directory_from_recents(p, self._toolbox.qsettings()) return fm_index = self.file_model.index(p) self.ui.treeView_file_system.collapseAll() self.ui.treeView_file_system.setCurrentIndex(fm_index) self.ui.treeView_file_system.expand(fm_index) self.ui.treeView_file_system.scrollTo( fm_index, hint=QAbstractItemView.PositionAtTop) @Slot("QModelIndex", "QModelIndex", name="current_changed") def current_changed(self, current, previous): """Processed when the current item in file system tree view has been changed with keyboard or mouse. Updates the text in combobox. Args: current (QModelIndex): Currently selected index previous (QModelIndex): Previously selected index """ self.set_selected_path(current) @Slot("QModelIndex", name="set_selected_path") def set_selected_path(self, index): """Sets the text in the combobox as the selected path in the file system tree view. Args: index (QModelIndex): The index which was mouse clicked. """ if not index.isValid(): return selected_path = os.path.abspath(self.file_model.filePath(index)) self.ui.comboBox_current_path.setCurrentText( selected_path) # Emits editTextChanged signal self.selected_path = selected_path @Slot(str) def combobox_text_edited(self, text): """Updates selected path when combobox text is edited. Note: pressing enter in combobox does not trigger this. """ self.selected_path = text def selection(self): """Returns the selected path from dialog.""" return os.path.abspath(self.selected_path) @Slot(bool, name="go_root") def go_root(self, checked=False): """Slot for the 'Root' button. Scrolls the treeview to show and select the user's root directory. Note: We need to expand and scroll the tree view here after setCurrentIndex just in case the directory has been loaded already. """ self.ui.comboBox_current_path.setCurrentIndex(-1) root_index = self.file_model.index(QDir.rootPath()) self.ui.treeView_file_system.collapseAll() self.ui.treeView_file_system.setCurrentIndex(root_index) self.ui.treeView_file_system.expand(root_index) self.ui.treeView_file_system.scrollTo( root_index, hint=QAbstractItemView.PositionAtTop) @Slot(bool, name="go_home") def go_home(self, checked=False): """Slot for the 'Home' button. Scrolls the treeview to show and select the user's home directory.""" self.ui.comboBox_current_path.setCurrentIndex(-1) home_index = self.file_model.index(QDir.homePath()) self.ui.treeView_file_system.collapseAll() self.ui.treeView_file_system.setCurrentIndex(home_index) self.ui.treeView_file_system.expand(home_index) self.ui.treeView_file_system.scrollTo( home_index, hint=QAbstractItemView.PositionAtTop) @Slot(bool, name="go_documents") def go_documents(self, checked=False): """Slot for the 'Documents' button. Scrolls the treeview to show and select the user's documents directory.""" docs = QStandardPaths.writableLocation( QStandardPaths.DocumentsLocation) if not docs: return self.ui.comboBox_current_path.setCurrentIndex(-1) docs_index = self.file_model.index(docs) self.ui.treeView_file_system.collapseAll() self.ui.treeView_file_system.setCurrentIndex(docs_index) self.ui.treeView_file_system.expand(docs_index) self.ui.treeView_file_system.scrollTo( docs_index, hint=QAbstractItemView.PositionAtTop) @Slot(bool, name="go_desktop") def go_desktop(self, checked=False): """Slot for the 'Desktop' button. Scrolls the treeview to show and select the user's desktop directory.""" desktop = QStandardPaths.writableLocation( QStandardPaths.DesktopLocation) # Return a list if not desktop: return self.ui.comboBox_current_path.setCurrentIndex(-1) desktop_index = self.file_model.index(desktop) self.ui.treeView_file_system.collapseAll() self.ui.treeView_file_system.setCurrentIndex(desktop_index) self.ui.treeView_file_system.expand(desktop_index) self.ui.treeView_file_system.scrollTo( desktop_index, hint=QAbstractItemView.PositionAtTop) def done(self, r): """Checks that selected path exists and is a valid Spine Toolbox directory when ok button is clicked or when enter is pressed without the combobox being in focus. Args: r (int) Return code """ if r == QDialog.Accepted: if not os.path.isdir(self.selection()): notification = Notification(self, "Path does not exist") notification.show() return project_json_fp = os.path.abspath( os.path.join(self.selection(), ".spinetoolbox", "project.json")) if not os.path.isfile(project_json_fp): notification = Notification( self, "Not a valid Spine Toolbox project") notification.show() return # self.selection() now contains a valid Spine Toolbox project directory. # Add the parent directory of selected directory to qsettings self.update_recents( os.path.abspath(os.path.join(self.selection(), os.path.pardir)), self._toolbox.qsettings()) super().done(r) @staticmethod def update_recents(entry, qsettings): """Adds a new entry to QSettings variable that remembers the five most recent project storages. Args: entry (str): Abs. path to a directory that most likely contains other Spine Toolbox Projects as well. First entry is also used as the initial path for File->New Project dialog. qsettings (QSettings): Toolbox qsettings object """ recents = qsettings.value("appSettings/recentProjectStorages", defaultValue=None) if not recents: updated_recents = entry else: recents = str(recents) recents_list = recents.split("\n") # Add path only if it's not in the list already if entry not in recents_list: recents_list.insert(0, entry) if len(recents_list) > 5: recents_list.pop() else: # If entry was on the list, move it as the first item recents_list.insert( 0, recents_list.pop(recents_list.index(entry))) updated_recents = "\n".join(recents_list) # Save updated recent paths qsettings.setValue("appSettings/recentProjectStorages", updated_recents) qsettings.sync() # Commit change immediately @staticmethod def remove_directory_from_recents(p, qsettings): """Removes directory from the recent project storages. Args: p (str): Full path to a project directory qsettings (QSettings): Toolbox qsettings object """ recents = qsettings.value("appSettings/recentProjectStorages", defaultValue=None) if not recents: return recents = str(recents) recents_list = recents.split("\n") if p in recents_list: recents_list.pop(recents_list.index(p)) updated_recents = "\n".join(recents_list) # Save updated recent paths qsettings.setValue("appSettings/recentProjectStorages", updated_recents) qsettings.sync() # Commit change immediately @Slot("QPoint") def show_context_menu(self, pos): """Shows the context menu for the QCombobox with a 'Clear history' entry. Args: pos (QPoint): Mouse position """ # ind = self.ui.comboBox_current_path.indexAt(pos) global_pos = self.ui.comboBox_current_path.mapToGlobal(pos) # if not ind.isValid(): self.combobox_context_menu = OpenProjectDialogComboBoxContextMenu( self, global_pos) action = self.combobox_context_menu.get_action() if action == "Clear history": self.ui.comboBox_current_path.clear() self._toolbox.qsettings().setValue( "appSettings/recentProjectStorages", "") self.go_root() else: # No option selected pass self.combobox_context_menu.deleteLater() self.combobox_context_menu = None def closeEvent(self, event=None): """Handles dialog closing. Args: event (QCloseEvent): Close event """ if event: event.accept()
def initActions(self): self.newGameAction = QAction("&New Game", self) self.newGameAction.setIcon(QIcon(CONST.ICONS["New"])) self.newGameAction.triggered.connect(self.newGame) self.newGameAction.setShortcut('CTRL+N') self.openAction = QAction("&Open", self) self.openAction.setIcon(QIcon(CONST.ICONS["Open"])) self.openAction.triggered.connect(self.openFile) self.openAction.setShortcut('CTRL+O') self.saveGameAction = QAction("&Save", self) self.saveGameAction.setIcon(QIcon(CONST.ICONS["Save"])) self.saveGameAction.triggered.connect(self.saveGame) self.saveGameAction.setShortcut('CTRL+S') self.saveAsAction = QAction("Save &As", self) self.saveAsAction.setIcon(QIcon(CONST.ICONS["Save"])) self.saveAsAction.triggered.connect(self.saveGameAs) self.saveAsAction.setShortcut('CTRL+A') self.showAboutDialogAction = QAction("&About DCS Liberation", self) self.showAboutDialogAction.setIcon(QIcon.fromTheme("help-about")) self.showAboutDialogAction.triggered.connect(self.showAboutDialog) self.showLiberationPrefDialogAction = QAction("&Preferences", self) self.showLiberationPrefDialogAction.setIcon( QIcon.fromTheme("help-about")) self.showLiberationPrefDialogAction.triggered.connect( self.showLiberationDialog)
def __init__(self, toolbox): """ Args: toolbox (ToolboxUI): QMainWindow instance """ from ..ui import open_project_dialog # pylint: disable=import-outside-toplevel super().__init__( parent=toolbox, f=Qt.Dialog) # Setting the parent inherits the stylesheet self._toolbox = toolbox # Set up the user interface from Designer file self.ui = open_project_dialog.Ui_Dialog() self.ui.setupUi(self) self.combobox_context_menu = None # Ensure this dialog is garbage-collected when closed self.setAttribute(Qt.WA_DeleteOnClose) # QActions for keyboard shortcuts self.go_root_action = QAction(self) self.go_home_action = QAction(self) self.go_documents_action = QAction(self) self.go_desktop_action = QAction(self) self.set_keyboard_shortcuts() self.selected_path = "" self.cb_ss = self.ui.comboBox_current_path.styleSheet() self.file_model = CustomQFileSystemModel() self.file_model.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot) self.icon_provider = ProjectDirectoryIconProvider() self.file_model.setIconProvider(self.icon_provider) self.file_model.setRootPath(QDir.rootPath()) self.ui.treeView_file_system.setModel(self.file_model) self.file_model.sort(0, Qt.AscendingOrder) # Enable validator (experimental, not very useful here) # Validator prevents typing Invalid strings to combobox. (not in use) # When text in combobox is Intermediate, the validator prevents emitting # currentIndexChanged signal when enter is pressed. # Pressing enter still triggers the done() slot of the QDialog. self.validator = DirValidator() self.ui.comboBox_current_path.setValidator(self.validator) self.ui.comboBox_current_path.setInsertPolicy(QComboBox.NoInsert) # Override QCombobox keyPressEvent to catch the Enter key press self.ui.comboBox_current_path.keyPressEvent = self.combobox_key_press_event # Read recent project directories and populate combobox recents = self._toolbox.qsettings().value( "appSettings/recentProjectStorages", defaultValue=None) if recents: recents_lst = str(recents).split("\n") self.ui.comboBox_current_path.insertItems(0, recents_lst) # Set start index to most recent project storage or to root if it does not exist p = self.ui.comboBox_current_path.itemText(0) if os.path.isdir(p): start_index = self.file_model.index(p) else: start_index = self.file_model.index(QDir.rootPath()) else: start_index = self.file_model.index(QDir.rootPath()) self.ui.comboBox_current_path.setCurrentIndex(-1) self.file_model.directoryLoaded.connect(self.expand_and_resize) # Start browsing to start index immediately when dialog is shown self.ui.treeView_file_system.setCurrentIndex(start_index) self.connect_signals()
def open_dcm_action(self): act = QAction(QIcon('exit.png'), 'Open dicom', self) act.setStatusTip('Open dicom images') act.triggered.connect(self.open_dcm) return act
def create_management_actions(self): manageAct = QAction(QIcon("images/settings.png"), self.tr("&Manage filters"), self) # manageAct.setShortcuts(QKeySequence.Open) manageAct.setStatusTip(self.tr("Manage added filters")) manageAct.triggered.connect(self.manage_filters) docker_conf_action = QAction(QIcon("images/docker.png"), self.tr("&Docker"), self) # docker_conf_action.setShortcuts() docker_conf_action.setStatusTip(self.tr("Docker")) docker_conf_action.triggered.connect(self.docker_conf) apparmor_conf_action = QAction(QIcon("images/apparmor.png"), self.tr("&Apparmor"), self) # apparmor_conf_action.setShortcuts() apparmor_conf_action.setStatusTip(self.tr("Apparmor")) apparmor_conf_action.triggered.connect(self.apparmor_conf) seccomp_conf_action = QAction(QIcon("images/lock.png"), self.tr("&Seccomp"), self) # docker_conf_action.setShortcuts() seccomp_conf_action.setStatusTip(self.tr("Seccomp")) seccomp_conf_action.triggered.connect(self.seccomp_conf) return [ manageAct, docker_conf_action, apparmor_conf_action, seccomp_conf_action ]
def __init__(self, owner): super(self.__class__, self).__init__(owner) Ui_download.__init__(self) self.setupUi(self) self.owner = weakref.ref(owner) self.downloadingList = [] # 正在下载列表 self.downloadList = [] # 下载队列 self.downloadDict = {} # bookId :downloadInfo self.convertList = [] self.convertingList = [] self.tableWidget.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) self.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows) self.tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers) self.tableWidget.setContextMenuPolicy(Qt.CustomContextMenu) self.tableWidget.setColumnCount(10) self.tableWidget.setHorizontalHeaderLabels([ "id", "标题", "下载状态", "下载进度", "下载章节", "下载速度", "转码进度", "转码章节", "转码耗时", "转码状态" ]) self.timer = QTimer(self.tableWidget) self.timer.setInterval(1000) self.timer.timeout.connect(self.UpdateTable) self.timer.start() # self.settings = QSettings('download.ini', QSettings.IniFormat) # self.InitSetting() self.tableWidget.customContextMenuRequested.connect(self.SelectMenu) self.openDirAction = QAction("打开目录", self) self.openDirAction.triggered.connect(self.ClickOpenFilePath) self.pauseAction = QAction("暂停", self) self.pauseAction.triggered.connect(self.ClickPause) self.removeAction = QAction("刪除记录", self) self.removeAction.triggered.connect(self.DelRecording) self.removeFileAction = QAction("刪除记录和文件", self) self.removeFileAction.triggered.connect(self.DelRecordingAndFile) self.openDirAction = QAction("打开目录", self) self.openDirAction.triggered.connect(self.ClickOpenFilePath) self.selectEpsAction = QAction("选择下载章节", self) self.selectEpsAction.triggered.connect(self.ClickDownloadEps) self.startAction = QAction("开始", self) self.startAction.triggered.connect(self.ClickStart) self.startConvertAction = QAction("开始转码", self) self.startConvertAction.triggered.connect(self.ClickConvertStart) self.pauseConvertAction = QAction("暂停转码", self) self.pauseConvertAction.triggered.connect(self.ClickConvertPause) self.tableWidget.doubleClicked.connect(self.OpenBookInfo) self.tableWidget.horizontalHeader().sectionClicked.connect(self.Sort) self.order = {} self.autoConvert = True self.db = DownloadDb() datas = self.db.LoadDownload(self) for task in datas.values(): self.downloadDict[task.bookId] = task rowCont = self.tableWidget.rowCount() task.tableRow = rowCont if task.status != DownloadInfo.Success: task.status = DownloadInfo.Pause if task.convertStatus != DownloadInfo.ConvertSuccess: task.convertStatus = DownloadInfo.Pause self.tableWidget.insertRow(rowCont) self.UpdateTableItem(task)
def __init__(self): super().__init__() self.setWindowTitle("Cobaya input generator for Cosmology") self.setStyleSheet("* {font-size:%s;}" % font_size) # Menu bar for defaults self.menubar = QMenuBar() defaults_menu = self.menubar.addMenu( '&Show defaults and bibliography for a component...') menu_actions = {} for kind in kinds: submenu = defaults_menu.addMenu(subfolders[kind]) components = get_available_internal_class_names(kind) menu_actions[kind] = {} for component in components: menu_actions[kind][component] = QAction(component, self) menu_actions[kind][component].setData((kind, component)) menu_actions[kind][component].triggered.connect( self.show_defaults) submenu.addAction(menu_actions[kind][component]) # Main layout self.menu_layout = QVBoxLayout() self.menu_layout.addWidget(self.menubar) self.setLayout(self.menu_layout) self.layout = QHBoxLayout() self.menu_layout.addLayout(self.layout) self.layout_left = QVBoxLayout() self.layout.addLayout(self.layout_left) self.layout_output = QVBoxLayout() self.layout.addLayout(self.layout_output) # LEFT: Options self.options = QWidget() self.layout_options = QVBoxLayout() self.options.setLayout(self.layout_options) self.options_scroll = QScrollArea() self.options_scroll.setWidget(self.options) self.options_scroll.setWidgetResizable(True) self.layout_left.addWidget(self.options_scroll) self.combos = dict() for group, fields in _combo_dict_text: group_box = QGroupBox(group) self.layout_options.addWidget(group_box) group_layout = QVBoxLayout(group_box) for a, desc in fields: self.combos[a] = QComboBox() # Combo box label only if not single element in group if len(fields) > 1: label = QLabel(desc) group_layout.addWidget(label) group_layout.addWidget(self.combos[a]) self.combos[a].addItems([ text(k, v) for k, v in getattr(input_database, a).items() ]) # PLANCK NAMES CHECKBOX TEMPORARILY DISABLED # if a == "theory": # # Add Planck-naming checkbox # self.planck_names = QCheckBox( # "Keep common parameter names " # "(useful for fast CLASS/CAMB switching)") # group_layout.addWidget(self.planck_names) # Connect to refreshers -- needs to be after adding all elements for field, combo in self.combos.items(): if field == "preset": combo.currentIndexChanged.connect(self.refresh_preset) else: combo.currentIndexChanged.connect(self.refresh) # self.planck_names.stateChanged.connect(self.refresh_keep_preset) # RIGHT: Output + buttons self.display_tabs = QTabWidget() self.display = {} for k in ["yaml", "python", "bibliography"]: self.display[k] = QTextEdit() self.display[k].setLineWrapMode(QTextEdit.NoWrap) self.display[k].setFontFamily("mono") self.display[k].setCursorWidth(0) self.display[k].setReadOnly(True) self.display_tabs.addTab(self.display[k], k) self.display["covmat"] = QWidget() covmat_tab_layout = QVBoxLayout() self.display["covmat"].setLayout(covmat_tab_layout) self.covmat_text = QLabel() self.covmat_text.setWordWrap(True) self.covmat_table = QTableWidget(0, 0) self.covmat_table.setEditTriggers( QAbstractItemView.NoEditTriggers) # ReadOnly! covmat_tab_layout.addWidget(self.covmat_text) covmat_tab_layout.addWidget(self.covmat_table) self.display_tabs.addTab(self.display["covmat"], "covariance matrix") self.layout_output.addWidget(self.display_tabs) # Buttons self.buttons = QHBoxLayout() self.save_button = QPushButton('Save as...', self) self.copy_button = QPushButton('Copy to clipboard', self) self.buttons.addWidget(self.save_button) self.buttons.addWidget(self.copy_button) self.save_button.released.connect(self.save_file) self.copy_button.released.connect(self.copy_clipb) self.layout_output.addLayout(self.buttons) self.save_dialog = QFileDialog() self.save_dialog.setFileMode(QFileDialog.AnyFile) self.save_dialog.setAcceptMode(QFileDialog.AcceptSave) self.read_settings() self.show()
def __init__(self, config): assertMainThread() super().__init__(config) # state self.preventSeek = False self.beginTime = None self.timeRatio = 1.0 # gui srv = Services.getService("MainWindow") config.configLoaded.connect(self.restoreState) config.configAboutToSave.connect(self.saveState) self.config = config playbackMenu = srv.menuBar().addMenu("&Playback") style = QApplication.style() self.actStart = QAction( QIcon.fromTheme("media-playback-start", style.standardIcon(QStyle.SP_MediaPlay)), "Start Playback", self) self.actPause = QAction( QIcon.fromTheme("media-playback-pause", style.standardIcon(QStyle.SP_MediaPause)), "Pause Playback", self) self.actPause.setEnabled(False) self.actStepFwd = QAction( QIcon.fromTheme("media-seek-forward", style.standardIcon(QStyle.SP_MediaSeekForward)), "Step Forward", self) self.actStepBwd = QAction( QIcon.fromTheme("media-seek-backward", style.standardIcon(QStyle.SP_MediaSeekBackward)), "Step Backward", self) self.actSeekEnd = QAction( QIcon.fromTheme("media-skip-forward", style.standardIcon(QStyle.SP_MediaSkipForward)), "Seek End", self) self.actSeekBegin = QAction( QIcon.fromTheme("media-skip-backward", style.standardIcon(QStyle.SP_MediaSkipBackward)), "Seek Begin", self) self.actSetTimeFactor = { r: QAction("x 1/%d" % (1 / r), self) if r < 1 else QAction("x %d" % r, self) for r in (1 / 8, 1 / 4, 1 / 2, 1, 2, 4, 8) } # pylint: disable=unnecessary-lambda # let's stay on the safe side and do not use emit as a slot... self.actStart.triggered.connect(self.startPlayback) self.actPause.triggered.connect(self.pausePlayback) self.actStepFwd.triggered.connect( lambda: self.stepForward(self.selectedStream())) self.actStepBwd.triggered.connect( lambda: self.stepBackward(self.selectedStream())) self.actSeekEnd.triggered.connect(self.seekEnd) self.actSeekBegin.triggered.connect(self.seekBeginning) # pylint: enable=unnecessary-lambda def setTimeFactor(newFactor): logger.debug("new time factor %f", newFactor) self.setTimeFactor(newFactor) for r in self.actSetTimeFactor: logger.debug("adding action for time factor %f", r) self.actSetTimeFactor[r].triggered.connect( functools.partial(setTimeFactor, r)) self.dockWidget = srv.newDockWidget("PlaybackControl", None, Qt.LeftDockWidgetArea) self.dockWidgetContents = QWidget(self.dockWidget) self.dockWidget.setWidget(self.dockWidgetContents) toolLayout = QBoxLayout(QBoxLayout.TopToBottom, self.dockWidgetContents) toolLayout.setContentsMargins(0, 0, 0, 0) toolBar = QToolBar() toolLayout.addWidget(toolBar) toolBar.addAction(self.actSeekBegin) toolBar.addAction(self.actStepBwd) toolBar.addAction(self.actStart) toolBar.addAction(self.actPause) toolBar.addAction(self.actStepFwd) toolBar.addAction(self.actSeekEnd) playbackMenu.addAction(self.actSeekBegin) playbackMenu.addAction(self.actStepBwd) playbackMenu.addAction(self.actStart) playbackMenu.addAction(self.actPause) playbackMenu.addAction(self.actStepFwd) playbackMenu.addAction(self.actSeekEnd) playbackMenu.addSeparator() for r in self.actSetTimeFactor: playbackMenu.addAction(self.actSetTimeFactor[r]) self.timeRatioLabel = QLabel("x 1") self.timeRatioLabel.addActions(list(self.actSetTimeFactor.values())) self.timeRatioLabel.setContextMenuPolicy(Qt.ActionsContextMenu) toolBar.addSeparator() toolBar.addWidget(self.timeRatioLabel) contentsLayout = QGridLayout() toolLayout.addLayout(contentsLayout, 10) # now we add a position view self.positionSlider = QSlider(Qt.Horizontal, self.dockWidgetContents) self.beginLabel = QLabel(parent=self.dockWidgetContents) self.beginLabel.setAlignment(Qt.AlignLeft | Qt.AlignCenter) self.currentLabel = QLabel(parent=self.dockWidgetContents) self.currentLabel.setAlignment(Qt.AlignHCenter | Qt.AlignCenter) self.endLabel = QLabel(parent=self.dockWidgetContents) self.endLabel.setAlignment(Qt.AlignRight | Qt.AlignCenter) contentsLayout.addWidget(self.beginLabel, 0, 0, alignment=Qt.AlignLeft) contentsLayout.addWidget(self.currentLabel, 0, 1, alignment=Qt.AlignHCenter) contentsLayout.addWidget(self.endLabel, 0, 2, alignment=Qt.AlignRight) contentsLayout.addWidget(self.positionSlider, 1, 0, 1, 3) self.positionSlider.setTracking(False) self.positionSlider.valueChanged.connect(self.onSliderValueChanged, Qt.DirectConnection) self.positionSlider.sliderMoved.connect(self.displayPosition) # file browser self.browser = BrowserWidget(self.dockWidget) self.nameFiltersChanged.connect(self._onNameFiltersChanged, Qt.QueuedConnection) contentsLayout.addWidget(self.browser, 3, 0, 1, 3) contentsLayout.setRowStretch(3, 100) self.browser.activated.connect(self.browserActivated) self.actShowAllFiles = QAction("Show all files") self.actShowAllFiles.setCheckable(True) self.actShowAllFiles.setChecked(False) self.actShowAllFiles.toggled.connect(self._onShowAllFiles) playbackMenu.addSeparator() playbackMenu.addAction(self.actShowAllFiles) self.actGroupStream = QActionGroup(self) self.actGroupStream.setExclusionPolicy( QActionGroup.ExclusionPolicy.ExclusiveOptional) playbackMenu.addSeparator() self.actGroupStreamMenu = playbackMenu.addMenu("Step Stream") self._selectedStream = None self.recentSeqs = [QAction() for i in range(10)] playbackMenu.addSeparator() recentMenu = playbackMenu.addMenu("Recent") for a in self.recentSeqs: a.setVisible(False) a.triggered.connect(self.openRecent) recentMenu.addAction(a) self._supportedFeaturesChanged(set(), set())
class MainWindow(QMainWindow): sequence_number = 1 window_list = [] dict_modified = False max_recent_files = 5 def __init__(self, regex_map, file_name='', dict_src='regex_map.json'): super().__init__() self.setAttribute(Qt.WA_DeleteOnClose) self.is_untitled = True self.cur_file = '' self.dict_src = dict_src self.regex_map = regex_map self.text_edit = MyPlainTextEdit(regex_map) self.mode_label = QLabel('Insert Mode') self.mode_label.setAlignment( Qt.AlignRight | Qt.AlignVCenter) # Prevents minor shift on toggling. self.md_text_edit = QTextEdit() self.md_text_edit.setReadOnly(True) self.setCentralWidget(self.text_edit) self.create_actions() self.create_menus() self.create_status_bar() self.create_dock_widget() QApplication.processEvents() # fixes status bar color issue. self.read_settings( ) # must go after processEvents() or `size` is overwritten for some reason # self.text_edit.document().contentsChanged.connect(self.document_was_modified) self.text_edit.textChanged.connect(self.document_was_modified) self.text_edit.entry_default_set.connect(self.handle_entry_default_set) self.text_edit.mode_toggled.connect(self.mode_label.setText) if file_name: self.load_file(file_name) else: self.set_current_file('') def closeEvent(self, event): if self.maybe_save(): self.write_settings() event.accept() else: event.ignore() def document_was_modified(self): self.setWindowModified(True) def update_markdown_viewer(self): """Updates contents and viewport of the dock widget.""" self.md_text_edit.document().setMarkdown( self.text_edit.document().toPlainText()) md_cur = self.md_text_edit.textCursor() md_cur.movePosition(QTextCursor.End) # This strategy could be problematic for large documents, as the lengths diverge. Not especially testable. md_cur.setPosition( min(md_cur.position(), self.text_edit.textCursor().position()) ) # len(md) <= len(plaintext) self.md_text_edit.setTextCursor(md_cur) def new_file(self): other = MainWindow(self.regex_map) MainWindow.window_list.append(other) other.move(self.x() + 40, self.y() + 40) other.show() def open_file(self, file_name: str): """ Handles opening a file: checking if already open, if we need a new MainWindow, or can safely overwrite. :param file_name: A canonical (or absolute?) file path. :return: """ existing = self.find_main_window(file_name) if existing is not None: existing.show() existing.raise_() existing.activateWindow() return if self.is_untitled and self.text_edit.document().isEmpty( ) and not self.isWindowModified(): self.load_file(file_name) else: other = MainWindow(self.regex_map, file_name) if other.is_untitled: # impossible? del other return MainWindow.window_list.append(other) other.move(self.x() + 40, self.y() + 40) other.show() def open_recent(self): """Only use as slot for QAction with data() set to a canonical (or absolute?) file name.""" # ref: https://stackoverflow.com/questions/21974449/extract-menu-action-data-in-receiving-function-or-slot action = self.sender() if action: self.open_file(action.data()) def open(self): file_name, _ = QFileDialog.getOpenFileName( self, filter="Text files (*.txt *.md)") if file_name: self.open_file(file_name) def about(self): about_dialog = QMessageBox(QMessageBox.Information, "OneHandTextEdit", "<h2>OneHandTextEdit</h2>", QMessageBox.Close, self) # TODO: set in deploy # icon = QPixmap("/Users/Scott/Desktop/128.png") # if not icon.isNull(): # about_dialog.setIconPixmap(icon) about_dialog.setInformativeText(""" <b>v 0.1.0</b> <br> license: <a href='https://www.gnu.org/licenses/gpl-3.0.txt'>GNU GPL 3.0</a> <br> inspiration: <a href='https://blog.xkcd.com/2007/08/14/mirrorboard-a-one-handed-keyboard-layout-for-the-lazy/'>xkcd</a> """) about_dialog.exec_() def print_(self, printer: QPrinter, text_edit: Union[QTextEdit, MyPlainTextEdit]): """ Prints text at any resolution with one-inch margins. :param printer: A configured printer (put through QPrintDialog probably) :param text_edit: The text editor. :return: None. Side-effect: Prints the document. """ doc_clone = text_edit.document().clone() printer.setPageMargins(25.4, 25.4, 25.4, 25.4, QPrinter.Millimeter) # 1 inch margins doc_clone.documentLayout().setPaintDevice(printer) doc_clone.setPageSize(printer.pageRect().size()) doc_clone.print_(printer) def print_with_setup(self, text_edit: Union[QTextEdit, MyPlainTextEdit]): """ Get printer settings from the user, then print at High Resolution with those settings or cancel. :param text_edit: A text editor containing a `document()` QTextDocument. :return: None. Side effect: modal dialog, possibly printing. """ if text_edit == self.md_text_edit: self.update_markdown_viewer() printer = QPrinter(QPrinter.HighResolution) print_dialog = QPrintDialog(printer, self) if print_dialog.exec_() == QDialog.Accepted: self.print_(printer, text_edit) def print_preview(self, text_edit: Union[QTextEdit, MyPlainTextEdit]): """ Print preview a given text editor's `document()` at High Resolution. :param text_edit: A text editor containing a `document()` QTextDocument. :return: None. Side effect: modal print preview. """ if text_edit == self.md_text_edit: self.update_markdown_viewer() printer = QPrinter(QPrinter.HighResolution) ppd = QPrintPreviewDialog(printer) ppd.paintRequested.connect(lambda: self.print_(printer, text_edit)) ppd.exec_() # Format def set_markdown_font(self): (ok, font) = QFontDialog.getFont(self.md_text_edit.font(), self, "Markdown Font") self.md_text_edit.setFont(font) # noinspection PyAttributeOutsideInit def create_actions(self): # File self.new_act = QAction("&New", self, statusTip="Create a new file", triggered=self.new_file) self.new_act.setShortcuts( [QKeySequence.New, QKeySequence(Qt.CTRL + Qt.Key_B)]) self.open_act = QAction("&Open...", self, statusTip="Open an existing file", triggered=self.open) self.open_act.setShortcuts( [QKeySequence.Open, QKeySequence(Qt.CTRL + Qt.Key_T)]) self.save_act = QAction("&Save", self, statusTip="Save the document to disk", triggered=self.save) self.save_act.setShortcuts( [QKeySequence.Save, QKeySequence(Qt.CTRL + Qt.Key_L)]) self.save_as_act = QAction( "Save &As...", self, statusTip="Save the document under a new name", triggered=self.save_as) self.save_as_act.setShortcuts( [QKeySequence.SaveAs, QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_L)]) self.recent_file_acts = [] for i in range(MainWindow.max_recent_files): self.recent_file_acts.append( QAction(self, visible=False, triggered=self.open_recent)) self.clear_recent_files_act = QAction( "Clear Menu", self, enabled=False, triggered=self.clear_recent_files) self.print_act = QAction("&Print...", self, statusTip="Print the document", triggered=functools.partial( self.print_with_setup, text_edit=self.text_edit)) self.print_act.setShortcuts( [QKeySequence.Print, QKeySequence(Qt.CTRL + Qt.Key_R)]) self.print_markdown_act = QAction("Print &Markdown...", self, triggered=functools.partial( self.print_with_setup, text_edit=self.md_text_edit)) self.print_markdown_act.setShortcuts([ QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_P), QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_R) ]) self.print_preview_act = QAction("Print Preview...", self, triggered=functools.partial( self.print_preview, text_edit=self.text_edit)) self.print_preview_markdown_act = QAction( "Print Markdown Preview...", self, triggered=functools.partial(self.print_preview, text_edit=self.md_text_edit)) self.close_act = QAction("&Close", self, statusTip="Close this window", triggered=self.close) self.close_act.setShortcuts([ QKeySequence(Qt.CTRL + Qt.Key_W), QKeySequence(Qt.CTRL + Qt.Key_BracketRight) ]) self.exit_act = QAction( "E&xit", self, statusTip="Exit the application", triggered=QApplication.instance().closeAllWindows) self.exit_act.setShortcuts([ QKeySequence(Qt.CTRL + Qt.Key_Q), QKeySequence(Qt.CTRL + Qt.Key_BracketLeft) ]) # Edit self.undo_act = QAction("Undo", self, enabled=False, triggered=self.text_edit.undo) self.undo_act.setShortcuts( [QKeySequence.Undo, QKeySequence(Qt.CTRL + Qt.Key_Slash)]) self.redo_act = QAction("Redo", self, enabled=False, triggered=self.text_edit.redo) self.redo_act.setShortcuts( [QKeySequence.Redo, QKeySequence(Qt.CTRL + Qt.Key_Y)]) self.cut_act = QAction( "Cu&t", self, enabled=False, statusTip="Cut the current selection's contents to the clipboard", triggered=self.text_edit.cut) self.cut_act.setShortcuts( [QKeySequence.Cut, QKeySequence(Qt.CTRL + Qt.Key_Period)]) self.copy_act = QAction( "&Copy", self, enabled=False, statusTip="Copy the current selection's contents to the clipboard", triggered=self.text_edit.copy) self.copy_act.setShortcuts( [QKeySequence.Copy, QKeySequence(Qt.CTRL + Qt.Key_Comma)]) self.paste_act = QAction( "&Paste", self, statusTip= "Paste the clipboard's contents into the current selection", triggered=self.text_edit.paste) self.paste_act.setShortcuts( [QKeySequence.Paste, QKeySequence(Qt.CTRL + Qt.Key_M)]) self.select_all_act = QAction("Select All", self, triggered=self.text_edit.selectAll) self.select_all_act.setShortcuts( [QKeySequence.SelectAll, QKeySequence(Qt.CTRL + Qt.Key_Semicolon)]) self.find_and_replace_act = QAction( "Find and Replace...", self, triggered=self.show_find_and_replace_dialog) self.find_and_replace_act.setShortcuts( [QKeySequence.Find, QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_J)]) # About self.about_act = QAction("&About", self, statusTip="Show the application's About box", triggered=self.about) self.about_Qt_act = QAction( "About &Qt", self, statusTip="Show the Qt library's About box", triggered=QApplication.instance().aboutQt) # View self.zoom_in_act = QAction("Zoom In", self, triggered=self.text_edit.zoomIn, shortcut=QKeySequence.ZoomIn) self.zoom_out_act = QAction("Zoom Out", self, triggered=self.text_edit.zoomOut, shortcut=QKeySequence.ZoomOut) # Format self.md_font_act = QAction("Markdown Font...", self, triggered=self.set_markdown_font) # Dictionary self.add_word_act = QAction("Add Word...", self, statusTip="Add a word to the dictionary", triggered=self.show_add_word_dialog) self.add_word_act.setShortcuts([ QKeySequence(Qt.CTRL + Qt.Key_J), QKeySequence(Qt.CTRL + Qt.Key_G) ]) self.delete_word_act = QAction( "Delete Word...", self, statusTip="Delete a word from the dictionary", triggered=self.show_del_word_dialog) self.delete_word_act.setShortcuts([ QKeySequence(Qt.CTRL + Qt.Key_D), QKeySequence(Qt.CTRL + Qt.Key_U) ]) self.toggle_mode_act = QAction( "Switch Mode", self, triggered=self.text_edit.handle_mode_toggle) self.toggle_mode_act.setShortcuts([ QKeySequence(Qt.CTRL + Qt.Key_I), QKeySequence(Qt.CTRL + Qt.Key_E) ]) # Connections self.text_edit.copyAvailable.connect(self.cut_act.setEnabled) self.text_edit.copyAvailable.connect(self.copy_act.setEnabled) self.text_edit.undoAvailable.connect(self.undo_act.setEnabled) self.text_edit.redoAvailable.connect(self.redo_act.setEnabled) # noinspection PyAttributeOutsideInit def create_menus(self): self.file_menu = self.menuBar().addMenu("&File") self.file_menu.addAction(self.new_act) self.file_menu.addAction(self.open_act) self.recent_file_submenu = self.file_menu.addMenu("Open Recent") for act in self.recent_file_acts: self.recent_file_submenu.addAction(act) self.recent_file_submenu.addSeparator() self.recent_file_submenu.addAction(self.clear_recent_files_act) self.update_recent_file_actions() self.file_menu.addAction(self.save_act) self.file_menu.addAction(self.save_as_act) self.file_menu.addSeparator() self.print_submenu = self.file_menu.addMenu("&Print") self.print_submenu.addAction(self.print_act) self.print_submenu.addAction(self.print_markdown_act) self.print_submenu.addAction(self.print_preview_act) self.print_submenu.addAction(self.print_preview_markdown_act) self.file_menu.addSeparator() self.file_menu.addAction(self.close_act) self.file_menu.addAction(self.exit_act) # End file menu self.edit_menu = self.menuBar().addMenu("&Edit") self.edit_menu.addAction(self.undo_act) self.edit_menu.addAction(self.redo_act) self.edit_menu.addSeparator() self.edit_menu.addAction(self.cut_act) self.edit_menu.addAction(self.copy_act) self.edit_menu.addAction(self.paste_act) self.edit_menu.addAction(self.select_all_act) self.edit_menu.addSeparator() self.edit_menu.addAction(self.find_and_replace_act) self.format_menu = self.menuBar().addMenu("For&mat") self.font_submenu = self.format_menu.addMenu("&Font") self.font_submenu.addAction(self.md_font_act) self.view_menu = self.menuBar().addMenu("&View") self.view_menu.addAction(self.zoom_in_act) self.view_menu.addAction(self.zoom_out_act) self.dict_menu = self.menuBar().addMenu('&Dictionary') self.dict_menu.addAction(self.add_word_act) self.dict_menu.addAction(self.delete_word_act) self.dict_menu.addSeparator() self.dict_menu.addAction(self.toggle_mode_act) self.menuBar().addSeparator() self.help_menu = self.menuBar().addMenu("&Help") self.help_menu.addAction(self.about_act) self.help_menu.addAction(self.about_Qt_act) def create_dock_widget(self): """ Sets up a dock widget for Markdown hot previewing, hidden by default. :return: None """ dock = QDockWidget("Markdown Viewer", self) dock.setWidget(self.md_text_edit) dock.visibilityChanged.connect( lambda visible: visible and self.update_markdown_viewer( )) # precludes slowdowns self.addDockWidget(Qt.BottomDockWidgetArea, dock) dock_act = dock.toggleViewAction() dock_act.setShortcuts([ QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_M), QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_C) ]) self.view_menu.addSeparator() # Does nothing on Mac self.view_menu.addAction(dock_act) dock.close() def create_status_bar(self): self.statusBar().showMessage("Ready") self.statusBar().addPermanentWidget(self.mode_label) def read_settings(self): settings = QSettings('PMA', 'OneHandTextEdit') pos = settings.value('pos', QPoint(200, 200)) size = settings.value('size', QSize(400, 400)) md_font = settings.value('md_font', self.md_text_edit.document().defaultFont()) self.md_text_edit.document().setDefaultFont(md_font) self.move(pos) self.resize(size) def write_settings(self): settings = QSettings('PMA', 'OneHandTextEdit') settings.setValue('pos', self.pos()) settings.setValue('size', self.size()) settings.setValue('md_font', self.md_text_edit.document().defaultFont()) def maybe_save(self): if self.text_edit.document().isModified(): ret = QMessageBox.warning( self, "OneHandTextEdit", "The document has been modified.\nDo you want to save your changes?", QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel) if ret == QMessageBox.Save: return self.save() elif ret == QMessageBox.Cancel: return False return True def save(self): if self.is_untitled: return self.save_as() else: return self.save_file(self.cur_file) def save_as(self): file_name, _ = QFileDialog.getSaveFileName(self, "Save As", self.cur_file) if not file_name: return False return self.save_file(file_name) def save_file(self, file_name): """ :param file_name: A canonical file path or whatever QFileDialog.getSaveFileName returns. :return: boolean for use in closeEvent method. """ error = None QApplication.setOverrideCursor(Qt.WaitCursor) file = QSaveFile(file_name) if file.open(QFile.WriteOnly | QFile.Text): outstr = QTextStream(file) outstr << self.text_edit.toPlainText() if not file.commit(): error = "Cannot write file {}:\n{}.".format( file_name, file.errorString()) else: error = "Cannot open file {}:\n{}.".format(file_name, file.errorString()) QApplication.restoreOverrideCursor() if error: QMessageBox.warning(self, "OneHandTextEdit", error) return False self.set_current_file(file_name) self.statusBar().showMessage("File saved", 2000) return True def load_file(self, file_name): """ Load file into current instance. :param file_name: whatever QFileDialog.getOpenFileName returns (abs or canonical path?), or canonical :return: """ file = QFile(file_name) if not file.open(QFile.ReadOnly | QFile.Text): QMessageBox.warning( self, "OneHandTextEdit", "Cannot read file {}:\n{}.".format(file_name, file.errorString())) return instr = QTextStream(file) QApplication.setOverrideCursor(Qt.WaitCursor) self.text_edit.setPlainText(instr.readAll()) QApplication.restoreOverrideCursor() self.set_current_file(file_name) self.statusBar().showMessage("File loaded", 2000) def set_current_file(self, file_name: str): """Sets cur_file to a canonical file path if file exists, otherwise a default placeholder bare file name. Updates window title and resets widget to unmodified. Updates recent files list. """ self.is_untitled = not file_name if self.is_untitled: self.cur_file = "document{!s}.txt".format( MainWindow.sequence_number) MainWindow.sequence_number += 1 else: self.cur_file = QFileInfo(file_name).canonicalFilePath() self.text_edit.document().setModified(False) self.setWindowModified(False) stripped_name = QFileInfo(self.cur_file).fileName() self.setWindowTitle("{}[*]".format(stripped_name)) # Recent files if self.is_untitled: return settings = QSettings('PMA', 'OneHandTextEdit') recent_files: List = settings.value('recent_files', []) try: recent_files.remove(self.cur_file) except ValueError: pass recent_files.insert(0, self.cur_file) recent_files = recent_files[:MainWindow.max_recent_files] settings.setValue('recent_files', recent_files) for widget in QApplication.topLevelWidgets(): if isinstance(widget, MainWindow): widget.update_recent_file_actions() def clear_recent_files(self): """Clears the recent files setting and updates menus across all main windows.""" settings = QSettings('PMA', 'OneHandTextEdit') settings.setValue('recent_files', []) for widget in QApplication.topLevelWidgets(): if isinstance(widget, MainWindow): widget.update_recent_file_actions() def update_recent_file_actions(self): settings = QSettings('PMA', 'OneHandTextEdit') recent_files: List = settings.value('recent_files', []) self.clear_recent_files_act.setEnabled(len(recent_files) > 0) for i, file in enumerate(recent_files): self.recent_file_acts[i].setText(QFileInfo(file).fileName()) self.recent_file_acts[i].setData(file) self.recent_file_acts[i].setVisible(True) for j in range(len(recent_files), MainWindow.max_recent_files): self.recent_file_acts[j].setVisible(False) def find_main_window(self, file_name): canonical_file_path = QFileInfo(file_name).canonicalFilePath() for widget in QApplication.instance().topLevelWidgets(): if isinstance( widget, MainWindow) and widget.cur_file == canonical_file_path: return widget return def show_validating_dialog(self, input_label: str, handler: Callable[[str], None]): regex = QRegExp(r'[A-Za-z]+([A-Za-z\'-]+[A-Za-z]+|[A-Za-z]*)') validator = QRegExpValidator(regex) help_dialog = QMessageBox( QMessageBox.Information, "OneHandTextEdit", "A word can only contain letters (upper or lower case) and " "contain (but not start or end with) - (dashes) and ' (apostrophes).", buttons=QMessageBox.Ok) dialog = ValidatingDialog(validator, help_dialog, input_label=input_label, parent=self) dialog.submitted.connect(handler) dialog.show() dialog.raise_() dialog.activateWindow() def show_find_and_replace_dialog(self): find_replace_dialog = PlainTextFindReplaceDialog(self.text_edit, parent=self) find_replace_dialog.show() def show_add_word_dialog(self): self.show_validating_dialog("Add word:", self.handle_add_word) def show_del_word_dialog(self): self.show_validating_dialog("Remove word:", self.handle_delete_word) def handle_add_word(self, word: str): """ Adds word to dictionary and marks dictionary as modified. :param word: Word to add to dictionary. :return: """ added: bool = add_word_to_dict(word, self.regex_map) if added: MainWindow.dict_modified = True else: QMessageBox.information(self, "One Hand Text Edit", "Word already in your dictionary") def handle_delete_word(self, word: str): """ Deletes word from dictionary and marks dictionary as modified. :param word: Word to remove from dictionary. :return: """ deleted: bool = del_word_from_dict(word, self.regex_map) if deleted: MainWindow.dict_modified = True else: QMessageBox.information(self, "One Hand Text Edit", "Word not found in dictionary") def handle_entry_default_set(self): MainWindow.dict_modified = True
class MainWindow(QMainWindow): def __init__(self): super().__init__() self.__activeCount: int = 0 # number of operations in progress centralWidget = MainWidget() self.setCentralWidget(centralWidget) self.notifier = None # Set by main script # Initialise a thread pool self.threadPool = QThreadPool.globalInstance() logging.info('Multithreading with maximum {} threads'.format( self.threadPool.maxThreadCount())) self.setWindowTitle('dataMole') self.setUpMenus() self.__spinnerMutex = QMutex() centralWidget.frameInfoPanel.operationRequest.connect( self.executeOperation) centralWidget.workbenchView.selectedRowChanged[str, str].connect( self.changedSelectedFrame) def moveEvent(self, event: QtGui.QMoveEvent) -> None: self.notifier.mNotifier.updatePosition() super().moveEvent(event) def resizeEvent(self, event: QtGui.QResizeEvent) -> None: self.notifier.mNotifier.updatePosition() super().resizeEvent(event) @Slot() def openComparePanel(self) -> None: w = self.centralWidget().workbenchModel dv = DataframeSideBySideView(self) dv.setWindowFlags(Qt.Window) dv.setAttribute(Qt.WA_DeleteOnClose) dv.setWindowTitle('Side by side view') dv.dataWidgetL.setWorkbench(w) dv.dataWidgetR.setWorkbench(w) if w.rowCount(): dv.dataWidgetL.setDataframe(dv.dataWidgetL.inputCB.currentText()) dv.dataWidgetR.setDataframe(dv.dataWidgetR.inputCB.currentText()) dv.show() # @Slot() # def openDiffPanel(self) -> None: # TODO: remove this? # dv = DiffDataframeWidget(self) # dv.setWindowFlags(Qt.Window) # dv.setAttribute(Qt.WA_DeleteOnClose) # dv.setWindowTitle('Diff view') # dv.setWorkbench(self.centralWidget().workbenchModel) # dv.show() @Slot(str, str) def changedSelectedFrame(self, newName: str, _: str) -> None: # Slot called when workbench selection change self.aWriteCsv.setOperationArgs(w=self.centralWidget().workbenchModel, frameName=newName) self.aWritePickle.setOperationArgs( w=self.centralWidget().workbenchModel, frameName=newName) @Slot(int, str) def operationStateChanged(self, uid: int, state: str) -> None: if state == 'success': logging.info('Operation uid={:d} succeeded'.format(uid)) self.statusBar().showMessage('Operation succeeded', 10000) elif state == 'error': logging.error('Operation uid={:d} stopped with errors'.format(uid)) self.statusBar().showMessage('Operation stopped with errors', 10000) elif state == 'start': self.__spinnerMutex.lock() self.__activeCount += 1 self.__spinnerMutex.unlock() self.statusBar().startSpinner() logging.info('Operation uid={:d} started'.format(uid)) self.statusBar().showMessage('Executing...', 10000) elif state == 'finish': logging.info('Operation uid={:d} finished'.format(uid)) self.__spinnerMutex.lock() self.__activeCount -= 1 if self.__activeCount == 0: self.statusBar().stopSpinner() self.__spinnerMutex.unlock() # print('Emit', uid, state, 'count={}'.format(self.__activeCount)) @Slot(type) def executeOperation(self, opType: type) -> None: action = OperationAction(opType, self, opType.name(), self.rect().center(), self.centralWidget().workbenchModel) # Set selected frame in the input combo box of the action selection = self.centralWidget().workbenchView.selectedIndexes() if selection: selectedFrame: str = selection[0].data(Qt.DisplayRole) action.setSelectedFrame(selectedFrame) # Delete action when finished action.stateChanged.connect(self.operationStateChanged) action.stateChanged.connect(self.deleteAction) # Start operation action.trigger() @Slot(int, str) def deleteAction(self, uid: int, state: str): if state == 'finish': action: QAction = self.sender() action.deleteLater() logging.info( 'Action for operation uid={:d} scheduled for deletion'.format( uid)) def setUpMenus(self) -> None: menuBar = QMenuBar() fileMenu = menuBar.addMenu('File') importMenu = fileMenu.addMenu('Import') exportMenu = fileMenu.addMenu('Export') flowMenu = menuBar.addMenu('Flow') viewMenu = menuBar.addMenu('View') helpMenu = menuBar.addMenu('Help') aAppendEmpty = QAction('Add frame', fileMenu) aAppendEmpty.setStatusTip('Create an empty dataframe in the workbench') aQuit = QAction('Quit', fileMenu) aLoadCsv = OperationAction(CsvLoader, fileMenu, 'From csv', self.rect().center(), self.centralWidget().workbenchModel) self.aWriteCsv = OperationAction(CsvWriter, fileMenu, 'To csv', self.rect().center(), w=self.centralWidget().workbenchModel) aLoadPickle = OperationAction(PickleLoader, fileMenu, 'From pickle', self.rect().center(), self.centralWidget().workbenchModel) self.aWritePickle = OperationAction( PickleWriter, fileMenu, 'To pickle', self.mapToGlobal(self.rect().center()), w=self.centralWidget().workbenchModel) aCompareFrames = QAction('Compare dataframes', viewMenu) aLogDir = QAction('Open log directory', helpMenu) aClearLogs = QAction('Delete old logs', helpMenu) fileMenu.addActions([aAppendEmpty, aQuit]) exportMenu.addActions([self.aWriteCsv, self.aWritePickle]) importMenu.addActions([aLoadCsv, aLoadPickle]) self._aStartFlow = QAction('Execute', flowMenu) self._aResetFlow = QAction('Reset', flowMenu) aSaveFlow = QAction('Save', flowMenu) aLoadFlow = QAction('Load', flowMenu) flowMenu.addActions( [self._aStartFlow, self._aResetFlow, aSaveFlow, aLoadFlow]) viewMenu.addAction(aCompareFrames) helpMenu.addActions([aLogDir, aClearLogs]) self.setMenuBar(menuBar) # Tips aLoadCsv.setStatusTip('Load a csv file in the workbench') aLoadPickle.setStatusTip('Load a Pickle file in the workbench') self.aWriteCsv.setStatusTip('Write a dataframe to a csv file') self.aWritePickle.setStatusTip( 'Serializes a dataframe into a pickle file') aCompareFrames.setStatusTip('Open two dataframes side by side') self._aStartFlow.setStatusTip('Start flow-graph execution') self._aResetFlow.setStatusTip('Reset the node status in flow-graph') aLogDir.setStatusTip('Open the folder containing all logs') aClearLogs.setStatusTip('Delete older logs and keep the last 5') # Connect aAppendEmpty.triggered.connect( self.centralWidget().workbenchModel.appendEmptyRow) aQuit.triggered.connect(self.close) self._aStartFlow.triggered.connect( self.centralWidget().controller.executeFlow) self._aResetFlow.triggered.connect( self.centralWidget().controller.resetFlowStatus) aCompareFrames.triggered.connect(self.openComparePanel) aLogDir.triggered.connect(self.openLogDirectory) aClearLogs.triggered.connect(self.clearLogDir) aSaveFlow.triggered.connect(self.saveFlow) aLoadFlow.triggered.connect(self.readFlow) aLoadCsv.stateChanged.connect(self.operationStateChanged) aLoadPickle.stateChanged.connect(self.operationStateChanged) self.aWriteCsv.stateChanged.connect(self.operationStateChanged) self.aWritePickle.stateChanged.connect(self.operationStateChanged) @Slot() def openLogDirectory(self) -> None: QDesktopServices.openUrl( QUrl(os.path.join(os.getcwd(), flogging.LOG_FOLDER))) @Slot() def clearLogDir(self) -> None: flogging.deleteOldLogs() gui.statusBar.showMessage('Logs cleared') @Slot() def saveFlow(self): """ Dump a pipeline in a pickle file """ path, ext = QFileDialog.getSaveFileName(self, 'Save flow graph') if path: path = getFileNameWithExtension(path, ext) serialization = self.centralWidget().graph.serialize() # Add info about scene position to node data nodeSerialization: Dict[int, Dict] = serialization['nodes'] for nodeId, data in nodeSerialization.items(): data['pos'] = self.centralWidget( ).graphScene.nodesDict[nodeId].scenePos().toTuple() with open(path, 'wb') as file: pickle.dump(serialization, file) gui.statusBar.showMessage( 'Pipeline was successfully exported in {:s}'.format(path), 15) @Slot() def readFlow(self): """ Load a pipeline from a pickle file """ path, ext = QFileDialog.getOpenFileName( self, 'Open flow graph', filter='Pickle (*.pickle);;All files (*)') if path: try: with open(path, 'rb') as file: serialization: Dict = pickle.load(file) graph = flow.dag.OperationDag.deserialize(serialization) except pickle.PickleError as e: gui.notifier.addMessage('Pickle error', str(e), QMessageBox.Critical) except exc.DagException as e: gui.notifier.addMessage('Error while creating pipeline', str(e), QMessageBox.Critical) else: # Add the workbench g = graph.getNxGraph() for nodeId in g.nodes: g.nodes[nodeId][ 'op'].operation._workbench = self.centralWidget( ).workbenchModel self.centralWidget().createNewFlow(graph) self.centralWidget().controller.showGraphInScene() # Set position of every node nodeDict = serialization['nodes'] for nodeId, node in self.centralWidget( ).graphScene.nodesDict.items(): pos: Tuple[float, float] = nodeDict[nodeId]['pos'] node.setPos(*pos) node.refresh() # Update connected edges # Reconnect actions to the new controller self._aStartFlow.triggered.connect( self.centralWidget().controller.executeFlow) self._aResetFlow.triggered.connect( self.centralWidget().controller.resetFlowStatus) gui.statusBar.showMessage('Pipeline was successfully imported', 15)
def create_actions(self): # File self.new_act = QAction("&New", self, statusTip="Create a new file", triggered=self.new_file) self.new_act.setShortcuts( [QKeySequence.New, QKeySequence(Qt.CTRL + Qt.Key_B)]) self.open_act = QAction("&Open...", self, statusTip="Open an existing file", triggered=self.open) self.open_act.setShortcuts( [QKeySequence.Open, QKeySequence(Qt.CTRL + Qt.Key_T)]) self.save_act = QAction("&Save", self, statusTip="Save the document to disk", triggered=self.save) self.save_act.setShortcuts( [QKeySequence.Save, QKeySequence(Qt.CTRL + Qt.Key_L)]) self.save_as_act = QAction( "Save &As...", self, statusTip="Save the document under a new name", triggered=self.save_as) self.save_as_act.setShortcuts( [QKeySequence.SaveAs, QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_L)]) self.recent_file_acts = [] for i in range(MainWindow.max_recent_files): self.recent_file_acts.append( QAction(self, visible=False, triggered=self.open_recent)) self.clear_recent_files_act = QAction( "Clear Menu", self, enabled=False, triggered=self.clear_recent_files) self.print_act = QAction("&Print...", self, statusTip="Print the document", triggered=functools.partial( self.print_with_setup, text_edit=self.text_edit)) self.print_act.setShortcuts( [QKeySequence.Print, QKeySequence(Qt.CTRL + Qt.Key_R)]) self.print_markdown_act = QAction("Print &Markdown...", self, triggered=functools.partial( self.print_with_setup, text_edit=self.md_text_edit)) self.print_markdown_act.setShortcuts([ QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_P), QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_R) ]) self.print_preview_act = QAction("Print Preview...", self, triggered=functools.partial( self.print_preview, text_edit=self.text_edit)) self.print_preview_markdown_act = QAction( "Print Markdown Preview...", self, triggered=functools.partial(self.print_preview, text_edit=self.md_text_edit)) self.close_act = QAction("&Close", self, statusTip="Close this window", triggered=self.close) self.close_act.setShortcuts([ QKeySequence(Qt.CTRL + Qt.Key_W), QKeySequence(Qt.CTRL + Qt.Key_BracketRight) ]) self.exit_act = QAction( "E&xit", self, statusTip="Exit the application", triggered=QApplication.instance().closeAllWindows) self.exit_act.setShortcuts([ QKeySequence(Qt.CTRL + Qt.Key_Q), QKeySequence(Qt.CTRL + Qt.Key_BracketLeft) ]) # Edit self.undo_act = QAction("Undo", self, enabled=False, triggered=self.text_edit.undo) self.undo_act.setShortcuts( [QKeySequence.Undo, QKeySequence(Qt.CTRL + Qt.Key_Slash)]) self.redo_act = QAction("Redo", self, enabled=False, triggered=self.text_edit.redo) self.redo_act.setShortcuts( [QKeySequence.Redo, QKeySequence(Qt.CTRL + Qt.Key_Y)]) self.cut_act = QAction( "Cu&t", self, enabled=False, statusTip="Cut the current selection's contents to the clipboard", triggered=self.text_edit.cut) self.cut_act.setShortcuts( [QKeySequence.Cut, QKeySequence(Qt.CTRL + Qt.Key_Period)]) self.copy_act = QAction( "&Copy", self, enabled=False, statusTip="Copy the current selection's contents to the clipboard", triggered=self.text_edit.copy) self.copy_act.setShortcuts( [QKeySequence.Copy, QKeySequence(Qt.CTRL + Qt.Key_Comma)]) self.paste_act = QAction( "&Paste", self, statusTip= "Paste the clipboard's contents into the current selection", triggered=self.text_edit.paste) self.paste_act.setShortcuts( [QKeySequence.Paste, QKeySequence(Qt.CTRL + Qt.Key_M)]) self.select_all_act = QAction("Select All", self, triggered=self.text_edit.selectAll) self.select_all_act.setShortcuts( [QKeySequence.SelectAll, QKeySequence(Qt.CTRL + Qt.Key_Semicolon)]) self.find_and_replace_act = QAction( "Find and Replace...", self, triggered=self.show_find_and_replace_dialog) self.find_and_replace_act.setShortcuts( [QKeySequence.Find, QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_J)]) # About self.about_act = QAction("&About", self, statusTip="Show the application's About box", triggered=self.about) self.about_Qt_act = QAction( "About &Qt", self, statusTip="Show the Qt library's About box", triggered=QApplication.instance().aboutQt) # View self.zoom_in_act = QAction("Zoom In", self, triggered=self.text_edit.zoomIn, shortcut=QKeySequence.ZoomIn) self.zoom_out_act = QAction("Zoom Out", self, triggered=self.text_edit.zoomOut, shortcut=QKeySequence.ZoomOut) # Format self.md_font_act = QAction("Markdown Font...", self, triggered=self.set_markdown_font) # Dictionary self.add_word_act = QAction("Add Word...", self, statusTip="Add a word to the dictionary", triggered=self.show_add_word_dialog) self.add_word_act.setShortcuts([ QKeySequence(Qt.CTRL + Qt.Key_J), QKeySequence(Qt.CTRL + Qt.Key_G) ]) self.delete_word_act = QAction( "Delete Word...", self, statusTip="Delete a word from the dictionary", triggered=self.show_del_word_dialog) self.delete_word_act.setShortcuts([ QKeySequence(Qt.CTRL + Qt.Key_D), QKeySequence(Qt.CTRL + Qt.Key_U) ]) self.toggle_mode_act = QAction( "Switch Mode", self, triggered=self.text_edit.handle_mode_toggle) self.toggle_mode_act.setShortcuts([ QKeySequence(Qt.CTRL + Qt.Key_I), QKeySequence(Qt.CTRL + Qt.Key_E) ]) # Connections self.text_edit.copyAvailable.connect(self.cut_act.setEnabled) self.text_edit.copyAvailable.connect(self.copy_act.setEnabled) self.text_edit.undoAvailable.connect(self.undo_act.setEnabled) self.text_edit.redoAvailable.connect(self.redo_act.setEnabled)
central_widget = CentralWidget(window) window.setCentralWidget(central_widget) status_bar = StatusBar(window) window.setStatusBar(status_bar) dock = Dock("File Explorer", central_widget, window) dock.tree_init() window.addDockWidget(Qt.LeftDockWidgetArea, dock) toggle_dock_action = dock.toggleViewAction() toggle_dock_action.setShortcut("Ctrl+B") menubar.view_menu.addAction(toggle_dock_action) dock_db = Dock("Connected Databases", central_widget, window) dock_db.init_db_tree() window.addDockWidget(Qt.LeftDockWidgetArea, dock_db) toggle_connected_dbs_action = dock_db.toggleViewAction() toggle_connected_dbs_action.setShortcut("Ctrl+D") refresh_action = QAction(QIcon("view/images/menubar/refresh.png"), "Refresh Connected DBs", dock_db) refresh_action.setShortcut("Ctrl+R") refresh_action.setStatusTip("Refreshes list of connected databases in the dock") refresh_action.triggered.connect(dock_db.connected_dbs) menubar.database_menu.addAction(refresh_action) menubar.view_menu.addAction(toggle_connected_dbs_action) window.showMaximized() sys.exit(app.exec())
def initActions(self): self.newGameAction = QAction("&New Game", self) self.newGameAction.setIcon(QIcon(CONST.ICONS["New"])) self.newGameAction.triggered.connect(self.newGame) self.newGameAction.setShortcut('CTRL+N') self.openAction = QAction("&Open", self) self.openAction.setIcon(QIcon(CONST.ICONS["Open"])) self.openAction.triggered.connect(self.openFile) self.openAction.setShortcut('CTRL+O') self.saveGameAction = QAction("&Save", self) self.saveGameAction.setIcon(QIcon(CONST.ICONS["Save"])) self.saveGameAction.triggered.connect(self.saveGame) self.saveGameAction.setShortcut('CTRL+S') self.saveAsAction = QAction("Save &As", self) self.saveAsAction.setIcon(QIcon(CONST.ICONS["Save"])) self.saveAsAction.triggered.connect(self.saveGameAs) self.saveAsAction.setShortcut('CTRL+A') self.showAboutDialogAction = QAction("&About DCS Liberation", self) self.showAboutDialogAction.setIcon(QIcon.fromTheme("help-about")) self.showAboutDialogAction.triggered.connect(self.showAboutDialog) self.showLiberationPrefDialogAction = QAction("&Preferences", self) self.showLiberationPrefDialogAction.setIcon( QIcon.fromTheme("help-about")) self.showLiberationPrefDialogAction.triggered.connect( self.showLiberationDialog) self.openDiscordAction = QAction("&Discord Server", self) self.openDiscordAction.setIcon(CONST.ICONS["Discord"]) self.openDiscordAction.triggered.connect( lambda: webbrowser.open_new_tab("https://" + "discord.gg" + "/" + "bKrt" + "rkJ")) self.openGithubAction = QAction("&Github Repo", self) self.openGithubAction.setIcon(CONST.ICONS["Github"]) self.openGithubAction.triggered.connect( lambda: webbrowser.open_new_tab( "https://github.com/khopa/dcs_liberation"))
def __init__(self, game): """ La fenêtre principale est initialisée avec l'organisation suivante : +--------------------------------------------------------------------------+ | self.menu = self.menuBar() | | | +--------------------------------------------------------------------------+ | toolbar = QToolBar() (déplacement/sélection des robots, | | boutons annuler, indice et solution) | +------------------------layout0 = QHBoxLayout()---------------------------+ | layout2 = QHBoxLayout() + moves_label | | l + | | | a grid_choice | nb_robots_choice | (affichage des | +-y--------------------+----------------------+ L | | o | a mouvements effectués) | | u | y | | t +--o-------------------------+ | = label = QLabel() | u | | Q | t tip_label | | V contient la grille de jeu | 3 | | B | (Affichage de l'indice| | o | = | | x | si demandé) | | L | Q | | a +--V+------------------------+ | y | B solution_label | | o | o | | u | x (Affichage de la | | t | | | | solution si demandée) | +---------------------------------------------+----------------------------+ """ super().__init__() self.game = game self.initial_game_state = self.game.get_state() self.number_moves = 0 self.setWindowTitle("Robot Ricochet") self.resize(self.DIMENSION + 150, self.DIMENSION + 100) # label contient la grille de jeu self.label = QLabel() canvas = QPixmap(self.DIMENSION, self.DIMENSION) canvas.fill(Qt.white) self.label.setPixmap(canvas) # layout0 contient, à gauche, les barres d'outils et la grille, # et à droite, la liste des états et l'indice affiché par le solveur layout0 = QHBoxLayout() layout0.setContentsMargins(0, 0, 0, 0) layout0.setSpacing(0) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout2 = QHBoxLayout() # Choix de la grille self.choice_of_grid_menu() # choix du nombre de robots self.nb_robots_choice_menu() # CheckBox placement aléatoire widget3 = QCheckBox("Placement aléatoire des robots et de l'objectif") widget3.setCheckState(Qt.Checked) widget3.stateChanged.connect(self.placer_aleatoirement) # layout2 contient les 3 widgets horizontaux de choix de grille, robots et aléa layout2.addWidget(self.grid_choice) layout2.addWidget(self.nb_robots_choice) #layout2.addWidget(widget3) layout2.setContentsMargins(0, 0, 0, 0) layout2.setSpacing(0) widget2 = QWidget() widget2.setLayout(layout2) layout.addWidget(widget2) layout.addWidget(self.label) # liste des mouvement effectués, indice et solution: layout3 = QVBoxLayout() layout3.setContentsMargins(0, 0, 0, 0) layout3.setSpacing(0) self.moves_label = QLabel() self.print_moves_list() self.tip_label = QLabel() self.solution_label = QLabel() layout3.addWidget(self.moves_label) layout3.addWidget(self.tip_label) layout3.addWidget(self.solution_label) layout0.addLayout(layout) layout0.addLayout(layout3) widget = QWidget() widget.setLayout(layout0) self.setCentralWidget(widget) # Menu self.menu = self.menuBar() self.file_menu = self.menu.addMenu("File") self.help_menu = self.menu.addMenu("Aide et instructions") # A faire self.size_fenetre = self.geometry() # Play QAction play_action = QAction("Réinitialiser !", self) play_action.triggered.connect(self.replay) self.file_menu.addAction(play_action) # Open_grid QAction open_grid_action = QAction("Ouvrir une grille", self) open_grid_action.setShortcut('Ctrl+O') open_grid_action.triggered.connect(self.open_grid) self.file_menu.addAction(open_grid_action) # Open_game QAction open_game_action = QAction("Ouvrir un jeu", self) open_game_action.triggered.connect(self.open_game) self.file_menu.addAction(open_game_action) # Save_grid QAction save_grid_action = QAction("Enregistrer cette grille", self) save_grid_action.triggered.connect(self.save_grid) self.file_menu.addAction(save_grid_action) # Save_game QAction save_game_action = QAction("Enregistrer ce jeu", self) save_game_action.triggered.connect(self.save_game) self.file_menu.addAction(save_game_action) # Exit QAction exit_action = QAction("Quitter", self) exit_action.setShortcut(QKeySequence.Quit) exit_action.triggered.connect(self.close) self.file_menu.addAction(exit_action) # Help QAction help_action = QAction("Aide", self) help_action.triggered.connect(self.help) self.help_menu.addAction(help_action) self.toolbar_menus() #Le robot rouge est sélectionné par défaut self.selected_robot = 'R' self.draw_robots_and_goal()
class QLiberationWindow(QMainWindow): def __init__(self, game: Optional[Game]) -> None: super(QLiberationWindow, self).__init__() self.game = game self.game_model = GameModel(game) Dialog.set_game(self.game_model) self.ato_panel = QAirTaskingOrderPanel(self.game_model) self.info_panel = QInfoPanel(self.game) self.liberation_map = QLiberationMap(self.game_model) self.setGeometry(300, 100, 270, 100) self.setWindowTitle(f"DCS Liberation - v{VERSION}") self.setWindowIcon(QIcon("./resources/icon.png")) self.statusBar().showMessage('Ready') self.initUi() self.initActions() self.initToolbar() self.initMenuBar() self.connectSignals() screen = QDesktopWidget().screenGeometry() self.setGeometry(0, 0, screen.width(), screen.height()) self.setWindowState(Qt.WindowMaximized) if self.game is None: last_save_file = liberation_install.get_last_save_file() if last_save_file: try: logging.info("Loading last saved game : " + str(last_save_file)) game = persistency.load_game(last_save_file) self.onGameGenerated(game) except: logging.info("Error loading latest save game") else: logging.info("No existing save game") else: self.onGameGenerated(self.game) def initUi(self): hbox = QSplitter(Qt.Horizontal) vbox = QSplitter(Qt.Vertical) hbox.addWidget(self.ato_panel) hbox.addWidget(vbox) vbox.addWidget(self.liberation_map) vbox.addWidget(self.info_panel) # Will make the ATO sidebar as small as necessary to fit the content. In # practice this means it is sized by the hints in the panel. hbox.setSizes([1, 10000000]) vbox.setSizes([600, 100]) vbox = QVBoxLayout() vbox.setMargin(0) vbox.addWidget(QTopPanel(self.game_model)) vbox.addWidget(hbox) central_widget = QWidget() central_widget.setLayout(vbox) self.setCentralWidget(central_widget) def connectSignals(self): GameUpdateSignal.get_instance().gameupdated.connect(self.setGame) GameUpdateSignal.get_instance().debriefingReceived.connect( self.onDebriefing) def initActions(self): self.newGameAction = QAction("&New Game", self) self.newGameAction.setIcon(QIcon(CONST.ICONS["New"])) self.newGameAction.triggered.connect(self.newGame) self.newGameAction.setShortcut('CTRL+N') self.openAction = QAction("&Open", self) self.openAction.setIcon(QIcon(CONST.ICONS["Open"])) self.openAction.triggered.connect(self.openFile) self.openAction.setShortcut('CTRL+O') self.saveGameAction = QAction("&Save", self) self.saveGameAction.setIcon(QIcon(CONST.ICONS["Save"])) self.saveGameAction.triggered.connect(self.saveGame) self.saveGameAction.setShortcut('CTRL+S') self.saveAsAction = QAction("Save &As", self) self.saveAsAction.setIcon(QIcon(CONST.ICONS["Save"])) self.saveAsAction.triggered.connect(self.saveGameAs) self.saveAsAction.setShortcut('CTRL+A') self.showAboutDialogAction = QAction("&About DCS Liberation", self) self.showAboutDialogAction.setIcon(QIcon.fromTheme("help-about")) self.showAboutDialogAction.triggered.connect(self.showAboutDialog) self.showLiberationPrefDialogAction = QAction("&Preferences", self) self.showLiberationPrefDialogAction.setIcon( QIcon.fromTheme("help-about")) self.showLiberationPrefDialogAction.triggered.connect( self.showLiberationDialog) self.openDiscordAction = QAction("&Discord Server", self) self.openDiscordAction.setIcon(CONST.ICONS["Discord"]) self.openDiscordAction.triggered.connect( lambda: webbrowser.open_new_tab("https://" + "discord.gg" + "/" + "bKrt" + "rkJ")) self.openGithubAction = QAction("&Github Repo", self) self.openGithubAction.setIcon(CONST.ICONS["Github"]) self.openGithubAction.triggered.connect( lambda: webbrowser.open_new_tab( "https://github.com/khopa/dcs_liberation")) def initToolbar(self): self.tool_bar = self.addToolBar("File") self.tool_bar.addAction(self.newGameAction) self.tool_bar.addAction(self.openAction) self.tool_bar.addAction(self.saveGameAction) self.links_bar = self.addToolBar("Links") self.links_bar.addAction(self.openDiscordAction) self.links_bar.addAction(self.openGithubAction) self.display_bar = self.addToolBar("Display") def initMenuBar(self): self.menu = self.menuBar() file_menu = self.menu.addMenu("&File") file_menu.addAction(self.newGameAction) file_menu.addAction(self.openAction) file_menu.addSeparator() file_menu.addAction(self.saveGameAction) file_menu.addAction(self.saveAsAction) file_menu.addSeparator() file_menu.addAction(self.showLiberationPrefDialogAction) file_menu.addSeparator() file_menu.addAction("E&xit", self.close) displayMenu = self.menu.addMenu("&Display") last_was_group = False for item in DisplayOptions.menu_items(): if isinstance(item, DisplayRule): if last_was_group: displayMenu.addSeparator() self.display_bar.addSeparator() action = self.make_display_rule_action(item) displayMenu.addAction(action) if action.icon(): self.display_bar.addAction(action) last_was_group = False elif isinstance(item, DisplayGroup): displayMenu.addSeparator() self.display_bar.addSeparator() group = QActionGroup(displayMenu) for display_rule in item: action = self.make_display_rule_action(display_rule, group) displayMenu.addAction(action) if action.icon(): self.display_bar.addAction(action) last_was_group = True help_menu = self.menu.addMenu("&Help") help_menu.addAction(self.openDiscordAction) help_menu.addAction(self.openGithubAction) help_menu.addAction( "&Releases", lambda: webbrowser.open_new_tab( "https://github.com/Khopa/dcs_liberation/releases")) help_menu.addAction("&Online Manual", lambda: webbrowser.open_new_tab(URLS["Manual"])) help_menu.addAction( "&ED Forum Thread", lambda: webbrowser.open_new_tab(URLS["ForumThread"])) help_menu.addAction("Report an &issue", lambda: webbrowser.open_new_tab(URLS["Issues"])) help_menu.addSeparator() help_menu.addAction(self.showAboutDialogAction) @staticmethod def make_display_rule_action(display_rule, group: Optional[QActionGroup] = None ) -> QAction: def make_check_closure(): def closure(): display_rule.value = action.isChecked() return closure action = QAction(f"&{display_rule.menu_text}", group) if display_rule.menu_text in CONST.ICONS.keys(): action.setIcon(CONST.ICONS[display_rule.menu_text]) action.setCheckable(True) action.setChecked(display_rule.value) action.toggled.connect(make_check_closure()) return action def newGame(self): wizard = NewGameWizard(self) wizard.show() wizard.accepted.connect( lambda: self.onGameGenerated(wizard.generatedGame)) def openFile(self): file = QFileDialog.getOpenFileName( self, "Select game file to open", dir=persistency._dcs_saved_game_folder, filter="*.liberation") if file is not None: game = persistency.load_game(file[0]) GameUpdateSignal.get_instance().updateGame(game) def saveGame(self): logging.info("Saving game") if self.game.savepath: persistency.save_game(self.game) GameUpdateSignal.get_instance().updateGame(self.game) liberation_install.setup_last_save_file(self.game.savepath) liberation_install.save_config() else: self.saveGameAs() def saveGameAs(self): file = QFileDialog.getSaveFileName( self, "Save As", dir=persistency._dcs_saved_game_folder, filter="*.liberation") if file is not None: self.game.savepath = file[0] persistency.save_game(self.game) liberation_install.setup_last_save_file(self.game.savepath) liberation_install.save_config() def onGameGenerated(self, game: Game): logging.info("On Game generated") self.game = game GameUpdateSignal.get_instance().updateGame(self.game) def setGame(self, game: Optional[Game]): try: self.game = game if self.info_panel is not None: self.info_panel.setGame(game) self.game_model.set(self.game) if self.liberation_map is not None: self.liberation_map.setGame(game) except AttributeError: logging.exception("Incompatible save game") QMessageBox.critical( self, "Could not load save game", "The save game you have loaded is incompatible with this " "version of DCS Liberation.\n" "\n" f"{traceback.format_exc()}", QMessageBox.Ok) GameUpdateSignal.get_instance().updateGame(None) def showAboutDialog(self): text = "<h3>DCS Liberation " + VERSION + "</h3>" + \ "<b>Source code :</b> https://github.com/khopa/dcs_liberation" + \ "<h4>Authors</h4>" + \ "<p>DCS Liberation was originally developed by <b>shdwp</b>, DCS Liberation 2.0 is a partial rewrite based on this work by <b>Khopa</b>." \ "<h4>Contributors</h4>" + \ "shdwp, Khopa, ColonelPanic, Roach, Wrycu, calvinmorrow, JohanAberg, Deus, root0fall, Captain Cody, steveveepee, pedromagueija, parithon, bwRavencl, davidp57, Plob, Hawkmoon" + \ "<h4>Special Thanks :</h4>" \ "<b>rp-</b> <i>for the pydcs framework</i><br/>"\ "<b>Grimes (mrSkortch)</b> & <b>Speed</b> <i>for the MIST framework</i><br/>"\ "<b>Ciribob </b> <i>for the JTACAutoLase.lua script</i><br/>"\ "<b>Walder </b> <i>for the Skynet-IADS script</i><br/>"\ "<b>Anubis Yinepu </b> <i>for the Hercules Cargo script</i><br/>" about = QMessageBox() about.setWindowTitle("About DCS Liberation") about.setIcon(QMessageBox.Icon.Information) about.setText(text) logging.info(about.textFormat()) about.exec_() def showLiberationDialog(self): self.subwindow = QLiberationPreferencesWindow() self.subwindow.show() def onDebriefing(self, debrief: Debriefing): logging.info("On Debriefing") self.debriefing = QDebriefingWindow(debrief) self.debriefing.show() def closeEvent(self, event: QCloseEvent) -> None: result = QMessageBox.question( self, "Quit Liberation?", "Are you sure you want to quit? All unsaved progress will be lost.", QMessageBox.Yes | QMessageBox.No) if result == QMessageBox.Yes: super().closeEvent(event) else: event.ignore()
def start(_exit: bool = False) -> None: app = QApplication(sys.argv) first_start = False if not os.path.isfile(STATE_FILE): first_start = True logo = QIcon(LOGO) main_window = MainWindow() ui = main_window.ui main_window.setWindowIcon(logo) tray = QSystemTrayIcon(logo, app) tray.activated.connect(main_window.systray_clicked) menu = QMenu() action_show = QAction("Show") action_show.triggered.connect(main_window.show) action_exit = QAction("Exit") action_exit.triggered.connect(app.exit) menu.addAction(action_show) menu.addAction(action_exit) tray.setContextMenu(menu) ui.text.textChanged.connect(partial(queue_text_change, ui)) ui.command.textChanged.connect(partial(update_button_command, ui)) ui.keys.textChanged.connect(partial(update_button_keys, ui)) ui.write.textChanged.connect(partial(update_button_write, ui)) ui.change_brightness.valueChanged.connect( partial(update_change_brightness, ui)) ui.switch_page.valueChanged.connect(partial(update_switch_page, ui)) ui.imageButton.clicked.connect(partial(select_image, main_window)) ui.brightness.valueChanged.connect(partial(set_brightness, ui)) ui.information.currentIndexChanged.connect(partial(set_information, ui)) items = api.open_decks().items() print("wait for device(s)") while len(items) == 0: time.sleep(3) items = api.open_decks().items() print("found " + str(len(items)) + ": " + ",".join(str(i) for i in list(items))) for deck_id, deck in items: ui.device_list.addItem(f"{deck['type']} - {deck_id}", userData=deck_id) build_device(ui) ui.device_list.currentIndexChanged.connect(partial(build_device, ui)) ui.pages.currentChanged.connect(partial(change_page, ui)) ui.actionExport.triggered.connect(partial(export_config, main_window)) ui.actionImport.triggered.connect(partial(import_config, main_window)) ui.actionExit.triggered.connect(app.exit) timer = QTimer() timer.timeout.connect(partial(sync, ui)) timer.start(1000) api.render() tray.show() if first_start: main_window.show() if _exit: return else: sys.exit(app.exec_())
class AppMenuBar(QMenuBar): """ This code is for the menu bar at the top of the main window. File, help, etc. """ def __init__(self, parent, log_handlers: [StreamHandler], lang: LangEnum): self._logger = getLogger(__name__) for h in log_handlers: self._logger.addHandler(h) self._logger.debug("Initializing") super().__init__(parent) self.setGeometry(QRect(0, 0, 840, 22)) self._file_menu = QMenu(self) self.addAction(self._file_menu.menuAction()) self._open_last_save_dir_action = QAction(self) self._file_menu.addAction(self._open_last_save_dir_action) # self._cam_list_menu = QMenu(self) # self._file_menu.addMenu(self._cam_list_menu) # self._use_cams_action = QAction(self) # self._use_cams_action.setCheckable(True) # self._cam_list_menu.addAction(self._use_cams_action) # sep = self._cam_list_menu.addSeparator() self._settings_menu = QMenu(self) self.addAction(self._settings_menu.menuAction()) self._debug_actions = [] self._debug_menu = QMenu(self) self._settings_menu.addMenu(self._debug_menu) self._debug_action = QAction(self) self._debug_action.setCheckable(True) self._debug_action.triggered.connect(self._debug_clicked) self._debug_actions.append(self._debug_action) self._debug_menu.addAction(self._debug_action) self._warning_action = QAction(self) self._warning_action.setCheckable(True) self._warning_action.triggered.connect(self._warning_clicked) self._debug_actions.append(self._warning_action) self._debug_menu.addAction(self._warning_action) self._lang_actions = [] self._language_menu = QMenu(self) self._settings_menu.addMenu(self._language_menu) self._english_action = QAction(self) self._lang_actions.append(self._english_action) self._english_action.setCheckable(True) self._english_action.triggered.connect(self._eng_clicked) self._language_menu.addAction(self._english_action) self._french_action = QAction(self) self._lang_actions.append(self._french_action) self._french_action.setCheckable(True) self._french_action.triggered.connect(self._fre_clicked) self._language_menu.addAction(self._french_action) self._german_action = QAction(self) self._lang_actions.append(self._german_action) self._german_action.setCheckable(True) self._german_action.triggered.connect(self._ger_clicked) self._language_menu.addAction(self._german_action) self._spanish_action = QAction(self) self._lang_actions.append(self._spanish_action) self._spanish_action.setCheckable(True) self._spanish_action.triggered.connect(self._spa_clicked) self._language_menu.addAction(self._spanish_action) # TODO: issue with Chinese characters. # Maybe use traditional instead of simplified. # self._chinese_action = QAction(self) # self._lang_actions.append(self._chinese_action) # self._chinese_action.setCheckable(True) # self._chinese_action.triggered.connect(self._chi_clicked) # self._language_menu.addAction(self._chinese_action) self._help_menu = QMenu(self) self.addAction(self._help_menu.menuAction()) self._about_app_action = QAction(self) self._help_menu.addAction(self._about_app_action) self._about_company_action = QAction(self) self._help_menu.addAction(self._about_company_action) self._update_action = QAction(self) self._help_menu.addAction(self._update_action) self._log_window_action = QAction(self) self._help_menu.addAction(self._log_window_action) self._cam_actions = {} self._debug_callback = None self._lang_callback = None self._strings = dict() self.set_lang(lang) self._logger.debug("Initialized") def set_lang(self, lang: LangEnum) -> None: """ Set the language of this view item. :param lang: The language enum to use. :return None: """ self._strings = strings[lang] self._set_texts() if lang == LangEnum.ENG: self._reset_lang_actions(self._english_action) elif lang == LangEnum.FRE: self._reset_lang_actions(self._french_action) elif lang == LangEnum.GER: self._reset_lang_actions(self._german_action) elif lang == LangEnum.SPA: self._reset_lang_actions(self._spanish_action) # elif lang == LangEnum.CHI: # self._reset_lang_actions(self._chinese_action) def add_lang_select_handler(self, func: classmethod) -> None: """ Add handler for these selectable. Handler must take a LangEnum :param func: The handler. :return None: """ self._lang_callback = func def set_debug_action(self, level) -> None: """ Set which debug action is checked. :param level: The debug level. :return None: """ if level == DEBUG: self._reset_debug_actions(self._debug_action) elif level == WARNING: self._reset_debug_actions(self._warning_action) def add_debug_select_handler(self, func: classmethod) -> None: """ Add handler for these selectables. Handler must take a string. :param func: The handler. :return None: """ self._debug_callback = func def set_cam_action_enabled(self, is_active: bool) -> None: """ Set whether or not the camera actions can be used. :param is_active: can be used. :return None: """ self._use_cams_action.setEnabled(is_active) for action in self._cam_actions.values(): action.setEnabled(is_active) def set_cam_bool_checked(self, is_active: bool) -> None: """ Set whether or not this action is checked. :param is_active: whether or not this action should be checked. :return None: """ self._use_cams_action.setChecked(is_active) self.empty_cam_actions() def add_cam_bool_handler(self, func: classmethod) -> None: """ Add handler to this selectable. :param func: The handler. :return None: """ self._logger.debug("running") self._use_cams_action.toggled.connect(func) self._logger.debug("done") def set_use_cams_action_active(self, is_active: bool) -> None: """ Set whether or not this action is usable. :param is_active: whether or not to let this action be usable. :return None: """ self._use_cams_action.setEnabled(is_active) def add_open_last_save_dir_handler(self, func: classmethod) -> None: """ Add handler to this selectable. :param func: The handler. :return None: """ self._logger.debug("running") self._open_last_save_dir_action.triggered.connect(func) self._logger.debug("done") def add_about_app_handler(self, func: classmethod) -> None: """ Add handler to this selectable. :param func: The handler. :return None: """ self._logger.debug("running") self._about_app_action.triggered.connect(func) self._logger.debug("done") def add_about_company_handler(self, func: classmethod) -> None: """ Add handler to this selectable. :param func: The handler. :return None: """ self._logger.debug("running") self._about_company_action.triggered.connect(func) self._logger.debug("done") def add_update_handler(self, func: classmethod) -> None: """ Add handler to this selectable. :param func: The handler. :return None: """ self._logger.debug("running") self._update_action.triggered.connect(func) self._logger.debug("done") def add_log_window_handler(self, func: classmethod) -> None: """ Add handler to this selectable. :param func: The handler. :return None: """ self._logger.debug("running") self._log_window_action.triggered.connect(func) self._logger.debug("done") def add_cam_action(self, name: str, handler: classmethod, is_active: bool = True) -> None: """ Add a new action in the camera menu by name. :param name: The name of the camera being added. :param handler: The button handler. :param is_active: Whether or not this camera is considered active. :return None: """ new_cam_action = QAction(self) new_cam_action.setText(name) new_cam_action.setCheckable(True) new_cam_action.setChecked(is_active) new_cam_action.toggled.connect(handler) self._cam_actions[name] = new_cam_action self._cam_list_menu.addAction(new_cam_action) def remove_cam_action(self, name: str) -> None: """ Remove a cam action by name. :param name: The name of the camera being removed. :return None: """ if name in self._cam_actions.keys(): self._cam_list_menu.removeAction(self._cam_actions[name]) del self._cam_actions[name] def _eng_clicked(self) -> None: """ Private handler for self._english_action :return None: """ if self._lang_callback: self._lang_callback(LangEnum.ENG) def _fre_clicked(self) -> None: """ Private handler for self._french_action :return None: """ if self._lang_callback: self._lang_callback(LangEnum.FRE) def _ger_clicked(self) -> None: """ Private handler for self._german_action :return None: """ if self._lang_callback: self._lang_callback(LangEnum.GER) def _spa_clicked(self) -> None: """ Private handler for self._spanish_action :return None: """ if self._lang_callback: self._lang_callback(LangEnum.SPA) def _chi_clicked(self) -> None: """ Private handler for self._chinese_action :return None: """ if self._lang_callback: self._lang_callback(LangEnum.CHI) def _debug_clicked(self) -> None: """ Private handler for self._french_action :return None: """ if self._debug_callback: self._debug_callback(DEBUG) self._reset_debug_actions(self._debug_action) def _warning_clicked(self) -> None: """ Private handler for self._french_action :return None: """ if self._debug_callback: self._debug_callback(WARNING) self._reset_debug_actions(self._warning_action) def _reset_debug_actions(self, keep_checked: QAction) -> None: """ Unset all except for keep_checked QAction. :param keep_checked: The QAction to keep checked. :return None: """ for action in self._debug_actions: action.setChecked(False) keep_checked.setChecked(True) def _reset_lang_actions(self, keep_checked: QAction) -> None: """ Unset all except for keep_checked QAction. :param keep_checked: The QAction to keep checked. :return None: """ for action in self._lang_actions: action.setChecked(False) keep_checked.setChecked(True) def _set_texts(self) -> None: """ Set the texts of this view object. :return None: """ self._logger.debug("running") self._file_menu.setTitle(self._strings[StringsEnum.FILE]) self._open_last_save_dir_action.setText( self._strings[StringsEnum.LAST_DIR]) self._settings_menu.setTitle(self._strings[StringsEnum.SETTINGS]) self._debug_menu.setTitle(self._strings[StringsEnum.DEBUG_MENU]) self._debug_action.setText(self._strings[StringsEnum.DEBUG]) self._warning_action.setText(self._strings[StringsEnum.WARNING]) # self._cam_list_menu.setTitle(self._strings[StringsEnum.ATTACHED_CAMS]) # self._use_cams_action.setText(self._strings[StringsEnum.USE_CAMS]) self._language_menu.setTitle(self._strings[StringsEnum.LANG]) self._english_action.setText(self._strings[StringsEnum.ENG]) self._french_action.setText(self._strings[StringsEnum.FRE]) self._german_action.setText(self._strings[StringsEnum.GER]) self._spanish_action.setText(self._strings[StringsEnum.SPA]) # self._chinese_action.setText(self._strings[StringsEnum.CHI]) self._help_menu.setTitle(self._strings[StringsEnum.HELP]) self._about_app_action.setText(self._strings[StringsEnum.ABOUT_APP]) self._about_company_action.setText( self._strings[StringsEnum.ABOUT_COMPANY]) self._update_action.setText(self._strings[StringsEnum.UPDATE_CHECK]) self._log_window_action.setText( self._strings[StringsEnum.SHOW_LOG_WINDOW]) self._logger.debug("done") def empty_cam_actions(self) -> None: """ :return None: """ for name in self._cam_actions.keys(): self._cam_list_menu.removeAction(self._cam_actions[name]) self._cam_actions = {}
def create_menu(self): mainMenu = self.menuBar() fileMenu = mainMenu.addMenu("File") viewMenu = mainMenu.addMenu("View") editMenu = mainMenu.addMenu("Edit") analysisMenu = mainMenu.addMenu("Analysis") helpMenu = mainMenu.addMenu("Help") openAction = QAction("Open", self) openAction.triggered.connect(self.load) openAction.setShortcut("Ctrl+O") exportAction = QAction("Export", self) exportAction.setShortcut("Ctrl+e") exportAction.triggered.connect(self.export_img) exitAction = QAction("Exit", self) exitAction.triggered.connect(self.close) exitAction.setShortcut("Ctrl+q") membTransAction = QAction('Membrane Translocation', self) thresholdAction = QAction('Threshold', self) RoIAction = QAction('RoI', self) RoIAction.triggered.connect(self.set_roi) MeasureAction = QAction('Measure', self) MeasureAction.triggered.connect(self.measure) int_trackAction = QAction('Intensity Tracking', self) int_trackAction.triggered.connect(self.int_track) analysisMenu.addAction(membTransAction) analysisMenu.addAction(thresholdAction) analysisMenu.addAction(RoIAction) analysisMenu.addAction(MeasureAction) analysisMenu.addAction(int_trackAction) fileMenu.addAction(openAction) fileMenu.addAction(exportAction) fileMenu.addAction(exitAction)
def __init__(self, parent, log_handlers: [StreamHandler], lang: LangEnum): self._logger = getLogger(__name__) for h in log_handlers: self._logger.addHandler(h) self._logger.debug("Initializing") super().__init__(parent) self.setGeometry(QRect(0, 0, 840, 22)) self._file_menu = QMenu(self) self.addAction(self._file_menu.menuAction()) self._open_last_save_dir_action = QAction(self) self._file_menu.addAction(self._open_last_save_dir_action) # self._cam_list_menu = QMenu(self) # self._file_menu.addMenu(self._cam_list_menu) # self._use_cams_action = QAction(self) # self._use_cams_action.setCheckable(True) # self._cam_list_menu.addAction(self._use_cams_action) # sep = self._cam_list_menu.addSeparator() self._settings_menu = QMenu(self) self.addAction(self._settings_menu.menuAction()) self._debug_actions = [] self._debug_menu = QMenu(self) self._settings_menu.addMenu(self._debug_menu) self._debug_action = QAction(self) self._debug_action.setCheckable(True) self._debug_action.triggered.connect(self._debug_clicked) self._debug_actions.append(self._debug_action) self._debug_menu.addAction(self._debug_action) self._warning_action = QAction(self) self._warning_action.setCheckable(True) self._warning_action.triggered.connect(self._warning_clicked) self._debug_actions.append(self._warning_action) self._debug_menu.addAction(self._warning_action) self._lang_actions = [] self._language_menu = QMenu(self) self._settings_menu.addMenu(self._language_menu) self._english_action = QAction(self) self._lang_actions.append(self._english_action) self._english_action.setCheckable(True) self._english_action.triggered.connect(self._eng_clicked) self._language_menu.addAction(self._english_action) self._french_action = QAction(self) self._lang_actions.append(self._french_action) self._french_action.setCheckable(True) self._french_action.triggered.connect(self._fre_clicked) self._language_menu.addAction(self._french_action) self._german_action = QAction(self) self._lang_actions.append(self._german_action) self._german_action.setCheckable(True) self._german_action.triggered.connect(self._ger_clicked) self._language_menu.addAction(self._german_action) self._spanish_action = QAction(self) self._lang_actions.append(self._spanish_action) self._spanish_action.setCheckable(True) self._spanish_action.triggered.connect(self._spa_clicked) self._language_menu.addAction(self._spanish_action) # TODO: issue with Chinese characters. # Maybe use traditional instead of simplified. # self._chinese_action = QAction(self) # self._lang_actions.append(self._chinese_action) # self._chinese_action.setCheckable(True) # self._chinese_action.triggered.connect(self._chi_clicked) # self._language_menu.addAction(self._chinese_action) self._help_menu = QMenu(self) self.addAction(self._help_menu.menuAction()) self._about_app_action = QAction(self) self._help_menu.addAction(self._about_app_action) self._about_company_action = QAction(self) self._help_menu.addAction(self._about_company_action) self._update_action = QAction(self) self._help_menu.addAction(self._update_action) self._log_window_action = QAction(self) self._help_menu.addAction(self._log_window_action) self._cam_actions = {} self._debug_callback = None self._lang_callback = None self._strings = dict() self.set_lang(lang) self._logger.debug("Initialized")
def _create_menu(self): file_menu = self.menuBar().addMenu("&File") exit_action = QAction(QIcon.fromTheme("application-exit"), "E&xit", self, shortcut = "Ctrl+Q", triggered=qApp.quit) file_menu.addAction(exit_action) navigation_menu = self.menuBar().addMenu("&Navigation") style_icons = ':/qt-project.org/styles/commonstyle/images/' back_action = QAction(QIcon.fromTheme("go-previous", QIcon(style_icons + 'left-32.png')), "Back", self, shortcut = QKeySequence(QKeySequence.Back), triggered = self._tab_widget.back) self._actions[QWebEnginePage.Back] = back_action back_action.setEnabled(False) navigation_menu.addAction(back_action) forward_action = QAction(QIcon.fromTheme("go-next", QIcon(style_icons + 'right-32.png')), "Forward", self, shortcut = QKeySequence(QKeySequence.Forward), triggered = self._tab_widget.forward) forward_action.setEnabled(False) self._actions[QWebEnginePage.Forward] = forward_action navigation_menu.addAction(forward_action) reload_action = QAction(QIcon(style_icons + 'refresh-32.png'), "Reload", self, shortcut = QKeySequence(QKeySequence.Refresh), triggered = self._tab_widget.reload) self._actions[QWebEnginePage.Reload] = reload_action reload_action.setEnabled(False) navigation_menu.addAction(reload_action) navigation_menu.addSeparator() new_tab_action = QAction("New Tab", self, shortcut = 'Ctrl+T', triggered = self.add_browser_tab) navigation_menu.addAction(new_tab_action) close_tab_action = QAction("Close Current Tab", self, shortcut = "Ctrl+W", triggered = self._close_current_tab) navigation_menu.addAction(close_tab_action) edit_menu = self.menuBar().addMenu("&Edit") find_action = QAction("Find", self, shortcut = QKeySequence(QKeySequence.Find), triggered = self._show_find) edit_menu.addAction(find_action) edit_menu.addSeparator() undo_action = QAction("Undo", self, shortcut = QKeySequence(QKeySequence.Undo), triggered = self._tab_widget.undo) self._actions[QWebEnginePage.Undo] = undo_action undo_action.setEnabled(False) edit_menu.addAction(undo_action) redo_action = QAction("Redo", self, shortcut = QKeySequence(QKeySequence.Redo), triggered = self._tab_widget.redo) self._actions[QWebEnginePage.Redo] = redo_action redo_action.setEnabled(False) edit_menu.addAction(redo_action) edit_menu.addSeparator() cut_action = QAction("Cut", self, shortcut = QKeySequence(QKeySequence.Cut), triggered = self._tab_widget.cut) self._actions[QWebEnginePage.Cut] = cut_action cut_action.setEnabled(False) edit_menu.addAction(cut_action) copy_action = QAction("Copy", self, shortcut = QKeySequence(QKeySequence.Copy), triggered = self._tab_widget.copy) self._actions[QWebEnginePage.Copy] = copy_action copy_action.setEnabled(False) edit_menu.addAction(copy_action) paste_action = QAction("Paste", self, shortcut = QKeySequence(QKeySequence.Paste), triggered = self._tab_widget.paste) self._actions[QWebEnginePage.Paste] = paste_action paste_action.setEnabled(False) edit_menu.addAction(paste_action) edit_menu.addSeparator() select_all_action = QAction("Select All", self, shortcut = QKeySequence(QKeySequence.SelectAll), triggered = self._tab_widget.select_all) self._actions[QWebEnginePage.SelectAll] = select_all_action select_all_action.setEnabled(False) edit_menu.addAction(select_all_action) self._bookmark_menu = self.menuBar().addMenu("&Bookmarks") add_bookmark_action = QAction("&Add Bookmark", self, triggered = self._add_bookmark) self._bookmark_menu.addAction(add_bookmark_action) add_tool_bar_bookmark_action = QAction("&Add Bookmark to Tool Bar", self, triggered = self._add_tool_bar_bookmark) self._bookmark_menu.addAction(add_tool_bar_bookmark_action) self._bookmark_menu.addSeparator() tools_menu = self.menuBar().addMenu("&Tools") download_action = QAction("Open Downloads", self, triggered = DownloadWidget.open_download_directory) tools_menu.addAction(download_action) window_menu = self.menuBar().addMenu("&Window") window_menu.addAction(self._bookmark_dock.toggleViewAction()) window_menu.addSeparator() zoom_in_action = QAction(QIcon.fromTheme("zoom-in"), "Zoom In", self, shortcut = QKeySequence(QKeySequence.ZoomIn), triggered = self._zoom_in) window_menu.addAction(zoom_in_action) zoom_out_action = QAction(QIcon.fromTheme("zoom-out"), "Zoom Out", self, shortcut = QKeySequence(QKeySequence.ZoomOut), triggered = self._zoom_out) window_menu.addAction(zoom_out_action) reset_zoom_action = QAction(QIcon.fromTheme("zoom-original"), "Reset Zoom", self, shortcut = "Ctrl+0", triggered = self._reset_zoom) window_menu.addAction(reset_zoom_action) about_menu = self.menuBar().addMenu("&About") about_action = QAction("About Qt", self, shortcut = QKeySequence(QKeySequence.HelpContents), triggered=qApp.aboutQt) about_menu.addAction(about_action)
class Header(QLabel): currPosX = 0 currPosY = 0 selectedHeader = None def __init__(self,parent = None): QLabel.__init__(self, parent) self.headers = [] self.position = 0 self.viewWidth = 800 self.viewHeight = 100 self.hideFlag = False self.w = 180 self.h = 40 self.t = 10 self.setMouseTracking(True) self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.createHeaderMenus) self.deleteAct = QAction('Hide', self) self.deleteAct.setStatusTip('Hide this life-line') self.deleteAct.triggered.connect(self.hideLifeLine) self.groupAct = QAction('Make a cluster', self) self.groupAct.setStatusTip('Make a cluster of multiple life-lines') self.groupAct.triggered.connect(self.showClusterDialog) self.scatterAct = QAction('Scatter', self) self.scatterAct.setStatusTip('Scatter this cluster') self.scatterAct.triggered.connect(self.scatterCluster) def connectMainView(self,mainView): """ mainView contains sequence diagram """ self.mainView = mainView def reset(self): self.headers = [] self.update() # Clear up all the heads def createHeaderMenus(self): for headInfo in self.headers: x = headInfo['x'] x = x - self.position if self.currPosX > x and self.currPosX < x+self.w and self.currPosY > self.t and self.currPosY < self.h+self.t: if 0 == self.headers.index(headInfo): break self.selectedHeader = headInfo menu = QMenu() if headInfo['flagCluster']: menu.addAction(self.scatterAct) else: menu.addAction(self.deleteAct) menu.addAction(self.groupAct) menu.exec_(QtGui.QCursor.pos()) def showClusterDialog(self): response, cluster_name = ClusterDialog.ClusterDialog.getClusterName(self.mainView.getLifeLines(),self.selectedHeader['name']) if response: if self.mainView.createCluster(cluster_name): pass else: pass def scatterCluster(self): self.mainView.removeCluster(self.selectedHeader['name']) def hideLifeLine(self): self.mainView.hideLifeline(self.selectedHeader['name']) self.mainView.refreshData() def mouseMoveEvent(self,e): self.currPosX = e.x() self.currPosY = e.y() selected_head_name = None for headInfo in self.headers: x = headInfo['x'] headx = x - self.position if e.x() > headx and e.x() < headx+self.w and e.y() > self.t and e.y() < self.h+self.t: msg_list = self.mainView.getMessageList(headInfo['name']) formatted = '<< ' + headInfo['name'] + ' >>\n' for m in msg_list: formatted += '\n- ' + m self.setToolTip(formatted) break else: self.setToolTip('') def updateViewSize(self,width,height): self.viewWidth = width self.setFixedHeight(self.viewHeight) def paintEvent(self, event): QLabel.paintEvent(self,event) self.resize(self.viewWidth,self.viewHeight) for headInfo in self.headers: name = headInfo['name'] x = headInfo['x'] x = x - self.position if x+self.w < 0: continue if x > self.viewWidth: continue self.headLeftPos = x self.headRightPos = x+self.w self.headTopPos = self.t self.headBottomPos = self.t+self.h qp = QtGui.QPainter() qp.begin(self) qp.setBrush(headInfo['color']) qp.drawRect(x,self.t,self.w,self.h) qp.setPen(QtGui.QColor(20, 20, 30)) qp.setFont(QtGui.QFont('Decorative', 10)) leaf_name = list(reversed(name.split(".")))[0] parent_name = ".".join(list(reversed(list(reversed(name.split(".")))[1:]))) if parent_name == "": qp.setFont(QtGui.QFont('Decorative', 11)) wd = QtGui.QFontMetrics(self.font()).boundingRect(leaf_name).width() align_option = QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter if wd > self.w-20 else QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter qp.drawText(QtCore.QRect(x+10,self.t,self.w-20,self.h), align_option, leaf_name) else: qp.setFont(QtGui.QFont('Decorative', 7)) wd = QtGui.QFontMetrics(self.font()).boundingRect(parent_name).width() align_option = QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter if wd > self.w-20 else QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter qp.drawText(QtCore.QRect(x+10,self.t,self.w-20,self.h/2), align_option, parent_name) qp.setFont(QtGui.QFont('Decorative', 11)) qp.drawText(QtCore.QRect(x+10,self.t+self.h/2,self.w-20,self.h/2), QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter, leaf_name) qp.drawLine(x+self.w/2,self.h+self.t,x+self.w/2,90000) qp.end() def mouseReleaseEvent(self, e): self.currPosX = e.x() self.currPosY = e.y() selected_head_name = None def wheelEvent(self, e): print("mouse wheel event") def drawAtHeaderRegion(self,x,color,name,flagCluster): item = {'x':x, 'color':color, 'name':name, 'flagCluster':flagCluster} if item not in self.headers: self.headers.append(item) def resetLifelines(self): del self.headers[:] def setPosition(self, x): self.position = x self.update() def activateHide(self,flag): self.hideFlag = flag def resetAllLifelines(self): pass def activateCapture(self,flag): pass def searchMessage(self,str): pass def moveToPrev(self): pass def moveToNext(self): pass