Exemple #1
0
 def _createDock(self):
     dock = QDockWidget("Workflow")
     dock.setObjectName("ERTGUI Workflow")
     dock.setWidget(self.contentsWidget)
     dock.setFeatures(QDockWidget.DockWidgetClosable)
     dock.setAllowedAreas(Qt.LeftDockWidgetArea)
     return dock
Exemple #2
0
    def create_dockwidget(self):
        """Add to parent QMainWindow as a dock widget"""

        # This is not clear yet why the following do not work...
        # (see Issue #880)
        ##         # Using Qt.Window window flags solves Issue #880 (detached dockwidgets
        ##         # are not painted after restarting Spyder and restoring their hexstate)
        ##         # but it does not work with PyQt <=v4.7 (dockwidgets can't be docked)
        ##         # or non-Windows platforms (lot of warnings are printed out)
        ##         # (so in those cases, we use the default window flags: Qt.Widget):
        ##         flags = Qt.Widget if is_old_pyqt or os.name != 'nt' else Qt.Window
        dock = QDockWidget(self.get_plugin_title(), self.main)  #, flags)

        dock.setObjectName(self.__class__.__name__ + "_dw")
        dock.setAllowedAreas(self.ALLOWED_AREAS)
        dock.setFeatures(self.FEATURES)
        dock.setWidget(self)
        self.update_margins()
        self.connect(dock, SIGNAL('visibilityChanged(bool)'),
                     self.visibility_changed)
        self.dockwidget = dock
        short = self.get_option("shortcut", None)
        if short is not None:
            shortcut = QShortcut(QKeySequence(short), self.main,
                                 self.switch_to_plugin)
            self.register_shortcut(shortcut,
                                   "_",
                                   "Switch to %s" % self.CONF_SECTION,
                                   default=short)
        return (dock, self.LOCATION)
Exemple #3
0
class Main(plugin.Plugin):
    " dock Class "
    def initialize(self):
        " Init Class dock "
        self.dock = QDockWidget()
        self.dock.setFeatures(QDockWidget.DockWidgetFloatable |
                              QDockWidget.DockWidgetMovable)
        self.dock.setWindowTitle(__doc__)
        self.dock.setStyleSheet('QDockWidget::title{text-align: center;}')
        self.boton = QPushButton(QIcon.fromTheme("media-eject"),
                                 ' Open Media ', self.dock)
        try:
            self.factory = KPluginLoader("dragonpart").factory()
            self.part = self.factory.create(self)
            self.boton.clicked.connect(lambda: self.part.openUrl(KUrl(str(
                QFileDialog.getOpenFileName(self.dock, ' Open Media File ',
                path.expanduser("~"), ';;'.join(['(*.{})'.format(e) for e in
                ['ogv', 'webm', 'avi', 'mpg', 'mpeg', '3gp', 'wmv', 'mp3',
                'asf', 'dat', 'flv', 'flac', 'ogg', 'mkv', 'mov', 'swf', 'wav',
                'rm', 'm4v', 'aaf', 'mp4', 'raw', '*']]))))))
            self.dock.setWidget(self.part.widget())
        except:
            self.dock.setWidget(QLabel(""" <center> <h3>ಠ_ಠ<br>
            ERROR: Please, install Dragon Player and PyKDE ! </h3><br>
            <br><i> (Sorry, cant embed non-Qt Apps). </i>><center>"""))
        self.misc = self.locator.get_service('misc')
        self.misc.add_widget(self.dock,
                            QIcon.fromTheme("applications-multimedia"), __doc__)
Exemple #4
0
 def _createDock(self):
     dock = QDockWidget("")
     dock.setObjectName("ERTGUI Workflow")
     dock.setWidget(self.contentsWidget)
     dock.setFeatures(QDockWidget.DockWidgetClosable)
     dock.setAllowedAreas(Qt.LeftDockWidgetArea)
     return dock
Exemple #5
0
    def create_dockwidget(self):
        """Add to parent QMainWindow as a dock widget"""

        # This is not clear yet why the following do not work...
        # (see Issue #880)
##         # Using Qt.Window window flags solves Issue #880 (detached dockwidgets
##         # are not painted after restarting Spyder and restoring their hexstate)
##         # but it does not work with PyQt <=v4.7 (dockwidgets can't be docked)
##         # or non-Windows platforms (lot of warnings are printed out)
##         # (so in those cases, we use the default window flags: Qt.Widget):
##         flags = Qt.Widget if is_old_pyqt or os.name != 'nt' else Qt.Window
        dock = QDockWidget(self.get_plugin_title(), self.main)#, flags)

        dock.setObjectName(self.__class__.__name__+"_dw")
        dock.setAllowedAreas(self.ALLOWED_AREAS)
        dock.setFeatures(self.FEATURES)
        dock.setWidget(self)
        self.update_margins()
        self.connect(dock, SIGNAL('visibilityChanged(bool)'),
                     self.visibility_changed)
        self.dockwidget = dock
        short = self.get_option("shortcut", None)
        if short is not None:
            shortcut = QShortcut(QKeySequence(short),
                                 self.main, self.switch_to_plugin)
            self.register_shortcut(shortcut, "_",
                                   "Switch to %s" % self.CONF_SECTION,
                                   default=short)
        return (dock, self.LOCATION)
Exemple #6
0
class Main(plugin.Plugin):
    " dock Class "
    def initialize(self):
        " Init Class dock "
        self.dock = QDockWidget()
        self.dock.setFeatures(QDockWidget.DockWidgetFloatable |
                              QDockWidget.DockWidgetMovable)
        self.dock.setWindowTitle(__doc__)
        self.dock.setStyleSheet('QDockWidget::title{text-align: center;}')
        self.boton = QPushButton(QIcon.fromTheme("document-open-recent"),
                                 'Edit Track', self.dock)
        self.boton.setToolTip('Edit iCal: {}'.format(TRACK_FILE))
        try:
            self.factory = KPluginLoader("ktimetrackerpart").factory()
            self.part = self.factory.create(self)
            self.part.setReadWrite(True)
            self.part.closeUrl()
            self.part.openUrl(KUrl(str(TRACK_FILE)))
            self.boton.clicked.connect(lambda:
                            call('xdg-open {}'.format(TRACK_FILE), shell=True))
            self.dock.setWidget(self.part.widget())
        except:
            self.dock.setWidget(QLabel(""" <center> <h3>ಠ_ಠ<br>
            ERROR: Please, install kTimeTracker and PyKDE ! </h3><br>
            <br><i> (Sorry, cant embed non-Qt Apps). </i><center>"""))
        self.misc = self.locator.get_service('misc')
        self.misc.add_widget(self.dock, QIcon.fromTheme("user-away"), __doc__)
Exemple #7
0
 def setupUI(self):
     
     self.browser = browser = SimpleServerBrowser()
     self.player = player = video.Player() #VideoPlayer()        
     
     dock = QDockWidget(u'Przeglądarka serwerów', self)
     dock.setContentsMargins(0, 0, 0, 0)
     
     dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
     dock.setWidget(browser)        
     
     self.addDockWidget(Qt.LeftDockWidgetArea, dock)        
             
     #self.setCentralWidget(player.videoWidget())
     self.setCentralWidget(player.videoWidget)
     
     dock = QDockWidget(self)
     dock.setAllowedAreas(Qt.BottomDockWidgetArea)
     dock.setWidget(player.controlPanel)
     
     dock.setFloating(False)
     dock.setFeatures(QDockWidget.NoDockWidgetFeatures)
     self.addDockWidget(Qt.BottomDockWidgetArea, dock)
 
     QWidget.connect(self.browser, SIGNAL("play"), self.play)
     self.resize(800, 480)
Exemple #8
0
    def addDock(self, name, widget, area=Qt.LeftDockWidgetArea, allowed_areas=Qt.AllDockWidgetAreas):
        dock_widget = QDockWidget(name)
        dock_widget.setObjectName("%sDock" % name)
        dock_widget.setWidget(widget)
        dock_widget.setAllowedAreas(allowed_areas)
        dock_widget.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable)

        self.addDockWidget(area, dock_widget)
        return dock_widget
Exemple #9
0
    def addDock(self, name, widget, area=Qt.LeftDockWidgetArea, allowed_areas=Qt.AllDockWidgetAreas):
        dock_widget = QDockWidget(name)
        dock_widget.setObjectName("%sDock" % name)
        dock_widget.setWidget(widget)
        dock_widget.setAllowedAreas(allowed_areas)
        dock_widget.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable)

        self.addDockWidget(area, dock_widget)
        return dock_widget
Exemple #10
0
 def createDock(self, widgetClass, widgetName, widgetArea):
     """创建停靠组件"""
     widget = widgetClass(self.mainEngine, self.eventEngine)
     dock = QDockWidget(widgetName)
     dock.setWidget(widget)
     dock.setObjectName(widgetName)
     dock.setFeatures(dock.DockWidgetFloatable | dock.DockWidgetMovable)
     self.addDockWidget(widgetArea, dock)
     return widget, dock
Exemple #11
0
 def __createDockWindow(self, name):
     """
     Private method to create a dock window with common properties.
     
     @param name object name of the new dock window (string)
     @return the generated dock window (QDockWindow)
     """
     dock = QDockWidget()
     dock.setObjectName(name)
     dock.setFeatures(
         QDockWidget.DockWidgetFeatures(QDockWidget.AllDockWidgetFeatures))
     return dock
Exemple #12
0
 def __createDockWindow(self, name):
     """
     Private method to create a dock window with common properties.
     
     @param name object name of the new dock window (string)
     @return the generated dock window (QDockWindow)
     """
     dock = QDockWidget()
     dock.setObjectName(name)
     dock.setFeatures(
         QDockWidget.DockWidgetFeatures(QDockWidget.AllDockWidgetFeatures))
     return dock
Exemple #13
0
class Main(plugin.Plugin):
    " dock Class "
    def initialize(self):
        " Init Class dock "
        self.dock = QDockWidget()
        self.dock.setFeatures(QDockWidget.DockWidgetFloatable |
                              QDockWidget.DockWidgetMovable)
        self.dock.setWindowTitle(__doc__)
        self.dock.setStyleSheet('QDockWidget::title{text-align: center;}')
        self.open = QAction(QIcon.fromTheme("document-open"), 'Open DIFF', self)
        self.diff = QAction(QIcon.fromTheme("document-new"), 'Make DIFF', self)
        self.diff.triggered.connect(self.run_gui_and_get_results)
        self.save = QAction(QIcon.fromTheme("document-save"), 'Save DIFF', self)
        self.save.triggered.connect(self.save_a_diff)
        self.patc = QAction(QIcon.fromTheme("document-edit"), 'PATCH it!', self)
        self.patc.triggered.connect(lambda: QMessageBox.information(self.dock,
            __doc__, ' Sorry. This Feature is not ready yet !, thank you... '))
        QToolBar(self.dock).addActions((self.open, self.diff,
                                        self.save, self.patc))
        try:
            self.factory = KPluginLoader("komparepart").factory()
            self.part = self.factory.create(self)
            self.dock.setWidget(self.part.widget())
            self.open.triggered.connect(lambda: self.part.openUrl(KUrl(str(
                QFileDialog.getOpenFileName(self.dock, ' Open a DIFF file ',
                                        path.expanduser("~"), ';;(*.diff)')))))
        except:
            self.dock.setWidget(QLabel(""" <center> <h3>ಠ_ಠ<br>
            ERROR: Please, install Kompare and PyKDE ! </h3><br>
            <br><i> (Sorry, cant embed non-Qt Apps). </i><center>"""))

        self.misc = self.locator.get_service('misc')
        self.misc.add_widget(self.dock,
                                QIcon.fromTheme("edit-select-all"), __doc__)

    def run_gui_and_get_results(self):
        ' run_gui_and_get_results '
        gui = Diff_GUI()
        if gui.diff_path is not None and path.isfile(gui.diff_path) is True:
            self.part.openUrl(KUrl(str(gui.diff_path)))
        return gui.diff_path

    def save_a_diff(self):
        ' save a diff '
        out_file = path.abspath(str(QFileDialog.getSaveFileName(self.dock,
                   'Save a Diff file', path.expanduser("~"), ';;(*.diff)')))
        inp_file = file(str(QUrl(
               self.part.url()).toString()).replace('file://', ''), 'r').read()
        end_file = file(out_file, 'w')
        end_file.write(inp_file)
        end_file.close()
Exemple #14
0
class Main(plugin.Plugin):
    " dock Class "

    def initialize(self):
        " Init Class dock "
        self.dock = QDockWidget()
        self.dock.setFeatures(QDockWidget.DockWidgetFloatable
                              | QDockWidget.DockWidgetMovable)
        self.dock.setWindowTitle(__doc__)
        self.dock.setStyleSheet('QDockWidget::title{text-align: center;}')
        self.boton = QAction(QIcon.fromTheme("list-add"), 'Open', self)
        self.saver = QAction(QIcon.fromTheme("document-save"), 'Save', self)
        self.apiss = QAction(QIcon.fromTheme("help"), 'Python API Help', self)
        QToolBar(self.dock).addActions((self.boton, self.saver, self.apiss))
        try:
            self.factory = KPluginLoader("kigpart").factory()
            self.part = self.factory.create(self)
            self.part.setReadWrite(True)
            self.boton.triggered.connect(lambda: self.part.openUrl(
                KUrl(
                    str(
                        QFileDialog.getOpenFileName(
                            self.dock, ' Open Geometry Plot ',
                            path.expanduser("~"), ';;'.join([
                                '(*.{})'.format(e)
                                for e in ['fig', 'kig', 'kigz', 'seg', 'fgeo']
                            ]))))))
            self.saver.triggered.connect(lambda: self.part.saveAs(
                KUrl(
                    str(
                        QFileDialog.getSaveFileName(
                            self.dock, ' Save Geometry Plot ',
                            path.expanduser("~"), ';;'.join([
                                '(*.{})'.format(e)
                                for e in ['kig', 'kigz', 'fig']
                            ]))))))
            self.apiss.triggered.connect(lambda: open_new_tab(
                'http://edu.kde.org/kig/manual/scripting-api/classObject.html')
                                         )
            self.dock.setWidget(self.part.widget())
        except:
            self.dock.setWidget(
                QLabel(""" <center> <h3>ಠ_ಠ<br>
            ERROR: Please, install KIG and PyKDE ! </h3><br>
            <br><i> (Sorry, cant embed non-Qt Apps). </i><center>"""))
        self.misc = self.locator.get_service('misc')
        self.misc.add_widget(self.dock,
                             QIcon.fromTheme("accessories-calculator"),
                             __doc__)
Exemple #15
0
 def create_dockwidget(self):
     """Add to parent QMainWindow as a dock widget"""
     dock = QDockWidget(self.get_plugin_title(), self.main)#, self.FLAGS) -> bug in Qt 4.4
     dock.setObjectName(self.__class__.__name__+"_dw")
     dock.setAllowedAreas(self.ALLOWED_AREAS)
     dock.setFeatures(self.FEATURES)
     dock.setWidget(self)
     self.connect(dock, SIGNAL('visibilityChanged(bool)'),
                  self.visibility_changed)
     self.dockwidget = dock
     self.refresh_plugin()
     short = CONF.get(self.ID, "shortcut", None)
     if short is not None:
         QShortcut(QKeySequence(short), self.main,
                   lambda: self.visibility_changed(True))
     return (dock, self.LOCATION)
Exemple #16
0
 def create_dockwidget(self):
     """Add to parent QMainWindow as a dock widget"""
     dock = QDockWidget(self.get_widget_title(), self.main)#, self.flags) -> bug in Qt 4.4
     dock.setObjectName(self.__class__.__name__+"_dw")
     dock.setAllowedAreas(self.allowed_areas)
     dock.setFeatures(self.features)
     dock.setWidget(self)
     self.connect(dock, SIGNAL('visibilityChanged(bool)'),
                  self.visibility_changed)
     self.dockwidget = dock
     self.refresh()
     short = CONF.get(self.ID, "shortcut", None)
     if short is not None:
         QShortcut(QKeySequence(short), self.main,
                   lambda: self.visibility_changed(True))
     return (dock, self.location)
Exemple #17
0
class Terminal(plugin.Plugin):
    " Terminal Class "
    def initialize(self):
        " Init Class Terminal "
        self.terminal = QDockWidget()
        self.terminal.setFeatures(QDockWidget.DockWidgetFloatable |
                                  QDockWidget.DockWidgetMovable)
        self.terminal.setWindowTitle(__doc__)
        self.terminal.setStyleSheet('QDockWidget::title{text-align: center;}')
        try:
            self.factory = KPluginLoader("libkonsolepart").factory()
            self.terminal.setWidget(self.factory.create(self).widget())
        except:
            self.terminal.setWidget(QLabel(""" <center> <h3>ಠ_ಠ<br>
            ERROR: Please, install Konsole Terminal and PyKDE ! </h3><br>
            <br><i> (Sorry, cant embed non-Qt Terminal Apps). </i><center>"""))
        self.misc = self.locator.get_service('misc')
        self.misc.add_widget(self.terminal,
                               QIcon.fromTheme("utilities-terminal"), __doc__)
Exemple #18
0
    def __init__(self, game, parent = None):
        super(MainWindow, self).__init__(parent)
        self.game = game
        self.boardWidget = BoardWidget(self.game)
        self.chat = Chat()
        self.playerInfo = PlayerInfo(self.game)

        playerDock = QDockWidget("Players")
        playerDock.setWidget(self.playerInfo)
        playerDock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable)
        self.addDockWidget(Qt.RightDockWidgetArea, playerDock)

        chatDock = QDockWidget("Chat")
        chatDock.setWidget(self.chat)
        chatDock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable)
        self.addDockWidget(Qt.RightDockWidgetArea, chatDock)

        self.setCentralWidget(self.boardWidget)
        self.statusBar()
        self.initMenuBar()
Exemple #19
0
    def __init__(self, content, plot_labels, plot_name, *args, parent=None, **kwargs):
        """
        Initialisation of the PlotContainer widget.

        content - Content for the plotcontainer
        plot_lables - Labels of the plot widget
        plot_name - Name of the associated software
        parent - Parent widget (default None)

        Returns:
        None
        """
        super(PlotContainer, self).__init__(parent)
        self.setCentralWidget(None)
        self.setTabPosition(Qt.TopDockWidgetArea, QTabWidget.North)
        self.plot_name = plot_name

        self.content = []
        dock_widgets = []
        for label in plot_labels:
            label = label[0]
            if label == 'mic_number':
                continue
            elif label == 'file_name':
                continue
            else:
                pass

            widget = PlotWidget(label=label, plot_typ=content, parent=self)
            self.content.append(widget)

            dock_widget = QDockWidget(label, self)
            dock_widget.setWidget(widget)
            dock_widget.installEventFilter(self)
            dock_widget.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable)
            dock_widgets.append(dock_widget)
            self.addDockWidget(Qt.TopDockWidgetArea, dock_widget, Qt.Horizontal)

        for idx in range(1, len(dock_widgets)):
            self.tabifyDockWidget(dock_widgets[0], dock_widgets[idx])
Exemple #20
0
    def __init__(self):
        super().__init__()

        daq = Traces(args)
        dock_daq = QDockWidget('DAQ', self)
        dock_daq.setWidget(daq)
        dock_daq.setObjectName('DAQ')
        dock_daq.setFeatures(QDockWidget.DockWidgetMovable)
        self.addDockWidget(Qt.TopDockWidgetArea, dock_daq)

        widgets = {
            'daq': daq,
        }
        self.controlpanel = ControlPanel(widgets)
        self.setCentralWidget(self.controlpanel)

        window_geometry = settings.value('window/geometry')
        if window_geometry is not None:
            self.restoreGeometry(window_geometry)
        window_state = settings.value('window/state')
        if window_state is not None:
            self.restoreState(window_state, float(VERSION))
Exemple #21
0
class Main(plugin.Plugin):
    " dock Class "
    def initialize(self):
        " Init Class dock "
        self.dock = QDockWidget()
        self.dock.setFeatures(QDockWidget.DockWidgetFloatable |
                              QDockWidget.DockWidgetMovable)
        self.dock.setWindowTitle(__doc__)
        self.dock.setStyleSheet('QDockWidget::title{text-align: center;}')
        self.boton = QAction(QIcon.fromTheme("list-add"), 'Open', self)
        self.saver = QAction(QIcon.fromTheme("document-save"), 'Save', self)
        self.apiss = QAction(QIcon.fromTheme("help"), 'Python API Help', self)
        QToolBar(self.dock).addActions((self.boton, self.saver, self.apiss))
        try:
            self.factory = KPluginLoader("kigpart").factory()
            self.part = self.factory.create(self)
            self.part.setReadWrite(True)
            self.boton.triggered.connect(lambda: self.part.openUrl(KUrl(str(
                QFileDialog.getOpenFileName(self.dock, ' Open Geometry Plot ',
                path.expanduser("~"),
                ';;'.join(['(*.{})'.format(e) for e in ['fig', 'kig', 'kigz',
                                                        'seg', 'fgeo']]))))))
            self.saver.triggered.connect(lambda: self.part.saveAs(KUrl(str(
                QFileDialog.getSaveFileName(self.dock, ' Save Geometry Plot ',
                path.expanduser("~"),
                ';;'.join(['(*.{})'.format(e) for e in ['kig', 'kigz', 'fig']])
            )))))
            self.apiss.triggered.connect(lambda: open_new_tab(
                'http://edu.kde.org/kig/manual/scripting-api/classObject.html'))
            self.dock.setWidget(self.part.widget())
        except:
            self.dock.setWidget(QLabel(""" <center> <h3>ಠ_ಠ<br>
            ERROR: Please, install KIG and PyKDE ! </h3><br>
            <br><i> (Sorry, cant embed non-Qt Apps). </i><center>"""))
        self.misc = self.locator.get_service('misc')
        self.misc.add_widget(self.dock,
                            QIcon.fromTheme("accessories-calculator"), __doc__)
Exemple #22
0
class Main(plugin.Plugin):
    " dock Class "

    def initialize(self):
        " Init Class dock "
        self.dock = QDockWidget()
        self.dock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable)
        self.dock.setWindowTitle(__doc__)
        self.dock.setStyleSheet("QDockWidget::title{text-align: center;}")
        self.scrollable = QScrollArea()
        self.dock.setWidget(self.scrollable)
        try:
            self.factory = KPluginLoader("klinkstatuspart").factory()
            self.scrollable.setWidget(self.factory.create(self).widget())
        except:
            self.scrollable.setWidget(
                QLabel(
                    """ <center> <h3>ಠ_ಠ<br>
            ERROR: Please, install KLinkCheck and PyKDE ! </h3><br>
            <br><i> (Sorry, cant embed non-Qt Apps). </i><center>"""
                )
            )
        self.misc = self.locator.get_service("misc")
        self.misc.add_widget(self.dock, QIcon.fromTheme("insert-link"), __doc__)
