class MSMainWindow(QMainWindow): """Gui of the main window""" # MAX_RECENT_FILES = 10 # start putting links spyder numpy scipy et tutti quanti links = ( "http://numpy.scipy.org/", "http://packages.python.org/spyder/", "http://www.riverbankcomputing.co.uk/software/pyqt/intro", ) pluginPath = path.normcase("pluginmanager/plugins/") def __init__(self, availablePlugins): """ Constructor with all the models needed setup menus """ QMainWindow.__init__(self) self.setDockOptions(QMainWindow.VerticalTabs | QMainWindow.AnimatedDocks) self.plugins = availablePlugins self.pluginsInst = [] settings = QSettings( "INRA/INSA", "-".join([QApplication.instance().APPLICATION_NAME_STR, QApplication.instance().VERSION_STR]) ) self.recentFiles = list(settings.value("RecentFiles").toStringList()) self.setStyleSheet(stylesheet) self.pipeline = MSPipelineToolBar("Pipeline toolbar", parent=self) self.addToolBar(0x1, self.pipeline) self._setupModels() self._setupUi() self._setupMenus() def _setupModels(self): """ Warning:Causes segfault when horizontal labels set to True on aura peu etre a la fin un model par sampleList c'est ce qui parait le plus logique """ # drag and drop table sample self.sampleModel = QStandardItemModel(self) self.sampleModel.setHorizontalHeaderLabels(["Sample", "Class"]) # treeView1 self.spectraModel = QStandardItemModel(self) # treeview2 self.peakModel = QStandardItemModel(self) # treeview3 self.clusterModel = QStandardItemModel(self) def _setupMenus(self): # file self.fileMenu = QMenu("&File") self.fileMenu.setTearOffEnabled(True) self.op = QMenu("&Open...", self.fileMenu) self.op.setIcon(QIcon(path.normcase("gui/icons/fileopen.png"))) open_ = QAction("&Open rawfiles", self) open_.setToolTip("Open an mzXML or netCDF file") open_.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_O)) open_icon = QIcon(path.normcase("gui/icons/fileopen.png")) open_.setIcon(open_icon) self.op.addAction(open_) load_ = QAction("&Open projects...", self) load_.setToolTip("load binary file containing saved objects") load_.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_S)) load_icon = QIcon(QPixmap(path.normcase("gui/icons/project_open.png"))) load_.setIcon(load_icon) self.op.addAction(load_) self.fileMenu.addMenu(self.op) save_ = QAction("&Save...", self) save_.setToolTip("save the actual application model") save_.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_S)) save_icon = QIcon(path.normcase("gui/icons/save_all.png")) save_.setIcon(save_icon) self.fileMenu.addAction(save_) pkl = QAction("&load a peaklist", self) # TODO:load peaklist pkl.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_P)) pkl.setToolTip("load a peaklist and process it") pkl.setIcon(QIcon(path.normcase("gui/icons/featuredetect.png"))) self.fileMenu.addAction(pkl) convert_ = QAction("&Convert...", self) convert_.setEnabled(False) convert_.setToolTip("Convert a .wiff file if Analyst(c) is installed") convert_icon = QIcon(path.normcase("gui/icons/goto.png")) convert_.setIcon(convert_icon) self.fileMenu.addAction(convert_) a = self.fileMenu.addAction(QIcon(path.normcase("gui/icons/process.png")), "&Launch a batch") a.setEnabled(False) b = self.fileMenu.addAction(QIcon(path.normcase("gui/icons/process.png")), "&Merge") b.setToolTip("Merge MRM file") # b.setEnabled(False) self.fileMenu.addSeparator() # # for i in xrange(self.MAX_RECENT_FILES): # a = QAction('', self) # a.setVisible(False) # self.fileMenu.addAction(a) # # for i in xrange(min(self.MAX_RECENT_FILES, len(self.recentFiles))): # self.fileMenu.actions()[5+i].setVisible(True) # self.fileMenu.actions()[5+i].setText(self.recentFiles[i].split('/')[-1]) exit_action = QAction("&Exit", self) exit_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_Q)) exit_action.setIcon(QIcon(QPixmap(path.normcase("gui/icons/exit.png")))) self.fileMenu.addAction(exit_action) self.menuBar().addMenu(self.fileMenu) self.editMenu = QMenu("&Edit") self.editMenu.setTearOffEnabled(True) self.editMenu.addAction(QIcon(path.normcase("gui/icons/edit_undo.png")), "&Undo...") self.editMenu.addAction(QIcon(path.normcase("gui/icons/edit_redo.png")), "&Redo...") self.editMenu.actions()[0].setEnabled(False) self.editMenu.actions()[1].setEnabled(False) self.editMenu.addSeparator() self.editMenu.addAction(QIcon(path.normcase("gui/icons/run.png")), "&Preferences") self.exportMenu = QMenu("&Export...") self.exportMenu.setIcon(QIcon(path.normcase("gui/icons/file_export.png"))) self.exportMenu.addAction("&Peaklist") self.exportMenu.addAction("&Clusters intensity matrix") self.editMenu.addMenu(self.exportMenu) self.menuBar().addMenu(self.editMenu) # view self.viewMenu = QMenu("&View") self.viewMenu.setTearOffEnabled(True) self.viewMenu.addAction( QIcon(path.normcase("gui/icons/window_duplicate")), "&Cascade View", self.mdiArea.cascadeSubWindows, QKeySequence(Qt.CTRL + Qt.Key_K), ) self.viewMenu.addAction( QIcon(path.normcase("gui/icons/view_icon")), "&Title View", self.mdiArea.tileSubWindows, QKeySequence(Qt.CTRL + Qt.Key_N), ) self.viewMenu.addAction( QIcon(path.normcase("gui/icons/stop_process.png")), "&Close all subWindows", self.mdiArea.closeAllSubWindows, QKeySequence(Qt.CTRL + Qt.Key_W), ) self.plotting = QMenu("&Plotting...") self.plotting.setIcon(QIcon(QPixmap(path.normcase("gui/icons/plot.png")))) self.plotting.addAction("&3D Plot") # self.plotting.addAction("&Cytoscape web") self.plotting.addAction("&Spectrogram Plot") # self.multiplePlot = QAction("&Visualize Raw/Treated Data", self) # self.multiplePlot.setCheckable(True) # self.multiplePlot.setEnabled(False) # self.sub_plot_.addAction(self.multiplePlot) self.viewMenu.addMenu(self.plotting) self.viewMenu.addSeparator() self.show_hide = QMenu("&Show/Hide") m = self.createPopupMenu() m.setTitle("&Show/Hide") self.viewMenu.addMenu(m) # self.pref = QMenu("&Preferences") # self.pref.addAction(self.multiplePlot) # self.viewMenu.addMenu(self.pref) self.menuBar().addMenu(self.viewMenu) # algorithm self.algoMenu = QMenu("&Algorithm") self.algoMenu.setTearOffEnabled(True) self.preProcessing = QMenu("&PreProcessing(experimental)") self.preProcessing.addAction("&Smoothing raw data...") self.preProcessing.addAction("&Cut off raw data...") self.preProcessing.addAction("&Calibration (mz dimension)") self.preProcessing.addAction("&Resize sample...") self.algoMenu.addMenu(self.preProcessing) self.peakPickingMenu = QMenu("&Peack Picking & Alignement(XCMS)", self) self.peakPickingMenu.setIcon(QIcon(path.normcase("gui/icons/pickedpeakicon.png"))) matched = QAction("&MatchedFiltered", self) matched.setIcon(QIcon(path.normcase("gui/icons/RLogo"))) matched.setToolTip("Peak Detection and Integration using MatchedFiltered algorithm") self.peakPickingMenu.addAction(matched) centwave = QAction("&CentWave", self) centwave.setIcon(QIcon(path.normcase("gui/icons/RLogo"))) centwave.setToolTip("Peak Detection and Integration using CentWave algorithm") self.peakPickingMenu.addAction(centwave) # peak_.setShortcut(.QKeySequence(CTRL + Key_P)) # peak_icon=.QIcon(.QPixmap(path.normcase("gui/icons/pickedpeakicon.png"))) # peak_.setIcon(peak_icon) self.algoMenu.addMenu(self.peakPickingMenu) self.alignment = QMenu("&Alignment") self.alignment.setIcon(QIcon(path.normcase("gui/icons/format_indent_more.png"))) self.alignment.addAction("&Polynomial fitting(exp)") self.alignment.addAction("&DynamicTimeWarping") self.alignment.addAction("&ObiWarp") self.alignment.actions()[2].setEnabled(False) self.algoMenu.addMenu(self.alignment) self.algoMenu.addAction("Normalization") clust_ = QAction("&Clustering", self) clust_.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_L)) clust_icon = QIcon(QPixmap(path.normcase("gui/icons/cluster.png"))) clust_.setIcon(clust_icon) self.algoMenu.addAction(clust_) id_ = QAction("&Identification", self) id_.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_I)) id_.setToolTip("Try to identify peaks with several methods") id_.setIcon(QIcon(QPixmap(path.normcase("gui/icons/findcompound.png")))) self.algoMenu.addAction(id_) self.menuBar().addMenu(self.algoMenu) # tools self.toolsMenu = QMenu("&Tools") self.toolsMenu.setTearOffEnabled(True) web = QAction("&Web Browser", self) web.setIcon(QIcon(QPixmap(path.normcase("gui/icons/applications_internet.png")))) self.toolsMenu.addAction(web) # cyto = QAction("&cytoscape", self) # cyto_icon =QIcon(QPixmap(path.normcase("gui/icons/cytoscape.jpeg"))) # cyto.setIcon(cyto_icon) # self.toolsMenu.addAction(cyto) editor = QAction("&Editor", self) editor.setIcon(QIcon(QPixmap(path.normcase("gui/icons/document_sign.png")))) self.toolsMenu.addAction(editor) pet = QAction("&Short Periodic Table", self) pet.setIcon(QIcon(QPixmap(path.normcase("gui/icons/pet.jpg")))) self.toolsMenu.addAction(pet) self.menuBar().addMenu(self.toolsMenu) # plugins self.pluginMenu = QMenu("&Plugins") self.pluginMenu.setTearOffEnabled(True) instPl = QAction("&Install a plugin", self) instPl.setIcon(QIcon(path.normcase("gui/icons/pluginInstall.png"))) self.pluginMenu.addAction(instPl) self.launchingMenu = QMenu("&Launch PLugins", self) self.launchingMenu.setIcon(QIcon(path.normcase("gui/icons/plugin"))) for plug in self.plugins: # fullname="".join([self.pluginPath, str(plug)]) mod = imp.load_source(self.__module__, plug) if mod.autoActivation: qApp = QApplication.instance() name = getattr(mod, "className") cls = getattr(mod, name) p = cls(qApp.model, self, parent=self) # p=qApp.pluginManager.loadPlugin(qApp.model, self, plug.split('/')[-1]) self.pluginsInst.append(p) else: self.launchingMenu.addAction(plug.split("/")[-1]) self.pluginMenu.addMenu(self.launchingMenu) self.pluginMenu.addAction(QIcon(path.normcase("gui/icons/process_stop.png")), "&Remove loaded Plugin") self.menuBar().addMenu(self.pluginMenu) # about self.aboutMenu = QMenu("&About") self.aboutMenu.setTearOffEnabled(True) metms = QAction(QIcon(path.normcase("gui/icons/deluge.png")), "&about metMS...", self) self.aboutMenu.addAction(metms) pyqt = QAction("&about PyQt4...", self) pyqt_icon = QIcon(QPixmap(path.normcase("gui/icons/logo_QT4.png"))) pyqt.setIcon(pyqt_icon) self.aboutMenu.addAction(pyqt) metms = QAction("&metMS Documentation", self) metms_icon = QIcon(QPixmap(path.normcase("gui/icons/deluge.png"))) metms.setIcon(metms_icon) self.aboutMenu.addAction(metms) self.menuBar().addMenu(self.aboutMenu) def _setupUi(self, background=None): """ Make the GUI """ # mdi self.mdiArea = MSMdiArea(self) self.mdiArea.setBackground(QBrush(QPixmap(path.normcase("gui/icons/blac2.png")))) # QColor(Qt.blue).darker())) self.setCentralWidget(self.mdiArea) # sample dock widget self.sampleDockWidget = QDockWidget("Samples", self) # sampleWidget = QWidget() self.sampleTableView = MSDragFromTableView() self.sampleTableView.setModel(self.sampleModel) self.sampleTableView.setSelectionBehavior(1) self.sampleTableView.verticalHeader().hide() self.sampleTableView.verticalHeader().setDefaultSectionSize(15) self.sampleTableView.horizontalHeader().setDefaultSectionSize(150) self.sampleDockWidget.setWidget(self.sampleTableView) # sampleWidget) self.sampleDockWidget.visible = True # workflow dock self.workflowDockWidget = QDockWidget("Visualizer", self) self.workflowDockWidget.visible = True a = QWidget(self) v = QVBoxLayout(a) q = QToolBar() # self.workingSample = QLabel("Working Sample:None") # q.addWidget(self.workingSample) q.addWidget(QLabel("ppm :")) self.ppmEditer = QDoubleSpinBox() self.usePpm = QCheckBox("use ?") q.addWidget(self.ppmEditer) q.addWidget(self.usePpm) q.addSeparator() self.removeButton = QToolButton(self) self.removeButton.setIcon(QIcon(path.normcase("gui/icons/delete.png"))) q.addWidget(self.removeButton) self.markAsGood = QAction(QIcon(path.normcase("gui/icons/button_ok.png")), "mark peak as good", self) self.markAsBad = QAction(QIcon(path.normcase("gui/icons/stop.png")), "mark peak as bad", self) self.hideItem = QAction(QIcon(path.normcase("gui/icons/list_remove.png")), "Hide Item", self) q.addAction(self.markAsGood) q.addAction(self.markAsBad) q.addAction(self.hideItem) v.addWidget(q) self.tabWidget = QTabWidget() self.tab = QWidget() verticalLayout = QVBoxLayout(self.tab) self.treeView = MSToDropTableView() self.treeView.verticalHeader().setDefaultSectionSize(20) self.treeView.setModel(self.spectraModel) self.spectraLabel = QLabel("Sample: None") verticalLayout.addWidget(self.treeView) verticalLayout.addWidget(self.spectraLabel) self.tabWidget.addTab(self.tab, QIcon(path.normcase("gui/icons/spectrumicon.png")), "Spectra") self.tab_2 = QWidget() verticalLayout_4 = QVBoxLayout(self.tab_2) self.treeView_2 = MSToDropTableView() # MSTreeView(self.tab_2)# QTableView(self)# self.treeView_2.verticalHeader().setDefaultSectionSize(20) self.treeView_2.setModel(self.peakModel) self.peakLabel = QLabel("Sample: None") verticalLayout_4.addWidget(self.treeView_2) verticalLayout_4.addWidget(self.peakLabel) self.tabWidget.addTab(self.tab_2, QIcon(path.normcase("gui/icons/peakicon.png")), "Peaks List") self.tab_3 = QWidget() verticalLayout_5 = QVBoxLayout(self.tab_3) self.treeView_3 = MSToDropTreeView() self.treeView_3.setAnimated(True) self.treeView_3.setModel(self.clusterModel) self.clusterLabel = QLabel("Sample: None") verticalLayout_5.addWidget(self.treeView_3) verticalLayout_5.addWidget(self.clusterLabel) self.tabWidget.addTab(self.tab_3, QIcon(path.normcase("gui/icons/clustering.png")), "Clusters") self.tabWidget.setCurrentIndex(0) for l in (self.spectraLabel, self.peakLabel, self.clusterLabel): l.setAutoFillBackground(True) v.addWidget(self.tabWidget) self.workflowDockWidget.setWidget(a) self.addDockWidget(Qt.DockWidgetArea(0x2), self.workflowDockWidget) from gui.MetBaseGui import MSIsoCalculator self.isoCalc = MSIsoCalculator(self) self.isoCalcDockWidget = QDockWidget("isotopes calculation", self) self.isoCalcDockWidget.setWidget(self.isoCalc) self.addDockWidget(Qt.DockWidgetArea(0x2), self.isoCalcDockWidget) self.isoCalcDockWidget.setVisible(False) self.isoCalcDockWidget.visible = False from gui.MetBaseGui import FormulaGenerator self.generator = FormulaGenerator(self) self.generatorDockWidget = QDockWidget("formula generator", self) self.generatorDockWidget.setWidget(self.generator) self.addDockWidget(Qt.DockWidgetArea(0x2), self.generatorDockWidget) self.generatorDockWidget.setVisible(False) self.generatorDockWidget.visible = False self.compoundTreeView = MSCompoundTreeView(self) self.compoundDockWidget = QDockWidget("Compounds", self) self.compoundDockWidget.setWidget(self.compoundTreeView) self.addDockWidget(Qt.DockWidgetArea(0x2), self.compoundDockWidget) self.compoundDockWidget.setVisible(False) self.compoundDockWidget.visible = False self.comparativeTableView = QTableView(self) self.comparativeTableView.horizontalHeader().setStretchLastSection(True) self.comparativeTableView.verticalHeader().setDefaultSectionSize(20) self.comparativeDock = QDockWidget("Comparative View", self) self.comparativeDock.setWidget(self.comparativeTableView) self.addDockWidget(Qt.DockWidgetArea(0x8), self.comparativeDock) self.comparativeDock.setVisible(False) self.comparativeDock.visible = False self.tabifyDockWidget(self.compoundDockWidget, self.isoCalcDockWidget) self.tabifyDockWidget(self.isoCalcDockWidget, self.workflowDockWidget) self.tabifyDockWidget(self.workflowDockWidget, self.generatorDockWidget) # set the end # WARNING: possible that the internal shell widget cause random segfault # with the error of QObject::killTimers...? not sure ! self.shell = QWidget() # InternalShell(namespace={'metms': QApplication.instance()}, # parent=self, # multithreaded=False) self.shellDock = QDockWidget("Python Shell", self) self.shellDock.setWindowIcon(QIcon(path.normcase("gui/icons/stop.png"))) self.shellDock.setWidget(self.shell) self.shellDock.setMinimumWidth(255) self.shellDock.visible = True self.addDockWidget(0x2, self.shellDock) self.addDockWidget(0x2, self.sampleDockWidget) self.tabifyDockWidget(self.shellDock, self.sampleDockWidget) self.pb = QProgressBar(self) self.pb.setMaximumWidth(245) self.stopProcess = QToolButton(self) self.stopProcess.setIcon(QIcon(path.normcase("gui/icons/process_stop.png"))) m = QMenu() # self.connect(m, SIGNAL('triggered(QAction*'), QApplication.instance().taskManager.abortByName) self.stopProcess.setMenu(m) self.stopProcess.setPopupMode(1) # Menu Button # self.connect(self.stopProcess, SIGNAL("clicked()"), self.stopThread) self.statusBar().addPermanentWidget(self.stopProcess) self.statusBar().addPermanentWidget(self.pb) def updateStopProcessMenu(self): """ update the menu of the stop process button, based directly on the processes stored by the task manager """ self.stopProcess.menu().clear() for c in QApplication.instance().taskManager: self.stopProcess.menu().addAction(c.title) # QApplication.instance().taskManager.abort(QApplication.instance().taskManager[-1]) def addMdiSubWindow(self, plot, title="", showMaximized=False): """ Allow addition of new window in the mdiarea """ win = self.mdiArea.addSubWindow(plot) # print "widget parent", plot.parent() win.setAttribute(Qt.WA_DeleteOnClose) # win.connect(win, SIGNAL('destroyed(QObject *)'), self.testdestroy) # plot.setParent(win) win.setWindowTitle(title) if showMaximized: win.showMaximized() else: win.resize(400, 300) win.show() return win def updateTreeView(self): """ Tree View update switch spectre/chromato """ if self.treeView.model() == self.spectraModel: self.treeView.setModel(self.chromaModel) self.tabWidget.setTabText(0, "Chroma") else: self.treeView.setModel(self.spectraModel) # self.treeView.setSelectionMode(1) self.tabWidget.setTabText(0, "Spectra") def addTreeViewModel(self, model1, model2): """Add a model """ self.chromaModel.appendRow(model1) self.spectraModel.appendRow(model2) def _actionHovered(self, action): """emulate tooltip cause they do not work that much""" tip = action.toolTip() QToolTip.showText(QCursor.pos(), tip) def showErrorMessage(self, title, string): QMessageBox.critical(self, title, string, 0, 0) def showWarningMessage(self, title, string): return QMessageBox.warning(self, title, string, QMessageBox.Ok | QMessageBox.Cancel) def showInformationMessage(self, title, string): QMessageBox.information(self, title, string, 0) def updateProgressBar(self, i): """update the value of the progress bar for all the treatment""" self.pb.setValue(min(i, 100)) def to_indetermined_mode(self): self.pb.setMaximum(0) def to_determined_mode(self): self.pb.setMaximum(100) def showInStatusBar(self, string, time=5000): self.statusBar().showMessage(string, time) def addInterpreterDock(self, shell): self.shellDock = QDockWidget(self) self.shellDock.setWidget(shell) self.shellDock.setWindowTitle("shell") self.addDockWidget(0x2, self.shellDock) def showMetMSInformation(self): QMessageBox.about( self, self.tr("About %1").arg("metMS"), self.tr( """<b>%1 %2</b> <br>metabolite Mass Spectrometry <p>Copyright © 2010 Marco INSA, INRA <br>Licensed under the terms of the CeciLL License <p>Developed and maintained by Marco <br>Bug reports and feature requests: <a href="http://github.com/jerkos/metms">metMS site</a><br> Discussions around the project: <a href="http://groups.google.com/group/spyderlib">Google Group</a> <p>This project is part of the BRIDGE project <p>Python %3, Qt %4, PyQt %5""" ) .arg("metMS") .arg(__version__) .arg(platform.python_version()) .arg(QT_VERSION_STR) .arg(PYQT_VERSION_STR), )
def lauchGUI(self, WorkSpace, aCase, sobjXML, Args): """ mw.dockWidgetBrowser is the Browser of the CFD MainView """ log.debug("lauchGUI") from cs_gui import process_cmd_line if CFD_Code() == CFD_Saturne: from cs_package import package from code_saturne.Base.MainView import MainView elif CFD_Code() == CFD_Neptune: from nc_package import package from neptune_cfd.core.MainView import MainView if sobjXML == None: Title = "unnamed" else: Title = sobjXML.GetName() self.Workspace = WorkSpace pkg = package() case, splash = process_cmd_line(Args) mw = MainView(pkg, case, aCase) # Put the standard panel of the MainView inside a QDockWidget # in the SALOME Desktop aTitle = self.setWindowTitle_CFD(mw, aCase, Title) dsk = sgPyQt.getDesktop() dock = QDockWidget(aTitle) dock.setWidget(mw.frame) dock.setMinimumWidth(520) dsk.addDockWidget(Qt.RightDockWidgetArea, dock) dock.setVisible(True) dock.show() # Put the QTreeView of the MainView which is already inside a QDockWidget # in the SALOME Desktop BrowserTitle = aTitle + " Browser" mw.dockWidgetBrowser.setWindowTitle(BrowserTitle) dsk.addDockWidget(Qt.LeftDockWidgetArea, mw.dockWidgetBrowser) mw.dockWidgetBrowser.setVisible(True) mw.dockWidgetBrowser.show() mw.dockWidgetBrowser.raise_() dock.raise_() #Add Dock windows are managed by CFDGUI_Management class studyId = sgPyQt.getStudyId() aStudyCFD = aCase.GetFather() aCaseCFD = aCase xmlFileName = str(Title) _c_CFDGUI.set_d_CfdCases(studyId, dock, mw.dockWidgetBrowser, mw, aStudyCFD, aCaseCFD, xmlFileName, sobjXML) self.connect(dock, SIGNAL("visibilityChanged(bool)"), self.setdockWindowBrowserActivated) self.connect(mw.dockWidgetBrowser, SIGNAL("visibilityChanged(bool)"),self.setdockWindowActivated) self.connect(dock.toggleViewAction(), SIGNAL("toggled(bool)"), self.setdockWB) self.connect(mw.dockWidgetBrowser.toggleViewAction(), SIGNAL("toggled(bool)"), self.setdock) _c_CFDGUI.tabifyDockWindows(dsk, studyId) self.showDockWindows(studyId, xmlFileName, aCaseCFD.GetName(), aStudyCFD.GetName()) updateObjectBrowser() return mw
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 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 Main(plugin.Plugin): " Main Class " def initialize(self, *args, **kwargs): " Init Main Class " ec = ExplorerContainer() super(Main, self).initialize(*args, **kwargs) self.editor_s = self.locator.get_service('editor') # directory auto completer self.completer = QCompleter(self) self.dirs = QDirModel(self) self.dirs.setFilter(QDir.AllEntries | QDir.NoDotAndDotDot) self.completer.setModel(self.dirs) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setCompletionMode(QCompleter.PopupCompletion) self.group0 = QGroupBox() self.group0.setTitle(' Source ') self.source = QComboBox() self.source.addItems(['Clipboard', 'Local File', 'Remote URL', 'Ninja']) self.source.currentIndexChanged.connect(self.on_source_changed) self.infile = QLineEdit(path.expanduser("~")) self.infile.setPlaceholderText(' /full/path/to/file.html ') self.infile.setCompleter(self.completer) self.open = QPushButton(QIcon.fromTheme("folder-open"), 'Open') self.open.setCursor(QCursor(Qt.PointingHandCursor)) self.open.clicked.connect(lambda: self.infile.setText(str( QFileDialog.getOpenFileName(self.dock, "Open a File to read from", path.expanduser("~"), ';;'.join(['{}(*.{})'.format(e.upper(), e) for e in ['css', 'html', 'js', 'txt', '*']]))))) self.inurl = QLineEdit('http://www.') self.inurl.setPlaceholderText('http://www.full/url/to/remote/file.html') self.output = QPlainTextEdit(SAMPLE_TEXT) vboxg0 = QVBoxLayout(self.group0) for each_widget in (self.source, self.infile, self.open, self.inurl, self.output, ): vboxg0.addWidget(each_widget) [a.hide() for a in iter((self.infile, self.open, self.inurl))] self.group1 = QGroupBox() self.group1.setTitle(' CSS3 ') self.group1.setCheckable(True) self.group1.setGraphicsEffect(QGraphicsBlurEffect(self)) self.group1.graphicsEffect().setEnabled(False) self.group1.toggled.connect(self.toggle_css_group) self.ckcss1 = QCheckBox('Remove unnecessary Comments') self.ckcss2 = QCheckBox('Remove unnecessary Whitespace characters') self.ckcss3 = QCheckBox('Remove unnecessary Semicolons') self.ckcss4 = QCheckBox('Remove unnecessary Empty rules') self.ckcss5 = QCheckBox('Condense and Convert Colors from RGB to HEX') self.ckcss6 = QCheckBox('Condense all Zero units') self.ckcss7 = QCheckBox('Condense Multidimensional Zero units') self.ckcss8 = QCheckBox('Condense Floating point numbers') self.ckcss9 = QCheckBox('Condense HEX Colors') self.ckcss10 = QCheckBox('Condense multiple adjacent Whitespace chars') self.ckcss11 = QCheckBox('Condense multiple adjacent semicolon chars') self.ckcss12 = QCheckBox('Wrap the lines of the to 80 character length') self.ckcss13 = QCheckBox('Condense Font Weight values') self.ckcss14 = QCheckBox('Condense the 17 Standard Named Colors values') self.ckcss15 = QCheckBox('Condense the 124 Extra Named Colors values') self.ckcss16 = QCheckBox('Condense all Percentages values when posible') self.ckcss17 = QCheckBox('Condense all Pixels values when posible') self.ckcss18 = QCheckBox('Remove unnecessary quotes from url()') self.ckcss19 = QCheckBox('Add standard Encoding Declaration if missing') vboxg1 = QVBoxLayout(self.group1) for each_widget in (self.ckcss1, self.ckcss2, self.ckcss3, self.ckcss4, self.ckcss5, self.ckcss6, self.ckcss7, self.ckcss8, self.ckcss9, self.ckcss10, self.ckcss11, self.ckcss12, self.ckcss13, self.ckcss14, self.ckcss15, self.ckcss16, self.ckcss17, self.ckcss18, self.ckcss19): vboxg1.addWidget(each_widget) each_widget.setToolTip(each_widget.text()) self.group2 = QGroupBox() self.group2.setTitle(' HTML5 ') self.group2.setCheckable(True) self.group2.setGraphicsEffect(QGraphicsBlurEffect(self)) self.group2.graphicsEffect().setEnabled(False) self.group2.toggled.connect(self.toggle_html_group) self.ckhtml0 = QCheckBox('Condense Style and Script HTML Tags') self.ckhtml1 = QCheckBox('Condense DOCTYPE to new HTML5 Tags') self.ckhtml2 = QCheckBox('Condense Href and Src to protocol agnostic') self.ckhtml4 = QCheckBox('Remove unnecessary Tags but keep HTML valid') self.help1 = QLabel('''<a href= "https://developers.google.com/speed/articles/optimizing-html"> <small><center>Help about Unneeded Unnecessary HTML tags ?</a>''') self.help1.setTextInteractionFlags(Qt.LinksAccessibleByMouse) self.help1.setOpenExternalLinks(True) vboxg2 = QVBoxLayout(self.group2) for each_widget in (self.ckhtml0, self.ckhtml1, self.ckhtml2, self.ckhtml4, self.help1, ): vboxg2.addWidget(each_widget) each_widget.setToolTip(each_widget.text()) self.group3 = QGroupBox() self.group3.setTitle(' Javascript ') self.ckjs0 = QCheckBox('Condense and Compress Javascript') self.ckjs1 = QCheckBox('Condense $(document).ready(function(){ });') vboxg2 = QVBoxLayout(self.group3) for each_widget in (self.ckjs0, self.ckjs1): vboxg2.addWidget(each_widget) each_widget.setToolTip(each_widget.text()) self.group4 = QGroupBox() self.group4.setTitle(' General ') self.chckbx1 = QCheckBox('Lower case ALL the text') self.chckbx2 = QCheckBox('Remove Spaces, Tabs, New Lines, Empty Lines') self.befor, self.after = QProgressBar(), QProgressBar() self.befor.setFormat("%v Chars") self.after.setFormat("%v Chars") vboxg4 = QVBoxLayout(self.group4) for each_widget in (self.chckbx1, self.chckbx2, QLabel('<b>Before:'), self.befor, QLabel('<b>After:'), self.after): vboxg4.addWidget(each_widget) each_widget.setToolTip(each_widget.text()) [a.setChecked(True) for a in iter((self.ckcss1, self.ckcss2, self.ckcss3, self.ckcss4, self.ckcss5, self.ckcss6, self.ckcss7, self.ckcss8, self.ckcss9, self.ckcss10, self.ckcss11, self.ckcss12, self.ckcss13, self.ckcss14, self.ckcss15, self.ckcss16, self.ckcss17, self.ckcss18, self.ckcss19, self.ckjs1, self.ckhtml0, self.ckhtml1, self.ckhtml2, self.ckhtml4, self.chckbx1, self.chckbx2))] self.button = QPushButton(QIcon.fromTheme("face-cool"), 'Process Text') self.button.setCursor(QCursor(Qt.PointingHandCursor)) self.button.setMinimumSize(100, 50) self.button.clicked.connect(self.run) def must_glow(widget_list): ' apply an glow effect to the widget ' for glow, each_widget in enumerate(widget_list): try: if each_widget.graphicsEffect() is None: glow = QGraphicsDropShadowEffect(self) glow.setOffset(0) glow.setBlurRadius(99) glow.setColor(QColor(99, 255, 255)) each_widget.setGraphicsEffect(glow) glow.setEnabled(True) except: pass must_glow((self.button, )) 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((QLabel('<b>HTML5/CSS3/JS Optimizer Compressor'), self.group0, self.group1, self.group2, self.group3, self.group4, self.button, )) self.scrollable = QScrollArea() self.scrollable.setWidgetResizable(True) self.scrollable.setWidget(tw) self.dock = QDockWidget() self.dock.setWindowTitle(__doc__) self.dock.setStyleSheet('QDockWidget::title{text-align: center;}') self.dock.setMinimumWidth(350) self.dock.setWidget(self.scrollable) ec.addTab(self.dock, "Web") QPushButton(QIcon.fromTheme("help-about"), 'About', self.dock ).clicked.connect(lambda: QMessageBox.information(self.dock, __doc__, HELPMSG)) def run(self): ' run the string replacing ' if self.source.currentText() == 'Local File': with open(path.abspath(str(self.infile.text()).strip()), 'r') as f: txt = f.read() elif self.source.currentText() == 'Remote URL': txt = urlopen(str(self.inurl.text()).strip()).read() elif self.source.currentText() == 'Clipboard': txt = str(self.output.toPlainText()) if str(self.output.toPlainText()) is not '' else str(QApplication.clipboard().text()) else: txt = self.editor_s.get_text() self.output.clear() self.befor.setMaximum(len(txt) + 10) self.after.setMaximum(len(txt) + 10) self.befor.setValue(len(txt)) txt = txt.lower() if self.chckbx1.isChecked() is True else txt txt = condense_style(txt) if self.ckhtml0.isChecked() is True else txt txt = condense_script(txt) if self.ckhtml0.isChecked() is True else txt txt = condense_doctype(txt) if self.ckhtml1.isChecked() is True else txt txt = condense_href_src(txt) if self.ckhtml2 is True else txt txt = clean_unneeded_tags(txt) if self.ckhtml4.isChecked() is True else txt txt = condense_doc_ready(txt) if self.ckjs1.isChecked() is True else txt txt = jsmin(txt) if self.ckjs0.isChecked() is True else txt txt = remove_comments(txt) if self.ckcss1.isChecked() is True else txt txt = condense_whitespace(txt) if self.ckcss10.isChecked() is True else txt txt = remove_empty_rules(txt) if self.ckcss4.isChecked() is True else txt txt = remove_unnecessary_whitespace(txt) if self.ckcss2.isChecked() is True else txt txt = remove_unnecessary_semicolons(txt) if self.ckcss3.isChecked() is True else txt txt = condense_zero_units(txt) if self.ckcss6.isChecked() is True else txt txt = condense_multidimensional_zeros(txt) if self.ckcss7.isChecked() is True else txt txt = condense_floating_points(txt) if self.ckcss8.isChecked() is True else txt txt = normalize_rgb_colors_to_hex(txt) if self.ckcss5.isChecked() is True else txt txt = condense_hex_colors(txt) if self.ckcss9.isChecked() is True else txt txt = wrap_css_lines(txt, 80) if self.ckcss12.isChecked() is True else txt txt = condense_semicolons(txt) if self.ckcss11.isChecked() is True else txt txt = condense_font_weight(txt) if self.ckcss13.isChecked() is True else txt txt = condense_std_named_colors(txt) if self.ckcss14.isChecked() is True else txt # txt = condense_xtra_named_colors(txt) if self.ckcss14.isChecked() is True else txt # FIXME txt = condense_percentage_values(txt) if self.ckcss16.isChecked() is True else txt txt = condense_pixel_values(txt) if self.ckcss17.isChecked() is True else txt txt = remove_url_quotes(txt) if self.ckcss18.isChecked() is True else txt txt = add_encoding(txt) if self.ckcss19.isChecked() is True else txt txt = " ".join(txt.strip().split()) if self.chckbx2.isChecked() is True else txt self.after.setValue(len(txt)) self.output.setPlainText(txt) self.output.show() self.output.setFocus() self.output.selectAll() def on_source_changed(self): ' do something when the desired source has changed ' if self.source.currentText() == 'Local File': self.open.show() self.infile.show() self.inurl.hide() self.output.hide() elif self.source.currentText() == 'Remote URL': self.inurl.show() self.open.hide() self.infile.hide() self.output.hide() elif self.source.currentText() == 'Clipboard': self.output.show() self.open.hide() self.infile.hide() self.inurl.hide() self.output.setText(QApplication.clipboard().text()) else: self.output.show() self.open.hide() self.infile.hide() self.inurl.hide() self.output.setText(self.editor_s.get_text()) def toggle_css_group(self): ' toggle on or off the css checkboxes ' if self.group1.isChecked() is True: [a.setChecked(True) for a in iter((self.ckcss1, self.ckcss2, self.ckcss3, self.ckcss4, self.ckcss5, self.ckcss6, self.ckcss7, self.ckcss8, self.ckcss9, self.ckcss10, self.ckcss11, self.ckcss12, self.ckcss13, self.ckcss14, self.ckcss15, self.ckcss16, self.ckcss17, self.ckcss18, self.ckcss19))] self.group1.graphicsEffect().setEnabled(False) else: [a.setChecked(False) for a in iter((self.ckcss1, self.ckcss2, self.ckcss3, self.ckcss4, self.ckcss5, self.ckcss6, self.ckcss7, self.ckcss8, self.ckcss9, self.ckcss10, self.ckcss11, self.ckcss12, self.ckcss13, self.ckcss14, self.ckcss15, self.ckcss16, self.ckcss17, self.ckcss18, self.ckcss19))] self.group1.graphicsEffect().setEnabled(True) def toggle_html_group(self): ' toggle on or off the css checkboxes ' if self.group2.isChecked() is True: [a.setChecked(True) for a in iter((self.ckhtml0, self.ckhtml1, self.ckhtml2, self.ckhtml4))] self.group2.graphicsEffect().setEnabled(False) else: [a.setChecked(False) for a in iter((self.ckhtml0, self.ckhtml1, self.ckhtml2, self.ckhtml4))] self.group2.graphicsEffect().setEnabled(True)
class MSMainWindow(QMainWindow): """Gui of the main window""" #MAX_RECENT_FILES = 10 #start putting links spyder numpy scipy et tutti quanti links=('http://numpy.scipy.org/', 'http://packages.python.org/spyder/', 'http://www.riverbankcomputing.co.uk/software/pyqt/intro') pluginPath=path.normcase('pluginmanager/plugins/') def __init__(self, availablePlugins): """ Constructor with all the models needed setup menus """ QMainWindow.__init__(self) self.setDockOptions(QMainWindow.VerticalTabs | QMainWindow.AnimatedDocks) self.plugins = availablePlugins self.pluginsInst=[] settings=QSettings('INRA/INSA', '-'.join([QApplication.instance().APPLICATION_NAME_STR, QApplication.instance().VERSION_STR])) self.recentFiles = list(settings.value("RecentFiles").toStringList()) self.setStyleSheet(stylesheet) self.pipeline = MSPipelineToolBar("Pipeline toolbar", parent=self) self.addToolBar(0x1,self.pipeline) self._setupModels() self._setupUi() self._setupMenus() def _setupModels(self): """ Warning:Causes segfault when horizontal labels set to True on aura peu etre a la fin un model par sampleList c'est ce qui parait le plus logique """ #drag and drop table sample self.sampleModel = QStandardItemModel(self) self.sampleModel.setHorizontalHeaderLabels(["Sample", "Class"]) #treeView1 self.spectraModel = QStandardItemModel(self) #treeview2 self.peakModel = QStandardItemModel(self) #treeview3 self.clusterModel = QStandardItemModel(self) def _setupMenus(self): #file self.fileMenu = QMenu('&File') self.fileMenu.setTearOffEnabled(True) self.op=QMenu("&Open...",self.fileMenu) self.op.setIcon(QIcon(path.normcase("gui/icons/fileopen.png"))) open_=QAction("&Open rawfiles", self) open_.setToolTip("Open an mzXML or netCDF file") open_.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_O)) open_icon=QIcon(path.normcase("gui/icons/fileopen.png")) open_.setIcon(open_icon) self.op.addAction(open_) load_=QAction("&Open projects...", self) load_.setToolTip("load binary file containing saved objects") load_.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_S)) load_icon=QIcon(QPixmap(path.normcase("gui/icons/project_open.png"))) load_.setIcon(load_icon) self.op.addAction(load_) self.fileMenu.addMenu(self.op) save_=QAction("&Save...", self) save_.setToolTip("save the actual application model") save_.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_S)) save_icon=QIcon(path.normcase("gui/icons/save_all.png")) save_.setIcon(save_icon) self.fileMenu.addAction(save_) pkl = QAction("&load a peaklist", self) #TODO:load peaklist pkl.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_P)) pkl.setToolTip("load a peaklist and process it") pkl.setIcon(QIcon(path.normcase("gui/icons/featuredetect.png"))) self.fileMenu.addAction(pkl) convert_=QAction("&Convert...", self) convert_.setEnabled(False) convert_.setToolTip("Convert a .wiff file if Analyst(c) is installed") convert_icon=QIcon(path.normcase("gui/icons/goto.png")) convert_.setIcon(convert_icon) self.fileMenu.addAction(convert_) a = self.fileMenu.addAction(QIcon(path.normcase("gui/icons/process.png")), "&Launch a batch") a.setEnabled(False) b = self.fileMenu.addAction(QIcon(path.normcase("gui/icons/process.png")), "&Merge") b.setToolTip("Merge MRM file") #b.setEnabled(False) self.fileMenu.addSeparator() # # for i in xrange(self.MAX_RECENT_FILES): # a = QAction('', self) # a.setVisible(False) # self.fileMenu.addAction(a) # # for i in xrange(min(self.MAX_RECENT_FILES, len(self.recentFiles))): # self.fileMenu.actions()[5+i].setVisible(True) # self.fileMenu.actions()[5+i].setText(self.recentFiles[i].split('/')[-1]) exit_action =QAction("&Exit", self) exit_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_Q)) exit_action.setIcon(QIcon(QPixmap(path.normcase('gui/icons/exit.png')))) self.fileMenu.addAction(exit_action) self.menuBar().addMenu(self.fileMenu) self.editMenu=QMenu("&Edit") self.editMenu.setTearOffEnabled(True) self.editMenu.addAction(QIcon(path.normcase('gui/icons/edit_undo.png')), '&Undo...') self.editMenu.addAction(QIcon(path.normcase('gui/icons/edit_redo.png')), '&Redo...') self.editMenu.actions()[0].setEnabled(False) self.editMenu.actions()[1].setEnabled(False) self.editMenu.addSeparator() self.editMenu.addAction(QIcon(path.normcase('gui/icons/run.png')), '&Preferences') self.exportMenu = QMenu("&Export...") self.exportMenu.setIcon(QIcon(path.normcase('gui/icons/file_export.png'))) self.exportMenu.addAction("&Peaklist") self.exportMenu.addAction("&Clusters intensity matrix") self.editMenu.addMenu(self.exportMenu) self.menuBar().addMenu(self.editMenu) #view self.viewMenu =QMenu("&View") self.viewMenu.setTearOffEnabled(True) self.viewMenu.addAction(QIcon(path.normcase('gui/icons/window_duplicate')), "&Cascade View", self.mdiArea.cascadeSubWindows, QKeySequence(Qt.CTRL + Qt.Key_K)) self.viewMenu.addAction(QIcon(path.normcase('gui/icons/view_icon')), "&Title View", self.mdiArea.tileSubWindows, QKeySequence(Qt.CTRL + Qt.Key_N)) self.viewMenu.addAction(QIcon(path.normcase("gui/icons/stop_process.png")), "&Close all subWindows", self.mdiArea.closeAllSubWindows, QKeySequence(Qt.CTRL+Qt.Key_W)) self.plotting =QMenu("&Plotting...") self.plotting.setIcon(QIcon(QPixmap(path.normcase("gui/icons/plot.png")))) self.plotting.addAction("&3D Plot") #self.plotting.addAction("&Cytoscape web") self.plotting.addAction("&Spectrogram Plot") #self.multiplePlot = QAction("&Visualize Raw/Treated Data", self) #self.multiplePlot.setCheckable(True) #self.multiplePlot.setEnabled(False) #self.sub_plot_.addAction(self.multiplePlot) self.viewMenu.addMenu(self.plotting) self.viewMenu.addSeparator() self.show_hide=QMenu("&Show/Hide") m=self.createPopupMenu() m.setTitle("&Show/Hide") self.viewMenu.addMenu(m) #self.pref = QMenu("&Preferences") #self.pref.addAction(self.multiplePlot) #self.viewMenu.addMenu(self.pref) self.menuBar().addMenu(self.viewMenu) #algorithm self.algoMenu= QMenu("&Algorithm") self.algoMenu.setTearOffEnabled(True) self.preProcessing=QMenu("&PreProcessing(experimental)") self.preProcessing.addAction("&Smoothing raw data...") self.preProcessing.addAction("&Cut off raw data...") self.preProcessing.addAction('&Calibration (mz dimension)') self.preProcessing.addAction("&Resize sample...") self.algoMenu.addMenu(self.preProcessing) self.peakPickingMenu = QMenu("&Peack Picking & Alignement(XCMS)", self) self.peakPickingMenu.setIcon(QIcon(path.normcase("gui/icons/pickedpeakicon.png"))) matched = QAction("&MatchedFiltered", self) matched.setIcon(QIcon(path.normcase('gui/icons/RLogo'))) matched.setToolTip("Peak Detection and Integration using MatchedFiltered algorithm") self.peakPickingMenu.addAction(matched) centwave=QAction("&CentWave", self) centwave.setIcon(QIcon(path.normcase('gui/icons/RLogo'))) centwave.setToolTip("Peak Detection and Integration using CentWave algorithm") self.peakPickingMenu.addAction(centwave) #peak_.setShortcut(.QKeySequence(CTRL + Key_P)) # peak_icon=.QIcon(.QPixmap(path.normcase("gui/icons/pickedpeakicon.png"))) #peak_.setIcon(peak_icon) self.algoMenu.addMenu(self.peakPickingMenu) self.alignment = QMenu("&Alignment") self.alignment.setIcon(QIcon(path.normcase('gui/icons/format_indent_more.png'))) self.alignment.addAction("&Polynomial fitting(exp)") self.alignment.addAction("&DynamicTimeWarping") self.alignment.addAction("&ObiWarp") self.alignment.actions()[2].setEnabled(False) self.algoMenu.addMenu(self.alignment) self.algoMenu.addAction("Normalization") clust_ = QAction("&Clustering", self) clust_.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_L)) clust_icon=QIcon(QPixmap(path.normcase("gui/icons/cluster.png"))) clust_.setIcon(clust_icon) self.algoMenu.addAction(clust_) id_ = QAction("&Identification", self) id_.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_I)) id_.setToolTip("Try to identify peaks with several methods") id_.setIcon(QIcon(QPixmap(path.normcase("gui/icons/findcompound.png")))) self.algoMenu.addAction(id_) self.menuBar().addMenu(self.algoMenu) #tools self.toolsMenu =QMenu("&Tools") self.toolsMenu.setTearOffEnabled(True) web = QAction("&Web Browser", self) web.setIcon(QIcon(QPixmap(path.normcase("gui/icons/applications_internet.png")))) self.toolsMenu.addAction(web) #cyto = QAction("&cytoscape", self) #cyto_icon =QIcon(QPixmap(path.normcase("gui/icons/cytoscape.jpeg"))) #cyto.setIcon(cyto_icon) #self.toolsMenu.addAction(cyto) editor = QAction("&Editor", self) editor.setIcon(QIcon(QPixmap(path.normcase("gui/icons/document_sign.png")))) self.toolsMenu.addAction(editor) pet=QAction("&Short Periodic Table", self) pet.setIcon(QIcon(QPixmap(path.normcase("gui/icons/pet.jpg")))) self.toolsMenu.addAction(pet) self.menuBar().addMenu(self.toolsMenu) #plugins self.pluginMenu = QMenu('&Plugins') self.pluginMenu.setTearOffEnabled(True) instPl= QAction("&Install a plugin", self) instPl.setIcon(QIcon(path.normcase('gui/icons/pluginInstall.png'))) self.pluginMenu.addAction(instPl) self.launchingMenu = QMenu("&Launch PLugins", self) self.launchingMenu.setIcon(QIcon(path.normcase('gui/icons/plugin'))) for plug in self.plugins: #fullname="".join([self.pluginPath, str(plug)]) mod=imp.load_source(self.__module__, plug) if mod.autoActivation: qApp=QApplication.instance() name=getattr(mod, 'className') cls=getattr(mod, name) p=cls(qApp.model, self, parent=self) #p=qApp.pluginManager.loadPlugin(qApp.model, self, plug.split('/')[-1]) self.pluginsInst.append(p) else: self.launchingMenu.addAction(plug.split('/')[-1]) self.pluginMenu.addMenu(self.launchingMenu) self.pluginMenu.addAction(QIcon(path.normcase("gui/icons/process_stop.png")), "&Remove loaded Plugin") self.menuBar().addMenu(self.pluginMenu) #about self.aboutMenu= QMenu("&About") self.aboutMenu.setTearOffEnabled(True) metms = QAction(QIcon(path.normcase('gui/icons/deluge.png')), "&about metMS...", self) self.aboutMenu.addAction(metms) pyqt = QAction("&about PyQt4...", self) pyqt_icon =QIcon(QPixmap(path.normcase("gui/icons/logo_QT4.png"))) pyqt.setIcon(pyqt_icon) self.aboutMenu.addAction(pyqt) metms = QAction("&metMS Documentation", self) metms_icon =QIcon(QPixmap(path.normcase("gui/icons/deluge.png"))) metms.setIcon(metms_icon) self.aboutMenu.addAction(metms) self.menuBar().addMenu(self.aboutMenu) def _setupUi(self, background=None): """ Make the GUI """ #mdi self.mdiArea = MSMdiArea(self) self.mdiArea.setBackground(QBrush(QPixmap(path.normcase('gui/icons/blac2.png'))))#QColor(Qt.blue).darker())) self.setCentralWidget(self.mdiArea) #sample dock widget self.sampleDockWidget = QDockWidget("Samples", self) #sampleWidget = QWidget() self.sampleTableView = MSDragFromTableView() self.sampleTableView.setModel(self.sampleModel) self.sampleTableView.setSelectionBehavior(1) self.sampleTableView.verticalHeader().hide() self.sampleTableView.verticalHeader().setDefaultSectionSize(15) self.sampleTableView.horizontalHeader().setDefaultSectionSize(150) self.sampleDockWidget.setWidget(self.sampleTableView)#sampleWidget) self.sampleDockWidget.visible=True #workflow dock self.workflowDockWidget = QDockWidget("Visualizer", self) self.workflowDockWidget.visible = True a=QWidget(self) v=QVBoxLayout(a) q=QToolBar() #self.workingSample = QLabel("Working Sample:None") #q.addWidget(self.workingSample) q.addWidget(QLabel("ppm :")) self.ppmEditer=QDoubleSpinBox() self.usePpm=QCheckBox("use ?") q.addWidget(self.ppmEditer) q.addWidget(self.usePpm) q.addSeparator() self.removeButton=QToolButton(self) self.removeButton.setIcon(QIcon(path.normcase("gui/icons/delete.png"))) q.addWidget(self.removeButton) self.markAsGood=QAction(QIcon(path.normcase("gui/icons/button_ok.png")),"mark peak as good", self) self.markAsBad=QAction(QIcon(path.normcase("gui/icons/stop.png")), "mark peak as bad", self) self.hideItem = QAction(QIcon(path.normcase("gui/icons/list_remove.png")), "Hide Item", self) q.addAction(self.markAsGood) q.addAction(self.markAsBad) q.addAction(self.hideItem) v.addWidget(q) self.tabWidget = QTabWidget() self.tab = QWidget() verticalLayout = QVBoxLayout(self.tab) self.treeView = MSToDropTableView() self.treeView.verticalHeader().setDefaultSectionSize(20) self.treeView.setModel(self.spectraModel) self.spectraLabel = QLabel("Sample: None") verticalLayout.addWidget(self.treeView) verticalLayout.addWidget(self.spectraLabel) self.tabWidget.addTab(self.tab, QIcon(path.normcase("gui/icons/spectrumicon.png")),"Spectra") self.tab_2 = QWidget() verticalLayout_4 = QVBoxLayout(self.tab_2) self.treeView_2 = MSToDropTableView()#MSTreeView(self.tab_2)# QTableView(self)# self.treeView_2.verticalHeader().setDefaultSectionSize(20) self.treeView_2.setModel(self.peakModel) self.peakLabel = QLabel("Sample: None") verticalLayout_4.addWidget(self.treeView_2) verticalLayout_4.addWidget(self.peakLabel) self.tabWidget.addTab(self.tab_2,QIcon(path.normcase("gui/icons/peakicon.png")), "Peaks List") self.tab_3 = QWidget() verticalLayout_5 = QVBoxLayout(self.tab_3) self.treeView_3 = MSToDropTreeView() self.treeView_3.setAnimated(True) self.treeView_3.setModel(self.clusterModel) self.clusterLabel = QLabel("Sample: None") verticalLayout_5.addWidget(self.treeView_3) verticalLayout_5.addWidget(self.clusterLabel) self.tabWidget.addTab(self.tab_3, QIcon(path.normcase("gui/icons/clustering.png")), "Clusters") self.tabWidget.setCurrentIndex(0) for l in (self.spectraLabel, self.peakLabel, self.clusterLabel): l.setAutoFillBackground(True) v.addWidget(self.tabWidget) self.workflowDockWidget.setWidget(a) self.addDockWidget(Qt.DockWidgetArea(0x2),self.workflowDockWidget) from gui.MetBaseGui import MSIsoCalculator self.isoCalc = MSIsoCalculator(self) self.isoCalcDockWidget=QDockWidget('isotopes calculation', self) self.isoCalcDockWidget.setWidget(self.isoCalc) self.addDockWidget(Qt.DockWidgetArea(0x2), self.isoCalcDockWidget) self.isoCalcDockWidget.setVisible(False) self.isoCalcDockWidget.visible=False from gui.MetBaseGui import FormulaGenerator self.generator=FormulaGenerator(self) self.generatorDockWidget=QDockWidget('formula generator', self) self.generatorDockWidget.setWidget(self.generator) self.addDockWidget(Qt.DockWidgetArea(0x2), self.generatorDockWidget) self.generatorDockWidget.setVisible(False) self.generatorDockWidget.visible=False self.compoundTreeView = MSCompoundTreeView(self) self.compoundDockWidget = QDockWidget("Compounds", self) self.compoundDockWidget.setWidget(self.compoundTreeView) self.addDockWidget(Qt.DockWidgetArea(0x2),self.compoundDockWidget) self.compoundDockWidget.setVisible(False) self.compoundDockWidget.visible=False self.comparativeTableView = QTableView(self) self.comparativeTableView.horizontalHeader().setStretchLastSection(True) self.comparativeTableView.verticalHeader().setDefaultSectionSize(20) self.comparativeDock = QDockWidget("Comparative View", self) self.comparativeDock.setWidget(self.comparativeTableView) self.addDockWidget(Qt.DockWidgetArea(0x8), self.comparativeDock) self.comparativeDock.setVisible(False) self.comparativeDock.visible = False self.tabifyDockWidget(self.compoundDockWidget, self.isoCalcDockWidget) self.tabifyDockWidget(self.isoCalcDockWidget, self.workflowDockWidget ) self.tabifyDockWidget(self.workflowDockWidget, self.generatorDockWidget) #set the end #WARNING: possible that the internal shell widget cause random segfault #with the error of QObject::killTimers...? not sure ! self.shell = QWidget()#InternalShell(namespace={'metms': QApplication.instance()}, # parent=self, # multithreaded=False) self.shellDock = QDockWidget("Python Shell", self) self.shellDock.setWindowIcon(QIcon(path.normcase('gui/icons/stop.png'))) self.shellDock.setWidget(self.shell) self.shellDock.setMinimumWidth(255) self.shellDock.visible=True self.addDockWidget(0x2, self.shellDock) self.addDockWidget(0x2, self.sampleDockWidget) self.tabifyDockWidget(self.shellDock, self.sampleDockWidget) self.pb = QProgressBar(self) self.pb.setMaximumWidth(245) self.stopProcess = QToolButton(self) self.stopProcess.setIcon(QIcon(path.normcase("gui/icons/process_stop.png"))) m = QMenu() #self.connect(m, SIGNAL('triggered(QAction*'), QApplication.instance().taskManager.abortByName) self.stopProcess.setMenu(m) self.stopProcess.setPopupMode(1) #Menu Button #self.connect(self.stopProcess, SIGNAL("clicked()"), self.stopThread) self.statusBar().addPermanentWidget(self.stopProcess) self.statusBar().addPermanentWidget(self.pb) def updateStopProcessMenu(self): """ update the menu of the stop process button, based directly on the processes stored by the task manager """ self.stopProcess.menu().clear() for c in QApplication.instance().taskManager: self.stopProcess.menu().addAction(c.title) #QApplication.instance().taskManager.abort(QApplication.instance().taskManager[-1]) def addMdiSubWindow(self, plot, title="", showMaximized=False): """ Allow addition of new window in the mdiarea """ win=self.mdiArea.addSubWindow(plot) #print "widget parent", plot.parent() win.setAttribute(Qt.WA_DeleteOnClose) #win.connect(win, SIGNAL('destroyed(QObject *)'), self.testdestroy) #plot.setParent(win) win.setWindowTitle(title) if showMaximized: win.showMaximized() else: win.resize(400, 300) win.show() return win def updateTreeView(self): """ Tree View update switch spectre/chromato """ if self.treeView.model() == self.spectraModel: self.treeView.setModel(self.chromaModel) self.tabWidget.setTabText(0, "Chroma") else: self.treeView.setModel(self.spectraModel) #self.treeView.setSelectionMode(1) self.tabWidget.setTabText(0, "Spectra") def addTreeViewModel (self,model1, model2): """Add a model """ self.chromaModel.appendRow(model1) self.spectraModel.appendRow(model2) def _actionHovered(self, action): """emulate tooltip cause they do not work that much""" tip = action.toolTip() QToolTip.showText(QCursor.pos(), tip) def showErrorMessage(self, title, string): QMessageBox.critical(self, title, string, 0, 0) def showWarningMessage(self, title, string): return QMessageBox.warning(self, title, string, QMessageBox.Ok|QMessageBox.Cancel) def showInformationMessage(self, title, string): QMessageBox.information(self, title, string, 0) def updateProgressBar(self, i): """update the value of the progress bar for all the treatment""" self.pb.setValue(min(i, 100)) def to_indetermined_mode(self): self.pb.setMaximum(0) def to_determined_mode(self): self.pb.setMaximum(100) def showInStatusBar(self, string, time=5000): self.statusBar().showMessage(string, time) def addInterpreterDock(self, shell): self.shellDock = QDockWidget(self) self.shellDock.setWidget(shell) self.shellDock.setWindowTitle("shell") self.addDockWidget(0x2, self.shellDock) def showMetMSInformation(self): QMessageBox.about(self, self.tr("About %1").arg("metMS"), self.tr("""<b>%1 %2</b> <br>metabolite Mass Spectrometry <p>Copyright © 2010 Marco INSA, INRA <br>Licensed under the terms of the CeciLL License <p>Developed and maintained by Marco <br>Bug reports and feature requests: <a href="http://github.com/jerkos/metms">metMS site</a><br> Discussions around the project: <a href="http://groups.google.com/group/spyderlib">Google Group</a> <p>This project is part of the BRIDGE project <p>Python %3, Qt %4, PyQt %5""") \ .arg("metMS").arg(__version__) \ .arg(platform.python_version()).arg(QT_VERSION_STR) \ .arg(PYQT_VERSION_STR))
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)
def lauchGUI(self, WorkSpace, aCase, sobjXML, Args): """ mw.dockWidgetBrowser is the Browser of the CFD MainView """ log.debug("lauchGUI") from cs_gui import process_cmd_line if CFD_Code() == CFD_Saturne: from cs_package import package from code_saturne.Base.MainView import MainView elif CFD_Code() == CFD_Neptune: from nc_package import package from neptune_cfd.core.MainView import MainView if sobjXML == None: Title = "unnamed" else: Title = sobjXML.GetName() self.Workspace = WorkSpace pkg = package() case, splash = process_cmd_line(Args) mw = MainView(pkg, case, aCase) # Put the standard panel of the MainView inside a QDockWidget # in the SALOME Desktop aTitle = self.setWindowTitle_CFD(mw, aCase, Title) dsk = sgPyQt.getDesktop() dock = QDockWidget(aTitle) dock.setWidget(mw.frame) dock.setMinimumWidth(520) dsk.addDockWidget(Qt.RightDockWidgetArea, dock) dock.setVisible(True) dock.show() # Put the QTreeView of the MainView which is already inside a QDockWidget # in the SALOME Desktop BrowserTitle = aTitle + " Browser" mw.dockWidgetBrowser.setWindowTitle(BrowserTitle) dsk.addDockWidget(Qt.LeftDockWidgetArea, mw.dockWidgetBrowser) mw.dockWidgetBrowser.setVisible(True) mw.dockWidgetBrowser.show() mw.dockWidgetBrowser.raise_() dock.raise_() #Add Dock windows are managed by CFDGUI_Management class studyId = sgPyQt.getStudyId() aStudyCFD = aCase.GetFather() aCaseCFD = aCase xmlFileName = str(Title) _c_CFDGUI.set_d_CfdCases(studyId, dock, mw.dockWidgetBrowser, mw, aStudyCFD, aCaseCFD, xmlFileName, sobjXML) self.connect(dock, SIGNAL("visibilityChanged(bool)"), self.setdockWindowBrowserActivated) self.connect(mw.dockWidgetBrowser, SIGNAL("visibilityChanged(bool)"), self.setdockWindowActivated) self.connect(dock.toggleViewAction(), SIGNAL("toggled(bool)"), self.setdockWB) self.connect(mw.dockWidgetBrowser.toggleViewAction(), SIGNAL("toggled(bool)"), self.setdock) _c_CFDGUI.tabifyDockWindows(dsk, studyId) self.showDockWindows(studyId, xmlFileName, aCaseCFD.GetName(), aStudyCFD.GetName()) updateObjectBrowser() return mw
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)