def _createDock(self): dock = QDockWidget("Workflow") dock.setObjectName("ERTGUI Workflow") dock.setWidget(self.contentsWidget) dock.setFeatures(QDockWidget.DockWidgetClosable) dock.setAllowedAreas(Qt.LeftDockWidgetArea) return dock
def create_dockwidget(self): """Add to parent QMainWindow as a dock widget""" # This is not clear yet why the following do not work... # (see Issue #880) ## # Using Qt.Window window flags solves Issue #880 (detached dockwidgets ## # are not painted after restarting Spyder and restoring their hexstate) ## # but it does not work with PyQt <=v4.7 (dockwidgets can't be docked) ## # or non-Windows platforms (lot of warnings are printed out) ## # (so in those cases, we use the default window flags: Qt.Widget): ## flags = Qt.Widget if is_old_pyqt or os.name != 'nt' else Qt.Window dock = QDockWidget(self.get_plugin_title(), self.main) #, flags) dock.setObjectName(self.__class__.__name__ + "_dw") dock.setAllowedAreas(self.ALLOWED_AREAS) dock.setFeatures(self.FEATURES) dock.setWidget(self) self.update_margins() self.connect(dock, SIGNAL('visibilityChanged(bool)'), self.visibility_changed) self.dockwidget = dock short = self.get_option("shortcut", None) if short is not None: shortcut = QShortcut(QKeySequence(short), self.main, self.switch_to_plugin) self.register_shortcut(shortcut, "_", "Switch to %s" % self.CONF_SECTION, default=short) return (dock, self.LOCATION)
class Main(plugin.Plugin): " dock Class " def initialize(self): " Init Class dock " self.dock = QDockWidget() self.dock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.dock.setWindowTitle(__doc__) self.dock.setStyleSheet('QDockWidget::title{text-align: center;}') self.boton = QPushButton(QIcon.fromTheme("media-eject"), ' Open Media ', self.dock) try: self.factory = KPluginLoader("dragonpart").factory() self.part = self.factory.create(self) self.boton.clicked.connect(lambda: self.part.openUrl(KUrl(str( QFileDialog.getOpenFileName(self.dock, ' Open Media File ', path.expanduser("~"), ';;'.join(['(*.{})'.format(e) for e in ['ogv', 'webm', 'avi', 'mpg', 'mpeg', '3gp', 'wmv', 'mp3', 'asf', 'dat', 'flv', 'flac', 'ogg', 'mkv', 'mov', 'swf', 'wav', 'rm', 'm4v', 'aaf', 'mp4', 'raw', '*']])))))) self.dock.setWidget(self.part.widget()) except: self.dock.setWidget(QLabel(""" <center> <h3>ಠ_ಠ<br> ERROR: Please, install Dragon Player and PyKDE ! </h3><br> <br><i> (Sorry, cant embed non-Qt Apps). </i>><center>""")) self.misc = self.locator.get_service('misc') self.misc.add_widget(self.dock, QIcon.fromTheme("applications-multimedia"), __doc__)
def _createDock(self): dock = QDockWidget("") dock.setObjectName("ERTGUI Workflow") dock.setWidget(self.contentsWidget) dock.setFeatures(QDockWidget.DockWidgetClosable) dock.setAllowedAreas(Qt.LeftDockWidgetArea) return dock
def create_dockwidget(self): """Add to parent QMainWindow as a dock widget""" # This is not clear yet why the following do not work... # (see Issue #880) ## # Using Qt.Window window flags solves Issue #880 (detached dockwidgets ## # are not painted after restarting Spyder and restoring their hexstate) ## # but it does not work with PyQt <=v4.7 (dockwidgets can't be docked) ## # or non-Windows platforms (lot of warnings are printed out) ## # (so in those cases, we use the default window flags: Qt.Widget): ## flags = Qt.Widget if is_old_pyqt or os.name != 'nt' else Qt.Window dock = QDockWidget(self.get_plugin_title(), self.main)#, flags) dock.setObjectName(self.__class__.__name__+"_dw") dock.setAllowedAreas(self.ALLOWED_AREAS) dock.setFeatures(self.FEATURES) dock.setWidget(self) self.update_margins() self.connect(dock, SIGNAL('visibilityChanged(bool)'), self.visibility_changed) self.dockwidget = dock short = self.get_option("shortcut", None) if short is not None: shortcut = QShortcut(QKeySequence(short), self.main, self.switch_to_plugin) self.register_shortcut(shortcut, "_", "Switch to %s" % self.CONF_SECTION, default=short) return (dock, self.LOCATION)
class Main(plugin.Plugin): " dock Class " def initialize(self): " Init Class dock " self.dock = QDockWidget() self.dock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.dock.setWindowTitle(__doc__) self.dock.setStyleSheet('QDockWidget::title{text-align: center;}') self.boton = QPushButton(QIcon.fromTheme("document-open-recent"), 'Edit Track', self.dock) self.boton.setToolTip('Edit iCal: {}'.format(TRACK_FILE)) try: self.factory = KPluginLoader("ktimetrackerpart").factory() self.part = self.factory.create(self) self.part.setReadWrite(True) self.part.closeUrl() self.part.openUrl(KUrl(str(TRACK_FILE))) self.boton.clicked.connect(lambda: call('xdg-open {}'.format(TRACK_FILE), shell=True)) self.dock.setWidget(self.part.widget()) except: self.dock.setWidget(QLabel(""" <center> <h3>ಠ_ಠ<br> ERROR: Please, install kTimeTracker and PyKDE ! </h3><br> <br><i> (Sorry, cant embed non-Qt Apps). </i><center>""")) self.misc = self.locator.get_service('misc') self.misc.add_widget(self.dock, QIcon.fromTheme("user-away"), __doc__)
def setupUI(self): self.browser = browser = SimpleServerBrowser() self.player = player = video.Player() #VideoPlayer() dock = QDockWidget(u'Przeglądarka serwerów', self) dock.setContentsMargins(0, 0, 0, 0) dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) dock.setWidget(browser) self.addDockWidget(Qt.LeftDockWidgetArea, dock) #self.setCentralWidget(player.videoWidget()) self.setCentralWidget(player.videoWidget) dock = QDockWidget(self) dock.setAllowedAreas(Qt.BottomDockWidgetArea) dock.setWidget(player.controlPanel) dock.setFloating(False) dock.setFeatures(QDockWidget.NoDockWidgetFeatures) self.addDockWidget(Qt.BottomDockWidgetArea, dock) QWidget.connect(self.browser, SIGNAL("play"), self.play) self.resize(800, 480)
def addDock(self, name, widget, area=Qt.LeftDockWidgetArea, allowed_areas=Qt.AllDockWidgetAreas): dock_widget = QDockWidget(name) dock_widget.setObjectName("%sDock" % name) dock_widget.setWidget(widget) dock_widget.setAllowedAreas(allowed_areas) dock_widget.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.addDockWidget(area, dock_widget) return dock_widget
def createDock(self, widgetClass, widgetName, widgetArea): """创建停靠组件""" widget = widgetClass(self.mainEngine, self.eventEngine) dock = QDockWidget(widgetName) dock.setWidget(widget) dock.setObjectName(widgetName) dock.setFeatures(dock.DockWidgetFloatable | dock.DockWidgetMovable) self.addDockWidget(widgetArea, dock) return widget, dock
def __createDockWindow(self, name): """ Private method to create a dock window with common properties. @param name object name of the new dock window (string) @return the generated dock window (QDockWindow) """ dock = QDockWidget() dock.setObjectName(name) dock.setFeatures( QDockWidget.DockWidgetFeatures(QDockWidget.AllDockWidgetFeatures)) return dock
class Main(plugin.Plugin): " dock Class " def initialize(self): " Init Class dock " self.dock = QDockWidget() self.dock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.dock.setWindowTitle(__doc__) self.dock.setStyleSheet('QDockWidget::title{text-align: center;}') self.open = QAction(QIcon.fromTheme("document-open"), 'Open DIFF', self) self.diff = QAction(QIcon.fromTheme("document-new"), 'Make DIFF', self) self.diff.triggered.connect(self.run_gui_and_get_results) self.save = QAction(QIcon.fromTheme("document-save"), 'Save DIFF', self) self.save.triggered.connect(self.save_a_diff) self.patc = QAction(QIcon.fromTheme("document-edit"), 'PATCH it!', self) self.patc.triggered.connect(lambda: QMessageBox.information(self.dock, __doc__, ' Sorry. This Feature is not ready yet !, thank you... ')) QToolBar(self.dock).addActions((self.open, self.diff, self.save, self.patc)) try: self.factory = KPluginLoader("komparepart").factory() self.part = self.factory.create(self) self.dock.setWidget(self.part.widget()) self.open.triggered.connect(lambda: self.part.openUrl(KUrl(str( QFileDialog.getOpenFileName(self.dock, ' Open a DIFF file ', path.expanduser("~"), ';;(*.diff)'))))) except: self.dock.setWidget(QLabel(""" <center> <h3>ಠ_ಠ<br> ERROR: Please, install Kompare and PyKDE ! </h3><br> <br><i> (Sorry, cant embed non-Qt Apps). </i><center>""")) self.misc = self.locator.get_service('misc') self.misc.add_widget(self.dock, QIcon.fromTheme("edit-select-all"), __doc__) def run_gui_and_get_results(self): ' run_gui_and_get_results ' gui = Diff_GUI() if gui.diff_path is not None and path.isfile(gui.diff_path) is True: self.part.openUrl(KUrl(str(gui.diff_path))) return gui.diff_path def save_a_diff(self): ' save a diff ' out_file = path.abspath(str(QFileDialog.getSaveFileName(self.dock, 'Save a Diff file', path.expanduser("~"), ';;(*.diff)'))) inp_file = file(str(QUrl( self.part.url()).toString()).replace('file://', ''), 'r').read() end_file = file(out_file, 'w') end_file.write(inp_file) end_file.close()
class Main(plugin.Plugin): " dock Class " def initialize(self): " Init Class dock " self.dock = QDockWidget() self.dock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.dock.setWindowTitle(__doc__) self.dock.setStyleSheet('QDockWidget::title{text-align: center;}') self.boton = QAction(QIcon.fromTheme("list-add"), 'Open', self) self.saver = QAction(QIcon.fromTheme("document-save"), 'Save', self) self.apiss = QAction(QIcon.fromTheme("help"), 'Python API Help', self) QToolBar(self.dock).addActions((self.boton, self.saver, self.apiss)) try: self.factory = KPluginLoader("kigpart").factory() self.part = self.factory.create(self) self.part.setReadWrite(True) self.boton.triggered.connect(lambda: self.part.openUrl( KUrl( str( QFileDialog.getOpenFileName( self.dock, ' Open Geometry Plot ', path.expanduser("~"), ';;'.join([ '(*.{})'.format(e) for e in ['fig', 'kig', 'kigz', 'seg', 'fgeo'] ])))))) self.saver.triggered.connect(lambda: self.part.saveAs( KUrl( str( QFileDialog.getSaveFileName( self.dock, ' Save Geometry Plot ', path.expanduser("~"), ';;'.join([ '(*.{})'.format(e) for e in ['kig', 'kigz', 'fig'] ])))))) self.apiss.triggered.connect(lambda: open_new_tab( 'http://edu.kde.org/kig/manual/scripting-api/classObject.html') ) self.dock.setWidget(self.part.widget()) except: self.dock.setWidget( QLabel(""" <center> <h3>ಠ_ಠ<br> ERROR: Please, install KIG and PyKDE ! </h3><br> <br><i> (Sorry, cant embed non-Qt Apps). </i><center>""")) self.misc = self.locator.get_service('misc') self.misc.add_widget(self.dock, QIcon.fromTheme("accessories-calculator"), __doc__)
def create_dockwidget(self): """Add to parent QMainWindow as a dock widget""" dock = QDockWidget(self.get_plugin_title(), self.main)#, self.FLAGS) -> bug in Qt 4.4 dock.setObjectName(self.__class__.__name__+"_dw") dock.setAllowedAreas(self.ALLOWED_AREAS) dock.setFeatures(self.FEATURES) dock.setWidget(self) self.connect(dock, SIGNAL('visibilityChanged(bool)'), self.visibility_changed) self.dockwidget = dock self.refresh_plugin() short = CONF.get(self.ID, "shortcut", None) if short is not None: QShortcut(QKeySequence(short), self.main, lambda: self.visibility_changed(True)) return (dock, self.LOCATION)
def create_dockwidget(self): """Add to parent QMainWindow as a dock widget""" dock = QDockWidget(self.get_widget_title(), self.main)#, self.flags) -> bug in Qt 4.4 dock.setObjectName(self.__class__.__name__+"_dw") dock.setAllowedAreas(self.allowed_areas) dock.setFeatures(self.features) dock.setWidget(self) self.connect(dock, SIGNAL('visibilityChanged(bool)'), self.visibility_changed) self.dockwidget = dock self.refresh() short = CONF.get(self.ID, "shortcut", None) if short is not None: QShortcut(QKeySequence(short), self.main, lambda: self.visibility_changed(True)) return (dock, self.location)
class Terminal(plugin.Plugin): " Terminal Class " def initialize(self): " Init Class Terminal " self.terminal = QDockWidget() self.terminal.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.terminal.setWindowTitle(__doc__) self.terminal.setStyleSheet('QDockWidget::title{text-align: center;}') try: self.factory = KPluginLoader("libkonsolepart").factory() self.terminal.setWidget(self.factory.create(self).widget()) except: self.terminal.setWidget(QLabel(""" <center> <h3>ಠ_ಠ<br> ERROR: Please, install Konsole Terminal and PyKDE ! </h3><br> <br><i> (Sorry, cant embed non-Qt Terminal Apps). </i><center>""")) self.misc = self.locator.get_service('misc') self.misc.add_widget(self.terminal, QIcon.fromTheme("utilities-terminal"), __doc__)
def __init__(self, game, parent = None): super(MainWindow, self).__init__(parent) self.game = game self.boardWidget = BoardWidget(self.game) self.chat = Chat() self.playerInfo = PlayerInfo(self.game) playerDock = QDockWidget("Players") playerDock.setWidget(self.playerInfo) playerDock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable) self.addDockWidget(Qt.RightDockWidgetArea, playerDock) chatDock = QDockWidget("Chat") chatDock.setWidget(self.chat) chatDock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable) self.addDockWidget(Qt.RightDockWidgetArea, chatDock) self.setCentralWidget(self.boardWidget) self.statusBar() self.initMenuBar()
def __init__(self, content, plot_labels, plot_name, *args, parent=None, **kwargs): """ Initialisation of the PlotContainer widget. content - Content for the plotcontainer plot_lables - Labels of the plot widget plot_name - Name of the associated software parent - Parent widget (default None) Returns: None """ super(PlotContainer, self).__init__(parent) self.setCentralWidget(None) self.setTabPosition(Qt.TopDockWidgetArea, QTabWidget.North) self.plot_name = plot_name self.content = [] dock_widgets = [] for label in plot_labels: label = label[0] if label == 'mic_number': continue elif label == 'file_name': continue else: pass widget = PlotWidget(label=label, plot_typ=content, parent=self) self.content.append(widget) dock_widget = QDockWidget(label, self) dock_widget.setWidget(widget) dock_widget.installEventFilter(self) dock_widget.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) dock_widgets.append(dock_widget) self.addDockWidget(Qt.TopDockWidgetArea, dock_widget, Qt.Horizontal) for idx in range(1, len(dock_widgets)): self.tabifyDockWidget(dock_widgets[0], dock_widgets[idx])
def __init__(self): super().__init__() daq = Traces(args) dock_daq = QDockWidget('DAQ', self) dock_daq.setWidget(daq) dock_daq.setObjectName('DAQ') dock_daq.setFeatures(QDockWidget.DockWidgetMovable) self.addDockWidget(Qt.TopDockWidgetArea, dock_daq) widgets = { 'daq': daq, } self.controlpanel = ControlPanel(widgets) self.setCentralWidget(self.controlpanel) window_geometry = settings.value('window/geometry') if window_geometry is not None: self.restoreGeometry(window_geometry) window_state = settings.value('window/state') if window_state is not None: self.restoreState(window_state, float(VERSION))
class Main(plugin.Plugin): " dock Class " def initialize(self): " Init Class dock " self.dock = QDockWidget() self.dock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.dock.setWindowTitle(__doc__) self.dock.setStyleSheet('QDockWidget::title{text-align: center;}') self.boton = QAction(QIcon.fromTheme("list-add"), 'Open', self) self.saver = QAction(QIcon.fromTheme("document-save"), 'Save', self) self.apiss = QAction(QIcon.fromTheme("help"), 'Python API Help', self) QToolBar(self.dock).addActions((self.boton, self.saver, self.apiss)) try: self.factory = KPluginLoader("kigpart").factory() self.part = self.factory.create(self) self.part.setReadWrite(True) self.boton.triggered.connect(lambda: self.part.openUrl(KUrl(str( QFileDialog.getOpenFileName(self.dock, ' Open Geometry Plot ', path.expanduser("~"), ';;'.join(['(*.{})'.format(e) for e in ['fig', 'kig', 'kigz', 'seg', 'fgeo']])))))) self.saver.triggered.connect(lambda: self.part.saveAs(KUrl(str( QFileDialog.getSaveFileName(self.dock, ' Save Geometry Plot ', path.expanduser("~"), ';;'.join(['(*.{})'.format(e) for e in ['kig', 'kigz', 'fig']]) ))))) self.apiss.triggered.connect(lambda: open_new_tab( 'http://edu.kde.org/kig/manual/scripting-api/classObject.html')) self.dock.setWidget(self.part.widget()) except: self.dock.setWidget(QLabel(""" <center> <h3>ಠ_ಠ<br> ERROR: Please, install KIG and PyKDE ! </h3><br> <br><i> (Sorry, cant embed non-Qt Apps). </i><center>""")) self.misc = self.locator.get_service('misc') self.misc.add_widget(self.dock, QIcon.fromTheme("accessories-calculator"), __doc__)
class Main(plugin.Plugin): " dock Class " def initialize(self): " Init Class dock " self.dock = QDockWidget() self.dock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.dock.setWindowTitle(__doc__) self.dock.setStyleSheet("QDockWidget::title{text-align: center;}") self.scrollable = QScrollArea() self.dock.setWidget(self.scrollable) try: self.factory = KPluginLoader("klinkstatuspart").factory() self.scrollable.setWidget(self.factory.create(self).widget()) except: self.scrollable.setWidget( QLabel( """ <center> <h3>ಠ_ಠ<br> ERROR: Please, install KLinkCheck and PyKDE ! </h3><br> <br><i> (Sorry, cant embed non-Qt Apps). </i><center>""" ) ) self.misc = self.locator.get_service("misc") self.misc.add_widget(self.dock, QIcon.fromTheme("insert-link"), __doc__)
class DBManager(QMainWindow): def __init__(self, iface, parent=None): QMainWindow.__init__(self, parent) self.setAttribute(Qt.WA_DeleteOnClose) self.setupUi() self.iface = iface # restore the window state settings = QSettings() self.restoreGeometry(settings.value("/DB_Manager/mainWindow/geometry", QByteArray(), type=QByteArray)) self.restoreState(settings.value("/DB_Manager/mainWindow/windowState", QByteArray(), type=QByteArray)) self.connect(self.tabs, SIGNAL("currentChanged(int)"), self.tabChanged) self.connect(self.tree, SIGNAL("selectedItemChanged"), self.itemChanged) self.itemChanged(None) def closeEvent(self, e): self.unregisterAllActions() # save the window state settings = QSettings() settings.setValue("/DB_Manager/mainWindow/windowState", self.saveState()) settings.setValue("/DB_Manager/mainWindow/geometry", self.saveGeometry()) QMainWindow.closeEvent(self, e) def refreshItem(self, item=None): QApplication.setOverrideCursor(Qt.WaitCursor) try: if item is None: item = self.tree.currentItem() self.tree.refreshItem(item) # refresh item children in the db tree except BaseError as e: DlgDbError.showError(e, self) return finally: QApplication.restoreOverrideCursor() def itemChanged(self, item): QApplication.setOverrideCursor(Qt.WaitCursor) try: self.reloadButtons() self.refreshTabs() except BaseError as e: DlgDbError.showError(e, self) return finally: QApplication.restoreOverrideCursor() def reloadButtons(self): db = self.tree.currentDatabase() if not hasattr(self, '_lastDb'): self._lastDb = db elif db == self._lastDb: return # remove old actions if self._lastDb is not None: self.unregisterAllActions() # add actions of the selected database self._lastDb = db if self._lastDb is not None: self._lastDb.registerAllActions(self) def tabChanged(self, index): QApplication.setOverrideCursor(Qt.WaitCursor) try: self.refreshTabs() except BaseError as e: DlgDbError.showError(e, self) return finally: QApplication.restoreOverrideCursor() def refreshTabs(self): index = self.tabs.currentIndex() item = self.tree.currentItem() table = self.tree.currentTable() # enable/disable tabs self.tabs.setTabEnabled(self.tabs.indexOf(self.table), table is not None) self.tabs.setTabEnabled(self.tabs.indexOf(self.preview), table is not None and table.type in [table.VectorType, table.RasterType] and table.geomColumn is not None) # show the info tab if the current tab is disabled if not self.tabs.isTabEnabled(index): self.tabs.setCurrentWidget(self.info) current_tab = self.tabs.currentWidget() if current_tab == self.info: self.info.showInfo(item) elif current_tab == self.table: self.table.loadData(item) elif current_tab == self.preview: self.preview.loadPreview(item) def refreshActionSlot(self): self.info.setDirty() self.table.setDirty() self.preview.setDirty() self.refreshItem() def importActionSlot(self): db = self.tree.currentDatabase() if db is None: self.infoBar.pushMessage(self.tr("No database selected or you are not connected to it."), QgsMessageBar.INFO, self.iface.messageTimeout()) return outUri = db.uri() schema = self.tree.currentSchema() if schema: outUri.setDataSource(schema.name, "", "", "") from .dlg_import_vector import DlgImportVector dlg = DlgImportVector(None, db, outUri, self) dlg.exec_() def exportActionSlot(self): table = self.tree.currentTable() if table is None: self.infoBar.pushMessage(self.tr("Select the table you want export to file."), QgsMessageBar.INFO, self.iface.messageTimeout()) return inLayer = table.toMapLayer() from .dlg_export_vector import DlgExportVector dlg = DlgExportVector(inLayer, table.database(), self) dlg.exec_() inLayer.deleteLater() def runSqlWindow(self): db = self.tree.currentDatabase() if db is None: self.infoBar.pushMessage(self.tr("No database selected or you are not connected to it."), QgsMessageBar.INFO, self.iface.messageTimeout()) # force displaying of the message, it appears on the first tab (i.e. Info) self.tabs.setCurrentIndex(0) return from dlg_sql_window import DlgSqlWindow query = DlgSqlWindow(self.iface, db, self) dbname = db.connection().connectionName() tabname = self.tr("Query") + u" (%s)" % dbname index = self.tabs.addTab(query, tabname) self.tabs.setTabIcon(index, db.connection().icon()) self.tabs.setCurrentIndex(index) query.nameChanged.connect(functools.partial(self.update_query_tab_name, index, dbname)) def update_query_tab_name(self, index, dbname, queryname): if not queryname: queryname = self.tr("Query") tabname = u"%s (%s)" % (queryname, dbname) self.tabs.setTabText(index, tabname) def showSystemTables(self): self.tree.showSystemTables(self.actionShowSystemTables.isChecked()) def registerAction(self, action, menuName, callback=None): """ register an action to the manager's main menu """ if not hasattr(self, '_registeredDbActions'): self._registeredDbActions = {} if callback is not None: invoke_callback = lambda x: self.invokeCallback(callback) if menuName is None or menuName == "": self.addAction(action) if menuName not in self._registeredDbActions: self._registeredDbActions[menuName] = list() self._registeredDbActions[menuName].append(action) if callback is not None: QObject.connect(action, SIGNAL("triggered(bool)"), invoke_callback) return True # search for the menu actionMenu = None helpMenuAction = None for a in self.menuBar.actions(): if not a.menu() or a.menu().title() != menuName: continue if a.menu() != self.menuHelp: helpMenuAction = a actionMenu = a break # not found, add a new menu before the help menu if actionMenu is None: menu = QMenu(menuName, self) if helpMenuAction is not None: actionMenu = self.menuBar.insertMenu(helpMenuAction, menu) else: actionMenu = self.menuBar.addMenu(menu) menu = actionMenu.menu() menuActions = menu.actions() # get the placeholder's position to insert before it pos = 0 for pos in range(len(menuActions)): if menuActions[pos].isSeparator() and menuActions[pos].objectName().endswith("_placeholder"): menuActions[pos].setVisible(True) break if pos < len(menuActions): before = menuActions[pos] menu.insertAction(before, action) else: menu.addAction(action) actionMenu.setVisible(True) # show the menu if menuName not in self._registeredDbActions: self._registeredDbActions[menuName] = list() self._registeredDbActions[menuName].append(action) if callback is not None: QObject.connect(action, SIGNAL("triggered(bool)"), invoke_callback) return True def invokeCallback(self, callback, *params): """ Call a method passing the selected item in the database tree, the sender (usually a QAction), the plugin mainWindow and optionally additional parameters. This method takes care to override and restore the cursor, but also catches exceptions and displays the error dialog. """ QApplication.setOverrideCursor(Qt.WaitCursor) try: callback(self.tree.currentItem(), self.sender(), self, *params) except BaseError as e: # catch database errors and display the error dialog DlgDbError.showError(e, self) return finally: QApplication.restoreOverrideCursor() def unregisterAction(self, action, menuName): if not hasattr(self, '_registeredDbActions'): return if menuName is None or menuName == "": self.removeAction(action) if menuName in self._registeredDbActions: if self._registeredDbActions[menuName].count(action) > 0: self._registeredDbActions[menuName].remove(action) action.deleteLater() return True for a in self.menuBar.actions(): if not a.menu() or a.menu().title() != menuName: continue menu = a.menu() menuActions = menu.actions() menu.removeAction(action) if menu.isEmpty(): # hide the menu a.setVisible(False) if menuName in self._registeredDbActions: if self._registeredDbActions[menuName].count(action) > 0: self._registeredDbActions[menuName].remove(action) # hide the placeholder if there're no other registered actions if len(self._registeredDbActions[menuName]) <= 0: for i in range(len(menuActions)): if menuActions[i].isSeparator() and menuActions[i].objectName().endswith("_placeholder"): menuActions[i].setVisible(False) break action.deleteLater() return True return False def unregisterAllActions(self): if not hasattr(self, '_registeredDbActions'): return for menuName in self._registeredDbActions: for action in list(self._registeredDbActions[menuName]): self.unregisterAction(action, menuName) del self._registeredDbActions def close_tab(self, index): widget = self.tabs.widget(index) if widget not in [self.info, self.table, self.preview]: self.tabs.removeTab(index) widget.deleteLater() def setupUi(self): self.setWindowTitle(self.tr("DB Manager")) self.setWindowIcon(QIcon(":/db_manager/icon")) self.resize(QSize(700, 500).expandedTo(self.minimumSizeHint())) # create central tab widget and add the first 3 tabs: info, table and preview self.tabs = QTabWidget() self.info = InfoViewer(self) self.tabs.addTab(self.info, self.tr("Info")) self.table = TableViewer(self) self.tabs.addTab(self.table, self.tr("Table")) self.preview = LayerPreview(self) self.tabs.addTab(self.preview, self.tr("Preview")) self.setCentralWidget(self.tabs) # display close button for all tabs but the first 3 ones, i.e. # HACK: just hide the close button where not needed (GS) self.tabs.setTabsClosable(True) self.tabs.tabCloseRequested.connect(self.close_tab) tabbar = self.tabs.tabBar() for i in range(3): btn = tabbar.tabButton(i, QTabBar.RightSide) if tabbar.tabButton(i, QTabBar.RightSide) else tabbar.tabButton(i, QTabBar.LeftSide) btn.resize(0, 0) btn.hide() # Creates layout for message bar self.layout = QGridLayout(self.info) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # init messageBar instance self.infoBar = QgsMessageBar(self.info) sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # create database tree self.dock = QDockWidget("Tree", self) self.dock.setObjectName("DB_Manager_DBView") self.dock.setFeatures(QDockWidget.DockWidgetMovable) self.tree = DBTree(self) self.dock.setWidget(self.tree) self.addDockWidget(Qt.LeftDockWidgetArea, self.dock) # create status bar self.statusBar = QStatusBar(self) self.setStatusBar(self.statusBar) # create menus self.menuBar = QMenuBar(self) self.menuDb = QMenu(self.tr("&Database"), self) self.menuBar.addMenu(self.menuDb) self.menuSchema = QMenu(self.tr("&Schema"), self) actionMenuSchema = self.menuBar.addMenu(self.menuSchema) self.menuTable = QMenu(self.tr("&Table"), self) actionMenuTable = self.menuBar.addMenu(self.menuTable) self.menuHelp = None # QMenu(self.tr("&Help"), self) # actionMenuHelp = self.menuBar.addMenu(self.menuHelp) self.setMenuBar(self.menuBar) # create toolbar self.toolBar = QToolBar("Default", self) self.toolBar.setObjectName("DB_Manager_ToolBar") self.addToolBar(self.toolBar) # create menus' actions # menu DATABASE sep = self.menuDb.addSeparator() sep.setObjectName("DB_Manager_DbMenu_placeholder") sep.setVisible(False) self.actionRefresh = self.menuDb.addAction(QIcon(":/db_manager/actions/refresh"), self.tr("&Refresh"), self.refreshActionSlot, QKeySequence("F5")) self.actionSqlWindow = self.menuDb.addAction(QIcon(":/db_manager/actions/sql_window"), self.tr("&SQL window"), self.runSqlWindow, QKeySequence("F2")) self.menuDb.addSeparator() self.actionClose = self.menuDb.addAction(QIcon(), self.tr("&Exit"), self.close, QKeySequence("CTRL+Q")) # menu SCHEMA sep = self.menuSchema.addSeparator() sep.setObjectName("DB_Manager_SchemaMenu_placeholder") sep.setVisible(False) actionMenuSchema.setVisible(False) # menu TABLE sep = self.menuTable.addSeparator() sep.setObjectName("DB_Manager_TableMenu_placeholder") sep.setVisible(False) self.actionImport = self.menuTable.addAction(QIcon(":/db_manager/actions/import"), self.tr("&Import layer/file"), self.importActionSlot) self.actionExport = self.menuTable.addAction(QIcon(":/db_manager/actions/export"), self.tr("&Export to file"), self.exportActionSlot) self.menuTable.addSeparator() #self.actionShowSystemTables = self.menuTable.addAction(self.tr("Show system tables/views"), self.showSystemTables) #self.actionShowSystemTables.setCheckable(True) #self.actionShowSystemTables.setChecked(True) actionMenuTable.setVisible(False) # add actions to the toolbar self.toolBar.addAction(self.actionRefresh) self.toolBar.addAction(self.actionSqlWindow) self.toolBar.addAction(self.actionImport) self.toolBar.addAction(self.actionExport)
class GUI(QMainWindow): def __init__(self, debug=False): QMainWindow.__init__(self) # initialise filename self.filename = None # make the data store from xmlstore import Store self.store = Store(debug = debug) # view the current table self.tableView = TableView(self) self.tableView.setDragEnabled(True); self.tableView.setAcceptDrops(True); self.tableView.setDropIndicatorShown(True); self.setCentralWidget(self.tableView) # add a custom delegate to it self.delegate = ComboBoxDelegate() self.tableView.setItemDelegate(self.delegate) # dock the table selection on the left self.dock1 = QDockWidget(self) self.listView = ListView(self) self.listView.setDragEnabled(True); self.listView.setDragDropMode(QAbstractItemView.InternalMove) self.listView.setDropIndicatorShown(True); self.dock1.setWidget(self.listView) self.dock1.setFeatures( QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.addDockWidget(Qt.LeftDockWidgetArea, self.dock1) # connect it to the populate method self.connect(self.listView, SIGNAL('activated ( const QModelIndex & )'), self.populate) self.connect(self.listView, SIGNAL('clicked ( const QModelIndex & )'), self.populate) # dock the undoView on the left self.dock2 = QDockWidget(self) self.undoView = QUndoView(self.store.stack) self.dock2.setWidget(self.undoView) self.dock2.setFeatures( QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.dock2.setWindowTitle('Undo Stack') self.addDockWidget(Qt.LeftDockWidgetArea, self.dock2) # create a menubar self.menu = self.menuBar() # create file menu headings self.menuFile = self.menu.addMenu('File') self.menuFile.addAction('New', self.New).setShortcut('CTRL+N') self.menuFile.addAction('Open...', self.Open).setShortcut('CTRL+O') self.menuFile.addAction('Reload', self.Reload) self.menuFile.addAction('Save', self.Save).setShortcut('CTRL+S') self.menuFile.addAction('Save As...', self.SaveAs) self.menuFile.addSeparator() self.menuFile.addAction('Set Architecture...', self.setArch) self.menuFile.addSeparator() self.menuFile.addAction('Quit', self.closeEvent).setShortcuts(['CTRL+Q', 'ALT+F4']) # create edit menu headings self.menuEdit = self.menu.addMenu('Edit') self.menuEdit.addAction('Insert Row', self.tableView.insertRow).setShortcut('CTRL+I') self.menuEdit.addAction('Insert Row Under', self.tableView.insertRowUnder).setShortcut('CTRL+U') self.menuEdit.addAction('Remove Row', self.tableView.removeRow) self.menuEdit.addSeparator() self.menuEdit.addAction('Cut', self.tableView.cut).setShortcut('CTRL+X') self.menuEdit.addAction('Copy', self.tableView.copy).setShortcuts(['CTRL+C', 'CTRL+INS']) self.menuEdit.addAction('Paste', self.tableView.paste).setShortcuts(['CTRL+V', 'SHIFT+INS']) self.menuEdit.addAction('Clear', self.tableView.menuClear).setShortcut('CTRL+D') self.menuEdit.addSeparator() self.menuEdit.addAction('Fill Cells', self.tableView.fillCells).setShortcut('CTRL+L') self.menuEdit.addAction('Fill Cells and Increment', self.tableView.fillCellsInc).setShortcut('CTRL+R') self.menuEdit.addAction('Python Code...', self.tableView.pythonCode).setShortcut('CTRL+P') self.tableView.codeBox = pythonCode() self.menuEdit.addSeparator() self.menuEdit.addAction('Undo', self.store.stack, SLOT('undo()')).setShortcut('CTRL+Z') self.menuEdit.addAction('Redo', self.store.stack, SLOT('redo()')).setShortcut('CTRL+SHIFT+Z') # create component menu self.menuComponents = self.menu.addMenu('Components') self.resize(QSize(1000,500)) def Save(self): # save menu command self.SaveAs(self.filename) def setArch(self): arch = self.store.getArch() arch, ok = QInputDialog.getText(self, 'Architecture Dialog', 'Enter Architecture', QLineEdit.Normal, arch) arch = str(arch) if ok: self.store.setArch(arch) def SaveAs(self, filename = ''): # save as menu command if filename == '': filename = str(QFileDialog.getSaveFileName()) if filename != '': self.filename = filename self.store.Save(filename) self._setClean() def Reload(self): if self.filename is not None: self.Open(self.filename, name = self.tablename) def Open(self, filename = '', name = None): # make sure the user is sure if there are unsaved changes if self.__prompt_unsaved() != QMessageBox.Yes: return # ask for a filename if filename == '': filename = str(QFileDialog.getOpenFileName()) if filename == '': return # store the filename self.filename = filename # tell the store to open a new set of tables try: problems, warnings = self.store.Open(filename) if problems: errorstr = 'Can\'t load all object types: '+', '.join(problems) QMessageBox.warning(self,'Open Error',errorstr) if warnings: errorstr = \ 'The following warnings were generated:\n' + \ '\n'.join(warnings) QMessageBox.warning(self,'Open Warnings',errorstr) except Exception, e: x = formLog('An error ocurred. Make sure all the modules listed ' 'in RELEASE files are built. Check the text below for ' 'details:\n\n' + traceback.format_exc(), self) x.show() return # populate self.setWindowTitle('XEB - %s[*]'%filename) self.listView.clear() for t in self.store.getTableNames(): self.__insertListViewItem(t) self.__populateMenu() self.populate(name = name) self._setClean()
class ComposerWrapper(QObject): """ Embeds custom STDM tools in a QgsComposer instance for managing map-based STDM document templates. """ dataSourceSelected = pyqtSignal(str) def __init__(self,composerView): QObject.__init__(self,composerView) self._compView = composerView self._stdmTB = self.mainWindow().addToolBar("STDM") self._selectMoveAction = None #Container for custom editor widgets self._widgetMappings = {} #Create dock widget for configuring STDM data source self._stdmDataSourceDock = QDockWidget(QApplication.translate("ComposerWrapper","STDM Data Source"),self.mainWindow()) self._stdmDataSourceDock.setObjectName("STDMDataSourceDock") self._stdmDataSourceDock.setMinimumWidth(300) self._stdmDataSourceDock.setFeatures(QDockWidget.DockWidgetMovable|QDockWidget.DockWidgetClosable) self.mainWindow().addDockWidget(Qt.RightDockWidgetArea,self._stdmDataSourceDock) dataSourceWidget = ComposerDataSourceSelector() self._stdmDataSourceDock.setWidget(dataSourceWidget) self._stdmDataSourceDock.show() #Create dock widget for configuring STDM item properties self._stdmItemPropDock = QDockWidget(QApplication.translate("ComposerWrapper","STDM data properties"),self.mainWindow()) self._stdmItemPropDock.setObjectName("STDMItemDock") self._stdmItemPropDock.setMinimumWidth(300) self._stdmItemPropDock.setFeatures(QDockWidget.DockWidgetMovable|QDockWidget.DockWidgetClosable) self.mainWindow().addDockWidget(Qt.RightDockWidgetArea,self._stdmItemPropDock) self._stdmItemPropDock.show() if self.itemDock() != None: self.mainWindow().tabifyDockWidget(self.itemDock(),self._stdmItemPropDock) if self.atlasDock() != None: self.atlasDock().hide() if self.generalDock() != None: self.generalDock().raise_() #Connect signals self.composition().itemRemoved.connect(self._onItemRemoved) dataSourceWidget.cboDataSource.currentIndexChanged[str].connect(self.propagateDataSourceSelection) self.composerView().selectedItemChanged.connect(self._onItemSelected) #Current template document file self._currDocFile = None def _removeActions(self): """ Remove inapplicable actions and their corresponding toolbars and menus. """ removeActions = ["mActionSaveProject","mActionNewComposer","mActionDuplicateComposer"] composerToolbar = self.composerMainToolBar() if composerToolbar != None: saveProjectAction = None for itemAction in composerToolbar.actions(): if itemAction.objectName() == "mActionSaveProject": saveProjectAction = itemAction break if saveProjectAction != None: composerMenu = saveProjectAction.menu() def configure(self): #Create instances of custom STDM composer item configurations for ciConfig in ComposerItemConfig.itemConfigurations: ciConfigObj = ciConfig(self) def addWidgetMapping(self,uniqueIdentifier,widget): """ Add custom STDM editor widget based on the unique identifier of the composer item """ self._widgetMappings[uniqueIdentifier] = widget def widgetMappings(self): """ Returns a dictionary containing uuid values of composer items linked to STDM widgets. """ return self._widgetMappings def clearWidgetMappings(self): """ Resets the widget mappings collection. """ self._widgetMappings = {} def mainWindow(self): """ Returns the QMainWindow used by the composer view. """ return self._compView.composerWindow() def stdmToolBar(self): """ Returns the instance of the STDM toolbar added to the QgsComposer. """ return self._stdmTB def composerView(self): """ Returns the composer view. """ return self._compView def composition(self): """ Returns the QgsComposition instance used in the composer view. """ return self._compView.composition() def composerItemToolBar(self): """ Returns the toolbar containing actions for adding composer items. """ return self.mainWindow().findChild(QToolBar,"mItemToolbar") def composerMainToolBar(self): """ Returns the toolbar containing actions for managing templates. """ return self.mainWindow().findChild(QToolBar,"mComposerToolbar") def selectMoveAction(self): """ Returns the QAction for selecting or moving composer items. """ if self.composerItemToolBar() != None: if self._selectMoveAction == None: for itemAction in self.composerItemToolBar().actions(): if itemAction.objectName() == "mActionSelectMoveItem": self._selectMoveAction = itemAction break return self._selectMoveAction def checkedItemAction(self): """ Returns the currently selected composer item action. """ if self.selectMoveAction() != None: return self.selectMoveAction().actionGroup().checkedAction() return None def itemDock(self): """ Get the 'Item Properties' dock widget. """ return self.mainWindow().findChild(QDockWidget,"ItemDock") def atlasDock(self): """ Get the 'Atlas generation' dock widget. """ return self.mainWindow().findChild(QDockWidget,"AtlasDock") def generalDock(self): """ Get the 'Composition' dock widget. """ return self.mainWindow().findChild(QDockWidget,"CompositionDock") def stdmDataSourceDock(self): """ Returns the STDM data source dock widget. """ return self._stdmDataSourceDock def stdmItemDock(self): """ Returns the STDM item dock widget. """ return self._stdmItemPropDock def documentFile(self): """ Returns the QFile instance associated with the current document. 'None' will be returned for new, unsaved documents. """ return self._currDocFile def setDocumentFile(self,docFile): """ Sets the document file. """ if not isinstance(docFile,QFile): return self._currDocFile = docFile def selectedDataSource(self): """ Returns the name of the data source specified by the user. """ return self._stdmDataSourceDock.widget().cboDataSource.currentText() def selectedDataSourceCategory(self): """ Returns the category (view or table) that the data source belongs to. """ if self.stdmDataSourceDock().widget() != None: return self.stdmDataSourceDock().widget().category() return "" def propagateDataSourceSelection(self,dataSourceName): """ Propagates the signal when a user select a data source. Listening objects can hook on to it. """ self.dataSourceSelected.emit(dataSourceName) def loadTemplate(self,filePath): """ Loads a document template into the view and updates the necessary STDM-related controls. """ if not QFile.exists(filePath): QMessageBox.critical(self.composerView(), QApplication.translate("OpenTemplateConfig","Open Template Error"), \ QApplication.translate("OpenTemplateConfig","The specified template does not exist.")) return templateFile = QFile(filePath) if not templateFile.open(QIODevice.ReadOnly): QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Open Operation Error"), \ "{0}\n{1}".format(QApplication.translate("ComposerWrapper","Cannot read template file."), \ templateFile.errorString() )) return templateDoc = QDomDocument() if templateDoc.setContent(templateFile): #Load items into the composition and configure STDM data controls self.composition().loadFromTemplate(templateDoc) self.clearWidgetMappings() #Load data controls composerDS = ComposerDataSource.create(templateDoc) self._configureDataControls(composerDS) #Load symbol editors spatialFieldsConfig = SpatialFieldsConfiguration.create(templateDoc) self._configureSpatialSymbolEditor(spatialFieldsConfig) def saveTemplate(self): """ Creates and saves a new document template. """ #Validate if the user has specified the data source if self.selectedDataSource() == "": QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Error"), \ QApplication.translate("ComposerWrapper","Please specify the " \ "data source name for the document composition.")) return #If it is a new unsaved document template then prompt for the document name. docFile = self.documentFile() if docFile == None: docName,ok = QInputDialog.getText(self.composerView(), \ QApplication.translate("ComposerWrapper","Template Name"), \ QApplication.translate("ComposerWrapper","Please enter the template name below"), \ ) if ok and docName != "": templateDir = self._composerTemplatesPath() if templateDir == None: QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Error"), \ QApplication.translate("ComposerWrapper","Directory for document templates could not be found.")) return absPath = templateDir + "/" + docName + ".sdt" docFile= QFile(absPath) else: return docFileInfo = QFileInfo(docFile) if not docFile.open(QIODevice.WriteOnly): QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Save Operation Error"), \ "{0}\n{1}".format(QApplication.translate("ComposerWrapper","Could not save template file."), \ docFile.errorString() )) return templateDoc = QDomDocument() self._writeXML(templateDoc,docFileInfo.completeBaseName()) if docFile.write(templateDoc.toByteArray()) == -1: QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Save Error"), \ QApplication.translate("ComposerWrapper","Could not save template file.")) return docFile.close() self.setDocumentFile(docFile) def _writeXML(self,xmlDoc,docName): """ Write the template configuration into the XML document. """ #Write default composer configuration composerElement = xmlDoc.createElement("Composer") composerElement.setAttribute("title",docName) composerElement.setAttribute("visible",1) xmlDoc.appendChild(composerElement) self.composition().writeXML(composerElement,xmlDoc) #Write STDM data field configurations dataSourceElement = ComposerDataSource.domElement(self, xmlDoc) composerElement.appendChild(dataSourceElement) #Write spatial field configurations spatialColumnsElement = SpatialFieldsConfiguration.domElement(self, xmlDoc) dataSourceElement.appendChild(spatialColumnsElement) def _configureDataControls(self,composerDataSource): """ Configure the data source and data field controls based on the composer data source configuration. """ if self.stdmDataSourceDock().widget() != None: #Set data source dataSourceWidget = self.stdmDataSourceDock().widget() dataSourceWidget.setCategory(composerDataSource.category()) dataSourceWidget.setSelectedSource(composerDataSource.name()) #Set data field controls for composerId in composerDataSource.dataFieldMappings().reverse: #Use composer item id since the uuid is stripped off composerItem = self.composition().getComposerItemById(composerId) if composerItem != None: compFieldSelector = ComposerFieldSelector(self,composerItem,self.composerView()) compFieldSelector.selectFieldName(composerDataSource.dataFieldName(composerId)) #Add widget to the collection but now use the current uuid of the composition item self.addWidgetMapping(composerItem.uuid(),compFieldSelector) def _configureSpatialSymbolEditor(self,spatialFieldConfig): """ Configure symbol editor controls. """ if self.stdmDataSourceDock().widget() != None: for itemId,spFieldsMappings in spatialFieldConfig.spatialFieldsMapping().iteritems(): mapItem = self.composition().getComposerItemById(itemId) if mapItem != None: composerSymbolEditor = ComposerSymbolEditor(self,self.composerView()) composerSymbolEditor.addSpatialFieldMappings(spFieldsMappings) #Add widget to the collection but now use the current uuid of the composer map self.addWidgetMapping(mapItem.uuid(),composerSymbolEditor) def _composerTemplatesPath(self): """ Reads the path of composer templates in the registry. """ regConfig = RegistryConfig() keyName = "ComposerTemplates" valueCollection = regConfig.read([keyName]) if len(valueCollection) == 0: return None else: return valueCollection[keyName] def _onItemRemoved(self,item): """ Slot raised when a composer item is removed from the scene. """ """ Code will not work since a QObject instance is returned instead of a QgsComposerItem if item.uuid() in self._widgetMappings: del self._widgetMappings[item.uuid()] """ pass def _onItemSelected(self,item): """ Slot raised when a composer item is selected. Load the corresponding field selector if the selection is an STDM data field label. QComposerLabel is returned as a QObject in the slot argument hence, we have resorted to capturing the currently selected items in the composition. """ selectedItems = self.composition().selectedComposerItems() if len(selectedItems) == 0: self._stdmItemPropDock.setWidget(None) elif len(selectedItems) == 1: composerItem = selectedItems[0] if composerItem.uuid() in self._widgetMappings: stdmWidget = self._widgetMappings[composerItem.uuid()] if stdmWidget == self._stdmItemPropDock.widget(): return else: self._stdmItemPropDock.setWidget(stdmWidget) #Playing it safe in applying the formatting for the editor controls where applicable itemFormatter = None if isinstance(composerItem,QgsComposerArrow): itemFormatter = LineFormatter() elif isinstance(composerItem,QgsComposerLabel): itemFormatter = DataLabelFormatter() elif isinstance(composerItem,QgsComposerMap): itemFormatter = MapFormatter() if itemFormatter != None: itemFormatter.apply(composerItem,self,True) else: self._stdmItemPropDock.setWidget(None) elif len(selectedItems) > 1: self._stdmItemPropDock.setWidget(None)
class Main(plugin.Plugin): " dock Class " def initialize(self): " Init Class dock " self.dock = QDockWidget() self.dock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.dock.setWindowTitle(__doc__) self.dock.setStyleSheet('QDockWidget::title{text-align: center;}') self.open = QAction(QIcon.fromTheme("document-open"), 'Open DIFF', self) self.diff = QAction(QIcon.fromTheme("document-new"), 'Make DIFF', self) self.diff.triggered.connect(self.run_gui_and_get_results) self.save = QAction(QIcon.fromTheme("document-save"), 'Save DIFF', self) self.save.triggered.connect(self.save_a_diff) self.patc = QAction(QIcon.fromTheme("document-edit"), 'PATCH it!', self) self.patc.triggered.connect(lambda: QMessageBox.information( self.dock, __doc__, ' Sorry. This Feature is not ready yet !, thank you... ')) QToolBar(self.dock).addActions( (self.open, self.diff, self.save, self.patc)) try: self.factory = KPluginLoader("komparepart").factory() self.part = self.factory.create(self) self.dock.setWidget(self.part.widget()) self.open.triggered.connect(lambda: self.part.openUrl( KUrl( str( QFileDialog.getOpenFileName( self.dock, ' Open a DIFF file ', path.expanduser("~"), ';;(*.diff)'))))) except: self.dock.setWidget( QLabel(""" <center> <h3>ಠ_ಠ<br> ERROR: Please, install Kompare App ! </h3><br> <br><i> (Sorry, cant embed non-Qt Apps). </i><center>""")) self.misc = self.locator.get_service('misc') self.misc.add_widget(self.dock, QIcon.fromTheme("edit-select-all"), __doc__) def run_gui_and_get_results(self): ' run_gui_and_get_results ' gui = Diff_GUI() if gui.diff_path is not None and path.isfile(gui.diff_path) is True: self.part.openUrl(KUrl(str(gui.diff_path))) return run_gui.diff_path def save_a_diff(self): ' save a diff ' out_file = path.abspath( str( QFileDialog.getSaveFileName(self.dock, 'Save a Diff file', path.expanduser("~"), ';;(*.diff)'))) inp_file = file( str(QUrl(self.part.url()).toString()).replace('file://', ''), 'r').read() end_file = file(out_file, 'w') end_file.write(inp_file) end_file.close()
slice_settings.inactiveCellsHidden.connect(viewer.hideInactiveCells) slice_settings.currentSliceChanged.connect(viewer.setCurrentSlice) slice_settings.toggleOrthographicProjection.connect(viewer.useOrthographicProjection) slice_settings.toggleLighting.connect(viewer.useLighting) slice_settings.colorScalesChanged.connect(viewer.changeColorScale) slice_settings.regionToggling.connect(viewer.useRegionScaling) slice_settings.toggleInterpolation.connect(viewer.useInterpolationOnData) slice_settings.mirrorX.connect(viewer.mirrorX) slice_settings.mirrorY.connect(viewer.mirrorY) slice_settings.mirrorZ.connect(viewer.mirrorZ) slice_settings.toggleFlatPolylines.connect(viewer.toggleFlatPolylines) dock_widget = QDockWidget("Settings") dock_widget.setObjectName("SliceSettingsDock") dock_widget.setWidget(slice_settings) dock_widget.setAllowedAreas(Qt.AllDockWidgetAreas) dock_widget.setFeatures(QDockWidget.NoDockWidgetFeatures) window.addDockWidget(Qt.LeftDockWidgetArea, dock_widget) window.setCentralWidget(viewer) window.show() window.activateWindow() window.raise_() app.exec_()
class OrbitPlotMainWindow(QMainWindow): """ the main window has three major widgets: current, orbit tabs and element editor. """ def __init__(self, parent=None, machines=[], **kwargs): QMainWindow.__init__(self, parent) self.iqtApp = kwargs.get("iqt", None) self.setIconSize(QSize(32, 32)) self.error_bar = True self._dlgOrbitCor = None # logging self.logdock = QDockWidget("Log") self.logdock.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) textedit = QPlainTextEdit(self.logdock) self.logger = logging.getLogger(__name__) self.guilogger = logging.getLogger("aphla.gui") # the "aphla" include lib part logging. When the lib is inside # QThread, logging message will be sent to TextEdit which is cross # thread. # self.guilogger = logging.getLogger("aphla") handler = QTextEditLoggingHandler(textedit) self.guilogger.addHandler(handler) self.guilogger.setLevel(logging.INFO) self.logdock.setWidget(textedit) self.logdock.setAllowedAreas(Qt.BottomDockWidgetArea) self.logdock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable) self.logdock.setFloating(False) self.logdock.setMinimumHeight(20) self.logdock.setMaximumHeight(100) self.logdock.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) self.logdock.resize(200, 60) #print self.logdock.sizeHint() self.addDockWidget(Qt.BottomDockWidgetArea, self.logdock) #print self.logdock.sizeHint() #print self.logdock.minimumSize() #print self.logdock.maximumSize() #self.logger.info("INFO") #self.logdock.setMinimumHeight(40) #self.logdock.setMaximumHeight(160) for msg in kwargs.get("infos", []): self.logger.info(msg) # dict of (machine, (lattice dict, default_lat, pvm)) self._mach = dict([(v[0], (v[1], v[2], v[3])) for v in machines]) for m, (lats, lat0, pvm) in self._mach.items(): self.logger.info( "machine '%s' initialized: [%s]" % (m, ", ".join([lat.name for k, lat in lats.items()]))) if pvm: for pv in pvm.dead(): self.logger.warn("'{0}' is disconnected.".format(pv)) ## DCCT current plot #self.dcct = DcctCurrentPlot() #self.dcct.setMinimumHeight(100) #self.dcct.setMaximumHeight(150) #t0 = time.time() #t = np.linspace(t0 - 8*3600*24, t0, 100) #self.dcct.curve.t = t #v = 500*np.exp((t[0] - t[:50])/(4*3600*24)) #self.dcct.curve.v = v.tolist()+v.tolist() #self.dcct.updatePlot() ## MDI area self.mdiarea = QMdiArea() self.connect(self.mdiarea, SIGNAL("subWindowActivated(QMdiSubWindow)"), self.updateMachineLatticeNames) self.mdiarea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.physics = ApOrbitPhysics(self.mdiarea, iqt=self.iqtApp) self.live_orbit = True self.setCentralWidget(self.mdiarea) #self._elemed = ElementPropertyTabs() #self.elemeditor = ElementEditorDock(parent=self) #self.elemeditor.setAllowedAreas(Qt.RightDockWidgetArea) #self.elemeditor.setFeatures(QDockWidget.DockWidgetMovable| # QDockWidget.DockWidgetClosable) #self.elemeditor.setFloating(False) #self.elemeditor.setEnabled(False) #self.elemeditor.setMinimumWidth(400) #self.elemeditor.setWidget(self._elemed) #self.elemeditor.show() #self.elemeditor.hide() #self.connect(self.elemeditor, # SIGNAL("elementChecked(PyQt_PyObject, bool)"), # self.physics.elementChecked) #self.addDockWidget(Qt.RightDockWidgetArea, self.elemeditor) self.createMenuToolBar() # the first machine is the default self.machBox.addItems([v for v in self._mach.keys()]) self.reloadLatticeNames(self.machBox.currentText()) self.connect(self.machBox, SIGNAL("currentIndexChanged(QString)"), self.reloadLatticeNames) # update at 1/2Hz self.dt, self.itimer = 1500, 0 #self.timerId = None self.timerId = self.startTimer(self.dt) self.vbpm = None self.statusBar().showMessage("Welcome") #self.initMachine("nsls2v2") #self._newVelemPlot("V2SR", aphla.machines.HLA_VBPM, 'x', # "H Orbit", c = None) #print "Thread started", self.machinit.isRunning() #self.newElementPlots("BPM", "x, y") #self.newElementPlot("BPM", "y") #self.newElementPlot("HCOR", "x") #self.newElementPlot("VCOR", "y") #self.newElementPlot("QUAD", "b1") #self.newElementPlot("SEXT", "b2") def updateMachineLatticeNames(self, wsub): i = self.machBox.findText(wsub.machlat[0]) self.machBox.setCurrentIndex(i) self.reloadLatticeNames(wsub.machlat[0]) def reloadLatticeNames(self, mach): self.latBox.clear() cur_mach = str(self.machBox.currentText()) lats, lat0, pvm = self._mach.get(cur_mach, ({}, None, None)) self.latBox.addItems([lat for lat in lats.keys()]) if lat0: i = self.latBox.findText(lat0.name) self.latBox.setCurrentIndex(i) def closeEvent(self, event): self.physics.close() self.mdiarea.closeAllSubWindows() event.accept() def createMenuToolBar(self): # # file menu # #self.machMenu = self.menuBar().addMenu("&Machines") #self.connect(self.machMenu, SIGNAL("aboutToShow()"), # self.updateMachMenu) self.openMenu = self.menuBar().addMenu("&Open") self.openMenu.addAction("New Plot ...", self.openNewPlot) self.openMenu.addAction("New Tune Plot", self.openTunePlot) self.openMenu.addAction("New BPM Plot", partial(self.newElementPlots, "BPM", "x,y")) self.openMenu.addAction("New HCOR Plot", partial(self.newElementPlots, "HCOR", "x")) self.openMenu.addAction("New VCOR Plot", partial(self.newElementPlots, "VCOR", "y")) self.openMenu.addSeparator() self.openMenu.addAction("Open ORM", self.loadOrm) self.openMenu.addSeparator() self.openMenu.addAction("Save Lattice ...", self.saveSnapshot) fileQuitAction = QAction(QIcon(":/file_quit.png"), "&Quit", self) fileQuitAction.setShortcut("Ctrl+Q") fileQuitAction.setToolTip("Quit the application") fileQuitAction.setStatusTip("Quit the application") #fileQuitAction.setIcon(Qt.QIcon(":/filequit.png")) self.connect(fileQuitAction, SIGNAL("triggered()"), self.close) self.openMenu.addAction(fileQuitAction) # view self.viewMenu = self.menuBar().addMenu("&View") mkmenu = QMenu("&Mark", self.viewMenu) for fam in ["BPM", "COR", "QUAD", "SEXT", "INSERTION"]: famAct = QAction(fam, self) famAct.setCheckable(True) self.connect(famAct, SIGNAL("toggled(bool)"), self.click_markfam) mkmenu.addAction(famAct) # # errorbar #viewErrorBarAction = QAction(QIcon(":/view_errorbar.png"), # "Errorbar", self) #viewErrorBarAction.setCheckable(True) #viewErrorBarAction.setChecked(True) #self.connect(viewErrorBarAction, SIGNAL("toggled(bool)"), # self.errorBar) # #zoomM = QMenu("Zoom", self.viewMenu) # # #drift_from_now = QAction("Drift from Now", self) #drift_from_now.setCheckable(True) #drift_from_now.setShortcut("Ctrl+N") #drift_from_golden = QAction("Drift from Golden", self) #drift_from_golden.setCheckable(True) #drift_from_none = QAction("None", self) #drift_from_none.setCheckable(True) #self.viewMenu.addAction(viewLiveAction) #self.viewMenu.addAction(viewSingleShotAction) #self.viewMenu.addSeparator() #self.viewMenu.addAction(drift_from_now) #self.viewMenu.addAction(drift_from_golden) #self.viewMenu.addAction(drift_from_none) #self.viewMenu.addAction(viewAutoScale) #self.viewMenu.addAction(viewErrorBarAction) #self.viewMenu.addSeparator() self.viewMenu.addMenu(mkmenu) #drift_group = QActionGroup(self) #drift_group.addAction(drift_from_none) #drift_group.addAction(drift_from_now) #drift_group.addAction(drift_from_golden) #drift_from_none.setChecked(True) sep = self.viewMenu.addSeparator() #sep.setText("Drift") #self.connect(drift_from_now, SIGNAL("triggered()"), self.setDriftNow) #self.connect(drift_from_none, SIGNAL("triggered()"), self.setDriftNone) #self.connect(drift_from_golden, SIGNAL("triggered()"), # self.setDriftGolden) #viewStyle = QMenu("Line Style", self.viewMenu) #for act in ["Increase Point Size", "Decrease Point Size", None, # "NoCurve", "Lines", "Sticks", None, # "Solid Line", "Dashed Line", "Dotted Line", None, # "Increase Line Width", "Decrease Line Width", None, # "NoSymbol", "Ellipse", "Rect", "Diamond", "Triangle", # "Cross", "XCross", "HLine", "VLine", # "Star1", "Star2", "Hexagon", None, # "Red", "Blue", "Green"]: # if act is None: # viewStyle.addSeparator() # else: # viewStyle.addAction(act, self.setPlotStyle) #self.viewMenu.addMenu(viewStyle) #self.viewMenu.addSeparator() #self.viewMenu.addAction(viewZoomOut15Action) #self.viewMenu.addAction(viewZoomIn15Action) #self.viewMenu.addAction(viewZoomAutoAction) #self.viewMenu.addSeparator() self.viewMenu.addAction("ORM SV", self.plotSVD) # a bug in PyQwt5 for datetime x-axis, waiting for Debian 7 #self.viewMenu.addAction(viewDcct) #for ac in self.viewMenu.actions(): ac.setDisabled(True) # self.controlMenu = self.menuBar().addMenu("&Tools") self.controlMenu.addAction(QIcon(":/control_choosebpm.png"), "En-/Disable BPM", partial(chooseElement, 'BPM')) self.controlMenu.addAction("En-/Disable COR", partial(chooseElement, 'COR')) #self.controlMenu.addAction(controlResetPvDataAction) self.controlMenu.addSeparator() self.controlMenu.addAction("Lattice Snapshot ...", self.openSnapshot) #self.controlMenu.addAction(controlZoomInPlot1Action) #self.controlMenu.addAction(controlZoomOutPlot1Action) #self.controlMenu.addAction(controlZoomInPlot2Action) #self.controlMenu.addAction(controlZoomOutPlot2Action) self.controlMenu.addSeparator() self.controlMenu.addAction("Correct Hor. orbit", partial(aphla.correctOrbit, plane="H")) self.controlMenu.addAction("Correct Vert. orbit", partial(aphla.correctOrbit, plane="V")) self.controlMenu.addAction(QIcon(":/control_corrorbit.png"), "Correct orbit", partial(aphla.correctOrbit, plane="HV")) #steer_orbit.setDisabled(True) self.controlMenu.addAction("Local Bump ...", self.createLocalBump) self.controlMenu.addAction("Element Editor ...", self.showElementEditor) self.controlMenu.addSeparator() self.controlMenu.addAction("meas Beta", self.physics.measBeta) self.controlMenu.addAction("meas Dispersion", self.physics.measDispersion) self.controlMenu.addAction("beam based alignment", self.runBba) #for ac in self.controlMenu.actions(): ac.setDisabled(True) # Window self.windowMenu = self.menuBar().addMenu("&Windows") #self.windowMenu.addAction(self.elemeditor.toggleViewAction()) self.windowMenu.addAction(self.logdock.toggleViewAction()) #viewDcct = QAction("Beam Current", self) #viewDcct.setCheckable(True) #viewDcct.setChecked(True) #self.connect(viewDcct, SIGNAL("toggled(bool)"), self.dcct.setVisible) #self.windowMenu.addAction(viewDcct) self.windowMenu.addSeparator() self.windowMenu.addAction("Cascade", self.mdiarea.cascadeSubWindows) self.windowMenu.addAction("Tile", self.mdiarea.tileSubWindows) self.windowMenu.addAction("Tile Horizontally", self.tileSubWindowsHorizontally) # "ctrl+page up", "ctrl+page down" self.windowMenu.addAction("Previous", self.mdiarea.activatePreviousSubWindow, "Ctrl+Left") self.windowMenu.addAction("Next", self.mdiarea.activateNextSubWindow, "Ctrl+Right") self.windowMenu.addSeparator() # debug self.debugMenu = self.menuBar().addMenu("&Debug") self.debugMenu.addAction("_Reset Correctors_", self._reset_correctors) self.debugMenu.addAction("_Reset Quadrupoles_", self._reset_quadrupoles) self.debugMenu.addAction("_Random V Kick_", self._random_vkick) self.debugMenu.addAction("_Random H Kick_", self._random_hkick) #for ac in self.debugMenu.actions(): ac.setDisabled(True) # help self.helpMenu = self.menuBar().addMenu("&Help") self.helpMenu.addAction("About mleap", self.showAbout) #toolbar machToolBar = self.addToolBar("Machines") self.machBox = QtGui.QComboBox() self.latBox = QtGui.QComboBox() #self.connect(self.latBox, SIGNAL("currentIndexChanged(QString)"), # self.__setLattice) machToolBar.addWidget(self.machBox) machToolBar.addWidget(self.latBox) #toolbar = QToolBar(self) #self.addToolBar(toolbar) #fileToolBar = self.addToolBar("File") #fileToolBar.setObjectName("FileToolBar") #fileToolBar.addAction(fileQuitAction) # viewToolBar1 = self.addToolBar("Live View") #viewToolBar.setObjectName("ViewToolBar") #viewToolBar.addAction(viewZoomOut15Action) #viewToolBar.addAction(viewZoomIn15Action) #viewToolBar.addAction(viewZoomAutoAction) #viewToolBar1.addAction(viewLiveAction) #viewToolBar1.addAction(viewSingleShotAction) #viewToolBar1.addSeparator() viewToolBar1.addAction(QIcon(":/new_bpm.png"), "Orbits", partial(self.newElementPlots, "BPM", "x,y")) viewToolBar1.addAction(QIcon(":/new_cor.png"), "Correctors", partial(self.newElementPlots, "COR", "x,y")) viewToolBar1.addAction(QIcon(":/new_quad.png"), "Quadrupoles", partial(self.newElementPlots, "QUAD", "b1")) viewToolBar1.addAction(QIcon(":/new_sext.png"), "Sextupoles", partial(self.newElementPlots, "SEXT", "b2")) #viewToolBar.addAction(viewErrorBarAction) #viewToolBar.addAction(QWhatsThis.createAction(self)) #viewToolBar2 = self.addToolBar("Scale Plot") #zoomActions = [(":/view_zoom_xy.png", "Fit", self.scalePlot), # (None, None, None), # (":/view_zoom_y.png", "Fit In Y", self.scalePlot), # (":/view_zoomin_y.png", "Zoom In Y", self.scalePlot), # (":/view_zoomout_y.png", "Zoom Out Y", self.scalePlot), # (":/view_move_up.png", "Move Up", self.scalePlot), # (":/view_move_down.png", "Move Down", self.scalePlot), # (None, None, None), # (":/view_zoom_x.png", "Fit In X", self.scalePlot), # (":/view_zoomin_x.png", "Zoom In X", self.scalePlot), # (":/view_zoomout_x.png", "Zoom Out X", self.scalePlot), # (":/view_move_left.png", "Move Left", self.scalePlot), # (":/view_move_right.png", "Move Right", self.scalePlot), # ] #for ico,name,hdl in zoomActions: # if hdl is None: continue # viewToolBar2.addAction(QIcon(ico), name, hdl) controlToolBar = self.addToolBar("Control") controlToolBar.addAction(QIcon(":/control_orbitcor.png"), "Correct Orbit", aphla.correctOrbit) controlToolBar.addAction(QIcon(":/control_localbump.png"), "Local Bump ...", self.createLocalBump) #controlToolBar.addAction(controlResetPvDataAction) def showAbout(self): QMessageBox.about( self, self.tr("mleap"), (self.tr("""<b>Machine/Lattice Editor And Plotter</b> v %1 <p>Copyright © Lingyun Yang, BNL, 2013-2014. All rights reserved. <p>This application can be used to perform high level accelerator controls. <p>Python %2 - Qt %3 - PyQt %4 on %5""").arg(aphla.version.version).arg( platform.python_version()).arg(QtCore.QT_VERSION_STR).arg( QtCore.PYQT_VERSION_STR).arg(platform.system()))) def showElementEditor(self): mach, lat = self.getCurrentMachLattice() ed = ElementEditor(lat, parent=self) ed.setWindowFlags(Qt.Window) ed.setAttribute(Qt.WA_DeleteOnClose) ed.show() def getCurrentMachLattice(self, cadata=False): """return the current machine name and lattice object""" mach = str(self.machBox.currentText()) latname = str(self.latBox.currentText()) lat_dict, lat0, pvm = self._mach[mach] if not cadata: return mach, lat_dict[latname] else: return mach, lat_dict[latname], pvm def newElementPlots(self, elem, fields, **kw): self.logger.info("new plots: %s %s" % (elem, fields)) _mach, _lat, _pvm = self.getCurrentMachLattice(cadata=True) mach, lat = kw.get("machlat", (_mach, _lat)) handle = kw.get("handle", "readback") elems = lat.getElementList(elem) x, pvs = [], [] field_list = re.findall(r'[^ ,]+', fields) for fld in field_list: si, pvsi = [], [] for e in elems: if not e.isEnabled(): continue epv = e.pv(field=fld, handle=handle) if not epv: continue pvsi.append(epv[0]) si.append(e.sb) x.append(si) pvs.append(pvsi) if not pvs: self.logger.error("no data found for elements '{0}' " "and field '{1}'".format(elem, field)) return p = ApMdiSubPlot(pvs=pvs, x=x, labels=["%s.%s" % (elem, fld) for fld in field_list], magprof=lat.getBeamlineProfile(), iqt=self.iqtApp, **kw) #QObject.installEventFilter(p.aplot) #p.data = ManagedPvData(pvm, s, pvs, element=elemnames, # label="{0}.{1}".format(elem,field)) p.setAttribute(Qt.WA_DeleteOnClose) str_elem = "{0}".format(elem) if len(str_elem) > 12: str_elem = str_elem[:9] + "..." str_field = "{0}".format(fields) if len(str_field) > 12: str_field = str_field[:9] + "..." p.setWindowTitle("[%s.%s] %s %s" % (mach, lat.name, str_elem, str_field)) self.connect(p, SIGNAL("elementSelected(PyQt_PyObject)"), self.elementSelected) self.connect(p, SIGNAL("destroyed()"), self.subPlotDestroyed) #p.updatePlot() # set the zoom stack #p.aplot.setErrorBar(self.error_bar) #p.wid.autoScaleXY() #p.aplot.replot() self.mdiarea.addSubWindow(p) #print "Show" p.show() ##print "Enable the buttons" #if len(self.mdiarea.subWindowList()) > 0: # self.elemeditor.setEnabled(True) def subPlotDestroyed(self): #if len(self.mdiarea.subWindowList()) == 0: # self.elemeditor.setEnabled(False) pass def loadOrm(self): fileName = QtGui.QFileDialog.getOpenFileName( self, "Open Orbit Response Matrix", "", "ORM Files (*.h5 *.hdf5);;Text File (*.txt);;All Files(*)") fileName = str(fileName) try: m = np.loadtxt(fileName) except: QMessageBox.critical(self, "Abort", "Invalid matrix data") return mach, lat = self.getCurrentMachLattice() # assuming we already have the PV, name, field but just want to # replace the matrix elements. assert np.shape(m) == np.shape(lat.ormdata.m) nx, ny = np.shape(lat.ormdata.m) for i in range(nx): for j in range(ny): lat.ormdata.m[i, j] = m[i, j] def saveSnapshot(self): latdict = dict([(k, v[0]) for k, v in self._mach.items()]) mach, lat = self.getCurrentMachLattice() snapdlg = SaveSnapshotDialog(latdict, mach) snapdlg.exec_() def saveLatSnapshot(self): mach, lat = self.getCurrentMachLattice() dpath = self._prepare_parent_dirs(mach) if not dpath: QMessageBox.warning(self, "Abort", "Aborted") return dt = datetime.datetime.now() fname = os.path.join( dpath, dt.strftime("snapshot_%d_%H%M%S_") + lat.name + ".hdf5") fileName = QtGui.QFileDialog.getSaveFileName( self, "Save Lattice Snapshot Data", fname, "Data Files (*.h5 *.hdf5);;All Files(*)") fileName = str(fileName) if not fileName: return aphla.catools.save_lat_epics(fileName, lat, mode='a') self.logger.info("snapshot created '%s'" % fileName) def saveMachSnapshot(self): mach, lat = self.getCurrentMachLattice() dpath = self._prepare_parent_dirs(mach) if not dpath: QMessageBox.warning(self, "Abort", "Aborted") return dt = datetime.datetime.now() fname = os.path.join(dpath, dt.strftime("snapshot_%d_%H%M%S.hdf5")) fileName = QtGui.QFileDialog.getSaveFileName( self, "Save Lattice Snapshot Data", fname, "Data Files (*.h5 *.hdf5);;All Files(*)") if not fileName: return fileName = str(fileName) import h5py f = h5py.File(str(fileName), 'w') f.close() self.logger.info("clean snapshot file created: '%s'" % fileName) for k, lat in self._mach[mach][0].items(): aphla.catools.save_lat_epics(fileName, lat, mode='a') self.logger.info("lattice snapshot appended for '%s'" % lat.name) def openSnapshot(self): #self.logger.info("loading snapshot?") latdict = dict([(k, v[0]) for k, v in self._mach.items()]) mach, lat = self.getCurrentMachLattice() lv = LatSnapshotMain(self, latdict, mach, self.logger) lv.setWindowFlags(Qt.Window) #self.logger.info("initialized") #lv.loadLatSnapshotH5() lv.exec_() def openTunePlot(self): mach, lat = self.getCurrentMachLattice() nu = lat.getElementList('tune') pvs = [(e.pv(field="x", handle="readback")[0], e.pv(field="y", handle="readback")[0]) for e in nu] labels = [e.name for e in nu] twiss = lat.getElementList("VA") pvs.extend([(e.pv(field="nux", handle="readback")[0], e.pv(field="nuy", handle="readback")[0]) for e in twiss]) labels.extend([e.name for e in twiss]) p = ApMdiSubPlot(pvs=pvs, labels=labels, dtype="Tunes") #QObject.installEventFilter(p.aplot) #p.data = ManagedPvData(pvm, s, pvs, element=elemnames, # label="{0}.{1}".format(elem,field)) p.setAttribute(Qt.WA_DeleteOnClose) p.setWindowTitle("[%s.%s] Tunes" % (mach, lat.name)) self.connect(p, SIGNAL("elementSelected(PyQt_PyObject)"), self.elementSelected) self.connect(p, SIGNAL("destroyed()"), self.subPlotDestroyed) #p.updatePlot() # set the zoom stack #p.aplot.setErrorBar(self.error_bar) #p.wid.autoScaleXY() #p.aplot.replot() self.mdiarea.addSubWindow(p) #print "Show" p.show() def openNewPlot(self): mach, lat = self.getCurrentMachLattice() fl = QtGui.QFormLayout() fl.addRow("Machine", QtGui.QLabel("%s" % mach)) fl.addRow("Lattice", QtGui.QLabel("%s" % lat.name)) elem, fld = QtGui.QLineEdit(), QtGui.QLineEdit() fl.addRow("Elements", elem) fl.addRow("Field", fld) dtype = QtGui.QComboBox() for tx in ["Array", "Waveform", "Time Series"]: dtype.addItem(tx) fl.addRow("Data Type", dtype) dlg = QtGui.QDialog() bx = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel) self.connect(bx, SIGNAL("accepted()"), dlg.accept) self.connect(bx, SIGNAL("rejected()"), dlg.reject) h1 = QtGui.QHBoxLayout() h1.addStretch() h1.addWidget(bx) v1 = QtGui.QVBoxLayout() v1.addLayout(fl) v1.addLayout(h1) dlg.setLayout(v1) if dlg.exec_(): self.newElementPlots(str(elem.text()), str(fld.text()), machlat=(mach, lat), dtype=str(dtype.currentText())) def click_markfam(self, on): famname = self.sender().text() mks = [] mach, lat = self.getCurrentMachLattice() # need to convert to python str for elem in lat.getElementList(str(famname)): if elem.family != famname: continue if elem.virtual: continue mks.append([elem.name, 0.5 * (elem.sb + elem.se)]) for w in self.mdiarea.subWindowList(): w.setMarkers(mks, on) #print self._machlat.keys() def _reset_correctors(self): self.logger.info("reset correctors") aphla.hlalib._reset_trims() def _reset_quadrupoles(self): self.logger.info("reset quadrupoles") aphla.hlalib._reset_quad() def _random_hkick(self): mach, lat = self.getCurrentMachLattice() hcors = lat.getElementList('HCOR') for k in range(3): i = np.random.randint(len(hcors)) self.logger.info("Setting {0}/{1} HCOR".format(i, len(hcors))) hcors[i].x += np.random.rand() * 2e-6 def _random_vkick(self): mach, lat = self.getCurrentMachLattice() cors = lat.getElementList('VCOR') for k in range(3): i = np.random.randint(len(cors)) cors[i].y += np.random.rand() * 1e-6 self.logger.info("increased kicker '{0}' by 1e-7 ({1} {2})".format( cors[i].name, cors[i].y, cors[i].getUnit('y', None))) def viewDcctPlot(self, on): self.dcct.setVisible(on) def liveData(self, on): """Switch on/off live data taking""" self.live_orbit = on def scalePlot(self): w = self.mdiarea.currentSubWindow() if not w: return st, p = self.sender().text(), w.aplot if st == "Fit": p.scaleXBottom() p.scaleYLeft() # a hack bound = p.curvesBound() p.zoomer1.setZoomStack([bound]) elif st == "Fit In Y": p.scaleYLeft() elif st == "Zoom In Y": p.scaleYLeft(1. / 1.5) elif st == "Zoom Out Y": p.scaleYLeft(1.5) elif st == "Move Up": p.moveCurves(Qwt.QwtPlot.yLeft, 0.8) elif st == "Move Down": p.moveCurves(Qwt.QwtPlot.yLeft, -0.8) elif st == "Fit In X": p.scaleXBottom() elif st == "Zoom In X": p.scaleXBottom(1.0 / 1.5) elif st == "Zoom Out X": p.scaleXBottom(1.5) elif st == "Move Left": p.moveCurves(Qwt.QwtPlot.xBottom, 0.8) elif st == "Move Right": p.moveCurves(Qwt.QwtPlot.xBottom, -0.8) else: self.logger.error("unknow action '{0}'".format(st)) def getVisibleRange(self): w = self.mdiarea.currentSubWindow() if not w: mach, lat = self.getCurrentMachLattice() self.logger.warn( "no active plot, use full range of {0}.{1}".format( mach, lat.name)) return lat.getLocationRange() else: return w.currentXlim() def getVisibleElements(self, elemname, sb=None, se=None): w = self.mdiarea.currentSubWindow() mach, lat = self.getCurrentMachLattice() elems = lat.getElementList(elemname) if sb is not None: elems = [e for e in elems if e.sb >= sb] if se is not None: elems = [e for e in elems if e.se <= se] self.logger.info("searching for '{0}' in range [{1}, {2}]".format( elemname, sb, se)) return elems def timerEvent(self, e): if e.timerId() != self.timerId: return #if not self.elemeditor.isHidden(): # self.elemeditor.updateModelData() #if self.live_orbit: # self.itimer += 1 # #self.updatePlots() # #self.updateStatus() # for w in self.mdiarea.subWindowList(): # if not isinstance(w, ApMdiSubPlot): continue # if not w.live: continue # w.updatePlot() # self.statusBar().showMessage("plot updated: {0}".format( # time.strftime("%F %T"))) #else: # self.statusBar().showMessage("live update disabled") def singleShot(self): for w in self.mdiarea.subWindowList(): if not isinstance(w, ApMdiSubPlot): continue w.updatePlot() self.statusBar().showMessage("plot updated: {0}".format( time.strftime("%F %T"))) def elementSelected(self, elems): """this action is ignored""" mach, lat, elemnames = elems #_lat = self._machlat[mach][lat] self.logger.info("element selected") #elemobjs = _lat.getElementList(elemnames) #self._elemed.addElements(elemobjs) def activeOrbitPlot(self, field): mach = str(self.machBox.currentText()) lat = str(self.latBox.currentText()) for w in self.mdiarea.subWindowList(): #print w.machine(), w.lattice(), w.data.yfield if not isinstance(w, ApMdiSubPlot): continue if w.machine() != mach: continue if w.lattice() != lat: continue if w.data.yfield != field: continue return w return None def createLocalBump_(self): """create local bump""" if self._dlgOrbitCor is None: bpms = ap.getElements("BPM") cors = ap.getElements("COR") self._dlgOrbitCor = OrbitCorrDlg(bpms, cors) #corbitdlg.resize(600, 500) self._dlgOrbitCor.setWindowTitle("Create Local Bump") self._dlgOrbitCor.show() self._dlgOrbitCor.raise_() self._dlgOrbitCor.activateWindow() def createLocalBump(self): """create local bump""" bpms = ap.getElements("BPM") cors = ap.getElements("COR") dlgOrbitCor = OrbitCorrDlg(bpms, cors, parent=self) #corbitdlg.resize(600, 500) dlgOrbitCor.setWindowTitle("Create Local Bump") dlgOrbitCor.show() dlgOrbitCor.raise_() dlgOrbitCor.activateWindow() dlgOrbitCor.setAttribute(Qt.WA_DeleteOnClose) def runBba(self): mach, lat = self.getCurrentMachLattice() bpms = [ e for e in lat.getElementList('BPM') if e not in self.physics.deadelems ] self.physics.runBba(bpms) def plotSVD(self): mach, lat = self.getCurrentMachLattice() if not lat.ormdata: QMessageBox.critical(self, "ORM SVD", "machine '%s' ORM data is not available" % \ mach, QMessageBox.Ok) return m, brec, trec = lat.ormdata.getMatrix(None, None, full=False, ignore=self.getDeadElements()) U, s, V = np.linalg.svd(m, full_matrices=True) #print np.shape(s), s self.sp = ApSvdPlot(s) self.sp.show() def tileSubWindowsHorizontally(self): pos = QtCore.QPoint(0, 0) subwins = self.mdiarea.subWindowList() for w in subwins: height = self.mdiarea.height() / len(subwins) rect = QtCore.QRect(0, 0, self.mdiarea.width(), height) w.setGeometry(rect) w.move(pos) pos.setY(pos.y() + w.height())
class GUI(QMainWindow): def __init__(self, debug=False): QMainWindow.__init__(self) # initialise filename self.filename = None # make the data store from xmlstore import Store self.store = Store(debug=debug) # view the current table self.tableView = TableView(self) #self.tableView.setDragEnabled(True); #self.tableView.setDragDropMode(QAbstractItemView.InternalMove) #self.tableView.setAcceptDrops(True); #self.tableView.setDropIndicatorShown(True); self.tableView.verticalHeader().sectionMoved.connect(self.sectionMoved) self.tableView.verticalHeader().setMovable(True) self.setCentralWidget(self.tableView) # add a custom delegate to it self.delegate = ComboBoxDelegate() self.tableView.setItemDelegate(self.delegate) # dock the table selection on the left self.dock1 = QDockWidget(self) self.listView = ListView(self) self.listView.setDragEnabled(True) self.listView.setDragDropMode(QAbstractItemView.InternalMove) self.listView.setDropIndicatorShown(True) self.dock1.setWidget(self.listView) self.dock1.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.addDockWidget(Qt.LeftDockWidgetArea, self.dock1) # connect it to the populate method self.connect(self.listView, SIGNAL('activated ( const QModelIndex & )'), self.populate) self.connect(self.listView, SIGNAL('clicked ( const QModelIndex & )'), self.populate) # dock the undoView on the left self.dock2 = QDockWidget(self) self.undoView = QUndoView(self.store.stack) self.dock2.setWidget(self.undoView) self.dock2.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.dock2.setWindowTitle('Undo Stack') self.addDockWidget(Qt.LeftDockWidgetArea, self.dock2) # create a menubar self.menu = self.menuBar() # create file menu headings self.menuFile = self.menu.addMenu('File') self.menuFile.addAction('New', self.New).setShortcut('CTRL+N') self.menuFile.addAction('Open...', self.Open).setShortcut('CTRL+O') self.menuFile.addAction('Reload', self.Reload) self.menuFile.addAction('Save', self.Save).setShortcut('CTRL+S') self.menuFile.addAction('Save As...', self.SaveAs) self.menuFile.addSeparator() self.menuFile.addAction('Set Architecture...', self.setArch) self.menuFile.addSeparator() self.menuFile.addAction('Quit', self.closeEvent).setShortcuts( ['CTRL+Q', 'ALT+F4']) # create edit menu headings self.menuEdit = self.menu.addMenu('Edit') self.menuEdit.addAction('Insert Row', self.tableView.insertRow).setShortcut('CTRL+I') self.menuEdit.addAction( 'Insert Row Under', self.tableView.insertRowUnder).setShortcut('CTRL+U') self.menuEdit.addAction('Remove Row', self.tableView.removeRow) self.menuEdit.addSeparator() self.menuEdit.addAction('Cut', self.tableView.cut).setShortcut('CTRL+X') self.menuEdit.addAction('Copy', self.tableView.copy).setShortcuts( ['CTRL+C', 'CTRL+INS']) self.menuEdit.addAction('Paste', self.tableView.paste).setShortcuts( ['CTRL+V', 'SHIFT+INS']) self.menuEdit.addAction('Clear', self.tableView.menuClear).setShortcut('CTRL+D') self.menuEdit.addSeparator() self.menuEdit.addAction('Fill Cells', self.tableView.fillCells).setShortcut('CTRL+L') self.menuEdit.addAction( 'Fill Cells and Increment', self.tableView.fillCellsInc).setShortcut('CTRL+R') self.menuEdit.addAction( 'Python Code...', self.tableView.pythonCode).setShortcut('CTRL+P') self.tableView.codeBox = pythonCode() self.menuEdit.addSeparator() self.menuEdit.addAction('Undo', self.store.stack, SLOT('undo()')).setShortcut('CTRL+Z') self.menuEdit.addAction('Redo', self.store.stack, SLOT('redo()')).setShortcut('CTRL+SHIFT+Z') # create component menu self.menuComponents = self.menu.addMenu('Components') self.resize(QSize(1000, 500)) def Save(self): # save menu command self.SaveAs(self.filename) def setArch(self): arch = self.store.getArch() arch, ok = QInputDialog.getText(self, 'Architecture Dialog', 'Enter Architecture', QLineEdit.Normal, arch) arch = str(arch) if ok: self.store.setArch(arch) def SaveAs(self, filename=''): # save as menu command if filename == '': filename = str(QFileDialog.getSaveFileName()) if filename != '': self.filename = filename self.store.Save(filename) self._setClean() def Reload(self): if self.filename is not None: self.Open(self.filename, name=self.tablename) def Open(self, filename='', name=None): # make sure the user is sure if there are unsaved changes if self.__prompt_unsaved() == QMessageBox.Cancel: return # ask for a filename if filename == '': filename = str( QFileDialog.getOpenFileName( filter="Xml Files (*.xml);;All files (*.*)")) if filename == '': return # store the filename self.filename = filename # tell the store to open a new set of tables try: problems, warnings = self.store.Open(filename) if problems: errorstr = 'Can\'t load all object types: ' + ', '.join( problems) QMessageBox.warning(self, 'Open Error', errorstr) if warnings: errorstr = \ 'The following warnings were generated:\n' + \ '\n'.join(warnings) QMessageBox.warning(self, 'Open Warnings', errorstr) except Exception, e: x = formLog( 'An error ocurred. Make sure all the modules listed ' 'in RELEASE files are built. Check the text below for ' 'details:\n\n' + traceback.format_exc(), self) x.show() return # populate self.setWindowTitle('XEB - %s[*]' % filename) self.listView.clear() for t in self.store.getTableNames(): self.__insertListViewItem(t) self.__populateMenu() self.populate(name=name) self._setClean()
class DBMainWindow(QMainWindow): """Main Window and Portal .. todo:: Remember the dock position """ W_NAME = "DBMainWindow" def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.db = None self.setWindowTitle("PyQtDb") self.setWindowIcon(Ico.icon(Ico.FavIcon)) self.setMinimumWidth(800) self.setMinimumHeight(800) """ topBar = QToolBar() self.addToolBar(Qt.TopToolBarArea, topBar) topBar.addAction("New Server") """ ## Servers Widget self.dockServers = QDockWidget("Servers") self.dockServers.setFeatures(QDockWidget.DockWidgetMovable) self.dockServers.setAllowedAreas(Qt.LeftDockWidgetArea|Qt.RightDockWidgetArea) self.addDockWidget(Qt.LeftDockWidgetArea, self.dockServers) self.serversWidget = DBServersWidget.DBServersWidget(self) """Instance of :py:class:`~pyqtdb.DBServersWidget.DBServersWidget` in dock""" self.dockServers.setWidget(self.serversWidget) self.connect(self.serversWidget, SIGNAL("open_server"), self.on_open_server) self.cenWid = QWidget() self.setCentralWidget(self.cenWid) self.mainLayout = QHBoxLayout() self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.setSpacing(0) self.cenWid.setLayout( self.mainLayout ) self.tabWidget = QTabWidget() """The main tabs""" self.tabWidget.setTabsClosable(True) self.mainLayout.addWidget(self.tabWidget) G.settings.restore_window(self) def on_open_server(self, srv): """Opens server by adding a :py:class:`~pyqtdb.DBBrowser.DBBrowser` in the the tabWidget""" server = G.settings.get_server( str(srv) ) print "oopen", srv, server widget = DBBrowser.DBBrowser(self, server) self.tabWidget.addTab(widget, Ico.icon(Ico.Server), server['server']) self.tabWidget.setCurrentIndex(self.tabWidget.indexOf(widget)) def closeEvent( self, event ): """Save window settings on close with :py:class:`~pyqtdb.XSettings.XSettings.save_window` """ G.settings.save_window( self )
class OrbitPlotMainWindow(QMainWindow): """ the main window has three major widgets: current, orbit tabs and element editor. """ def __init__(self, parent = None, machines=[], **kwargs): QMainWindow.__init__(self, parent) self.iqtApp = kwargs.get("iqt", None) self.setIconSize(QSize(32, 32)) self.error_bar = True self._dlgOrbitCor = None # logging self.logdock = QDockWidget("Log") self.logdock.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) textedit = QPlainTextEdit(self.logdock) self.logger = logging.getLogger(__name__) self.guilogger = logging.getLogger("aphla.gui") # the "aphla" include lib part logging. When the lib is inside # QThread, logging message will be sent to TextEdit which is cross # thread. # self.guilogger = logging.getLogger("aphla") handler = QTextEditLoggingHandler(textedit) self.guilogger.addHandler(handler) self.guilogger.setLevel(logging.INFO) self.logdock.setWidget(textedit) self.logdock.setAllowedAreas(Qt.BottomDockWidgetArea) self.logdock.setFeatures(QDockWidget.DockWidgetMovable| QDockWidget.DockWidgetClosable) self.logdock.setFloating(False) self.logdock.setMinimumHeight(20) self.logdock.setMaximumHeight(100) self.logdock.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) self.logdock.resize(200, 60) #print self.logdock.sizeHint() self.addDockWidget(Qt.BottomDockWidgetArea, self.logdock) #print self.logdock.sizeHint() #print self.logdock.minimumSize() #print self.logdock.maximumSize() #self.logger.info("INFO") #self.logdock.setMinimumHeight(40) #self.logdock.setMaximumHeight(160) for msg in kwargs.get("infos", []): self.logger.info(msg) # dict of (machine, (lattice dict, default_lat, pvm)) self._mach = dict([(v[0], (v[1],v[2],v[3])) for v in machines]) for m,(lats,lat0,pvm) in self._mach.items(): self.logger.info("machine '%s' initialized: [%s]" % ( m, ", ".join([lat.name for k,lat in lats.items()]))) if pvm: for pv in pvm.dead(): self.logger.warn("'{0}' is disconnected.".format(pv)) ## DCCT current plot #self.dcct = DcctCurrentPlot() #self.dcct.setMinimumHeight(100) #self.dcct.setMaximumHeight(150) #t0 = time.time() #t = np.linspace(t0 - 8*3600*24, t0, 100) #self.dcct.curve.t = t #v = 500*np.exp((t[0] - t[:50])/(4*3600*24)) #self.dcct.curve.v = v.tolist()+v.tolist() #self.dcct.updatePlot() ## MDI area self.mdiarea = QMdiArea() self.connect(self.mdiarea, SIGNAL("subWindowActivated(QMdiSubWindow)"), self.updateMachineLatticeNames) self.mdiarea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.physics = ApOrbitPhysics(self.mdiarea, iqt=self.iqtApp) self.live_orbit = True self.setCentralWidget(self.mdiarea) #self._elemed = ElementPropertyTabs() #self.elemeditor = ElementEditorDock(parent=self) #self.elemeditor.setAllowedAreas(Qt.RightDockWidgetArea) #self.elemeditor.setFeatures(QDockWidget.DockWidgetMovable| # QDockWidget.DockWidgetClosable) #self.elemeditor.setFloating(False) #self.elemeditor.setEnabled(False) #self.elemeditor.setMinimumWidth(400) #self.elemeditor.setWidget(self._elemed) #self.elemeditor.show() #self.elemeditor.hide() #self.connect(self.elemeditor, # SIGNAL("elementChecked(PyQt_PyObject, bool)"), # self.physics.elementChecked) #self.addDockWidget(Qt.RightDockWidgetArea, self.elemeditor) self.createMenuToolBar() # the first machine is the default self.machBox.addItems([v for v in self._mach.keys()]) self.reloadLatticeNames(self.machBox.currentText()) self.connect(self.machBox, SIGNAL("currentIndexChanged(QString)"), self.reloadLatticeNames) # update at 1/2Hz self.dt, self.itimer = 1500, 0 #self.timerId = None self.timerId = self.startTimer(self.dt) self.vbpm = None self.statusBar().showMessage("Welcome") #self.initMachine("nsls2v2") #self._newVelemPlot("V2SR", aphla.machines.HLA_VBPM, 'x', # "H Orbit", c = None) #print "Thread started", self.machinit.isRunning() #self.newElementPlots("BPM", "x, y") #self.newElementPlot("BPM", "y") #self.newElementPlot("HCOR", "x") #self.newElementPlot("VCOR", "y") #self.newElementPlot("QUAD", "b1") #self.newElementPlot("SEXT", "b2") def updateMachineLatticeNames(self, wsub): i = self.machBox.findText(wsub.machlat[0]) self.machBox.setCurrentIndex(i) self.reloadLatticeNames(wsub.machlat[0]) def reloadLatticeNames(self, mach): self.latBox.clear() cur_mach = str(self.machBox.currentText()) lats, lat0, pvm = self._mach.get(cur_mach, ({}, None, None)) self.latBox.addItems([lat for lat in lats.keys()]) if lat0: i = self.latBox.findText(lat0.name) self.latBox.setCurrentIndex(i) def closeEvent(self, event): self.physics.close() self.mdiarea.closeAllSubWindows() event.accept() def createMenuToolBar(self): # # file menu # #self.machMenu = self.menuBar().addMenu("&Machines") #self.connect(self.machMenu, SIGNAL("aboutToShow()"), # self.updateMachMenu) self.openMenu = self.menuBar().addMenu("&Open") self.openMenu.addAction("New Plot ...", self.openNewPlot) self.openMenu.addAction("New Tune Plot", self.openTunePlot) self.openMenu.addAction("New BPM Plot", partial( self.newElementPlots, "BPM", "x,y")) self.openMenu.addAction("New HCOR Plot", partial( self.newElementPlots, "HCOR", "x")) self.openMenu.addAction("New VCOR Plot", partial( self.newElementPlots, "VCOR", "y")) self.openMenu.addSeparator() self.openMenu.addAction("Open ORM", self.loadOrm) self.openMenu.addSeparator() self.openMenu.addAction("Save Lattice ...", self.saveSnapshot) fileQuitAction = QAction(QIcon(":/file_quit.png"), "&Quit", self) fileQuitAction.setShortcut("Ctrl+Q") fileQuitAction.setToolTip("Quit the application") fileQuitAction.setStatusTip("Quit the application") #fileQuitAction.setIcon(Qt.QIcon(":/filequit.png")) self.connect(fileQuitAction, SIGNAL("triggered()"), self.close) self.openMenu.addAction(fileQuitAction) # view self.viewMenu = self.menuBar().addMenu("&View") mkmenu = QMenu("&Mark", self.viewMenu) for fam in ["BPM", "COR", "QUAD", "SEXT", "INSERTION"]: famAct = QAction(fam, self) famAct.setCheckable(True) self.connect(famAct, SIGNAL("toggled(bool)"), self.click_markfam) mkmenu.addAction(famAct) # # errorbar #viewErrorBarAction = QAction(QIcon(":/view_errorbar.png"), # "Errorbar", self) #viewErrorBarAction.setCheckable(True) #viewErrorBarAction.setChecked(True) #self.connect(viewErrorBarAction, SIGNAL("toggled(bool)"), # self.errorBar) # #zoomM = QMenu("Zoom", self.viewMenu) # # #drift_from_now = QAction("Drift from Now", self) #drift_from_now.setCheckable(True) #drift_from_now.setShortcut("Ctrl+N") #drift_from_golden = QAction("Drift from Golden", self) #drift_from_golden.setCheckable(True) #drift_from_none = QAction("None", self) #drift_from_none.setCheckable(True) #self.viewMenu.addAction(viewLiveAction) #self.viewMenu.addAction(viewSingleShotAction) #self.viewMenu.addSeparator() #self.viewMenu.addAction(drift_from_now) #self.viewMenu.addAction(drift_from_golden) #self.viewMenu.addAction(drift_from_none) #self.viewMenu.addAction(viewAutoScale) #self.viewMenu.addAction(viewErrorBarAction) #self.viewMenu.addSeparator() self.viewMenu.addMenu(mkmenu) #drift_group = QActionGroup(self) #drift_group.addAction(drift_from_none) #drift_group.addAction(drift_from_now) #drift_group.addAction(drift_from_golden) #drift_from_none.setChecked(True) sep = self.viewMenu.addSeparator() #sep.setText("Drift") #self.connect(drift_from_now, SIGNAL("triggered()"), self.setDriftNow) #self.connect(drift_from_none, SIGNAL("triggered()"), self.setDriftNone) #self.connect(drift_from_golden, SIGNAL("triggered()"), # self.setDriftGolden) #viewStyle = QMenu("Line Style", self.viewMenu) #for act in ["Increase Point Size", "Decrease Point Size", None, # "NoCurve", "Lines", "Sticks", None, # "Solid Line", "Dashed Line", "Dotted Line", None, # "Increase Line Width", "Decrease Line Width", None, # "NoSymbol", "Ellipse", "Rect", "Diamond", "Triangle", # "Cross", "XCross", "HLine", "VLine", # "Star1", "Star2", "Hexagon", None, # "Red", "Blue", "Green"]: # if act is None: # viewStyle.addSeparator() # else: # viewStyle.addAction(act, self.setPlotStyle) #self.viewMenu.addMenu(viewStyle) #self.viewMenu.addSeparator() #self.viewMenu.addAction(viewZoomOut15Action) #self.viewMenu.addAction(viewZoomIn15Action) #self.viewMenu.addAction(viewZoomAutoAction) #self.viewMenu.addSeparator() self.viewMenu.addAction("ORM SV", self.plotSVD) # a bug in PyQwt5 for datetime x-axis, waiting for Debian 7 #self.viewMenu.addAction(viewDcct) #for ac in self.viewMenu.actions(): ac.setDisabled(True) # self.controlMenu = self.menuBar().addMenu("&Tools") self.controlMenu.addAction( QIcon(":/control_choosebpm.png"), "En-/Disable BPM", partial(chooseElement, 'BPM')) self.controlMenu.addAction( "En-/Disable COR", partial(chooseElement, 'COR')) #self.controlMenu.addAction(controlResetPvDataAction) self.controlMenu.addSeparator() self.controlMenu.addAction("Lattice Snapshot ...", self.openSnapshot) #self.controlMenu.addAction(controlZoomInPlot1Action) #self.controlMenu.addAction(controlZoomOutPlot1Action) #self.controlMenu.addAction(controlZoomInPlot2Action) #self.controlMenu.addAction(controlZoomOutPlot2Action) self.controlMenu.addSeparator() self.controlMenu.addAction("Correct Hor. orbit", partial(aphla.correctOrbit, plane="H")) self.controlMenu.addAction("Correct Vert. orbit", partial(aphla.correctOrbit, plane="V")) self.controlMenu.addAction( QIcon(":/control_corrorbit.png"), "Correct orbit", partial(aphla.correctOrbit, plane="HV")) #steer_orbit.setDisabled(True) self.controlMenu.addAction("Local Bump ...", self.createLocalBump) self.controlMenu.addAction("Element Editor ...", self.showElementEditor) self.controlMenu.addSeparator() self.controlMenu.addAction("meas Beta", self.physics.measBeta) self.controlMenu.addAction("meas Dispersion", self.physics.measDispersion) self.controlMenu.addAction("beam based alignment", self.runBba) #for ac in self.controlMenu.actions(): ac.setDisabled(True) # Window self.windowMenu = self.menuBar().addMenu("&Windows") #self.windowMenu.addAction(self.elemeditor.toggleViewAction()) self.windowMenu.addAction(self.logdock.toggleViewAction()) #viewDcct = QAction("Beam Current", self) #viewDcct.setCheckable(True) #viewDcct.setChecked(True) #self.connect(viewDcct, SIGNAL("toggled(bool)"), self.dcct.setVisible) #self.windowMenu.addAction(viewDcct) self.windowMenu.addSeparator() self.windowMenu.addAction("Cascade", self.mdiarea.cascadeSubWindows) self.windowMenu.addAction("Tile", self.mdiarea.tileSubWindows) self.windowMenu.addAction("Tile Horizontally", self.tileSubWindowsHorizontally) # "ctrl+page up", "ctrl+page down" self.windowMenu.addAction("Previous", self.mdiarea.activatePreviousSubWindow, "Ctrl+Left") self.windowMenu.addAction("Next", self.mdiarea.activateNextSubWindow, "Ctrl+Right") self.windowMenu.addSeparator() # debug self.debugMenu = self.menuBar().addMenu("&Debug") self.debugMenu.addAction("_Reset Correctors_", self._reset_correctors) self.debugMenu.addAction("_Reset Quadrupoles_", self._reset_quadrupoles) self.debugMenu.addAction("_Random V Kick_", self._random_vkick) self.debugMenu.addAction("_Random H Kick_", self._random_hkick) #for ac in self.debugMenu.actions(): ac.setDisabled(True) # help self.helpMenu = self.menuBar().addMenu("&Help") self.helpMenu.addAction("About mleap", self.showAbout) #toolbar machToolBar = self.addToolBar("Machines") self.machBox = QtGui.QComboBox() self.latBox = QtGui.QComboBox() #self.connect(self.latBox, SIGNAL("currentIndexChanged(QString)"), # self.__setLattice) machToolBar.addWidget(self.machBox) machToolBar.addWidget(self.latBox) #toolbar = QToolBar(self) #self.addToolBar(toolbar) #fileToolBar = self.addToolBar("File") #fileToolBar.setObjectName("FileToolBar") #fileToolBar.addAction(fileQuitAction) # viewToolBar1 = self.addToolBar("Live View") #viewToolBar.setObjectName("ViewToolBar") #viewToolBar.addAction(viewZoomOut15Action) #viewToolBar.addAction(viewZoomIn15Action) #viewToolBar.addAction(viewZoomAutoAction) #viewToolBar1.addAction(viewLiveAction) #viewToolBar1.addAction(viewSingleShotAction) #viewToolBar1.addSeparator() viewToolBar1.addAction( QIcon(":/new_bpm.png"), "Orbits", partial(self.newElementPlots, "BPM", "x,y")) viewToolBar1.addAction( QIcon(":/new_cor.png"), "Correctors", partial(self.newElementPlots, "COR", "x,y")) viewToolBar1.addAction( QIcon(":/new_quad.png"), "Quadrupoles", partial(self.newElementPlots, "QUAD", "b1")) viewToolBar1.addAction( QIcon(":/new_sext.png"), "Sextupoles", partial(self.newElementPlots, "SEXT", "b2")) #viewToolBar.addAction(viewErrorBarAction) #viewToolBar.addAction(QWhatsThis.createAction(self)) #viewToolBar2 = self.addToolBar("Scale Plot") #zoomActions = [(":/view_zoom_xy.png", "Fit", self.scalePlot), # (None, None, None), # (":/view_zoom_y.png", "Fit In Y", self.scalePlot), # (":/view_zoomin_y.png", "Zoom In Y", self.scalePlot), # (":/view_zoomout_y.png", "Zoom Out Y", self.scalePlot), # (":/view_move_up.png", "Move Up", self.scalePlot), # (":/view_move_down.png", "Move Down", self.scalePlot), # (None, None, None), # (":/view_zoom_x.png", "Fit In X", self.scalePlot), # (":/view_zoomin_x.png", "Zoom In X", self.scalePlot), # (":/view_zoomout_x.png", "Zoom Out X", self.scalePlot), # (":/view_move_left.png", "Move Left", self.scalePlot), # (":/view_move_right.png", "Move Right", self.scalePlot), # ] #for ico,name,hdl in zoomActions: # if hdl is None: continue # viewToolBar2.addAction(QIcon(ico), name, hdl) controlToolBar = self.addToolBar("Control") controlToolBar.addAction( QIcon(":/control_orbitcor.png"), "Correct Orbit", aphla.correctOrbit) controlToolBar.addAction( QIcon(":/control_localbump.png"), "Local Bump ...", self.createLocalBump) #controlToolBar.addAction(controlResetPvDataAction) def showAbout(self): QMessageBox.about( self, self.tr("mleap"), (self.tr("""<b>Machine/Lattice Editor And Plotter</b> v %1 <p>Copyright © Lingyun Yang, BNL, 2013-2014. All rights reserved. <p>This application can be used to perform high level accelerator controls. <p>Python %2 - Qt %3 - PyQt %4 on %5""").arg(aphla.version.version) .arg(platform.python_version()).arg(QtCore.QT_VERSION_STR) .arg(QtCore.PYQT_VERSION_STR).arg(platform.system()))) def showElementEditor(self): mach, lat = self.getCurrentMachLattice() ed = ElementEditor(lat, parent=self) ed.setWindowFlags(Qt.Window) ed.setAttribute(Qt.WA_DeleteOnClose) ed.show() def getCurrentMachLattice(self, cadata = False): """return the current machine name and lattice object""" mach = str(self.machBox.currentText()) latname = str(self.latBox.currentText()) lat_dict, lat0, pvm = self._mach[mach] if not cadata: return mach, lat_dict[latname] else: return mach, lat_dict[latname], pvm def newElementPlots(self, elem, fields, **kw): self.logger.info("new plots: %s %s" % (elem, fields)) _mach, _lat, _pvm = self.getCurrentMachLattice(cadata=True) mach, lat = kw.get("machlat", (_mach, _lat)) handle = kw.get("handle", "readback") elems = lat.getElementList(elem) x, pvs = [], [] field_list = re.findall(r'[^ ,]+', fields) for fld in field_list: si, pvsi = [], [] for e in elems: if not e.isEnabled(): continue epv = e.pv(field=fld, handle=handle) if not epv: continue pvsi.append(epv[0]) si.append(e.sb) x.append(si) pvs.append(pvsi) if not pvs: self.logger.error("no data found for elements '{0}' " "and field '{1}'".format(elem, field)) return p = ApMdiSubPlot(pvs=pvs, x = x, labels=["%s.%s" % (elem,fld) for fld in field_list], magprof = lat.getBeamlineProfile(), iqt = self.iqtApp, **kw) #QObject.installEventFilter(p.aplot) #p.data = ManagedPvData(pvm, s, pvs, element=elemnames, # label="{0}.{1}".format(elem,field)) p.setAttribute(Qt.WA_DeleteOnClose) str_elem = "{0}".format(elem) if len(str_elem) > 12: str_elem = str_elem[:9] + "..." str_field = "{0}".format(fields) if len(str_field) > 12: str_field = str_field[:9] + "..." p.setWindowTitle("[%s.%s] %s %s" % ( mach, lat.name, str_elem, str_field)) self.connect(p, SIGNAL("elementSelected(PyQt_PyObject)"), self.elementSelected) self.connect(p, SIGNAL("destroyed()"), self.subPlotDestroyed) #p.updatePlot() # set the zoom stack #p.aplot.setErrorBar(self.error_bar) #p.wid.autoScaleXY() #p.aplot.replot() self.mdiarea.addSubWindow(p) #print "Show" p.show() ##print "Enable the buttons" #if len(self.mdiarea.subWindowList()) > 0: # self.elemeditor.setEnabled(True) def subPlotDestroyed(self): #if len(self.mdiarea.subWindowList()) == 0: # self.elemeditor.setEnabled(False) pass def loadOrm(self): fileName = QtGui.QFileDialog.getOpenFileName( self, "Open Orbit Response Matrix", "", "ORM Files (*.h5 *.hdf5);;Text File (*.txt);;All Files(*)") fileName = str(fileName) try: m = np.loadtxt(fileName) except: QMessageBox.critical(self, "Abort", "Invalid matrix data") return mach, lat = self.getCurrentMachLattice() # assuming we already have the PV, name, field but just want to # replace the matrix elements. assert np.shape(m) == np.shape(lat.ormdata.m) nx, ny = np.shape(lat.ormdata.m) for i in range(nx): for j in range(ny): lat.ormdata.m[i,j] = m[i,j] def saveSnapshot(self): latdict = dict([(k,v[0]) for k,v in self._mach.items()]) mach, lat = self.getCurrentMachLattice() snapdlg = SaveSnapshotDialog(latdict, mach) snapdlg.exec_() def saveLatSnapshot(self): mach, lat = self.getCurrentMachLattice() dpath = self._prepare_parent_dirs(mach) if not dpath: QMessageBox.warning(self, "Abort", "Aborted") return dt = datetime.datetime.now() fname = os.path.join(dpath, dt.strftime("snapshot_%d_%H%M%S_") + lat.name + ".hdf5") fileName = QtGui.QFileDialog.getSaveFileName( self, "Save Lattice Snapshot Data", fname, "Data Files (*.h5 *.hdf5);;All Files(*)") fileName = str(fileName) if not fileName: return aphla.catools.save_lat_epics(fileName, lat, mode='a') self.logger.info("snapshot created '%s'" % fileName) def saveMachSnapshot(self): mach, lat = self.getCurrentMachLattice() dpath = self._prepare_parent_dirs(mach) if not dpath: QMessageBox.warning(self, "Abort", "Aborted") return dt = datetime.datetime.now() fname = os.path.join(dpath, dt.strftime("snapshot_%d_%H%M%S.hdf5")) fileName = QtGui.QFileDialog.getSaveFileName( self, "Save Lattice Snapshot Data", fname, "Data Files (*.h5 *.hdf5);;All Files(*)") if not fileName: return fileName = str(fileName) import h5py f = h5py.File(str(fileName), 'w') f.close() self.logger.info("clean snapshot file created: '%s'" % fileName) for k,lat in self._mach[mach][0].items(): aphla.catools.save_lat_epics(fileName, lat, mode='a') self.logger.info("lattice snapshot appended for '%s'" % lat.name) def openSnapshot(self): #self.logger.info("loading snapshot?") latdict = dict([(k,v[0]) for k,v in self._mach.items()]) mach, lat = self.getCurrentMachLattice() lv = LatSnapshotMain(self, latdict, mach, self.logger) lv.setWindowFlags(Qt.Window) #self.logger.info("initialized") #lv.loadLatSnapshotH5() lv.exec_() def openTunePlot(self): mach, lat = self.getCurrentMachLattice() nu = lat.getElementList('tune') pvs = [(e.pv(field="x", handle="readback")[0], e.pv(field="y", handle="readback")[0]) for e in nu] labels = [e.name for e in nu] twiss = lat.getElementList("VA") pvs.extend([(e.pv(field="nux", handle="readback")[0], e.pv(field="nuy", handle="readback")[0]) for e in twiss]) labels.extend([e.name for e in twiss]) p = ApMdiSubPlot(pvs=pvs, labels=labels, dtype = "Tunes") #QObject.installEventFilter(p.aplot) #p.data = ManagedPvData(pvm, s, pvs, element=elemnames, # label="{0}.{1}".format(elem,field)) p.setAttribute(Qt.WA_DeleteOnClose) p.setWindowTitle("[%s.%s] Tunes" % (mach, lat.name)) self.connect(p, SIGNAL("elementSelected(PyQt_PyObject)"), self.elementSelected) self.connect(p, SIGNAL("destroyed()"), self.subPlotDestroyed) #p.updatePlot() # set the zoom stack #p.aplot.setErrorBar(self.error_bar) #p.wid.autoScaleXY() #p.aplot.replot() self.mdiarea.addSubWindow(p) #print "Show" p.show() def openNewPlot(self): mach, lat = self.getCurrentMachLattice() fl = QtGui.QFormLayout() fl.addRow("Machine", QtGui.QLabel("%s" % mach)) fl.addRow("Lattice", QtGui.QLabel("%s" % lat.name)) elem, fld = QtGui.QLineEdit(), QtGui.QLineEdit() fl.addRow("Elements", elem) fl.addRow("Field", fld) dtype = QtGui.QComboBox() for tx in ["Array", "Waveform", "Time Series"]: dtype.addItem(tx) fl.addRow("Data Type", dtype) dlg = QtGui.QDialog() bx = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel) self.connect(bx, SIGNAL("accepted()"), dlg.accept) self.connect(bx, SIGNAL("rejected()"), dlg.reject) h1 = QtGui.QHBoxLayout() h1.addStretch() h1.addWidget(bx) v1 = QtGui.QVBoxLayout() v1.addLayout(fl) v1.addLayout(h1) dlg.setLayout(v1) if dlg.exec_(): self.newElementPlots(str(elem.text()), str(fld.text()), machlat=(mach, lat), dtype = str(dtype.currentText())) def click_markfam(self, on): famname = self.sender().text() mks = [] mach, lat = self.getCurrentMachLattice() # need to convert to python str for elem in lat.getElementList(str(famname)): if elem.family != famname: continue if elem.virtual: continue mks.append([elem.name, 0.5*(elem.sb+elem.se)]) for w in self.mdiarea.subWindowList(): w.setMarkers(mks, on) #print self._machlat.keys() def _reset_correctors(self): self.logger.info("reset correctors") aphla.hlalib._reset_trims() def _reset_quadrupoles(self): self.logger.info("reset quadrupoles") aphla.hlalib._reset_quad() def _random_hkick(self): mach, lat = self.getCurrentMachLattice() hcors = lat.getElementList('HCOR') for k in range(3): i = np.random.randint(len(hcors)) self.logger.info("Setting {0}/{1} HCOR".format(i, len(hcors))) hcors[i].x += np.random.rand() * 2e-6 def _random_vkick(self): mach, lat = self.getCurrentMachLattice() cors = lat.getElementList('VCOR') for k in range(3): i = np.random.randint(len(cors)) cors[i].y += np.random.rand() * 1e-6 self.logger.info("increased kicker '{0}' by 1e-7 ({1} {2})".format( cors[i].name, cors[i].y, cors[i].getUnit('y', None))) def viewDcctPlot(self, on): self.dcct.setVisible(on) def liveData(self, on): """Switch on/off live data taking""" self.live_orbit = on def scalePlot(self): w = self.mdiarea.currentSubWindow() if not w: return st, p = self.sender().text(), w.aplot if st == "Fit": p.scaleXBottom() p.scaleYLeft() # a hack bound = p.curvesBound() p.zoomer1.setZoomStack([bound]) elif st == "Fit In Y": p.scaleYLeft() elif st == "Zoom In Y": p.scaleYLeft(1./1.5) elif st == "Zoom Out Y": p.scaleYLeft(1.5) elif st == "Move Up": p.moveCurves(Qwt.QwtPlot.yLeft, 0.8) elif st == "Move Down": p.moveCurves(Qwt.QwtPlot.yLeft, -0.8) elif st == "Fit In X": p.scaleXBottom() elif st == "Zoom In X": p.scaleXBottom(1.0/1.5) elif st == "Zoom Out X": p.scaleXBottom(1.5) elif st == "Move Left": p.moveCurves(Qwt.QwtPlot.xBottom, 0.8) elif st == "Move Right": p.moveCurves(Qwt.QwtPlot.xBottom, -0.8) else: self.logger.error("unknow action '{0}'".format(st)) def getVisibleRange(self): w = self.mdiarea.currentSubWindow() if not w: mach, lat = self.getCurrentMachLattice() self.logger.warn("no active plot, use full range of {0}.{1}".format( mach, lat.name)) return lat.getLocationRange() else: return w.currentXlim() def getVisibleElements(self, elemname, sb = None, se = None): w = self.mdiarea.currentSubWindow() mach, lat = self.getCurrentMachLattice() elems = lat.getElementList(elemname) if sb is not None: elems = [e for e in elems if e.sb >= sb] if se is not None: elems = [e for e in elems if e.se <= se] self.logger.info("searching for '{0}' in range [{1}, {2}]".format( elemname, sb, se)) return elems def timerEvent(self, e): if e.timerId() != self.timerId: return #if not self.elemeditor.isHidden(): # self.elemeditor.updateModelData() #if self.live_orbit: # self.itimer += 1 # #self.updatePlots() # #self.updateStatus() # for w in self.mdiarea.subWindowList(): # if not isinstance(w, ApMdiSubPlot): continue # if not w.live: continue # w.updatePlot() # self.statusBar().showMessage("plot updated: {0}".format( # time.strftime("%F %T"))) #else: # self.statusBar().showMessage("live update disabled") def singleShot(self): for w in self.mdiarea.subWindowList(): if not isinstance(w, ApMdiSubPlot): continue w.updatePlot() self.statusBar().showMessage("plot updated: {0}".format( time.strftime("%F %T"))) def elementSelected(self, elems): """this action is ignored""" mach, lat, elemnames = elems #_lat = self._machlat[mach][lat] self.logger.info("element selected") #elemobjs = _lat.getElementList(elemnames) #self._elemed.addElements(elemobjs) def activeOrbitPlot(self, field): mach = str(self.machBox.currentText()) lat = str(self.latBox.currentText()) for w in self.mdiarea.subWindowList(): #print w.machine(), w.lattice(), w.data.yfield if not isinstance(w, ApMdiSubPlot): continue if w.machine() != mach: continue if w.lattice() != lat: continue if w.data.yfield != field: continue return w return None def createLocalBump_(self): """create local bump""" if self._dlgOrbitCor is None: bpms = ap.getElements("BPM") cors = ap.getElements("COR") self._dlgOrbitCor = OrbitCorrDlg(bpms, cors) #corbitdlg.resize(600, 500) self._dlgOrbitCor.setWindowTitle("Create Local Bump") self._dlgOrbitCor.show() self._dlgOrbitCor.raise_() self._dlgOrbitCor.activateWindow() def createLocalBump(self): """create local bump""" bpms = ap.getElements("BPM") cors = ap.getElements("COR") dlgOrbitCor = OrbitCorrDlg(bpms, cors, parent=self) #corbitdlg.resize(600, 500) dlgOrbitCor.setWindowTitle("Create Local Bump") dlgOrbitCor.show() dlgOrbitCor.raise_() dlgOrbitCor.activateWindow() dlgOrbitCor.setAttribute(Qt.WA_DeleteOnClose) def runBba(self): mach, lat = self.getCurrentMachLattice() bpms = [e for e in lat.getElementList('BPM') if e not in self.physics.deadelems] self.physics.runBba(bpms) def plotSVD(self): mach, lat = self.getCurrentMachLattice() if not lat.ormdata: QMessageBox.critical(self, "ORM SVD", "machine '%s' ORM data is not available" % \ mach, QMessageBox.Ok) return m, brec, trec = lat.ormdata.getMatrix(None, None, full=False, ignore=self.getDeadElements()) U, s, V = np.linalg.svd(m, full_matrices=True) #print np.shape(s), s self.sp = ApSvdPlot(s) self.sp.show() def tileSubWindowsHorizontally(self): pos = QtCore.QPoint(0, 0) subwins = self.mdiarea.subWindowList() for w in subwins: height = self.mdiarea.height()/len(subwins) rect = QtCore.QRect(0, 0, self.mdiarea.width(), height) w.setGeometry(rect) w.move(pos) pos.setY(pos.y() + w.height())
class DBManager(QMainWindow): def __init__(self, iface, parent=None): QMainWindow.__init__(self, parent) self.setAttribute(Qt.WA_DeleteOnClose) self.setupUi() self.iface = iface # restore the window state settings = QSettings() self.restoreGeometry( settings.value("/DB_Manager/mainWindow/geometry", QByteArray(), type=QByteArray)) self.restoreState( settings.value("/DB_Manager/mainWindow/windowState", QByteArray(), type=QByteArray)) self.connect(self.tabs, SIGNAL("currentChanged(int)"), self.tabChanged) self.connect(self.tree, SIGNAL("selectedItemChanged"), self.itemChanged) self.itemChanged(None) def closeEvent(self, e): self.unregisterAllActions() # save the window state settings = QSettings() settings.setValue("/DB_Manager/mainWindow/windowState", self.saveState()) settings.setValue("/DB_Manager/mainWindow/geometry", self.saveGeometry()) QMainWindow.closeEvent(self, e) def refreshItem(self, item=None): QApplication.setOverrideCursor(Qt.WaitCursor) try: if item is None: item = self.tree.currentItem() self.tree.refreshItem(item) # refresh item children in the db tree except BaseError as e: DlgDbError.showError(e, self) return finally: QApplication.restoreOverrideCursor() def itemChanged(self, item): QApplication.setOverrideCursor(Qt.WaitCursor) try: self.reloadButtons() self.refreshTabs() except BaseError as e: DlgDbError.showError(e, self) return finally: QApplication.restoreOverrideCursor() def reloadButtons(self): db = self.tree.currentDatabase() if not hasattr(self, '_lastDb'): self._lastDb = db elif db == self._lastDb: return # remove old actions if self._lastDb is not None: self.unregisterAllActions() # add actions of the selected database self._lastDb = db if self._lastDb is not None: self._lastDb.registerAllActions(self) def tabChanged(self, index): QApplication.setOverrideCursor(Qt.WaitCursor) try: self.refreshTabs() except BaseError as e: DlgDbError.showError(e, self) return finally: QApplication.restoreOverrideCursor() def refreshTabs(self): index = self.tabs.currentIndex() item = self.tree.currentItem() table = self.tree.currentTable() # enable/disable tabs self.tabs.setTabEnabled(self.tabs.indexOf(self.table), table is not None) self.tabs.setTabEnabled( self.tabs.indexOf(self.preview), table is not None and table.type in [table.VectorType, table.RasterType] and table.geomColumn is not None) # show the info tab if the current tab is disabled if not self.tabs.isTabEnabled(index): self.tabs.setCurrentWidget(self.info) current_tab = self.tabs.currentWidget() if current_tab == self.info: self.info.showInfo(item) elif current_tab == self.table: self.table.loadData(item) elif current_tab == self.preview: self.preview.loadPreview(item) def refreshActionSlot(self): self.info.setDirty() self.table.setDirty() self.preview.setDirty() self.refreshItem() def importActionSlot(self): db = self.tree.currentDatabase() if db is None: self.infoBar.pushMessage( self.tr( "No database selected or you are not connected to it."), QgsMessageBar.INFO, self.iface.messageTimeout()) return outUri = db.uri() schema = self.tree.currentSchema() if schema: outUri.setDataSource(schema.name, "", "", "") from .dlg_import_vector import DlgImportVector dlg = DlgImportVector(None, db, outUri, self) dlg.exec_() def exportActionSlot(self): table = self.tree.currentTable() if table is None: self.infoBar.pushMessage( self.tr("Select the table you want export to file."), QgsMessageBar.INFO, self.iface.messageTimeout()) return inLayer = table.toMapLayer() from .dlg_export_vector import DlgExportVector dlg = DlgExportVector(inLayer, table.database(), self) dlg.exec_() inLayer.deleteLater() def runSqlWindow(self): db = self.tree.currentDatabase() if db is None: self.infoBar.pushMessage( self.tr( "No database selected or you are not connected to it."), QgsMessageBar.INFO, self.iface.messageTimeout()) # force displaying of the message, it appears on the first tab (i.e. Info) self.tabs.setCurrentIndex(0) return from dlg_sql_window import DlgSqlWindow query = DlgSqlWindow(self.iface, db, self) dbname = db.connection().connectionName() tabname = self.tr("Query") + u" (%s)" % dbname index = self.tabs.addTab(query, tabname) self.tabs.setTabIcon(index, db.connection().icon()) self.tabs.setCurrentIndex(index) query.nameChanged.connect( functools.partial(self.update_query_tab_name, index, dbname)) def update_query_tab_name(self, index, dbname, queryname): if not queryname: queryname = self.tr("Query") tabname = u"%s (%s)" % (queryname, dbname) self.tabs.setTabText(index, tabname) def showSystemTables(self): self.tree.showSystemTables(self.actionShowSystemTables.isChecked()) def registerAction(self, action, menuName, callback=None): """ register an action to the manager's main menu """ if not hasattr(self, '_registeredDbActions'): self._registeredDbActions = {} if callback is not None: invoke_callback = lambda x: self.invokeCallback(callback) if menuName is None or menuName == "": self.addAction(action) if menuName not in self._registeredDbActions: self._registeredDbActions[menuName] = list() self._registeredDbActions[menuName].append(action) if callback is not None: QObject.connect(action, SIGNAL("triggered(bool)"), invoke_callback) return True # search for the menu actionMenu = None helpMenuAction = None for a in self.menuBar.actions(): if not a.menu() or a.menu().title() != menuName: continue if a.menu() != self.menuHelp: helpMenuAction = a actionMenu = a break # not found, add a new menu before the help menu if actionMenu is None: menu = QMenu(menuName, self) if helpMenuAction is not None: actionMenu = self.menuBar.insertMenu(helpMenuAction, menu) else: actionMenu = self.menuBar.addMenu(menu) menu = actionMenu.menu() menuActions = menu.actions() # get the placeholder's position to insert before it pos = 0 for pos in range(len(menuActions)): if menuActions[pos].isSeparator() and menuActions[pos].objectName( ).endswith("_placeholder"): menuActions[pos].setVisible(True) break if pos < len(menuActions): before = menuActions[pos] menu.insertAction(before, action) else: menu.addAction(action) actionMenu.setVisible(True) # show the menu if menuName not in self._registeredDbActions: self._registeredDbActions[menuName] = list() self._registeredDbActions[menuName].append(action) if callback is not None: QObject.connect(action, SIGNAL("triggered(bool)"), invoke_callback) return True def invokeCallback(self, callback, *params): """ Call a method passing the selected item in the database tree, the sender (usually a QAction), the plugin mainWindow and optionally additional parameters. This method takes care to override and restore the cursor, but also catches exceptions and displays the error dialog. """ QApplication.setOverrideCursor(Qt.WaitCursor) try: callback(self.tree.currentItem(), self.sender(), self, *params) except BaseError as e: # catch database errors and display the error dialog DlgDbError.showError(e, self) return finally: QApplication.restoreOverrideCursor() def unregisterAction(self, action, menuName): if not hasattr(self, '_registeredDbActions'): return if menuName is None or menuName == "": self.removeAction(action) if menuName in self._registeredDbActions: if self._registeredDbActions[menuName].count(action) > 0: self._registeredDbActions[menuName].remove(action) action.deleteLater() return True for a in self.menuBar.actions(): if not a.menu() or a.menu().title() != menuName: continue menu = a.menu() menuActions = menu.actions() menu.removeAction(action) if menu.isEmpty(): # hide the menu a.setVisible(False) if menuName in self._registeredDbActions: if self._registeredDbActions[menuName].count(action) > 0: self._registeredDbActions[menuName].remove(action) # hide the placeholder if there're no other registered actions if len(self._registeredDbActions[menuName]) <= 0: for i in range(len(menuActions)): if menuActions[i].isSeparator() and menuActions[ i].objectName().endswith("_placeholder"): menuActions[i].setVisible(False) break action.deleteLater() return True return False def unregisterAllActions(self): if not hasattr(self, '_registeredDbActions'): return for menuName in self._registeredDbActions: for action in list(self._registeredDbActions[menuName]): self.unregisterAction(action, menuName) del self._registeredDbActions def close_tab(self, index): widget = self.tabs.widget(index) if widget not in [self.info, self.table, self.preview]: self.tabs.removeTab(index) widget.deleteLater() def setupUi(self): self.setWindowTitle(self.tr("DB Manager")) self.setWindowIcon(QIcon(":/db_manager/icon")) self.resize(QSize(700, 500).expandedTo(self.minimumSizeHint())) # create central tab widget and add the first 3 tabs: info, table and preview self.tabs = QTabWidget() self.info = InfoViewer(self) self.tabs.addTab(self.info, self.tr("Info")) self.table = TableViewer(self) self.tabs.addTab(self.table, self.tr("Table")) self.preview = LayerPreview(self) self.tabs.addTab(self.preview, self.tr("Preview")) self.setCentralWidget(self.tabs) # display close button for all tabs but the first 3 ones, i.e. # HACK: just hide the close button where not needed (GS) self.tabs.setTabsClosable(True) self.tabs.tabCloseRequested.connect(self.close_tab) tabbar = self.tabs.tabBar() for i in range(3): btn = tabbar.tabButton(i, QTabBar.RightSide) if tabbar.tabButton( i, QTabBar.RightSide) else tabbar.tabButton( i, QTabBar.LeftSide) btn.resize(0, 0) btn.hide() # Creates layout for message bar self.layout = QGridLayout(self.info) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # init messageBar instance self.infoBar = QgsMessageBar(self.info) sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # create database tree self.dock = QDockWidget("Tree", self) self.dock.setObjectName("DB_Manager_DBView") self.dock.setFeatures(QDockWidget.DockWidgetMovable) self.tree = DBTree(self) self.dock.setWidget(self.tree) self.addDockWidget(Qt.LeftDockWidgetArea, self.dock) # create status bar self.statusBar = QStatusBar(self) self.setStatusBar(self.statusBar) # create menus self.menuBar = QMenuBar(self) self.menuDb = QMenu(self.tr("&Database"), self) self.menuBar.addMenu(self.menuDb) self.menuSchema = QMenu(self.tr("&Schema"), self) actionMenuSchema = self.menuBar.addMenu(self.menuSchema) self.menuTable = QMenu(self.tr("&Table"), self) actionMenuTable = self.menuBar.addMenu(self.menuTable) self.menuHelp = None # QMenu(self.tr("&Help"), self) # actionMenuHelp = self.menuBar.addMenu(self.menuHelp) self.setMenuBar(self.menuBar) # create toolbar self.toolBar = QToolBar("Default", self) self.toolBar.setObjectName("DB_Manager_ToolBar") self.addToolBar(self.toolBar) # create menus' actions # menu DATABASE sep = self.menuDb.addSeparator() sep.setObjectName("DB_Manager_DbMenu_placeholder") sep.setVisible(False) self.actionRefresh = self.menuDb.addAction( QIcon(":/db_manager/actions/refresh"), self.tr("&Refresh"), self.refreshActionSlot, QKeySequence("F5")) self.actionSqlWindow = self.menuDb.addAction( QIcon(":/db_manager/actions/sql_window"), self.tr("&SQL window"), self.runSqlWindow, QKeySequence("F2")) self.menuDb.addSeparator() self.actionClose = self.menuDb.addAction(QIcon(), self.tr("&Exit"), self.close, QKeySequence("CTRL+Q")) # menu SCHEMA sep = self.menuSchema.addSeparator() sep.setObjectName("DB_Manager_SchemaMenu_placeholder") sep.setVisible(False) actionMenuSchema.setVisible(False) # menu TABLE sep = self.menuTable.addSeparator() sep.setObjectName("DB_Manager_TableMenu_placeholder") sep.setVisible(False) self.actionImport = self.menuTable.addAction( QIcon(":/db_manager/actions/import"), self.tr("&Import layer/file"), self.importActionSlot) self.actionExport = self.menuTable.addAction( QIcon(":/db_manager/actions/export"), self.tr("&Export to file"), self.exportActionSlot) self.menuTable.addSeparator() #self.actionShowSystemTables = self.menuTable.addAction(self.tr("Show system tables/views"), self.showSystemTables) #self.actionShowSystemTables.setCheckable(True) #self.actionShowSystemTables.setChecked(True) actionMenuTable.setVisible(False) # add actions to the toolbar self.toolBar.addAction(self.actionRefresh) self.toolBar.addAction(self.actionSqlWindow) self.toolBar.addAction(self.actionImport) self.toolBar.addAction(self.actionExport)
class ComposerWrapper(QObject): """ Embeds custom STDM tools in a QgsComposer instance for managing map-based STDM document templates. """ dataSourceSelected = pyqtSignal(str) def __init__(self, composerView, iface): QObject.__init__(self, composerView) self._compView = composerView self._stdmTB = self.mainWindow().addToolBar("STDM") self._selectMoveAction = None self._iface = iface #Container for custom editor widgets self._widgetMappings = {} #Hide default dock widgets if not self.itemDock() is None: self.itemDock().hide() if not self.atlasDock() is None: self.atlasDock().hide() if not self.generalDock() is None: self.generalDock().hide() #Create dock widget for configuring STDM data source self._stdmDataSourceDock = QDockWidget( QApplication.translate("ComposerWrapper","STDM Data Source"), self.mainWindow()) self._stdmDataSourceDock.setObjectName("STDMDataSourceDock") self._stdmDataSourceDock.setMinimumWidth(300) self._stdmDataSourceDock.setFeatures(QDockWidget.DockWidgetMovable|QDockWidget.DockWidgetClosable) self.mainWindow().addDockWidget(Qt.RightDockWidgetArea, self._stdmDataSourceDock) self._dataSourceWidget = ComposerDataSourceSelector() self._stdmDataSourceDock.setWidget(self._dataSourceWidget) self._stdmDataSourceDock.show() #Re-insert dock widgets if not self.generalDock() is None: self.generalDock().show() if not self.itemDock() is None: self.itemDock().show() #Create dock widget for configuring STDM item properties self._stdmItemPropDock = QDockWidget( QApplication.translate("ComposerWrapper","STDM item properties"), self.mainWindow()) self._stdmItemPropDock.setObjectName("STDMItemDock") self._stdmItemPropDock.setMinimumWidth(300) self._stdmItemPropDock.setFeatures(QDockWidget.DockWidgetMovable|QDockWidget.DockWidgetClosable) self.mainWindow().addDockWidget(Qt.RightDockWidgetArea,self._stdmItemPropDock) self._stdmItemPropDock.show() #Re-arrange dock widgets and push up STDM data source dock widget if not self.generalDock() is None: self.mainWindow().splitDockWidget(self._stdmDataSourceDock, self.generalDock(),Qt.Vertical) if not self.itemDock() is None: self.mainWindow().splitDockWidget(self._stdmDataSourceDock, self.itemDock(),Qt.Vertical) if not self.generalDock() is None: self.mainWindow().tabifyDockWidget(self.generalDock(), self.itemDock()) if not self.itemDock() is None: self.mainWindow().splitDockWidget(self.itemDock(), self._stdmItemPropDock, Qt.Vertical) #Set focus on composition properties window if not self.generalDock() is None: self.generalDock().activateWindow() self.generalDock().raise_() #Connect signals self.composition().itemRemoved.connect(self._onItemRemoved) self._dataSourceWidget.cboDataSource.currentIndexChanged[str].connect( self.propagateDataSourceSelection ) self.composerView().selectedItemChanged.connect(self._onItemSelected) #Current template document file self._currDocFile = None def _removeActions(self): """ Remove inapplicable actions and their corresponding toolbars and menus. """ removeActions = ["mActionSaveProject","mActionNewComposer","mActionDuplicateComposer"] composerToolbar = self.composerMainToolBar() if composerToolbar != None: saveProjectAction = None for itemAction in composerToolbar.actions(): if itemAction.objectName() == "mActionSaveProject": saveProjectAction = itemAction break if saveProjectAction != None: composerMenu = saveProjectAction.menu() def configure(self): #Create instances of custom STDM composer item configurations for ciConfig in ComposerItemConfig.itemConfigurations: ciConfigObj = ciConfig(self) def addWidgetMapping(self,uniqueIdentifier,widget): """ Add custom STDM editor widget based on the unique identifier of the composer item """ self._widgetMappings[uniqueIdentifier] = widget def widgetMappings(self): """ Returns a dictionary containing uuid values of composer items linked to STDM widgets. """ return self._widgetMappings def clearWidgetMappings(self): """ Resets the widget mappings collection. """ self._widgetMappings = {} def mainWindow(self): """ Returns the QMainWindow used by the composer view. """ return self._compView.composerWindow() def stdmToolBar(self): """ Returns the instance of the STDM toolbar added to the QgsComposer. """ return self._stdmTB def composerView(self): """ Returns the composer view. """ return self._compView def composition(self): """ Returns the QgsComposition instance used in the composer view. """ return self._compView.composition() def composerItemToolBar(self): """ Returns the toolbar containing actions for adding composer items. """ return self.mainWindow().findChild(QToolBar,"mItemToolbar") def composerMainToolBar(self): """ Returns the toolbar containing actions for managing templates. """ return self.mainWindow().findChild(QToolBar,"mComposerToolbar") def selectMoveAction(self): """ Returns the QAction for selecting or moving composer items. """ if self.composerItemToolBar() != None: if self._selectMoveAction == None: for itemAction in self.composerItemToolBar().actions(): if itemAction.objectName() == "mActionSelectMoveItem": self._selectMoveAction = itemAction break return self._selectMoveAction def checkedItemAction(self): """ Returns the currently selected composer item action. """ if self.selectMoveAction() != None: return self.selectMoveAction().actionGroup().checkedAction() return None def itemDock(self): """ Get the 'Item Properties' dock widget. """ return self.mainWindow().findChild(QDockWidget,"ItemDock") def atlasDock(self): """ Get the 'Atlas generation' dock widget. """ return self.mainWindow().findChild(QDockWidget,"AtlasDock") def generalDock(self): """ Get the 'Composition' dock widget. """ return self.mainWindow().findChild(QDockWidget,"CompositionDock") def stdmDataSourceDock(self): """ Returns the STDM data source dock widget. """ return self._stdmDataSourceDock def stdmItemDock(self): """ Returns the STDM item dock widget. """ return self._stdmItemPropDock def documentFile(self): """ Returns the QFile instance associated with the current document. 'None' will be returned for new, unsaved documents. """ return self._currDocFile def setDocumentFile(self,docFile): """ Sets the document file. """ if not isinstance(docFile,QFile): return self._currDocFile = docFile def selectedDataSource(self): """ Returns the name of the data source specified by the user. """ return self._stdmDataSourceDock.widget().cboDataSource.currentText() def selectedDataSourceCategory(self): """ Returns the category (view or table) that the data source belongs to. """ if self.stdmDataSourceDock().widget() != None: return self.stdmDataSourceDock().widget().category() return "" def propagateDataSourceSelection(self, dataSourceName): """ Propagates the signal when a user select a data source. Listening objects can hook on to it. """ self.dataSourceSelected.emit(dataSourceName) def composer_items(self): """ :return: Returns a list of custom composer items. :rtype: list """ return [self.composition().getComposerItemById(uuid) for uuid in self._widgetMappings.keys() if not self.composition().getComposerItemById(uuid) is None] def _clear_composition(self): """ Removes composer items which, otherwise, are causing QGIS to crash when loading a subsequent document template. """ items = self.composition().items() for c_item in items: if isinstance(c_item, QgsComposerItem) and not isinstance(c_item, QgsPaperItem): if c_item.uuid() in self._widgetMappings: #Remove corresponding widget as well as reference in the collection del self._widgetMappings[c_item.uuid()] self.composition().removeItem(c_item) self.composition().itemRemoved.emit(c_item) del c_item self.composition().undoStack().clear() self.composition().itemsModel().clear() def create_new_document_designer(self, file_path): """ Creates a new document designer and loads the document template defined in file path. :param file_path: Path to document template :type file_path: str """ document_designer = self._iface.createNewComposer("STDM Document Designer") #Embed STDM customizations cw = ComposerWrapper(document_designer, self._iface) cw.configure() #Load template cw.loadTemplate(file_path) def loadTemplate(self, filePath): """ Loads a document template into the view and updates the necessary STDM-related composer items. """ if not QFile.exists(filePath): QMessageBox.critical(self.composerView(), QApplication.translate("OpenTemplateConfig", "Open Template Error"), QApplication.translate("OpenTemplateConfig", "The specified template does not exist.")) return templateFile = QFile(filePath) if not templateFile.open(QIODevice.ReadOnly): QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper", "Open Operation Error"), "{0}\n{1}".format(QApplication.translate( "ComposerWrapper", "Cannot read template file."), templateFile.errorString() )) return templateDoc = QDomDocument() if templateDoc.setContent(templateFile): table_config_collection = TableConfigurationCollection.create(templateDoc) ''' First load vector layers for the table definitions in the config collection before loading rhe composition from file. ''' load_table_layers(table_config_collection) #Load items into the composition and configure STDM data controls self.composition().loadFromTemplate(templateDoc) self.clearWidgetMappings() #Load data controls composerDS = ComposerDataSource.create(templateDoc) #Set title by appending template name title = QApplication.translate("STDMPlugin", "STDM Document Designer") composer_el = templateDoc.documentElement() if not composer_el is None: template_name = "" if composer_el.hasAttribute("title"): template_name = composer_el.attribute("title", "") elif composer_el.hasAttribute("_title"): template_name = composer_el.attribute("_title", "") if template_name: win_title = u"{0} - {1}".format(title, template_name) self.mainWindow().setWindowTitle(template_name) self._configure_data_controls(composerDS) #Load symbol editors spatialFieldsConfig = SpatialFieldsConfiguration.create(templateDoc) self._configureSpatialSymbolEditor(spatialFieldsConfig) #Load photo editors photo_config_collection = PhotoConfigurationCollection.create(templateDoc) self._configure_photo_editors(photo_config_collection) #Load table editors self._configure_table_editors(table_config_collection) #Load chart property editors chart_config_collection = ChartConfigurationCollection.create(templateDoc) self._configure_chart_editors(chart_config_collection) self._sync_ids_with_uuids() def saveTemplate(self): """ Creates and saves a new document template. """ #Validate if the user has specified the data source if not self.selectedDataSource(): QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Error"), QApplication.translate("ComposerWrapper","Please specify the " "data source name for the document composition.")) return #If it is a new unsaved document template then prompt for the document name. docFile = self.documentFile() if docFile is None: docName,ok = QInputDialog.getText(self.composerView(), QApplication.translate("ComposerWrapper","Template Name"), QApplication.translate("ComposerWrapper","Please enter the template name below"), ) if ok and docName: templateDir = self._composerTemplatesPath() if templateDir is None: QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Error"), QApplication.translate("ComposerWrapper", "Directory for document templates cannot not be found.")) return absPath = templateDir + "/" + docName + ".sdt" #Check if there is an existing document with the same name caseInsenDic = CaseInsensitiveDict(documentTemplates()) if docName in caseInsenDic: result = QMessageBox.warning(self.composerView(), QApplication.translate("ComposerWrapper", "Existing Template"), u"'{0}' {1}.\nDo you want to replace the " "existing template?".format(docName, QApplication.translate("ComposerWrapper", "already exists")), QMessageBox.Yes|QMessageBox.No) if result == QMessageBox.Yes: #Delete the existing template delFile = QFile(absPath) remStatus = delFile.remove() if not remStatus: QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper", "Delete Error"), "'{0}' {1}.".format(docName, QApplication.translate("ComposerWrapper", "template could not be removed by the system," " please remove it manually from the document templates directory."))) return else: return docFile= QFile(absPath) else: return docFileInfo = QFileInfo(docFile) if not docFile.open(QIODevice.WriteOnly): QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper", "Save Operation Error"), "{0}\n{1}".format(QApplication.translate("ComposerWrapper", "Could not save template file."), docFile.errorString() )) return templateDoc = QDomDocument() template_name = docFileInfo.completeBaseName() self._writeXML(templateDoc, template_name) if docFile.write(templateDoc.toByteArray()) == -1: QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Save Error"), QApplication.translate("ComposerWrapper","Could not save template file.")) return else: self.mainWindow().setWindowTitle(template_name) docFile.close() def _writeXML(self, xml_doc, doc_name): """ Write the template configuration into the XML document. """ #Write default composer configuration composer_element = xml_doc.createElement("Composer") composer_element.setAttribute("title", doc_name) composer_element.setAttribute("visible", 1) xml_doc.appendChild(composer_element) self.composition().writeXML(composer_element, xml_doc) #Write STDM data field configurations dataSourceElement = ComposerDataSource.domElement(self, xml_doc) composer_element.appendChild(dataSourceElement) #Write spatial field configurations spatialColumnsElement = SpatialFieldsConfiguration.domElement(self, xml_doc) dataSourceElement.appendChild(spatialColumnsElement) #Write photo configuration tables_element = PhotoConfigurationCollection.dom_element(self, xml_doc) dataSourceElement.appendChild(tables_element) #Write table configuration tables_element = TableConfigurationCollection.dom_element(self, xml_doc) dataSourceElement.appendChild(tables_element) #Write chart configuration charts_element = ChartConfigurationCollection.dom_element(self, xml_doc) dataSourceElement.appendChild(charts_element) def _configure_data_controls(self, composer_data_source): """ Configure the data source and data field controls based on the composer data source configuration. """ if not self.stdmDataSourceDock().widget() is None: #Set data source dataSourceWidget = self.stdmDataSourceDock().widget() dataSourceWidget.setCategory(composer_data_source.category()) dataSourceWidget.setSelectedSource(composer_data_source.name()) #Set data field controls for composerId in composer_data_source.dataFieldMappings().reverse: #Use composer item id since the uuid is stripped off composerItem = self.composition().getComposerItemById(composerId) if not composerItem is None: compFieldSelector = ComposerFieldSelector(self, composerItem, self.composerView()) compFieldSelector.selectFieldName(composer_data_source.dataFieldName(composerId)) #Add widget to the collection but now use the current uuid of the composition item self.addWidgetMapping(composerItem.uuid(), compFieldSelector) def _configureSpatialSymbolEditor(self,spatial_field_config): """ Configure symbol editor controls. """ if not self.stdmDataSourceDock().widget() is None: for item_id, spFieldsMappings in spatial_field_config.spatialFieldsMapping().iteritems(): mapItem = self.composition().getComposerItemById(item_id) if not mapItem is None: composerSymbolEditor = ComposerSymbolEditor(self, self.composerView()) composerSymbolEditor.add_spatial_field_mappings(spFieldsMappings) #Add widget to the collection but now use the current uuid of the composer map self.addWidgetMapping(mapItem.uuid(), composerSymbolEditor) def _configure_photo_editors(self, photo_config_collection): """ Creates widgets for editing photo data sources. :param photo_config_collection: PhotoConfigurationCollection instance. :type photo_config_collection: PhotoConfigurationCollection """ if self.stdmDataSourceDock().widget() is None: return for item_id, photo_config in photo_config_collection.mapping().iteritems(): pic_item = self.composition().getComposerItemById(item_id) if not pic_item is None: photo_editor = ComposerPhotoDataSourceEditor(self, self.composerView()) photo_editor.set_configuration(photo_config) self.addWidgetMapping(pic_item.uuid(), photo_editor) def _configure_chart_editors(self, chart_config_collection): """ Creates widgets for editing chart properties. :param chart_config_collection: ChartConfigurationCollection instance. :type chart_config_collection: ChartConfigurationCollection """ if self.stdmDataSourceDock().widget() is None: return for item_id, chart_config in chart_config_collection.mapping().iteritems(): chart_item = self.composition().getComposerItemById(item_id) if not chart_item is None: chart_editor = ComposerChartConfigEditor(self, self.composerView()) chart_editor.set_configuration(chart_config) self.addWidgetMapping(chart_item.uuid(), chart_editor) def _configure_table_editors(self, table_config_collection): """ Creates widgets for editing table data sources. :param table_config_collection: TableConfigurationCollection instance. :type table_config_collection: TableConfigurationCollection """ if self.stdmDataSourceDock().widget() is None: return for item_id, table_config in table_config_collection.mapping().iteritems(): table_item = self.composition().getComposerItemById(item_id) if not table_item is None: table_editor = ComposerTableDataSourceEditor(self, table_item, self.composerView()) table_editor.set_configuration(table_config) self.addWidgetMapping(table_item.uuid(), table_editor) def _sync_ids_with_uuids(self): """ Matches IDs of custom STDM items with the corresponding UUIDs. This is applied when loading existing templates so that the saved document contains a matching pair of ID and UUID for each composer item. """ items = self._widgetMappings.keys() for item_uuid in self._widgetMappings.keys(): item = self.composition().getComposerItemByUuid(item_uuid) if not item is None: item.setId(item_uuid) def _composerTemplatesPath(self): """ Reads the path of composer templates in the registry. """ regConfig = RegistryConfig() keyName = "ComposerTemplates" valueCollection = regConfig.read([keyName]) if len(valueCollection) == 0: return None else: return valueCollection[keyName] def _onItemRemoved(self,item): """ Slot raised when a composer item is removed from the scene. """ """ Code will not work since a QObject instance is returned instead of a QgsComposerItem if item.uuid() in self._widgetMappings: del self._widgetMappings[item.uuid()] """ pass def _onItemSelected(self, item): """ Slot raised when a composer item is selected. Load the corresponding field selector if the selection is an STDM data field label. QComposerLabel is returned as a QObject in the slot argument hence, we have resorted to capturing the current selected items in the composition. """ selectedItems = self.composition().selectedComposerItems() if len(selectedItems) == 0: self._stdmItemPropDock.setWidget(None) elif len(selectedItems) == 1: composer_item = selectedItems[0] if composer_item.uuid() in self._widgetMappings: stdmWidget = self._widgetMappings[composer_item.uuid()] if stdmWidget == self._stdmItemPropDock.widget(): return else: self._stdmItemPropDock.setWidget(stdmWidget) #Playing it safe in applying the formatting for the editor controls where applicable itemFormatter = None if isinstance(composer_item, QgsComposerArrow): itemFormatter = LineFormatter() elif isinstance(composer_item, QgsComposerLabel): itemFormatter = DataLabelFormatter() elif isinstance(composer_item, QgsComposerMap): itemFormatter = MapFormatter() elif isinstance(composer_item, QgsComposerPicture): """ Use widget attribute to distinguish type i.e. whether it is a photo, graph etc. """ editor_widget = self._widgetMappings[composer_item.uuid()] if isinstance(editor_widget, ComposerPhotoDataSourceEditor): itemFormatter = PhotoFormatter() elif isinstance(editor_widget, ComposerChartConfigEditor): itemFormatter = ChartFormatter() elif isinstance(composer_item, QgsComposerAttributeTable): itemFormatter = TableFormatter() if not itemFormatter is None: itemFormatter.apply(composer_item, self, True) else: self._stdmItemPropDock.setWidget(None) elif len(selectedItems) > 1: self._stdmItemPropDock.setWidget(None)
class ComposerWrapper(QObject): """ Embeds custom STDM tools in a QgsComposer instance for managing map-based STDM document templates. """ dataSourceSelected = pyqtSignal(str) def __init__(self, composerView, iface): QObject.__init__(self, composerView) self._compView = composerView self._stdmTB = self.mainWindow().addToolBar("STDM Document Designer") self._selectMoveAction = None self._iface = iface #Container for custom editor widgets self._widgetMappings = {} #Hide default dock widgets if self.itemDock() is not None: self.itemDock().hide() if self.atlasDock() is not None: self.atlasDock().hide() if self.generalDock() is not None: self.generalDock().hide() # Remove default toolbars self._remove_composer_toolbar('mAtlasToolbar') self._remove_composer_toolbar('mComposerToolbar') #Create dock widget for configuring STDM data source self._stdmDataSourceDock = QDockWidget( QApplication.translate("ComposerWrapper", "STDM Data Source"), self.mainWindow()) self._stdmDataSourceDock.setObjectName("STDMDataSourceDock") self._stdmDataSourceDock.setMinimumWidth(300) self._stdmDataSourceDock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable) self.mainWindow().addDockWidget(Qt.RightDockWidgetArea, self._stdmDataSourceDock) self._dataSourceWidget = ComposerDataSourceSelector() self._stdmDataSourceDock.setWidget(self._dataSourceWidget) self._stdmDataSourceDock.show() #Re-insert dock widgets if self.generalDock() is not None: self.generalDock().show() if self.itemDock() is not None: self.itemDock().show() #Create dock widget for configuring STDM item properties self._stdmItemPropDock = QDockWidget( QApplication.translate("ComposerWrapper", "STDM item properties"), self.mainWindow()) self._stdmItemPropDock.setObjectName("STDMItemDock") self._stdmItemPropDock.setMinimumWidth(300) self._stdmItemPropDock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable) self.mainWindow().addDockWidget(Qt.RightDockWidgetArea, self._stdmItemPropDock) self._stdmItemPropDock.show() #Re-arrange dock widgets and push up STDM data source dock widget if self.generalDock() is not None: self.mainWindow().splitDockWidget(self._stdmDataSourceDock, self.generalDock(), Qt.Vertical) if self.itemDock() is not None: self.mainWindow().splitDockWidget(self._stdmDataSourceDock, self.itemDock(), Qt.Vertical) if self.generalDock() is not None: self.mainWindow().tabifyDockWidget(self.generalDock(), self.itemDock()) if self.itemDock() is not None: self.mainWindow().splitDockWidget(self.itemDock(), self._stdmItemPropDock, Qt.Vertical) #Set focus on composition properties window if self.generalDock() is not None: self.generalDock().activateWindow() self.generalDock().raise_() #Connect signals self.composition().itemRemoved.connect(self._onItemRemoved) self._dataSourceWidget.cboDataSource.currentIndexChanged.connect( self.propagateDataSourceSelection) self.composerView().selectedItemChanged.connect(self._onItemSelected) #Current template document file self._currDocFile = None #Copy of template document file self._copy_template_file = None self._selected_item_uuid = unicode() self._current_ref_table_index = -1 @property def copy_template_file(self): return self._copy_template_file @copy_template_file.setter def copy_template_file(self, value): self._copy_template_file = value @property def selected_item_uuid(self): return self._selected_item_uuid @selected_item_uuid.setter def selected_item_uuid(self, uuid): self._selected_item_uuid = uuid @property def current_ref_table_index(self): return self._current_ref_table_index @current_ref_table_index.setter def current_ref_table_index(self, value): self._current_ref_table_index = value def _remove_composer_toolbar(self, object_name): """ Removes toolbars from composer window. :param object_name: The object name of the toolbar :type object_name: String :return: None :rtype: NoneType """ composers = self._iface.activeComposers() for i in range(len(composers)): comp = composers[i].composerWindow() widgets = comp.findChildren(QToolBar, object_name) for widget in widgets: comp.removeToolBar(widget) def _removeActions(self): """ Remove inapplicable actions and their corresponding toolbars and menus. """ removeActions = [ "mActionSaveProject", "mActionNewComposer", "mActionDuplicateComposer" ] composerToolbar = self.composerMainToolBar() if composerToolbar != None: saveProjectAction = None for itemAction in composerToolbar.actions(): if itemAction.objectName() == "mActionSaveProject": saveProjectAction = itemAction break if saveProjectAction != None: composerMenu = saveProjectAction.menu() def configure(self): #Create instances of custom STDM composer item configurations for ciConfig in ComposerItemConfig.itemConfigurations: ciConfig(self) def addWidgetMapping(self, uniqueIdentifier, widget): """ Add custom STDM editor widget based on the unique identifier of the composer item """ self._widgetMappings[uniqueIdentifier] = widget def widgetMappings(self): """ Returns a dictionary containing uuid values of composer items linked to STDM widgets. """ return self._widgetMappings def clearWidgetMappings(self): """ Resets the widget mappings collection. """ self._widgetMappings = {} def mainWindow(self): """ Returns the QMainWindow used by the composer view. """ return self._compView.composerWindow() def stdmToolBar(self): """ Returns the instance of the STDM toolbar added to the QgsComposer. """ return self._stdmTB def composerView(self): """ Returns the composer view. """ return self._compView def composition(self): """ Returns the QgsComposition instance used in the composer view. """ return self._compView.composition() def composerItemToolBar(self): """ Returns the toolbar containing actions for adding composer items. """ return self.mainWindow().findChild(QToolBar, "mItemToolbar") def composerMainToolBar(self): """ Returns the toolbar containing actions for managing templates. """ return self.mainWindow().findChild(QToolBar, "mComposerToolbar") def selectMoveAction(self): """ Returns the QAction for selecting or moving composer items. """ if self.composerItemToolBar() != None: if self._selectMoveAction == None: for itemAction in self.composerItemToolBar().actions(): if itemAction.objectName() == "mActionSelectMoveItem": self._selectMoveAction = itemAction break return self._selectMoveAction def checkedItemAction(self): """ Returns the currently selected composer item action. """ if self.selectMoveAction() != None: return self.selectMoveAction().actionGroup().checkedAction() return None def itemDock(self): """ Get the 'Item Properties' dock widget. """ return self.mainWindow().findChild(QDockWidget, "ItemDock") def atlasDock(self): """ Get the 'Atlas generation' dock widget. """ return self.mainWindow().findChild(QDockWidget, "AtlasDock") def generalDock(self): """ Get the 'Composition' dock widget. """ return self.mainWindow().findChild(QDockWidget, "CompositionDock") def stdmDataSourceDock(self): """ Returns the STDM data source dock widget. """ return self._stdmDataSourceDock def stdmItemDock(self): """ Returns the STDM item dock widget. """ return self._stdmItemPropDock def documentFile(self): """ Returns the QFile instance associated with the current document. 'None' will be returned for new, unsaved documents. """ return self._currDocFile def setDocumentFile(self, docFile): """ Sets the document file. """ if not isinstance(docFile, QFile): return self._currDocFile = docFile def selectedDataSource(self): """ Returns the name of the data source specified by the user. """ row = self._stdmDataSourceDock.widget().cboDataSource.currentIndex() table_name = self._stdmDataSourceDock.widget().cboDataSource.itemData( row) return table_name def selected_referenced_table(self): """ :return: Returns the name of currently specified referenced table name. :rtype: str """ return self._stdmDataSourceDock.widget().referenced_table_name() def selectedDataSourceCategory(self): """ Returns the category (view or table) that the data source belongs to. """ if not self.stdmDataSourceDock().widget() is None: return self.stdmDataSourceDock().widget().category() return "" def propagateDataSourceSelection(self, index): """ Propagates the signal when a user select a data source. Listening objects can hook on to it. """ data_source_name = self._stdmDataSourceDock.widget( ).cboDataSource.itemData(index) self.dataSourceSelected.emit(data_source_name) def composer_items(self): """ :return: Returns a list of custom composer items. :rtype: list """ return [ self.composition().getComposerItemById(uuid) for uuid in self._widgetMappings.keys() if not self.composition().getComposerItemById(uuid) is None ] def _clear_composition(self): """ Removes composer items which, otherwise, are causing QGIS to crash when loading a subsequent document template. """ items = self.composition().items() for c_item in items: if isinstance(c_item, QgsComposerItem) and not isinstance( c_item, QgsPaperItem): if c_item.uuid() in self._widgetMappings: #Remove corresponding widget as well as reference in the collection del self._widgetMappings[c_item.uuid()] self.composition().removeItem(c_item) self.composition().itemRemoved.emit(c_item) del c_item self.composition().undoStack().clear() self.composition().itemsModel().clear() def create_new_document_designer(self, file_path): """ Creates a new document designer and loads the document template defined in file path. :param file_path: Path to document template :type file_path: str """ if len(self.composerView().items()) == 3: self.composerView().composerWindow().close() document_designer = self._iface.createNewComposer( "STDM Document Designer") #Embed STDM customizations cw = ComposerWrapper(document_designer, self._iface) cw.configure() #Load template cw.loadTemplate(file_path) def loadTemplate(self, filePath): """ Loads a document template into the view and updates the necessary STDM-related composer items. """ if not QFile.exists(filePath): QMessageBox.critical( self.composerView(), QApplication.translate("OpenTemplateConfig", "Open Template Error"), QApplication.translate( "OpenTemplateConfig", "The specified template does not exist.")) return copy_file = filePath.replace('sdt', 'cpy') # remove existing copy file if QFile.exists(copy_file): copy_template = QFile(copy_file) copy_template.remove() orig_template_file = QFile(filePath) self.setDocumentFile(orig_template_file) # make a copy of the original orig_template_file.copy(copy_file) #templateFile = QFile(filePath) # work with copy templateFile = QFile(copy_file) self.copy_template_file = templateFile if not templateFile.open(QIODevice.ReadOnly): QMessageBox.critical( self.composerView(), QApplication.translate("ComposerWrapper", "Open Operation Error"), "{0}\n{1}".format( QApplication.translate("ComposerWrapper", "Cannot read template file."), templateFile.errorString())) return templateDoc = QDomDocument() if templateDoc.setContent(templateFile): table_config_collection = TableConfigurationCollection.create( templateDoc) ''' First load vector layers for the table definitions in the config collection before loading the composition from file. ''' load_table_layers(table_config_collection) self.clearWidgetMappings() #Load items into the composition and configure STDM data controls self.composition().loadFromTemplate(templateDoc) #Load data controls composerDS = ComposerDataSource.create(templateDoc) #Set title by appending template name title = QApplication.translate("STDMPlugin", "STDM Document Designer") composer_el = templateDoc.documentElement() if not composer_el is None: template_name = "" if composer_el.hasAttribute("title"): template_name = composer_el.attribute("title", "") elif composer_el.hasAttribute("_title"): template_name = composer_el.attribute("_title", "") if template_name: win_title = u"{0} - {1}".format(title, template_name) self.mainWindow().setWindowTitle(template_name) self._configure_data_controls(composerDS) #Load symbol editors spatialFieldsConfig = SpatialFieldsConfiguration.create( templateDoc) self._configureSpatialSymbolEditor(spatialFieldsConfig) #Load photo editors photo_config_collection = PhotoConfigurationCollection.create( templateDoc) self._configure_photo_editors(photo_config_collection) # Load table editors self._configure_table_editors(table_config_collection) items = self.composerView().items() items = [] #Load chart property editors chart_config_collection = ChartConfigurationCollection.create( templateDoc) self._configure_chart_editors(chart_config_collection) # Load QR code property editors qrc_config_collection = QRCodeConfigurationCollection.create( templateDoc) self._configure_qr_code_editors(qrc_config_collection) self._sync_ids_with_uuids() def saveTemplate(self): """ Creates and saves a new document template. """ # Validate if the user has specified the data source if not self.selectedDataSource(): QMessageBox.critical( self.composerView(), QApplication.translate("ComposerWrapper", "Error"), QApplication.translate( "ComposerWrapper", "Please specify the " "data source name for the document composition.")) return # Assert if the referenced table name has been set if not self.selected_referenced_table(): QMessageBox.critical( self.composerView(), QApplication.translate("ComposerWrapper", "Error"), QApplication.translate( "ComposerWrapper", "Please specify the " "referenced table name for the selected data source.")) return # If it is a new unsaved document template then prompt for the document name. docFile = self.documentFile() if docFile is None: docName, ok = QInputDialog.getText( self.composerView(), QApplication.translate("ComposerWrapper", "Template Name"), QApplication.translate("ComposerWrapper", "Please enter the template name below"), ) if not ok: return if ok and not docName: QMessageBox.critical( self.composerView(), QApplication.translate("ComposerWrapper", "Error"), QApplication.translate("ComposerWrapper", "Please enter a template name!")) self.saveTemplate() if ok and docName: templateDir = self._composerTemplatesPath() if templateDir is None: QMessageBox.critical( self.composerView(), QApplication.translate("ComposerWrapper", "Error"), QApplication.translate( "ComposerWrapper", "Directory for document templates cannot not be found." )) return absPath = templateDir + "/" + docName + ".sdt" #Check if there is an existing document with the same name caseInsenDic = CaseInsensitiveDict(documentTemplates()) if docName in caseInsenDic: result = QMessageBox.warning( self.composerView(), QApplication.translate("ComposerWrapper", "Existing Template"), u"'{0}' {1}.\nDo you want to replace the " "existing template?".format( docName, QApplication.translate("ComposerWrapper", "already exists")), QMessageBox.Yes | QMessageBox.No) if result == QMessageBox.Yes: #Delete the existing template delFile = QFile(absPath) remStatus = delFile.remove() if not remStatus: QMessageBox.critical( self.composerView(), QApplication.translate("ComposerWrapper", "Delete Error"), "'{0}' {1}.".format( docName, QApplication.translate( "ComposerWrapper", "template could not be removed by the system," " please remove it manually from the document templates directory." ))) return else: return docFile = QFile(absPath) else: return docFileInfo = QFileInfo(docFile) if not docFile.open(QIODevice.WriteOnly): QMessageBox.critical( self.composerView(), QApplication.translate("ComposerWrapper", "Save Operation Error"), "{0}\n{1}".format( QApplication.translate("ComposerWrapper", "Could not save template file."), docFile.errorString())) return templateDoc = QDomDocument() template_name = docFileInfo.completeBaseName() # Catch exception raised when writing items' elements try: self._writeXML(templateDoc, template_name) except Exception as exc: msg = unicode(exc) QMessageBox.critical( self.composerView(), QApplication.translate("ComposerWrapper", "Save Error"), msg) docFile.close() docFile.remove() return if docFile.write(templateDoc.toByteArray()) == -1: QMessageBox.critical( self.composerView(), QApplication.translate("ComposerWrapper", "Save Error"), QApplication.translate("ComposerWrapper", "Could not save template file.")) return else: self.mainWindow().setWindowTitle(template_name) self.setDocumentFile(docFile) docFile.close() if self.copy_template_file: self.copy_template_file.close() def _writeXML(self, xml_doc, doc_name): """ Write the template configuration into the XML document. """ #Write default composer configuration composer_element = xml_doc.createElement("Composer") composer_element.setAttribute("title", doc_name) composer_element.setAttribute("visible", 1) xml_doc.appendChild(composer_element) self.composition().writeXML(composer_element, xml_doc) #Write STDM data field configurations dataSourceElement = ComposerDataSource.domElement(self, xml_doc) composer_element.appendChild(dataSourceElement) #Write spatial field configurations spatialColumnsElement = SpatialFieldsConfiguration.domElement( self, xml_doc) dataSourceElement.appendChild(spatialColumnsElement) #Write photo configuration photos_element = PhotoConfigurationCollection.dom_element( self, xml_doc) dataSourceElement.appendChild(photos_element) #Write table configuration tables_element = TableConfigurationCollection.dom_element( self, xml_doc) dataSourceElement.appendChild(tables_element) #Write chart configuration charts_element = ChartConfigurationCollection.dom_element( self, xml_doc) dataSourceElement.appendChild(charts_element) # Write QRCode configuration qr_codes_element = QRCodeConfigurationCollection.dom_element( self, xml_doc) dataSourceElement.appendChild(qr_codes_element) def _configure_data_controls(self, composer_data_source): """ Configure the data source and data field controls based on the composer data source configuration. """ if not self.stdmDataSourceDock().widget() is None: #Set data source dataSourceWidget = self.stdmDataSourceDock().widget() dataSourceWidget.setCategory(composer_data_source.category()) dataSourceWidget.setSelectedSource(composer_data_source.name()) dataSourceWidget.set_referenced_table( composer_data_source.referenced_table_name) #Set data field controls for composerId in composer_data_source.dataFieldMappings().reverse: #Use composer item id since the uuid is stripped off composerItem = self.composition().getComposerItemById( composerId) if not composerItem is None: compFieldSelector = ComposerFieldSelector( self, composerItem, self.composerView()) compFieldSelector.selectFieldName( composer_data_source.dataFieldName(composerId)) #Add widget to the collection but now use the current uuid of the composition item self.addWidgetMapping(composerItem.uuid(), compFieldSelector) def _configureSpatialSymbolEditor(self, spatial_field_config): """ Configure symbol editor controls. """ if not self.stdmDataSourceDock().widget() is None: for item_id, spFieldsMappings in spatial_field_config.spatialFieldsMapping( ).iteritems(): mapItem = self.composition().getComposerItemById(item_id) if not mapItem is None: composerSymbolEditor = ComposerSymbolEditor( self, self.composerView()) composerSymbolEditor.add_spatial_field_mappings( spFieldsMappings) #Add widget to the collection but now use the current uuid of the composer map self.addWidgetMapping(mapItem.uuid(), composerSymbolEditor) def _configure_photo_editors(self, photo_config_collection): """ Creates widgets for editing photo data sources. :param photo_config_collection: PhotoConfigurationCollection instance. :type photo_config_collection: PhotoConfigurationCollection """ if self.stdmDataSourceDock().widget() is None: return for item_id, photo_config in photo_config_collection.mapping( ).iteritems(): pic_item = self.composition().getComposerItemById(item_id) if not pic_item is None: photo_editor = ComposerPhotoDataSourceEditor( self, self.composerView()) photo_editor.set_configuration(photo_config) self.addWidgetMapping(pic_item.uuid(), photo_editor) def _configure_chart_editors(self, chart_config_collection): """ Creates widgets for editing chart properties. :param chart_config_collection: ChartConfigurationCollection instance. :type chart_config_collection: ChartConfigurationCollection """ if self.stdmDataSourceDock().widget() is None: return for item_id, chart_config in chart_config_collection.mapping( ).iteritems(): chart_item = self.composition().getComposerItemById(item_id) if not chart_item is None: chart_editor = ComposerChartConfigEditor( self, self.composerView()) chart_editor.set_configuration(chart_config) self.addWidgetMapping(chart_item.uuid(), chart_editor) def _configure_table_editors(self, table_config_collection): """ Creates widgets for editing table data sources. :param table_config_collection: TableConfigurationCollection instance. :type table_config_collection: TableConfigurationCollection """ if self.stdmDataSourceDock().widget() is None: return for item_id, table_config in table_config_collection.mapping( ).iteritems(): table_item = self.composition().getComposerItemById(item_id) if table_item is not None: table_editor = ComposerTableDataSourceEditor( self, table_item, self.composerView()) table_editor.set_configuration(table_config) table_editor.ref_table.cbo_ref_table.currentIndexChanged[ str].connect(table_editor.set_table_vector_layer) self.addWidgetMapping(table_item.uuid(), table_editor) def _configure_qr_code_editors(self, qr_code_config_collection): """ Creates widgets for editing QR code properties. :param qr_code_config_collection: QRCodeConfigurationCollection instance. :type qr_code_config_collection: QRCodeConfigurationCollection """ if self.stdmDataSourceDock().widget() is None: return for item_id, qrc_config in qr_code_config_collection.mapping( ).iteritems(): qrc_item = self.composition().getComposerItemById(item_id) if not qrc_item is None: qrc_editor_cls = qr_code_config_collection.editor_type qrc_editor = qrc_editor_cls(self, self.composerView()) qrc_editor.set_configuration(qrc_config) self.addWidgetMapping(qrc_item.uuid(), qrc_editor) def _sync_ids_with_uuids(self): """ Matches IDs of custom STDM items with the corresponding UUIDs. This is applied when loading existing templates so that the saved document contains a matching pair of ID and UUID for each composer item. """ items = self._widgetMappings.keys() for item_uuid in self._widgetMappings.keys(): item = self.composition().getComposerItemByUuid(item_uuid) if not item is None: item.setId(item_uuid) def _composerTemplatesPath(self): """ Reads the path of composer templates in the registry. """ regConfig = RegistryConfig() keyName = "ComposerTemplates" valueCollection = regConfig.read([keyName]) if len(valueCollection) == 0: return None else: return valueCollection[keyName] def _onItemRemoved(self, item): """ Slot raised when a composer item is removed from the scene. """ """ Code will not work since a QObject instance is returned instead of a QgsComposerItem if item.uuid() in self._widgetMappings: del self._widgetMappings[item.uuid()] """ pass def _onItemSelected(self, item): """ Slot raised when a composer item is selected. Load the corresponding field selector if the selection is an STDM data field label. QComposerLabel is returned as a QObject in the slot argument hence, we have resorted to capturing the current selected items in the composition. """ selectedItems = self.composition().selectedComposerItems() if len(selectedItems) == 0: self._stdmItemPropDock.setWidget(None) elif len(selectedItems) == 1: composer_item = selectedItems[0] if composer_item.uuid() in self._widgetMappings: stdmWidget = self._widgetMappings[composer_item.uuid()] self.selected_item_uuid = composer_item.uuid() if stdmWidget == self._stdmItemPropDock.widget(): return else: self._stdmItemPropDock.setWidget(stdmWidget) #Playing it safe in applying the formatting for the editor controls where applicable itemFormatter = None if isinstance(stdmWidget, ComposerTableDataSourceEditor): itemFormatter = TableFormatter() if isinstance(composer_item, QgsComposerArrow): itemFormatter = LineFormatter() elif isinstance(composer_item, QgsComposerLabel): itemFormatter = DataLabelFormatter() elif isinstance(composer_item, QgsComposerMap): itemFormatter = MapFormatter() elif isinstance(composer_item, QgsComposerPicture): """ Use widget attribute to distinguish type i.e. whether it is a photo, graph etc. """ editor_widget = self._widgetMappings[composer_item.uuid()] if isinstance(editor_widget, ComposerPhotoDataSourceEditor): itemFormatter = PhotoFormatter() elif isinstance(editor_widget, ComposerChartConfigEditor): itemFormatter = ChartFormatter() elif isinstance(editor_widget, QRCodeConfigurationCollection.editor_type): itemFormatter = QRCodeFormatter() elif isinstance(composer_item, QgsComposerAttributeTableV2): itemFormatter = TableFormatter() if itemFormatter is not None: itemFormatter.apply(composer_item, self, True) else: self._stdmItemPropDock.setWidget(None) elif len(selectedItems) > 1: self._stdmItemPropDock.setWidget(None)
class MyMainWindow(QMainWindow): ' Main Window ' def __init__(self, parent=None): ' Initialize QWidget inside MyMainWindow ' super(MyMainWindow, self).__init__(parent) self.statusBar().showMessage(__doc__.title()) self.setWindowTitle(__doc__) self.setMinimumSize(600, 800) self.setMaximumSize(2048, 1024) self.resize(1024, 800) self.setWindowIcon(QIcon.fromTheme("face-monkey")) if not A11Y: self.setStyleSheet('''QWidget{color:#fff;font-family:Oxygen} QWidget:item:hover, QWidget:item:selected { background-color: cyan; color: #000 } QWidget:disabled { color: #404040; background-color: #323232 } QWidget:focus { border: 1px solid cyan } QPushButton { background-color: gray; padding: 3px; border: 1px solid gray; border-radius: 9px; margin: 0;font-size: 12px; padding-left: 5px; padding-right: 5px } QLineEdit, QTextEdit { background-color: #4a4a4a; border: 1px solid gray; border-radius: 0; font-size: 12px; } QPushButton:pressed { background-color: #323232 } QComboBox { background-color: #4a4a4a; padding-left: 9px; border: 1px solid gray; border-radius: 5px; } QComboBox:pressed { background-color: gray } QComboBox QAbstractItemView, QMenu { border: 1px solid #4a4a4a; background:grey; selection-background-color: cyan; selection-color: #000; } QSlider { padding: 3px; font-size: 8px; padding-left: 2px; padding-right: 2px; border: 5px solid #1e1e1e } QSlider::sub-page:vertical { background-color: QLinearGradient(spread:pad, x1:0, y1:0, x2:1, y2:0.27, stop:0 rgba(255, 0, 0, 255), stop:1 rgba(50, 0, 0, 200)); border: 4px solid #1e1e1e; border-radius: 5px } QSlider::add-page:vertical { background-color: QLinearGradient(spread:pad, x1:0, y1:0, x2:1, y2:0.27, stop:0 rgba(0, 255, 0, 255), stop:1 rgba(0, 99, 0, 255)); border: 4px solid #1e1e1e; border-radius: 5px; } QSlider::handle:vertical { background-color: QLinearGradient(spread:pad, x1:0, y1:0, x2:1, y2:0.273, stop:0 rgba(0, 0, 0, 255), stop:1 gray); height: 5px; border: 1px dotted #fff; text-align: center; border-top-left-radius: 2px; border-bottom-left-radius: 2px; border-top-right-radius: 2px; border-bottom-right-radius 2px; margin-left: 2px; margin-right: 2px; } QSlider::handle:vertical:hover { border: 1px solid cyan } QSlider::sub-page:vertical:disabled { background: #bbb; border-color: #999; } QSlider::add-page:vertical:disabled { background: #eee; border-color: #999; } QSlider::handle:vertical:disabled { background: #eee; border: 1px solid #aaa; border-radius: 4px; } QToolBar, QStatusBar, QDockWidget::title{background-color:#323232;} QToolBar::handle, QToolBar::handle:vertical, QToolBar::handle:horizontal { border: 1px solid gray; border-radius: 9px; width: 19px; height: 19px; margin: 0.5px } QGroupBox { border: 1px solid gray; border-radius: 9px; padding-top: 9px; } QStatusBar, QToolBar::separator:horizontal, QToolBar::separator:vertical {color:gray} QScrollBar:vertical{ background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #212121,stop: 1.0 #323232); width: 10px; } QScrollBar:horizontal{ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #212121,stop: 1.0 #323232); height: 10px; } QScrollBar::handle:vertical{ padding: 2px; min-height: 50px; background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #585858,stop: 1.0 #404040); border-radius: 5px; border: 1px solid #191919; } QScrollBar::handle:horizontal{ padding: 2px; min-width: 50px; background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #585858,stop: 1.0 #404040); border-radius: 5px; border: 1px solid #191919; } QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical, QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical, QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal, QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { background: none; border: none; } QDockWidget::close-button, QDockWidget::float-button { border: 1px solid gray; border-radius: 3px; background: darkgray; }''') self.process = QProcess() self.process.readyReadStandardOutput.connect(self.read_output) self.process.readyReadStandardError.connect(self.read_errors) self.process.finished.connect(self._process_finished) self.process.error.connect(self._process_finished) self.group0, self.group1 = QGroupBox("Options"), QGroupBox("Paths") self.group2 = QGroupBox("Nodes") self.group3 = QGroupBox("Python Code") self.group4, self.group5 = QGroupBox("Logs"), QGroupBox("Backend") g0grid, g1vlay = QGridLayout(self.group0), QVBoxLayout(self.group1) g5vlay = QVBoxLayout(self.group5) self.treeview_nodes, self.textedit_source = QTextEdit(), QTextEdit() self.dock1, self.dock2 = QDockWidget(), QDockWidget() self.output, self.dock3 = QTextEdit(), QDockWidget() self.treeview_nodes.setAutoFormatting(QTextEdit.AutoAll) self.treeview_nodes.setWordWrapMode(QTextOption.NoWrap) self.dock1.setWidget(self.treeview_nodes) self.dock2.setWidget(self.textedit_source) self.dock3.setWidget(self.output) self.dock1.setWindowTitle("Tree") self.dock2.setWindowTitle("Sources") self.dock3.setWindowTitle("STDOutput") featur = QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable self.dock1.setFeatures(featur) self.dock2.setFeatures(featur) self.dock3.setFeatures(featur) QVBoxLayout(self.group2).addWidget(self.dock1) QVBoxLayout(self.group3).addWidget(self.dock2) QVBoxLayout(self.group4).addWidget(self.dock3) self.slider1, self.slider2 = QSlider(), QSlider() g0grid.addWidget(self.slider1, 0, 0) g0grid.addWidget(QLabel('Use Debug'), 0, 1) self.slider2.setValue(1) g0grid.addWidget(self.slider2, 1, 0) g0grid.addWidget(QLabel('Use verbose'), 1, 1) self.slider3, self.slider4 = QSlider(), QSlider() self.slider3.setValue(1) g0grid.addWidget(self.slider3, 2, 0) g0grid.addWidget(QLabel('Show compiling progress'), 2, 1) self.slider4.setValue(1) g0grid.addWidget(self.slider4, 3, 0) g0grid.addWidget(QLabel('Show Scons building debug'), 3, 1) self.slider5, self.slider6 = QSlider(), QSlider() g0grid.addWidget(self.slider5, 4, 0) g0grid.addWidget(QLabel('Keep debug unstriped binary'), 4, 1) g0grid.addWidget(self.slider6, 5, 0) g0grid.addWidget(QLabel('Traced execution outputs'), 5, 1) self.slider7, self.slider8 = QSlider(), QSlider() self.slider7.setValue(1) g0grid.addWidget(self.slider7, 6, 0) g0grid.addWidget(QLabel('Remove the build folder'), 6, 1) g0grid.addWidget(self.slider8, 7, 0) g0grid.addWidget(QLabel('No Python Optimizations'), 7, 1) self.slider9, self.slider10 = QSlider(), QSlider() g0grid.addWidget(self.slider9, 8, 0) g0grid.addWidget(QLabel('No Statements line numbers'), 8, 1) g0grid.addWidget(self.slider10, 9, 0) g0grid.addWidget(QLabel('Execute the output binary'), 9, 1) self.slider11, self.slider12 = QSlider(), QSlider() g0grid.addWidget(self.slider11, 10, 0) g0grid.addWidget(QLabel('Warning detected implicit exceptions'), 10, 1) g0grid.addWidget(self.slider12, 11, 0) g0grid.addWidget(QLabel('Keep the PYTHONPATH, do not Reset it'), 11, 1) self.slider13 = QSlider() g0grid.addWidget(self.slider13, 12, 0) g0grid.addWidget(QLabel('Enhance compile, CPython incompatible'), 12, 1) self.slider1a, self.slider2a = QSlider(), QSlider() g0grid.addWidget(self.slider1a, 0, 2) g0grid.addWidget(QLabel('Descendent Recursive Compile'), 0, 3) self.slider2a.setValue(1) g0grid.addWidget(self.slider2a, 1, 2) g0grid.addWidget(QLabel('Force non recursive compile'), 1, 3) self.slider3a, self.slider4a = QSlider(), QSlider() g0grid.addWidget(self.slider3a, 2, 2) g0grid.addWidget(QLabel('STD Lib Recursive Compile'), 2, 3) g0grid.addWidget(self.slider4a, 3, 2) g0grid.addWidget(QLabel('Enforce the use of Clang'), 3, 3) self.slider5a, self.slider6a = QSlider(), QSlider() self.slider5a.setValue(1) g0grid.addWidget(self.slider5a, 4, 2) g0grid.addWidget(QLabel('Use G++ link time optimizations'), 4, 3) g0grid.addWidget(self.slider6a, 5, 2) g0grid.addWidget(QLabel('Disable the console window'), 5, 3) self.slider7a, self.slider8a = QSlider(), QSlider() g0grid.addWidget(self.slider7a, 6, 2) g0grid.addWidget(QLabel('Force compile for MS Windows'), 6, 3) g0grid.addWidget(self.slider8a, 7, 2) g0grid.addWidget(QLabel('Use Python Debug versions'), 7, 3) self.slider9a, self.slider10a = QSlider(), QSlider() self.slider9a.setValue(1) g0grid.addWidget(self.slider9a, 8, 2) g0grid.addWidget(QLabel('Create standalone executable'), 8, 3) g0grid.addWidget(self.slider10a, 9, 2) g0grid.addWidget(QLabel('Enable Standalone mode build'), 9, 3) self.slider11a, self.slider12a = QSlider(), QSlider() g0grid.addWidget(self.slider11a, 10, 2) g0grid.addWidget(QLabel('Make module executable instead of app'), 10, 3) g0grid.addWidget(self.slider12a, 11, 2) g0grid.addWidget(QLabel('No froze module of stdlib as bytecode'), 11, 3) self.slider13a = QSlider() g0grid.addWidget(self.slider13a, 12, 2) g0grid.addWidget(QLabel('Force use of MinGW on MS Windows'), 12, 3) for each_widget in (self.slider1, self.slider2, self.slider3, self.slider4, self.slider5, self.slider6, self.slider7, self.slider8, self.slider9, self.slider10, self.slider11, self.slider12, self.slider13, self.slider1a, self.slider2a, self.slider3a, self.slider4a, self.slider5a, self.slider6a, self.slider7a, self.slider8a, self.slider9a, self.slider10a, self.slider11a, self.slider12a, self.slider13a): each_widget.setRange(0, 1) each_widget.setCursor(QCursor(Qt.OpenHandCursor)) each_widget.setTickInterval(1) each_widget.TickPosition(QSlider.TicksBothSides) self.combo1 = QComboBox() self.combo1.addItems(('2.7', '2.6', '3.2', '3.3')) g5vlay.addWidget(QLabel('Python Version')) g5vlay.addWidget(self.combo1) self.combo2 = QComboBox() self.combo2.addItems(('Default', 'Low', 'High')) g5vlay.addWidget(QLabel('CPU priority')) g5vlay.addWidget(self.combo2) self.combo3 = QComboBox() self.combo3.addItems(('1', '2', '3', '4', '5', '6', '7', '8', '9')) g5vlay.addWidget(QLabel('MultiProcessing Workers')) g5vlay.addWidget(self.combo3) self.outdir = QLineEdit() self.outdir.setStyleSheet("QLineEdit{margin-left:25px}") self.clearButton = QToolButton(self.outdir) self.clearButton.setIcon(QIcon.fromTheme("edit-clear")) self.clearButton.setIconSize(QSize(25, 25)) self.clearButton.setStyleSheet("QToolButton{border:none}") self.clearButton.hide() self.clearButton.clicked.connect(self.outdir.clear) self.outdir.textChanged.connect( lambda: self.clearButton.setVisible(True)) self.clearButton.clicked.connect( lambda: self.clearButton.setVisible(False)) self.outdir.setPlaceholderText('Output Directory') if path.isfile('.nuitka-output-dir.txt'): self.outdir.setText(open('.nuitka-output-dir.txt', 'r').read()) else: self.outdir.setText(path.expanduser("~")) self.completer, self.dirs = QCompleter(self), QDirModel(self) self.dirs.setFilter(QDir.Dirs | QDir.NoDotAndDotDot) self.completer.setModel(self.dirs) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setCompletionMode(QCompleter.PopupCompletion) self.completer.popup().setStyleSheet( """border:1px solid #4a4a4a;background:grey; selection-background-color:cyan;selection-color:#000""") self.completer.popup().setVerticalScrollBarPolicy( Qt.ScrollBarAlwaysOff) self.outdir.setCompleter(self.completer) self.btn1 = QPushButton(QIcon.fromTheme("document-open"), 'Open' if IS_WIN else '') self.btn1.clicked.connect( lambda: open('.nuitka-output-dir.txt', 'w').write( str( QFileDialog.getExistingDirectory( None, 'Open Output Directory', path.expanduser("~"))))) self.btn1.released.connect(lambda: self.outdir.setText( open('.nuitka-output-dir.txt', 'r').read())) g1vlay.addWidget(QLabel('Output Directory')) g1vlay.addWidget(self.outdir) g1vlay.addWidget(self.btn1) self.target = QLineEdit() self.target.setStyleSheet("QLineEdit{margin-left:25px}") self.clearButton2 = QToolButton(self.target) self.clearButton2.setIcon(QIcon.fromTheme("edit-clear")) self.clearButton2.setIconSize(QSize(25, 25)) self.clearButton2.setStyleSheet("QToolButton{border:none}") self.clearButton2.hide() self.clearButton2.clicked.connect(self.target.clear) self.target.textChanged.connect( lambda: self.clearButton2.setVisible(True)) self.clearButton2.clicked.connect( lambda: self.clearButton2.setVisible(False)) self.target.setPlaceholderText('Target Python App to Binary Compile') self.target.setCompleter(self.completer) self.btn2 = QPushButton(QIcon.fromTheme("document-open"), 'Open' if IS_WIN else '') self.btn2.clicked.connect(lambda: self.target.setText( str( QFileDialog.getOpenFileName( None, "Open", path.expanduser("~"), ';;'.join([ '{}(*.{})'.format(e.upper(), e) for e in ('py', 'pyw', '*') ]))))) g1vlay.addWidget(QLabel('Input File')) g1vlay.addWidget(self.target) g1vlay.addWidget(self.btn2) self.icon, self.icon_label = QLineEdit(), QLabel('Icon File') self.icon.setStyleSheet("QLineEdit{margin-left:25px}") self.clearButton3 = QToolButton(self.icon) self.clearButton3.setIcon(QIcon.fromTheme("edit-clear")) self.clearButton3.setIconSize(QSize(25, 25)) self.clearButton3.setStyleSheet("QToolButton{border:none}") self.clearButton3.hide() self.clearButton3.clicked.connect(self.icon.clear) self.icon.textChanged.connect( lambda: self.clearButton3.setVisible(True)) self.clearButton3.clicked.connect( lambda: self.clearButton3.setVisible(False)) self.icon.setPlaceholderText('Path to Icon file for your App') self.icon.setCompleter(self.completer) self.btn3 = QPushButton(QIcon.fromTheme("document-open"), 'Open' if IS_WIN else '') self.btn3.clicked.connect(lambda: self.icon.setText( str( QFileDialog.getOpenFileName( None, "Open", path.expanduser("~"), ';;'.join([ '{}(*.{})'.format(e.upper(), e) for e in ('ico', 'png', 'bmp', 'svg', '*') ]))))) g1vlay.addWidget(self.icon_label) g1vlay.addWidget(self.icon) g1vlay.addWidget(self.btn3) # Menu Bar inicialization and detail definitions menu_salir = QAction(QIcon.fromTheme("application-exit"), 'Quit', self) menu_salir.setStatusTip('Quit') menu_salir.triggered.connect(exit) menu_minimize = QAction(QIcon.fromTheme("go-down"), 'Minimize', self) menu_minimize.setStatusTip('Minimize') menu_minimize.triggered.connect(lambda: self.showMinimized()) menu_qt = QAction(QIcon.fromTheme("help-about"), 'About Qt', self) menu_qt.setStatusTip('About Qt...') menu_qt.triggered.connect(lambda: QMessageBox.aboutQt(self)) menu_dev = QAction(QIcon.fromTheme("applications-development"), 'Developer Manual PDF', self) menu_dev.setStatusTip('Open Nuitka Developer Manual PDF...') menu_dev.triggered.connect(lambda: call( OPEN + '/usr/share/doc/nuitka/Developer_Manual.pdf.gz', shell=True) ) menu_usr = QAction(QIcon.fromTheme("help-contents"), 'User Docs', self) menu_usr.setStatusTip('Open Nuitka End User Manual PDF...') menu_usr.triggered.connect(lambda: call( OPEN + '/usr/share/doc/nuitka/README.pdf.gz', shell=True)) menu_odoc = QAction(QIcon.fromTheme("help-browser"), 'OnLine Doc', self) menu_odoc.setStatusTip('Open Nuitka on line Documentation pages...') menu_odoc.triggered.connect( lambda: open_new_tab('http://nuitka.net/doc/user-manual.html')) menu_man = QAction(QIcon.fromTheme("utilities-terminal"), 'Man', self) menu_man.setStatusTip('Open Nuitka technical command line Man Pages..') menu_man.triggered.connect( lambda: call('xterm -e "man nuitka"', shell=True)) menu_tra = QAction(QIcon.fromTheme("applications-development"), 'View Nuitka-GUI Source Code', self) menu_tra.setStatusTip('View, study, edit Nuitka-GUI Libre Source Code') menu_tra.triggered.connect(lambda: call(OPEN + __file__, shell=True)) menu_foo = QAction(QIcon.fromTheme("folder"), 'Open Output Dir', self) menu_foo.setStatusTip('Open the actual Output Directory location...') menu_foo.triggered.connect( lambda: call(OPEN + str(self.outdir.text()), shell=True)) menu_pic = QAction(QIcon.fromTheme("camera-photo"), 'Screenshot', self) menu_pic.setStatusTip('Take a Screenshot for Documentation purposes..') menu_pic.triggered.connect( lambda: QPixmap.grabWindow(QApplication.desktop().winId()).save( QFileDialog.getSaveFileName(None, "Save", path.expanduser("~"), 'PNG(*.png)', 'png'))) menu_don = QAction(QIcon.fromTheme("emblem-favorite"), 'Help Nuitka', self) menu_don.setStatusTip('Help the Nuitka Open Source Libre Free Project') menu_don.triggered.connect( lambda: open_new_tab('http://nuitka.net/pages/donations.html')) # movable draggable toolbar self.toolbar = QToolBar(self) self.toolbar.setIconSize(QSize(16, 16)) self.toolbar.toggleViewAction().setText("Show/Hide Toolbar") l_spacer, r_spacer = QWidget(self), QWidget(self) l_spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) r_spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.toolbar.addWidget(l_spacer) self.toolbar.addSeparator() self.toolbar.addActions((menu_salir, menu_minimize, menu_qt, menu_odoc, menu_foo, menu_pic, menu_don)) if not IS_WIN: self.toolbar.addActions((menu_man, menu_dev, menu_tra, menu_usr)) self.toolbar.addSeparator() self.toolbar.addWidget(r_spacer) self.addToolBar(Qt.BottomToolBarArea, self.toolbar) # Bottom Buttons Bar self.buttonBox = QDialogButtonBox(self) self.buttonBox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Close) self.buttonBox.rejected.connect(exit) self.buttonBox.accepted.connect(self.run) self.guimode = QComboBox() self.guimode.addItems(('Full UX / UI', 'Simple UX / UI')) self.guimode.setStyleSheet( """QComboBox{background:transparent;border:0; margin-left:25px;color:gray;text-decoration:underline}""") self.guimode.currentIndexChanged.connect(self.set_guimode) container = QWidget() container_layout = QGridLayout(container) # Y, X container_layout.addWidget(self.guimode, 0, 1) container_layout.addWidget(self.group2, 1, 0) container_layout.addWidget(self.group3, 2, 0) container_layout.addWidget(self.group0, 1, 1) container_layout.addWidget(self.group1, 2, 1) container_layout.addWidget(self.group4, 1, 2) container_layout.addWidget(self.group5, 2, 2) container_layout.addWidget(self.buttonBox, 3, 1) self.setCentralWidget(container) # Paleta de colores para pintar transparente if not A11Y: palette = self.palette() palette.setBrush(QPalette.Base, Qt.transparent) self.setPalette(palette) self.setAttribute(Qt.WA_OpaquePaintEvent, False) def get_fake_tree(self, target): """Return the fake tree.""" try: fake_tree = check_output(NUITKA + ' --dump-xml ' + target, shell=True) except: fake_tree = "ERROR: Failed to get Tree Dump." finally: return fake_tree.strip() def run(self): ' run the actual backend process ' self.treeview_nodes.clear() self.textedit_source.clear() self.output.clear() self.statusBar().showMessage('WAIT!, Working...') target = str(self.target.text()).strip() self.treeview_nodes.setText(self.get_fake_tree(target)) self.textedit_source.setText(open(target, "r").read().strip()) conditional_1 = sys.platform.startswith('linux') conditional_2 = self.combo3.currentIndex() != 2 command_to_run_nuitka = " ".join( ('chrt -i 0' if conditional_1 and conditional_2 else '', NUITKA, '--debug' if self.slider1.value() else '', '--verbose' if self.slider2.value() else '', '--show-progress' if self.slider3.value() else '', '--show-scons --show-modules' if self.slider4.value() else '', '--unstriped' if self.slider5.value() else '', '--trace-execution' if self.slider6.value() else '', '--remove-output' if self.slider7.value() else '', '--no-optimization' if self.slider8.value() else '', '--code-gen-no-statement-lines' if self.slider9.value() else '', '--execute' if self.slider10.value() else '', '--recurse-all' if self.slider1a.value() else '', '--recurse-none' if self.slider2a.value() else '', '--recurse-stdlib' if self.slider3a.value() else '', '--clang' if self.slider4a.value() else '', '--lto' if self.slider5a.value() else '', '--windows-disable-console' if self.slider6a.value() else '', '--windows-target' if self.slider7a.value() else '', '--python-debug' if self.slider8a.value() else '', '--exe' if self.slider9a.value() else '', '--standalone' if self.slider10a.value() else '', '--module' if self.slider11a.value() else '', '--nofreeze-stdlib' if self.slider12a.value() else '', '--mingw' if self.slider13a.value() else '', '--warn-implicit-exceptions' if self.slider11.value() else '', '--execute-with-pythonpath' if self.slider12.value() else '', '--enhanced' if self.slider13.value() else '', '--icon="{}"'.format(self.icon.text()) if self.icon.text() else '', '--python-version={}'.format( self.combo1.currentText()), '--jobs={}'.format( self.combo3.currentText()), '--output-dir="{}"'.format( self.outdir.text()), "{}".format(target))) if DEBUG: print(command_to_run_nuitka) self.process.start(command_to_run_nuitka) if not self.process.waitForStarted() and not IS_WIN: return # ERROR ! self.statusBar().showMessage(__doc__.title()) def _process_finished(self): """finished sucessfully""" self.output.setFocus() self.output.selectAll() def read_output(self): """Read and append output to the log""" self.output.append(str(self.process.readAllStandardOutput())) def read_errors(self): """Read and append errors to the log""" self.output.append(str(self.process.readAllStandardError())) def paintEvent(self, event): """Paint semi-transparent background,animated pattern,background text""" if not A11Y: p = QPainter(self) p.setRenderHint(QPainter.Antialiasing) p.setRenderHint(QPainter.TextAntialiasing) p.setRenderHint(QPainter.HighQualityAntialiasing) p.fillRect(event.rect(), Qt.transparent) # animated random dots background pattern for i in range(4096): x = randint(25, self.size().width() - 25) y = randint(25, self.size().height() - 25) # p.setPen(QPen(QColor(randint(9, 255), 255, 255), 1)) p.drawPoint(x, y) p.setPen(QPen(Qt.white, 1)) p.rotate(40) p.setFont(QFont('Ubuntu', 250)) p.drawText(200, 99, "Nuitka") p.rotate(-40) p.setPen(Qt.NoPen) p.setBrush(QColor(0, 0, 0)) p.setOpacity(0.8) p.drawRoundedRect(self.rect(), 9, 9) p.end() def set_guimode(self): """Switch between simple and full UX""" for widget in (self.group2, self.group3, self.group4, self.group5, self.icon, self.icon_label, self.btn3, self.toolbar, self.statusBar()): widget.hide() if self.guimode.currentIndex() else widget.show()
slice_settings = SliceSettingsWidget(max_slice_count=nz, color_scales=color_scales.keys()) slice_settings.inactiveCellsHidden.connect(viewer.hideInactiveCells) slice_settings.currentSliceChanged.connect(viewer.setCurrentSlice) slice_settings.toggleOrthographicProjection.connect(viewer.useOrthographicProjection) slice_settings.toggleLighting.connect(viewer.useLighting) slice_settings.colorScalesChanged.connect(viewer.changeColorScale) slice_settings.regionToggling.connect(viewer.useRegionScaling) slice_settings.toggleInterpolation.connect(viewer.useInterpolationOnData) slice_settings.mirrorX.connect(viewer.mirrorX) slice_settings.mirrorY.connect(viewer.mirrorY) slice_settings.mirrorZ.connect(viewer.mirrorZ) slice_settings.toggleFlatPolylines.connect(viewer.toggleFlatPolylines) dock_widget = QDockWidget("Settings") dock_widget.setObjectName("SliceSettingsDock") dock_widget.setWidget(slice_settings) dock_widget.setAllowedAreas(Qt.AllDockWidgetAreas) dock_widget.setFeatures(QDockWidget.NoDockWidgetFeatures) window.addDockWidget(Qt.LeftDockWidgetArea, dock_widget) window.setCentralWidget(viewer) window.show() window.activateWindow() window.raise_() app.exec_()
class DBMainWindow(QMainWindow): """Main Window and Portal .. todo:: Remember the dock position """ W_NAME = "DBMainWindow" def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.db = None self.setWindowTitle("PyQtDb") self.setWindowIcon(Ico.icon(Ico.FavIcon)) self.setMinimumWidth(800) self.setMinimumHeight(800) """ topBar = QToolBar() self.addToolBar(Qt.TopToolBarArea, topBar) topBar.addAction("New Server") """ ## Servers Widget self.dockServers = QDockWidget("Servers") self.dockServers.setFeatures(QDockWidget.DockWidgetMovable) self.dockServers.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.addDockWidget(Qt.LeftDockWidgetArea, self.dockServers) self.serversWidget = DBServersWidget.DBServersWidget(self) """Instance of :py:class:`~pyqtdb.DBServersWidget.DBServersWidget` in dock""" self.dockServers.setWidget(self.serversWidget) self.connect(self.serversWidget, SIGNAL("open_server"), self.on_open_server) self.cenWid = QWidget() self.setCentralWidget(self.cenWid) self.mainLayout = QHBoxLayout() self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.setSpacing(0) self.cenWid.setLayout(self.mainLayout) self.tabWidget = QTabWidget() """The main tabs""" self.tabWidget.setTabsClosable(True) self.mainLayout.addWidget(self.tabWidget) G.settings.restore_window(self) def on_open_server(self, srv): """Opens server by adding a :py:class:`~pyqtdb.DBBrowser.DBBrowser` in the the tabWidget""" server = G.settings.get_server(str(srv)) print "oopen", srv, server widget = DBBrowser.DBBrowser(self, server) self.tabWidget.addTab(widget, Ico.icon(Ico.Server), server['server']) self.tabWidget.setCurrentIndex(self.tabWidget.indexOf(widget)) def closeEvent(self, event): """Save window settings on close with :py:class:`~pyqtdb.XSettings.XSettings.save_window` """ G.settings.save_window(self)
class filexplorerPluginMain(plugin.Plugin): ' main class for plugin ' def initialize(self, *args, **kwargs): ' class init ' global CONFIG_DIR ec = ExplorerContainer() super(filexplorerPluginMain, self).initialize(*args, **kwargs) self.dock = QDockWidget() self.dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.dock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.dock.setWindowTitle("fileXplorer") self.dock.setStyleSheet('QDockWidget::title { text-align: center; }') # search for the truth self.srch = QLineEdit() #self.srch.resize(self.srch.size().height(), self.dock.size().width()) self.srch.setPlaceholderText(' Search for Python files Local or PyPI ') self.srch.returnPressed.connect(self.search) # Disk Usage Bar self.hdbar = QProgressBar() if sys.platform != 'win32': self.hdbar.setMaximum(statvfs(HOME).f_blocks * statvfs(HOME).f_frsize / 1024 / 1024 / 1024) self.hdbar.setValue(statvfs(HOME).f_bfree * statvfs(HOME).f_frsize / 1024 / 1024 / 1024) self.hdbar.setToolTip(str(self.hdbar.value()) + '% Total Disk Use ') #self.hdbar.setStyleSheet('''QProgressBar{background-color: #QLinearGradient(spread:pad,x1:0,y1:0,x2:1,y2:1,stop:0 rgba(255,0,0,99), #stop:1 rgba(9,255,9,200));color:#fff;border:none;border-radius:9px;} #QProgressBar::chunk{background-color:QLinearGradient(spread:pad,y1:0, #x1:0,y2:1,x2:0.27,stop:0 rgb(0,0,0),stop:1 rgb(9,99,255));padding:0; #border:none;border-radius:9px;height:9px;margin:1px;}''') self.model = QDirModel() self.fileView = QColumnView(self.dock) self.fileView.setAlternatingRowColors(True) # self.fileView.setFont(QFont(self.fileView.font().setBold(True))) self.fileView.setIconSize(QSize(32, 32)) self.fileView.setModel(self.model) self.fileView.updatePreviewWidget.connect(self.runfile) self.sli = QSlider() self.sli.setRange(16, 128) self.sli.setValue(32) self.sli.setToolTip('Icon Size: 32 px. Move Slider to change.') self.sli.setOrientation(Qt.Horizontal) self.sli.valueChanged.connect(lambda: self.fileView.setIconSize( QSize(self.sli.value(), self.sli.value()))) self.sli.sliderReleased.connect(lambda: self.sli.setToolTip('Icon Size: ' + str(self.sli.value()))) class TransientWidget(QWidget): ' persistant widget thingy ' def __init__(self, widget_list): ' init sub class ' super(TransientWidget, self).__init__() vbox = QVBoxLayout(self) for each_widget in widget_list: vbox.addWidget(each_widget) tw = TransientWidget((self.srch, self.dock, self.sli, self.hdbar)) ec.addTab(tw, "fileXplorer") #### self.process = QProcess() self.process.finished.connect(self.processFinished) self.preview = QLabel(self.fileView) self.preview.setTextFormat(0) self.preview.setStyleSheet('QLabel{font-size:9px;}') self.preview.setAutoFillBackground(True) self.fileView.setPreviewWidget(self.preview) self.dock.setWidget(self.fileView) # take a shot self.pic = QAction(QIcon.fromTheme("camera-photo"), 'Screenshot', self) self.pic.triggered.connect(lambda: QPixmap.grabWindow( QApplication.desktop().winId()).save(QFileDialog.getSaveFileName( self.dock, " Save Screenshot As ... ", HOME, ';;(*.png)'))) # copy time self.tim = QAction(QIcon.fromTheme("user-away"), 'Date and Time to Clipboard', self) self.tim.triggered.connect(lambda: QApplication.clipboard().setText( datetime.now().strftime(" %A %B %d-%m-%Y %H:%M:%S %p "))) # color chooser self.cl = QAction(QIcon.fromTheme("applications-graphics"), 'Color Chooser to Clipboard', self) self.cl.triggered.connect(lambda: QApplication.clipboard().setText( '{}'.format(QColorDialog.getColor().name()))) # icon chooser self.icn = QAction(QIcon.fromTheme("insert-image"), 'Icon Chooser to Clipboard', self) self.icn.triggered.connect(self.iconChooser) # tool bar with actions QToolBar(self.dock).addActions((self.cl, self.icn, self.tim, self.pic)) self.textBrowser = QTextBrowser(self.dock) self.textBrowser.setAutoFillBackground(True) self.textBrowser.setGeometry(self.dock.geometry()) self.textBrowser.hide() def processFinished(self): ' print info of finished processes ' print(" INFO: OK: QProcess finished . . . ") def search(self): ' function to search python files ' # get search results of python filenames local or remote pypi_url = 'http://pypi.python.org/pypi' # pypi query pypi = xmlrpclib.ServerProxy(pypi_url, transport=ProxyTransport()) try: pypi_query = pypi.search({'name': str(self.srch.text()).lower()}) pypi_fls = list(set(['pypi.python.org/pypi/' + a['name'] + ' | pip install ' + a['name'] for a in pypi_query])) except: pypi_fls = '<b> ERROR: Internet not available! ಠ_ಠ </b>' s_out = ('<br> <br> <br> <h3> Search Local Python files: </h3> <hr> ' + # Jedi list comprehension for LOCAL search str(["{}/{}".format(root, f) for root, f in list(itertools.chain(* [list(itertools.product([root], files)) for root, dirs, files in walk(str( QFileDialog.getExistingDirectory(self.dock, 'Open Directory to Search', path.expanduser("~"))))])) if f.endswith(('.py', '.pyw', '.pth')) and not f.startswith('.') and str(self.srch.text()).lower().strip() in f] ).replace(',', '<br>') + '<hr><h3> Search PyPI Python files: </h3>' + # wraped pypi query REMOTE search str(pypi_fls).replace(',', '<br>') + '<hr>Auto-Proxy:ON,DoNotTrack:ON') # print(s_out) try: call('notify-send fileXplorer Searching...', shell=True) except: pass self.srch.clear() self.textBrowser.setGeometry(self.dock.geometry()) self.textBrowser.setHtml(s_out) self.textBrowser.show() tmr = QTimer(self.fileView) tmr.timeout.connect(self.textBrowser.hide) tmr.start(20000) def iconChooser(self): ' Choose a Icon and copy it to clipboard ' # from .std_icon_naming import std_icon_naming as a # prv = QDialog(self.dock) prv.setWindowFlags(Qt.FramelessWindowHint) prv.setAutoFillBackground(True) prv.setGeometry(self.fileView.geometry()) table = QTableWidget(prv) table.setColumnCount(1) table.setRowCount(len(a)) table.verticalHeader().setVisible(True) table.horizontalHeader().setVisible(False) table.setShowGrid(True) table.setIconSize(QSize(128, 128)) for index, icon in enumerate(a): item = QTableWidgetItem(QIcon.fromTheme(icon), '') # item.setData(Qt.UserRole, '') item.setToolTip(icon) table.setItem(index, 0, item) table.clicked.connect(lambda: QApplication.clipboard().setText( 'QtGui.QIcon.fromTheme("{}")'.format(table.currentItem().toolTip()))) table.doubleClicked.connect(prv.close) table.resizeColumnsToContents() table.resizeRowsToContents() QLabel('<h3> <br> 1 Click Copy, 2 Clicks Close </h3>', table) table.resize(prv.size()) prv.exec_() def runfile(self, index): ' run the choosed file ' s = str(file(self.model.filePath(index), 'r').read().strip()) f = str(self.model.filePath(index)) # ctime is NOT crossplatform,metadata change on *nix,creation on Window # http://docs.python.org/library/os.path.html#os.path.getctime m = ''.join((f, N, str(path.getsize(f) / 1024), ' Kilobytes', N, str(len(file(f, 'r').readlines())), ' Lines', N, str(len(s.replace(N, ''))), ' Characters', N, str(len([a for a in sub('[^a-zA-Z0-9 ]', '', s).split(' ') if a != ''])), ' Words', N, str(len([a for a in s if a in punctuation])), ' Punctuation', N, oct(stat(f).st_mode)[-3:], ' Permissions', N, time.ctime(path.getatime(f)), ' Accessed', N, time.ctime(path.getmtime(f)), ' Modified', N, 'Owner: ', str(self.model.fileInfo(index).owner()), N, 'Is Writable: ', str(self.model.fileInfo(index).isWritable()), N, 'Is Executable: ', str(self.model.fileInfo(index).isExecutable()), N, 'Is Hidden: ', str(self.model.fileInfo(index).isHidden()), N, 'Is SymLink: ', str(self.model.fileInfo(index).isSymLink()), N, 'File Extension: ', str(self.model.fileInfo(index).suffix()) )) #print(m) self.preview.setToolTip(m) self.preview.setText(s) self.preview.resize(self.preview.size().width(), self.dock.size().height()) self.process.start('xdg-open {}'.format(f)) if not self.process.waitForStarted(): print((" ERROR: Process {} Failed ! ".format(str(f)))) return
class ComposerWrapper(QObject): """ Embeds custom STDM tools in a QgsComposer instance for managing map-based STDM document templates. """ dataSourceSelected = pyqtSignal(str) def __init__(self, composerView): QObject.__init__(self, composerView) self._compView = composerView self._stdmTB = self.mainWindow().addToolBar("STDM") self._selectMoveAction = None #Container for custom editor widgets self._widgetMappings = {} #Create dock widget for configuring STDM data source self._stdmDataSourceDock = QDockWidget( QApplication.translate("ComposerWrapper", "STDM Data Source"), self.mainWindow()) self._stdmDataSourceDock.setObjectName("STDMDataSourceDock") self._stdmDataSourceDock.setMinimumWidth(300) self._stdmDataSourceDock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable) self.mainWindow().addDockWidget(Qt.RightDockWidgetArea, self._stdmDataSourceDock) dataSourceWidget = ComposerDataSourceSelector() self._stdmDataSourceDock.setWidget(dataSourceWidget) self._stdmDataSourceDock.show() #Create dock widget for configuring STDM item properties self._stdmItemPropDock = QDockWidget( QApplication.translate("ComposerWrapper", "STDM data properties"), self.mainWindow()) self._stdmItemPropDock.setObjectName("STDMItemDock") self._stdmItemPropDock.setMinimumWidth(300) self._stdmItemPropDock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetClosable) self.mainWindow().addDockWidget(Qt.RightDockWidgetArea, self._stdmItemPropDock) self._stdmItemPropDock.show() if self.itemDock() != None: self.mainWindow().tabifyDockWidget(self.itemDock(), self._stdmItemPropDock) if self.atlasDock() != None: self.atlasDock().hide() if self.generalDock() != None: self.generalDock().raise_() #Connect signals self.composition().itemRemoved.connect(self._onItemRemoved) dataSourceWidget.cboDataSource.currentIndexChanged[str].connect( self.propagateDataSourceSelection) self.composerView().selectedItemChanged.connect(self._onItemSelected) #Current template document file self._currDocFile = None def _removeActions(self): """ Remove inapplicable actions and their corresponding toolbars and menus. """ removeActions = [ "mActionSaveProject", "mActionNewComposer", "mActionDuplicateComposer" ] composerToolbar = self.composerMainToolBar() if composerToolbar != None: saveProjectAction = None for itemAction in composerToolbar.actions(): if itemAction.objectName() == "mActionSaveProject": saveProjectAction = itemAction break if saveProjectAction != None: composerMenu = saveProjectAction.menu() def configure(self): #Create instances of custom STDM composer item configurations for ciConfig in ComposerItemConfig.itemConfigurations: ciConfigObj = ciConfig(self) def addWidgetMapping(self, uniqueIdentifier, widget): """ Add custom STDM editor widget based on the unique identifier of the composer item """ self._widgetMappings[uniqueIdentifier] = widget def widgetMappings(self): """ Returns a dictionary containing uuid values of composer items linked to STDM widgets. """ return self._widgetMappings def clearWidgetMappings(self): """ Resets the widget mappings collection. """ self._widgetMappings = {} def mainWindow(self): """ Returns the QMainWindow used by the composer view. """ return self._compView.composerWindow() def stdmToolBar(self): """ Returns the instance of the STDM toolbar added to the QgsComposer. """ return self._stdmTB def composerView(self): """ Returns the composer view. """ return self._compView def composition(self): """ Returns the QgsComposition instance used in the composer view. """ return self._compView.composition() def composerItemToolBar(self): """ Returns the toolbar containing actions for adding composer items. """ return self.mainWindow().findChild(QToolBar, "mItemToolbar") def composerMainToolBar(self): """ Returns the toolbar containing actions for managing templates. """ return self.mainWindow().findChild(QToolBar, "mComposerToolbar") def selectMoveAction(self): """ Returns the QAction for selecting or moving composer items. """ if self.composerItemToolBar() != None: if self._selectMoveAction == None: for itemAction in self.composerItemToolBar().actions(): if itemAction.objectName() == "mActionSelectMoveItem": self._selectMoveAction = itemAction break return self._selectMoveAction def checkedItemAction(self): """ Returns the currently selected composer item action. """ if self.selectMoveAction() != None: return self.selectMoveAction().actionGroup().checkedAction() return None def itemDock(self): """ Get the 'Item Properties' dock widget. """ return self.mainWindow().findChild(QDockWidget, "ItemDock") def atlasDock(self): """ Get the 'Atlas generation' dock widget. """ return self.mainWindow().findChild(QDockWidget, "AtlasDock") def generalDock(self): """ Get the 'Composition' dock widget. """ return self.mainWindow().findChild(QDockWidget, "CompositionDock") def stdmDataSourceDock(self): """ Returns the STDM data source dock widget. """ return self._stdmDataSourceDock def stdmItemDock(self): """ Returns the STDM item dock widget. """ return self._stdmItemPropDock def documentFile(self): """ Returns the QFile instance associated with the current document. 'None' will be returned for new, unsaved documents. """ return self._currDocFile def setDocumentFile(self, docFile): """ Sets the document file. """ if not isinstance(docFile, QFile): return self._currDocFile = docFile def selectedDataSource(self): """ Returns the name of the data source specified by the user. """ return self._stdmDataSourceDock.widget().cboDataSource.currentText() def selectedDataSourceCategory(self): """ Returns the category (view or table) that the data source belongs to. """ if self.stdmDataSourceDock().widget() != None: return self.stdmDataSourceDock().widget().category() return "" def propagateDataSourceSelection(self, dataSourceName): """ Propagates the signal when a user select a data source. Listening objects can hook on to it. """ self.dataSourceSelected.emit(dataSourceName) def loadTemplate(self, filePath): """ Loads a document template into the view and updates the necessary STDM-related controls. """ if not QFile.exists(filePath): QMessageBox.critical(self.composerView(), QApplication.translate("OpenTemplateConfig","Open Template Error"), \ QApplication.translate("OpenTemplateConfig","The specified template does not exist.")) return templateFile = QFile(filePath) if not templateFile.open(QIODevice.ReadOnly): QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Open Operation Error"), \ "{0}\n{1}".format(QApplication.translate("ComposerWrapper","Cannot read template file."), \ templateFile.errorString() )) return templateDoc = QDomDocument() if templateDoc.setContent(templateFile): #Load items into the composition and configure STDM data controls self.composition().loadFromTemplate(templateDoc) self.clearWidgetMappings() #Load data controls composerDS = ComposerDataSource.create(templateDoc) self._configureDataControls(composerDS) #Load symbol editors spatialFieldsConfig = SpatialFieldsConfiguration.create( templateDoc) self._configureSpatialSymbolEditor(spatialFieldsConfig) def saveTemplate(self): """ Creates and saves a new document template. """ #Validate if the user has specified the data source if self.selectedDataSource() == "": QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Error"), \ QApplication.translate("ComposerWrapper","Please specify the " \ "data source name for the document composition.")) return #If it is a new unsaved document template then prompt for the document name. docFile = self.documentFile() if docFile == None: docName,ok = QInputDialog.getText(self.composerView(), \ QApplication.translate("ComposerWrapper","Template Name"), \ QApplication.translate("ComposerWrapper","Please enter the template name below"), \ ) if ok and docName != "": templateDir = self._composerTemplatesPath() if templateDir == None: QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Error"), \ QApplication.translate("ComposerWrapper","Directory for document templates could not be found.")) return absPath = templateDir + "/" + docName + ".sdt" docFile = QFile(absPath) else: return docFileInfo = QFileInfo(docFile) if not docFile.open(QIODevice.WriteOnly): QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Save Operation Error"), \ "{0}\n{1}".format(QApplication.translate("ComposerWrapper","Could not save template file."), \ docFile.errorString() )) return templateDoc = QDomDocument() self._writeXML(templateDoc, docFileInfo.completeBaseName()) if docFile.write(templateDoc.toByteArray()) == -1: QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Save Error"), \ QApplication.translate("ComposerWrapper","Could not save template file.")) return docFile.close() self.setDocumentFile(docFile) def _writeXML(self, xmlDoc, docName): """ Write the template configuration into the XML document. """ #Write default composer configuration composerElement = xmlDoc.createElement("Composer") composerElement.setAttribute("title", docName) composerElement.setAttribute("visible", 1) xmlDoc.appendChild(composerElement) self.composition().writeXML(composerElement, xmlDoc) #Write STDM data field configurations dataSourceElement = ComposerDataSource.domElement(self, xmlDoc) composerElement.appendChild(dataSourceElement) #Write spatial field configurations spatialColumnsElement = SpatialFieldsConfiguration.domElement( self, xmlDoc) dataSourceElement.appendChild(spatialColumnsElement) def _configureDataControls(self, composerDataSource): """ Configure the data source and data field controls based on the composer data source configuration. """ if self.stdmDataSourceDock().widget() != None: #Set data source dataSourceWidget = self.stdmDataSourceDock().widget() dataSourceWidget.setCategory(composerDataSource.category()) dataSourceWidget.setSelectedSource(composerDataSource.name()) #Set data field controls for composerId in composerDataSource.dataFieldMappings().reverse: #Use composer item id since the uuid is stripped off composerItem = self.composition().getComposerItemById( composerId) if composerItem != None: compFieldSelector = ComposerFieldSelector( self, composerItem, self.composerView()) compFieldSelector.selectFieldName( composerDataSource.dataFieldName(composerId)) #Add widget to the collection but now use the current uuid of the composition item self.addWidgetMapping(composerItem.uuid(), compFieldSelector) def _configureSpatialSymbolEditor(self, spatialFieldConfig): """ Configure symbol editor controls. """ if self.stdmDataSourceDock().widget() != None: for itemId, spFieldsMappings in spatialFieldConfig.spatialFieldsMapping( ).iteritems(): mapItem = self.composition().getComposerItemById(itemId) if mapItem != None: composerSymbolEditor = ComposerSymbolEditor( self, self.composerView()) composerSymbolEditor.addSpatialFieldMappings( spFieldsMappings) #Add widget to the collection but now use the current uuid of the composer map self.addWidgetMapping(mapItem.uuid(), composerSymbolEditor) def _composerTemplatesPath(self): """ Reads the path of composer templates in the registry. """ regConfig = RegistryConfig() keyName = "ComposerTemplates" valueCollection = regConfig.read([keyName]) if len(valueCollection) == 0: return None else: return valueCollection[keyName] def _onItemRemoved(self, item): """ Slot raised when a composer item is removed from the scene. """ """ Code will not work since a QObject instance is returned instead of a QgsComposerItem if item.uuid() in self._widgetMappings: del self._widgetMappings[item.uuid()] """ pass def _onItemSelected(self, item): """ Slot raised when a composer item is selected. Load the corresponding field selector if the selection is an STDM data field label. QComposerLabel is returned as a QObject in the slot argument hence, we have resorted to capturing the currently selected items in the composition. """ selectedItems = self.composition().selectedComposerItems() if len(selectedItems) == 0: self._stdmItemPropDock.setWidget(None) elif len(selectedItems) == 1: composerItem = selectedItems[0] if composerItem.uuid() in self._widgetMappings: stdmWidget = self._widgetMappings[composerItem.uuid()] if stdmWidget == self._stdmItemPropDock.widget(): return else: self._stdmItemPropDock.setWidget(stdmWidget) #Playing it safe in applying the formatting for the editor controls where applicable itemFormatter = None if isinstance(composerItem, QgsComposerArrow): itemFormatter = LineFormatter() elif isinstance(composerItem, QgsComposerLabel): itemFormatter = DataLabelFormatter() elif isinstance(composerItem, QgsComposerMap): itemFormatter = MapFormatter() if itemFormatter != None: itemFormatter.apply(composerItem, self, True) else: self._stdmItemPropDock.setWidget(None) elif len(selectedItems) > 1: self._stdmItemPropDock.setWidget(None)
class OneClockAddon: def __init__(self): self.db = TomatoDB("_TomatoClock.db") self.dlg = OneClock(mw) self.pb = None self._connect_slots() self._set_style_sheet(mw) self.tm = None self.dlg_rest = None self.pb_w = None self.replace_mw_overview() self.replace_mw_deckbrowser() def replace_mw_overview(self): mw.overview = anki_overview(self.dlg, self.db) def replace_mw_reviewer(self): mw.reviewer = anki_reviewer(self.dlg.mode, self.db) def replace_mw_deckbrowser(self): mw.deckBrowser = anki_deckbrowser(self.db) mw.deckBrowser.refresh() @staticmethod def _set_style_sheet(obj): with open(os.path.join(os.path.dirname(__file__), "ui", "designer", "style.css"), "r") as f: obj.setStyleSheet(f.read()) def _connect_slots(self): self.dlg.btn_start.clicked.connect(self.on_btn_start_clicked) def perform_hooks(self, func): func('reviewCleanup', self.on_review_cleanup) func('profileLoaded', self.on_profile_loaded) func('afterStateChange', self.after_anki_state_change) def on_profile_loaded(self): ProfileConfig.donate_alerted = False UserConfig.BREAK_MINUTES # just ensure json file is generated try: if UserConfig.LIVE_CODE_DOWNLOAD: thr = _live_chart_py_downloader() thr.start() except: pass def on_review_cleanup(self): mw.setWindowIcon(QIcon(":/icons/anki.png")) if self.tm and self.tm.isActive(): self.tm.stop() if self.pb: self.pb_w.hide() self.pb.reset() if self.dlg_rest: self.dlg_rest.hide() try: mw.reviewer.restore_layouts() except AttributeError: # just in case "replace_mw_reviewer" is not called pass def on_btn_start_clicked(self): self.replace_mw_reviewer() mw.setWindowIcon(QIcon(":/icon/tomato.png")) assert isinstance(mw, AnkiQt) self.setup_progressbar() self.pb.set_seconds(self.dlg.min * MIN_SECS) if not self.tm: self.tm = Timer(mw) self.tm.timeout.connect(self.on_timer) self.tm.start() # click study button mw.overview._linkHandler("study") self.db.start_session( self.dlg.min, UserConfig.ANSWER_TIMEOUT_SECONDS, self.dlg.mode ) def after_anki_state_change(self, state, oldState): if state == 'overview' and oldState == 'review': self.on_tomato(False) mw.overview.refresh() def on_tomato(self, from_timer=True): self.db.end_session() if self.tm: self.tm.stop() self.pb_w.hide() self.pb.reset() if from_timer: mw.moveToState("overview") if not self.dlg_rest: self.dlg_rest = RestDialog(mw) self._set_style_sheet(self.dlg_rest) self.dlg_rest.accepted.connect(self.on_dlg_rest_accepted) self.dlg_rest.rejected.connect(self.on_dlg_rest_rejected) if UserConfig.PLAY_SOUNDS["break"]: play(BREAK) self.dlg_rest.exec_(self.dlg.min) @staticmethod def on_dlg_rest_accepted(): mw.overview._linkHandler("tomato_clock") def on_dlg_rest_rejected(self): pass def on_timer(self): if self.pb: self.pb_w.show() self.pb.on_timer() self.db.commit() def setup_progressbar(self): dockArea = Qt.TopDockWidgetArea # dockArea = Qt.LeftDockWidgetArea # dockArea = Qt.BottomDockWidgetArea self.pb_w = QDockWidget(mw) self.pb_w.setObjectName("progress_dock") if not self.pb: self.pb = ClockProgress(mw, dockArea) self.pb.tomato.connect(self.on_tomato) else: self.pb.reset() self.pb.set_seconds(self.dlg.min * MIN_SECS) self.pb_w.setWidget(self.pb) w = QWidget(self.pb_w) w.setFixedHeight(self.pb.height()) self.pb_w.setTitleBarWidget(w) self.pb_w.setFeatures(QDockWidget.NoDockWidgetFeatures) # first check existing widgets existing_widgets = [widget for widget in mw.findChildren(QDockWidget) if mw.dockWidgetArea(widget) == dockArea] # then add ourselves mw.addDockWidget(dockArea, self.pb_w) # stack with any existing widgets if len(existing_widgets) > 0: mw.setDockNestingEnabled(True) if dockArea == Qt.TopDockWidgetArea or dockArea == Qt.BottomDockWidgetArea: stack_method = Qt.Vertical if dockArea == Qt.LeftDockWidgetArea or dockArea == Qt.RightDockWidgetArea: stack_method = Qt.Horizontal mw.splitDockWidget(existing_widgets[0], self.pb_w, stack_method) mw.web.setFocus() self._set_style_sheet(self.pb)