Exemple #23
0
class DBManager(QMainWindow):

    def __init__(self, iface, parent=None):
        QMainWindow.__init__(self, parent)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setupUi()
        self.iface = iface

        # restore the window state
        settings = QSettings()
        self.restoreGeometry(settings.value("/DB_Manager/mainWindow/geometry", QByteArray(), type=QByteArray))
        self.restoreState(settings.value("/DB_Manager/mainWindow/windowState", QByteArray(), type=QByteArray))

        self.connect(self.tabs, SIGNAL("currentChanged(int)"), self.tabChanged)
        self.connect(self.tree, SIGNAL("selectedItemChanged"), self.itemChanged)
        self.itemChanged(None)

    def closeEvent(self, e):
        self.unregisterAllActions()

        # save the window state
        settings = QSettings()
        settings.setValue("/DB_Manager/mainWindow/windowState", self.saveState())
        settings.setValue("/DB_Manager/mainWindow/geometry", self.saveGeometry())

        QMainWindow.closeEvent(self, e)

    def refreshItem(self, item=None):
        QApplication.setOverrideCursor(Qt.WaitCursor)
        try:
            if item is None:
                item = self.tree.currentItem()
            self.tree.refreshItem(item)  # refresh item children in the db tree
        except BaseError as e:
            DlgDbError.showError(e, self)
            return
        finally:
            QApplication.restoreOverrideCursor()

    def itemChanged(self, item):
        QApplication.setOverrideCursor(Qt.WaitCursor)
        try:
            self.reloadButtons()
            self.refreshTabs()
        except BaseError as e:
            DlgDbError.showError(e, self)
            return
        finally:
            QApplication.restoreOverrideCursor()

    def reloadButtons(self):
        db = self.tree.currentDatabase()
        if not hasattr(self, '_lastDb'):
            self._lastDb = db

        elif db == self._lastDb:
            return

        # remove old actions
        if self._lastDb is not None:
            self.unregisterAllActions()

        # add actions of the selected database
        self._lastDb = db
        if self._lastDb is not None:
            self._lastDb.registerAllActions(self)

    def tabChanged(self, index):
        QApplication.setOverrideCursor(Qt.WaitCursor)
        try:
            self.refreshTabs()
        except BaseError as e:
            DlgDbError.showError(e, self)
            return
        finally:
            QApplication.restoreOverrideCursor()

    def refreshTabs(self):
        index = self.tabs.currentIndex()
        item = self.tree.currentItem()
        table = self.tree.currentTable()

        # enable/disable tabs
        self.tabs.setTabEnabled(self.tabs.indexOf(self.table), table is not None)
        self.tabs.setTabEnabled(self.tabs.indexOf(self.preview), table is not None and table.type in [table.VectorType,
                                                                                                      table.RasterType] and table.geomColumn is not None)
        # show the info tab if the current tab is disabled
        if not self.tabs.isTabEnabled(index):
            self.tabs.setCurrentWidget(self.info)

        current_tab = self.tabs.currentWidget()
        if current_tab == self.info:
            self.info.showInfo(item)
        elif current_tab == self.table:
            self.table.loadData(item)
        elif current_tab == self.preview:
            self.preview.loadPreview(item)

    def refreshActionSlot(self):
        self.info.setDirty()
        self.table.setDirty()
        self.preview.setDirty()
        self.refreshItem()

    def importActionSlot(self):
        db = self.tree.currentDatabase()
        if db is None:
            self.infoBar.pushMessage(self.tr("No database selected or you are not connected to it."),
                                     QgsMessageBar.INFO, self.iface.messageTimeout())
            return

        outUri = db.uri()
        schema = self.tree.currentSchema()
        if schema:
            outUri.setDataSource(schema.name, "", "", "")

        from .dlg_import_vector import DlgImportVector

        dlg = DlgImportVector(None, db, outUri, self)
        dlg.exec_()

    def exportActionSlot(self):
        table = self.tree.currentTable()
        if table is None:
            self.infoBar.pushMessage(self.tr("Select the table you want export to file."), QgsMessageBar.INFO,
                                     self.iface.messageTimeout())
            return

        inLayer = table.toMapLayer()

        from .dlg_export_vector import DlgExportVector

        dlg = DlgExportVector(inLayer, table.database(), self)
        dlg.exec_()

        inLayer.deleteLater()

    def runSqlWindow(self):
        db = self.tree.currentDatabase()
        if db is None:
            self.infoBar.pushMessage(self.tr("No database selected or you are not connected to it."),
                                     QgsMessageBar.INFO, self.iface.messageTimeout())
            # force displaying of the message, it appears on the first tab (i.e. Info)
            self.tabs.setCurrentIndex(0)
            return

        from dlg_sql_window import DlgSqlWindow

        query = DlgSqlWindow(self.iface, db, self)
        dbname = db.connection().connectionName()
        tabname = self.tr("Query") + u" (%s)" % dbname
        index = self.tabs.addTab(query, tabname)
        self.tabs.setTabIcon(index, db.connection().icon())
        self.tabs.setCurrentIndex(index)
        query.nameChanged.connect(functools.partial(self.update_query_tab_name, index, dbname))

    def update_query_tab_name(self, index, dbname, queryname):
        if not queryname:
            queryname = self.tr("Query")
        tabname = u"%s (%s)" % (queryname, dbname)
        self.tabs.setTabText(index, tabname)

    def showSystemTables(self):
        self.tree.showSystemTables(self.actionShowSystemTables.isChecked())

    def registerAction(self, action, menuName, callback=None):
        """ register an action to the manager's main menu """
        if not hasattr(self, '_registeredDbActions'):
            self._registeredDbActions = {}

        if callback is not None:
            invoke_callback = lambda x: self.invokeCallback(callback)

        if menuName is None or menuName == "":
            self.addAction(action)

            if menuName not in self._registeredDbActions:
                self._registeredDbActions[menuName] = list()
            self._registeredDbActions[menuName].append(action)

            if callback is not None:
                QObject.connect(action, SIGNAL("triggered(bool)"), invoke_callback)
            return True

        # search for the menu
        actionMenu = None
        helpMenuAction = None
        for a in self.menuBar.actions():
            if not a.menu() or a.menu().title() != menuName:
                continue
            if a.menu() != self.menuHelp:
                helpMenuAction = a

            actionMenu = a
            break

        # not found, add a new menu before the help menu
        if actionMenu is None:
            menu = QMenu(menuName, self)
            if helpMenuAction is not None:
                actionMenu = self.menuBar.insertMenu(helpMenuAction, menu)
            else:
                actionMenu = self.menuBar.addMenu(menu)

        menu = actionMenu.menu()
        menuActions = menu.actions()

        # get the placeholder's position to insert before it
        pos = 0
        for pos in range(len(menuActions)):
            if menuActions[pos].isSeparator() and menuActions[pos].objectName().endswith("_placeholder"):
                menuActions[pos].setVisible(True)
                break

        if pos < len(menuActions):
            before = menuActions[pos]
            menu.insertAction(before, action)
        else:
            menu.addAction(action)

        actionMenu.setVisible(True)  # show the menu

        if menuName not in self._registeredDbActions:
            self._registeredDbActions[menuName] = list()
        self._registeredDbActions[menuName].append(action)

        if callback is not None:
            QObject.connect(action, SIGNAL("triggered(bool)"), invoke_callback)

        return True

    def invokeCallback(self, callback, *params):
        """ Call a method passing the selected item in the database tree,
                the sender (usually a QAction), the plugin mainWindow and
                optionally additional parameters.

                This method takes care to override and restore the cursor,
                but also catches exceptions and displays the error dialog.
        """
        QApplication.setOverrideCursor(Qt.WaitCursor)
        try:
            callback(self.tree.currentItem(), self.sender(), self, *params)

        except BaseError as e:
            # catch database errors and display the error dialog
            DlgDbError.showError(e, self)
            return

        finally:
            QApplication.restoreOverrideCursor()

    def unregisterAction(self, action, menuName):
        if not hasattr(self, '_registeredDbActions'):
            return

        if menuName is None or menuName == "":
            self.removeAction(action)

            if menuName in self._registeredDbActions:
                if self._registeredDbActions[menuName].count(action) > 0:
                    self._registeredDbActions[menuName].remove(action)

            action.deleteLater()
            return True

        for a in self.menuBar.actions():
            if not a.menu() or a.menu().title() != menuName:
                continue

            menu = a.menu()
            menuActions = menu.actions()

            menu.removeAction(action)
            if menu.isEmpty():  # hide the menu
                a.setVisible(False)

            if menuName in self._registeredDbActions:
                if self._registeredDbActions[menuName].count(action) > 0:
                    self._registeredDbActions[menuName].remove(action)

                # hide the placeholder if there're no other registered actions
                if len(self._registeredDbActions[menuName]) <= 0:
                    for i in range(len(menuActions)):
                        if menuActions[i].isSeparator() and menuActions[i].objectName().endswith("_placeholder"):
                            menuActions[i].setVisible(False)
                            break

            action.deleteLater()
            return True

        return False

    def unregisterAllActions(self):
        if not hasattr(self, '_registeredDbActions'):
            return

        for menuName in self._registeredDbActions:
            for action in list(self._registeredDbActions[menuName]):
                self.unregisterAction(action, menuName)
        del self._registeredDbActions

    def close_tab(self, index):
        widget = self.tabs.widget(index)
        if widget not in [self.info, self.table, self.preview]:
            self.tabs.removeTab(index)
            widget.deleteLater()

    def setupUi(self):
        self.setWindowTitle(self.tr("DB Manager"))
        self.setWindowIcon(QIcon(":/db_manager/icon"))
        self.resize(QSize(700, 500).expandedTo(self.minimumSizeHint()))

        # create central tab widget and add the first 3 tabs: info, table and preview
        self.tabs = QTabWidget()
        self.info = InfoViewer(self)
        self.tabs.addTab(self.info, self.tr("Info"))
        self.table = TableViewer(self)
        self.tabs.addTab(self.table, self.tr("Table"))
        self.preview = LayerPreview(self)
        self.tabs.addTab(self.preview, self.tr("Preview"))
        self.setCentralWidget(self.tabs)

        # display close button for all tabs but the first 3 ones, i.e.
        # HACK: just hide the close button where not needed (GS)
        self.tabs.setTabsClosable(True)
        self.tabs.tabCloseRequested.connect(self.close_tab)
        tabbar = self.tabs.tabBar()
        for i in range(3):
            btn = tabbar.tabButton(i, QTabBar.RightSide) if tabbar.tabButton(i, QTabBar.RightSide) else tabbar.tabButton(i, QTabBar.LeftSide)
            btn.resize(0, 0)
            btn.hide()

        # Creates layout for message bar
        self.layout = QGridLayout(self.info)
        self.layout.setContentsMargins(0, 0, 0, 0)
        spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
        self.layout.addItem(spacerItem, 1, 0, 1, 1)
        # init messageBar instance
        self.infoBar = QgsMessageBar(self.info)
        sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.infoBar.setSizePolicy(sizePolicy)
        self.layout.addWidget(self.infoBar, 0, 0, 1, 1)

        # create database tree
        self.dock = QDockWidget("Tree", self)
        self.dock.setObjectName("DB_Manager_DBView")
        self.dock.setFeatures(QDockWidget.DockWidgetMovable)
        self.tree = DBTree(self)
        self.dock.setWidget(self.tree)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.dock)

        # create status bar
        self.statusBar = QStatusBar(self)
        self.setStatusBar(self.statusBar)

        # create menus
        self.menuBar = QMenuBar(self)
        self.menuDb = QMenu(self.tr("&Database"), self)
        self.menuBar.addMenu(self.menuDb)
        self.menuSchema = QMenu(self.tr("&Schema"), self)
        actionMenuSchema = self.menuBar.addMenu(self.menuSchema)
        self.menuTable = QMenu(self.tr("&Table"), self)
        actionMenuTable = self.menuBar.addMenu(self.menuTable)
        self.menuHelp = None  # QMenu(self.tr("&Help"), self)
        # actionMenuHelp = self.menuBar.addMenu(self.menuHelp)

        self.setMenuBar(self.menuBar)

        # create toolbar
        self.toolBar = QToolBar("Default", self)
        self.toolBar.setObjectName("DB_Manager_ToolBar")
        self.addToolBar(self.toolBar)

        # create menus' actions

        # menu DATABASE
        sep = self.menuDb.addSeparator()
        sep.setObjectName("DB_Manager_DbMenu_placeholder")
        sep.setVisible(False)

        self.actionRefresh = self.menuDb.addAction(QIcon(":/db_manager/actions/refresh"), self.tr("&Refresh"),
                                                   self.refreshActionSlot, QKeySequence("F5"))
        self.actionSqlWindow = self.menuDb.addAction(QIcon(":/db_manager/actions/sql_window"), self.tr("&SQL window"),
                                                     self.runSqlWindow, QKeySequence("F2"))
        self.menuDb.addSeparator()
        self.actionClose = self.menuDb.addAction(QIcon(), self.tr("&Exit"), self.close, QKeySequence("CTRL+Q"))

        # menu SCHEMA
        sep = self.menuSchema.addSeparator()
        sep.setObjectName("DB_Manager_SchemaMenu_placeholder")
        sep.setVisible(False)

        actionMenuSchema.setVisible(False)

        # menu TABLE
        sep = self.menuTable.addSeparator()
        sep.setObjectName("DB_Manager_TableMenu_placeholder")
        sep.setVisible(False)

        self.actionImport = self.menuTable.addAction(QIcon(":/db_manager/actions/import"),
                                                     self.tr("&Import layer/file"), self.importActionSlot)
        self.actionExport = self.menuTable.addAction(QIcon(":/db_manager/actions/export"), self.tr("&Export to file"),
                                                     self.exportActionSlot)
        self.menuTable.addSeparator()
        #self.actionShowSystemTables = self.menuTable.addAction(self.tr("Show system tables/views"), self.showSystemTables)
        #self.actionShowSystemTables.setCheckable(True)
        #self.actionShowSystemTables.setChecked(True)
        actionMenuTable.setVisible(False)

        # add actions to the toolbar
        self.toolBar.addAction(self.actionRefresh)
        self.toolBar.addAction(self.actionSqlWindow)
        self.toolBar.addAction(self.actionImport)
        self.toolBar.addAction(self.actionExport)
Exemple #24
0
class GUI(QMainWindow):

    def __init__(self, debug=False):
        QMainWindow.__init__(self)
        # initialise filename
        self.filename = None
        # make the data store
        from xmlstore import Store
        self.store = Store(debug = debug)
        # view the current table
        self.tableView = TableView(self)
        self.tableView.setDragEnabled(True);
        self.tableView.setAcceptDrops(True);
        self.tableView.setDropIndicatorShown(True);
        self.setCentralWidget(self.tableView)
        # add a custom delegate to it
        self.delegate = ComboBoxDelegate()
        self.tableView.setItemDelegate(self.delegate)
        # dock the table selection on the left
        self.dock1 = QDockWidget(self)
        self.listView = ListView(self)
        self.listView.setDragEnabled(True);
        self.listView.setDragDropMode(QAbstractItemView.InternalMove)
        self.listView.setDropIndicatorShown(True);
        self.dock1.setWidget(self.listView)
        self.dock1.setFeatures(
            QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.dock1)
        # connect it to the populate method
        self.connect(self.listView, SIGNAL('activated ( const QModelIndex & )'),
                     self.populate)
        self.connect(self.listView, SIGNAL('clicked ( const QModelIndex & )'),
                     self.populate)
        # dock the undoView on the left
        self.dock2 = QDockWidget(self)
        self.undoView = QUndoView(self.store.stack)
        self.dock2.setWidget(self.undoView)
        self.dock2.setFeatures(
            QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable)
        self.dock2.setWindowTitle('Undo Stack')
        self.addDockWidget(Qt.LeftDockWidgetArea, self.dock2)
        # create a menubar
        self.menu = self.menuBar()
        # create file menu headings
        self.menuFile = self.menu.addMenu('File')
        self.menuFile.addAction('New', self.New).setShortcut('CTRL+N')
        self.menuFile.addAction('Open...', self.Open).setShortcut('CTRL+O')
        self.menuFile.addAction('Reload', self.Reload)
        self.menuFile.addAction('Save', self.Save).setShortcut('CTRL+S')
        self.menuFile.addAction('Save As...', self.SaveAs)
        self.menuFile.addSeparator()
        self.menuFile.addAction('Set Architecture...', self.setArch)
        self.menuFile.addSeparator()
        self.menuFile.addAction('Quit', self.closeEvent).setShortcuts(['CTRL+Q', 'ALT+F4'])
        # create edit menu headings
        self.menuEdit = self.menu.addMenu('Edit')
        self.menuEdit.addAction('Insert Row',
            self.tableView.insertRow).setShortcut('CTRL+I')
        self.menuEdit.addAction('Insert Row Under',
            self.tableView.insertRowUnder).setShortcut('CTRL+U')
        self.menuEdit.addAction('Remove Row', self.tableView.removeRow)
        self.menuEdit.addSeparator()
        self.menuEdit.addAction('Cut',
            self.tableView.cut).setShortcut('CTRL+X')
        self.menuEdit.addAction('Copy',
            self.tableView.copy).setShortcuts(['CTRL+C', 'CTRL+INS'])
        self.menuEdit.addAction('Paste',
            self.tableView.paste).setShortcuts(['CTRL+V', 'SHIFT+INS'])
        self.menuEdit.addAction('Clear',
            self.tableView.menuClear).setShortcut('CTRL+D')
        self.menuEdit.addSeparator()
        self.menuEdit.addAction('Fill Cells',
            self.tableView.fillCells).setShortcut('CTRL+L')
        self.menuEdit.addAction('Fill Cells and Increment',
            self.tableView.fillCellsInc).setShortcut('CTRL+R')
        self.menuEdit.addAction('Python Code...',
            self.tableView.pythonCode).setShortcut('CTRL+P')
        self.tableView.codeBox = pythonCode()
        self.menuEdit.addSeparator()
        self.menuEdit.addAction('Undo',
            self.store.stack, SLOT('undo()')).setShortcut('CTRL+Z')
        self.menuEdit.addAction('Redo',
            self.store.stack, SLOT('redo()')).setShortcut('CTRL+SHIFT+Z')
        # create component menu
        self.menuComponents = self.menu.addMenu('Components')
        self.resize(QSize(1000,500))

    def Save(self):
        # save menu command
        self.SaveAs(self.filename)

    def setArch(self):
        arch = self.store.getArch()
        arch, ok = QInputDialog.getText(self, 'Architecture Dialog',
             'Enter Architecture', QLineEdit.Normal, arch)
        arch = str(arch)
        if ok:
            self.store.setArch(arch)

    def SaveAs(self, filename = ''):
        # save as menu command
        if filename == '':
            filename = str(QFileDialog.getSaveFileName())
        if filename != '':
            self.filename = filename
            self.store.Save(filename)
            self._setClean()

    def Reload(self):
        if self.filename is not None:
            self.Open(self.filename, name = self.tablename)

    def Open(self, filename = '', name = None):
        # make sure the user is sure if there are unsaved changes
        if self.__prompt_unsaved() != QMessageBox.Yes:
            return
        # ask for a filename
        if filename == '':
            filename = str(QFileDialog.getOpenFileName())
        if filename == '':
            return
        # store the filename
        self.filename = filename
        # tell the store to open a new set of tables
        try:
            problems, warnings = self.store.Open(filename)
            if problems:
                errorstr = 'Can\'t load all object types: '+', '.join(problems)
                QMessageBox.warning(self,'Open Error',errorstr)
            if warnings:
                errorstr = \
                    'The following warnings were generated:\n' + \
                    '\n'.join(warnings)
                QMessageBox.warning(self,'Open Warnings',errorstr)
        except Exception, e:
            x = formLog('An error ocurred. Make sure all the modules listed '
                'in RELEASE files are built. Check the text below for '
                'details:\n\n' + traceback.format_exc(), self)
            x.show()
            return
        # populate
        self.setWindowTitle('XEB - %s[*]'%filename)
        self.listView.clear()
        for t in self.store.getTableNames():
            self.__insertListViewItem(t)
        self.__populateMenu()
        self.populate(name = name)
        self._setClean()
class ComposerWrapper(QObject):
    """
    Embeds custom STDM tools in a QgsComposer instance for managing map-based
    STDM document templates.
    """
    dataSourceSelected = pyqtSignal(str)
    
    def __init__(self,composerView):
        QObject.__init__(self,composerView)
        
        self._compView = composerView
        self._stdmTB = self.mainWindow().addToolBar("STDM")
        self._selectMoveAction = None
        
        #Container for custom editor widgets
        self._widgetMappings = {}
        
        #Create dock widget for configuring STDM data source
        self._stdmDataSourceDock = QDockWidget(QApplication.translate("ComposerWrapper","STDM Data Source"),self.mainWindow())
        self._stdmDataSourceDock.setObjectName("STDMDataSourceDock")
        self._stdmDataSourceDock.setMinimumWidth(300)
        self._stdmDataSourceDock.setFeatures(QDockWidget.DockWidgetMovable|QDockWidget.DockWidgetClosable)
        self.mainWindow().addDockWidget(Qt.RightDockWidgetArea,self._stdmDataSourceDock)
        
        dataSourceWidget = ComposerDataSourceSelector()
        self._stdmDataSourceDock.setWidget(dataSourceWidget)
        self._stdmDataSourceDock.show()
        
        #Create dock widget for configuring STDM item properties
        self._stdmItemPropDock = QDockWidget(QApplication.translate("ComposerWrapper","STDM data properties"),self.mainWindow())
        self._stdmItemPropDock.setObjectName("STDMItemDock")
        self._stdmItemPropDock.setMinimumWidth(300)
        self._stdmItemPropDock.setFeatures(QDockWidget.DockWidgetMovable|QDockWidget.DockWidgetClosable)
        self.mainWindow().addDockWidget(Qt.RightDockWidgetArea,self._stdmItemPropDock)
        self._stdmItemPropDock.show()
        
        if self.itemDock() != None:
            self.mainWindow().tabifyDockWidget(self.itemDock(),self._stdmItemPropDock)
            
        if self.atlasDock() != None:
            self.atlasDock().hide()
            
        if self.generalDock() != None:
            self.generalDock().raise_()
            
        #Connect signals
        self.composition().itemRemoved.connect(self._onItemRemoved)
        dataSourceWidget.cboDataSource.currentIndexChanged[str].connect(self.propagateDataSourceSelection)
        self.composerView().selectedItemChanged.connect(self._onItemSelected)
        
        #Current template document file
        self._currDocFile = None
        
    def _removeActions(self):
        """
        Remove inapplicable actions and their corresponding toolbars and menus.
        """
        removeActions = ["mActionSaveProject","mActionNewComposer","mActionDuplicateComposer"]
        
        composerToolbar = self.composerMainToolBar()
        if composerToolbar != None:
            saveProjectAction = None
            
            for itemAction in composerToolbar.actions():
                if itemAction.objectName() == "mActionSaveProject":
                    saveProjectAction = itemAction
                    break
                
            if saveProjectAction != None:
                composerMenu = saveProjectAction.menu()
        
    def configure(self):
        #Create instances of custom STDM composer item configurations
        for ciConfig in ComposerItemConfig.itemConfigurations:
            ciConfigObj = ciConfig(self)
            
    def addWidgetMapping(self,uniqueIdentifier,widget):
        """
        Add custom STDM editor widget based on the unique identifier of the composer item
        """
        self._widgetMappings[uniqueIdentifier] = widget
        
    def widgetMappings(self):
        """
        Returns a dictionary containing uuid values of composer items linked to STDM widgets.
        """
        return self._widgetMappings
    
    def clearWidgetMappings(self):
        """
        Resets the widget mappings collection.
        """
        self._widgetMappings = {}
        
    def mainWindow(self):
        """
        Returns the QMainWindow used by the composer view.
        """
        return self._compView.composerWindow()
    
    def stdmToolBar(self):
        """
        Returns the instance of the STDM toolbar added to the QgsComposer.
        """
        return self._stdmTB
    
    def composerView(self):
        """
        Returns the composer view.
        """
        return self._compView
    
    def composition(self):
        """
        Returns the QgsComposition instance used in the composer view.
        """
        return self._compView.composition()
    
    def composerItemToolBar(self):
        """
        Returns the toolbar containing actions for adding composer items.
        """
        return self.mainWindow().findChild(QToolBar,"mItemToolbar")
    
    def composerMainToolBar(self):
        """
        Returns the toolbar containing actions for managing templates.
        """
        return self.mainWindow().findChild(QToolBar,"mComposerToolbar")
    
    def selectMoveAction(self):
        """
        Returns the QAction for selecting or moving composer items.
        """
        if self.composerItemToolBar() != None:
            if self._selectMoveAction == None:
                for itemAction in self.composerItemToolBar().actions():
                    if itemAction.objectName() == "mActionSelectMoveItem":
                        self._selectMoveAction = itemAction
                        break
        
        return self._selectMoveAction
    
    def checkedItemAction(self):
        """
        Returns the currently selected composer item action.
        """
        if self.selectMoveAction() != None:
            return self.selectMoveAction().actionGroup().checkedAction()
        
        return None
    
    def itemDock(self):
        """
        Get the 'Item Properties' dock widget.
        """
        return self.mainWindow().findChild(QDockWidget,"ItemDock")
    
    def atlasDock(self):
        """
        Get the 'Atlas generation' dock widget.
        """
        return self.mainWindow().findChild(QDockWidget,"AtlasDock")
    
    def generalDock(self):
        """
        Get the 'Composition' dock widget.
        """
        return self.mainWindow().findChild(QDockWidget,"CompositionDock")
    
    def stdmDataSourceDock(self):
        """
        Returns the STDM data source dock widget.
        """
        return self._stdmDataSourceDock
    
    def stdmItemDock(self):
        """
        Returns the STDM item dock widget.
        """
        return self._stdmItemPropDock
    
    def documentFile(self):
        """
        Returns the QFile instance associated with the current document. 'None' will be returned for
        new, unsaved documents.
        """
        return self._currDocFile
    
    def setDocumentFile(self,docFile):
        """
        Sets the document file.
        """
        if not isinstance(docFile,QFile):
            return
        
        self._currDocFile = docFile
    
    def selectedDataSource(self):
        """
        Returns the name of the data source specified by the user.
        """
        return self._stdmDataSourceDock.widget().cboDataSource.currentText()
    
    def selectedDataSourceCategory(self):
        """
        Returns the category (view or table) that the data source belongs to.
        """
        if self.stdmDataSourceDock().widget() != None:
            return self.stdmDataSourceDock().widget().category()
        
        return ""
    
    def propagateDataSourceSelection(self,dataSourceName):
        """
        Propagates the signal when a user select a data source. Listening objects can hook on to it.
        """
        self.dataSourceSelected.emit(dataSourceName)
        
    def loadTemplate(self,filePath):
        """
        Loads a document template into the view and updates the necessary STDM-related controls.
        """
        if not QFile.exists(filePath):
                QMessageBox.critical(self.composerView(), QApplication.translate("OpenTemplateConfig","Open Template Error"), \
                                            QApplication.translate("OpenTemplateConfig","The specified template does not exist."))
                return
            
        templateFile = QFile(filePath)
        
        if not templateFile.open(QIODevice.ReadOnly):
            QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Open Operation Error"), \
                                            "{0}\n{1}".format(QApplication.translate("ComposerWrapper","Cannot read template file."), \
                                                      templateFile.errorString()
                                                      ))
            return    
         
        templateDoc = QDomDocument()
        
        if templateDoc.setContent(templateFile):
            #Load items into the composition and configure STDM data controls
            self.composition().loadFromTemplate(templateDoc)
            self.clearWidgetMappings()
            
            #Load data controls
            composerDS = ComposerDataSource.create(templateDoc)
            self._configureDataControls(composerDS)
            
            #Load symbol editors
            spatialFieldsConfig = SpatialFieldsConfiguration.create(templateDoc)
            self._configureSpatialSymbolEditor(spatialFieldsConfig)
            
    def saveTemplate(self):
        """
        Creates and saves a new document template.
        """
        #Validate if the user has specified the data source
        if self.selectedDataSource() == "":
            QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Error"), \
                                            QApplication.translate("ComposerWrapper","Please specify the " \
                                                                   "data source name for the document composition."))
            return
            
        #If it is a new unsaved document template then prompt for the document name.
        docFile = self.documentFile()
        
        if docFile == None:
            docName,ok = QInputDialog.getText(self.composerView(), \
                                              QApplication.translate("ComposerWrapper","Template Name"), \
                                              QApplication.translate("ComposerWrapper","Please enter the template name below"), \
                                              )
            if ok and docName != "":
                templateDir = self._composerTemplatesPath()
                
                if templateDir == None:
                    QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Error"), \
                                            QApplication.translate("ComposerWrapper","Directory for document templates could not be found."))
                    return
                
                absPath = templateDir + "/" + docName + ".sdt"            
                docFile= QFile(absPath)
            
            else:
                return
        
        docFileInfo = QFileInfo(docFile)    
        
        if not docFile.open(QIODevice.WriteOnly):
            QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Save Operation Error"), \
                                            "{0}\n{1}".format(QApplication.translate("ComposerWrapper","Could not save template file."), \
                                                      docFile.errorString()
                                                      ))
            return
                                              
        templateDoc = QDomDocument()
        self._writeXML(templateDoc,docFileInfo.completeBaseName())
        
        if docFile.write(templateDoc.toByteArray()) == -1:
            QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Save Error"), \
                                            QApplication.translate("ComposerWrapper","Could not save template file."))
            return
        
        docFile.close()                   
        self.setDocumentFile(docFile)
        
    def _writeXML(self,xmlDoc,docName):
        """
        Write the template configuration into the XML document.
        """        
        #Write default composer configuration
        composerElement = xmlDoc.createElement("Composer")
        composerElement.setAttribute("title",docName)
        composerElement.setAttribute("visible",1)
        
        xmlDoc.appendChild(composerElement)
        
        self.composition().writeXML(composerElement,xmlDoc)
        
        #Write STDM data field configurations
        dataSourceElement = ComposerDataSource.domElement(self, xmlDoc)
        composerElement.appendChild(dataSourceElement)
        
        #Write spatial field configurations
        spatialColumnsElement = SpatialFieldsConfiguration.domElement(self, xmlDoc)
        dataSourceElement.appendChild(spatialColumnsElement)
        
    def _configureDataControls(self,composerDataSource):
        """
        Configure the data source and data field controls based on the composer data
        source configuration.
        """
        if self.stdmDataSourceDock().widget() != None:
            #Set data source
            dataSourceWidget = self.stdmDataSourceDock().widget()
            dataSourceWidget.setCategory(composerDataSource.category())
            dataSourceWidget.setSelectedSource(composerDataSource.name())
            
            #Set data field controls
            for composerId in composerDataSource.dataFieldMappings().reverse:
                #Use composer item id since the uuid is stripped off
                composerItem = self.composition().getComposerItemById(composerId)
                
                if composerItem != None:
                    compFieldSelector = ComposerFieldSelector(self,composerItem,self.composerView())
                    compFieldSelector.selectFieldName(composerDataSource.dataFieldName(composerId))
                    
                    #Add widget to the collection but now use the current uuid of the composition item
                    self.addWidgetMapping(composerItem.uuid(),compFieldSelector)
                    
    def _configureSpatialSymbolEditor(self,spatialFieldConfig):
        """
        Configure symbol editor controls.
        """
        if self.stdmDataSourceDock().widget() != None:
            for itemId,spFieldsMappings in spatialFieldConfig.spatialFieldsMapping().iteritems():
                mapItem =  self.composition().getComposerItemById(itemId)
                
                if mapItem != None:
                    composerSymbolEditor = ComposerSymbolEditor(self,self.composerView())
                    composerSymbolEditor.addSpatialFieldMappings(spFieldsMappings)
                    
                    #Add widget to the collection but now use the current uuid of the composer map
                    self.addWidgetMapping(mapItem.uuid(),composerSymbolEditor)
                        
    def _composerTemplatesPath(self):
        """
        Reads the path of composer templates in the registry.
        """
        regConfig = RegistryConfig()
        keyName = "ComposerTemplates"
        
        valueCollection = regConfig.read([keyName])
        
        if len(valueCollection) == 0:
            return None
        
        else:
            return valueCollection[keyName]
    
    def _onItemRemoved(self,item):
        """
        Slot raised when a composer item is removed from the scene.
        """
        """
        Code will not work since a QObject instance is returned instead of a QgsComposerItem
        if item.uuid() in self._widgetMappings:
            del self._widgetMappings[item.uuid()]
        """
        pass
    
    def _onItemSelected(self,item):
        """
        Slot raised when a composer item is selected. Load the corresponding field selector
        if the selection is an STDM data field label.
        QComposerLabel is returned as a QObject in the slot argument hence, we have resorted to 
        capturing the currently selected items in the composition.
        """
        selectedItems = self.composition().selectedComposerItems()
        
        if len(selectedItems) == 0:
            self._stdmItemPropDock.setWidget(None)
        
        elif len(selectedItems) == 1:
            composerItem = selectedItems[0]
            
            if composerItem.uuid() in self._widgetMappings:
                stdmWidget = self._widgetMappings[composerItem.uuid()]
                
                if stdmWidget == self._stdmItemPropDock.widget():
                    return
                
                else:
                    self._stdmItemPropDock.setWidget(stdmWidget)
                    
                #Playing it safe in applying the formatting for the editor controls where applicable
                itemFormatter = None
                if isinstance(composerItem,QgsComposerArrow):
                    itemFormatter = LineFormatter()
                elif isinstance(composerItem,QgsComposerLabel):
                    itemFormatter = DataLabelFormatter()
                elif isinstance(composerItem,QgsComposerMap):
                    itemFormatter = MapFormatter()
                        
                if itemFormatter != None:
                    itemFormatter.apply(composerItem,self,True)
                    
            else:
                self._stdmItemPropDock.setWidget(None)
            
        elif len(selectedItems) > 1:
            self._stdmItemPropDock.setWidget(None)
Exemple #26
0
class Main(plugin.Plugin):
    " dock Class "

    def initialize(self):
        " Init Class dock "
        self.dock = QDockWidget()
        self.dock.setFeatures(QDockWidget.DockWidgetFloatable
                              | QDockWidget.DockWidgetMovable)
        self.dock.setWindowTitle(__doc__)
        self.dock.setStyleSheet('QDockWidget::title{text-align: center;}')
        self.open = QAction(QIcon.fromTheme("document-open"), 'Open DIFF',
                            self)
        self.diff = QAction(QIcon.fromTheme("document-new"), 'Make DIFF', self)
        self.diff.triggered.connect(self.run_gui_and_get_results)
        self.save = QAction(QIcon.fromTheme("document-save"), 'Save DIFF',
                            self)
        self.save.triggered.connect(self.save_a_diff)
        self.patc = QAction(QIcon.fromTheme("document-edit"), 'PATCH it!',
                            self)
        self.patc.triggered.connect(lambda: QMessageBox.information(
            self.dock, __doc__,
            ' Sorry. This Feature is not ready yet !, thank you... '))
        QToolBar(self.dock).addActions(
            (self.open, self.diff, self.save, self.patc))
        try:
            self.factory = KPluginLoader("komparepart").factory()
            self.part = self.factory.create(self)
            self.dock.setWidget(self.part.widget())
            self.open.triggered.connect(lambda: self.part.openUrl(
                KUrl(
                    str(
                        QFileDialog.getOpenFileName(
                            self.dock, ' Open a DIFF file ',
                            path.expanduser("~"), ';;(*.diff)')))))
        except:
            self.dock.setWidget(
                QLabel(""" <center>
            <h3>ಠ_ಠ<br> ERROR: Please, install Kompare App ! </h3><br>
            <br><i> (Sorry, cant embed non-Qt Apps). </i><center>"""))

        self.misc = self.locator.get_service('misc')
        self.misc.add_widget(self.dock, QIcon.fromTheme("edit-select-all"),
                             __doc__)

    def run_gui_and_get_results(self):
        ' run_gui_and_get_results '
        gui = Diff_GUI()
        if gui.diff_path is not None and path.isfile(gui.diff_path) is True:
            self.part.openUrl(KUrl(str(gui.diff_path)))
        return run_gui.diff_path

    def save_a_diff(self):
        ' save a diff '
        out_file = path.abspath(
            str(
                QFileDialog.getSaveFileName(self.dock, 'Save a Diff file',
                                            path.expanduser("~"),
                                            ';;(*.diff)')))
        inp_file = file(
            str(QUrl(self.part.url()).toString()).replace('file://', ''),
            'r').read()
        end_file = file(out_file, 'w')
        end_file.write(inp_file)
        end_file.close()
Exemple #27
0
    slice_settings.inactiveCellsHidden.connect(viewer.hideInactiveCells)
    slice_settings.currentSliceChanged.connect(viewer.setCurrentSlice)

    slice_settings.toggleOrthographicProjection.connect(viewer.useOrthographicProjection)

    slice_settings.toggleLighting.connect(viewer.useLighting)
    slice_settings.colorScalesChanged.connect(viewer.changeColorScale)
    slice_settings.regionToggling.connect(viewer.useRegionScaling)
    slice_settings.toggleInterpolation.connect(viewer.useInterpolationOnData)
    slice_settings.mirrorX.connect(viewer.mirrorX)
    slice_settings.mirrorY.connect(viewer.mirrorY)
    slice_settings.mirrorZ.connect(viewer.mirrorZ)
    slice_settings.toggleFlatPolylines.connect(viewer.toggleFlatPolylines)


    dock_widget = QDockWidget("Settings")
    dock_widget.setObjectName("SliceSettingsDock")
    dock_widget.setWidget(slice_settings)
    dock_widget.setAllowedAreas(Qt.AllDockWidgetAreas)
    dock_widget.setFeatures(QDockWidget.NoDockWidgetFeatures)

    window.addDockWidget(Qt.LeftDockWidgetArea, dock_widget)


    window.setCentralWidget(viewer)

    window.show()
    window.activateWindow()
    window.raise_()
    app.exec_()
Exemple #28
0
class OrbitPlotMainWindow(QMainWindow):
    """
    the main window has three major widgets: current, orbit tabs and element
    editor.
    """
    def __init__(self, parent=None, machines=[], **kwargs):
        QMainWindow.__init__(self, parent)
        self.iqtApp = kwargs.get("iqt", None)

        self.setIconSize(QSize(32, 32))
        self.error_bar = True
        self._dlgOrbitCor = None
        # logging
        self.logdock = QDockWidget("Log")
        self.logdock.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        textedit = QPlainTextEdit(self.logdock)

        self.logger = logging.getLogger(__name__)

        self.guilogger = logging.getLogger("aphla.gui")
        # the "aphla" include lib part logging. When the lib is inside
        # QThread, logging message will be sent to TextEdit which is cross
        # thread.
        # self.guilogger = logging.getLogger("aphla")
        handler = QTextEditLoggingHandler(textedit)
        self.guilogger.addHandler(handler)
        self.guilogger.setLevel(logging.INFO)
        self.logdock.setWidget(textedit)
        self.logdock.setAllowedAreas(Qt.BottomDockWidgetArea)
        self.logdock.setFeatures(QDockWidget.DockWidgetMovable
                                 | QDockWidget.DockWidgetClosable)
        self.logdock.setFloating(False)
        self.logdock.setMinimumHeight(20)
        self.logdock.setMaximumHeight(100)
        self.logdock.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        self.logdock.resize(200, 60)
        #print self.logdock.sizeHint()
        self.addDockWidget(Qt.BottomDockWidgetArea, self.logdock)
        #print self.logdock.sizeHint()
        #print self.logdock.minimumSize()
        #print self.logdock.maximumSize()
        #self.logger.info("INFO")
        #self.logdock.setMinimumHeight(40)
        #self.logdock.setMaximumHeight(160)

        for msg in kwargs.get("infos", []):
            self.logger.info(msg)
        # dict of (machine, (lattice dict, default_lat, pvm))
        self._mach = dict([(v[0], (v[1], v[2], v[3])) for v in machines])
        for m, (lats, lat0, pvm) in self._mach.items():
            self.logger.info(
                "machine '%s' initialized: [%s]" %
                (m, ", ".join([lat.name for k, lat in lats.items()])))
            if pvm:
                for pv in pvm.dead():
                    self.logger.warn("'{0}' is disconnected.".format(pv))
        ## DCCT current plot
        #self.dcct = DcctCurrentPlot()
        #self.dcct.setMinimumHeight(100)
        #self.dcct.setMaximumHeight(150)

        #t0 = time.time()
        #t = np.linspace(t0 - 8*3600*24, t0, 100)
        #self.dcct.curve.t = t
        #v = 500*np.exp((t[0] - t[:50])/(4*3600*24))
        #self.dcct.curve.v = v.tolist()+v.tolist()
        #self.dcct.updatePlot()

        ## MDI area
        self.mdiarea = QMdiArea()
        self.connect(self.mdiarea, SIGNAL("subWindowActivated(QMdiSubWindow)"),
                     self.updateMachineLatticeNames)
        self.mdiarea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.physics = ApOrbitPhysics(self.mdiarea, iqt=self.iqtApp)
        self.live_orbit = True

        self.setCentralWidget(self.mdiarea)

        #self._elemed = ElementPropertyTabs()
        #self.elemeditor = ElementEditorDock(parent=self)
        #self.elemeditor.setAllowedAreas(Qt.RightDockWidgetArea)
        #self.elemeditor.setFeatures(QDockWidget.DockWidgetMovable|
        #                            QDockWidget.DockWidgetClosable)
        #self.elemeditor.setFloating(False)
        #self.elemeditor.setEnabled(False)
        #self.elemeditor.setMinimumWidth(400)
        #self.elemeditor.setWidget(self._elemed)
        #self.elemeditor.show()
        #self.elemeditor.hide()
        #self.connect(self.elemeditor,
        #             SIGNAL("elementChecked(PyQt_PyObject, bool)"),
        #             self.physics.elementChecked)
        #self.addDockWidget(Qt.RightDockWidgetArea, self.elemeditor)

        self.createMenuToolBar()

        # the first machine is the default
        self.machBox.addItems([v for v in self._mach.keys()])
        self.reloadLatticeNames(self.machBox.currentText())
        self.connect(self.machBox, SIGNAL("currentIndexChanged(QString)"),
                     self.reloadLatticeNames)

        # update at 1/2Hz
        self.dt, self.itimer = 1500, 0
        #self.timerId = None
        self.timerId = self.startTimer(self.dt)

        self.vbpm = None
        self.statusBar().showMessage("Welcome")

        #self.initMachine("nsls2v2")
        #self._newVelemPlot("V2SR", aphla.machines.HLA_VBPM, 'x',
        #                   "H Orbit", c = None)
        #print "Thread started", self.machinit.isRunning()

        #self.newElementPlots("BPM", "x, y")
        #self.newElementPlot("BPM", "y")
        #self.newElementPlot("HCOR", "x")
        #self.newElementPlot("VCOR", "y")
        #self.newElementPlot("QUAD", "b1")
        #self.newElementPlot("SEXT", "b2")

    def updateMachineLatticeNames(self, wsub):
        i = self.machBox.findText(wsub.machlat[0])
        self.machBox.setCurrentIndex(i)
        self.reloadLatticeNames(wsub.machlat[0])

    def reloadLatticeNames(self, mach):
        self.latBox.clear()
        cur_mach = str(self.machBox.currentText())
        lats, lat0, pvm = self._mach.get(cur_mach, ({}, None, None))
        self.latBox.addItems([lat for lat in lats.keys()])
        if lat0:
            i = self.latBox.findText(lat0.name)
            self.latBox.setCurrentIndex(i)

    def closeEvent(self, event):
        self.physics.close()
        self.mdiarea.closeAllSubWindows()
        event.accept()

    def createMenuToolBar(self):
        #
        # file menu
        #
        #self.machMenu = self.menuBar().addMenu("&Machines")
        #self.connect(self.machMenu, SIGNAL("aboutToShow()"),
        #             self.updateMachMenu)
        self.openMenu = self.menuBar().addMenu("&Open")
        self.openMenu.addAction("New Plot ...", self.openNewPlot)
        self.openMenu.addAction("New Tune Plot", self.openTunePlot)
        self.openMenu.addAction("New BPM Plot",
                                partial(self.newElementPlots, "BPM", "x,y"))
        self.openMenu.addAction("New HCOR Plot",
                                partial(self.newElementPlots, "HCOR", "x"))
        self.openMenu.addAction("New VCOR Plot",
                                partial(self.newElementPlots, "VCOR", "y"))

        self.openMenu.addSeparator()
        self.openMenu.addAction("Open ORM", self.loadOrm)

        self.openMenu.addSeparator()
        self.openMenu.addAction("Save Lattice ...", self.saveSnapshot)

        fileQuitAction = QAction(QIcon(":/file_quit.png"), "&Quit", self)
        fileQuitAction.setShortcut("Ctrl+Q")
        fileQuitAction.setToolTip("Quit the application")
        fileQuitAction.setStatusTip("Quit the application")
        #fileQuitAction.setIcon(Qt.QIcon(":/filequit.png"))
        self.connect(fileQuitAction, SIGNAL("triggered()"), self.close)
        self.openMenu.addAction(fileQuitAction)

        # view
        self.viewMenu = self.menuBar().addMenu("&View")

        mkmenu = QMenu("&Mark", self.viewMenu)
        for fam in ["BPM", "COR", "QUAD", "SEXT", "INSERTION"]:
            famAct = QAction(fam, self)
            famAct.setCheckable(True)
            self.connect(famAct, SIGNAL("toggled(bool)"), self.click_markfam)
            mkmenu.addAction(famAct)
        #

        # errorbar
        #viewErrorBarAction = QAction(QIcon(":/view_errorbar.png"),
        #                            "Errorbar", self)
        #viewErrorBarAction.setCheckable(True)
        #viewErrorBarAction.setChecked(True)
        #self.connect(viewErrorBarAction, SIGNAL("toggled(bool)"),
        #             self.errorBar)
        #
        #zoomM = QMenu("Zoom", self.viewMenu)

        #
        #
        #drift_from_now = QAction("Drift from Now", self)
        #drift_from_now.setCheckable(True)
        #drift_from_now.setShortcut("Ctrl+N")
        #drift_from_golden = QAction("Drift from Golden", self)
        #drift_from_golden.setCheckable(True)
        #drift_from_none = QAction("None", self)
        #drift_from_none.setCheckable(True)

        #self.viewMenu.addAction(viewLiveAction)
        #self.viewMenu.addAction(viewSingleShotAction)
        #self.viewMenu.addSeparator()

        #self.viewMenu.addAction(drift_from_now)
        #self.viewMenu.addAction(drift_from_golden)
        #self.viewMenu.addAction(drift_from_none)
        #self.viewMenu.addAction(viewAutoScale)
        #self.viewMenu.addAction(viewErrorBarAction)
        #self.viewMenu.addSeparator()

        self.viewMenu.addMenu(mkmenu)

        #drift_group = QActionGroup(self)
        #drift_group.addAction(drift_from_none)
        #drift_group.addAction(drift_from_now)
        #drift_group.addAction(drift_from_golden)
        #drift_from_none.setChecked(True)

        sep = self.viewMenu.addSeparator()
        #sep.setText("Drift")
        #self.connect(drift_from_now, SIGNAL("triggered()"), self.setDriftNow)
        #self.connect(drift_from_none, SIGNAL("triggered()"), self.setDriftNone)
        #self.connect(drift_from_golden, SIGNAL("triggered()"),
        #             self.setDriftGolden)

        #viewStyle = QMenu("Line Style", self.viewMenu)
        #for act in ["Increase Point Size", "Decrease Point Size", None,
        #            "NoCurve", "Lines", "Sticks", None,
        #            "Solid Line", "Dashed Line", "Dotted Line", None,
        #            "Increase Line Width", "Decrease Line Width", None,
        #            "NoSymbol", "Ellipse", "Rect", "Diamond", "Triangle",
        #            "Cross", "XCross", "HLine", "VLine",
        #            "Star1", "Star2", "Hexagon", None,
        #            "Red", "Blue", "Green"]:
        #    if act is None:
        #        viewStyle.addSeparator()
        #    else:
        #        viewStyle.addAction(act, self.setPlotStyle)
        #self.viewMenu.addMenu(viewStyle)

        #self.viewMenu.addSeparator()
        #self.viewMenu.addAction(viewZoomOut15Action)
        #self.viewMenu.addAction(viewZoomIn15Action)
        #self.viewMenu.addAction(viewZoomAutoAction)
        #self.viewMenu.addSeparator()
        self.viewMenu.addAction("ORM SV", self.plotSVD)
        # a bug in PyQwt5 for datetime x-axis, waiting for Debian 7
        #self.viewMenu.addAction(viewDcct)
        #for ac in self.viewMenu.actions(): ac.setDisabled(True)

        #
        self.controlMenu = self.menuBar().addMenu("&Tools")

        self.controlMenu.addAction(QIcon(":/control_choosebpm.png"),
                                   "En-/Disable BPM",
                                   partial(chooseElement, 'BPM'))
        self.controlMenu.addAction("En-/Disable COR",
                                   partial(chooseElement, 'COR'))
        #self.controlMenu.addAction(controlResetPvDataAction)
        self.controlMenu.addSeparator()
        self.controlMenu.addAction("Lattice Snapshot ...", self.openSnapshot)
        #self.controlMenu.addAction(controlZoomInPlot1Action)
        #self.controlMenu.addAction(controlZoomOutPlot1Action)
        #self.controlMenu.addAction(controlZoomInPlot2Action)
        #self.controlMenu.addAction(controlZoomOutPlot2Action)
        self.controlMenu.addSeparator()
        self.controlMenu.addAction("Correct Hor. orbit",
                                   partial(aphla.correctOrbit, plane="H"))
        self.controlMenu.addAction("Correct Vert. orbit",
                                   partial(aphla.correctOrbit, plane="V"))
        self.controlMenu.addAction(QIcon(":/control_corrorbit.png"),
                                   "Correct orbit",
                                   partial(aphla.correctOrbit, plane="HV"))
        #steer_orbit.setDisabled(True)
        self.controlMenu.addAction("Local Bump ...", self.createLocalBump)
        self.controlMenu.addAction("Element Editor ...",
                                   self.showElementEditor)
        self.controlMenu.addSeparator()
        self.controlMenu.addAction("meas Beta", self.physics.measBeta)
        self.controlMenu.addAction("meas Dispersion",
                                   self.physics.measDispersion)
        self.controlMenu.addAction("beam based alignment", self.runBba)
        #for ac in self.controlMenu.actions(): ac.setDisabled(True)

        # Window
        self.windowMenu = self.menuBar().addMenu("&Windows")
        #self.windowMenu.addAction(self.elemeditor.toggleViewAction())
        self.windowMenu.addAction(self.logdock.toggleViewAction())
        #viewDcct = QAction("Beam Current", self)
        #viewDcct.setCheckable(True)
        #viewDcct.setChecked(True)
        #self.connect(viewDcct, SIGNAL("toggled(bool)"), self.dcct.setVisible)
        #self.windowMenu.addAction(viewDcct)
        self.windowMenu.addSeparator()
        self.windowMenu.addAction("Cascade", self.mdiarea.cascadeSubWindows)
        self.windowMenu.addAction("Tile", self.mdiarea.tileSubWindows)
        self.windowMenu.addAction("Tile Horizontally",
                                  self.tileSubWindowsHorizontally)
        # "ctrl+page up", "ctrl+page down"
        self.windowMenu.addAction("Previous",
                                  self.mdiarea.activatePreviousSubWindow,
                                  "Ctrl+Left")
        self.windowMenu.addAction("Next", self.mdiarea.activateNextSubWindow,
                                  "Ctrl+Right")
        self.windowMenu.addSeparator()

        # debug
        self.debugMenu = self.menuBar().addMenu("&Debug")
        self.debugMenu.addAction("_Reset Correctors_", self._reset_correctors)
        self.debugMenu.addAction("_Reset Quadrupoles_",
                                 self._reset_quadrupoles)
        self.debugMenu.addAction("_Random V Kick_", self._random_vkick)
        self.debugMenu.addAction("_Random H Kick_", self._random_hkick)
        #for ac in self.debugMenu.actions(): ac.setDisabled(True)

        # help
        self.helpMenu = self.menuBar().addMenu("&Help")
        self.helpMenu.addAction("About mleap", self.showAbout)

        #toolbar
        machToolBar = self.addToolBar("Machines")
        self.machBox = QtGui.QComboBox()
        self.latBox = QtGui.QComboBox()
        #self.connect(self.latBox, SIGNAL("currentIndexChanged(QString)"),
        #             self.__setLattice)
        machToolBar.addWidget(self.machBox)
        machToolBar.addWidget(self.latBox)
        #toolbar = QToolBar(self)
        #self.addToolBar(toolbar)
        #fileToolBar = self.addToolBar("File")
        #fileToolBar.setObjectName("FileToolBar")
        #fileToolBar.addAction(fileQuitAction)

        #
        viewToolBar1 = self.addToolBar("Live View")
        #viewToolBar.setObjectName("ViewToolBar")
        #viewToolBar.addAction(viewZoomOut15Action)
        #viewToolBar.addAction(viewZoomIn15Action)
        #viewToolBar.addAction(viewZoomAutoAction)
        #viewToolBar1.addAction(viewLiveAction)
        #viewToolBar1.addAction(viewSingleShotAction)
        #viewToolBar1.addSeparator()
        viewToolBar1.addAction(QIcon(":/new_bpm.png"), "Orbits",
                               partial(self.newElementPlots, "BPM", "x,y"))
        viewToolBar1.addAction(QIcon(":/new_cor.png"), "Correctors",
                               partial(self.newElementPlots, "COR", "x,y"))
        viewToolBar1.addAction(QIcon(":/new_quad.png"), "Quadrupoles",
                               partial(self.newElementPlots, "QUAD", "b1"))
        viewToolBar1.addAction(QIcon(":/new_sext.png"), "Sextupoles",
                               partial(self.newElementPlots, "SEXT", "b2"))
        #viewToolBar.addAction(viewErrorBarAction)
        #viewToolBar.addAction(QWhatsThis.createAction(self))

        #viewToolBar2 = self.addToolBar("Scale Plot")
        #zoomActions = [(":/view_zoom_xy.png", "Fit", self.scalePlot),
        #               (None, None, None),
        #               (":/view_zoom_y.png", "Fit In Y", self.scalePlot),
        #               (":/view_zoomin_y.png", "Zoom In Y", self.scalePlot),
        #               (":/view_zoomout_y.png", "Zoom Out Y", self.scalePlot),
        #               (":/view_move_up.png", "Move Up", self.scalePlot),
        #               (":/view_move_down.png", "Move Down", self.scalePlot),
        #               (None, None, None),
        #               (":/view_zoom_x.png", "Fit In X", self.scalePlot),
        #               (":/view_zoomin_x.png", "Zoom In X", self.scalePlot),
        #               (":/view_zoomout_x.png", "Zoom Out X", self.scalePlot),
        #               (":/view_move_left.png", "Move Left", self.scalePlot),
        #               (":/view_move_right.png", "Move Right", self.scalePlot),
        #               ]
        #for ico,name,hdl in zoomActions:
        #    if hdl is None: continue
        #    viewToolBar2.addAction(QIcon(ico), name, hdl)

        controlToolBar = self.addToolBar("Control")
        controlToolBar.addAction(QIcon(":/control_orbitcor.png"),
                                 "Correct Orbit", aphla.correctOrbit)
        controlToolBar.addAction(QIcon(":/control_localbump.png"),
                                 "Local Bump ...", self.createLocalBump)
        #controlToolBar.addAction(controlResetPvDataAction)

    def showAbout(self):
        QMessageBox.about(
            self, self.tr("mleap"),
            (self.tr("""<b>Machine/Lattice Editor And Plotter</b> v %1
                <p>Copyright &copy; Lingyun Yang, BNL, 2013-2014. 
                All rights reserved.
                <p>This application can be used to perform
                high level accelerator controls.
                <p>Python %2 - Qt %3 - PyQt %4 
                on %5""").arg(aphla.version.version).arg(
                platform.python_version()).arg(QtCore.QT_VERSION_STR).arg(
                    QtCore.PYQT_VERSION_STR).arg(platform.system())))

    def showElementEditor(self):
        mach, lat = self.getCurrentMachLattice()
        ed = ElementEditor(lat, parent=self)
        ed.setWindowFlags(Qt.Window)
        ed.setAttribute(Qt.WA_DeleteOnClose)
        ed.show()

    def getCurrentMachLattice(self, cadata=False):
        """return the current machine name and lattice object"""
        mach = str(self.machBox.currentText())
        latname = str(self.latBox.currentText())
        lat_dict, lat0, pvm = self._mach[mach]
        if not cadata:
            return mach, lat_dict[latname]
        else:
            return mach, lat_dict[latname], pvm

    def newElementPlots(self, elem, fields, **kw):
        self.logger.info("new plots: %s %s" % (elem, fields))
        _mach, _lat, _pvm = self.getCurrentMachLattice(cadata=True)
        mach, lat = kw.get("machlat", (_mach, _lat))
        handle = kw.get("handle", "readback")
        elems = lat.getElementList(elem)
        x, pvs = [], []
        field_list = re.findall(r'[^ ,]+', fields)
        for fld in field_list:
            si, pvsi = [], []
            for e in elems:
                if not e.isEnabled(): continue
                epv = e.pv(field=fld, handle=handle)
                if not epv: continue
                pvsi.append(epv[0])
                si.append(e.sb)
            x.append(si)
            pvs.append(pvsi)

        if not pvs:
            self.logger.error("no data found for elements '{0}' "
                              "and field '{1}'".format(elem, field))
            return

        p = ApMdiSubPlot(pvs=pvs,
                         x=x,
                         labels=["%s.%s" % (elem, fld) for fld in field_list],
                         magprof=lat.getBeamlineProfile(),
                         iqt=self.iqtApp,
                         **kw)
        #QObject.installEventFilter(p.aplot)
        #p.data = ManagedPvData(pvm, s, pvs, element=elemnames,
        #                       label="{0}.{1}".format(elem,field))
        p.setAttribute(Qt.WA_DeleteOnClose)
        str_elem = "{0}".format(elem)
        if len(str_elem) > 12: str_elem = str_elem[:9] + "..."
        str_field = "{0}".format(fields)
        if len(str_field) > 12: str_field = str_field[:9] + "..."
        p.setWindowTitle("[%s.%s] %s %s" %
                         (mach, lat.name, str_elem, str_field))
        self.connect(p, SIGNAL("elementSelected(PyQt_PyObject)"),
                     self.elementSelected)
        self.connect(p, SIGNAL("destroyed()"), self.subPlotDestroyed)
        #p.updatePlot()
        # set the zoom stack
        #p.aplot.setErrorBar(self.error_bar)
        #p.wid.autoScaleXY()
        #p.aplot.replot()
        self.mdiarea.addSubWindow(p)
        #print "Show"
        p.show()

        ##print "Enable the buttons"
        #if len(self.mdiarea.subWindowList()) > 0:
        #    self.elemeditor.setEnabled(True)

    def subPlotDestroyed(self):
        #if len(self.mdiarea.subWindowList()) == 0:
        #    self.elemeditor.setEnabled(False)
        pass

    def loadOrm(self):
        fileName = QtGui.QFileDialog.getOpenFileName(
            self, "Open Orbit Response Matrix", "",
            "ORM Files (*.h5 *.hdf5);;Text File (*.txt);;All Files(*)")
        fileName = str(fileName)
        try:
            m = np.loadtxt(fileName)
        except:
            QMessageBox.critical(self, "Abort", "Invalid matrix data")
            return
        mach, lat = self.getCurrentMachLattice()
        # assuming we already have the PV, name, field but just want to
        # replace the matrix elements.
        assert np.shape(m) == np.shape(lat.ormdata.m)
        nx, ny = np.shape(lat.ormdata.m)
        for i in range(nx):
            for j in range(ny):
                lat.ormdata.m[i, j] = m[i, j]

    def saveSnapshot(self):
        latdict = dict([(k, v[0]) for k, v in self._mach.items()])
        mach, lat = self.getCurrentMachLattice()
        snapdlg = SaveSnapshotDialog(latdict, mach)
        snapdlg.exec_()

    def saveLatSnapshot(self):
        mach, lat = self.getCurrentMachLattice()
        dpath = self._prepare_parent_dirs(mach)
        if not dpath:
            QMessageBox.warning(self, "Abort", "Aborted")
            return
        dt = datetime.datetime.now()
        fname = os.path.join(
            dpath,
            dt.strftime("snapshot_%d_%H%M%S_") + lat.name + ".hdf5")
        fileName = QtGui.QFileDialog.getSaveFileName(
            self, "Save Lattice Snapshot Data", fname,
            "Data Files (*.h5 *.hdf5);;All Files(*)")
        fileName = str(fileName)
        if not fileName: return
        aphla.catools.save_lat_epics(fileName, lat, mode='a')
        self.logger.info("snapshot created '%s'" % fileName)

    def saveMachSnapshot(self):
        mach, lat = self.getCurrentMachLattice()
        dpath = self._prepare_parent_dirs(mach)
        if not dpath:
            QMessageBox.warning(self, "Abort", "Aborted")
            return
        dt = datetime.datetime.now()
        fname = os.path.join(dpath, dt.strftime("snapshot_%d_%H%M%S.hdf5"))
        fileName = QtGui.QFileDialog.getSaveFileName(
            self, "Save Lattice Snapshot Data", fname,
            "Data Files (*.h5 *.hdf5);;All Files(*)")
        if not fileName: return
        fileName = str(fileName)
        import h5py
        f = h5py.File(str(fileName), 'w')
        f.close()
        self.logger.info("clean snapshot file created: '%s'" % fileName)
        for k, lat in self._mach[mach][0].items():
            aphla.catools.save_lat_epics(fileName, lat, mode='a')
            self.logger.info("lattice snapshot appended for '%s'" % lat.name)

    def openSnapshot(self):
        #self.logger.info("loading snapshot?")
        latdict = dict([(k, v[0]) for k, v in self._mach.items()])
        mach, lat = self.getCurrentMachLattice()
        lv = LatSnapshotMain(self, latdict, mach, self.logger)
        lv.setWindowFlags(Qt.Window)
        #self.logger.info("initialized")
        #lv.loadLatSnapshotH5()
        lv.exec_()

    def openTunePlot(self):
        mach, lat = self.getCurrentMachLattice()
        nu = lat.getElementList('tune')
        pvs = [(e.pv(field="x",
                     handle="readback")[0], e.pv(field="y",
                                                 handle="readback")[0])
               for e in nu]
        labels = [e.name for e in nu]
        twiss = lat.getElementList("VA")
        pvs.extend([(e.pv(field="nux", handle="readback")[0],
                     e.pv(field="nuy", handle="readback")[0]) for e in twiss])
        labels.extend([e.name for e in twiss])

        p = ApMdiSubPlot(pvs=pvs, labels=labels, dtype="Tunes")
        #QObject.installEventFilter(p.aplot)
        #p.data = ManagedPvData(pvm, s, pvs, element=elemnames,
        #                       label="{0}.{1}".format(elem,field))
        p.setAttribute(Qt.WA_DeleteOnClose)
        p.setWindowTitle("[%s.%s] Tunes" % (mach, lat.name))
        self.connect(p, SIGNAL("elementSelected(PyQt_PyObject)"),
                     self.elementSelected)
        self.connect(p, SIGNAL("destroyed()"), self.subPlotDestroyed)
        #p.updatePlot()
        # set the zoom stack
        #p.aplot.setErrorBar(self.error_bar)
        #p.wid.autoScaleXY()
        #p.aplot.replot()
        self.mdiarea.addSubWindow(p)
        #print "Show"
        p.show()

    def openNewPlot(self):
        mach, lat = self.getCurrentMachLattice()
        fl = QtGui.QFormLayout()
        fl.addRow("Machine", QtGui.QLabel("%s" % mach))
        fl.addRow("Lattice", QtGui.QLabel("%s" % lat.name))
        elem, fld = QtGui.QLineEdit(), QtGui.QLineEdit()
        fl.addRow("Elements", elem)
        fl.addRow("Field", fld)
        dtype = QtGui.QComboBox()
        for tx in ["Array", "Waveform", "Time Series"]:
            dtype.addItem(tx)
        fl.addRow("Data Type", dtype)
        dlg = QtGui.QDialog()
        bx = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok
                                    | QtGui.QDialogButtonBox.Cancel)
        self.connect(bx, SIGNAL("accepted()"), dlg.accept)
        self.connect(bx, SIGNAL("rejected()"), dlg.reject)
        h1 = QtGui.QHBoxLayout()
        h1.addStretch()
        h1.addWidget(bx)
        v1 = QtGui.QVBoxLayout()
        v1.addLayout(fl)
        v1.addLayout(h1)
        dlg.setLayout(v1)

        if dlg.exec_():
            self.newElementPlots(str(elem.text()),
                                 str(fld.text()),
                                 machlat=(mach, lat),
                                 dtype=str(dtype.currentText()))

    def click_markfam(self, on):
        famname = self.sender().text()
        mks = []
        mach, lat = self.getCurrentMachLattice()
        # need to convert to python str
        for elem in lat.getElementList(str(famname)):
            if elem.family != famname: continue
            if elem.virtual: continue
            mks.append([elem.name, 0.5 * (elem.sb + elem.se)])

        for w in self.mdiarea.subWindowList():
            w.setMarkers(mks, on)
        #print self._machlat.keys()

    def _reset_correctors(self):
        self.logger.info("reset correctors")
        aphla.hlalib._reset_trims()

    def _reset_quadrupoles(self):
        self.logger.info("reset quadrupoles")
        aphla.hlalib._reset_quad()

    def _random_hkick(self):
        mach, lat = self.getCurrentMachLattice()
        hcors = lat.getElementList('HCOR')
        for k in range(3):
            i = np.random.randint(len(hcors))
            self.logger.info("Setting {0}/{1} HCOR".format(i, len(hcors)))
            hcors[i].x += np.random.rand() * 2e-6

    def _random_vkick(self):
        mach, lat = self.getCurrentMachLattice()
        cors = lat.getElementList('VCOR')
        for k in range(3):
            i = np.random.randint(len(cors))
            cors[i].y += np.random.rand() * 1e-6
            self.logger.info("increased kicker '{0}' by 1e-7 ({1} {2})".format(
                cors[i].name, cors[i].y, cors[i].getUnit('y', None)))

    def viewDcctPlot(self, on):
        self.dcct.setVisible(on)

    def liveData(self, on):
        """Switch on/off live data taking"""
        self.live_orbit = on

    def scalePlot(self):
        w = self.mdiarea.currentSubWindow()
        if not w: return
        st, p = self.sender().text(), w.aplot
        if st == "Fit":
            p.scaleXBottom()
            p.scaleYLeft()
            # a hack
            bound = p.curvesBound()
            p.zoomer1.setZoomStack([bound])
        elif st == "Fit In Y":
            p.scaleYLeft()
        elif st == "Zoom In Y":
            p.scaleYLeft(1. / 1.5)
        elif st == "Zoom Out Y":
            p.scaleYLeft(1.5)
        elif st == "Move Up":
            p.moveCurves(Qwt.QwtPlot.yLeft, 0.8)
        elif st == "Move Down":
            p.moveCurves(Qwt.QwtPlot.yLeft, -0.8)
        elif st == "Fit In X":
            p.scaleXBottom()
        elif st == "Zoom In X":
            p.scaleXBottom(1.0 / 1.5)
        elif st == "Zoom Out X":
            p.scaleXBottom(1.5)
        elif st == "Move Left":
            p.moveCurves(Qwt.QwtPlot.xBottom, 0.8)
        elif st == "Move Right":
            p.moveCurves(Qwt.QwtPlot.xBottom, -0.8)
        else:
            self.logger.error("unknow action '{0}'".format(st))

    def getVisibleRange(self):
        w = self.mdiarea.currentSubWindow()
        if not w:
            mach, lat = self.getCurrentMachLattice()
            self.logger.warn(
                "no active plot, use full range of {0}.{1}".format(
                    mach, lat.name))
            return lat.getLocationRange()
        else:
            return w.currentXlim()

    def getVisibleElements(self, elemname, sb=None, se=None):
        w = self.mdiarea.currentSubWindow()
        mach, lat = self.getCurrentMachLattice()
        elems = lat.getElementList(elemname)
        if sb is not None:
            elems = [e for e in elems if e.sb >= sb]
        if se is not None:
            elems = [e for e in elems if e.se <= se]

        self.logger.info("searching for '{0}' in range [{1}, {2}]".format(
            elemname, sb, se))

        return elems

    def timerEvent(self, e):
        if e.timerId() != self.timerId: return

        #if not self.elemeditor.isHidden():
        #    self.elemeditor.updateModelData()

        #if self.live_orbit:
        #    self.itimer += 1
        #    #self.updatePlots()
        #    #self.updateStatus()
        #    for w in self.mdiarea.subWindowList():
        #        if not isinstance(w, ApMdiSubPlot): continue
        #        if not w.live: continue
        #        w.updatePlot()
        #    self.statusBar().showMessage("plot updated: {0}".format(
        #        time.strftime("%F %T")))
        #else:
        #    self.statusBar().showMessage("live update disabled")

    def singleShot(self):
        for w in self.mdiarea.subWindowList():
            if not isinstance(w, ApMdiSubPlot): continue
            w.updatePlot()

        self.statusBar().showMessage("plot updated: {0}".format(
            time.strftime("%F %T")))

    def elementSelected(self, elems):
        """this action is ignored"""
        mach, lat, elemnames = elems
        #_lat = self._machlat[mach][lat]
        self.logger.info("element selected")

        #elemobjs = _lat.getElementList(elemnames)
        #self._elemed.addElements(elemobjs)

    def activeOrbitPlot(self, field):
        mach = str(self.machBox.currentText())
        lat = str(self.latBox.currentText())
        for w in self.mdiarea.subWindowList():
            #print w.machine(), w.lattice(), w.data.yfield
            if not isinstance(w, ApMdiSubPlot): continue
            if w.machine() != mach: continue
            if w.lattice() != lat: continue
            if w.data.yfield != field: continue
            return w

        return None

    def createLocalBump_(self):
        """create local bump"""
        if self._dlgOrbitCor is None:
            bpms = ap.getElements("BPM")
            cors = ap.getElements("COR")
            self._dlgOrbitCor = OrbitCorrDlg(bpms, cors)
            #corbitdlg.resize(600, 500)
            self._dlgOrbitCor.setWindowTitle("Create Local Bump")
        self._dlgOrbitCor.show()
        self._dlgOrbitCor.raise_()
        self._dlgOrbitCor.activateWindow()

    def createLocalBump(self):
        """create local bump"""
        bpms = ap.getElements("BPM")
        cors = ap.getElements("COR")
        dlgOrbitCor = OrbitCorrDlg(bpms, cors, parent=self)
        #corbitdlg.resize(600, 500)
        dlgOrbitCor.setWindowTitle("Create Local Bump")
        dlgOrbitCor.show()
        dlgOrbitCor.raise_()
        dlgOrbitCor.activateWindow()
        dlgOrbitCor.setAttribute(Qt.WA_DeleteOnClose)

    def runBba(self):
        mach, lat = self.getCurrentMachLattice()
        bpms = [
            e for e in lat.getElementList('BPM')
            if e not in self.physics.deadelems
        ]
        self.physics.runBba(bpms)

    def plotSVD(self):
        mach, lat = self.getCurrentMachLattice()
        if not lat.ormdata:
            QMessageBox.critical(self, "ORM SVD",
                                 "machine '%s' ORM data is not available" % \
                                 mach,
                                 QMessageBox.Ok)
            return
        m, brec, trec = lat.ormdata.getMatrix(None,
                                              None,
                                              full=False,
                                              ignore=self.getDeadElements())
        U, s, V = np.linalg.svd(m, full_matrices=True)
        #print np.shape(s), s
        self.sp = ApSvdPlot(s)
        self.sp.show()

    def tileSubWindowsHorizontally(self):
        pos = QtCore.QPoint(0, 0)
        subwins = self.mdiarea.subWindowList()
        for w in subwins:
            height = self.mdiarea.height() / len(subwins)
            rect = QtCore.QRect(0, 0, self.mdiarea.width(), height)
            w.setGeometry(rect)
            w.move(pos)
            pos.setY(pos.y() + w.height())
Exemple #29
0
class GUI(QMainWindow):
    def __init__(self, debug=False):
        QMainWindow.__init__(self)
        # initialise filename
        self.filename = None
        # make the data store
        from xmlstore import Store
        self.store = Store(debug=debug)
        # view the current table
        self.tableView = TableView(self)
        #self.tableView.setDragEnabled(True);
        #self.tableView.setDragDropMode(QAbstractItemView.InternalMove)
        #self.tableView.setAcceptDrops(True);
        #self.tableView.setDropIndicatorShown(True);
        self.tableView.verticalHeader().sectionMoved.connect(self.sectionMoved)
        self.tableView.verticalHeader().setMovable(True)

        self.setCentralWidget(self.tableView)
        # add a custom delegate to it
        self.delegate = ComboBoxDelegate()
        self.tableView.setItemDelegate(self.delegate)
        # dock the table selection on the left
        self.dock1 = QDockWidget(self)
        self.listView = ListView(self)
        self.listView.setDragEnabled(True)
        self.listView.setDragDropMode(QAbstractItemView.InternalMove)
        self.listView.setDropIndicatorShown(True)
        self.dock1.setWidget(self.listView)
        self.dock1.setFeatures(QDockWidget.DockWidgetFloatable
                               | QDockWidget.DockWidgetMovable)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.dock1)
        # connect it to the populate method
        self.connect(self.listView,
                     SIGNAL('activated ( const QModelIndex & )'),
                     self.populate)
        self.connect(self.listView, SIGNAL('clicked ( const QModelIndex & )'),
                     self.populate)
        # dock the undoView on the left
        self.dock2 = QDockWidget(self)
        self.undoView = QUndoView(self.store.stack)
        self.dock2.setWidget(self.undoView)
        self.dock2.setFeatures(QDockWidget.DockWidgetFloatable
                               | QDockWidget.DockWidgetMovable)
        self.dock2.setWindowTitle('Undo Stack')
        self.addDockWidget(Qt.LeftDockWidgetArea, self.dock2)
        # create a menubar
        self.menu = self.menuBar()
        # create file menu headings
        self.menuFile = self.menu.addMenu('File')
        self.menuFile.addAction('New', self.New).setShortcut('CTRL+N')
        self.menuFile.addAction('Open...', self.Open).setShortcut('CTRL+O')
        self.menuFile.addAction('Reload', self.Reload)
        self.menuFile.addAction('Save', self.Save).setShortcut('CTRL+S')
        self.menuFile.addAction('Save As...', self.SaveAs)
        self.menuFile.addSeparator()
        self.menuFile.addAction('Set Architecture...', self.setArch)
        self.menuFile.addSeparator()
        self.menuFile.addAction('Quit', self.closeEvent).setShortcuts(
            ['CTRL+Q', 'ALT+F4'])
        # create edit menu headings
        self.menuEdit = self.menu.addMenu('Edit')
        self.menuEdit.addAction('Insert Row',
                                self.tableView.insertRow).setShortcut('CTRL+I')
        self.menuEdit.addAction(
            'Insert Row Under',
            self.tableView.insertRowUnder).setShortcut('CTRL+U')
        self.menuEdit.addAction('Remove Row', self.tableView.removeRow)
        self.menuEdit.addSeparator()
        self.menuEdit.addAction('Cut',
                                self.tableView.cut).setShortcut('CTRL+X')
        self.menuEdit.addAction('Copy', self.tableView.copy).setShortcuts(
            ['CTRL+C', 'CTRL+INS'])
        self.menuEdit.addAction('Paste', self.tableView.paste).setShortcuts(
            ['CTRL+V', 'SHIFT+INS'])
        self.menuEdit.addAction('Clear',
                                self.tableView.menuClear).setShortcut('CTRL+D')
        self.menuEdit.addSeparator()
        self.menuEdit.addAction('Fill Cells',
                                self.tableView.fillCells).setShortcut('CTRL+L')
        self.menuEdit.addAction(
            'Fill Cells and Increment',
            self.tableView.fillCellsInc).setShortcut('CTRL+R')
        self.menuEdit.addAction(
            'Python Code...', self.tableView.pythonCode).setShortcut('CTRL+P')
        self.tableView.codeBox = pythonCode()
        self.menuEdit.addSeparator()
        self.menuEdit.addAction('Undo', self.store.stack,
                                SLOT('undo()')).setShortcut('CTRL+Z')
        self.menuEdit.addAction('Redo', self.store.stack,
                                SLOT('redo()')).setShortcut('CTRL+SHIFT+Z')
        # create component menu
        self.menuComponents = self.menu.addMenu('Components')
        self.resize(QSize(1000, 500))

    def Save(self):
        # save menu command
        self.SaveAs(self.filename)

    def setArch(self):
        arch = self.store.getArch()
        arch, ok = QInputDialog.getText(self, 'Architecture Dialog',
                                        'Enter Architecture', QLineEdit.Normal,
                                        arch)
        arch = str(arch)
        if ok:
            self.store.setArch(arch)

    def SaveAs(self, filename=''):
        # save as menu command
        if filename == '':
            filename = str(QFileDialog.getSaveFileName())
        if filename != '':
            self.filename = filename
            self.store.Save(filename)
            self._setClean()

    def Reload(self):
        if self.filename is not None:
            self.Open(self.filename, name=self.tablename)

    def Open(self, filename='', name=None):
        # make sure the user is sure if there are unsaved changes
        if self.__prompt_unsaved() == QMessageBox.Cancel:
            return
        # ask for a filename
        if filename == '':
            filename = str(
                QFileDialog.getOpenFileName(
                    filter="Xml Files (*.xml);;All files (*.*)"))
        if filename == '':
            return
        # store the filename
        self.filename = filename
        # tell the store to open a new set of tables
        try:
            problems, warnings = self.store.Open(filename)
            if problems:
                errorstr = 'Can\'t load all object types: ' + ', '.join(
                    problems)
                QMessageBox.warning(self, 'Open Error', errorstr)
            if warnings:
                errorstr = \
                    'The following warnings were generated:\n' + \
                    '\n'.join(warnings)
                QMessageBox.warning(self, 'Open Warnings', errorstr)
        except Exception, e:
            x = formLog(
                'An error ocurred. Make sure all the modules listed '
                'in RELEASE files are built. Check the text below for '
                'details:\n\n' + traceback.format_exc(), self)
            x.show()
            return
        # populate
        self.setWindowTitle('XEB - %s[*]' % filename)
        self.listView.clear()
        for t in self.store.getTableNames():
            self.__insertListViewItem(t)
        self.__populateMenu()
        self.populate(name=name)
        self._setClean()
Exemple #30
0
class DBMainWindow(QMainWindow):
    """Main Window and Portal
    
    .. todo:: Remember the dock position
    """
    
    W_NAME =  "DBMainWindow"

    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)

        
        
        self.db = None
        
        self.setWindowTitle("PyQtDb")
        self.setWindowIcon(Ico.icon(Ico.FavIcon))

        self.setMinimumWidth(800)
        self.setMinimumHeight(800)
        
        """
        topBar = QToolBar()
        self.addToolBar(Qt.TopToolBarArea, topBar)
        
        topBar.addAction("New Server")
        """
        
        ## Servers Widget
        self.dockServers = QDockWidget("Servers")
        self.dockServers.setFeatures(QDockWidget.DockWidgetMovable)
        self.dockServers.setAllowedAreas(Qt.LeftDockWidgetArea|Qt.RightDockWidgetArea)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.dockServers)
        
        self.serversWidget = DBServersWidget.DBServersWidget(self)
        """Instance of :py:class:`~pyqtdb.DBServersWidget.DBServersWidget` in dock"""
        self.dockServers.setWidget(self.serversWidget)
        self.connect(self.serversWidget, SIGNAL("open_server"), self.on_open_server)

        self.cenWid = QWidget()
        self.setCentralWidget(self.cenWid)
        
        self.mainLayout = QHBoxLayout()
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.mainLayout.setSpacing(0)
        self.cenWid.setLayout( self.mainLayout )
        
        self.tabWidget = QTabWidget()
        """The main tabs"""
        self.tabWidget.setTabsClosable(True)
        self.mainLayout.addWidget(self.tabWidget)
        
        G.settings.restore_window(self)
        
    def on_open_server(self, srv):
        """Opens server by adding a :py:class:`~pyqtdb.DBBrowser.DBBrowser` in the the tabWidget"""
        server = G.settings.get_server( str(srv) )
        print "oopen", srv, server
        widget = DBBrowser.DBBrowser(self, server)
        self.tabWidget.addTab(widget, Ico.icon(Ico.Server), server['server'])
        self.tabWidget.setCurrentIndex(self.tabWidget.indexOf(widget))
    
    def closeEvent( self, event ):
        """Save window settings on close with :py:class:`~pyqtdb.XSettings.XSettings.save_window` """ 
        G.settings.save_window( self )
        
        
Exemple #31
0
class OrbitPlotMainWindow(QMainWindow):
    """
    the main window has three major widgets: current, orbit tabs and element
    editor.
    """
    def __init__(self, parent = None, machines=[], **kwargs):
        QMainWindow.__init__(self, parent)
        self.iqtApp = kwargs.get("iqt", None)

        self.setIconSize(QSize(32, 32))
        self.error_bar = True
        self._dlgOrbitCor = None
        # logging
        self.logdock = QDockWidget("Log")
        self.logdock.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        textedit = QPlainTextEdit(self.logdock)
        
        self.logger = logging.getLogger(__name__)

        self.guilogger = logging.getLogger("aphla.gui")
        # the "aphla" include lib part logging. When the lib is inside
        # QThread, logging message will be sent to TextEdit which is cross
        # thread.
        # self.guilogger = logging.getLogger("aphla")
        handler = QTextEditLoggingHandler(textedit)
        self.guilogger.addHandler(handler)
        self.guilogger.setLevel(logging.INFO)
        self.logdock.setWidget(textedit)
        self.logdock.setAllowedAreas(Qt.BottomDockWidgetArea)
        self.logdock.setFeatures(QDockWidget.DockWidgetMovable|
                                 QDockWidget.DockWidgetClosable)
        self.logdock.setFloating(False)
        self.logdock.setMinimumHeight(20)
        self.logdock.setMaximumHeight(100)
        self.logdock.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        self.logdock.resize(200, 60)
        #print self.logdock.sizeHint()
        self.addDockWidget(Qt.BottomDockWidgetArea, self.logdock)
        #print self.logdock.sizeHint()
        #print self.logdock.minimumSize()
        #print self.logdock.maximumSize()
        #self.logger.info("INFO")
        #self.logdock.setMinimumHeight(40)
        #self.logdock.setMaximumHeight(160)

        for msg in kwargs.get("infos", []):
            self.logger.info(msg)
        # dict of (machine, (lattice dict, default_lat, pvm))
        self._mach = dict([(v[0], (v[1],v[2],v[3])) for v in machines])
        for m,(lats,lat0,pvm) in self._mach.items():
            self.logger.info("machine '%s' initialized: [%s]" % (
                m, ", ".join([lat.name for k,lat in lats.items()])))
            if pvm:
                for pv in pvm.dead():
                    self.logger.warn("'{0}' is disconnected.".format(pv))
        ## DCCT current plot
        #self.dcct = DcctCurrentPlot()
        #self.dcct.setMinimumHeight(100)
        #self.dcct.setMaximumHeight(150)

        #t0 = time.time()
        #t = np.linspace(t0 - 8*3600*24, t0, 100)
        #self.dcct.curve.t = t
        #v = 500*np.exp((t[0] - t[:50])/(4*3600*24))
        #self.dcct.curve.v = v.tolist()+v.tolist()
        #self.dcct.updatePlot()

        ## MDI area
        self.mdiarea = QMdiArea()
        self.connect(self.mdiarea, SIGNAL("subWindowActivated(QMdiSubWindow)"),
                     self.updateMachineLatticeNames)
        self.mdiarea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.physics = ApOrbitPhysics(self.mdiarea, iqt=self.iqtApp)
        self.live_orbit = True

        self.setCentralWidget(self.mdiarea)

        #self._elemed = ElementPropertyTabs()
        #self.elemeditor = ElementEditorDock(parent=self)
        #self.elemeditor.setAllowedAreas(Qt.RightDockWidgetArea)
        #self.elemeditor.setFeatures(QDockWidget.DockWidgetMovable|
        #                            QDockWidget.DockWidgetClosable)
        #self.elemeditor.setFloating(False)
        #self.elemeditor.setEnabled(False)
        #self.elemeditor.setMinimumWidth(400)
        #self.elemeditor.setWidget(self._elemed)
        #self.elemeditor.show()
        #self.elemeditor.hide()
        #self.connect(self.elemeditor, 
        #             SIGNAL("elementChecked(PyQt_PyObject, bool)"),
        #             self.physics.elementChecked)
        #self.addDockWidget(Qt.RightDockWidgetArea, self.elemeditor)

        self.createMenuToolBar()
        
        # the first machine is the default
        self.machBox.addItems([v for v in self._mach.keys()])
        self.reloadLatticeNames(self.machBox.currentText())
        self.connect(self.machBox, SIGNAL("currentIndexChanged(QString)"),
                     self.reloadLatticeNames)
        
        # update at 1/2Hz
        self.dt, self.itimer = 1500, 0
        #self.timerId = None
        self.timerId = self.startTimer(self.dt)

        self.vbpm = None
        self.statusBar().showMessage("Welcome")

        #self.initMachine("nsls2v2")
        #self._newVelemPlot("V2SR", aphla.machines.HLA_VBPM, 'x', 
        #                   "H Orbit", c = None)
        #print "Thread started", self.machinit.isRunning()

        #self.newElementPlots("BPM", "x, y")
        #self.newElementPlot("BPM", "y")
        #self.newElementPlot("HCOR", "x")
        #self.newElementPlot("VCOR", "y")
        #self.newElementPlot("QUAD", "b1")
        #self.newElementPlot("SEXT", "b2")
        

    def updateMachineLatticeNames(self, wsub):
        i = self.machBox.findText(wsub.machlat[0])
        self.machBox.setCurrentIndex(i)
        self.reloadLatticeNames(wsub.machlat[0])

    def reloadLatticeNames(self, mach):
        self.latBox.clear()
        cur_mach = str(self.machBox.currentText())
        lats, lat0, pvm = self._mach.get(cur_mach, ({}, None, None))
        self.latBox.addItems([lat for lat in lats.keys()])
        if lat0:
            i = self.latBox.findText(lat0.name)
            self.latBox.setCurrentIndex(i)

    def closeEvent(self, event):
        self.physics.close()
        self.mdiarea.closeAllSubWindows()
        event.accept()

    def createMenuToolBar(self):
        #
        # file menu
        #
        #self.machMenu = self.menuBar().addMenu("&Machines")
        #self.connect(self.machMenu, SIGNAL("aboutToShow()"),
        #             self.updateMachMenu)
        self.openMenu = self.menuBar().addMenu("&Open")
        self.openMenu.addAction("New Plot ...", self.openNewPlot)
        self.openMenu.addAction("New Tune Plot", self.openTunePlot)
        self.openMenu.addAction("New BPM Plot", partial(
                self.newElementPlots, "BPM", "x,y"))
        self.openMenu.addAction("New HCOR Plot", partial(
                self.newElementPlots, "HCOR", "x"))
        self.openMenu.addAction("New VCOR Plot", partial(
                self.newElementPlots, "VCOR", "y"))

        self.openMenu.addSeparator()
        self.openMenu.addAction("Open ORM", self.loadOrm)

        self.openMenu.addSeparator()        
        self.openMenu.addAction("Save Lattice ...", self.saveSnapshot)

        fileQuitAction = QAction(QIcon(":/file_quit.png"), "&Quit", self)
        fileQuitAction.setShortcut("Ctrl+Q")
        fileQuitAction.setToolTip("Quit the application")
        fileQuitAction.setStatusTip("Quit the application")
        #fileQuitAction.setIcon(Qt.QIcon(":/filequit.png"))
        self.connect(fileQuitAction, SIGNAL("triggered()"),
                     self.close)
        self.openMenu.addAction(fileQuitAction)

        # view
        self.viewMenu = self.menuBar().addMenu("&View")

        mkmenu = QMenu("&Mark", self.viewMenu)
        for fam in ["BPM", "COR", "QUAD", "SEXT", "INSERTION"]:
            famAct = QAction(fam, self)
            famAct.setCheckable(True)
            self.connect(famAct, SIGNAL("toggled(bool)"), self.click_markfam)
            mkmenu.addAction(famAct)
        # 
        
        # errorbar
        #viewErrorBarAction = QAction(QIcon(":/view_errorbar.png"),
        #                            "Errorbar", self)
        #viewErrorBarAction.setCheckable(True)
        #viewErrorBarAction.setChecked(True)
        #self.connect(viewErrorBarAction, SIGNAL("toggled(bool)"),
        #             self.errorBar)
        #
        #zoomM = QMenu("Zoom", self.viewMenu)

        #
        #
        #drift_from_now = QAction("Drift from Now", self)
        #drift_from_now.setCheckable(True)
        #drift_from_now.setShortcut("Ctrl+N")
        #drift_from_golden = QAction("Drift from Golden", self)
        #drift_from_golden.setCheckable(True)
        #drift_from_none = QAction("None", self)
        #drift_from_none.setCheckable(True)

        #self.viewMenu.addAction(viewLiveAction)
        #self.viewMenu.addAction(viewSingleShotAction)
        #self.viewMenu.addSeparator()

        #self.viewMenu.addAction(drift_from_now)
        #self.viewMenu.addAction(drift_from_golden)
        #self.viewMenu.addAction(drift_from_none)
        #self.viewMenu.addAction(viewAutoScale)
        #self.viewMenu.addAction(viewErrorBarAction)
        #self.viewMenu.addSeparator()

        self.viewMenu.addMenu(mkmenu)

        #drift_group = QActionGroup(self)
        #drift_group.addAction(drift_from_none)
        #drift_group.addAction(drift_from_now)
        #drift_group.addAction(drift_from_golden)
        #drift_from_none.setChecked(True)

        sep = self.viewMenu.addSeparator()
        #sep.setText("Drift")
        #self.connect(drift_from_now, SIGNAL("triggered()"), self.setDriftNow)
        #self.connect(drift_from_none, SIGNAL("triggered()"), self.setDriftNone)
        #self.connect(drift_from_golden, SIGNAL("triggered()"), 
        #             self.setDriftGolden)

        #viewStyle = QMenu("Line Style", self.viewMenu)
        #for act in ["Increase Point Size", "Decrease Point Size", None,
        #            "NoCurve", "Lines", "Sticks", None,
        #            "Solid Line", "Dashed Line", "Dotted Line", None,
        #            "Increase Line Width", "Decrease Line Width", None,
        #            "NoSymbol", "Ellipse", "Rect", "Diamond", "Triangle",
        #            "Cross", "XCross", "HLine", "VLine",
        #            "Star1", "Star2", "Hexagon", None,
        #            "Red", "Blue", "Green"]:
        #    if act is None:
        #        viewStyle.addSeparator()
        #    else:
        #        viewStyle.addAction(act, self.setPlotStyle)
        #self.viewMenu.addMenu(viewStyle)

        #self.viewMenu.addSeparator()
        #self.viewMenu.addAction(viewZoomOut15Action)
        #self.viewMenu.addAction(viewZoomIn15Action)
        #self.viewMenu.addAction(viewZoomAutoAction)
        #self.viewMenu.addSeparator()
        self.viewMenu.addAction("ORM SV", self.plotSVD)
        # a bug in PyQwt5 for datetime x-axis, waiting for Debian 7
        #self.viewMenu.addAction(viewDcct)
        #for ac in self.viewMenu.actions(): ac.setDisabled(True)

        #
        self.controlMenu = self.menuBar().addMenu("&Tools")
        
        self.controlMenu.addAction(
            QIcon(":/control_choosebpm.png"), "En-/Disable BPM",
            partial(chooseElement, 'BPM'))
        self.controlMenu.addAction(
            "En-/Disable COR", partial(chooseElement, 'COR'))
        #self.controlMenu.addAction(controlResetPvDataAction)
        self.controlMenu.addSeparator()
        self.controlMenu.addAction("Lattice Snapshot ...", self.openSnapshot)
        #self.controlMenu.addAction(controlZoomInPlot1Action)
        #self.controlMenu.addAction(controlZoomOutPlot1Action)
        #self.controlMenu.addAction(controlZoomInPlot2Action)
        #self.controlMenu.addAction(controlZoomOutPlot2Action)
        self.controlMenu.addSeparator()
        self.controlMenu.addAction("Correct Hor. orbit",
            partial(aphla.correctOrbit, plane="H"))
        self.controlMenu.addAction("Correct Vert. orbit",
            partial(aphla.correctOrbit, plane="V"))
        self.controlMenu.addAction(
            QIcon(":/control_corrorbit.png"), "Correct orbit",
            partial(aphla.correctOrbit, plane="HV"))
        #steer_orbit.setDisabled(True)
        self.controlMenu.addAction("Local Bump ...", self.createLocalBump)
        self.controlMenu.addAction("Element Editor ...", self.showElementEditor)
        self.controlMenu.addSeparator()
        self.controlMenu.addAction("meas Beta", self.physics.measBeta)
        self.controlMenu.addAction("meas Dispersion", self.physics.measDispersion)
        self.controlMenu.addAction("beam based alignment", self.runBba)
        #for ac in self.controlMenu.actions(): ac.setDisabled(True)

        # Window
        self.windowMenu = self.menuBar().addMenu("&Windows")
        #self.windowMenu.addAction(self.elemeditor.toggleViewAction())
        self.windowMenu.addAction(self.logdock.toggleViewAction())
        #viewDcct = QAction("Beam Current", self)
        #viewDcct.setCheckable(True)
        #viewDcct.setChecked(True)
        #self.connect(viewDcct, SIGNAL("toggled(bool)"), self.dcct.setVisible)
        #self.windowMenu.addAction(viewDcct)
        self.windowMenu.addSeparator()
        self.windowMenu.addAction("Cascade", self.mdiarea.cascadeSubWindows)
        self.windowMenu.addAction("Tile", self.mdiarea.tileSubWindows)
        self.windowMenu.addAction("Tile Horizontally",
                                  self.tileSubWindowsHorizontally)
        # "ctrl+page up", "ctrl+page down"
        self.windowMenu.addAction("Previous", self.mdiarea.activatePreviousSubWindow, "Ctrl+Left")
        self.windowMenu.addAction("Next", self.mdiarea.activateNextSubWindow, "Ctrl+Right")
        self.windowMenu.addSeparator()

        # debug
        self.debugMenu = self.menuBar().addMenu("&Debug")
        self.debugMenu.addAction("_Reset Correctors_", self._reset_correctors)
        self.debugMenu.addAction("_Reset Quadrupoles_", self._reset_quadrupoles)
        self.debugMenu.addAction("_Random V Kick_", self._random_vkick)
        self.debugMenu.addAction("_Random H Kick_", self._random_hkick)
        #for ac in self.debugMenu.actions(): ac.setDisabled(True)

        # help
        self.helpMenu = self.menuBar().addMenu("&Help")
        self.helpMenu.addAction("About mleap", self.showAbout)
                                                                         
        #toolbar
        machToolBar = self.addToolBar("Machines")
        self.machBox = QtGui.QComboBox()
        self.latBox = QtGui.QComboBox()
        #self.connect(self.latBox, SIGNAL("currentIndexChanged(QString)"), 
        #             self.__setLattice)
        machToolBar.addWidget(self.machBox)
        machToolBar.addWidget(self.latBox)
        #toolbar = QToolBar(self)
        #self.addToolBar(toolbar)
        #fileToolBar = self.addToolBar("File")
        #fileToolBar.setObjectName("FileToolBar")
        #fileToolBar.addAction(fileQuitAction)

        #
        viewToolBar1 = self.addToolBar("Live View")
        #viewToolBar.setObjectName("ViewToolBar")
        #viewToolBar.addAction(viewZoomOut15Action)
        #viewToolBar.addAction(viewZoomIn15Action)
        #viewToolBar.addAction(viewZoomAutoAction)
        #viewToolBar1.addAction(viewLiveAction)
        #viewToolBar1.addAction(viewSingleShotAction)
        #viewToolBar1.addSeparator()
        viewToolBar1.addAction(
            QIcon(":/new_bpm.png"), "Orbits",
            partial(self.newElementPlots, "BPM", "x,y"))
        viewToolBar1.addAction(
            QIcon(":/new_cor.png"), "Correctors",
            partial(self.newElementPlots, "COR", "x,y"))
        viewToolBar1.addAction(
            QIcon(":/new_quad.png"), "Quadrupoles",
            partial(self.newElementPlots, "QUAD", "b1"))
        viewToolBar1.addAction(
            QIcon(":/new_sext.png"), "Sextupoles",
            partial(self.newElementPlots, "SEXT", "b2"))
        #viewToolBar.addAction(viewErrorBarAction)
        #viewToolBar.addAction(QWhatsThis.createAction(self))

        #viewToolBar2 = self.addToolBar("Scale Plot")
        #zoomActions = [(":/view_zoom_xy.png", "Fit", self.scalePlot),
        #               (None, None, None),
        #               (":/view_zoom_y.png", "Fit In Y", self.scalePlot),
        #               (":/view_zoomin_y.png", "Zoom In Y", self.scalePlot),
        #               (":/view_zoomout_y.png", "Zoom Out Y", self.scalePlot),
        #               (":/view_move_up.png", "Move Up", self.scalePlot),
        #               (":/view_move_down.png", "Move Down", self.scalePlot),
        #               (None, None, None),
        #               (":/view_zoom_x.png", "Fit In X", self.scalePlot),
        #               (":/view_zoomin_x.png", "Zoom In X", self.scalePlot),
        #               (":/view_zoomout_x.png", "Zoom Out X", self.scalePlot),
        #               (":/view_move_left.png", "Move Left", self.scalePlot),
        #               (":/view_move_right.png", "Move Right", self.scalePlot),
        #               ]
        #for ico,name,hdl in zoomActions:
        #    if hdl is None: continue
        #    viewToolBar2.addAction(QIcon(ico), name, hdl)

        controlToolBar = self.addToolBar("Control")
        controlToolBar.addAction(
            QIcon(":/control_orbitcor.png"), "Correct Orbit",
            aphla.correctOrbit)
        controlToolBar.addAction(
            QIcon(":/control_localbump.png"), "Local Bump ...",
            self.createLocalBump)
        #controlToolBar.addAction(controlResetPvDataAction)

    def showAbout(self):
        QMessageBox.about(
            self, self.tr("mleap"),
            (self.tr("""<b>Machine/Lattice Editor And Plotter</b> v %1
                <p>Copyright &copy; Lingyun Yang, BNL, 2013-2014. 
                All rights reserved.
                <p>This application can be used to perform
                high level accelerator controls.
                <p>Python %2 - Qt %3 - PyQt %4 
                on %5""").arg(aphla.version.version)
                .arg(platform.python_version()).arg(QtCore.QT_VERSION_STR)
                .arg(QtCore.PYQT_VERSION_STR).arg(platform.system())))

    def showElementEditor(self):
        mach, lat = self.getCurrentMachLattice()
        ed = ElementEditor(lat, parent=self)
        ed.setWindowFlags(Qt.Window)
        ed.setAttribute(Qt.WA_DeleteOnClose)
        ed.show()

    def getCurrentMachLattice(self, cadata = False):
        """return the current machine name and lattice object"""
        mach     = str(self.machBox.currentText())
        latname  = str(self.latBox.currentText())
        lat_dict, lat0, pvm = self._mach[mach]
        if not cadata:
            return mach, lat_dict[latname]
        else:
            return mach, lat_dict[latname], pvm

    def newElementPlots(self, elem, fields, **kw):
        self.logger.info("new plots: %s %s" % (elem, fields))
        _mach, _lat, _pvm = self.getCurrentMachLattice(cadata=True)
        mach, lat = kw.get("machlat", (_mach, _lat))
        handle = kw.get("handle", "readback")
        elems = lat.getElementList(elem)
        x, pvs = [], []
        field_list = re.findall(r'[^ ,]+', fields)
        for fld in field_list:
            si, pvsi = [], []
            for e in elems:
                if not e.isEnabled(): continue
                epv = e.pv(field=fld, handle=handle)
                if not epv: continue
                pvsi.append(epv[0])
                si.append(e.sb)
            x.append(si)
            pvs.append(pvsi)

        if not pvs:
            self.logger.error("no data found for elements '{0}' "
                              "and field '{1}'".format(elem, field))
            return

        p = ApMdiSubPlot(pvs=pvs, x = x, 
                         labels=["%s.%s" % (elem,fld) for fld in field_list],
                         magprof = lat.getBeamlineProfile(),
                         iqt = self.iqtApp,
                         **kw)
        #QObject.installEventFilter(p.aplot)
        #p.data = ManagedPvData(pvm, s, pvs, element=elemnames,
        #                       label="{0}.{1}".format(elem,field))
        p.setAttribute(Qt.WA_DeleteOnClose)
        str_elem = "{0}".format(elem)
        if len(str_elem) > 12: str_elem = str_elem[:9] + "..."
        str_field = "{0}".format(fields)
        if len(str_field) > 12: str_field = str_field[:9] + "..."
        p.setWindowTitle("[%s.%s] %s %s" % (
                mach, lat.name, str_elem, str_field))
        self.connect(p, SIGNAL("elementSelected(PyQt_PyObject)"), 
                     self.elementSelected)
        self.connect(p, SIGNAL("destroyed()"), self.subPlotDestroyed)
        #p.updatePlot()
        # set the zoom stack
        #p.aplot.setErrorBar(self.error_bar)
        #p.wid.autoScaleXY()
        #p.aplot.replot()
        self.mdiarea.addSubWindow(p)
        #print "Show"
        p.show()

        ##print "Enable the buttons"
        #if len(self.mdiarea.subWindowList()) > 0:
        #    self.elemeditor.setEnabled(True)

    def subPlotDestroyed(self):
        #if len(self.mdiarea.subWindowList()) == 0:
        #    self.elemeditor.setEnabled(False)
        pass

    def loadOrm(self):
        fileName = QtGui.QFileDialog.getOpenFileName(
            self, "Open Orbit Response Matrix",
            "",
            "ORM Files (*.h5 *.hdf5);;Text File (*.txt);;All Files(*)")
        fileName = str(fileName)
        try:
            m = np.loadtxt(fileName)
        except:
            QMessageBox.critical(self, "Abort", "Invalid matrix data")
            return
        mach, lat = self.getCurrentMachLattice()
        # assuming we already have the PV, name, field but just want to
        # replace the matrix elements.
        assert np.shape(m) == np.shape(lat.ormdata.m)
        nx, ny = np.shape(lat.ormdata.m)
        for i in range(nx):
            for j in range(ny):
                lat.ormdata.m[i,j] = m[i,j]


    def saveSnapshot(self):
        latdict = dict([(k,v[0]) for k,v in self._mach.items()])
        mach, lat = self.getCurrentMachLattice()
        snapdlg = SaveSnapshotDialog(latdict, mach)
        snapdlg.exec_()

    def saveLatSnapshot(self):
        mach, lat = self.getCurrentMachLattice()
        dpath = self._prepare_parent_dirs(mach)
        if not dpath:
            QMessageBox.warning(self, "Abort", "Aborted")
            return
        dt = datetime.datetime.now()
        fname = os.path.join(dpath,
            dt.strftime("snapshot_%d_%H%M%S_") + lat.name + ".hdf5")
        fileName = QtGui.QFileDialog.getSaveFileName(
            self, "Save Lattice Snapshot Data",
            fname,
            "Data Files (*.h5 *.hdf5);;All Files(*)")
        fileName = str(fileName)
        if not fileName: return
        aphla.catools.save_lat_epics(fileName, lat, mode='a')
        self.logger.info("snapshot created '%s'" % fileName)

    def saveMachSnapshot(self):
        mach, lat = self.getCurrentMachLattice()
        dpath = self._prepare_parent_dirs(mach)
        if not dpath:
            QMessageBox.warning(self, "Abort", "Aborted")
            return
        dt = datetime.datetime.now()
        fname = os.path.join(dpath, dt.strftime("snapshot_%d_%H%M%S.hdf5"))
        fileName = QtGui.QFileDialog.getSaveFileName(
            self, "Save Lattice Snapshot Data",
            fname,
            "Data Files (*.h5 *.hdf5);;All Files(*)")
        if not fileName: return
        fileName = str(fileName)
        import h5py
        f = h5py.File(str(fileName), 'w')
        f.close()
        self.logger.info("clean snapshot file created: '%s'" % fileName)
        for k,lat in self._mach[mach][0].items():
            aphla.catools.save_lat_epics(fileName, lat, mode='a')
            self.logger.info("lattice snapshot appended for '%s'" % lat.name)

    def openSnapshot(self):
        #self.logger.info("loading snapshot?")
        latdict = dict([(k,v[0]) for k,v in self._mach.items()])
        mach, lat = self.getCurrentMachLattice()
        lv = LatSnapshotMain(self, latdict, mach, self.logger)
        lv.setWindowFlags(Qt.Window)
        #self.logger.info("initialized")
        #lv.loadLatSnapshotH5()
        lv.exec_()

    def openTunePlot(self):
        mach, lat = self.getCurrentMachLattice()
        nu = lat.getElementList('tune')
        pvs = [(e.pv(field="x", handle="readback")[0],
                e.pv(field="y", handle="readback")[0])
               for e in nu]
        labels = [e.name for e in nu]
        twiss = lat.getElementList("VA")
        pvs.extend([(e.pv(field="nux", handle="readback")[0],
                     e.pv(field="nuy", handle="readback")[0])
                    for e in twiss])
        labels.extend([e.name for e in twiss])

        p = ApMdiSubPlot(pvs=pvs, labels=labels, dtype = "Tunes")
        #QObject.installEventFilter(p.aplot)
        #p.data = ManagedPvData(pvm, s, pvs, element=elemnames,
        #                       label="{0}.{1}".format(elem,field))
        p.setAttribute(Qt.WA_DeleteOnClose)
        p.setWindowTitle("[%s.%s] Tunes" % (mach, lat.name))
        self.connect(p, SIGNAL("elementSelected(PyQt_PyObject)"), 
                     self.elementSelected)
        self.connect(p, SIGNAL("destroyed()"), self.subPlotDestroyed)
        #p.updatePlot()
        # set the zoom stack
        #p.aplot.setErrorBar(self.error_bar)
        #p.wid.autoScaleXY()
        #p.aplot.replot()
        self.mdiarea.addSubWindow(p)
        #print "Show"
        p.show()

    def openNewPlot(self):
        mach, lat = self.getCurrentMachLattice()
        fl = QtGui.QFormLayout()
        fl.addRow("Machine", QtGui.QLabel("%s" % mach))
        fl.addRow("Lattice", QtGui.QLabel("%s" % lat.name))
        elem, fld = QtGui.QLineEdit(), QtGui.QLineEdit()
        fl.addRow("Elements", elem)
        fl.addRow("Field", fld)
        dtype = QtGui.QComboBox()
        for tx in ["Array", "Waveform", "Time Series"]:
            dtype.addItem(tx)
        fl.addRow("Data Type", dtype)
        dlg = QtGui.QDialog()
        bx = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok |
                                    QtGui.QDialogButtonBox.Cancel)
        self.connect(bx, SIGNAL("accepted()"), dlg.accept)
        self.connect(bx, SIGNAL("rejected()"), dlg.reject)
        h1 = QtGui.QHBoxLayout()
        h1.addStretch()
        h1.addWidget(bx)
        v1 = QtGui.QVBoxLayout()
        v1.addLayout(fl)
        v1.addLayout(h1)
        dlg.setLayout(v1)

        if dlg.exec_():
            self.newElementPlots(str(elem.text()), str(fld.text()),
                                 machlat=(mach, lat),
                                 dtype = str(dtype.currentText()))

    def click_markfam(self, on):
        famname = self.sender().text()
        mks = []
        mach, lat = self.getCurrentMachLattice()
        # need to convert to python str
        for elem in lat.getElementList(str(famname)):
            if elem.family != famname: continue
            if elem.virtual: continue
            mks.append([elem.name, 0.5*(elem.sb+elem.se)])

        for w in self.mdiarea.subWindowList(): w.setMarkers(mks, on)
        #print self._machlat.keys()

    def _reset_correctors(self):
        self.logger.info("reset correctors")
        aphla.hlalib._reset_trims()

    def _reset_quadrupoles(self):
        self.logger.info("reset quadrupoles")
        aphla.hlalib._reset_quad()

    def _random_hkick(self):
        mach, lat = self.getCurrentMachLattice()
        hcors = lat.getElementList('HCOR')
        for k in range(3):
            i = np.random.randint(len(hcors))
            self.logger.info("Setting {0}/{1} HCOR".format(i, len(hcors)))
            hcors[i].x += np.random.rand() * 2e-6


    def _random_vkick(self):
        mach, lat = self.getCurrentMachLattice()
        cors = lat.getElementList('VCOR')
        for k in range(3):
            i = np.random.randint(len(cors))
            cors[i].y += np.random.rand() * 1e-6
            self.logger.info("increased kicker '{0}' by 1e-7 ({1} {2})".format(
                cors[i].name, cors[i].y, cors[i].getUnit('y', None)))

    def viewDcctPlot(self, on):
        self.dcct.setVisible(on)

    def liveData(self, on):
        """Switch on/off live data taking"""
        self.live_orbit = on

    def scalePlot(self):
        w = self.mdiarea.currentSubWindow()
        if not w: return
        st, p = self.sender().text(), w.aplot
        if st == "Fit":
            p.scaleXBottom()
            p.scaleYLeft()
            # a hack
            bound = p.curvesBound()
            p.zoomer1.setZoomStack([bound])
        elif st == "Fit In Y":
            p.scaleYLeft()
        elif st == "Zoom In Y":
            p.scaleYLeft(1./1.5)
        elif st == "Zoom Out Y":
            p.scaleYLeft(1.5)
        elif st == "Move Up":
            p.moveCurves(Qwt.QwtPlot.yLeft, 0.8)
        elif st == "Move Down":
            p.moveCurves(Qwt.QwtPlot.yLeft, -0.8)
        elif st == "Fit In X":
            p.scaleXBottom()
        elif st == "Zoom In X":
            p.scaleXBottom(1.0/1.5)
        elif st == "Zoom Out X":
            p.scaleXBottom(1.5)            
        elif st == "Move Left":
            p.moveCurves(Qwt.QwtPlot.xBottom, 0.8)
        elif st == "Move Right":
            p.moveCurves(Qwt.QwtPlot.xBottom, -0.8)
        else:
            self.logger.error("unknow action '{0}'".format(st))

    def getVisibleRange(self):
        w = self.mdiarea.currentSubWindow()
        if not w: 
            mach, lat = self.getCurrentMachLattice()
            self.logger.warn("no active plot, use full range of {0}.{1}".format(
                mach, lat.name))
            return lat.getLocationRange()
        else:
            return w.currentXlim()
        
    def getVisibleElements(self, elemname, sb = None, se = None):
        w = self.mdiarea.currentSubWindow()
        mach, lat = self.getCurrentMachLattice()
        elems = lat.getElementList(elemname)
        if sb is not None: 
            elems = [e for e in elems if e.sb >= sb]
        if se is not None:
            elems = [e for e in elems if e.se <= se]

        self.logger.info("searching for '{0}' in range [{1}, {2}]".format(
            elemname, sb, se))

        return elems

    def timerEvent(self, e):
        if e.timerId() != self.timerId: return

        #if not self.elemeditor.isHidden():
        #    self.elemeditor.updateModelData()

        #if self.live_orbit:
        #    self.itimer += 1
        #    #self.updatePlots()
        #    #self.updateStatus()
        #    for w in self.mdiarea.subWindowList():
        #        if not isinstance(w, ApMdiSubPlot): continue
        #        if not w.live: continue
        #        w.updatePlot()
        #    self.statusBar().showMessage("plot updated: {0}".format(
        #        time.strftime("%F %T")))
        #else:
        #    self.statusBar().showMessage("live update disabled")
            
            
    def singleShot(self):
        for w in self.mdiarea.subWindowList():
            if not isinstance(w, ApMdiSubPlot):  continue
            w.updatePlot()

        self.statusBar().showMessage("plot updated: {0}".format(
            time.strftime("%F %T")))


    def elementSelected(self, elems):
        """this action is ignored"""
        mach, lat, elemnames = elems
        #_lat = self._machlat[mach][lat]
        self.logger.info("element selected")

        #elemobjs = _lat.getElementList(elemnames)
        #self._elemed.addElements(elemobjs)

    def activeOrbitPlot(self, field):
        mach = str(self.machBox.currentText())
        lat = str(self.latBox.currentText())
        for w in self.mdiarea.subWindowList():
            #print w.machine(), w.lattice(), w.data.yfield
            if not isinstance(w, ApMdiSubPlot):  continue
            if w.machine() != mach: continue
            if w.lattice() != lat: continue
            if w.data.yfield != field: continue
            return w

        return None

    def createLocalBump_(self):
        """create local bump"""
        if self._dlgOrbitCor is None:
            bpms = ap.getElements("BPM")
            cors = ap.getElements("COR")
            self._dlgOrbitCor = OrbitCorrDlg(bpms, cors)
            #corbitdlg.resize(600, 500)
            self._dlgOrbitCor.setWindowTitle("Create Local Bump")
        self._dlgOrbitCor.show()
        self._dlgOrbitCor.raise_()
        self._dlgOrbitCor.activateWindow()

    def createLocalBump(self):
        """create local bump"""
        bpms = ap.getElements("BPM")
        cors = ap.getElements("COR")
        dlgOrbitCor = OrbitCorrDlg(bpms, cors, parent=self)
        #corbitdlg.resize(600, 500)
        dlgOrbitCor.setWindowTitle("Create Local Bump")
        dlgOrbitCor.show()
        dlgOrbitCor.raise_()
        dlgOrbitCor.activateWindow()
        dlgOrbitCor.setAttribute(Qt.WA_DeleteOnClose)

    def runBba(self):
        mach, lat = self.getCurrentMachLattice()
        bpms = [e for e in lat.getElementList('BPM') 
                if e not in self.physics.deadelems]
        self.physics.runBba(bpms)

    def plotSVD(self):
        mach, lat = self.getCurrentMachLattice()
        if not lat.ormdata:
            QMessageBox.critical(self, "ORM SVD", 
                                 "machine '%s' ORM data is not available" % \
                                 mach,
                                 QMessageBox.Ok)
            return
        m, brec, trec = lat.ormdata.getMatrix(None, None, full=False, 
                                              ignore=self.getDeadElements())
        U, s, V = np.linalg.svd(m, full_matrices=True)
        #print np.shape(s), s
        self.sp = ApSvdPlot(s)
        self.sp.show()


    def tileSubWindowsHorizontally(self):
        pos = QtCore.QPoint(0, 0)
        subwins = self.mdiarea.subWindowList()
        for w in subwins:
            height = self.mdiarea.height()/len(subwins)
            rect = QtCore.QRect(0, 0, self.mdiarea.width(), height)
            w.setGeometry(rect)
            w.move(pos)
            pos.setY(pos.y() + w.height())
Exemple #32
0
class DBManager(QMainWindow):
    def __init__(self, iface, parent=None):
        QMainWindow.__init__(self, parent)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setupUi()
        self.iface = iface

        # restore the window state
        settings = QSettings()
        self.restoreGeometry(
            settings.value("/DB_Manager/mainWindow/geometry",
                           QByteArray(),
                           type=QByteArray))
        self.restoreState(
            settings.value("/DB_Manager/mainWindow/windowState",
                           QByteArray(),
                           type=QByteArray))

        self.connect(self.tabs, SIGNAL("currentChanged(int)"), self.tabChanged)
        self.connect(self.tree, SIGNAL("selectedItemChanged"),
                     self.itemChanged)
        self.itemChanged(None)

    def closeEvent(self, e):
        self.unregisterAllActions()

        # save the window state
        settings = QSettings()
        settings.setValue("/DB_Manager/mainWindow/windowState",
                          self.saveState())
        settings.setValue("/DB_Manager/mainWindow/geometry",
                          self.saveGeometry())

        QMainWindow.closeEvent(self, e)

    def refreshItem(self, item=None):
        QApplication.setOverrideCursor(Qt.WaitCursor)
        try:
            if item is None:
                item = self.tree.currentItem()
            self.tree.refreshItem(item)  # refresh item children in the db tree
        except BaseError as e:
            DlgDbError.showError(e, self)
            return
        finally:
            QApplication.restoreOverrideCursor()

    def itemChanged(self, item):
        QApplication.setOverrideCursor(Qt.WaitCursor)
        try:
            self.reloadButtons()
            self.refreshTabs()
        except BaseError as e:
            DlgDbError.showError(e, self)
            return
        finally:
            QApplication.restoreOverrideCursor()

    def reloadButtons(self):
        db = self.tree.currentDatabase()
        if not hasattr(self, '_lastDb'):
            self._lastDb = db

        elif db == self._lastDb:
            return

        # remove old actions
        if self._lastDb is not None:
            self.unregisterAllActions()

        # add actions of the selected database
        self._lastDb = db
        if self._lastDb is not None:
            self._lastDb.registerAllActions(self)

    def tabChanged(self, index):
        QApplication.setOverrideCursor(Qt.WaitCursor)
        try:
            self.refreshTabs()
        except BaseError as e:
            DlgDbError.showError(e, self)
            return
        finally:
            QApplication.restoreOverrideCursor()

    def refreshTabs(self):
        index = self.tabs.currentIndex()
        item = self.tree.currentItem()
        table = self.tree.currentTable()

        # enable/disable tabs
        self.tabs.setTabEnabled(self.tabs.indexOf(self.table), table
                                is not None)
        self.tabs.setTabEnabled(
            self.tabs.indexOf(self.preview), table is not None
            and table.type in [table.VectorType, table.RasterType]
            and table.geomColumn is not None)
        # show the info tab if the current tab is disabled
        if not self.tabs.isTabEnabled(index):
            self.tabs.setCurrentWidget(self.info)

        current_tab = self.tabs.currentWidget()
        if current_tab == self.info:
            self.info.showInfo(item)
        elif current_tab == self.table:
            self.table.loadData(item)
        elif current_tab == self.preview:
            self.preview.loadPreview(item)

    def refreshActionSlot(self):
        self.info.setDirty()
        self.table.setDirty()
        self.preview.setDirty()
        self.refreshItem()

    def importActionSlot(self):
        db = self.tree.currentDatabase()
        if db is None:
            self.infoBar.pushMessage(
                self.tr(
                    "No database selected or you are not connected to it."),
                QgsMessageBar.INFO, self.iface.messageTimeout())
            return

        outUri = db.uri()
        schema = self.tree.currentSchema()
        if schema:
            outUri.setDataSource(schema.name, "", "", "")

        from .dlg_import_vector import DlgImportVector

        dlg = DlgImportVector(None, db, outUri, self)
        dlg.exec_()

    def exportActionSlot(self):
        table = self.tree.currentTable()
        if table is None:
            self.infoBar.pushMessage(
                self.tr("Select the table you want export to file."),
                QgsMessageBar.INFO, self.iface.messageTimeout())
            return

        inLayer = table.toMapLayer()

        from .dlg_export_vector import DlgExportVector

        dlg = DlgExportVector(inLayer, table.database(), self)
        dlg.exec_()

        inLayer.deleteLater()

    def runSqlWindow(self):
        db = self.tree.currentDatabase()
        if db is None:
            self.infoBar.pushMessage(
                self.tr(
                    "No database selected or you are not connected to it."),
                QgsMessageBar.INFO, self.iface.messageTimeout())
            # force displaying of the message, it appears on the first tab (i.e. Info)
            self.tabs.setCurrentIndex(0)
            return

        from dlg_sql_window import DlgSqlWindow

        query = DlgSqlWindow(self.iface, db, self)
        dbname = db.connection().connectionName()
        tabname = self.tr("Query") + u" (%s)" % dbname
        index = self.tabs.addTab(query, tabname)
        self.tabs.setTabIcon(index, db.connection().icon())
        self.tabs.setCurrentIndex(index)
        query.nameChanged.connect(
            functools.partial(self.update_query_tab_name, index, dbname))

    def update_query_tab_name(self, index, dbname, queryname):
        if not queryname:
            queryname = self.tr("Query")
        tabname = u"%s (%s)" % (queryname, dbname)
        self.tabs.setTabText(index, tabname)

    def showSystemTables(self):
        self.tree.showSystemTables(self.actionShowSystemTables.isChecked())

    def registerAction(self, action, menuName, callback=None):
        """ register an action to the manager's main menu """
        if not hasattr(self, '_registeredDbActions'):
            self._registeredDbActions = {}

        if callback is not None:
            invoke_callback = lambda x: self.invokeCallback(callback)

        if menuName is None or menuName == "":
            self.addAction(action)

            if menuName not in self._registeredDbActions:
                self._registeredDbActions[menuName] = list()
            self._registeredDbActions[menuName].append(action)

            if callback is not None:
                QObject.connect(action, SIGNAL("triggered(bool)"),
                                invoke_callback)
            return True

        # search for the menu
        actionMenu = None
        helpMenuAction = None
        for a in self.menuBar.actions():
            if not a.menu() or a.menu().title() != menuName:
                continue
            if a.menu() != self.menuHelp:
                helpMenuAction = a

            actionMenu = a
            break

        # not found, add a new menu before the help menu
        if actionMenu is None:
            menu = QMenu(menuName, self)
            if helpMenuAction is not None:
                actionMenu = self.menuBar.insertMenu(helpMenuAction, menu)
            else:
                actionMenu = self.menuBar.addMenu(menu)

        menu = actionMenu.menu()
        menuActions = menu.actions()

        # get the placeholder's position to insert before it
        pos = 0
        for pos in range(len(menuActions)):
            if menuActions[pos].isSeparator() and menuActions[pos].objectName(
            ).endswith("_placeholder"):
                menuActions[pos].setVisible(True)
                break

        if pos < len(menuActions):
            before = menuActions[pos]
            menu.insertAction(before, action)
        else:
            menu.addAction(action)

        actionMenu.setVisible(True)  # show the menu

        if menuName not in self._registeredDbActions:
            self._registeredDbActions[menuName] = list()
        self._registeredDbActions[menuName].append(action)

        if callback is not None:
            QObject.connect(action, SIGNAL("triggered(bool)"), invoke_callback)

        return True

    def invokeCallback(self, callback, *params):
        """ Call a method passing the selected item in the database tree,
                the sender (usually a QAction), the plugin mainWindow and
                optionally additional parameters.

                This method takes care to override and restore the cursor,
                but also catches exceptions and displays the error dialog.
        """
        QApplication.setOverrideCursor(Qt.WaitCursor)
        try:
            callback(self.tree.currentItem(), self.sender(), self, *params)

        except BaseError as e:
            # catch database errors and display the error dialog
            DlgDbError.showError(e, self)
            return

        finally:
            QApplication.restoreOverrideCursor()

    def unregisterAction(self, action, menuName):
        if not hasattr(self, '_registeredDbActions'):
            return

        if menuName is None or menuName == "":
            self.removeAction(action)

            if menuName in self._registeredDbActions:
                if self._registeredDbActions[menuName].count(action) > 0:
                    self._registeredDbActions[menuName].remove(action)

            action.deleteLater()
            return True

        for a in self.menuBar.actions():
            if not a.menu() or a.menu().title() != menuName:
                continue

            menu = a.menu()
            menuActions = menu.actions()

            menu.removeAction(action)
            if menu.isEmpty():  # hide the menu
                a.setVisible(False)

            if menuName in self._registeredDbActions:
                if self._registeredDbActions[menuName].count(action) > 0:
                    self._registeredDbActions[menuName].remove(action)

                # hide the placeholder if there're no other registered actions
                if len(self._registeredDbActions[menuName]) <= 0:
                    for i in range(len(menuActions)):
                        if menuActions[i].isSeparator() and menuActions[
                                i].objectName().endswith("_placeholder"):
                            menuActions[i].setVisible(False)
                            break

            action.deleteLater()
            return True

        return False

    def unregisterAllActions(self):
        if not hasattr(self, '_registeredDbActions'):
            return

        for menuName in self._registeredDbActions:
            for action in list(self._registeredDbActions[menuName]):
                self.unregisterAction(action, menuName)
        del self._registeredDbActions

    def close_tab(self, index):
        widget = self.tabs.widget(index)
        if widget not in [self.info, self.table, self.preview]:
            self.tabs.removeTab(index)
            widget.deleteLater()

    def setupUi(self):
        self.setWindowTitle(self.tr("DB Manager"))
        self.setWindowIcon(QIcon(":/db_manager/icon"))
        self.resize(QSize(700, 500).expandedTo(self.minimumSizeHint()))

        # create central tab widget and add the first 3 tabs: info, table and preview
        self.tabs = QTabWidget()
        self.info = InfoViewer(self)
        self.tabs.addTab(self.info, self.tr("Info"))
        self.table = TableViewer(self)
        self.tabs.addTab(self.table, self.tr("Table"))
        self.preview = LayerPreview(self)
        self.tabs.addTab(self.preview, self.tr("Preview"))
        self.setCentralWidget(self.tabs)

        # display close button for all tabs but the first 3 ones, i.e.
        # HACK: just hide the close button where not needed (GS)
        self.tabs.setTabsClosable(True)
        self.tabs.tabCloseRequested.connect(self.close_tab)
        tabbar = self.tabs.tabBar()
        for i in range(3):
            btn = tabbar.tabButton(i, QTabBar.RightSide) if tabbar.tabButton(
                i, QTabBar.RightSide) else tabbar.tabButton(
                    i, QTabBar.LeftSide)
            btn.resize(0, 0)
            btn.hide()

        # Creates layout for message bar
        self.layout = QGridLayout(self.info)
        self.layout.setContentsMargins(0, 0, 0, 0)
        spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum,
                                 QSizePolicy.Expanding)
        self.layout.addItem(spacerItem, 1, 0, 1, 1)
        # init messageBar instance
        self.infoBar = QgsMessageBar(self.info)
        sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.infoBar.setSizePolicy(sizePolicy)
        self.layout.addWidget(self.infoBar, 0, 0, 1, 1)

        # create database tree
        self.dock = QDockWidget("Tree", self)
        self.dock.setObjectName("DB_Manager_DBView")
        self.dock.setFeatures(QDockWidget.DockWidgetMovable)
        self.tree = DBTree(self)
        self.dock.setWidget(self.tree)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.dock)

        # create status bar
        self.statusBar = QStatusBar(self)
        self.setStatusBar(self.statusBar)

        # create menus
        self.menuBar = QMenuBar(self)
        self.menuDb = QMenu(self.tr("&Database"), self)
        self.menuBar.addMenu(self.menuDb)
        self.menuSchema = QMenu(self.tr("&Schema"), self)
        actionMenuSchema = self.menuBar.addMenu(self.menuSchema)
        self.menuTable = QMenu(self.tr("&Table"), self)
        actionMenuTable = self.menuBar.addMenu(self.menuTable)
        self.menuHelp = None  # QMenu(self.tr("&Help"), self)
        # actionMenuHelp = self.menuBar.addMenu(self.menuHelp)

        self.setMenuBar(self.menuBar)

        # create toolbar
        self.toolBar = QToolBar("Default", self)
        self.toolBar.setObjectName("DB_Manager_ToolBar")
        self.addToolBar(self.toolBar)

        # create menus' actions

        # menu DATABASE
        sep = self.menuDb.addSeparator()
        sep.setObjectName("DB_Manager_DbMenu_placeholder")
        sep.setVisible(False)

        self.actionRefresh = self.menuDb.addAction(
            QIcon(":/db_manager/actions/refresh"), self.tr("&Refresh"),
            self.refreshActionSlot, QKeySequence("F5"))
        self.actionSqlWindow = self.menuDb.addAction(
            QIcon(":/db_manager/actions/sql_window"), self.tr("&SQL window"),
            self.runSqlWindow, QKeySequence("F2"))
        self.menuDb.addSeparator()
        self.actionClose = self.menuDb.addAction(QIcon(), self.tr("&Exit"),
                                                 self.close,
                                                 QKeySequence("CTRL+Q"))

        # menu SCHEMA
        sep = self.menuSchema.addSeparator()
        sep.setObjectName("DB_Manager_SchemaMenu_placeholder")
        sep.setVisible(False)

        actionMenuSchema.setVisible(False)

        # menu TABLE
        sep = self.menuTable.addSeparator()
        sep.setObjectName("DB_Manager_TableMenu_placeholder")
        sep.setVisible(False)

        self.actionImport = self.menuTable.addAction(
            QIcon(":/db_manager/actions/import"),
            self.tr("&Import layer/file"), self.importActionSlot)
        self.actionExport = self.menuTable.addAction(
            QIcon(":/db_manager/actions/export"), self.tr("&Export to file"),
            self.exportActionSlot)
        self.menuTable.addSeparator()
        #self.actionShowSystemTables = self.menuTable.addAction(self.tr("Show system tables/views"), self.showSystemTables)
        #self.actionShowSystemTables.setCheckable(True)
        #self.actionShowSystemTables.setChecked(True)
        actionMenuTable.setVisible(False)

        # add actions to the toolbar
        self.toolBar.addAction(self.actionRefresh)
        self.toolBar.addAction(self.actionSqlWindow)
        self.toolBar.addAction(self.actionImport)
        self.toolBar.addAction(self.actionExport)
Exemple #33
0
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)
Exemple #34
0
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)
Exemple #35
0
class MyMainWindow(QMainWindow):
    ' Main Window '

    def __init__(self, parent=None):
        ' Initialize QWidget inside MyMainWindow '
        super(MyMainWindow, self).__init__(parent)
        self.statusBar().showMessage(__doc__.title())
        self.setWindowTitle(__doc__)
        self.setMinimumSize(600, 800)
        self.setMaximumSize(2048, 1024)
        self.resize(1024, 800)
        self.setWindowIcon(QIcon.fromTheme("face-monkey"))
        if not A11Y:
            self.setStyleSheet('''QWidget{color:#fff;font-family:Oxygen}
            QWidget:item:hover, QWidget:item:selected {
                background-color: cyan; color: #000
            }
            QWidget:disabled { color: #404040; background-color: #323232 }
            QWidget:focus { border: 1px solid cyan }
            QPushButton {
                background-color: gray;
                padding: 3px; border: 1px solid gray; border-radius: 9px;
                margin: 0;font-size: 12px;
                padding-left: 5px; padding-right: 5px
            }
            QLineEdit, QTextEdit {
                background-color: #4a4a4a; border: 1px solid gray;
                border-radius: 0; font-size: 12px;
            }
            QPushButton:pressed { background-color: #323232 }
            QComboBox {
                background-color: #4a4a4a; padding-left: 9px;
                border: 1px solid gray; border-radius: 5px;
            }
            QComboBox:pressed { background-color: gray }
            QComboBox QAbstractItemView, QMenu {
                border: 1px solid #4a4a4a; background:grey;
                selection-background-color: cyan;
                selection-color: #000;
            }
            QSlider {
                padding: 3px; font-size: 8px; padding-left: 2px;
                padding-right: 2px; border: 5px solid #1e1e1e
            }
            QSlider::sub-page:vertical {
                background-color: QLinearGradient(spread:pad, x1:0, y1:0, x2:1,
                    y2:0.27, stop:0 rgba(255, 0, 0, 255),
                    stop:1 rgba(50, 0, 0, 200));
                border: 4px solid #1e1e1e; border-radius: 5px
            }
            QSlider::add-page:vertical {
                background-color: QLinearGradient(spread:pad, x1:0, y1:0, x2:1,
                    y2:0.27, stop:0 rgba(0, 255, 0, 255),
                    stop:1 rgba(0, 99, 0, 255));
                border: 4px solid #1e1e1e; border-radius: 5px;
            }
            QSlider::handle:vertical {
                background-color: QLinearGradient(spread:pad, x1:0, y1:0, x2:1,
                    y2:0.273, stop:0 rgba(0, 0, 0, 255), stop:1 gray);
                height: 5px; border: 1px dotted #fff; text-align: center;
                border-top-left-radius: 2px; border-bottom-left-radius: 2px;
                border-top-right-radius: 2px; border-bottom-right-radius 2px;
                margin-left: 2px; margin-right: 2px;
            }
            QSlider::handle:vertical:hover { border: 1px solid cyan }
            QSlider::sub-page:vertical:disabled {
                background: #bbb; border-color: #999;
            }
            QSlider::add-page:vertical:disabled {
                background: #eee; border-color: #999;
            }
            QSlider::handle:vertical:disabled {
                background: #eee; border: 1px solid #aaa; border-radius: 4px;
            }
            QToolBar, QStatusBar, QDockWidget::title{background-color:#323232;}
            QToolBar::handle,
            QToolBar::handle:vertical, QToolBar::handle:horizontal {
                border: 1px solid gray; border-radius: 9px; width: 19px;
                height: 19px; margin: 0.5px
            }
            QGroupBox {
                border: 1px solid gray; border-radius: 9px; padding-top: 9px;
            }
            QStatusBar, QToolBar::separator:horizontal,
            QToolBar::separator:vertical {color:gray}
            QScrollBar:vertical{
                background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0,
                    stop: 0 #212121,stop: 1.0 #323232);
                width: 10px;
            }
            QScrollBar:horizontal{
                background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                    stop: 0 #212121,stop: 1.0 #323232);
                height: 10px;
            }
            QScrollBar::handle:vertical{
                padding: 2px;
                min-height: 50px;
                background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0,
                    stop: 0 #585858,stop: 1.0 #404040);
                border-radius: 5px;
                border: 1px solid #191919;
            }
            QScrollBar::handle:horizontal{
                padding: 2px;
                min-width: 50px;
                background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
                    stop: 0 #585858,stop: 1.0 #404040);
                border-radius: 5px;
                border: 1px solid #191919;
            }
            QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical,
            QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical,
            QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal,
            QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal {
                background: none; border: none;
            }
            QDockWidget::close-button, QDockWidget::float-button {
                border: 1px solid gray;
                border-radius: 3px;
                background: darkgray;
            }''')

        self.process = QProcess()
        self.process.readyReadStandardOutput.connect(self.read_output)
        self.process.readyReadStandardError.connect(self.read_errors)
        self.process.finished.connect(self._process_finished)
        self.process.error.connect(self._process_finished)

        self.group0, self.group1 = QGroupBox("Options"), QGroupBox("Paths")
        self.group2 = QGroupBox("Nodes")
        self.group3 = QGroupBox("Python Code")
        self.group4, self.group5 = QGroupBox("Logs"), QGroupBox("Backend")
        g0grid, g1vlay = QGridLayout(self.group0), QVBoxLayout(self.group1)
        g5vlay = QVBoxLayout(self.group5)

        self.treeview_nodes, self.textedit_source = QTextEdit(), QTextEdit()
        self.dock1, self.dock2 = QDockWidget(), QDockWidget()
        self.output, self.dock3 = QTextEdit(), QDockWidget()
        self.treeview_nodes.setAutoFormatting(QTextEdit.AutoAll)
        self.treeview_nodes.setWordWrapMode(QTextOption.NoWrap)
        self.dock1.setWidget(self.treeview_nodes)
        self.dock2.setWidget(self.textedit_source)
        self.dock3.setWidget(self.output)
        self.dock1.setWindowTitle("Tree")
        self.dock2.setWindowTitle("Sources")
        self.dock3.setWindowTitle("STDOutput")
        featur = QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable
        self.dock1.setFeatures(featur)
        self.dock2.setFeatures(featur)
        self.dock3.setFeatures(featur)
        QVBoxLayout(self.group2).addWidget(self.dock1)
        QVBoxLayout(self.group3).addWidget(self.dock2)
        QVBoxLayout(self.group4).addWidget(self.dock3)
        self.slider1, self.slider2 = QSlider(), QSlider()
        g0grid.addWidget(self.slider1, 0, 0)
        g0grid.addWidget(QLabel('Use Debug'), 0, 1)
        self.slider2.setValue(1)
        g0grid.addWidget(self.slider2, 1, 0)
        g0grid.addWidget(QLabel('Use verbose'), 1, 1)

        self.slider3, self.slider4 = QSlider(), QSlider()
        self.slider3.setValue(1)
        g0grid.addWidget(self.slider3, 2, 0)
        g0grid.addWidget(QLabel('Show compiling progress'), 2, 1)
        self.slider4.setValue(1)
        g0grid.addWidget(self.slider4, 3, 0)
        g0grid.addWidget(QLabel('Show Scons building debug'), 3, 1)

        self.slider5, self.slider6 = QSlider(), QSlider()
        g0grid.addWidget(self.slider5, 4, 0)
        g0grid.addWidget(QLabel('Keep debug unstriped binary'), 4, 1)
        g0grid.addWidget(self.slider6, 5, 0)
        g0grid.addWidget(QLabel('Traced execution outputs'), 5, 1)

        self.slider7, self.slider8 = QSlider(), QSlider()
        self.slider7.setValue(1)
        g0grid.addWidget(self.slider7, 6, 0)
        g0grid.addWidget(QLabel('Remove the build folder'), 6, 1)
        g0grid.addWidget(self.slider8, 7, 0)
        g0grid.addWidget(QLabel('No Python Optimizations'), 7, 1)

        self.slider9, self.slider10 = QSlider(), QSlider()
        g0grid.addWidget(self.slider9, 8, 0)
        g0grid.addWidget(QLabel('No Statements line numbers'), 8, 1)
        g0grid.addWidget(self.slider10, 9, 0)
        g0grid.addWidget(QLabel('Execute the output binary'), 9, 1)

        self.slider11, self.slider12 = QSlider(), QSlider()
        g0grid.addWidget(self.slider11, 10, 0)
        g0grid.addWidget(QLabel('Warning detected implicit exceptions'), 10, 1)
        g0grid.addWidget(self.slider12, 11, 0)
        g0grid.addWidget(QLabel('Keep the PYTHONPATH, do not Reset it'), 11, 1)

        self.slider13 = QSlider()
        g0grid.addWidget(self.slider13, 12, 0)
        g0grid.addWidget(QLabel('Enhance compile, CPython incompatible'), 12,
                         1)

        self.slider1a, self.slider2a = QSlider(), QSlider()
        g0grid.addWidget(self.slider1a, 0, 2)
        g0grid.addWidget(QLabel('Descendent Recursive Compile'), 0, 3)
        self.slider2a.setValue(1)
        g0grid.addWidget(self.slider2a, 1, 2)
        g0grid.addWidget(QLabel('Force non recursive compile'), 1, 3)

        self.slider3a, self.slider4a = QSlider(), QSlider()
        g0grid.addWidget(self.slider3a, 2, 2)
        g0grid.addWidget(QLabel('STD Lib Recursive Compile'), 2, 3)
        g0grid.addWidget(self.slider4a, 3, 2)
        g0grid.addWidget(QLabel('Enforce the use of Clang'), 3, 3)

        self.slider5a, self.slider6a = QSlider(), QSlider()
        self.slider5a.setValue(1)
        g0grid.addWidget(self.slider5a, 4, 2)
        g0grid.addWidget(QLabel('Use G++ link time optimizations'), 4, 3)
        g0grid.addWidget(self.slider6a, 5, 2)
        g0grid.addWidget(QLabel('Disable the console window'), 5, 3)

        self.slider7a, self.slider8a = QSlider(), QSlider()
        g0grid.addWidget(self.slider7a, 6, 2)
        g0grid.addWidget(QLabel('Force compile for MS Windows'), 6, 3)
        g0grid.addWidget(self.slider8a, 7, 2)
        g0grid.addWidget(QLabel('Use Python Debug versions'), 7, 3)

        self.slider9a, self.slider10a = QSlider(), QSlider()
        self.slider9a.setValue(1)
        g0grid.addWidget(self.slider9a, 8, 2)
        g0grid.addWidget(QLabel('Create standalone executable'), 8, 3)
        g0grid.addWidget(self.slider10a, 9, 2)
        g0grid.addWidget(QLabel('Enable Standalone mode build'), 9, 3)

        self.slider11a, self.slider12a = QSlider(), QSlider()
        g0grid.addWidget(self.slider11a, 10, 2)
        g0grid.addWidget(QLabel('Make module executable instead of app'), 10,
                         3)
        g0grid.addWidget(self.slider12a, 11, 2)
        g0grid.addWidget(QLabel('No froze module of stdlib as bytecode'), 11,
                         3)

        self.slider13a = QSlider()
        g0grid.addWidget(self.slider13a, 12, 2)
        g0grid.addWidget(QLabel('Force use of MinGW on MS Windows'), 12, 3)

        for each_widget in (self.slider1, self.slider2, self.slider3,
                            self.slider4, self.slider5, self.slider6,
                            self.slider7, self.slider8, self.slider9,
                            self.slider10, self.slider11, self.slider12,
                            self.slider13, self.slider1a, self.slider2a,
                            self.slider3a, self.slider4a, self.slider5a,
                            self.slider6a, self.slider7a, self.slider8a,
                            self.slider9a, self.slider10a, self.slider11a,
                            self.slider12a, self.slider13a):
            each_widget.setRange(0, 1)
            each_widget.setCursor(QCursor(Qt.OpenHandCursor))
            each_widget.setTickInterval(1)
            each_widget.TickPosition(QSlider.TicksBothSides)

        self.combo1 = QComboBox()
        self.combo1.addItems(('2.7', '2.6', '3.2', '3.3'))
        g5vlay.addWidget(QLabel('Python Version'))
        g5vlay.addWidget(self.combo1)
        self.combo2 = QComboBox()
        self.combo2.addItems(('Default', 'Low', 'High'))
        g5vlay.addWidget(QLabel('CPU priority'))
        g5vlay.addWidget(self.combo2)
        self.combo3 = QComboBox()
        self.combo3.addItems(('1', '2', '3', '4', '5', '6', '7', '8', '9'))
        g5vlay.addWidget(QLabel('MultiProcessing Workers'))
        g5vlay.addWidget(self.combo3)

        self.outdir = QLineEdit()
        self.outdir.setStyleSheet("QLineEdit{margin-left:25px}")
        self.clearButton = QToolButton(self.outdir)
        self.clearButton.setIcon(QIcon.fromTheme("edit-clear"))
        self.clearButton.setIconSize(QSize(25, 25))
        self.clearButton.setStyleSheet("QToolButton{border:none}")
        self.clearButton.hide()
        self.clearButton.clicked.connect(self.outdir.clear)
        self.outdir.textChanged.connect(
            lambda: self.clearButton.setVisible(True))
        self.clearButton.clicked.connect(
            lambda: self.clearButton.setVisible(False))
        self.outdir.setPlaceholderText('Output Directory')
        if path.isfile('.nuitka-output-dir.txt'):
            self.outdir.setText(open('.nuitka-output-dir.txt', 'r').read())
        else:
            self.outdir.setText(path.expanduser("~"))
        self.completer, self.dirs = QCompleter(self), QDirModel(self)
        self.dirs.setFilter(QDir.Dirs | QDir.NoDotAndDotDot)
        self.completer.setModel(self.dirs)
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.completer.setCompletionMode(QCompleter.PopupCompletion)
        self.completer.popup().setStyleSheet(
            """border:1px solid #4a4a4a;background:grey;
            selection-background-color:cyan;selection-color:#000""")
        self.completer.popup().setVerticalScrollBarPolicy(
            Qt.ScrollBarAlwaysOff)
        self.outdir.setCompleter(self.completer)

        self.btn1 = QPushButton(QIcon.fromTheme("document-open"),
                                'Open' if IS_WIN else '')
        self.btn1.clicked.connect(
            lambda: open('.nuitka-output-dir.txt', 'w').write(
                str(
                    QFileDialog.getExistingDirectory(
                        None, 'Open Output Directory', path.expanduser("~")))))
        self.btn1.released.connect(lambda: self.outdir.setText(
            open('.nuitka-output-dir.txt', 'r').read()))
        g1vlay.addWidget(QLabel('Output Directory'))
        g1vlay.addWidget(self.outdir)
        g1vlay.addWidget(self.btn1)

        self.target = QLineEdit()
        self.target.setStyleSheet("QLineEdit{margin-left:25px}")
        self.clearButton2 = QToolButton(self.target)
        self.clearButton2.setIcon(QIcon.fromTheme("edit-clear"))
        self.clearButton2.setIconSize(QSize(25, 25))
        self.clearButton2.setStyleSheet("QToolButton{border:none}")
        self.clearButton2.hide()
        self.clearButton2.clicked.connect(self.target.clear)
        self.target.textChanged.connect(
            lambda: self.clearButton2.setVisible(True))
        self.clearButton2.clicked.connect(
            lambda: self.clearButton2.setVisible(False))
        self.target.setPlaceholderText('Target Python App to Binary Compile')
        self.target.setCompleter(self.completer)
        self.btn2 = QPushButton(QIcon.fromTheme("document-open"),
                                'Open' if IS_WIN else '')
        self.btn2.clicked.connect(lambda: self.target.setText(
            str(
                QFileDialog.getOpenFileName(
                    None, "Open", path.expanduser("~"), ';;'.join([
                        '{}(*.{})'.format(e.upper(), e)
                        for e in ('py', 'pyw', '*')
                    ])))))
        g1vlay.addWidget(QLabel('Input File'))
        g1vlay.addWidget(self.target)
        g1vlay.addWidget(self.btn2)

        self.icon, self.icon_label = QLineEdit(), QLabel('Icon File')
        self.icon.setStyleSheet("QLineEdit{margin-left:25px}")
        self.clearButton3 = QToolButton(self.icon)
        self.clearButton3.setIcon(QIcon.fromTheme("edit-clear"))
        self.clearButton3.setIconSize(QSize(25, 25))
        self.clearButton3.setStyleSheet("QToolButton{border:none}")
        self.clearButton3.hide()
        self.clearButton3.clicked.connect(self.icon.clear)
        self.icon.textChanged.connect(
            lambda: self.clearButton3.setVisible(True))
        self.clearButton3.clicked.connect(
            lambda: self.clearButton3.setVisible(False))
        self.icon.setPlaceholderText('Path to Icon file for your App')
        self.icon.setCompleter(self.completer)
        self.btn3 = QPushButton(QIcon.fromTheme("document-open"),
                                'Open' if IS_WIN else '')
        self.btn3.clicked.connect(lambda: self.icon.setText(
            str(
                QFileDialog.getOpenFileName(
                    None, "Open", path.expanduser("~"), ';;'.join([
                        '{}(*.{})'.format(e.upper(), e)
                        for e in ('ico', 'png', 'bmp', 'svg', '*')
                    ])))))
        g1vlay.addWidget(self.icon_label)
        g1vlay.addWidget(self.icon)
        g1vlay.addWidget(self.btn3)

        # Menu Bar inicialization and detail definitions
        menu_salir = QAction(QIcon.fromTheme("application-exit"), 'Quit', self)
        menu_salir.setStatusTip('Quit')
        menu_salir.triggered.connect(exit)
        menu_minimize = QAction(QIcon.fromTheme("go-down"), 'Minimize', self)
        menu_minimize.setStatusTip('Minimize')
        menu_minimize.triggered.connect(lambda: self.showMinimized())
        menu_qt = QAction(QIcon.fromTheme("help-about"), 'About Qt', self)
        menu_qt.setStatusTip('About Qt...')
        menu_qt.triggered.connect(lambda: QMessageBox.aboutQt(self))
        menu_dev = QAction(QIcon.fromTheme("applications-development"),
                           'Developer Manual PDF', self)
        menu_dev.setStatusTip('Open Nuitka Developer Manual PDF...')
        menu_dev.triggered.connect(lambda: call(
            OPEN + '/usr/share/doc/nuitka/Developer_Manual.pdf.gz', shell=True)
                                   )
        menu_usr = QAction(QIcon.fromTheme("help-contents"), 'User Docs', self)
        menu_usr.setStatusTip('Open Nuitka End User Manual PDF...')
        menu_usr.triggered.connect(lambda: call(
            OPEN + '/usr/share/doc/nuitka/README.pdf.gz', shell=True))
        menu_odoc = QAction(QIcon.fromTheme("help-browser"), 'OnLine Doc',
                            self)
        menu_odoc.setStatusTip('Open Nuitka on line Documentation pages...')
        menu_odoc.triggered.connect(
            lambda: open_new_tab('http://nuitka.net/doc/user-manual.html'))
        menu_man = QAction(QIcon.fromTheme("utilities-terminal"), 'Man', self)
        menu_man.setStatusTip('Open Nuitka technical command line Man Pages..')
        menu_man.triggered.connect(
            lambda: call('xterm -e "man nuitka"', shell=True))
        menu_tra = QAction(QIcon.fromTheme("applications-development"),
                           'View Nuitka-GUI Source Code', self)
        menu_tra.setStatusTip('View, study, edit Nuitka-GUI Libre Source Code')
        menu_tra.triggered.connect(lambda: call(OPEN + __file__, shell=True))
        menu_foo = QAction(QIcon.fromTheme("folder"), 'Open Output Dir', self)
        menu_foo.setStatusTip('Open the actual Output Directory location...')
        menu_foo.triggered.connect(
            lambda: call(OPEN + str(self.outdir.text()), shell=True))
        menu_pic = QAction(QIcon.fromTheme("camera-photo"), 'Screenshot', self)
        menu_pic.setStatusTip('Take a Screenshot for Documentation purposes..')
        menu_pic.triggered.connect(
            lambda: QPixmap.grabWindow(QApplication.desktop().winId()).save(
                QFileDialog.getSaveFileName(None, "Save", path.expanduser("~"),
                                            'PNG(*.png)', 'png')))
        menu_don = QAction(QIcon.fromTheme("emblem-favorite"), 'Help Nuitka',
                           self)
        menu_don.setStatusTip('Help the Nuitka Open Source Libre Free Project')
        menu_don.triggered.connect(
            lambda: open_new_tab('http://nuitka.net/pages/donations.html'))

        # movable draggable toolbar
        self.toolbar = QToolBar(self)
        self.toolbar.setIconSize(QSize(16, 16))
        self.toolbar.toggleViewAction().setText("Show/Hide Toolbar")
        l_spacer, r_spacer = QWidget(self), QWidget(self)
        l_spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        r_spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.toolbar.addWidget(l_spacer)
        self.toolbar.addSeparator()
        self.toolbar.addActions((menu_salir, menu_minimize, menu_qt, menu_odoc,
                                 menu_foo, menu_pic, menu_don))
        if not IS_WIN:
            self.toolbar.addActions((menu_man, menu_dev, menu_tra, menu_usr))
        self.toolbar.addSeparator()
        self.toolbar.addWidget(r_spacer)
        self.addToolBar(Qt.BottomToolBarArea, self.toolbar)

        # Bottom Buttons Bar
        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Ok
                                          | QDialogButtonBox.Close)
        self.buttonBox.rejected.connect(exit)
        self.buttonBox.accepted.connect(self.run)

        self.guimode = QComboBox()
        self.guimode.addItems(('Full UX / UI', 'Simple UX / UI'))
        self.guimode.setStyleSheet(
            """QComboBox{background:transparent;border:0;
            margin-left:25px;color:gray;text-decoration:underline}""")
        self.guimode.currentIndexChanged.connect(self.set_guimode)

        container = QWidget()
        container_layout = QGridLayout(container)  # Y, X
        container_layout.addWidget(self.guimode, 0, 1)
        container_layout.addWidget(self.group2, 1, 0)
        container_layout.addWidget(self.group3, 2, 0)
        container_layout.addWidget(self.group0, 1, 1)
        container_layout.addWidget(self.group1, 2, 1)
        container_layout.addWidget(self.group4, 1, 2)
        container_layout.addWidget(self.group5, 2, 2)
        container_layout.addWidget(self.buttonBox, 3, 1)
        self.setCentralWidget(container)
        # Paleta de colores para pintar transparente
        if not A11Y:
            palette = self.palette()
            palette.setBrush(QPalette.Base, Qt.transparent)
            self.setPalette(palette)
            self.setAttribute(Qt.WA_OpaquePaintEvent, False)

    def get_fake_tree(self, target):
        """Return the fake tree."""
        try:
            fake_tree = check_output(NUITKA + ' --dump-xml ' + target,
                                     shell=True)
        except:
            fake_tree = "ERROR: Failed to get Tree Dump."
        finally:
            return fake_tree.strip()

    def run(self):
        ' run the actual backend process '
        self.treeview_nodes.clear()
        self.textedit_source.clear()
        self.output.clear()
        self.statusBar().showMessage('WAIT!, Working...')
        target = str(self.target.text()).strip()
        self.treeview_nodes.setText(self.get_fake_tree(target))
        self.textedit_source.setText(open(target, "r").read().strip())
        conditional_1 = sys.platform.startswith('linux')
        conditional_2 = self.combo3.currentIndex() != 2
        command_to_run_nuitka = " ".join(
            ('chrt -i 0' if conditional_1 and conditional_2 else '', NUITKA,
             '--debug' if self.slider1.value() else '',
             '--verbose' if self.slider2.value() else '',
             '--show-progress' if self.slider3.value() else '',
             '--show-scons --show-modules' if self.slider4.value() else '',
             '--unstriped' if self.slider5.value() else '',
             '--trace-execution' if self.slider6.value() else '',
             '--remove-output' if self.slider7.value() else '',
             '--no-optimization' if self.slider8.value() else '',
             '--code-gen-no-statement-lines' if self.slider9.value() else '',
             '--execute' if self.slider10.value() else '',
             '--recurse-all' if self.slider1a.value() else '',
             '--recurse-none' if self.slider2a.value() else '',
             '--recurse-stdlib' if self.slider3a.value() else '',
             '--clang' if self.slider4a.value() else '',
             '--lto' if self.slider5a.value() else '',
             '--windows-disable-console' if self.slider6a.value() else '',
             '--windows-target' if self.slider7a.value() else '',
             '--python-debug' if self.slider8a.value() else '',
             '--exe' if self.slider9a.value() else '',
             '--standalone' if self.slider10a.value() else '',
             '--module' if self.slider11a.value() else '',
             '--nofreeze-stdlib' if self.slider12a.value() else '',
             '--mingw' if self.slider13a.value() else '',
             '--warn-implicit-exceptions' if self.slider11.value() else '',
             '--execute-with-pythonpath' if self.slider12.value() else '',
             '--enhanced' if self.slider13.value() else '',
             '--icon="{}"'.format(self.icon.text())
             if self.icon.text() else '', '--python-version={}'.format(
                 self.combo1.currentText()), '--jobs={}'.format(
                     self.combo3.currentText()), '--output-dir="{}"'.format(
                         self.outdir.text()), "{}".format(target)))
        if DEBUG:
            print(command_to_run_nuitka)
        self.process.start(command_to_run_nuitka)
        if not self.process.waitForStarted() and not IS_WIN:
            return  # ERROR !
        self.statusBar().showMessage(__doc__.title())

    def _process_finished(self):
        """finished sucessfully"""
        self.output.setFocus()
        self.output.selectAll()

    def read_output(self):
        """Read and append output to the log"""
        self.output.append(str(self.process.readAllStandardOutput()))

    def read_errors(self):
        """Read and append errors to the log"""
        self.output.append(str(self.process.readAllStandardError()))

    def paintEvent(self, event):
        """Paint semi-transparent background,animated pattern,background text"""
        if not A11Y:
            p = QPainter(self)
            p.setRenderHint(QPainter.Antialiasing)
            p.setRenderHint(QPainter.TextAntialiasing)
            p.setRenderHint(QPainter.HighQualityAntialiasing)
            p.fillRect(event.rect(), Qt.transparent)
            # animated random dots background pattern
            for i in range(4096):
                x = randint(25, self.size().width() - 25)
                y = randint(25, self.size().height() - 25)
                # p.setPen(QPen(QColor(randint(9, 255), 255, 255), 1))
                p.drawPoint(x, y)
            p.setPen(QPen(Qt.white, 1))
            p.rotate(40)
            p.setFont(QFont('Ubuntu', 250))
            p.drawText(200, 99, "Nuitka")
            p.rotate(-40)
            p.setPen(Qt.NoPen)
            p.setBrush(QColor(0, 0, 0))
            p.setOpacity(0.8)
            p.drawRoundedRect(self.rect(), 9, 9)
            p.end()

    def set_guimode(self):
        """Switch between simple and full UX"""
        for widget in (self.group2, self.group3, self.group4, self.group5,
                       self.icon, self.icon_label, self.btn3, self.toolbar,
                       self.statusBar()):
            widget.hide() if self.guimode.currentIndex() else widget.show()
Exemple #36
0
    slice_settings = SliceSettingsWidget(max_slice_count=nz, color_scales=color_scales.keys())
    slice_settings.inactiveCellsHidden.connect(viewer.hideInactiveCells)
    slice_settings.currentSliceChanged.connect(viewer.setCurrentSlice)
    slice_settings.toggleOrthographicProjection.connect(viewer.useOrthographicProjection)
    slice_settings.toggleLighting.connect(viewer.useLighting)
    slice_settings.colorScalesChanged.connect(viewer.changeColorScale)
    slice_settings.regionToggling.connect(viewer.useRegionScaling)
    slice_settings.toggleInterpolation.connect(viewer.useInterpolationOnData)
    slice_settings.mirrorX.connect(viewer.mirrorX)
    slice_settings.mirrorY.connect(viewer.mirrorY)
    slice_settings.mirrorZ.connect(viewer.mirrorZ)
    slice_settings.toggleFlatPolylines.connect(viewer.toggleFlatPolylines)


    dock_widget = QDockWidget("Settings")
    dock_widget.setObjectName("SliceSettingsDock")
    dock_widget.setWidget(slice_settings)
    dock_widget.setAllowedAreas(Qt.AllDockWidgetAreas)
    dock_widget.setFeatures(QDockWidget.NoDockWidgetFeatures)

    window.addDockWidget(Qt.LeftDockWidgetArea, dock_widget)


    window.setCentralWidget(viewer)

    window.show()
    window.activateWindow()
    window.raise_()
    app.exec_()
class DBMainWindow(QMainWindow):
    """Main Window and Portal
    
    .. todo:: Remember the dock position
    """

    W_NAME = "DBMainWindow"

    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)

        self.db = None

        self.setWindowTitle("PyQtDb")
        self.setWindowIcon(Ico.icon(Ico.FavIcon))

        self.setMinimumWidth(800)
        self.setMinimumHeight(800)
        """
        topBar = QToolBar()
        self.addToolBar(Qt.TopToolBarArea, topBar)
        
        topBar.addAction("New Server")
        """

        ## Servers Widget
        self.dockServers = QDockWidget("Servers")
        self.dockServers.setFeatures(QDockWidget.DockWidgetMovable)
        self.dockServers.setAllowedAreas(Qt.LeftDockWidgetArea
                                         | Qt.RightDockWidgetArea)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.dockServers)

        self.serversWidget = DBServersWidget.DBServersWidget(self)
        """Instance of :py:class:`~pyqtdb.DBServersWidget.DBServersWidget` in dock"""
        self.dockServers.setWidget(self.serversWidget)
        self.connect(self.serversWidget, SIGNAL("open_server"),
                     self.on_open_server)

        self.cenWid = QWidget()
        self.setCentralWidget(self.cenWid)

        self.mainLayout = QHBoxLayout()
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.mainLayout.setSpacing(0)
        self.cenWid.setLayout(self.mainLayout)

        self.tabWidget = QTabWidget()
        """The main tabs"""
        self.tabWidget.setTabsClosable(True)
        self.mainLayout.addWidget(self.tabWidget)

        G.settings.restore_window(self)

    def on_open_server(self, srv):
        """Opens server by adding a :py:class:`~pyqtdb.DBBrowser.DBBrowser` in the the tabWidget"""
        server = G.settings.get_server(str(srv))
        print "oopen", srv, server
        widget = DBBrowser.DBBrowser(self, server)
        self.tabWidget.addTab(widget, Ico.icon(Ico.Server), server['server'])
        self.tabWidget.setCurrentIndex(self.tabWidget.indexOf(widget))

    def closeEvent(self, event):
        """Save window settings on close with :py:class:`~pyqtdb.XSettings.XSettings.save_window` """
        G.settings.save_window(self)
Exemple #38
0
class filexplorerPluginMain(plugin.Plugin):
    ' main class for plugin '
    def initialize(self, *args, **kwargs):
        ' class init '
        global CONFIG_DIR
        ec = ExplorerContainer()
        super(filexplorerPluginMain, self).initialize(*args, **kwargs)

        self.dock = QDockWidget()
        self.dock.setAllowedAreas(Qt.LeftDockWidgetArea |
                                  Qt.RightDockWidgetArea)
        self.dock.setFeatures(QDockWidget.DockWidgetFloatable |
                              QDockWidget.DockWidgetMovable)
        self.dock.setWindowTitle("fileXplorer")
        self.dock.setStyleSheet('QDockWidget::title { text-align: center; }')

        # search for the truth
        self.srch = QLineEdit()
        #self.srch.resize(self.srch.size().height(), self.dock.size().width())
        self.srch.setPlaceholderText(' Search for Python files Local or PyPI ')
        self.srch.returnPressed.connect(self.search)

        # Disk Usage Bar
        self.hdbar = QProgressBar()
        if sys.platform != 'win32':
            self.hdbar.setMaximum(statvfs(HOME).f_blocks *
                statvfs(HOME).f_frsize / 1024 / 1024 / 1024)
            self.hdbar.setValue(statvfs(HOME).f_bfree *
                statvfs(HOME).f_frsize / 1024 / 1024 / 1024)
        self.hdbar.setToolTip(str(self.hdbar.value()) + '% Total Disk Use ')
        #self.hdbar.setStyleSheet('''QProgressBar{background-color:
        #QLinearGradient(spread:pad,x1:0,y1:0,x2:1,y2:1,stop:0 rgba(255,0,0,99),
        #stop:1 rgba(9,255,9,200));color:#fff;border:none;border-radius:9px;}
        #QProgressBar::chunk{background-color:QLinearGradient(spread:pad,y1:0,
        #x1:0,y2:1,x2:0.27,stop:0 rgb(0,0,0),stop:1 rgb(9,99,255));padding:0;
        #border:none;border-radius:9px;height:9px;margin:1px;}''')

        self.model = QDirModel()
        self.fileView = QColumnView(self.dock)
        self.fileView.setAlternatingRowColors(True)
        # self.fileView.setFont(QFont(self.fileView.font().setBold(True)))
        self.fileView.setIconSize(QSize(32, 32))
        self.fileView.setModel(self.model)
        self.fileView.updatePreviewWidget.connect(self.runfile)

        self.sli = QSlider()
        self.sli.setRange(16, 128)
        self.sli.setValue(32)
        self.sli.setToolTip('Icon Size: 32 px. Move Slider to change.')
        self.sli.setOrientation(Qt.Horizontal)
        self.sli.valueChanged.connect(lambda: self.fileView.setIconSize(
            QSize(self.sli.value(), self.sli.value())))
        self.sli.sliderReleased.connect(lambda:
            self.sli.setToolTip('Icon Size: ' + str(self.sli.value())))

        class TransientWidget(QWidget):
            ' persistant widget thingy '
            def __init__(self, widget_list):
                ' init sub class '
                super(TransientWidget, self).__init__()
                vbox = QVBoxLayout(self)
                for each_widget in widget_list:
                    vbox.addWidget(each_widget)

        tw = TransientWidget((self.srch, self.dock, self.sli, self.hdbar))
        ec.addTab(tw, "fileXplorer")

        ####

        self.process = QProcess()
        self.process.finished.connect(self.processFinished)

        self.preview = QLabel(self.fileView)
        self.preview.setTextFormat(0)
        self.preview.setStyleSheet('QLabel{font-size:9px;}')
        self.preview.setAutoFillBackground(True)
        self.fileView.setPreviewWidget(self.preview)
        self.dock.setWidget(self.fileView)

        # take a shot
        self.pic = QAction(QIcon.fromTheme("camera-photo"), 'Screenshot', self)
        self.pic.triggered.connect(lambda: QPixmap.grabWindow(
            QApplication.desktop().winId()).save(QFileDialog.getSaveFileName(
            self.dock, " Save Screenshot As ... ", HOME, ';;(*.png)')))

        # copy time
        self.tim = QAction(QIcon.fromTheme("user-away"),
                           'Date and Time to Clipboard', self)
        self.tim.triggered.connect(lambda: QApplication.clipboard().setText(
            datetime.now().strftime(" %A %B %d-%m-%Y %H:%M:%S %p ")))

        # color chooser
        self.cl = QAction(QIcon.fromTheme("applications-graphics"),
                          'Color Chooser to Clipboard', self)
        self.cl.triggered.connect(lambda: QApplication.clipboard().setText(
            '{}'.format(QColorDialog.getColor().name())))

        # icon chooser
        self.icn = QAction(QIcon.fromTheme("insert-image"),
                          'Icon Chooser to Clipboard', self)
        self.icn.triggered.connect(self.iconChooser)

        # tool bar with actions
        QToolBar(self.dock).addActions((self.cl, self.icn, self.tim, self.pic))

        self.textBrowser = QTextBrowser(self.dock)
        self.textBrowser.setAutoFillBackground(True)
        self.textBrowser.setGeometry(self.dock.geometry())
        self.textBrowser.hide()

    def processFinished(self):
        ' print info of finished processes '
        print(" INFO: OK: QProcess finished . . . ")

    def search(self):
        ' function to search python files '
        # get search results of python filenames local or remote
        pypi_url = 'http://pypi.python.org/pypi'
        # pypi query
        pypi = xmlrpclib.ServerProxy(pypi_url, transport=ProxyTransport())
        try:
            pypi_query = pypi.search({'name': str(self.srch.text()).lower()})
            pypi_fls = list(set(['pypi.python.org/pypi/' + a['name'] +
                   ' | pip install ' + a['name'] for a in pypi_query]))
        except:
            pypi_fls = '<b> ERROR: Internet not available! ಠ_ಠ </b>'
        s_out = ('<br> <br> <br> <h3> Search Local Python files: </h3> <hr> ' +
        # Jedi list comprehension for LOCAL search
        str(["{}/{}".format(root, f) for root, f in list(itertools.chain(*
            [list(itertools.product([root], files))
            for root, dirs, files in walk(str(
            QFileDialog.getExistingDirectory(self.dock,
            'Open Directory to Search', path.expanduser("~"))))]))
            if f.endswith(('.py', '.pyw', '.pth')) and not f.startswith('.')
            and str(self.srch.text()).lower().strip() in f]
        ).replace(',', '<br>') + '<hr><h3> Search PyPI Python files: </h3>' +
        # wraped pypi query REMOTE search
        str(pypi_fls).replace(',', '<br>') + '<hr>Auto-Proxy:ON,DoNotTrack:ON')
        # print(s_out)
        try:
            call('notify-send fileXplorer Searching...', shell=True)
        except:
            pass
        self.srch.clear()
        self.textBrowser.setGeometry(self.dock.geometry())
        self.textBrowser.setHtml(s_out)
        self.textBrowser.show()
        tmr = QTimer(self.fileView)
        tmr.timeout.connect(self.textBrowser.hide)
        tmr.start(20000)

    def iconChooser(self):
        ' Choose a Icon and copy it to clipboard '
        #
        from .std_icon_naming import std_icon_naming as a
        #
        prv = QDialog(self.dock)
        prv.setWindowFlags(Qt.FramelessWindowHint)
        prv.setAutoFillBackground(True)
        prv.setGeometry(self.fileView.geometry())
        table = QTableWidget(prv)
        table.setColumnCount(1)
        table.setRowCount(len(a))
        table.verticalHeader().setVisible(True)
        table.horizontalHeader().setVisible(False)
        table.setShowGrid(True)
        table.setIconSize(QSize(128, 128))
        for index, icon in enumerate(a):
            item = QTableWidgetItem(QIcon.fromTheme(icon), '')
            # item.setData(Qt.UserRole, '')
            item.setToolTip(icon)
            table.setItem(index, 0, item)
        table.clicked.connect(lambda: QApplication.clipboard().setText(
          'QtGui.QIcon.fromTheme("{}")'.format(table.currentItem().toolTip())))
        table.doubleClicked.connect(prv.close)
        table.resizeColumnsToContents()
        table.resizeRowsToContents()
        QLabel('<h3> <br> 1 Click Copy, 2 Clicks Close </h3>', table)
        table.resize(prv.size())
        prv.exec_()

    def runfile(self, index):
        ' run the choosed file '
        s = str(file(self.model.filePath(index), 'r').read().strip())
        f = str(self.model.filePath(index))
        # ctime is NOT crossplatform,metadata change on *nix,creation on Window
        # http://docs.python.org/library/os.path.html#os.path.getctime
        m = ''.join((f, N, str(path.getsize(f) / 1024), ' Kilobytes', N,
            str(len(file(f, 'r').readlines())), ' Lines', N,
            str(len(s.replace(N, ''))), ' Characters', N,
            str(len([a for a in sub('[^a-zA-Z0-9 ]', '', s).split(' ')
                if a != ''])), ' Words', N,
            str(len([a for a in s if a in punctuation])), ' Punctuation', N,
            oct(stat(f).st_mode)[-3:], ' Permissions', N,
            time.ctime(path.getatime(f)), ' Accessed', N,
            time.ctime(path.getmtime(f)), ' Modified', N,
            'Owner: ', str(self.model.fileInfo(index).owner()), N,
            'Is Writable: ', str(self.model.fileInfo(index).isWritable()), N,
            'Is Executable: ', str(self.model.fileInfo(index).isExecutable()),
            N, 'Is Hidden: ', str(self.model.fileInfo(index).isHidden()), N,
            'Is SymLink: ', str(self.model.fileInfo(index).isSymLink()), N,
            'File Extension: ', str(self.model.fileInfo(index).suffix())
        ))
        #print(m)
        self.preview.setToolTip(m)
        self.preview.setText(s)
        self.preview.resize(self.preview.size().width(),
                            self.dock.size().height())
        self.process.start('xdg-open {}'.format(f))
        if not self.process.waitForStarted():
            print((" ERROR: Process {} Failed ! ".format(str(f))))
            return
class ComposerWrapper(QObject):
    """
    Embeds custom STDM tools in a QgsComposer instance for managing map-based
    STDM document templates.
    """
    dataSourceSelected = pyqtSignal(str)

    def __init__(self, composerView):
        QObject.__init__(self, composerView)

        self._compView = composerView
        self._stdmTB = self.mainWindow().addToolBar("STDM")
        self._selectMoveAction = None

        #Container for custom editor widgets
        self._widgetMappings = {}

        #Create dock widget for configuring STDM data source
        self._stdmDataSourceDock = QDockWidget(
            QApplication.translate("ComposerWrapper", "STDM Data Source"),
            self.mainWindow())
        self._stdmDataSourceDock.setObjectName("STDMDataSourceDock")
        self._stdmDataSourceDock.setMinimumWidth(300)
        self._stdmDataSourceDock.setFeatures(QDockWidget.DockWidgetMovable
                                             | QDockWidget.DockWidgetClosable)
        self.mainWindow().addDockWidget(Qt.RightDockWidgetArea,
                                        self._stdmDataSourceDock)

        dataSourceWidget = ComposerDataSourceSelector()
        self._stdmDataSourceDock.setWidget(dataSourceWidget)
        self._stdmDataSourceDock.show()

        #Create dock widget for configuring STDM item properties
        self._stdmItemPropDock = QDockWidget(
            QApplication.translate("ComposerWrapper", "STDM data properties"),
            self.mainWindow())
        self._stdmItemPropDock.setObjectName("STDMItemDock")
        self._stdmItemPropDock.setMinimumWidth(300)
        self._stdmItemPropDock.setFeatures(QDockWidget.DockWidgetMovable
                                           | QDockWidget.DockWidgetClosable)
        self.mainWindow().addDockWidget(Qt.RightDockWidgetArea,
                                        self._stdmItemPropDock)
        self._stdmItemPropDock.show()

        if self.itemDock() != None:
            self.mainWindow().tabifyDockWidget(self.itemDock(),
                                               self._stdmItemPropDock)

        if self.atlasDock() != None:
            self.atlasDock().hide()

        if self.generalDock() != None:
            self.generalDock().raise_()

        #Connect signals
        self.composition().itemRemoved.connect(self._onItemRemoved)
        dataSourceWidget.cboDataSource.currentIndexChanged[str].connect(
            self.propagateDataSourceSelection)
        self.composerView().selectedItemChanged.connect(self._onItemSelected)

        #Current template document file
        self._currDocFile = None

    def _removeActions(self):
        """
        Remove inapplicable actions and their corresponding toolbars and menus.
        """
        removeActions = [
            "mActionSaveProject", "mActionNewComposer",
            "mActionDuplicateComposer"
        ]

        composerToolbar = self.composerMainToolBar()
        if composerToolbar != None:
            saveProjectAction = None

            for itemAction in composerToolbar.actions():
                if itemAction.objectName() == "mActionSaveProject":
                    saveProjectAction = itemAction
                    break

            if saveProjectAction != None:
                composerMenu = saveProjectAction.menu()

    def configure(self):
        #Create instances of custom STDM composer item configurations
        for ciConfig in ComposerItemConfig.itemConfigurations:
            ciConfigObj = ciConfig(self)

    def addWidgetMapping(self, uniqueIdentifier, widget):
        """
        Add custom STDM editor widget based on the unique identifier of the composer item
        """
        self._widgetMappings[uniqueIdentifier] = widget

    def widgetMappings(self):
        """
        Returns a dictionary containing uuid values of composer items linked to STDM widgets.
        """
        return self._widgetMappings

    def clearWidgetMappings(self):
        """
        Resets the widget mappings collection.
        """
        self._widgetMappings = {}

    def mainWindow(self):
        """
        Returns the QMainWindow used by the composer view.
        """
        return self._compView.composerWindow()

    def stdmToolBar(self):
        """
        Returns the instance of the STDM toolbar added to the QgsComposer.
        """
        return self._stdmTB

    def composerView(self):
        """
        Returns the composer view.
        """
        return self._compView

    def composition(self):
        """
        Returns the QgsComposition instance used in the composer view.
        """
        return self._compView.composition()

    def composerItemToolBar(self):
        """
        Returns the toolbar containing actions for adding composer items.
        """
        return self.mainWindow().findChild(QToolBar, "mItemToolbar")

    def composerMainToolBar(self):
        """
        Returns the toolbar containing actions for managing templates.
        """
        return self.mainWindow().findChild(QToolBar, "mComposerToolbar")

    def selectMoveAction(self):
        """
        Returns the QAction for selecting or moving composer items.
        """
        if self.composerItemToolBar() != None:
            if self._selectMoveAction == None:
                for itemAction in self.composerItemToolBar().actions():
                    if itemAction.objectName() == "mActionSelectMoveItem":
                        self._selectMoveAction = itemAction
                        break

        return self._selectMoveAction

    def checkedItemAction(self):
        """
        Returns the currently selected composer item action.
        """
        if self.selectMoveAction() != None:
            return self.selectMoveAction().actionGroup().checkedAction()

        return None

    def itemDock(self):
        """
        Get the 'Item Properties' dock widget.
        """
        return self.mainWindow().findChild(QDockWidget, "ItemDock")

    def atlasDock(self):
        """
        Get the 'Atlas generation' dock widget.
        """
        return self.mainWindow().findChild(QDockWidget, "AtlasDock")

    def generalDock(self):
        """
        Get the 'Composition' dock widget.
        """
        return self.mainWindow().findChild(QDockWidget, "CompositionDock")

    def stdmDataSourceDock(self):
        """
        Returns the STDM data source dock widget.
        """
        return self._stdmDataSourceDock

    def stdmItemDock(self):
        """
        Returns the STDM item dock widget.
        """
        return self._stdmItemPropDock

    def documentFile(self):
        """
        Returns the QFile instance associated with the current document. 'None' will be returned for
        new, unsaved documents.
        """
        return self._currDocFile

    def setDocumentFile(self, docFile):
        """
        Sets the document file.
        """
        if not isinstance(docFile, QFile):
            return

        self._currDocFile = docFile

    def selectedDataSource(self):
        """
        Returns the name of the data source specified by the user.
        """
        return self._stdmDataSourceDock.widget().cboDataSource.currentText()

    def selectedDataSourceCategory(self):
        """
        Returns the category (view or table) that the data source belongs to.
        """
        if self.stdmDataSourceDock().widget() != None:
            return self.stdmDataSourceDock().widget().category()

        return ""

    def propagateDataSourceSelection(self, dataSourceName):
        """
        Propagates the signal when a user select a data source. Listening objects can hook on to it.
        """
        self.dataSourceSelected.emit(dataSourceName)

    def loadTemplate(self, filePath):
        """
        Loads a document template into the view and updates the necessary STDM-related controls.
        """
        if not QFile.exists(filePath):
            QMessageBox.critical(self.composerView(), QApplication.translate("OpenTemplateConfig","Open Template Error"), \
                                        QApplication.translate("OpenTemplateConfig","The specified template does not exist."))
            return

        templateFile = QFile(filePath)

        if not templateFile.open(QIODevice.ReadOnly):
            QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Open Operation Error"), \
                                            "{0}\n{1}".format(QApplication.translate("ComposerWrapper","Cannot read template file."), \
                                                      templateFile.errorString()
                                                      ))
            return

        templateDoc = QDomDocument()

        if templateDoc.setContent(templateFile):
            #Load items into the composition and configure STDM data controls
            self.composition().loadFromTemplate(templateDoc)
            self.clearWidgetMappings()

            #Load data controls
            composerDS = ComposerDataSource.create(templateDoc)
            self._configureDataControls(composerDS)

            #Load symbol editors
            spatialFieldsConfig = SpatialFieldsConfiguration.create(
                templateDoc)
            self._configureSpatialSymbolEditor(spatialFieldsConfig)

    def saveTemplate(self):
        """
        Creates and saves a new document template.
        """
        #Validate if the user has specified the data source
        if self.selectedDataSource() == "":
            QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Error"), \
                                            QApplication.translate("ComposerWrapper","Please specify the " \
                                                                   "data source name for the document composition."))
            return

        #If it is a new unsaved document template then prompt for the document name.
        docFile = self.documentFile()

        if docFile == None:
            docName,ok = QInputDialog.getText(self.composerView(), \
                                              QApplication.translate("ComposerWrapper","Template Name"), \
                                              QApplication.translate("ComposerWrapper","Please enter the template name below"), \
                                              )
            if ok and docName != "":
                templateDir = self._composerTemplatesPath()

                if templateDir == None:
                    QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Error"), \
                                            QApplication.translate("ComposerWrapper","Directory for document templates could not be found."))
                    return

                absPath = templateDir + "/" + docName + ".sdt"
                docFile = QFile(absPath)

            else:
                return

        docFileInfo = QFileInfo(docFile)

        if not docFile.open(QIODevice.WriteOnly):
            QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Save Operation Error"), \
                                            "{0}\n{1}".format(QApplication.translate("ComposerWrapper","Could not save template file."), \
                                                      docFile.errorString()
                                                      ))
            return

        templateDoc = QDomDocument()
        self._writeXML(templateDoc, docFileInfo.completeBaseName())

        if docFile.write(templateDoc.toByteArray()) == -1:
            QMessageBox.critical(self.composerView(), QApplication.translate("ComposerWrapper","Save Error"), \
                                            QApplication.translate("ComposerWrapper","Could not save template file."))
            return

        docFile.close()
        self.setDocumentFile(docFile)

    def _writeXML(self, xmlDoc, docName):
        """
        Write the template configuration into the XML document.
        """
        #Write default composer configuration
        composerElement = xmlDoc.createElement("Composer")
        composerElement.setAttribute("title", docName)
        composerElement.setAttribute("visible", 1)

        xmlDoc.appendChild(composerElement)

        self.composition().writeXML(composerElement, xmlDoc)

        #Write STDM data field configurations
        dataSourceElement = ComposerDataSource.domElement(self, xmlDoc)
        composerElement.appendChild(dataSourceElement)

        #Write spatial field configurations
        spatialColumnsElement = SpatialFieldsConfiguration.domElement(
            self, xmlDoc)
        dataSourceElement.appendChild(spatialColumnsElement)

    def _configureDataControls(self, composerDataSource):
        """
        Configure the data source and data field controls based on the composer data
        source configuration.
        """
        if self.stdmDataSourceDock().widget() != None:
            #Set data source
            dataSourceWidget = self.stdmDataSourceDock().widget()
            dataSourceWidget.setCategory(composerDataSource.category())
            dataSourceWidget.setSelectedSource(composerDataSource.name())

            #Set data field controls
            for composerId in composerDataSource.dataFieldMappings().reverse:
                #Use composer item id since the uuid is stripped off
                composerItem = self.composition().getComposerItemById(
                    composerId)

                if composerItem != None:
                    compFieldSelector = ComposerFieldSelector(
                        self, composerItem, self.composerView())
                    compFieldSelector.selectFieldName(
                        composerDataSource.dataFieldName(composerId))

                    #Add widget to the collection but now use the current uuid of the composition item
                    self.addWidgetMapping(composerItem.uuid(),
                                          compFieldSelector)

    def _configureSpatialSymbolEditor(self, spatialFieldConfig):
        """
        Configure symbol editor controls.
        """
        if self.stdmDataSourceDock().widget() != None:
            for itemId, spFieldsMappings in spatialFieldConfig.spatialFieldsMapping(
            ).iteritems():
                mapItem = self.composition().getComposerItemById(itemId)

                if mapItem != None:
                    composerSymbolEditor = ComposerSymbolEditor(
                        self, self.composerView())
                    composerSymbolEditor.addSpatialFieldMappings(
                        spFieldsMappings)

                    #Add widget to the collection but now use the current uuid of the composer map
                    self.addWidgetMapping(mapItem.uuid(), composerSymbolEditor)

    def _composerTemplatesPath(self):
        """
        Reads the path of composer templates in the registry.
        """
        regConfig = RegistryConfig()
        keyName = "ComposerTemplates"

        valueCollection = regConfig.read([keyName])

        if len(valueCollection) == 0:
            return None

        else:
            return valueCollection[keyName]

    def _onItemRemoved(self, item):
        """
        Slot raised when a composer item is removed from the scene.
        """
        """
        Code will not work since a QObject instance is returned instead of a QgsComposerItem
        if item.uuid() in self._widgetMappings:
            del self._widgetMappings[item.uuid()]
        """
        pass

    def _onItemSelected(self, item):
        """
        Slot raised when a composer item is selected. Load the corresponding field selector
        if the selection is an STDM data field label.
        QComposerLabel is returned as a QObject in the slot argument hence, we have resorted to 
        capturing the currently selected items in the composition.
        """
        selectedItems = self.composition().selectedComposerItems()

        if len(selectedItems) == 0:
            self._stdmItemPropDock.setWidget(None)

        elif len(selectedItems) == 1:
            composerItem = selectedItems[0]

            if composerItem.uuid() in self._widgetMappings:
                stdmWidget = self._widgetMappings[composerItem.uuid()]

                if stdmWidget == self._stdmItemPropDock.widget():
                    return

                else:
                    self._stdmItemPropDock.setWidget(stdmWidget)

                #Playing it safe in applying the formatting for the editor controls where applicable
                itemFormatter = None
                if isinstance(composerItem, QgsComposerArrow):
                    itemFormatter = LineFormatter()
                elif isinstance(composerItem, QgsComposerLabel):
                    itemFormatter = DataLabelFormatter()
                elif isinstance(composerItem, QgsComposerMap):
                    itemFormatter = MapFormatter()

                if itemFormatter != None:
                    itemFormatter.apply(composerItem, self, True)

            else:
                self._stdmItemPropDock.setWidget(None)

        elif len(selectedItems) > 1:
            self._stdmItemPropDock.setWidget(None)
Exemple #40
0
class OneClockAddon:

    def __init__(self):
        self.db = TomatoDB("_TomatoClock.db")
        self.dlg = OneClock(mw)

        self.pb = None
        self._connect_slots()
        self._set_style_sheet(mw)
        self.tm = None
        self.dlg_rest = None
        self.pb_w = None

        self.replace_mw_overview()
        self.replace_mw_deckbrowser()

    def replace_mw_overview(self):
        mw.overview = anki_overview(self.dlg, self.db)

    def replace_mw_reviewer(self):
        mw.reviewer = anki_reviewer(self.dlg.mode, self.db)

    def replace_mw_deckbrowser(self):
        mw.deckBrowser = anki_deckbrowser(self.db)
        mw.deckBrowser.refresh()

    @staticmethod
    def _set_style_sheet(obj):
        with open(os.path.join(os.path.dirname(__file__), "ui", "designer", "style.css"), "r") as f:
            obj.setStyleSheet(f.read())

    def _connect_slots(self):
        self.dlg.btn_start.clicked.connect(self.on_btn_start_clicked)

    def perform_hooks(self, func):
        func('reviewCleanup', self.on_review_cleanup)
        func('profileLoaded', self.on_profile_loaded)
        func('afterStateChange', self.after_anki_state_change)

    def on_profile_loaded(self):
        ProfileConfig.donate_alerted = False
        UserConfig.BREAK_MINUTES  # just ensure json file is generated
        try:
            if UserConfig.LIVE_CODE_DOWNLOAD:
                thr = _live_chart_py_downloader()
                thr.start()
        except:
            pass

    def on_review_cleanup(self):
        mw.setWindowIcon(QIcon(":/icons/anki.png"))
        if self.tm and self.tm.isActive():
            self.tm.stop()
        if self.pb:
            self.pb_w.hide()
            self.pb.reset()

        if self.dlg_rest:
            self.dlg_rest.hide()

        try:
            mw.reviewer.restore_layouts()
        except AttributeError:  # just in case "replace_mw_reviewer" is not called
            pass

    def on_btn_start_clicked(self):
        self.replace_mw_reviewer()

        mw.setWindowIcon(QIcon(":/icon/tomato.png"))
        assert isinstance(mw, AnkiQt)

        self.setup_progressbar()
        self.pb.set_seconds(self.dlg.min * MIN_SECS)
        if not self.tm:
            self.tm = Timer(mw)
            self.tm.timeout.connect(self.on_timer)
        self.tm.start()

        # click study button
        mw.overview._linkHandler("study")

        self.db.start_session(
            self.dlg.min,
            UserConfig.ANSWER_TIMEOUT_SECONDS,
            self.dlg.mode
        )

    def after_anki_state_change(self, state, oldState):
        if state == 'overview' and oldState == 'review':
            self.on_tomato(False)
            mw.overview.refresh()

    def on_tomato(self, from_timer=True):
        self.db.end_session()
        if self.tm:
            self.tm.stop()
        self.pb_w.hide()
        self.pb.reset()
        if from_timer:
            mw.moveToState("overview")
            if not self.dlg_rest:
                self.dlg_rest = RestDialog(mw)
                self._set_style_sheet(self.dlg_rest)
                self.dlg_rest.accepted.connect(self.on_dlg_rest_accepted)
                self.dlg_rest.rejected.connect(self.on_dlg_rest_rejected)
            if UserConfig.PLAY_SOUNDS["break"]:
                play(BREAK)
            self.dlg_rest.exec_(self.dlg.min)

    @staticmethod
    def on_dlg_rest_accepted():
        mw.overview._linkHandler("tomato_clock")

    def on_dlg_rest_rejected(self):
        pass

    def on_timer(self):
        if self.pb:
            self.pb_w.show()
            self.pb.on_timer()
        self.db.commit()

    def setup_progressbar(self):

        dockArea = Qt.TopDockWidgetArea
        # dockArea = Qt.LeftDockWidgetArea
        # dockArea = Qt.BottomDockWidgetArea

        self.pb_w = QDockWidget(mw)
        self.pb_w.setObjectName("progress_dock")
        if not self.pb:
            self.pb = ClockProgress(mw, dockArea)
            self.pb.tomato.connect(self.on_tomato)
        else:
            self.pb.reset()

        self.pb.set_seconds(self.dlg.min * MIN_SECS)
        self.pb_w.setWidget(self.pb)
        w = QWidget(self.pb_w)
        w.setFixedHeight(self.pb.height())
        self.pb_w.setTitleBarWidget(w)
        self.pb_w.setFeatures(QDockWidget.NoDockWidgetFeatures)

        # first check existing widgets
        existing_widgets = [widget for widget in mw.findChildren(QDockWidget) if mw.dockWidgetArea(widget) == dockArea]

        # then add ourselves
        mw.addDockWidget(dockArea, self.pb_w)

        # stack with any existing widgets
        if len(existing_widgets) > 0:
            mw.setDockNestingEnabled(True)

            if dockArea == Qt.TopDockWidgetArea or dockArea == Qt.BottomDockWidgetArea:
                stack_method = Qt.Vertical
            if dockArea == Qt.LeftDockWidgetArea or dockArea == Qt.RightDockWidgetArea:
                stack_method = Qt.Horizontal
            mw.splitDockWidget(existing_widgets[0], self.pb_w, stack_method)

        mw.web.setFocus()
        self._set_style_sheet(self.pb)