예제 #1
0
class LEDDialog(QDialog):
    rowwidth = 20
    def __init__(self, strip, parent=None):
        super(LEDDialog,self).__init__(parent)
        self.strip = strip
        self.buttons = []
        layout = QVBoxLayout()
        btnlayout = QGridLayout()
        self.btnmapper = QSignalMapper()
        for i in xrange(int(self.strip.config['nleds'])):
            p = QPushButton()
            p.setFixedWidth(40)
            p.setFlat(True)
            p.setAutoFillBackground(True)
            self.btnmapper.setMapping( p, i)
            p.clicked.connect( self.btnmapper.map)
            self.buttons += [[p,QColor()]]
            btnlayout.addWidget(p, i/self.rowwidth, i%self.rowwidth)
        self.btnmapper.mapped['int'].connect(self.chooseColor)
        layout.addLayout(btnlayout)
        ctrllayout = QHBoxLayout()
        p = QPushButton("Refresh")
        p.clicked.connect(self.refresh)
        ctrllayout.addWidget(p)
        p = QPushButton("Set")
        p.clicked.connect(self.set)
        ctrllayout.addWidget(p)
        p = QPushButton("Close")
        p.clicked.connect(self.close)
        ctrllayout.addWidget(p)
        layout.addLayout(ctrllayout)
        self.setLayout( layout)
        self.refresh()

    def refresh(self):
        tmp = self.strip.state()
        for i in xrange(len(self.buttons)):
            c = QColor(int(tmp[i*3+0]),int(tmp[i*3+1]),int(tmp[i*3+2]))
            pal = self.buttons[i][0].palette()
            pal.setBrush( QPalette.Button, QColor(c.red(),c.green(),c.blue()))
            self.buttons[i][0].setPalette(pal)
            self.buttons[i][1] = c
        
    def set(self):
        leds = []
        for b,c in self.buttons:
            leds += [ c.red(), c.green(), c.blue() ]
        self.strip.setState(leds)
        time.sleep(0.1)
        self.refresh()
        
    def chooseColor(self, i):
        if not i < len(self.buttons): return
        initial = self.buttons[i][1]
        c = QColorDialog.getColor(initial,self)
        if initial == c: return
        pal = self.buttons[i][0].palette()
        pal.setBrush( QPalette.Button, QColor(c.red(),c.green(),c.blue()))
        self.buttons[i][0].setPalette(pal)
        self.buttons[i][1] = c
 def contextMenuEvent(self, event):
   menu     = QtGui.QMenu()
   last_pos = event.pos()
   cursor   = self.cursorForPosition(last_pos)
   pos      = cursor.positionInBlock()
   line     = cursor.blockNumber()
   
   keywords = self.words_at_pos(line, pos)
   
   if len(keywords) > 0:
     keyword_mapper = QSignalMapper(self)
     
     actions = []
     
     for keyword in keywords:
       
       action_text = "Copy \"%s\"" % keyword[0].meaning
       actions.append(QtGui.QAction(action_text, None))
       
       # We can only send strings with the signal mapper, so pickle our data.
       data = pickle.dumps(keyword[0])
       data = QtCore.QString.fromAscii(data)
       
       self.connect(actions[-1], QtCore.SIGNAL("triggered()"), keyword_mapper, QtCore.SLOT("map()"))
       keyword_mapper.setMapping(actions[-1], data)
     
     self.connect(keyword_mapper, QtCore.SIGNAL("mapped(QString)"), self.copy_keyword)
     menu.addActions(actions)
     menu.addSeparator()
   
   default_menu = self.createStandardContextMenu()
   menu.addActions(default_menu.actions())
   
   menu.exec_(event.globalPos())
예제 #3
0
class PeriodicInstrumentController(NonBlockingInstrumentController):
    def __init__(self, instrument_config):
        NonBlockingInstrumentController.__init__(self, instrument_config)

        self.mapper = QSignalMapper()
        self.timers = []

    def run(self):
        # create and start periodic timers for each command
        for i, command in enumerate(self.instr_cfg.operation_commands):
            self.log.debug("Creating timer for '{0}' command".format(
                command.name))
            timer = QTimer()
            timer.timeout.connect(self.mapper.map)
            self.mapper.setMapping(timer, i)
            timer.start(command.param)
            self.timers.append(timer)

        self.mapper.mapped.connect(self.send_command)

        NonBlockingInstrumentController.run(self)

    def quit(self):
        for timer in self.timers:
            timer.stop()
        NonBlockingInstrumentController.quit(self)

    def send_command(self, num):
        self.new_command.emit(self.instr_cfg.operation_commands[num])
예제 #4
0
class ProviderToolBar(QToolBar):
    '''
    Widget to display the vehicles/objects status and position
    '''

    triggered = pyqtSignal(str)

    def __init__(self, parent=None):
        super(ProviderToolBar, self).__init__(parent)
        self.signalMapper = QSignalMapper(self)
        self.setMovable(True)
        self.setFloatable(True)
        self.upToDate = False
        self.actions = []
        self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
        self.signalMapper.mapped[str].connect(self.triggered)

    def createAction(self, provider):
        icon = QIcon(':/plugins/PosiView/ledgreen.png')
        icon.addFile(':/plugins/PosiView/ledgrey.png', QSize(), QIcon.Disabled,
                     QIcon.Off)
        action = QAction(icon, provider.name, None)
        button = QToolButton()
        button.setDefaultAction(action)
        action.setEnabled(False)
        provider.deviceConnected.connect(action.setEnabled)
        provider.deviceDisconnected.connect(action.setDisabled)
        self.signalMapper.setMapping(action, provider.name)
        action.triggered.connect(self.signalMapper.map)
        self.addAction(action)
        self.actions.append(action)
예제 #5
0
class ProviderToolBar(QToolBar):
    '''
    Widget to display the vehicles/objects status and position
    '''

    triggered = pyqtSignal(str)

    def __init__(self, parent=None):
        super(ProviderToolBar, self).__init__(parent)
        self.signalMapper = QSignalMapper(self)
        self.setMovable(True)
        self.setFloatable(True)
        self.upToDate = False
        self.actions = []
        self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
        self.signalMapper.mapped[str].connect(self.triggered)

    def createAction(self, provider):
        icon = QIcon(':/plugins/PosiView/ledgreen.png')
        icon.addFile(':/plugins/PosiView/ledgrey.png', QSize(), QIcon.Disabled, QIcon.Off)
        action = QAction(icon, provider.name, None)
        button = QToolButton()
        button.setDefaultAction(action)
        action.setEnabled(False)
        provider.deviceConnected.connect(action.setEnabled)
        provider.deviceDisconnected.connect(action.setDisabled)
        self.signalMapper.setMapping(action, provider.name)
        action.triggered.connect(self.signalMapper.map)
        self.addAction(action)
        self.actions.append(action)
    def __init__(self, parent, tester):
        """Construct a new dockwindow following the tester """
        self.tester = tester
        
        QtGui.QDockWidget.__init__(self, tester.testname, parent)
        self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
        
        if not self.widget():
            self.setWidget(QtGui.QWidget(self))
        
        vl = QtGui.QVBoxLayout(self.widget())
        #self.widget().setLayout(vl)
        
        panel = QtGui.QFrame(self)
        vl.addWidget(panel)
        
        self.login = QtGui.QLineEdit(self)
        self.login.textEdited.connect(self.check_logpass)
        self.password = QtGui.QLineEdit(self)
        self.password.setEchoMode(QtGui.QLineEdit.Password)
        self.password.textEdited.connect(self.check_logpass)
        
        self.tests = []
        
        fl = QtGui.QFormLayout(panel)
        fl.addRow('&Login:'******'&Password:',self.password)
        panel.setLayout(fl)
        
        panel = QtGui.QFrame(self)
        panel.setFrameShadow(QtGui.QFrame.Sunken)
        panel.setFrameShape(QtGui.QFrame.Panel)
        vl.addWidget(panel)
        
        vl2 = QtGui.QVBoxLayout(panel)
        
        signalmapper = QSignalMapper(self)
        signalmapper.mapped[int].connect(self.test)
        
        for i,test in enumerate(self.tester.tests):
            btn = QtGui.QPushButton("Test {}: {}".format(i+1,test.name),panel)
            btn.setStyleSheet(self.btn_default_stylesheet)
            btn.setEnabled(False)
            vl2.addWidget(btn)
            self.tests.append(btn)
            signalmapper.setMapping(btn,i)
            btn.clicked.connect(signalmapper.map)
        
        panel.setLayout(vl2)
        
        self.text = QtGui.QLabel("Enter your Coursera login and assignments password and push one of the test buttons above to run the test and submit the results to Coursera.")
        self.text.setWordWrap(True)
        vl.addWidget(self.text)
        self.text.setFrameShadow(QtGui.QFrame.Sunken)
        self.text.setFrameShape(QtGui.QFrame.Panel)
        self.text.setMargin(5)

        #vl.setStretch(2,1)
        vl.addStretch(1)
예제 #7
0
    def setupUi(self, nx, ny, keys):
        mapper = QSignalMapper()

        layout = QGridLayout(self)
        for ix in range(nx):
            for iy in range(ny):
                key = keys[iy * nx + ix]
                keyPb = QPushButton(self)
                keyPb.setFixedHeight(40)
                keyPb.setText(key[0])
                keyPb.clicked.connect(mapper.map)
                layout.addWidget(keyPb, iy, ix)
                mapper.setMapping(keyPb, key[1])
        mapper.mapped.connect(self.keyClicked)
        self.mapper = mapper
 def setup_file_locs(self):
   
   # Because the default margins are ugly as h*ck.
   self.ui.tabLocs.layout().setContentsMargins(0, 0, 0, 0)
   
   # Map our buttons to functions that retrieve the necessary data.
   cfg_mapper = QSignalMapper(self)
   
   for i, item in enumerate(FILE_LOCATIONS):
     self.connect(self.ui.__dict__[item[BTN]], QtCore.SIGNAL("clicked()"), cfg_mapper, QtCore.SLOT("map()"))
     cfg_mapper.setMapping(self.ui.__dict__[item[BTN]], i)
   
   self.connect(cfg_mapper, QtCore.SIGNAL("mapped(int)"), self.__get_cfg_item)
   
   # Load in all our info from the config file.
   for item in FILE_LOCATIONS:
     self.ui.__dict__[item[TEXT]].setText(common.editor_config.get_pref(item[CFG]))
예제 #9
0
class SelectMapper:
  def setupSelectMapper(self, selectMapper, selectAllButton=None):
    """selectMapper should include keys 'button'(QPushButton) and 'widget'(QWidget)
    """
    self._select = None
    self.selectMapper = selectMapper
    self.selectAllButton = selectAllButton
    self.signalMapper = QSignalMapper(self)

    for key, value in self.selectMapper.iteritems():
      # config signal mapper
      value['button'].clicked.connect(self.signalMapper.map)
      self.signalMapper.setMapping(value['button'], key)

    # connect signal mapper to slot 'select' 
    QObject.connect(self.signalMapper, SIGNAL("mapped(QString)"), self.select)
    if selectAllButton:
      selectAllButton.clicked.connect(self.selectAll)

  def select(self, select):
    select = unicode(select)
    self.selectMapper[select]['button'].setChecked(True)
    if self._select == select: return
    if self._select:
      self.selectMapper[self._select]['button'].setChecked(False)
      self.selectMapper[self._select]['widget'].hide()
    else:
      if self.selectAllButton:
        self.selectAllButton.setChecked(False)
      for key, value in self.selectMapper.iteritems():
        value['widget'].hide()
    self.selectMapper[select]['widget'].show()
    self._select = select

  def selectAll(self):
    self.selectAllButton.setChecked(True)
    if self._select == None: return
    for key, value in self.selectMapper.iteritems():
      value['button'].setChecked(False)
      value['widget'].show()
    self._select = None
예제 #10
0
    def __init__(self, items, colCount, parent=None):
        """
        Create Grid and setup QSignalMapper.

        Connect each item's 'clicked' signal and use the sequence index
        as map value which will be passed as argument when the Grid's
        clicked signal is emitted.

        items: sequence with widgets having a 'void clicked()' signal
        colCount: column count for each row
        parent: parent widget, default None
        """
        super(Grid, self).__init__(parent)

        # Create a grid layout.
        layout = QGridLayout()
        self.setLayout(layout)

        # Create the signal mapper.
        signalMapper = QSignalMapper(self)

        for cnt, item in enumerate(items):
            # Setup mapping for the item. In this case, the
            # mapping is the sequence index of the item.
            signalMapper.setMapping(item, cnt)

            # Connect the item's 'clicked' signal to the signal
            # mapper's 'map' slot.
            # The 'map' slot will emit the 'mapped' signal
            # when invoked and the mapping set previously will be
            # passed as an argument to the slot/signal that is
            # connected to the signal mapper's 'mapped' signal.
            item.clicked.connect(signalMapper.map)

            # Add the widget to the grid layout
            layout.addWidget(item, cnt / colCount, cnt % colCount)

        # Forward the signal mapper's 'mapped' signal via the Grid's
        # 'clicked' signal. This will handle all widgets' 'clicked'
        # ssignals.
        signalMapper.mapped.connect(self.clicked)
예제 #11
0
    def setup_file_locs(self):

        # Because the default margins are ugly as h*ck.
        self.ui.tabLocs.layout().setContentsMargins(0, 0, 0, 0)

        # Map our buttons to functions that retrieve the necessary data.
        cfg_mapper = QSignalMapper(self)

        for i, item in enumerate(FILE_LOCATIONS):
            self.connect(self.ui.__dict__[item[BTN]],
                         QtCore.SIGNAL("clicked()"), cfg_mapper,
                         QtCore.SLOT("map()"))
            cfg_mapper.setMapping(self.ui.__dict__[item[BTN]], i)

        self.connect(cfg_mapper, QtCore.SIGNAL("mapped(int)"),
                     self.__get_cfg_item)

        # Load in all our info from the config file.
        for item in FILE_LOCATIONS:
            self.ui.__dict__[item[TEXT]].setText(
                common.editor_config.get_pref(item[CFG]))
    def contextMenuEvent(self, event):
        menu = QtGui.QMenu()
        last_pos = event.pos()
        cursor = self.cursorForPosition(last_pos)
        pos = cursor.positionInBlock()
        line = cursor.blockNumber()

        keywords = self.words_at_pos(line, pos)

        if len(keywords) > 0:
            keyword_mapper = QSignalMapper(self)

            actions = []

            for keyword in keywords:

                action_text = "Copy \"%s\"" % keyword[0].meaning
                actions.append(QtGui.QAction(action_text, None))

                # We can only send strings with the signal mapper, so pickle our data.
                data = pickle.dumps(keyword[0])
                data = QtCore.QString.fromAscii(data)

                self.connect(actions[-1], QtCore.SIGNAL("triggered()"),
                             keyword_mapper, QtCore.SLOT("map()"))
                keyword_mapper.setMapping(actions[-1], data)

            self.connect(keyword_mapper, QtCore.SIGNAL("mapped(QString)"),
                         self.copy_keyword)
            menu.addActions(actions)
            menu.addSeparator()

        default_menu = self.createStandardContextMenu()
        menu.addActions(default_menu.actions())

        menu.exec_(event.globalPos())
class StackedWidget(QStackedWidget):
    """A stacked widget."""
    
    def __init__(
            self, parentWidget, dockWidgetName, iface, pluginDir):
        """Constructor.
        
        Args:
            parentWidget (QToolBar): A reference to the parent widget.
            dockWidgetName (str): A name of the dock widget.
            iface (QgisInterface): A reference to the QgisInterface.
            pluginDir (QDir): A plugin directory.
        
        """
        
        self.dW = parentWidget
        self.dWName = dockWidgetName
        self.iface = iface
        self.pluginDir = pluginDir
        
        super(StackedWidget, self).__init__(self.dW)
        
        self._setup_self()
    
    def _setup_self(self):
        """Sets up self."""
        
        self.setObjectName(u'stackedWidget')
        
        self.openTabSignalMapper = QSignalMapper(self)
        
        self._build_widgets()
    
    def _build_widgets(self):
        """Builds own widgets."""
        
        self.loadVfkPuWidget = loadvfk_puwidget.LoadVfkPuWidget(
            self, self.dWName, self.iface, self.dW, self.pluginDir)
        self.dW.toolBar.loadVfkAction.triggered.connect(
            self.openTabSignalMapper.map)
        self.openTabSignalMapper.setMapping(self.dW.toolBar.loadVfkAction, 0)
        self.addWidget(self.loadVfkPuWidget)
        
        self.editPuWidget = edit_puwidget.EditPuWidget(
            self, self.dWName, self.iface, self.dW, self.pluginDir)
        self.dW.toolBar.editAction.triggered.connect(
            self.openTabSignalMapper.map)
        self.openTabSignalMapper.setMapping(self.dW.toolBar.editAction, 1)
        self.addWidget(self.editPuWidget)
        
        self.checkAnalysisPuWidget = \
            checkanalysis_puwidget.CheckAnalysisPuWidget(
                self, self.dWName, self.iface, self.dW, self.pluginDir)
        self.dW.toolBar.checkAnalysisAction.triggered.connect(
            self.openTabSignalMapper.map)
        self.openTabSignalMapper.setMapping(
            self.dW.toolBar.checkAnalysisAction, 2)
        self.addWidget(self.checkAnalysisPuWidget)
        
        self.openTabSignalMapper.mapped.connect(self.setCurrentIndex)
        
        self.currentChanged.connect(
            self.dW.statusBar.change_text)
예제 #14
0
파일: import_data.py 프로젝트: gltn/stdm
class ImportData(QWizard, Ui_frmImport):
    def __init__(self,parent=None):
        QWizard.__init__(self,parent)
        self.setupUi(self) 
        self.curr_profile = current_profile()

        #Connect signals   
        self.btnBrowseSource.clicked.connect(self.setSourceFile)
        self.lstDestTables.itemClicked.connect(self.destSelectChanged)
        self.btnSrcUp.clicked.connect(self.srcItemUp)
        self.btnSrcDown.clicked.connect(self.srcItemDown)
        self.btnSrcAll.clicked.connect(self.checkSrcItems)
        self.btnSrcNone.clicked.connect(self.uncheckSrcItems)
        self.btnDestUp.clicked.connect(self.targetItemUp)
        self.btnDestDown.clicked.connect(self.targetItemDown)
        self.lstSrcFields.currentRowChanged[int].connect(self.sourceRowChanged)
        self.lstTargetFields.currentRowChanged[int].connect(self.destRowChanged)
        self.lstTargetFields.currentRowChanged[int].connect(self._enable_disable_trans_tools)
        self.chk_virtual.toggled.connect(self._on_load_virtual_columns)

        #Data Reader
        self.dataReader = None
         
        #Init
        self.registerFields()
        
        #Geometry columns
        self.geomcols = []

        #Initialize value translators from definitions
        self._init_translators()

        #self._set_target_fields_stylesheet()

    def _init_translators(self):
        translator_menu = QMenu(self)

        self._trans_widget_mgr = TranslatorWidgetManager(self)
        self._trans_signal_mapper = QSignalMapper(self)

        for trans_name, config in ValueTranslatorConfig.translators.iteritems():
            trans_action = QAction( u'{}...'.format(trans_name),
                translator_menu
            )

            self._trans_signal_mapper.setMapping(trans_action, trans_name)
            trans_action.triggered.connect(self._trans_signal_mapper.map)

            translator_menu.addAction(trans_action)

        if len(translator_menu.actions()) == 0:
            self.btn_add_translator.setEnabled(False)

        else:
            self.btn_add_translator.setMenu(translator_menu)

            self._trans_signal_mapper.mapped[str].connect(self._load_translator_dialog)

        self.btn_edit_translator.setEnabled(False)
        self.btn_delete_translator.setEnabled(False)

        self.btn_edit_translator.clicked.connect(self._on_edit_translator)
        self.btn_delete_translator.clicked.connect(self._on_delete_translator)

    def _load_translator_dialog(self, config_key):
        """
        Load translator dialog.
        """
        dest_column = self._selected_destination_column()
        src_column = self._selected_source_column()

        if dest_column:
            #Check if there is an existing dialog in the manager
            trans_dlg = self._trans_widget_mgr.translator_widget(dest_column)

            if trans_dlg is None:
                trans_config = ValueTranslatorConfig.translators.get(config_key, None)

                #Safety precaution
                if trans_config is None: return

                try:
                    trans_dlg = trans_config.create(
                        self,
                        self._source_columns(),
                        self.targetTab,
                        dest_column,
                        src_column
                    )

                except RuntimeError as re:
                    QMessageBox.critical(
                        self,
                        QApplication.translate(
                            'ImportData',
                            'Value Translator'
                        ),
                        unicode(re)
                    )

                    return

            self._handle_translator_dlg(dest_column, trans_dlg)

    def _handle_translator_dlg(self, key, dlg):
        if dlg.exec_() == QDialog.Accepted:
            self._trans_widget_mgr.add_widget(key, dlg)

        self._enable_disable_trans_tools()

    def _on_edit_translator(self):
        """
        Slot to load the translator widget specific for the selected column for editing.
        """
        dest_column = self._selected_destination_column()

        if dest_column:
            #Check if there is an existing dialog in the manager
            trans_dlg = self._trans_widget_mgr.translator_widget(dest_column)

            self._handle_translator_dlg(dest_column, trans_dlg)

    def _on_delete_translator(self):
        """
        Slot for deleting the translator widget for the selected column.
        """
        dest_column = self._selected_destination_column()

        self._delete_translator(dest_column)

    def _delete_translator(self, destination_column):
        if not destination_column:
            return

        res = self._trans_widget_mgr.remove_translator_widget(destination_column)

        self._enable_disable_trans_tools()

    def _enable_disable_trans_tools(self, index=-1):
        """
        Enable/disable appropriate value translator tools based on the selected
        column.
        """
        dest_column = self._selected_destination_column()

        if dest_column:
            #Check if there is an existing dialog in the manager
            trans_dlg = self._trans_widget_mgr.translator_widget(dest_column)

            if trans_dlg is None:
                self.btn_add_translator.setEnabled(True)
                self.btn_edit_translator.setEnabled(False)
                self.btn_delete_translator.setEnabled(False)

            else:
                self.btn_add_translator.setEnabled(False)
                self.btn_edit_translator.setEnabled(True)
                self.btn_delete_translator.setEnabled(True)

        else:
            self.btn_add_translator.setEnabled(False)
            self.btn_edit_translator.setEnabled(False)
            self.btn_delete_translator.setEnabled(False)

    def _selected_destination_column(self):
        dest_field_item = self.lstTargetFields.currentItem()

        if dest_field_item is None:
            return ""

        else:
            return dest_field_item.text()

    def _selected_source_column(self):
        src_field_item = self.lstSrcFields.currentItem()

        if src_field_item is None:
            return ""

        else:
            return src_field_item.text()

    def _set_target_fields_stylesheet(self):
        self.lstTargetFields.setStyleSheet("QListWidget#lstTargetFields::item:selected"
                                           " { selection-background-color: darkblue }")

    def registerFields(self):
        #Register wizard fields
        pgSource = self.page(0)
        pgSource.registerField("srcFile*",self.txtDataSource)
        pgSource.registerField("typeText",self.rbTextType)
        pgSource.registerField("typeSpatial",self.rbSpType)
        
        #Destination table configuration
        destConf = self.page(1)
        destConf.registerField("optAppend",self.rbAppend)
        destConf.registerField("optOverwrite",self.rbOverwrite)
        destConf.registerField("tabIndex*",self.lstDestTables)
        destConf.registerField("geomCol",self.geomClm,"currentText",SIGNAL("currentIndexChanged(int)"))
        
    def initializePage(self,pageid):
        #Re-implementation of wizard page initialization
        if pageid == 1:
            #Reference to checked listwidget item representing table name
            self.destCheckedItem=None
            self.geomClm.clear()
            
            if self.field("typeText"):
                self.loadTables("textual")
                self.geomClm.setEnabled(False)
                
            elif self.field("typeSpatial"):
                self.loadTables("spatial")
                self.geomClm.setEnabled(True)
                
        if pageid == 2:
            self.lstSrcFields.clear()
            self.lstTargetFields.clear()
            self.assignCols()
            self._enable_disable_trans_tools()

    def _source_columns(self):
        return self.dataReader.getFields()

    def assignCols(self):
        #Load source and target columns respectively
        srcCols = self._source_columns()
        
        for c in srcCols:
            srcItem = QListWidgetItem(c,self.lstSrcFields)
            srcItem.setCheckState(Qt.Unchecked)
            srcItem.setIcon(QIcon(":/plugins/stdm/images/icons/column.png"))
            self.lstSrcFields.addItem(srcItem)
            
        #Destination Columns
        tabIndex = int(self.field("tabIndex"))
        self.targetTab = self.destCheckedItem.text()
        targetCols = table_column_names(self.targetTab, False, True)

        #Remove geometry columns in the target columns list
        for gc in self.geomcols:            
            colIndex = getIndex(targetCols,gc)
            if colIndex != -1:
                targetCols.remove(gc)

        #Remove 'id' column if there
        id_idx = getIndex(targetCols, 'id')
        if id_idx != -1:
            targetCols.remove('id')

        self._add_target_table_columns(targetCols)

    def _add_target_table_columns(self, items, style=False):
        for item in items:
            list_item = QListWidgetItem(item)

            if style:
                color = QColor(0, 128, 255)
                list_item.setTextColor(color)

            self.lstTargetFields.addItem(list_item)
                
    def _on_load_virtual_columns(self, state):
        """
        Load/unload relationships in the list of destination table columns.
        """
        virtual_columns = self.dataReader.entity_virtual_columns(self.targetTab)

        if state:
            if len(virtual_columns) == 0:
                msg = QApplication.translate("ImportData",
                    "There are no virtual columns for the specified table.")
                QMessageBox.warning(
                    self,
                    QApplication.translate(
                        'ImportData',
                        'Import Data'
                    ),
                    msg
                )
                self.chk_virtual.setChecked(False)

                return

            self._add_target_table_columns(virtual_columns, True)

        else:
            self._remove_destination_table_fields(virtual_columns)

    def _remove_destination_table_fields(self, fields):
        """Remove the specified columns from the destination view."""
        for f in fields:
            list_items = self.lstTargetFields.findItems(f, Qt.MatchFixedString)
            if len(list_items) > 0:
                list_item = list_items[0]

                row = self.lstTargetFields.row(list_item)

                rem_item = self.lstTargetFields.takeItem(row)
                del rem_item

                #Delete translator if already defined for the given column
                self._delete_translator(f)

    def loadGeomCols(self, table):
        #Load geometry columns based on the selected table 
        self.geomcols = table_column_names(table, True, True)
        self.geomClm.clear()
        self.geomClm.addItems(self.geomcols)
                
    def loadTables(self, type):
        #Load textual or spatial tables
        self.lstDestTables.clear()
        tables = None
        if type == "textual":
            tables = profile_user_tables(self.curr_profile, False, True)
            
        elif type == "spatial":
            tables = profile_spatial_tables(self.curr_profile)
        if tables is not None:
            for t in tables:
                tabItem = QListWidgetItem(t,self.lstDestTables)
                tabItem.setCheckState(Qt.Unchecked)
                tabItem.setIcon(QIcon(":/plugins/stdm/images/icons/table.png"))
                self.lstDestTables.addItem(tabItem)
                
    def validateCurrentPage(self):
        #Validate the current page before proceeding to the next one
        validPage=True
        
        if not QFile.exists(unicode(self.field("srcFile"))):
            self.ErrorInfoMessage("The specified source file does not exist.")
            validPage = False
            
        else:
            if self.dataReader:
                self.dataReader.reset()
            self.dataReader = OGRReader(unicode(self.field("srcFile")))
            
            if not self.dataReader.isValid():
                self.ErrorInfoMessage("The source file could not be opened."
                                      "\nPlease check is the given file type "
                                      "is supported")
                validPage = False
                
        if self.currentId()==1:
            if self.destCheckedItem == None:                                                        
                self.ErrorInfoMessage("Please select the destination table.")
                validPage = False
                
        if self.currentId()==2:
            validPage = self.execImport()

        return validPage

    def setSourceFile(self):
        #Set the file path to the source file
        imageFilters = "Comma Separated Value (*.csv);;ESRI Shapefile (*.shp);;AutoCAD DXF (*.dxf)" 
        sourceFile = QFileDialog.getOpenFileName(self,"Select Source File",vectorFileDir(),imageFilters)
        if sourceFile != "":
            self.txtDataSource.setText(sourceFile) 
        
    def getSrcDestPairs(self):
        #Return the matched source and destination columns
        srcDest = {}
        for l in range(self.lstTargetFields.count()):
            if l < self.lstSrcFields.count():                
                srcItem = self.lstSrcFields.item(l)
                if srcItem.checkState() == Qt.Checked:
                    destItem = self.lstTargetFields.item(l)
                    srcDest[srcItem.text()] = destItem.text()
                    
        return srcDest
        
    def execImport(self):
        #Initiate the import process
        success = False
        matchCols = self.getSrcDestPairs()
        
        #Specify geometry column
        geom_column=None
        
        if self.field("typeSpatial"):
            geom_column = self.field("geomCol")
            
        # Ensure that user has selected at least one column if it is a
        # non-spatial table
        if len(matchCols) == 0:
            self.ErrorInfoMessage("Please select at least one source column.")
            return success

        value_translator_manager = self._trans_widget_mgr.translator_manager()
               
        # try:
        if self.field("optOverwrite"):
            entity = self.curr_profile.entity_by_name(self.targetTab)
            dependencies = entity.dependencies()
            view_dep = dependencies['views']
            entity_dep = [e.name for e in entity.children()]
            entities_dep_str = ', '.join(entity_dep)
            views_dep_str = ', '.join(view_dep)

            if len(entity_dep) > 0 or len(view_dep) > 0:
                del_msg = QApplication.translate(
                    'ImportData',
                    "Overwriting existing records will permanently \n"
                    "remove records from other tables linked to the \n"
                    "records. The following tables will be affected."
                    "\n{}\n{}"
                    "\nClick Yes to proceed importing or No to cancel.".
                        format(entities_dep_str, views_dep_str)
                )
                del_result = QMessageBox.critical(
                    self,
                    QApplication.translate(
                        "ImportData",
                        "Overwrite Import Data Warning"
                    ),
                    del_msg,
                    QMessageBox.Yes | QMessageBox.No
                )

                if del_result == QMessageBox.Yes:
                    self.dataReader.featToDb(
                        self.targetTab, matchCols, False, self, geom_column,
                        translator_manager=value_translator_manager
                    )
                    # Update directory info in the registry
                    setVectorFileDir(self.field("srcFile"))

                    self.InfoMessage(
                        "All features have been imported successfully!"
                    )
                    success = True

                else:
                    success = False
        else:
            self.dataReader.featToDb(
                self.targetTab, matchCols, True, self, geom_column,
                translator_manager=value_translator_manager
            )
            self.InfoMessage(
                "All features have been imported successfully!"
            )
            #Update directory info in the registry
            setVectorFileDir(self.field("srcFile"))
            success = True
        # except:
        #     self.ErrorInfoMessage(unicode(sys.exc_info()[1]))

        return success

    def _clear_dest_table_selections(self, exclude=None):
        #Clears checked items in destination table list view
        if exclude is None:
            exclude = []

        for i in range(self.lstDestTables.count()):
            item = self.lstDestTables.item(i)
            if item.checkState() == Qt.Checked and not item.text() in exclude:
                item.setCheckState(Qt.Unchecked)
        
    def destSelectChanged(self, item):
        """
        Handler when a list widget item is clicked,
        clears previous selections
        """
        if not self.destCheckedItem is None:
            if item.checkState() == Qt.Checked:
                self.destCheckedItem.setCheckState(Qt.Unchecked) 
            else:
                self.destCheckedItem = None 
              
        if item.checkState() == Qt.Checked:
            self.destCheckedItem = item

            #Ensure other selected items have been cleared
            self._clear_dest_table_selections(exclude=[item.text()])

            #Load geometry columns if selection is a spatial table
            if self.field("typeSpatial"):
                self.loadGeomCols(item.text())
                
    def syncRowSelection(self, srcList, destList):
        """
        Sync the selection of an srcList item to the corresponding one in
        the destination column list.
        """
        if (srcList.currentRow() + 1) <= destList.count():
            destList.setCurrentRow(srcList.currentRow())
            
    def sourceRowChanged(self):
        #Slot when the source list's current row changes
        self.syncRowSelection(self.lstSrcFields,self.lstTargetFields)
        
    def destRowChanged(self):
        #Slot when the destination list's current row changes
        self.syncRowSelection(self.lstTargetFields, self.lstSrcFields)
                
    def itemUp(self, listWidget):
        #Moves the selected item in the list widget one level up
        curIndex = listWidget.currentRow()
        curItem = listWidget.takeItem(curIndex)
        listWidget.insertItem(curIndex - 1, curItem)
        listWidget.setCurrentRow(curIndex - 1)
        
    def itemDown(self, listWidget):
        #Moves the selected item in the list widget one level down
        curIndex=listWidget.currentRow()
        curItem=listWidget.takeItem(curIndex)
        listWidget.insertItem(curIndex + 1,curItem)
        listWidget.setCurrentRow(curIndex + 1)
        
    def checkAllItems(self, listWidget, state):
        #Checks all items in the list widget
        for l in range(listWidget.count()):
            item=listWidget.item(l)
            if state:
                item.setCheckState(Qt.Checked)
            else:
                item.setCheckState(Qt.Unchecked)
                
    def checkSrcItems(self):
        #Slot for checking all source table columns
        self.checkAllItems(self.lstSrcFields, True)
        
    def uncheckSrcItems(self):
        #Slot for unchecking all source table columns
        self.checkAllItems(self.lstSrcFields, False)
        
    def srcItemUp(self):
        #Slot for moving source list item up
        self.itemUp(self.lstSrcFields)
        
    def srcItemDown(self):
        #Slot for moving source list item down
        self.itemDown(self.lstSrcFields)
    
    def targetItemUp(self):
        #Slot for moving target item up
        self.itemUp(self.lstTargetFields)
        
    def targetItemDown(self):
        #Slot for moving target item down
        self.itemDown(self.lstTargetFields)
         
    def keyPressEvent(self,e):
        """
        Override method for preventing the dialog from
        closing itself when the escape key is hit
        """
        if e.key() == Qt.Key_Escape:
            pass
        
    def InfoMessage(self, message):
        #Information message box        
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Information)
        msg.setText(message)
        msg.exec_()
                  
    def ErrorInfoMessage(self, message):
        #Error Message Box
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Critical)
        msg.setText(message)
        msg.exec_()
예제 #15
0
파일: myterm.py 프로젝트: alex1818/MyTerm
class MainWindow(QMainWindow, Ui_MainWindow):
    """docstring for MainWindow."""
    def __init__(self, parent=None):
        super(MainWindow, self).__init__()
        self._csvFilePath = ""
        self.serialport = serial.Serial()
        self.receiver_thread = readerThread(self)
        self.receiver_thread.setPort(self.serialport)
        self._localEcho = None

        self.setupUi(self)
        self.setCorner(Qt.TopLeftCorner, Qt.LeftDockWidgetArea)
        self.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea)
        font = QtGui.QFont()
        font.setFamily(EDITOR_FONT)
        font.setPointSize(10)
        self.txtEdtOutput.setFont(font)
        self.txtEdtInput.setFont(font)
        self.quickSendTable.setFont(font)
        if UI_FONT is not None:
            font = QtGui.QFont()
            font.setFamily(UI_FONT)
            font.setPointSize(9)
            self.dockWidget_PortConfig.setFont(font)
            self.dockWidget_SendHex.setFont(font)
            self.dockWidget_QuickSend.setFont(font)
        self.setupFlatUi()
        self.onEnumPorts()

        icon = QtGui.QIcon(":/icon.ico")
        self.setWindowIcon(icon)
        self.actionAbout.setIcon(icon)

        icon = QtGui.QIcon(":/qt_logo_16.ico")
        self.actionAbout_Qt.setIcon(icon)

        self._viewGroup = QActionGroup(self)
        self._viewGroup.addAction(self.actionAscii)
        self._viewGroup.addAction(self.actionHex_lowercase)
        self._viewGroup.addAction(self.actionHEX_UPPERCASE)
        self._viewGroup.setExclusive(True)

        # bind events
        self.actionOpen_Cmd_File.triggered.connect(self.openCSV)
        self.actionSave_Log.triggered.connect(self.onSaveLog)
        self.actionExit.triggered.connect(self.onExit)

        self.actionOpen.triggered.connect(self.openPort)
        self.actionClose.triggered.connect(self.closePort)

        self.actionPort_Config_Panel.triggered.connect(self.onTogglePrtCfgPnl)
        self.actionQuick_Send_Panel.triggered.connect(self.onToggleQckSndPnl)
        self.actionSend_Hex_Panel.triggered.connect(self.onToggleHexPnl)
        self.dockWidget_PortConfig.visibilityChanged.connect(
            self.onVisiblePrtCfgPnl)
        self.dockWidget_QuickSend.visibilityChanged.connect(
            self.onVisibleQckSndPnl)
        self.dockWidget_SendHex.visibilityChanged.connect(self.onVisibleHexPnl)
        self.actionLocal_Echo.triggered.connect(self.onLocalEcho)
        self.actionAlways_On_Top.triggered.connect(self.onAlwaysOnTop)

        self.actionAscii.triggered.connect(self.onViewChanged)
        self.actionHex_lowercase.triggered.connect(self.onViewChanged)
        self.actionHEX_UPPERCASE.triggered.connect(self.onViewChanged)

        self.actionAbout.triggered.connect(self.onAbout)
        self.actionAbout_Qt.triggered.connect(self.onAboutQt)

        self.btnOpen.clicked.connect(self.onOpen)
        self.btnClear.clicked.connect(self.onClear)
        self.btnSaveLog.clicked.connect(self.onSaveLog)
        self.btnEnumPorts.clicked.connect(self.onEnumPorts)
        self.btnSendHex.clicked.connect(self.sendHex)

        self.receiver_thread.read.connect(self.receive)
        self.receiver_thread.exception.connect(self.readerExcept)
        self._signalMap = QSignalMapper(self)
        self._signalMap.mapped[int].connect(self.tableClick)

        # initial action
        self.actionHEX_UPPERCASE.setChecked(True)
        self.receiver_thread.setViewMode(VIEWMODE_HEX_UPPERCASE)
        self.initQuickSend()
        self.restoreLayout()
        self.moveScreenCenter()
        self.syncMenu()

        if self.isMaximized():
            self.setMaximizeButton("restore")
        else:
            self.setMaximizeButton("maximize")

        self.LoadSettings()

    def setupFlatUi(self):
        self._dragPos = self.pos()
        self._isDragging = False
        self.setMouseTracking(True)
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setStyleSheet("""
            QWidget {
                background-color:#99d9ea;
                /*background-image: url(:/background.png);*/
                outline: 1px solid #0057ff;
            }
            QLabel {
                color:#202020;
                font-size:13px;
                font-family:Century;
            }
            
            QComboBox {
                color:#202020;
                font-size:13px;
                font-family:Century Schoolbook;
            }
            QComboBox {
                border: none;
                padding: 1px 18px 1px 3px;
            }
            QComboBox:editable {
                background: white;
            }
            QComboBox:!editable, QComboBox::drop-down:editable {
                background: #62c7e0;
            }
            QComboBox:!editable:hover, QComboBox::drop-down:editable:hover {
                background: #c7eaf3;
            }
            QComboBox:!editable:pressed, QComboBox::drop-down:editable:pressed {
                background: #35b6d7;
            }
            QComboBox:on {
                padding-top: 3px;
                padding-left: 4px;
            }
            QComboBox::drop-down {
                subcontrol-origin: padding;
                subcontrol-position: top right;
                width: 16px;
                border: none;
            }
            QComboBox::down-arrow {
                image: url(:/downarrow.png);
            }
            QComboBox::down-arrow:on {
                image: url(:/uparrow.png);
            }
            
            QGroupBox {
                color:#202020;
                font-size:12px;
                font-family:Century Schoolbook;
                border: 1px solid gray;
                margin-top: 15px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                subcontrol-position: top left;
                left:5px;
                top:3px;
            }
            
            QCheckBox {
                color:#202020;
                spacing: 5px;
                font-size:12px;
                font-family:Century Schoolbook;
            }

            QScrollBar:horizontal {
                background-color:#99d9ea;
                border: none;
                height: 15px;
                margin: 0px 20px 0 20px;
            }
            QScrollBar::handle:horizontal {
                background: #61b9e1;
                min-width: 20px;
            }
            QScrollBar::add-line:horizontal {
                image: url(:/rightarrow.png);
                border: none;
                background: #7ecfe4;
                width: 20px;
                subcontrol-position: right;
                subcontrol-origin: margin;
            }
            QScrollBar::sub-line:horizontal {
                image: url(:/leftarrow.png);
                border: none;
                background: #7ecfe4;
                width: 20px;
                subcontrol-position: left;
                subcontrol-origin: margin;
            }
            
            QScrollBar:vertical {
                background-color:#99d9ea;
                border: none;
                width: 15px;
                margin: 20px 0px 20px 0px;
            }
            QScrollBar::handle::vertical {
                background: #61b9e1;
                min-height: 20px;
            }
            QScrollBar::add-line::vertical {
                image: url(:/downarrow.png);
                border: none;
                background: #7ecfe4;
                height: 20px;
                subcontrol-position: bottom;
                subcontrol-origin: margin;
            }
            QScrollBar::sub-line::vertical {
                image: url(:/uparrow.png);
                border: none;
                background: #7ecfe4;
                height: 20px;
                subcontrol-position: top;
                subcontrol-origin: margin;
            }
            
            QTableView {
                background-color: white;
                /*selection-background-color: #FF92BB;*/
                border: 1px solid #eeeeee;
                color: #2f2f2f;
            }
            QTableView::focus {
                /*border: 1px solid #2a7fff;*/
            }
            QTableView QTableCornerButton::section {
                border: none;
                border-right: 1px solid #eeeeee;
                border-bottom: 1px solid #eeeeee;
                background-color: #8ae6d2;
            }
            QTableView QWidget {
                background-color: white;
            }
            QTableView::item:focus {
                border: 1px red;
                background-color: transparent;
                color: #2f2f2f;
            }
            QHeaderView::section {
                border: none;
                border-right: 1px solid #eeeeee;
                border-bottom: 1px solid #eeeeee;
                padding-left: 2px;
                padding-right: 2px;
                color: #444444;
                background-color: #8ae6d2;
            }
            QTextEdit {
                background-color:white;
                color:#2f2f2f;
                border: 1px solid white;
            }
            QTextEdit::focus {
                border: 1px solid #2a7fff;
            }
            
            QPushButton {
                background-color:#30a7b8;
                border:none;
                color:#ffffff;
                font-size:14px;
                font-family:Century Schoolbook;
            }
            QPushButton:hover {
                background-color:#51c0d1;
            }
            QPushButton:pressed {
                background-color:#3a9ecc;
            }
            
            QMenuBar {
                color: #2f2f2f;
            }
            QMenuBar::item {
                background-color: transparent;
                margin: 8px 0px 0px 0px;
                padding: 1px 8px 1px 8px;
                height: 15px;
            }
            QMenuBar::item:selected {
                background: #51c0d1;
            }
            QMenuBar::item:pressed {
                
            }
            QMenu {
                color: #2f2f2f;
            }
            QMenu {
                margin: 2px;
            }
            QMenu::item {
                padding: 2px 25px 2px 21px;
                border: 1px solid transparent;
            }
            QMenu::item:selected {
                background: #51c0d1;
            }
            QMenu::icon {
                background: transparent;
                border: 2px inset transparent;
            }

            QDockWidget {
                font-size:13px;
                font-family:Century;
                color: #202020;
                titlebar-close-icon: none;
                titlebar-normal-icon: none;
            }
            QDockWidget::title {
                margin: 0;
                padding: 2px;
                subcontrol-origin: content;
                subcontrol-position: right top;
                text-align: left;
                background: #67baed;
                
            }
            QDockWidget::float-button {
                max-width: 12px;
                max-height: 12px;
                background-color:transparent;
                border:none;
                image: url(:/restore_inactive.png);
            }
            QDockWidget::float-button:hover {
                background-color:#227582;
                image: url(:/restore_active.png);
            }
            QDockWidget::float-button:pressed {
                padding: 0;
                background-color:#14464e;
                image: url(:/restore_active.png);
            }
            QDockWidget::close-button {
                max-width: 12px;
                max-height: 12px;
                background-color:transparent;
                border:none;
                image: url(:/close_inactive.png);
            }
            QDockWidget::close-button:hover {
                background-color:#ea5e00;
                image: url(:/close_active.png);
            }
            QDockWidget::close-button:pressed {
                background-color:#994005;
                image: url(:/close_active.png);
                padding: 0;
            }
            
        """)
        self.dockWidgetContents.setStyleSheet("""
            QPushButton {
                min-height:23px;
            }
        """)
        self.dockWidget_QuickSend.setStyleSheet("""
            QPushButton {
                background-color:#27b798;
                font-family:Consolas;
                font-size:12px;
                min-width:46px;
            }
            QPushButton:hover {
                background-color:#3bd5b4;
            }
            QPushButton:pressed {
                background-color:#1d8770;
            }
        """)
        self.dockWidgetContents_2.setStyleSheet("""
            QPushButton {
                min-height:23px;
                min-width:50px;
            }
        """)

        w = self.frameGeometry().width()
        self._minBtn = QPushButton(self)
        self._minBtn.setGeometry(w - 103, 0, 28, 24)
        self._minBtn.clicked.connect(self.onMinimize)
        self._minBtn.setStyleSheet("""
            QPushButton {
                background-color:transparent;
                border:none;
                outline: none;
                image: url(:/minimize_inactive.png);
            }
            QPushButton:hover {
                background-color:#227582;
                image: url(:/minimize_active.png);
            }
            QPushButton:pressed {
                background-color:#14464e;
                image: url(:/minimize_active.png);
            }
        """)

        self._maxBtn = QPushButton(self)
        self._maxBtn.setGeometry(w - 74, 0, 28, 24)
        self._maxBtn.clicked.connect(self.onMaximize)
        self.setMaximizeButton("maximize")

        self._closeBtn = QPushButton(self)
        self._closeBtn.setGeometry(w - 45, 0, 36, 24)
        self._closeBtn.clicked.connect(self.onExit)
        self._closeBtn.setStyleSheet("""
            QPushButton {
                background-color:transparent;
                border:none;
                outline: none;
                image: url(:/close_inactive.png);
            }
            QPushButton:hover {
                background-color:#ea5e00;
                image: url(:/close_active.png);
            }
            QPushButton:pressed {
                background-color:#994005;
                image: url(:/close_active.png);
            }
        """)

    def resizeEvent(self, event):
        w = event.size().width()
        self._minBtn.move(w - 103, 0)
        self._maxBtn.move(w - 74, 0)
        self._closeBtn.move(w - 45, 0)

    def onMinimize(self):
        self.showMinimized()

    def isMaximized(self):
        return ((self.windowState() == Qt.WindowMaximized))

    def onMaximize(self):
        if self.isMaximized():
            self.showNormal()
            self.setMaximizeButton("maximize")
        else:
            self.showMaximized()
            self.setMaximizeButton("restore")

    def setMaximizeButton(self, style):
        if "maximize" == style:
            self._maxBtn.setStyleSheet("""
                QPushButton {
                    background-color:transparent;
                    border:none;
                    outline: none;
                    image: url(:/maximize_inactive.png);
                }
                QPushButton:hover {
                    background-color:#227582;
                    image: url(:/maximize_active.png);
                }
                QPushButton:pressed {
                    background-color:#14464e;
                    image: url(:/maximize_active.png);
                }
            """)
        elif "restore" == style:
            self._maxBtn.setStyleSheet("""
                QPushButton {
                    background-color:transparent;
                    border:none;
                    outline: none;
                    image: url(:/restore_inactive.png);
                }
                QPushButton:hover {
                    background-color:#227582;
                    image: url(:/restore_active.png);
                }
                QPushButton:pressed {
                    background-color:#14464e;
                    image: url(:/restore_active.png);
                }
            """)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self._isDragging = True
            self._dragPos = event.globalPos() - self.pos()
        event.accept()

    def mouseMoveEvent(self, event):
        if event.buttons(
        ) and Qt.LeftButton and self._isDragging and not self.isMaximized():
            self.move(event.globalPos() - self._dragPos)
        event.accept()

    def mouseReleaseEvent(self, event):
        self._isDragging = False
        event.accept()

    def SaveSettings(self):
        root = ET.Element("MyTerm")
        GUISettings = ET.SubElement(root, "GUISettings")

        PortCfg = ET.SubElement(GUISettings, "PortConfig")
        ET.SubElement(PortCfg, "port").text = self.cmbPort.currentText()
        ET.SubElement(PortCfg,
                      "baudrate").text = self.cmbBaudRate.currentText()
        ET.SubElement(PortCfg,
                      "databits").text = self.cmbDataBits.currentText()
        ET.SubElement(PortCfg, "parity").text = self.cmbParity.currentText()
        ET.SubElement(PortCfg,
                      "stopbits").text = self.cmbStopBits.currentText()
        ET.SubElement(
            PortCfg,
            "rtscts").text = self.chkRTSCTS.isChecked() and "on" or "off"
        ET.SubElement(
            PortCfg,
            "xonxoff").text = self.chkXonXoff.isChecked() and "on" or "off"

        View = ET.SubElement(GUISettings, "View")
        ET.SubElement(
            View, "LocalEcho"
        ).text = self.actionLocal_Echo.isChecked() and "on" or "off"
        ET.SubElement(
            View, "ReceiveView").text = self._viewGroup.checkedAction().text()

        with open(get_config_path('settings.xml'), 'w') as f:
            f.write('<?xml version="1.0" encoding="UTF-8"?>\n')
            f.write(
                ET.tostring(root, encoding='utf-8',
                            pretty_print=True).decode("utf-8"))

    def LoadSettings(self):
        if os.path.isfile(get_config_path("settings.xml")):
            with open(get_config_path("settings.xml"), 'r') as f:
                tree = safeET.parse(f)

            port = tree.findtext('GUISettings/PortConfig/port', default='')
            if port != '':
                self.cmbPort.setCurrentText(port)

            baudrate = tree.findtext('GUISettings/PortConfig/baudrate',
                                     default='38400')
            if baudrate != '':
                self.cmbBaudRate.setCurrentText(baudrate)

            databits = tree.findtext('GUISettings/PortConfig/databits',
                                     default='8')
            id = self.cmbDataBits.findText(databits)
            if id >= 0:
                self.cmbDataBits.setCurrentIndex(id)

            parity = tree.findtext('GUISettings/PortConfig/parity',
                                   default='None')
            id = self.cmbParity.findText(parity)
            if id >= 0:
                self.cmbParity.setCurrentIndex(id)

            stopbits = tree.findtext('GUISettings/PortConfig/stopbits',
                                     default='1')
            id = self.cmbStopBits.findText(stopbits)
            if id >= 0:
                self.cmbStopBits.setCurrentIndex(id)

            rtscts = tree.findtext('GUISettings/PortConfig/rtscts',
                                   default='off')
            if 'on' == rtscts:
                self.chkRTSCTS.setChecked(True)
            else:
                self.chkRTSCTS.setChecked(False)

            xonxoff = tree.findtext('GUISettings/PortConfig/xonxoff',
                                    default='off')
            if 'on' == xonxoff:
                self.chkXonXoff.setChecked(True)
            else:
                self.chkXonXoff.setChecked(False)

            LocalEcho = tree.findtext('GUISettings/View/LocalEcho',
                                      default='off')
            if 'on' == LocalEcho:
                self.actionLocal_Echo.setChecked(True)
                self._localEcho = True
            else:
                self.actionLocal_Echo.setChecked(False)
                self._localEcho = False

            ReceiveView = tree.findtext('GUISettings/View/ReceiveView',
                                        default='HEX(UPPERCASE)')
            if 'Ascii' in ReceiveView:
                self.actionAscii.setChecked(True)
            elif 'lowercase' in ReceiveView:
                self.actionHex_lowercase.setChecked(True)
            elif 'UPPERCASE' in ReceiveView:
                self.actionHEX_UPPERCASE.setChecked(True)

    def closeEvent(self, event):
        self.saveLayout()
        self.saveCSV()
        self.SaveSettings()
        event.accept()

    def tableClick(self, row):
        self.sendTableRow(row)

    def initQuickSend(self):
        #self.quickSendTable.horizontalHeader().setDefaultSectionSize(40)
        #self.quickSendTable.horizontalHeader().setMinimumSectionSize(25)
        self.quickSendTable.setRowCount(50)
        self.quickSendTable.setColumnCount(20)

        for row in range(50):
            item = QPushButton(str("Send"))
            item.clicked.connect(self._signalMap.map)
            self._signalMap.setMapping(item, row)
            self.quickSendTable.setCellWidget(row, 0, item)
            self.quickSendTable.setRowHeight(row, 20)

        if os.path.isfile(get_config_path('QckSndBckup.csv')):
            self.loadCSV(get_config_path('QckSndBckup.csv'))

        self.quickSendTable.resizeColumnsToContents()

    def openCSV(self):
        fileName = QFileDialog.getOpenFileName(self, "Select a file",
                                               os.getcwd(),
                                               "CSV Files (*.csv)")
        if fileName:
            self.loadCSV(fileName, notifyExcept=True)

    def saveCSV(self):
        # scan table
        rows = self.quickSendTable.rowCount()
        cols = self.quickSendTable.columnCount()

        tmp_data = [[
            self.quickSendTable.item(row, col) is not None
            and self.quickSendTable.item(row, col).text() or ''
            for col in range(1, cols)
        ] for row in range(rows)]

        data = []
        # delete trailing blanks
        for row in tmp_data:
            for idx, d in enumerate(row[::-1]):
                if '' != d:
                    break
            new_row = row[:len(row) - idx]
            data.append(new_row)

        #import pprint
        #pprint.pprint(data, width=120, compact=True)

        # write to file
        with open(get_config_path('QckSndBckup.csv'), 'w') as csvfile:
            csvwriter = csv.writer(csvfile, delimiter=',', lineterminator='\n')
            csvwriter.writerows(data)

    def loadCSV(self, path, notifyExcept=False):
        data = []
        set_rows = 0
        set_cols = 0
        try:
            with open(path) as csvfile:
                csvData = csv.reader(csvfile)
                for row in csvData:
                    data.append(row)
                    set_rows = set_rows + 1
                    if len(row) > set_cols:
                        set_cols = len(row)
        except IOError as e:
            print("({})".format(e))
            if notifyExcept:
                QMessageBox.critical(self, "Open failed", str(e),
                                     QMessageBox.Close)
            return

        rows = self.quickSendTable.rowCount()
        cols = self.quickSendTable.columnCount()
        # clear table
        for col in range(cols):
            for row in range(rows):
                self.quickSendTable.setItem(row, col, QTableWidgetItem(""))

        self._csvFilePath = path
        if (cols -
                1) < set_cols:  # first colume is used by the "send" buttons.
            cols = set_cols + 10
            self.quickSendTable.setColumnCount(cols)
        if rows < set_rows:
            rows = set_rows + 20
            self.quickSendTable.setRowCount(rows)

        for row, rowdat in enumerate(data):
            if len(rowdat) > 0:
                for col, cell in enumerate(rowdat, 1):
                    self.quickSendTable.setItem(row, col,
                                                QTableWidgetItem(str(cell)))

        self.quickSendTable.resizeColumnsToContents()
        #self.quickSendTable.resizeRowsToContents()

    def sendTableRow(self, row):
        cols = self.quickSendTable.columnCount()
        try:
            data = [
                '0' + self.quickSendTable.item(row, col).text()
                for col in range(1, cols)
                if self.quickSendTable.item(row, col) is not None
                and self.quickSendTable.item(row, col).text() is not ''
            ]
        except:
            print("Exception in get table data(row = %d)" % (row + 1))
        else:
            tmp = [d[-2] + d[-1] for d in data if len(d) >= 2]
            for t in tmp:
                if not is_hex(t):
                    QMessageBox.critical(self, "Error",
                                         "'%s' is not hexadecimal." % (t),
                                         QMessageBox.Close)
                    return

            h = [int(t, 16) for t in tmp]
            self.transmitHex(h)

    def sendHex(self):
        hexStr = self.txtEdtInput.toPlainText()
        hexStr = ''.join(hexStr.split(" "))

        hexarray = []
        for i in range(0, len(hexStr), 2):
            hexarray.append(int(hexStr[i:i + 2], 16))

        self.transmitHex(hexarray)

    def readerExcept(self, e):
        self.closePort()
        QMessageBox.critical(self, "Read failed", str(e), QMessageBox.Close)

    def timestamp(self):
        return datetime.datetime.now().time().isoformat()[:-3]

    def receive(self, data):
        self.appendOutputText("\n%s R<-:%s" % (self.timestamp(), data))

    def appendOutputText(self, data, color=Qt.black):
        # the qEditText's "append" methon will add a unnecessary newline.
        # self.txtEdtOutput.append(data.decode('utf-8'))

        tc = self.txtEdtOutput.textColor()
        self.txtEdtOutput.moveCursor(QtGui.QTextCursor.End)
        self.txtEdtOutput.setTextColor(QtGui.QColor(color))
        self.txtEdtOutput.insertPlainText(data)
        self.txtEdtOutput.moveCursor(QtGui.QTextCursor.End)
        self.txtEdtOutput.setTextColor(tc)

    def transmitHex(self, hexarray):
        if len(hexarray) > 0:
            byteArray = bytearray(hexarray)
            if self.serialport.isOpen():
                try:
                    self.serialport.write(byteArray)
                except serial.SerialException as e:
                    print("Exception in transmitHex(%s)" % repr(hexarray))
                    QMessageBox.critical(self, "Exception in transmitHex",
                                         str(e), QMessageBox.Close)
                else:
                    # self.txCount += len( b )
                    # self.frame.statusbar.SetStatusText('Tx:%d' % self.txCount, 2)

                    text = ''.join(['%02X ' % i for i in hexarray])
                    self.appendOutputText(
                        "\n%s T->:%s" % (self.timestamp(), text), Qt.blue)

    def GetPort(self):
        return self.cmbPort.currentText()

    def GetDataBits(self):
        s = self.cmbDataBits.currentText()
        if s == '5':
            return serial.FIVEBITS
        elif s == '6':
            return serial.SIXBITS
        elif s == '7':
            return serial.SEVENBITS
        elif s == '8':
            return serial.EIGHTBITS

    def GetParity(self):
        s = self.cmbParity.currentText()
        if s == 'None':
            return serial.PARITY_NONE
        elif s == 'Even':
            return serial.PARITY_EVEN
        elif s == 'Odd':
            return serial.PARITY_ODD
        elif s == 'Mark':
            return serial.PARITY_MARK
        elif s == 'Space':
            return serial.PARITY_SPACE

    def GetStopBits(self):
        s = self.cmbStopBits.currentText()
        if s == '1':
            return serial.STOPBITS_ONE
        elif s == '1.5':
            return serial.STOPBITS_ONE_POINT_FIVE
        elif s == '2':
            return serial.STOPBITS_TWO

    def openPort(self):
        if self.serialport.isOpen():
            return

        _port = self.GetPort()
        if '' == _port:
            QMessageBox.information(self, "Invalid parameters",
                                    "Port is empty.")
            return

        _baudrate = self.cmbBaudRate.currentText()
        if '' == _baudrate:
            QMessageBox.information(self, "Invalid parameters",
                                    "Baudrate is empty.")
            return

        self.serialport.port = _port
        self.serialport.baudrate = _baudrate
        self.serialport.bytesize = self.GetDataBits()
        self.serialport.stopbits = self.GetStopBits()
        self.serialport.parity = self.GetParity()
        self.serialport.rtscts = self.chkRTSCTS.isChecked()
        self.serialport.xonxoff = self.chkXonXoff.isChecked()
        # self.serialport.timeout  = THREAD_TIMEOUT
        # self.serialport.writeTimeout = SERIAL_WRITE_TIMEOUT
        try:
            self.serialport.open()
        except serial.SerialException as e:
            QMessageBox.critical(self, "Could not open serial port", str(e),
                                 QMessageBox.Close)
        else:
            self._start_reader()
            self.setWindowTitle("%s on %s [%s, %s%s%s%s%s]" % (
                appInfo.title,
                self.serialport.portstr,
                self.serialport.baudrate,
                self.serialport.bytesize,
                self.serialport.parity,
                self.serialport.stopbits,
                self.serialport.rtscts and ' RTS/CTS' or '',
                self.serialport.xonxoff and ' Xon/Xoff' or '',
            ))
            pal = self.btnOpen.palette()
            pal.setColor(QtGui.QPalette.Button, QtGui.QColor(0, 0xff, 0x7f))
            self.btnOpen.setAutoFillBackground(True)
            self.btnOpen.setPalette(pal)
            self.btnOpen.setText('Close')
            self.btnOpen.update()

    def closePort(self):
        if self.serialport.isOpen():
            self._stop_reader()
            self.serialport.close()
            self.setWindowTitle(appInfo.title)
            pal = self.btnOpen.style().standardPalette()
            self.btnOpen.setAutoFillBackground(True)
            self.btnOpen.setPalette(pal)
            self.btnOpen.setText('Open')
            self.btnOpen.update()

    def _start_reader(self):
        """Start reader thread"""
        self.receiver_thread.start()

    def _stop_reader(self):
        """Stop reader thread only, wait for clean exit of thread"""
        self.receiver_thread.join()

    def onTogglePrtCfgPnl(self):
        if self.actionPort_Config_Panel.isChecked():
            self.dockWidget_PortConfig.show()
        else:
            self.dockWidget_PortConfig.hide()

    def onToggleQckSndPnl(self):
        if self.actionQuick_Send_Panel.isChecked():
            self.dockWidget_QuickSend.show()
        else:
            self.dockWidget_QuickSend.hide()

    def onToggleHexPnl(self):
        if self.actionSend_Hex_Panel.isChecked():
            self.dockWidget_SendHex.show()
        else:
            self.dockWidget_SendHex.hide()

    def onVisiblePrtCfgPnl(self, visible):
        self.actionPort_Config_Panel.setChecked(visible)

    def onVisibleQckSndPnl(self, visible):
        self.actionQuick_Send_Panel.setChecked(visible)

    def onVisibleHexPnl(self, visible):
        self.actionSend_Hex_Panel.setChecked(visible)

    def onLocalEcho(self):
        self._localEcho = self.actionLocal_Echo.isChecked()

    def onAlwaysOnTop(self):
        if self.actionAlways_On_Top.isChecked():
            style = self.windowFlags()
            self.setWindowFlags(style | Qt.WindowStaysOnTopHint)
            self.show()
        else:
            style = self.windowFlags()
            self.setWindowFlags(style & ~Qt.WindowStaysOnTopHint)
            self.show()

    def onOpen(self):
        if self.serialport.isOpen():
            self.closePort()
        else:
            self.openPort()

    def onClear(self):
        self.txtEdtOutput.clear()

    def onSaveLog(self):
        fileName = QFileDialog.getSaveFileName(
            self, "Save as", os.getcwd(),
            "Log files (*.log);;Text files (*.txt);;All files (*.*)")
        if fileName:
            import codecs
            f = codecs.open(fileName, 'w', 'utf-8')
            f.write(self.txtEdtOutput.toPlainText())
            f.close()

    def moveScreenCenter(self):
        w = self.frameGeometry().width()
        h = self.frameGeometry().height()
        desktop = QDesktopWidget()
        screenW = desktop.screen().width()
        screenH = desktop.screen().height()
        self.setGeometry((screenW - w) / 2, (screenH - h) / 2, w, h)

    def onEnumPorts(self):
        for p in enum_ports():
            self.cmbPort.addItem(p)
        # self.cmbPort.update()

    def onAbout(self):
        q = QWidget()
        icon = QtGui.QIcon(":/icon.ico")
        q.setWindowIcon(icon)
        QMessageBox.about(q, "About MyTerm", appInfo.aboutme)

    def onAboutQt(self):
        QMessageBox.aboutQt(None)

    def onExit(self):
        if self.serialport.isOpen():
            self.closePort()
        self.close()

    def restoreLayout(self):
        if os.path.isfile(get_config_path("layout.dat")):
            try:
                f = open(get_config_path("layout.dat"), 'rb')
                geometry, state = pickle.load(f)
                self.restoreGeometry(geometry)
                self.restoreState(state)
            except Exception as e:
                print("Exception on restoreLayout, {}".format(e))
        else:
            try:
                f = QFile(':/default_layout.dat')
                f.open(QIODevice.ReadOnly)
                geometry, state = pickle.loads(f.readAll())
                self.restoreGeometry(geometry)
                self.restoreState(state)
            except Exception as e:
                print("Exception on restoreLayout, {}".format(e))

    def saveLayout(self):
        with open(get_config_path("layout.dat"), 'wb') as f:
            pickle.dump((self.saveGeometry(), self.saveState()), f)

    def syncMenu(self):
        self.actionPort_Config_Panel.setChecked(
            not self.dockWidget_PortConfig.isHidden())
        self.actionQuick_Send_Panel.setChecked(
            not self.dockWidget_QuickSend.isHidden())
        self.actionSend_Hex_Panel.setChecked(
            not self.dockWidget_SendHex.isHidden())

    def onViewChanged(self):
        checked = self._viewGroup.checkedAction()
        if checked is None:
            self.actionHEX_UPPERCASE.setChecked(True)
            self.receiver_thread.setViewMode(VIEWMODE_HEX_UPPERCASE)
        else:
            if 'Ascii' in checked.text():
                self.receiver_thread.setViewMode(VIEWMODE_ASCII)
            elif 'lowercase' in checked.text():
                self.receiver_thread.setViewMode(VIEWMODE_HEX_LOWERCASE)
            elif 'UPPERCASE' in checked.text():
                self.receiver_thread.setViewMode(VIEWMODE_HEX_UPPERCASE)
예제 #16
0
def main(icon_spec):
    app = QApplication(sys.argv)

    main_window = QMainWindow()

    def sigint_handler(*args):
        main_window.close()

    signal.signal(signal.SIGINT, sigint_handler)
    # the timer enables triggering the sigint_handler
    signal_timer = QTimer()
    signal_timer.start(100)
    signal_timer.timeout.connect(lambda: None)

    tool_bar = QToolBar()
    main_window.addToolBar(Qt.TopToolBarArea, tool_bar)

    table_view = QTableView()
    table_view.setSelectionBehavior(QAbstractItemView.SelectRows)
    table_view.setSelectionMode(QAbstractItemView.SingleSelection)
    table_view.setSortingEnabled(True)
    main_window.setCentralWidget(table_view)

    proxy_model = QSortFilterProxyModel()
    proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive)
    proxy_model.setFilterKeyColumn(1)
    table_view.setModel(proxy_model)
    proxy_model.layoutChanged.connect(table_view.resizeRowsToContents)

    item_model = QStandardItemModel()
    proxy_model.setSourceModel(item_model)

    # get all icons and their available sizes
    QIcon.setThemeName("gnome")
    icons = []
    all_sizes = set([])
    for context, icon_names in icon_spec:
        for icon_name in icon_names:
            icon = QIcon.fromTheme(icon_name)
            sizes = []
            for size in icon.availableSizes():
                size = (size.width(), size.height())
                sizes.append(size)
                all_sizes.add(size)
            sizes.sort()
            icons.append({
                'context': context,
                'icon_name': icon_name,
                'icon': icon,
                'sizes': sizes,
            })
    all_sizes = list(all_sizes)
    all_sizes.sort()

    # input field for filter
    def filter_changed(value):
        proxy_model.setFilterRegExp(value)
        table_view.resizeRowsToContents()

    filter_line_edit = QLineEdit()
    filter_line_edit.setMaximumWidth(200)
    filter_line_edit.setPlaceholderText('Filter name')
    filter_line_edit.setToolTip(
        'Filter name optionally using regular expressions (' +
        QKeySequence(QKeySequence.Find).toString() + ')')
    filter_line_edit.textChanged.connect(filter_changed)
    tool_bar.addWidget(filter_line_edit)

    # actions to toggle visibility of available sizes/columns
    def action_toggled(index):
        column = 2 + index
        table_view.setColumnHidden(column,
                                   not table_view.isColumnHidden(column))
        table_view.resizeColumnsToContents()
        table_view.resizeRowsToContents()

    signal_mapper = QSignalMapper()
    for i, size in enumerate(all_sizes):
        action = QAction('%dx%d' % size, tool_bar)
        action.setCheckable(True)
        action.setChecked(True)
        tool_bar.addAction(action)
        action.toggled.connect(signal_mapper.map)
        signal_mapper.setMapping(action, i)
        # set tool tip and handle key sequence
        tool_tip = 'Toggle visibility of column'
        if i < 10:
            digit = ('%d' % (i + 1))[-1]
            tool_tip += ' (%s)' % QKeySequence('Ctrl+%s' % digit).toString()
        action.setToolTip(tool_tip)
    signal_mapper.mapped.connect(action_toggled)

    # label columns
    header_labels = ['context', 'name']
    for width, height in all_sizes:
        header_labels.append('%dx%d' % (width, height))
    item_model.setColumnCount(len(header_labels))
    item_model.setHorizontalHeaderLabels(header_labels)

    # fill rows
    item_model.setRowCount(len(icons))
    for row, icon_data in enumerate(icons):
        # context
        item = QStandardItem(icon_data['context'])
        item.setFlags(item.flags() ^ Qt.ItemIsEditable)
        item_model.setItem(row, 0, item)
        # icon name
        item = QStandardItem(icon_data['icon_name'])
        item.setFlags(item.flags() ^ Qt.ItemIsEditable)
        item_model.setItem(row, 1, item)
        for index_in_all_sizes, size in enumerate(all_sizes):
            column = 2 + index_in_all_sizes
            if size in icon_data['sizes']:
                # icon as pixmap to keep specific size
                item = QStandardItem('')
                pixmap = icon_data['icon'].pixmap(size[0], size[1])
                item.setData(pixmap, Qt.DecorationRole)
                item.setFlags(item.flags() ^ Qt.ItemIsEditable)
                item_model.setItem(row, column, item)
            else:
                # single space to be sortable against icons
                item = QStandardItem(' ')
                item.setFlags(item.flags() ^ Qt.ItemIsEditable)
                item_model.setItem(row, column, item)

    table_view.resizeColumnsToContents()
    # manually set row heights because resizeRowsToContents is not working properly
    for row, icon_data in enumerate(icons):
        if len(icon_data['sizes']) > 0:
            max_size = icon_data['sizes'][-1]
            table_view.setRowHeight(row, max_size[1])

    # enable focus find (ctrl+f) and toggle columns (ctrl+NUM)
    def main_window_keyPressEvent(self,
                                  event,
                                  old_keyPressEvent=QMainWindow.keyPressEvent):
        if event.matches(QKeySequence.Find):
            filter_line_edit.setFocus()
            return
        if event.modifiers() == Qt.ControlModifier and event.key(
        ) >= Qt.Key_0 and event.key() <= Qt.Key_9:
            index = event.key() - Qt.Key_1
            if event.key() == Qt.Key_0:
                index += 10
            action = signal_mapper.mapping(index)
            if action:
                action.toggle()
                return
        old_keyPressEvent(self, event)

    main_window.keyPressEvent = new.instancemethod(main_window_keyPressEvent,
                                                   table_view, None)

    # enable copy (ctrl+c) name of icon to clipboard
    def table_view_keyPressEvent(self,
                                 event,
                                 old_keyPressEvent=QTableView.keyPressEvent):
        if event.matches(QKeySequence.Copy):
            selection_model = self.selectionModel()
            if selection_model.hasSelection():
                index = selection_model.selectedRows()[0]
                source_index = self.model().mapToSource(index)
                item = self.model().sourceModel().item(source_index.row(), 1)
                icon_name = item.data(Qt.EditRole)
                app.clipboard().setText(icon_name.toString())
                return
        old_keyPressEvent(self, event)

    table_view.keyPressEvent = new.instancemethod(table_view_keyPressEvent,
                                                  table_view, None)
    print 'Icon Theme: ', QIcon.themeName()

    print 'Theme Search Paths:'
    for item in QIcon.themeSearchPaths():
        print item
    main_window.showMaximized()
    return app.exec_()
예제 #17
0
class ImportData(QWizard, Ui_frmImport):
    def __init__(self,parent=None):
        QWizard.__init__(self,parent)
        self.setupUi(self) 
        self.curr_profile = current_profile()

        #Connect signals   
        self.btnBrowseSource.clicked.connect(self.setSourceFile)
        self.lstDestTables.itemClicked.connect(self.destSelectChanged)
        self.btnSrcUp.clicked.connect(self.srcItemUp)
        self.btnSrcDown.clicked.connect(self.srcItemDown)
        self.btnSrcAll.clicked.connect(self.checkSrcItems)
        self.btnSrcNone.clicked.connect(self.uncheckSrcItems)
        self.btnDestUp.clicked.connect(self.targetItemUp)
        self.btnDestDown.clicked.connect(self.targetItemDown)
        self.lstSrcFields.currentRowChanged[int].connect(self.sourceRowChanged)
        self.lstTargetFields.currentRowChanged[int].connect(self.destRowChanged)
        self.lstTargetFields.currentRowChanged[int].connect(self._enable_disable_trans_tools)
        self.chk_virtual.toggled.connect(self._on_load_virtual_columns)

        #Data Reader
        self.dataReader = None
         
        #Init
        self.registerFields()
        
        #Geometry columns
        self.geomcols = []

        #Initialize value translators from definitions
        self._init_translators()

        #self._set_target_fields_stylesheet()

    def _init_translators(self):
        translator_menu = QMenu(self)

        self._trans_widget_mgr = TranslatorWidgetManager(self)
        self._trans_signal_mapper = QSignalMapper(self)

        for trans_name, config in ValueTranslatorConfig.translators.iteritems():
            trans_action = QAction( u'{}...'.format(trans_name),
                translator_menu
            )

            self._trans_signal_mapper.setMapping(trans_action, trans_name)
            trans_action.triggered.connect(self._trans_signal_mapper.map)

            translator_menu.addAction(trans_action)

        if len(translator_menu.actions()) == 0:
            self.btn_add_translator.setEnabled(False)

        else:
            self.btn_add_translator.setMenu(translator_menu)

            self._trans_signal_mapper.mapped[str].connect(self._load_translator_dialog)

        self.btn_edit_translator.setEnabled(False)
        self.btn_delete_translator.setEnabled(False)

        self.btn_edit_translator.clicked.connect(self._on_edit_translator)
        self.btn_delete_translator.clicked.connect(self._on_delete_translator)

    def _load_translator_dialog(self, config_key):
        """
        Load translator dialog.
        """
        dest_column = self._selected_destination_column()
        src_column = self._selected_source_column()

        if dest_column:
            #Check if there is an existing dialog in the manager
            trans_dlg = self._trans_widget_mgr.translator_widget(dest_column)

            if trans_dlg is None:
                trans_config = ValueTranslatorConfig.translators.get(config_key, None)

                #Safety precaution
                if trans_config is None: return

                try:
                    trans_dlg = trans_config.create(
                        self,
                        self._source_columns(),
                        self.targetTab,
                        dest_column,
                        src_column
                    )

                except RuntimeError as re:
                    QMessageBox.critical(
                        self,
                        QApplication.translate(
                            'ImportData',
                            'Value Translator'
                        ),
                        unicode(re)
                    )

                    return

            self._handle_translator_dlg(dest_column, trans_dlg)

    def _handle_translator_dlg(self, key, dlg):
        if dlg.exec_() == QDialog.Accepted:
            self._trans_widget_mgr.add_widget(key, dlg)

        self._enable_disable_trans_tools()

    def _on_edit_translator(self):
        """
        Slot to load the translator widget specific for the selected column for editing.
        """
        dest_column = self._selected_destination_column()

        if dest_column:
            #Check if there is an existing dialog in the manager
            trans_dlg = self._trans_widget_mgr.translator_widget(dest_column)

            self._handle_translator_dlg(dest_column, trans_dlg)

    def _on_delete_translator(self):
        """
        Slot for deleting the translator widget for the selected column.
        """
        dest_column = self._selected_destination_column()

        self._delete_translator(dest_column)

    def _delete_translator(self, destination_column):
        if not destination_column:
            return

        res = self._trans_widget_mgr.remove_translator_widget(destination_column)

        self._enable_disable_trans_tools()

    def _enable_disable_trans_tools(self, index=-1):
        """
        Enable/disable appropriate value translator tools based on the selected
        column.
        """
        dest_column = self._selected_destination_column()

        if dest_column:
            #Check if there is an existing dialog in the manager
            trans_dlg = self._trans_widget_mgr.translator_widget(dest_column)

            if trans_dlg is None:
                self.btn_add_translator.setEnabled(True)
                self.btn_edit_translator.setEnabled(False)
                self.btn_delete_translator.setEnabled(False)

            else:
                self.btn_add_translator.setEnabled(False)
                self.btn_edit_translator.setEnabled(True)
                self.btn_delete_translator.setEnabled(True)

        else:
            self.btn_add_translator.setEnabled(False)
            self.btn_edit_translator.setEnabled(False)
            self.btn_delete_translator.setEnabled(False)

    def _selected_destination_column(self):
        dest_field_item = self.lstTargetFields.currentItem()

        if dest_field_item is None:
            return ""

        else:
            return dest_field_item.text()

    def _selected_source_column(self):
        src_field_item = self.lstSrcFields.currentItem()

        if src_field_item is None:
            return ""

        else:
            return src_field_item.text()

    def _set_target_fields_stylesheet(self):
        self.lstTargetFields.setStyleSheet("QListWidget#lstTargetFields::item:selected"
                                           " { selection-background-color: darkblue }")

    def registerFields(self):
        #Register wizard fields
        pgSource = self.page(0)
        pgSource.registerField("srcFile*",self.txtDataSource)
        pgSource.registerField("typeText",self.rbTextType)
        pgSource.registerField("typeSpatial",self.rbSpType)
        
        #Destination table configuration
        destConf = self.page(1)
        destConf.registerField("optAppend",self.rbAppend)
        destConf.registerField("optOverwrite",self.rbOverwrite)
        destConf.registerField("tabIndex*",self.lstDestTables)
        destConf.registerField("geomCol",self.geomClm,"currentText",SIGNAL("currentIndexChanged(int)"))
        
    def initializePage(self,pageid):
        #Re-implementation of wizard page initialization
        if pageid == 1:
            #Reference to checked listwidget item representing table name
            self.destCheckedItem=None
            self.geomClm.clear()
            
            if self.field("typeText"):
                self.loadTables("textual")
                self.geomClm.setEnabled(False)
                
            elif self.field("typeSpatial"):
                self.loadTables("spatial")
                self.geomClm.setEnabled(True)
                
        if pageid == 2:
            self.lstSrcFields.clear()
            self.lstTargetFields.clear()
            self.assignCols()
            self._enable_disable_trans_tools()

    def _source_columns(self):
        return self.dataReader.getFields()

    def assignCols(self):
        #Load source and target columns respectively
        srcCols = self._source_columns()
        
        for c in srcCols:
            srcItem = QListWidgetItem(c,self.lstSrcFields)
            srcItem.setCheckState(Qt.Unchecked)
            srcItem.setIcon(QIcon(":/plugins/stdm/images/icons/column.png"))
            self.lstSrcFields.addItem(srcItem)
            
        #Destination Columns
        tabIndex = int(self.field("tabIndex"))
        self.targetTab = self.destCheckedItem.text()
        targetCols = table_column_names(self.targetTab, False, True)

        #Remove geometry columns in the target columns list
        for gc in self.geomcols:            
            colIndex = getIndex(targetCols,gc)
            if colIndex != -1:
                targetCols.remove(gc)

        #Remove 'id' column if there
        id_idx = getIndex(targetCols, 'id')
        if id_idx != -1:
            targetCols.remove('id')

        self._add_target_table_columns(targetCols)

    def _add_target_table_columns(self, items, style=False):
        for item in items:
            list_item = QListWidgetItem(item)

            if style:
                color = QColor(0, 128, 255)
                list_item.setTextColor(color)

            self.lstTargetFields.addItem(list_item)
                
    def _on_load_virtual_columns(self, state):
        """
        Load/unload relationships in the list of destination table columns.
        """
        virtual_columns = self.dataReader.entity_virtual_columns(self.targetTab)

        if state:
            if len(virtual_columns) == 0:
                msg = QApplication.translate("ImportData",
                    "There are no virtual columns for the specified table.")
                QMessageBox.warning(
                    self,
                    QApplication.translate(
                        'ImportData',
                        'Import Data'
                    ),
                    msg
                )
                self.chk_virtual.setChecked(False)

                return

            self._add_target_table_columns(virtual_columns, True)

        else:
            self._remove_destination_table_fields(virtual_columns)

    def _remove_destination_table_fields(self, fields):
        """Remove the specified columns from the destination view."""
        for f in fields:
            list_items = self.lstTargetFields.findItems(f, Qt.MatchFixedString)
            if len(list_items) > 0:
                list_item = list_items[0]

                row = self.lstTargetFields.row(list_item)

                rem_item = self.lstTargetFields.takeItem(row)
                del rem_item

                #Delete translator if already defined for the given column
                self._delete_translator(f)

    def loadGeomCols(self, table):
        #Load geometry columns based on the selected table 
        self.geomcols = table_column_names(table, True, True)
        self.geomClm.clear()
        self.geomClm.addItems(self.geomcols)
                
    def loadTables(self, type):
        #Load textual or spatial tables
        self.lstDestTables.clear()
        tables = None
        if type == "textual":
            tables = profile_user_tables(self.curr_profile, False, True)
            
        elif type == "spatial":
            tables = profile_spatial_tables(self.curr_profile)
        if tables is not None:
            for t in tables:
                tabItem = QListWidgetItem(t,self.lstDestTables)
                tabItem.setCheckState(Qt.Unchecked)
                tabItem.setIcon(QIcon(":/plugins/stdm/images/icons/table.png"))
                self.lstDestTables.addItem(tabItem)
                
    def validateCurrentPage(self):
        #Validate the current page before proceeding to the next one
        validPage=True
        
        if not QFile.exists(unicode(self.field("srcFile"))):
            self.ErrorInfoMessage("The specified source file does not exist.")
            validPage = False
            
        else:
            if self.dataReader:
                self.dataReader.reset()
            self.dataReader = OGRReader(unicode(self.field("srcFile")))
            
            if not self.dataReader.isValid():
                self.ErrorInfoMessage("The source file could not be opened."
                                      "\nPlease check is the given file type "
                                      "is supported")
                validPage = False
                
        if self.currentId()==1:
            if self.destCheckedItem == None:                                                        
                self.ErrorInfoMessage("Please select the destination table.")
                validPage = False
                
        if self.currentId()==2:
            validPage = self.execImport()

        return validPage

    def setSourceFile(self):
        #Set the file path to the source file
        imageFilters = "Comma Separated Value (*.csv);;ESRI Shapefile (*.shp);;AutoCAD DXF (*.dxf)" 
        sourceFile = QFileDialog.getOpenFileName(self,"Select Source File",vectorFileDir(),imageFilters)
        if sourceFile != "":
            self.txtDataSource.setText(sourceFile) 
        
    def getSrcDestPairs(self):
        #Return the matched source and destination columns
        srcDest = {}
        for l in range(self.lstTargetFields.count()):
            if l < self.lstSrcFields.count():                
                srcItem = self.lstSrcFields.item(l)
                if srcItem.checkState() == Qt.Checked:
                    destItem = self.lstTargetFields.item(l)
                    srcDest[srcItem.text()] = destItem.text()
                    
        return srcDest
        
    def execImport(self):
        #Initiate the import process
        success = False
        matchCols = self.getSrcDestPairs()
        
        #Specify geometry column
        geom_column=None
        
        if self.field("typeSpatial"):
            geom_column = self.field("geomCol")
            
        # Ensure that user has selected at least one column if it is a
        # non-spatial table
        if len(matchCols) == 0:
            self.ErrorInfoMessage("Please select at least one source column.")
            return success

        value_translator_manager = self._trans_widget_mgr.translator_manager()
               
        # try:
        if self.field("optOverwrite"):
            entity = self.curr_profile.entity_by_name(self.targetTab)
            dependencies = entity.dependencies()
            view_dep = dependencies['views']
            entity_dep = [e.name for e in entity.children()]
            entities_dep_str = ', '.join(entity_dep)
            views_dep_str = ', '.join(view_dep)

            if len(entity_dep) > 0 or len(view_dep) > 0:
                del_msg = QApplication.translate(
                    'ImportData',
                    "Overwriting existing records will permanently \n"
                    "remove records from other tables linked to the \n"
                    "records. The following tables will be affected."
                    "\n{}\n{}"
                    "\nClick Yes to proceed importing or No to cancel.".
                        format(entities_dep_str, views_dep_str)
                )
                del_result = QMessageBox.critical(
                    self,
                    QApplication.translate(
                        "ImportData",
                        "Overwrite Import Data Warning"
                    ),
                    del_msg,
                    QMessageBox.Yes | QMessageBox.No
                )

                if del_result == QMessageBox.Yes:
                    self.dataReader.featToDb(
                        self.targetTab, matchCols, False, self, geom_column,
                        translator_manager=value_translator_manager
                    )
                    # Update directory info in the registry
                    setVectorFileDir(self.field("srcFile"))

                    self.InfoMessage(
                        "All features have been imported successfully!")

                else:
                    success = False
        else:
            self.dataReader.featToDb(
                self.targetTab, matchCols, True, self, geom_column,
                translator_manager=value_translator_manager
            )
            self.InfoMessage(
                "All features have been imported successfully!"
            )
            #Update directory info in the registry
            setVectorFileDir(self.field("srcFile"))
            success = True
        # except:
        #     self.ErrorInfoMessage(unicode(sys.exc_info()[1]))

        return success

    def _clear_dest_table_selections(self, exclude=None):
        #Clears checked items in destination table list view
        if exclude is None:
            exclude = []

        for i in range(self.lstDestTables.count()):
            item = self.lstDestTables.item(i)
            if item.checkState() == Qt.Checked and not item.text() in exclude:
                item.setCheckState(Qt.Unchecked)
        
    def destSelectChanged(self, item):
        """
        Handler when a list widget item is clicked,
        clears previous selections
        """
        if not self.destCheckedItem is None:
            if item.checkState() == Qt.Checked:
                self.destCheckedItem.setCheckState(Qt.Unchecked) 
            else:
                self.destCheckedItem = None 
              
        if item.checkState() == Qt.Checked:
            self.destCheckedItem = item

            #Ensure other selected items have been cleared
            self._clear_dest_table_selections(exclude=[item.text()])

            #Load geometry columns if selection is a spatial table
            if self.field("typeSpatial"):
                self.loadGeomCols(item.text())
                
    def syncRowSelection(self, srcList, destList):
        """
        Sync the selection of an srcList item to the corresponding one in
        the destination column list.
        """
        if (srcList.currentRow() + 1) <= destList.count():
            destList.setCurrentRow(srcList.currentRow())
            
    def sourceRowChanged(self):
        #Slot when the source list's current row changes
        self.syncRowSelection(self.lstSrcFields,self.lstTargetFields)
        
    def destRowChanged(self):
        #Slot when the destination list's current row changes
        self.syncRowSelection(self.lstTargetFields, self.lstSrcFields)
                
    def itemUp(self, listWidget):
        #Moves the selected item in the list widget one level up
        curIndex = listWidget.currentRow()
        curItem = listWidget.takeItem(curIndex)
        listWidget.insertItem(curIndex - 1, curItem)
        listWidget.setCurrentRow(curIndex - 1)
        
    def itemDown(self, listWidget):
        #Moves the selected item in the list widget one level down
        curIndex=listWidget.currentRow()
        curItem=listWidget.takeItem(curIndex)
        listWidget.insertItem(curIndex + 1,curItem)
        listWidget.setCurrentRow(curIndex + 1)
        
    def checkAllItems(self, listWidget, state):
        #Checks all items in the list widget
        for l in range(listWidget.count()):
            item=listWidget.item(l)
            if state:
                item.setCheckState(Qt.Checked)
            else:
                item.setCheckState(Qt.Unchecked)
                
    def checkSrcItems(self):
        #Slot for checking all source table columns
        self.checkAllItems(self.lstSrcFields, True)
        
    def uncheckSrcItems(self):
        #Slot for unchecking all source table columns
        self.checkAllItems(self.lstSrcFields, False)
        
    def srcItemUp(self):
        #Slot for moving source list item up
        self.itemUp(self.lstSrcFields)
        
    def srcItemDown(self):
        #Slot for moving source list item down
        self.itemDown(self.lstSrcFields)
    
    def targetItemUp(self):
        #Slot for moving target item up
        self.itemUp(self.lstTargetFields)
        
    def targetItemDown(self):
        #Slot for moving target item down
        self.itemDown(self.lstTargetFields)
         
    def keyPressEvent(self,e):
        """
        Override method for preventing the dialog from
        closing itself when the escape key is hit
        """
        if e.key() == Qt.Key_Escape:
            pass
        
    def InfoMessage(self, message):
        #Information message box        
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Information)
        msg.setText(message)
        msg.exec_()
                  
    def ErrorInfoMessage(self, message):
        #Error Message Box
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Critical)
        msg.setText(message)
        msg.exec_()
    #
    # def drag_drop_handler(self, event):
    #     if event.source() == self:
    #         rows = set([mi.row() for mi in self.selectedIndexes()])
    #         targetRow = self.indexAt(event.pos()).row()
    #         rows.discard(targetRow)
    #         rows = sorted(rows)
    #         if not rows:
    #             return
    #         if targetRow == -1:
    #             targetRow = self.rowCount()
    #         for _ in range(len(rows)):
    #             self.insertRow(targetRow)
    #         rowMapping = dict()  # Src row to target row.
    #         for idx, row in enumerate(rows):
    #             if row < targetRow:
    #                 rowMapping[row] = targetRow + idx
    #             else:
    #                 rowMapping[row + len(rows)] = targetRow + idx
    #         colCount = self.columnCount()
    #         for srcRow, tgtRow in sorted(rowMapping.iteritems()):
    #             for col in range(0, colCount):
    #                 self.setItem(tgtRow, col, self.takeItem(srcRow, col))
    #         for row in reversed(sorted(rowMapping.iterkeys())):
    #             self.removeRow(row)
    #         event.accept()
    #         return


    def drag_move(self, e):
        e.accept()
예제 #18
0
class TrackingWindow(QMainWindow):
    """
    Main window of the application.

    This class is responsible for the global data structures too.

    :IVariables:
        undo_stack : `QUndoStack`
            Undo stack. All actions that can be undone should be pushed on the stack.
        _project : `project.Project`
            Project object managing the loaded data
        _data : `tracking_data.TrackingData`
            Data object keeping track of points and cells
        toolGroup : `QActionGroup`
            Group of actions to be enabled only when actions can be taken on images
        previousSelAct : `QActionGroup`
            Actions enabled when points are selected in the previous pane
        currentSelAct : `QActionGroup`
            Actions enabled when points are selected in the current pane
        projectAct : `QActionGroup`
            Actions to enable once a project is loaded
        _previousScene : `tracking_scene.TrackingScene`
            Object managing the previous pane
        _currentScene : `tracking_scene.LinkedTrackingScene`
            Object managing the current pane
    """
    def __init__(self, *args, **kwords):
        QMainWindow.__init__(self, *args)
        self.undo_stack = QUndoStack(self)
        self.ui = Ui_TrackingWindow()
        self.ui.setupUi(self)
        self._project = None
        self._data = None
        self.toolGroup = QActionGroup(self)
        self.toolGroup.addAction(self.ui.actionAdd_point)
        self.toolGroup.addAction(self.ui.action_Move_point)
        self.toolGroup.addAction(self.ui.actionAdd_cell)
        self.toolGroup.addAction(self.ui.actionRemove_cell)
        self.toolGroup.addAction(self.ui.action_Pan)
        self.toolGroup.addAction(self.ui.actionZoom_out)
        self.toolGroup.addAction(self.ui.actionZoom_in)
        self.previousSelAct = QActionGroup(self)
        self.previousSelAct.addAction(
            self.ui.actionCopy_selection_from_Previous)
        self.previousSelAct.addAction(self.ui.actionDelete_Previous)
        self.previousSelAct.setEnabled(False)
        self.currentSelAct = QActionGroup(self)
        self.currentSelAct.addAction(self.ui.actionCopy_selection_from_Current)
        self.currentSelAct.addAction(self.ui.actionDelete_Current)
        self.currentSelAct.setEnabled(False)
        self.projectAct = QActionGroup(self)
        self.projectAct.addAction(self.ui.action_Next_image)
        self.projectAct.addAction(self.ui.action_Previous_image)
        self.projectAct.addAction(self.ui.actionAdd_point)
        self.projectAct.addAction(self.ui.action_Move_point)
        self.projectAct.addAction(self.ui.action_Pan)
        self.projectAct.addAction(self.ui.actionAdd_cell)
        self.projectAct.addAction(self.ui.actionRemove_cell)
        self.projectAct.addAction(self.ui.action_Change_data_file)
        self.projectAct.addAction(self.ui.actionNew_data_file)
        self.projectAct.addAction(self.ui.actionZoom_out)
        self.projectAct.addAction(self.ui.actionZoom_in)
        self.projectAct.addAction(self.ui.actionSave_as)
        self.projectAct.addAction(self.ui.action_Fit)
        self.projectAct.addAction(self.ui.actionZoom_100)
        self.projectAct.addAction(self.ui.actionMerge_points)
        self.projectAct.addAction(self.ui.actionCopy_from_previous)
        self.projectAct.addAction(self.ui.actionCopy_from_current)
        self.projectAct.addAction(self.ui.actionReset_alignment)
        self.projectAct.addAction(self.ui.actionAlign_images)
        self.projectAct.addAction(self.ui.actionSelectPreviousAll)
        self.projectAct.addAction(self.ui.actionSelectPreviousNew)
        self.projectAct.addAction(self.ui.actionSelectPreviousNone)
        self.projectAct.addAction(self.ui.actionSelectPreviousNon_associated)
        self.projectAct.addAction(self.ui.actionSelectPreviousAssociated)
        self.projectAct.addAction(self.ui.actionSelectPreviousInvert)
        self.projectAct.addAction(self.ui.actionSelectCurrentAll)
        self.projectAct.addAction(self.ui.actionSelectCurrentNew)
        self.projectAct.addAction(self.ui.actionSelectCurrentNone)
        self.projectAct.addAction(self.ui.actionSelectCurrentNon_associated)
        self.projectAct.addAction(self.ui.actionSelectCurrentAssociated)
        self.projectAct.addAction(self.ui.actionSelectCurrentInvert)
        self.projectAct.addAction(self.ui.actionEdit_timing)
        self.projectAct.addAction(self.ui.actionEdit_scales)
        self.projectAct.addAction(self.ui.actionCompute_growth)
        self.projectAct.addAction(self.ui.actionClean_cells)
        self.projectAct.addAction(self.ui.actionGotoCell)

        self.projectAct.setEnabled(False)

        current_sel_actions = [
            self.ui.actionSelectCurrentAll, self.ui.actionSelectCurrentNew,
            self.ui.actionSelectCurrentNone, self.ui.actionSelectCurrentInvert,
            '-', self.ui.actionSelectCurrentNon_associated,
            self.ui.actionSelectCurrentAssociated,
            self.ui.actionCopy_selection_from_Previous
        ]

        previous_sel_actions = [
            self.ui.actionSelectPreviousAll, self.ui.actionSelectPreviousNew,
            self.ui.actionSelectPreviousNone,
            self.ui.actionSelectPreviousInvert, '-',
            self.ui.actionSelectPreviousNon_associated,
            self.ui.actionSelectPreviousAssociated,
            self.ui.actionCopy_selection_from_Current
        ]

        self._previousScene = TrackingScene(self.undo_stack,
                                            self.ui.actionDelete_Previous,
                                            previous_sel_actions, self)
        self._currentScene = LinkedTrackingScene(self._previousScene,
                                                 self.undo_stack,
                                                 self.ui.actionDelete_Current,
                                                 current_sel_actions, self)
        self._previousScene.hasSelectionChanged.connect(
            self.previousSelAct.setEnabled)
        self._currentScene.hasSelectionChanged.connect(
            self.currentSelAct.setEnabled)
        self._previousScene.realSceneSizeChanged.connect(self.sceneSizeChanged)
        self._currentScene.realSceneSizeChanged.connect(self.sceneSizeChanged)
        self._previousScene.zoomIn[QPointF].connect(self.zoomIn)
        self._currentScene.zoomIn.connect(self.zoomIn)
        self._previousScene.zoomOut[QPointF].connect(self.zoomOut)
        self._currentScene.zoomOut.connect(self.zoomOut)
        self.ui.previousData.setScene(self._previousScene)
        self.ui.currentData.setScene(self._currentScene)
        self.ui.previousData.setDragMode(QGraphicsView.ScrollHandDrag)
        self.ui.currentData.setDragMode(QGraphicsView.ScrollHandDrag)
        #self.ui.previousData.setCacheMode(QGraphicsView.CacheBackground)
        #self.ui.currentData.setCacheMode(QGraphicsView.CacheBackground)

        # Redefine shortcuts to standard key sequences
        self.ui.action_Save.setShortcut(QKeySequence.Save)
        self.ui.actionSave_as.setShortcut(QKeySequence.SaveAs)
        self.ui.action_Open_project.setShortcut(QKeySequence.Open)
        self.ui.action_Undo.setShortcut(QKeySequence.Undo)
        self.ui.action_Redo.setShortcut(QKeySequence.Redo)
        self.ui.action_Next_image.setShortcut(QKeySequence.Forward)
        self.ui.action_Previous_image.setShortcut(QKeySequence.Back)

        # Connecting undo stack signals
        self.ui.action_Undo.triggered.connect(self.undo)
        self.ui.action_Redo.triggered.connect(self.redo)
        self.undo_stack.canRedoChanged[bool].connect(
            self.ui.action_Redo.setEnabled)
        self.undo_stack.canUndoChanged[bool].connect(
            self.ui.action_Undo.setEnabled)
        self.undo_stack.redoTextChanged["const QString&"].connect(
            self.changeRedoText)
        self.undo_stack.undoTextChanged["const QString&"].connect(
            self.changeUndoText)
        self.undo_stack.cleanChanged[bool].connect(
            self.ui.action_Save.setDisabled)

        #        link_icon = QIcon()
        #        pix = QPixmap(":/icons/link.png")
        #        link_icon.addPixmap(pix, QIcon.Normal, QIcon.On)
        #        pix = QPixmap(":/icons/link_broken.png")
        #        link_icon.addPixmap(pix, QIcon.Normal, QIcon.Off)
        #        self.link_icon = link_icon
        #        #self.ui.linkViews.setIconSize(QSize(64,32))
        #        self.ui.linkViews.setIcon(link_icon)

        self._recent_projects_menu = QMenu(self)
        self.ui.actionRecent_projects.setMenu(self._recent_projects_menu)

        self._recent_projects_act = []
        self._projects_mapper = QSignalMapper(self)
        self._projects_mapper.mapped[int].connect(self.loadRecentProject)

        self.param_dlg = None

        # Setting up the status bar
        bar = self.statusBar()
        # Adding current directory
        cur_dir = QLabel("")
        bar.addPermanentWidget(cur_dir)
        self._current_dir_label = cur_dir
        # Adding up zoom
        zoom = QLabel("")
        bar.addPermanentWidget(zoom)
        self._zoom_label = zoom
        self.changeZoom(1)

        self.loadConfig()

        parameters.instance.renderingChanged.connect(self.changeRendering)
        self.changeRendering()

    def changeRendering(self):
        if parameters.instance.use_OpenGL:
            self.ui.previousData.setViewport(
                QGLWidget(QGLFormat(QGL.SampleBuffers)))
            self.ui.currentData.setViewport(
                QGLWidget(QGLFormat(QGL.SampleBuffers)))
        else:
            self.ui.previousData.setViewport(QWidget())
            self.ui.currentData.setViewport(QWidget())

    def undo(self):
        self.undo_stack.undo()

    def redo(self):
        self.undo_stack.redo()

    def changeRedoText(self, text):
        self.ui.action_Redo.setText(text)
        self.ui.action_Redo.setToolTip(text)
        self.ui.action_Redo.setStatusTip(text)

    def changeUndoText(self, text):
        self.ui.action_Undo.setText(text)
        self.ui.action_Undo.setToolTip(text)
        self.ui.action_Undo.setStatusTip(text)

    def closeEvent(self, event):
        self.saveConfig()
        if not self.ensure_save_data(
                "Exiting whith unsaved data",
                "The last modifications you made were not saved."
                " Are you sure you want to exit?"):
            event.ignore()
            return
        QMainWindow.closeEvent(self, event)
        #sys.exit(0)

    def loadConfig(self):
        params = parameters.instance
        self.ui.action_Show_vector.setChecked(params.show_vectors)
        self.ui.linkViews.setChecked(params.link_views)
        self.ui.action_Show_template.setChecked(
            parameters.instance.show_template)
        self.ui.actionShow_id.setChecked(parameters.instance.show_id)
        self.ui.action_Estimate_position.setChecked(
            parameters.instance.estimate)
        self.updateRecentFiles()
        params.recentProjectsChange.connect(self.updateRecentFiles)

    def updateRecentFiles(self):
        for a in self._recent_projects_act:
            self._projects_mapper.removeMappings(a)
        del self._recent_projects_act[:]
        menu = self._recent_projects_menu
        menu.clear()
        recent_projects = parameters.instance.recent_projects
        for i, p in enumerate(recent_projects):
            act = QAction(self)
            act.setText("&{0:d} {1}".format(i + 1, p))
            self._recent_projects_act.append(act)
            act.triggered.connect(self._projects_mapper.map)
            self._projects_mapper.setMapping(act, i)
            menu.addAction(act)

    def saveConfig(self):
        parameters.instance.save()

    def check_for_data(self):
        if self._project is None:
            QMessageBox.critical(
                self, "No project loaded",
                "You have to load a project before performing this operation")
            return False
        return True

    def loadRecentProject(self, i):
        if self.ensure_save_data(
                "Leaving unsaved data",
                "The last modifications you made were not saved."
                " Are you sure you want to change project?"):
            self.loadProject(parameters.instance.recent_projects[i])

    @pyqtSignature("")
    def on_action_Open_project_triggered(self):
        if self.ensure_save_data(
                "Leaving unsaved data",
                "The last modifications you made were not saved."
                " Are you sure you want to change project?"):
            dir_ = QFileDialog.getExistingDirectory(
                self, "Select a project directory",
                parameters.instance._last_dir)
            if dir_:
                self.loadProject(dir_)

    def loadProject(self, dir_):
        dir_ = path(dir_)
        project = Project(dir_)
        if project.valid:
            self._project = project
        else:
            create = QMessageBox.question(
                self, "Invalid project directory",
                "This directory does not contain a valid project. Turn into a directory?",
                QMessageBox.No, QMessageBox.Yes)
            if create == QMessageBox.No:
                return
            project.create()
            self._project = project
        self._project.use()
        parameters.instance.add_recent_project(dir_)
        parameters.instance._last_dir = dir_
        if self._data is not None:
            _data = self._data
            _data.saved.disconnect(self.undo_stack.setClean)
        try:
            #self._project.load()
            self.load_data()
            _data = self._project.data
            _data.saved.connect(self.undo_stack.setClean)
            self._project.changedDataFile.connect(self.dataFileChanged)
            self._data = _data
            self._previousScene.changeDataManager(self._data)
            self._currentScene.changeDataManager(self._data)
            self.initFromData()
            self.projectAct.setEnabled(True)
        except TrackingDataException as ex:
            showException(self, "Error while loaded data", ex)

    def dataFileChanged(self, new_file):
        if new_file is None:
            self._current_dir_label.setText("")
        else:
            self._current_dir_label.setText(new_file)

    def initFromData(self):
        """
        Initialize the interface using the current data
        """
        self.ui.previousState.clear()
        self.ui.currentState.clear()
        for name in self._data.images_name:
            self.ui.previousState.addItem(name)
            self.ui.currentState.addItem(name)
        self.ui.previousState.setCurrentIndex(0)
        self.ui.currentState.setCurrentIndex(1)
        self._previousScene.changeImage(
            self._data.image_path(self._data.images_name[0]))
        self._currentScene.changeImage(
            self._data.image_path(self._data.images_name[1]))
        self.dataFileChanged(self._project.data_file)

    @pyqtSignature("int")
    def on_previousState_currentIndexChanged(self, index):
        #print "Previous image loaded: %s" % self._data.images[index]
        self.changeScene(self._previousScene, index)
        self._currentScene.changeImage(None)

    @pyqtSignature("int")
    def on_currentState_currentIndexChanged(self, index):
        #print "Current image loaded: %s" % self._data.images[index]
        self.changeScene(self._currentScene, index)

    def changeScene(self, scene, index):
        """
        Set the scene to use the image number index.
        """
        scene.changeImage(self._data.image_path(self._data.images_name[index]))

    @pyqtSignature("")
    def on_action_Save_triggered(self):
        self.save_data()

    @pyqtSignature("")
    def on_actionSave_as_triggered(self):
        fn = QFileDialog.getSaveFileName(self, "Select a data file to save in",
                                         self._project.data_dir,
                                         "CSV Files (*.csv);;All files (*.*)")
        if fn:
            self.save_data(path(fn))

    def save_data(self, data_file=None):
        if self._data is None:
            raise TrackingDataException(
                "Trying to save data when none have been loaded")
        try:
            self._project.save(data_file)
            return True
        except TrackingDataException as ex:
            showException(self, "Error while saving data", ex)
            return False

    def load_data(self, **opts):
        if self._project is None:
            raise TrackingDataException(
                "Trying to load data when no project have been loaded")
        try:
            if self._project.load(**opts):
                log_debug("Data file was corrected. Need saving.")
                self.ui.action_Save.setEnabled(True)
            else:
                log_debug("Data file is clean.")
                self.ui.action_Save.setEnabled(False)
            return True
        except TrackingDataException as ex:
            showException(self, "Error while loading data", ex)
            return False
        except RetryTrackingDataException as ex:
            if retryException(self, "Problem while loading data", ex):
                new_opts = dict(opts)
                new_opts.update(ex.method_args)
                return self.load_data(**new_opts)
            return False

    def ensure_save_data(self, title, reason):
        if self._data is not None and not self.undo_stack.isClean():
            button = QMessageBox.warning(
                self, title, reason,
                QMessageBox.Yes | QMessageBox.Save | QMessageBox.Cancel)
            if button == QMessageBox.Save:
                return self.save_data()
            elif button == QMessageBox.Cancel:
                return False
            self.undo_stack.clear()
        return True

    @pyqtSignature("")
    def on_action_Change_data_file_triggered(self):
        if self.ensure_save_data(
                "Leaving unsaved data",
                "The last modifications you made were not saved."
                " Are you sure you want to change the current data file?"):
            fn = QFileDialog.getOpenFileName(
                self, "Select a data file to load", self._project.data_dir,
                "CSV Files (*.csv);;All files (*.*)")
            if fn:
                self._project.data_file = str(fn)
                if self.load_data():
                    self._previousScene.resetNewPoints()
                    self._currentScene.resetNewPoints()

    @pyqtSignature("bool")
    def on_action_Show_vector_toggled(self, value):
        parameters.instance.show_vector = value
        self._currentScene.showVector(value)

    @pyqtSignature("bool")
    def on_action_Show_template_toggled(self, value):
        parameters.instance.show_template = value

    @pyqtSignature("bool")
    def on_actionShow_id_toggled(self, value):
        parameters.instance.show_id = value

    @pyqtSignature("")
    def on_action_Next_image_triggered(self):
        cur = self.ui.currentState.currentIndex()
        pre = self.ui.previousState.currentIndex()
        l = len(self._data.images_name)
        if cur < l - 1 and pre < l - 1:
            self.ui.previousState.setCurrentIndex(pre + 1)
            self.ui.currentState.setCurrentIndex(cur + 1)

    @pyqtSignature("")
    def on_action_Previous_image_triggered(self):
        cur = self.ui.currentState.currentIndex()
        pre = self.ui.previousState.currentIndex()
        if cur > 0 and pre > 0:
            self.ui.previousState.setCurrentIndex(pre - 1)
            self.ui.currentState.setCurrentIndex(cur - 1)

    @pyqtSignature("")
    def on_copyToPrevious_clicked(self):
        self._currentScene.copyFromLinked(self._previousScene)

    @pyqtSignature("")
    def on_copyToCurrent_clicked(self):
        self._previousScene.copyToLinked(self._currentScene)

    @pyqtSignature("bool")
    def on_action_Estimate_position_toggled(self, value):
        parameters.instance.estimate = value

#  @pyqtSignature("")
#  def on_action_Undo_triggered(self):
#    print "Undo"

#  @pyqtSignature("")
#  def on_action_Redo_triggered(self):
#    print "Redo"

    @pyqtSignature("bool")
    def on_action_Parameters_toggled(self, value):
        if value:
            from .parametersdlg import ParametersDlg
            self._previousScene.showTemplates()
            self._currentScene.showTemplates()
            #tracking_scene.saveParameters()
            parameters.instance.save()
            max_size = max(self._currentScene.width(),
                           self._currentScene.height(),
                           self._previousScene.width(),
                           self._previousScene.height(), 400)
            self.param_dlg = ParametersDlg(max_size, self)
            self.param_dlg.setModal(False)
            self.ui.action_Pan.setChecked(True)
            self.ui.actionAdd_point.setEnabled(False)
            self.ui.action_Move_point.setEnabled(False)
            self.ui.actionAdd_cell.setEnabled(False)
            self.ui.actionRemove_cell.setEnabled(False)
            self.ui.action_Undo.setEnabled(False)
            self.ui.action_Redo.setEnabled(False)
            self.ui.action_Open_project.setEnabled(False)
            self.ui.actionRecent_projects.setEnabled(False)
            self.ui.action_Change_data_file.setEnabled(False)
            self.ui.copyToCurrent.setEnabled(False)
            self.ui.copyToPrevious.setEnabled(False)
            self.param_dlg.finished[int].connect(self.closeParam)
            self.param_dlg.show()
        elif self.param_dlg:
            self.param_dlg.accept()

    def closeParam(self, value):
        if value == QDialog.Rejected:
            parameters.instance.load()
        self.ui.actionAdd_point.setEnabled(True)
        self.ui.action_Move_point.setEnabled(True)
        self.ui.actionAdd_cell.setEnabled(True)
        self.ui.actionRemove_cell.setEnabled(True)
        self.ui.action_Undo.setEnabled(True)
        self.ui.action_Redo.setEnabled(True)
        self.ui.action_Open_project.setEnabled(True)
        self.ui.actionRecent_projects.setEnabled(True)
        self.ui.action_Change_data_file.setEnabled(True)
        self.ui.copyToCurrent.setEnabled(True)
        self.ui.copyToPrevious.setEnabled(True)
        self._previousScene.showTemplates(False)
        self._currentScene.showTemplates(False)
        self._previousScene.update()
        self._currentScene.update()
        self.param_dlg = None
        self.ui.action_Parameters.setChecked(False)

    @pyqtSignature("bool")
    def on_actionZoom_in_toggled(self, value):
        if value:
            self._previousScene.mode = TrackingScene.ZoomIn
            self._currentScene.mode = TrackingScene.ZoomIn

    @pyqtSignature("bool")
    def on_actionZoom_out_toggled(self, value):
        if value:
            self._previousScene.mode = TrackingScene.ZoomOut
            self._currentScene.mode = TrackingScene.ZoomOut

    #def resizeEvent(self, event):
    #    self.ensureZoomFit()

    def ensureZoomFit(self):
        if self._data:
            prev_rect = self._previousScene.sceneRect()
            cur_rect = self._currentScene.sceneRect()
            prev_wnd = QRectF(self.ui.previousData.childrenRect())
            cur_wnd = QRectF(self.ui.currentData.childrenRect())
            prev_matrix = self.ui.previousData.matrix()
            cur_matrix = self.ui.currentData.matrix()

            prev_mapped_rect = prev_matrix.mapRect(prev_rect)
            cur_mapped_rect = cur_matrix.mapRect(cur_rect)

            if (prev_mapped_rect.width() < prev_wnd.width()
                    or prev_mapped_rect.height() < prev_wnd.height()
                    or cur_mapped_rect.width() < cur_wnd.width()
                    or cur_mapped_rect.height() < cur_wnd.height()):
                self.on_action_Fit_triggered()

    @pyqtSignature("")
    def on_action_Fit_triggered(self):
        prev_rect = self._previousScene.sceneRect()
        cur_rect = self._currentScene.sceneRect()
        prev_wnd = self.ui.previousData.childrenRect()
        cur_wnd = self.ui.currentData.childrenRect()

        prev_sw = prev_wnd.width() / prev_rect.width()
        prev_sh = prev_wnd.height() / prev_rect.height()

        cur_sw = cur_wnd.width() / cur_rect.width()
        cur_sh = cur_wnd.height() / cur_rect.height()

        s = max(prev_sw, prev_sh, cur_sw, cur_sh)

        self.ui.previousData.resetMatrix()
        self.ui.previousData.scale(s, s)
        self.ui.currentData.resetMatrix()
        self.ui.currentData.scale(s, s)
        self.changeZoom(s)

    def zoomOut(self, point=None):
        self.ui.currentData.scale(0.5, 0.5)
        self.ui.previousData.scale(0.5, 0.5)
        self.changeZoom(self.ui.previousData.matrix().m11())
        if point is not None:
            self.ui.previousData.centerOn(point)
            self.ui.currentData.centerOn(point)
        #self.ensureZoomFit()

    def zoomIn(self, point=None):
        self.ui.currentData.scale(2, 2)
        self.ui.previousData.scale(2, 2)
        self.changeZoom(self.ui.previousData.matrix().m11())
        if point is not None:
            self.ui.previousData.centerOn(point)
            self.ui.currentData.centerOn(point)

    def changeZoom(self, zoom):
        self._zoom_label.setText("Zoom: %.5g%%" % (100 * zoom))

    @pyqtSignature("")
    def on_actionZoom_100_triggered(self):
        self.ui.previousData.resetMatrix()
        self.ui.currentData.resetMatrix()
        self.changeZoom(1)

    @pyqtSignature("bool")
    def on_actionAdd_point_toggled(self, value):
        if value:
            self._previousScene.mode = TrackingScene.Add
            self._currentScene.mode = TrackingScene.Add

    @pyqtSignature("bool")
    def on_actionAdd_cell_toggled(self, value):
        if value:
            self._previousScene.mode = TrackingScene.AddCell
            self._currentScene.mode = TrackingScene.AddCell

    @pyqtSignature("bool")
    def on_actionRemove_cell_toggled(self, value):
        if value:
            self._previousScene.mode = TrackingScene.RemoveCell
            self._currentScene.mode = TrackingScene.RemoveCell

    @pyqtSignature("bool")
    def on_action_Move_point_toggled(self, value):
        if value:
            self._previousScene.mode = TrackingScene.Move
            self._currentScene.mode = TrackingScene.Move

    @pyqtSignature("bool")
    def on_action_Pan_toggled(self, value):
        if value:
            self._previousScene.mode = TrackingScene.Pan
            self._currentScene.mode = TrackingScene.Pan

    @pyqtSignature("bool")
    def on_linkViews_toggled(self, value):
        parameters.instance.link_views = value
        phor = self.ui.previousData.horizontalScrollBar()
        pver = self.ui.previousData.verticalScrollBar()
        chor = self.ui.currentData.horizontalScrollBar()
        cver = self.ui.currentData.verticalScrollBar()
        if value:
            phor.valueChanged[int].connect(chor.setValue)
            pver.valueChanged[int].connect(cver.setValue)
            chor.valueChanged[int].connect(phor.setValue)
            cver.valueChanged[int].connect(pver.setValue)
            self._previousScene.templatePosChange.connect(
                self._currentScene.setTemplatePos)
            self._currentScene.templatePosChange.connect(
                self._previousScene.setTemplatePos)
            phor.setValue(chor.value())
            pver.setValue(cver.value())
        else:
            phor.valueChanged[int].disconnect(chor.setValue)
            pver.valueChanged[int].disconnect(cver.setValue)
            chor.valueChanged[int].disconnect(phor.setValue)
            cver.valueChanged[int].disconnect(pver.setValue)
            self._previousScene.templatePosChange.disconnect(
                self._currentScene.setTemplatePos)
            self._currentScene.templatePosChange.disconnect(
                self._previousScene.setTemplatePos)

    def copyFrom(self, start, items):
        if parameters.instance.estimate:
            dlg = createForm('copy_progress.ui', None)
            dlg.buttonBox.clicked["QAbstractButton*"].connect(self.cancelCopy)
            params = parameters.instance
            ts = params.template_size
            ss = params.search_size
            fs = params.filter_size
            self.copy_thread = algo.FindInAll(self._data, start, items, ts, ss,
                                              fs, self)
            dlg.imageProgress.setMaximum(self.copy_thread.num_images)
            self.copy_thread.start()
            self.copy_dlg = dlg
            dlg.exec_()
        else:
            algo.copyFromImage(self._data, start, items, self.undo_stack)

    def cancelCopy(self, *args):
        self.copy_thread.stop = True
        dlg = self.copy_dlg
        dlg.buttonBox.clicked['QAbstractButton*)'].disconnect(self.cancelCopy)
        self._previousScene.changeImage(None)
        self._currentScene.changeImage(None)

    def event(self, event):
        if isinstance(event, algo.NextImage):
            dlg = self.copy_dlg
            if dlg is not None:
                dlg.imageProgress.setValue(event.currentImage)
                dlg.pointProgress.setMaximum(event.nbPoints)
                dlg.pointProgress.setValue(0)
            return True
        elif isinstance(event, algo.NextPoint):
            dlg = self.copy_dlg
            if dlg is not None:
                dlg.pointProgress.setValue(event.currentPoint)
            return True
        elif isinstance(event, algo.FoundAll):
            dlg = self.copy_dlg
            if dlg is not None:
                self.cancelCopy()
                dlg.accept()
            return True
        elif isinstance(event, algo.Aborted):
            dlg = self.copy_dlg
            if dlg is not None:
                self.cancelCopy()
                dlg.accept()
            return True
        return QMainWindow.event(self, event)

    def itemsToCopy(self, scene):
        items = scene.getSelectedIds()
        if items:
            answer = QMessageBox.question(
                self, "Copy of points",
                "Some points were selected in the previous data window."
                " Do you want to copy only these point on the successive images?",
                QMessageBox.Yes, QMessageBox.No)
            if answer == QMessageBox.Yes:
                return items
        return scene.getAllIds()

    @pyqtSignature("")
    def on_actionCopy_from_previous_triggered(self):
        items = self.itemsToCopy(self._previousScene)
        if items:
            self.copyFrom(self.ui.previousState.currentIndex(), items)

    @pyqtSignature("")
    def on_actionCopy_from_current_triggered(self):
        items = self.itemsToCopy(self._currentScene)
        if items:
            self.copyFrom(self.ui.currentState.currentIndex(), items)

    @pyqtSignature("")
    def on_actionSelectPreviousAll_triggered(self):
        self._previousScene.selectAll()

    @pyqtSignature("")
    def on_actionSelectPreviousNew_triggered(self):
        self._previousScene.selectNew()

    @pyqtSignature("")
    def on_actionSelectPreviousNone_triggered(self):
        self._previousScene.selectNone()

    @pyqtSignature("")
    def on_actionSelectPreviousNon_associated_triggered(self):
        self._previousScene.selectNonAssociated()

    @pyqtSignature("")
    def on_actionSelectPreviousAssociated_triggered(self):
        self._previousScene.selectAssociated()

    @pyqtSignature("")
    def on_actionSelectPreviousInvert_triggered(self):
        self._previousScene.selectInvert()

    @pyqtSignature("")
    def on_actionSelectCurrentAll_triggered(self):
        self._currentScene.selectAll()

    @pyqtSignature("")
    def on_actionSelectCurrentNew_triggered(self):
        self._currentScene.selectNew()

    @pyqtSignature("")
    def on_actionSelectCurrentNone_triggered(self):
        self._currentScene.selectNone()

    @pyqtSignature("")
    def on_actionSelectCurrentNon_associated_triggered(self):
        self._currentScene.selectNonAssociated()

    @pyqtSignature("")
    def on_actionSelectCurrentAssociated_triggered(self):
        self._currentScene.selectAssociated()

    @pyqtSignature("")
    def on_actionSelectCurrentInvert_triggered(self):
        self._currentScene.selectInvert()

    def whichDelete(self):
        """
        Returns a function deleting what the user wants
        """
        dlg = createForm("deletedlg.ui", None)
        ret = dlg.exec_()
        if ret:
            if dlg.inAllImages.isChecked():
                return TrackingScene.deleteInAllImages
            if dlg.toImage.isChecked():
                return TrackingScene.deleteToImage
            if dlg.fromImage.isChecked():
                return TrackingScene.deleteFromImage
        return lambda x: None

    @pyqtSignature("")
    def on_actionDelete_Previous_triggered(self):
        del_fct = self.whichDelete()
        del_fct(self._previousScene)

    @pyqtSignature("")
    def on_actionDelete_Current_triggered(self):
        del_fct = self.whichDelete()
        del_fct(self._currentScene)

    @pyqtSignature("")
    def on_actionMerge_points_triggered(self):
        if self._previousScene.mode == TrackingScene.AddCell:
            old_cell = self._previousScene.selected_cell
            new_cell = self._currentScene.selected_cell
            if old_cell is None or new_cell is None:
                QMessageBox.critical(
                    self, "Cannot merge cells",
                    "You have to select exactly one cell in the old state "
                    "and one in the new state to merge them.")
                return
            try:
                if old_cell != new_cell:
                    self.undo_stack.push(
                        MergeCells(self._data, self._previousScene.image_name,
                                   old_cell, new_cell))
                else:
                    self.undo_stack.push(
                        SplitCells(self._data, self._previousScene.image_name,
                                   old_cell, new_cell))
            except AssertionError as error:
                QMessageBox.critical(self, "Cannot merge the cells",
                                     str(error))
        else:
            old_pts = self._previousScene.getSelectedIds()
            new_pts = self._currentScene.getSelectedIds()
            if len(old_pts) != 1 or len(new_pts) != 1:
                QMessageBox.critical(
                    self, "Cannot merge points",
                    "You have to select exactly one point in the old state "
                    "and one in the new state to link them.")
                return
            try:
                if old_pts != new_pts:
                    self.undo_stack.push(
                        ChangePointsId(self._data,
                                       self._previousScene.image_name, old_pts,
                                       new_pts))
                else:
                    log_debug("Splitting point of id %d" % old_pts[0])
                    self.undo_stack.push(
                        SplitPointsId(self._data,
                                      self._previousScene.image_name, old_pts))
            except AssertionError as error:
                QMessageBox.critical(self, "Cannot merge the points",
                                     str(error))

    @pyqtSignature("")
    def on_actionCopy_selection_from_Current_triggered(self):
        cur_sel = self._currentScene.getSelectedIds()
        self._previousScene.setSelectedIds(cur_sel)

    @pyqtSignature("")
    def on_actionCopy_selection_from_Previous_triggered(self):
        cur_sel = self._previousScene.getSelectedIds()
        self._currentScene.setSelectedIds(cur_sel)

    @pyqtSignature("")
    def on_actionNew_data_file_triggered(self):
        if self.ensure_save_data(
                "Leaving unsaved data",
                "The last modifications you made were not saved."
                " Are you sure you want to change the current data file?"):
            fn = QFileDialog.getSaveFileName(
                self, "Select a new data file to create",
                self._project.data_dir, "CSV Files (*.csv);;All files (*.*)")
            if fn:
                fn = path(fn)
                if fn.exists():
                    button = QMessageBox.question(
                        self, "Erasing existing file",
                        "Are you sure yo want to empty the file '%s' ?" % fn,
                        QMessageBox.Yes, QMessageBox.No)
                    if button == QMessageBox.No:
                        return
                    fn.remove()
                self._data.clear()
                self._previousScene.resetNewPoints()
                self._currentScene.resetNewPoints()
                self._project.data_file = fn
                self.initFromData()
                log_debug("Data file = %s" % (self._project.data_file, ))

    @pyqtSignature("")
    def on_actionAbout_triggered(self):
        dlg = QMessageBox(self)
        dlg.setWindowTitle("About Point Tracker")
        dlg.setIconPixmap(self.windowIcon().pixmap(64, 64))
        #dlg.setTextFormat(Qt.RichText)

        dlg.setText("""Point Tracker Tool version %s rev %s
Developper: Pierre Barbier de Reuille <*****@*****.**>
Copyright 2008
""" % (__version__, __revision__))

        img_read = ", ".join(
            str(s) for s in QImageReader.supportedImageFormats())
        img_write = ", ".join(
            str(s) for s in QImageWriter.supportedImageFormats())

        dlg.setDetailedText("""Supported image formats:
  - For reading: %s

  - For writing: %s
""" % (img_read, img_write))
        dlg.exec_()

    @pyqtSignature("")
    def on_actionAbout_Qt_triggered(self):
        QMessageBox.aboutQt(self, "About Qt")

    @pyqtSignature("")
    def on_actionReset_alignment_triggered(self):
        self.undo_stack.push(ResetAlignment(self._data))

    @pyqtSignature("")
    def on_actionAlign_images_triggered(self):
        fn = QFileDialog.getOpenFileName(self,
                                         "Select a data file for alignment",
                                         self._project.data_dir,
                                         "CSV Files (*.csv);;All files (*.*)")
        if fn:
            d = self._data.copy()
            fn = path(fn)
            try:
                d.load(fn)
            except TrackingDataException as ex:
                showException(self, "Error while loading data file", ex)
                return
            if d._last_pt_id > 0:
                dlg = AlignmentDlg(d._last_pt_id + 1, self)
                if dlg.exec_():
                    ref = dlg.ui.referencePoint.currentText()
                    try:
                        ref = int(ref)
                    except ValueError:
                        ref = str(ref)
                    if dlg.ui.twoPointsRotation.isChecked():
                        r1 = int(dlg.ui.rotationPt1.currentText())
                        r2 = int(dlg.ui.rotationPt2.currentText())
                        rotation = ("TwoPoint", r1, r2)
                    else:
                        rotation = None
                else:
                    return
            else:
                ref = 0
                rotation = None
            try:
                shifts, angles = algo.alignImages(self._data, d, ref, rotation)
                self.undo_stack.push(AlignImages(self._data, shifts, angles))
            except algo.AlgoException as ex:
                showException(self, "Error while aligning images", ex)

    def sceneSizeChanged(self):
        previous_rect = self._previousScene.real_scene_rect
        current_rect = self._currentScene.real_scene_rect
        rect = previous_rect | current_rect
        self._previousScene.setSceneRect(rect)
        self._currentScene.setSceneRect(rect)

    @pyqtSignature("")
    def on_actionEdit_timing_triggered(self):
        data = self._data
        dlg = TimeEditDlg(data.images_name, data.images_time,
                          [data.image_path(n) for n in data.images_name], self)
        self.current_dlg = dlg
        if dlg.exec_() == QDialog.Accepted:
            self.undo_stack.push(ChangeTiming(data, [t for n, t in dlg.model]))
        del self.current_dlg

    @pyqtSignature("")
    def on_actionEdit_scales_triggered(self):
        data = self._data
        dlg = EditResDlg(data.images_name, data.images_scale,
                         [data.image_path(n) for n in data.images_name], self)
        self.current_dlg = dlg
        if dlg.exec_() == QDialog.Accepted:
            self.undo_stack.push(
                ChangeScales(data, [sc for n, sc in dlg.model]))
        del self.current_dlg

    @pyqtSignature("")
    def on_actionCompute_growth_triggered(self):
        data = self._data
        dlg = GrowthComputationDlg(data, self)
        self.current_dlg = dlg
        dlg.exec_()
        del self.current_dlg

    @pyqtSignature("")
    def on_actionPlot_growth_triggered(self):
        data = self._data
        dlg = PlottingDlg(data, self)
        self.current_dlg = dlg
        dlg.exec_()
        del self.current_dlg

    @pyqtSignature("")
    def on_actionClean_cells_triggered(self):
        self.undo_stack.push(CleanCells(self._data))

    @pyqtSignature("")
    def on_actionGotoCell_triggered(self):
        cells = [str(cid) for cid in self._data.cells]
        selected, ok = QInputDialog.getItem(self, "Goto cell",
                                            "Select the cell to go to", cells,
                                            0)
        if ok:
            cid = int(selected)
            self.ui.actionAdd_cell.setChecked(True)
            data = self._data
            if cid not in data.cells:
                return
            ls = data.cells_lifespan[cid]
            prev_pos = self._previousScene.current_data._current_index
            cur_pos = self._currentScene.current_data._current_index
            full_poly = data.cells[cid]
            poly = [pid for pid in full_poly if pid in data[prev_pos]]
            #log_debug("Cell %d on time %d: %s" % (cid, prev_pos, poly))
            if prev_pos < ls.start or prev_pos >= ls.end or not poly:
                for i in range(*ls.slice().indices(len(data))):
                    poly = [pid for pid in full_poly if pid in data[i]]
                    if poly:
                        log_debug("Found cell %d on image %d with polygon %s" %
                                  (cid, i, poly))
                        new_prev_pos = i
                        break
                else:
                    log_debug("Cell %d found nowhere in range %s!!!" %
                              (cid, ls.slice()))
            else:
                new_prev_pos = prev_pos
            new_cur_pos = min(max(cur_pos + new_prev_pos - prev_pos, 0),
                              len(data))
            self.ui.previousState.setCurrentIndex(new_prev_pos)
            self.ui.currentState.setCurrentIndex(new_cur_pos)
            self._previousScene.current_cell = cid
            self._currentScene.current_cell = cid
            prev_data = self._previousScene.current_data
            poly = data.cells[cid]
            prev_poly = QPolygonF(
                [prev_data[ptid] for ptid in poly if ptid in prev_data])
            prev_bbox = prev_poly.boundingRect()
            log_debug("Previous bounding box = %dx%d+%d+%d" %
                      (prev_bbox.width(), prev_bbox.height(), prev_bbox.left(),
                       prev_bbox.top()))
            self.ui.previousData.ensureVisible(prev_bbox)

    @pyqtSignature("")
    def on_actionGotoPoint_triggered(self):
        data = self._data
        points = [str(pid) for pid in data.cell_points]
        selected, ok = QInputDialog.getItem(self, "Goto point",
                                            "Select the point to go to",
                                            points, 0)
        if ok:
            pid = int(selected)
            self.ui.action_Move_point.setChecked(True)
            if pid not in data.cell_points:
                return
            prev_pos = self._previousScene.current_data._current_index
            cur_pos = self._currentScene.current_data._current_index
            prev_data = self._previousScene.current_data
            if not pid in prev_data:
                closest = -1
                best_dist = len(data) + 1
                for img_data in data:
                    if pid in img_data:
                        dist = abs(img_data._current_index - prev_pos)
                        if dist < best_dist:
                            best_dist = dist
                            closest = img_data._current_index
                new_prev_pos = closest
            else:
                new_prev_pos = prev_pos
            new_cur_pos = min(max(cur_pos + new_prev_pos - prev_pos, 0),
                              len(data))
            self.ui.previousState.setCurrentIndex(new_prev_pos)
            self.ui.currentState.setCurrentIndex(new_cur_pos)
            self._previousScene.setSelectedIds([pid])
            self._currentScene.setSelectedIds([pid])
            self.ui.previousData.centerOn(
                self._previousScene.current_data[pid])
예제 #19
0
파일: scene.py 프로젝트: 675801717/orange3
class CanvasScene(QGraphicsScene):
    """
    A Graphics Scene for displaying an :class:`~.scheme.Scheme` instance.
    """

    #: Signal emitted when a :class:`NodeItem` has been added to the scene.
    node_item_added = Signal(object)

    #: Signal emitted when a :class:`NodeItem` has been removed from the
    #: scene.
    node_item_removed = Signal(object)

    #: Signal emitted when a new :class:`LinkItem` has been added to the
    #: scene.
    link_item_added = Signal(object)

    #: Signal emitted when a :class:`LinkItem` has been removed.
    link_item_removed = Signal(object)

    #: Signal emitted when a :class:`Annotation` item has been added.
    annotation_added = Signal(object)

    #: Signal emitted when a :class:`Annotation` item has been removed.
    annotation_removed = Signal(object)

    #: Signal emitted when the position of a :class:`NodeItem` has changed.
    node_item_position_changed = Signal(object, QPointF)

    #: Signal emitted when an :class:`NodeItem` has been double clicked.
    node_item_double_clicked = Signal(object)

    #: An node item has been activated (clicked)
    node_item_activated = Signal(object)

    #: An node item has been hovered
    node_item_hovered = Signal(object)

    #: Link item has been hovered
    link_item_hovered = Signal(object)

    def __init__(self, *args, **kwargs):
        QGraphicsScene.__init__(self, *args, **kwargs)

        self.scheme = None
        self.registry = None

        # All node items
        self.__node_items = []
        # Mapping from SchemeNodes to canvas items
        self.__item_for_node = {}
        # All link items
        self.__link_items = []
        # Mapping from SchemeLinks to canvas items.
        self.__item_for_link = {}

        # All annotation items
        self.__annotation_items = []
        # Mapping from SchemeAnnotations to canvas items.
        self.__item_for_annotation = {}

        # Is the scene editable
        self.editable = True

        # Anchor Layout
        self.__anchor_layout = AnchorLayout()
        self.addItem(self.__anchor_layout)

        self.__channel_names_visible = True
        self.__node_animation_enabled = True

        self.user_interaction_handler = None

        self.activated_mapper = QSignalMapper(self)
        self.activated_mapper.mapped[QObject].connect(
            lambda node: self.node_item_activated.emit(node)
        )
        self.hovered_mapper = QSignalMapper(self)
        self.hovered_mapper.mapped[QObject].connect(
            lambda node: self.node_item_hovered.emit(node)
        )
        self.position_change_mapper = QSignalMapper(self)
        self.position_change_mapper.mapped[QObject].connect(
            self._on_position_change
        )
        log.info("'%s' intitialized." % self)

    def clear_scene(self):
        """
        Clear (reset) the scene.
        """
        if self.scheme is not None:
            self.scheme.node_added.disconnect(self.add_node)
            self.scheme.node_removed.disconnect(self.remove_node)

            self.scheme.link_added.disconnect(self.add_link)
            self.scheme.link_removed.disconnect(self.remove_link)

            self.scheme.annotation_added.disconnect(self.add_annotation)
            self.scheme.annotation_removed.disconnect(self.remove_annotation)

            self.scheme.node_state_changed.disconnect(
                self.on_widget_state_change
            )
            self.scheme.channel_state_changed.disconnect(
                self.on_link_state_change
            )

            # Remove all items to make sure all signals from scheme items
            # to canvas items are disconnected.

            for annot in self.scheme.annotations:
                if annot in self.__item_for_annotation:
                    self.remove_annotation(annot)

            for link in self.scheme.links:
                if link in self.__item_for_link:
                    self.remove_link(link)

            for node in self.scheme.nodes:
                if node in self.__item_for_node:
                    self.remove_node(node)

        self.scheme = None
        self.__node_items = []
        self.__item_for_node = {}
        self.__link_items = []
        self.__item_for_link = {}
        self.__annotation_items = []
        self.__item_for_annotation = {}

        self.__anchor_layout.deleteLater()

        self.user_interaction_handler = None

        self.clear()
        log.info("'%s' cleared." % self)

    def set_scheme(self, scheme):
        """
        Set the scheme to display. Populates the scene with nodes and links
        already in the scheme. Any further change to the scheme will be
        reflected in the scene.

        Parameters
        ----------
        scheme : :class:`~.scheme.Scheme`

        """
        if self.scheme is not None:
            # Clear the old scheme
            self.clear_scene()

        log.info("Setting scheme '%s' on '%s'" % (scheme, self))

        self.scheme = scheme
        if self.scheme is not None:
            self.scheme.node_added.connect(self.add_node)
            self.scheme.node_removed.connect(self.remove_node)

            self.scheme.link_added.connect(self.add_link)
            self.scheme.link_removed.connect(self.remove_link)

            self.scheme.annotation_added.connect(self.add_annotation)
            self.scheme.annotation_removed.connect(self.remove_annotation)

            self.scheme.node_state_changed.connect(
                self.on_widget_state_change
            )
            self.scheme.channel_state_changed.connect(
                self.on_link_state_change
            )

            self.scheme.topology_changed.connect(self.on_scheme_change)

        for node in scheme.nodes:
            self.add_node(node)

        for link in scheme.links:
            self.add_link(link)

        for annot in scheme.annotations:
            self.add_annotation(annot)

    def set_registry(self, registry):
        """
        Set the widget registry.
        """
        # TODO: Remove/Deprecate. Is used only to get the category/background
        # color. That should be part of the SchemeNode/WidgetDescription.
        log.info("Setting registry '%s on '%s'." % (registry, self))
        self.registry = registry

    def set_anchor_layout(self, layout):
        """
        Set an :class:`~.layout.AnchorLayout`
        """
        if self.__anchor_layout != layout:
            if self.__anchor_layout:
                self.__anchor_layout.deleteLater()
                self.__anchor_layout = None

            self.__anchor_layout = layout

    def anchor_layout(self):
        """
        Return the anchor layout instance.
        """
        return self.__anchor_layout

    def set_channel_names_visible(self, visible):
        """
        Set the channel names visibility.
        """
        self.__channel_names_visible = visible
        for link in self.__link_items:
            link.setChannelNamesVisible(visible)

    def channel_names_visible(self):
        """
        Return the channel names visibility state.
        """
        return self.__channel_names_visible

    def set_node_animation_enabled(self, enabled):
        """
        Set node animation enabled state.
        """
        if self.__node_animation_enabled != enabled:
            self.__node_animation_enabled = enabled

            for node in self.__node_items:
                node.setAnimationEnabled(enabled)

    def add_node_item(self, item):
        """
        Add a :class:`.NodeItem` instance to the scene.
        """
        if item in self.__node_items:
            raise ValueError("%r is already in the scene." % item)

        if item.pos().isNull():
            if self.__node_items:
                pos = self.__node_items[-1].pos() + QPointF(150, 0)
            else:
                pos = QPointF(150, 150)

            item.setPos(pos)

        item.setFont(self.font())

        # Set signal mappings
        self.activated_mapper.setMapping(item, item)
        item.activated.connect(self.activated_mapper.map)

        self.hovered_mapper.setMapping(item, item)
        item.hovered.connect(self.hovered_mapper.map)

        self.position_change_mapper.setMapping(item, item)
        item.positionChanged.connect(self.position_change_mapper.map)

        self.addItem(item)

        self.__node_items.append(item)

        self.node_item_added.emit(item)

        log.info("Added item '%s' to '%s'" % (item, self))
        return item

    def add_node(self, node):
        """
        Add and return a default constructed :class:`.NodeItem` for a
        :class:`SchemeNode` instance `node`. If the `node` is already in
        the scene do nothing and just return its item.

        """
        if node in self.__item_for_node:
            # Already added
            return self.__item_for_node[node]

        item = self.new_node_item(node.description)

        if node.position:
            pos = QPointF(*node.position)
            item.setPos(pos)

        item.setTitle(node.title)
        item.setProcessingState(node.processing_state)
        item.setProgress(node.progress)

        for message in node.state_messages():
            item.setStateMessage(message)

        item.setStatusMessage(node.status_message())

        self.__item_for_node[node] = item

        node.position_changed.connect(self.__on_node_pos_changed)
        node.title_changed.connect(item.setTitle)
        node.progress_changed.connect(item.setProgress)
        node.processing_state_changed.connect(item.setProcessingState)
        node.state_message_changed.connect(item.setStateMessage)
        node.status_message_changed.connect(item.setStatusMessage)

        return self.add_node_item(item)

    def new_node_item(self, widget_desc, category_desc=None):
        """
        Construct an new :class:`.NodeItem` from a `WidgetDescription`.
        Optionally also set `CategoryDescription`.

        """
        item = items.NodeItem()
        item.setWidgetDescription(widget_desc)

        if category_desc is None and self.registry and widget_desc.category:
            category_desc = self.registry.category(widget_desc.category)

        if category_desc is None and self.registry is not None:
            try:
                category_desc = self.registry.category(widget_desc.category)
            except KeyError:
                pass

        if category_desc is not None:
            item.setWidgetCategory(category_desc)

        item.setAnimationEnabled(self.__node_animation_enabled)
        return item

    def remove_node_item(self, item):
        """
        Remove `item` (:class:`.NodeItem`) from the scene.
        """
        self.activated_mapper.removeMappings(item)
        self.hovered_mapper.removeMappings(item)
        self.position_change_mapper.removeMappings(item)

        item.hide()
        self.removeItem(item)
        self.__node_items.remove(item)

        self.node_item_removed.emit(item)

        log.info("Removed item '%s' from '%s'" % (item, self))

    def remove_node(self, node):
        """
        Remove the :class:`.NodeItem` instance that was previously
        constructed for a :class:`SchemeNode` `node` using the `add_node`
        method.

        """
        item = self.__item_for_node.pop(node)

        node.position_changed.disconnect(self.__on_node_pos_changed)
        node.title_changed.disconnect(item.setTitle)
        node.progress_changed.disconnect(item.setProgress)
        node.processing_state_changed.disconnect(item.setProcessingState)
        node.state_message_changed.disconnect(item.setStateMessage)

        self.remove_node_item(item)

    def node_items(self):
        """
        Return all :class:`.NodeItem` instances in the scene.
        """
        return list(self.__node_items)

    def add_link_item(self, item):
        """
        Add a link (:class:`.LinkItem`) to the scene.
        """
        if item.scene() is not self:
            self.addItem(item)

        item.setFont(self.font())
        self.__link_items.append(item)

        self.link_item_added.emit(item)

        log.info("Added link %r -> %r to '%s'" % \
                 (item.sourceItem.title(), item.sinkItem.title(), self))

        self.__anchor_layout.invalidateLink(item)

        return item

    def add_link(self, scheme_link):
        """
        Create and add a :class:`.LinkItem` instance for a
        :class:`SchemeLink` instance. If the link is already in the scene
        do nothing and just return its :class:`.LinkItem`.

        """
        if scheme_link in self.__item_for_link:
            return self.__item_for_link[scheme_link]

        source = self.__item_for_node[scheme_link.source_node]
        sink = self.__item_for_node[scheme_link.sink_node]

        item = self.new_link_item(source, scheme_link.source_channel,
                                  sink, scheme_link.sink_channel)

        item.setEnabled(scheme_link.enabled)
        scheme_link.enabled_changed.connect(item.setEnabled)

        if scheme_link.is_dynamic():
            item.setDynamic(True)
            item.setDynamicEnabled(scheme_link.dynamic_enabled)
            scheme_link.dynamic_enabled_changed.connect(item.setDynamicEnabled)

        self.add_link_item(item)
        self.__item_for_link[scheme_link] = item
        return item

    def new_link_item(self, source_item, source_channel,
                      sink_item, sink_channel):
        """
        Construct and return a new :class:`.LinkItem`
        """
        item = items.LinkItem()
        item.setSourceItem(source_item)
        item.setSinkItem(sink_item)

        def channel_name(channel):
            if isinstance(channel, str):
                return channel
            else:
                return channel.name

        source_name = channel_name(source_channel)
        sink_name = channel_name(sink_channel)

        fmt = "<b>{0}</b>&nbsp; \u2192 &nbsp;<b>{1}</b>"
        item.setToolTip(
            fmt.format(escape(source_name),
                       escape(sink_name))
        )

        item.setSourceName(source_name)
        item.setSinkName(sink_name)
        item.setChannelNamesVisible(self.__channel_names_visible)

        return item

    def remove_link_item(self, item):
        """
        Remove a link (:class:`.LinkItem`) from the scene.
        """
        # Invalidate the anchor layout.
        self.__anchor_layout.invalidateAnchorItem(
            item.sourceItem.outputAnchorItem
        )
        self.__anchor_layout.invalidateAnchorItem(
            item.sinkItem.inputAnchorItem
        )

        self.__link_items.remove(item)

        # Remove the anchor points.
        item.removeLink()
        self.removeItem(item)

        self.link_item_removed.emit(item)

        log.info("Removed link '%s' from '%s'" % (item, self))

        return item

    def remove_link(self, scheme_link):
        """
        Remove a :class:`.LinkItem` instance that was previously constructed
        for a :class:`SchemeLink` instance `link` using the `add_link` method.

        """
        item = self.__item_for_link.pop(scheme_link)
        scheme_link.enabled_changed.disconnect(item.setEnabled)

        if scheme_link.is_dynamic():
            scheme_link.dynamic_enabled_changed.disconnect(
                item.setDynamicEnabled
            )

        self.remove_link_item(item)

    def link_items(self):
        """
        Return all :class:`.LinkItem`\s in the scene.
        """
        return list(self.__link_items)

    def add_annotation_item(self, annotation):
        """
        Add an :class:`.Annotation` item to the scene.
        """
        self.__annotation_items.append(annotation)
        self.addItem(annotation)
        self.annotation_added.emit(annotation)
        return annotation

    def add_annotation(self, scheme_annot):
        """
        Create a new item for :class:`SchemeAnnotation` and add it
        to the scene. If the `scheme_annot` is already in the scene do
        nothing and just return its item.

        """
        if scheme_annot in self.__item_for_annotation:
            # Already added
            return self.__item_for_annotation[scheme_annot]

        if isinstance(scheme_annot, scheme.SchemeTextAnnotation):
            item = items.TextAnnotation()
            item.setPlainText(scheme_annot.text)
            x, y, w, h = scheme_annot.rect
            item.setPos(x, y)
            item.resize(w, h)
            item.setTextInteractionFlags(Qt.TextEditorInteraction)

            font = font_from_dict(scheme_annot.font, item.font())
            item.setFont(font)
            scheme_annot.text_changed.connect(item.setPlainText)

        elif isinstance(scheme_annot, scheme.SchemeArrowAnnotation):
            item = items.ArrowAnnotation()
            start, end = scheme_annot.start_pos, scheme_annot.end_pos
            item.setLine(QLineF(QPointF(*start), QPointF(*end)))
            item.setColor(QColor(scheme_annot.color))

        scheme_annot.geometry_changed.connect(
            self.__on_scheme_annot_geometry_change
        )

        self.add_annotation_item(item)
        self.__item_for_annotation[scheme_annot] = item

        return item

    def remove_annotation_item(self, annotation):
        """
        Remove an :class:`.Annotation` instance from the scene.

        """
        self.__annotation_items.remove(annotation)
        self.removeItem(annotation)
        self.annotation_removed.emit(annotation)

    def remove_annotation(self, scheme_annotation):
        """
        Remove an :class:`.Annotation` instance that was previously added
        using :func:`add_anotation`.

        """
        item = self.__item_for_annotation.pop(scheme_annotation)

        scheme_annotation.geometry_changed.disconnect(
            self.__on_scheme_annot_geometry_change
        )

        if isinstance(scheme_annotation, scheme.SchemeTextAnnotation):
            scheme_annotation.text_changed.disconnect(
                item.setPlainText
            )

        self.remove_annotation_item(item)

    def annotation_items(self):
        """
        Return all :class:`.Annotation` items in the scene.
        """
        return self.__annotation_items

    def item_for_annotation(self, scheme_annotation):
        return self.__item_for_annotation[scheme_annotation]

    def annotation_for_item(self, item):
        rev = dict(reversed(item) \
                   for item in self.__item_for_annotation.items())
        return rev[item]

    def commit_scheme_node(self, node):
        """
        Commit the `node` into the scheme.
        """
        if not self.editable:
            raise Exception("Scheme not editable.")

        if node not in self.__item_for_node:
            raise ValueError("No 'NodeItem' for node.")

        item = self.__item_for_node[node]

        try:
            self.scheme.add_node(node)
        except Exception:
            log.error("An error occurred while committing node '%s'",
                      node, exc_info=True)
            # Cleanup (remove the node item)
            self.remove_node_item(item)
            raise

        log.info("Commited node '%s' from '%s' to '%s'" % \
                 (node, self, self.scheme))

    def commit_scheme_link(self, link):
        """
        Commit a scheme link.
        """
        if not self.editable:
            raise Exception("Scheme not editable")

        if link not in self.__item_for_link:
            raise ValueError("No 'LinkItem' for link.")

        self.scheme.add_link(link)
        log.info("Commited link '%s' from '%s' to '%s'" % \
                 (link, self, self.scheme))

    def node_for_item(self, item):
        """
        Return the `SchemeNode` for the `item`.
        """
        rev = dict([(v, k) for k, v in self.__item_for_node.items()])
        return rev[item]

    def item_for_node(self, node):
        """
        Return the :class:`NodeItem` instance for a :class:`SchemeNode`.
        """
        return self.__item_for_node[node]

    def link_for_item(self, item):
        """
        Return the `SchemeLink for `item` (:class:`LinkItem`).
        """
        rev = dict([(v, k) for k, v in self.__item_for_link.items()])
        return rev[item]

    def item_for_link(self, link):
        """
        Return the :class:`LinkItem` for a :class:`SchemeLink`
        """
        return self.__item_for_link[link]

    def selected_node_items(self):
        """
        Return the selected :class:`NodeItem`'s.
        """
        return [item for item in self.__node_items if item.isSelected()]

    def selected_annotation_items(self):
        """
        Return the selected :class:`Annotation`'s
        """
        return [item for item in self.__annotation_items if item.isSelected()]

    def node_links(self, node_item):
        """
        Return all links from the `node_item` (:class:`NodeItem`).
        """
        return self.node_output_links(node_item) + \
               self.node_input_links(node_item)

    def node_output_links(self, node_item):
        """
        Return a list of all output links from `node_item`.
        """
        return [link for link in self.__link_items
                if link.sourceItem == node_item]

    def node_input_links(self, node_item):
        """
        Return a list of all input links for `node_item`.
        """
        return [link for link in self.__link_items
                if link.sinkItem == node_item]

    def neighbor_nodes(self, node_item):
        """
        Return a list of `node_item`'s (class:`NodeItem`) neighbor nodes.
        """
        neighbors = list(map(attrgetter("sourceItem"),
                             self.node_input_links(node_item)))

        neighbors.extend(map(attrgetter("sinkItem"),
                             self.node_output_links(node_item)))
        return neighbors

    def on_widget_state_change(self, widget, state):
        pass

    def on_link_state_change(self, link, state):
        pass

    def on_scheme_change(self, ):
        pass

    def _on_position_change(self, item):
        # Invalidate the anchor point layout and schedule a layout.
        self.__anchor_layout.invalidateNode(item)

        self.node_item_position_changed.emit(item, item.pos())

    def __on_node_pos_changed(self, pos):
        node = self.sender()
        item = self.__item_for_node[node]
        item.setPos(*pos)

    def __on_scheme_annot_geometry_change(self):
        annot = self.sender()
        item = self.__item_for_annotation[annot]
        if isinstance(annot, scheme.SchemeTextAnnotation):
            item.setGeometry(QRectF(*annot.rect))
        elif isinstance(annot, scheme.SchemeArrowAnnotation):
            p1 = item.mapFromScene(QPointF(*annot.start_pos))
            p2 = item.mapFromScene(QPointF(*annot.end_pos))
            item.setLine(QLineF(p1, p2))
        else:
            pass

    def item_at(self, pos, type_or_tuple=None, buttons=0):
        """Return the item at `pos` that is an instance of the specified
        type (`type_or_tuple`). If `buttons` (`Qt.MouseButtons`) is given
        only return the item if it is the top level item that would
        accept any of the buttons (`QGraphicsItem.acceptedMouseButtons`).

        """
        rect = QRectF(pos, QSizeF(1, 1))
        items = self.items(rect)

        if buttons:
            items = itertools.dropwhile(
                lambda item: not item.acceptedMouseButtons() & buttons,
                items
            )
            items = list(items)[:1]

        if type_or_tuple:
            items = [i for i in items if isinstance(i, type_or_tuple)]

        return items[0] if items else None

    if PYQT_VERSION < 0x40900:
        # For QGraphicsObject subclasses items, itemAt ... return a
        # QGraphicsItem wrapper instance and not the actual class instance.
        def itemAt(self, *args, **kwargs):
            item = QGraphicsScene.itemAt(self, *args, **kwargs)
            return toGraphicsObjectIfPossible(item)

        def items(self, *args, **kwargs):
            items = QGraphicsScene.items(self, *args, **kwargs)
            return list(map(toGraphicsObjectIfPossible, items))

        def selectedItems(self, *args, **kwargs):
            return list(map(toGraphicsObjectIfPossible,
                       QGraphicsScene.selectedItems(self, *args, **kwargs)))

        def collidingItems(self, *args, **kwargs):
            return list(map(toGraphicsObjectIfPossible,
                       QGraphicsScene.collidingItems(self, *args, **kwargs)))

        def focusItem(self, *args, **kwargs):
            item = QGraphicsScene.focusItem(self, *args, **kwargs)
            return toGraphicsObjectIfPossible(item)

        def mouseGrabberItem(self, *args, **kwargs):
            item = QGraphicsScene.mouseGrabberItem(self, *args, **kwargs)
            return toGraphicsObjectIfPossible(item)

    def mousePressEvent(self, event):
        if self.user_interaction_handler and \
                self.user_interaction_handler.mousePressEvent(event):
            return

        # Right (context) click on the node item. If the widget is not
        # in the current selection then select the widget (only the widget).
        # Else simply return and let customContextMenuReqested signal
        # handle it
        shape_item = self.item_at(event.scenePos(), items.NodeItem)
        if shape_item and event.button() == Qt.RightButton and \
                shape_item.flags() & QGraphicsItem.ItemIsSelectable:
            if not shape_item.isSelected():
                self.clearSelection()
                shape_item.setSelected(True)

        return QGraphicsScene.mousePressEvent(self, event)

    def mouseMoveEvent(self, event):
        if self.user_interaction_handler and \
                self.user_interaction_handler.mouseMoveEvent(event):
            return

        return QGraphicsScene.mouseMoveEvent(self, event)

    def mouseReleaseEvent(self, event):
        if self.user_interaction_handler and \
                self.user_interaction_handler.mouseReleaseEvent(event):
            return
        return QGraphicsScene.mouseReleaseEvent(self, event)

    def mouseDoubleClickEvent(self, event):
        if self.user_interaction_handler and \
                self.user_interaction_handler.mouseDoubleClickEvent(event):
            return

        return QGraphicsScene.mouseDoubleClickEvent(self, event)

    def keyPressEvent(self, event):
        if self.user_interaction_handler and \
                self.user_interaction_handler.keyPressEvent(event):
            return
        return QGraphicsScene.keyPressEvent(self, event)

    def keyReleaseEvent(self, event):
        if self.user_interaction_handler and \
                self.user_interaction_handler.keyReleaseEvent(event):
            return
        return QGraphicsScene.keyReleaseEvent(self, event)

    def set_user_interaction_handler(self, handler):
        if self.user_interaction_handler and \
                not self.user_interaction_handler.isFinished():
            self.user_interaction_handler.cancel()

        log.info("Setting interaction '%s' to '%s'" % (handler, self))

        self.user_interaction_handler = handler
        if handler:
            handler.start()

    def event(self, event):
        # TODO: change the base class of Node/LinkItem to QGraphicsWidget.
        # It already handles font changes.
        if event.type() == QEvent.FontChange:
            self.__update_font()

        return QGraphicsScene.event(self, event)

    def __update_font(self):
        font = self.font()
        for item in self.__node_items + self.__link_items:
            item.setFont(font)

    def __str__(self):
        return "%s(objectName=%r, ...)" % \
                (type(self).__name__, str(self.objectName()))
class MainApp(QDockWidget, QMainWindow, Ui_MainApp):
    # signals
    goBack = pyqtSignal()
    searchOpsubByName = pyqtSignal(str)
    enableSearch = pyqtSignal(bool)
    refreshLegend = pyqtSignal(QgsMapLayer)
    ogrDatasourceLoaded = pyqtSignal(bool)

    class VfkLayer(object):
        Par = 0
        Bud = 1

    def __init__(self, iface):
        QDockWidget.__init__(self, iface.mainWindow())
        self.setupUi(self)
        self.iface = iface

        # variables
        self.__mLastVfkFile = []
        self.__mOgrDataSource = None
        self.__mDataSourceName = ''
        self.__fileName = []
        self.__mLoadedLayers = {}
        self.__mDefaultPalette = self.vfkFileLineEdit.palette()

        # new lineEdits variables
        self.lineEditsCount = 1

        self.__browseButtons = {}
        self.__vfkLineEdits = {}

        # data will be load from source according to checked radiobox
        self.__source_for_data = 'file'

        # apply changes into main database
        self.__databases = {}
        # self.pb_applyChanges.setEnabled(False)
        self.changes_instance = ApplyChanges()

        # Connect ui with functions
        self.__createToolbarsAndConnect()

        # check GDAL version
        self.__gdal_version = int(gdal.VersionInfo())

        if self.__gdal_version < 2020000:
            self.actionZpracujZmeny.setEnabled(False)
            self.pb_nextFile.setEnabled(False)
            self.pb_nextFile.setToolTip(
                u'Není možné načíst více souborů, verze GDAL je nižší než 2.2.0.')
            self.actionZpracujZmeny.setToolTip(u'Zpracování změn není povoleno, verze GDAL je nižší než 2.2.0.')
            self.groupBox.setEnabled(False)

        self.loadVfkButton.setDisabled(True)

        self.searchFormMainControls = SearchFormController.MainControls()
        self.searchFormMainControls.formCombobox = self.searchCombo
        self.searchFormMainControls.searchForms = self.searchForms
        self.searchFormMainControls.searchButton = self.searchButton

        self.searchForms = SearchFormController.SearchForms()
        self.searchForms.vlastnici = self.vlastniciSearchForm
        self.searchForms.parcely = self.parcelySearchForm
        self.searchForms.budovy = self.budovySearchForm
        self.searchForms.jednotky = self.jednotkySearchForm

        # search form controller
        self.__mSearchController = SearchFormController(
            self.searchFormMainControls, self.searchForms, self)

        self.connect(self.__mSearchController, SIGNAL(
            "actionTriggered(QUrl)"), self.vfkBrowser.processAction)
        self.connect(
            self, SIGNAL("enableSearch"), self.searchButton.setEnabled)

        self.connect(self.vfkBrowser, SIGNAL("showParcely"), self.showParInMap)
        self.connect(self.vfkBrowser, SIGNAL("showBudovy"), self.showBudInMap)

        # connect lineEdits and returnPressed action
        self.connect(self.vfkFileLineEdit, SIGNAL(
            "returnPressed()"), self.loadVfkButton_clicked)
        self.connect(self.vlastniciSearchForm.ui.jmenoLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)
        self.connect(self.vlastniciSearchForm.ui.rcIcoLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)
        self.connect(self.vlastniciSearchForm.ui.lvVlastniciLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)

        self.connect(self.parcelySearchForm.ui.parCisloLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)
        self.connect(self.parcelySearchForm.ui.lvParcelyLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)

        self.connect(self.budovySearchForm.ui.cisloDomovniLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)
        self.connect(self.budovySearchForm.ui.naParceleLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)
        self.connect(self.budovySearchForm.ui.lvBudovyLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)

        self.connect(self.jednotkySearchForm.ui.mCisloJednotkyLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)
        self.connect(self.jednotkySearchForm.ui.mCisloDomovniLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)
        self.connect(self.jednotkySearchForm.ui.mNaParceleLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)
        self.connect(self.jednotkySearchForm.ui.mLvJednotkyLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)

        self.vfkBrowser.showHelpPage()

        self.settings = QSettings("CTU", "VFK plugin")
        self.full_data = True

    def browseButton_clicked(self, browseButton_id=1):
        """
        :param browseButton_id: ID of clicked browse button.
        :return:
        """
        title = u'Načti soubor VFK'
        sender = u'{}-lastUserFilePath'.format(self.sender().objectName())
        lastUsedDir = self.settings.value(sender, '')

        if self.__source_for_data == 'file':
            ext = '*.vfk'
            if self.__gdal_version >= 2020000:
                ext += ' *.db'
            loaded_file = QFileDialog.getOpenFileName(
                self, title, lastUsedDir, u'Soubory podporované ovladačem VFK GDAL ({})'.format(ext))

            if not loaded_file:
                return
            else:
                self.__fileName.append(loaded_file)
                if browseButton_id == 1:
                    self.vfkFileLineEdit.setText(self.__fileName[0])
                    #TODO:vyresit nacitani vice souboru
                else:
                    self.__vfkLineEdits['vfkLineEdit_{}'.format(len(self.__vfkLineEdits))].setText(
                        self.__fileName[browseButton_id - 1])
                self.settings.setValue(sender, os.path.dirname(loaded_file))
        elif self.__source_for_data == 'directory':
            loaded_file = QFileDialog.getExistingDirectory(self, u"Vyberte adresář s daty VFK")
            if not loaded_file:
                return
            else:
                self.__fileName = []
                self.__fileName.append(loaded_file)
                self.vfkFileLineEdit.setText(self.__fileName[0])
        else:
            qDebug('(VFK) Not valid source')

        self.loadVfkButton.setEnabled(True)

    def browserGoBack(self):
        self.vfkBrowser.goBack()

    def browserGoForward(self):
        self.vfkBrowser.goForth()

    def selectParInMap(self):
        self.showInMap(self.vfkBrowser.currentParIds(), "PAR")

    def selectBudInMap(self):
        self.showInMap(self.vfkBrowser.currentBudIds(), "BUD")

    def latexExport(self):
        fileName = QFileDialog.getSaveFileName(
            self, u"Jméno exportovaného souboru", ".tex", "LaTeX (*.tex)")
        if fileName:
            export_succesfull = self.vfkBrowser.exportDocument(
                self.vfkBrowser.currentUrl(), fileName, self.vfkBrowser.ExportFormat.Latex)
            if export_succesfull:
                self.succesfullExport("LaTeX")

    def htmlExport(self):
        fileName = QFileDialog.getSaveFileName(
            self, u"Jméno exportovaného souboru", ".html", "HTML (*.html)")
        if fileName:
            export_succesfull = self.vfkBrowser.exportDocument(
                self.vfkBrowser.currentUrl(), fileName, self.vfkBrowser.ExportFormat.Html)
            if export_succesfull:
                self.succesfullExport("HTML")

    def setSelectionChangedConnected(self, connected):
        """

        :type connected: bool
        :return:
        """
        for layer in self.__mLoadedLayers:
            id = self.__mLoadedLayers[layer]
            vectorLayer = QgsMapLayerRegistry.instance().mapLayer(id)

            if connected:
                self.connect(
                    vectorLayer, SIGNAL("selectionChanged()"), self.showInfoAboutSelection)
            else:
                self.disconnect(
                    vectorLayer, SIGNAL("selectionChanged()"), self.showInfoAboutSelection)

    def showInMap(self, ids, layerName):
        """

        :type ids: list
        :type layerName: str
        :return:
        """
        if layerName in self.__mLoadedLayers:
            id = self.__mLoadedLayers[layerName]
            vectorLayer = QgsMapLayerRegistry.instance().mapLayer(id)
            searchString = "ID IN ({})".format(", ".join(ids))
            error = ''
            fIds = self.__search(vectorLayer, searchString, error)
            if error:
                qDebug('\n (VFK) ERROR in showInMap: {}'.format(error))
                return
            else:
                vectorLayer.setSelectedFeatures(fIds)

    def __search(self, layer, searchString, error):
        """

        :type layer: QgsVectorLayer
        :type searchString: str
        :type error: str
        :return:
        """
        # parse search string and build parsed tree
        search = QgsExpression(searchString)
        rect = QgsRectangle()
        fIds = []

        if search.hasParserError():
            error += "Parsing error:" + search.parserErrorString()
            return fIds
        if not search.prepare(layer.pendingFields()):
            error + "Evaluation error:" + search.evalErrorString()

        layer.select(rect, False)
        fit = QgsFeatureIterator(layer.getFeatures())
        f = QgsFeature()

        while fit.nextFeature(f):

            if search.evaluate(f):
                fIds.append(f.id())
            # check if there were errors during evaluating
            if search.hasEvalError():
                qDebug('\n (VFK) Evaluate error: {}'.format(error))
                break

        return fIds

    def loadVfkButton_clicked(self):
        """
        After click method starts loading all inserted files
        """
        # check the source of data
        if self.__source_for_data == 'directory':
            dir_path = self.__fileName[0]
            self.__fileName = self.__findVFKFilesInDirectory(dir_path)

        # check if first file is amendment
        amendment_file = self.__checkIfAmendmentFile(self.__fileName[0])

        # prepare name for database
        if amendment_file:
            new_database_name = '{}_zmeny.db'.format(os.path.basename(self.__fileName[0]).split('.')[0])
        else:
            new_database_name = '{}_stav.db'.format(os.path.basename(self.__fileName[0]).split('.')[0])

        os.environ['OGR_VFK_DB_NAME'] = os.path.normpath(os.path.join(
            os.path.dirname(os.path.dirname(self.__fileName[0])), new_database_name))
        gdal.SetConfigOption('OGR_VFK_DB_NAME', str(os.environ['OGR_VFK_DB_NAME']))
        self.__mDataSourceName = self.__fileName[0]     # os.environ['OGR_VFK_DB_NAME']

        QgsApplication.processEvents()

        self.importThread = OpenThread(self.__fileName)
        self.importThread.working.connect(self.runLoadingLayer)
        if not self.importThread.isRunning():
            self.importThread.start()

    def runLoadingLayer(self, fileName):
        """

        :return:
        """
        if fileName not in self.__mLastVfkFile:

            self.labelLoading.setText(
                u'Načítám data do SQLite databáze (může nějaký čas trvat...)')

            try:
                self.loadVfkFile(fileName)
            except VFKError as e:
                QMessageBox.critical(
                    self, u'Chyba', u'{}'.format(e), QMessageBox.Ok)
                self.emit(SIGNAL("enableSearch"), False)
                return

            self.__mLastVfkFile.append(fileName)
            self.importThread.nextLayer = False

            if fileName == self.__fileName[-1]:
                self.loadingLayersFinished()

    def loadingLayersFinished(self):
        """

        :return:
        """
        try:
            self.__openDatabase(
                os.environ['OGR_VFK_DB_NAME'])  # self.__mDataSourceName)
        except VFKError as e:
            QMessageBox.critical(
                self, u'Chyba', u'{}'.format(e), QMessageBox.Ok)
            self.emit(SIGNAL("enableSearch"), False)
            return

        self.vfkBrowser.setConnectionName(self.property("connectionName"))
        self.__mSearchController.setConnectionName(
            self.property("connectionName"))

        self.emit(SIGNAL("enableSearch"), True)
        self.__mLoadedLayers.clear()

        if self.parCheckBox.isChecked():
            self.__loadVfkLayer('PAR')
        else:
            self.__unLoadVfkLayer('PAR')

        if self.budCheckBox.isChecked():
            self.__loadVfkLayer('BUD')
        else:
            self.__unLoadVfkLayer('BUD')

        self.labelLoading.setText(u'Načítání souborů VFK bylo dokončeno.')

    def vfkFileLineEdit_textChanged(self, arg1):
        """

        :type arg1: str
        :return:
        """
        info = QFileInfo(arg1)

        if info.isFile():
            self.loadVfkButton.setEnabled(True)
            self.vfkFileLineEdit.setPalette(self.__mDefaultPalette)
        else:
            self.loadVfkButton.setEnabled(False)
            pal = QPalette(self.vfkFileLineEdit.palette())
            pal.setColor(QPalette.text(), Qt.red)
            self.vfkFileLineEdit.setPalette(pal)

    def __loadVfkLayer(self, vfkLayerName):
        """

        :type vfkLayerName: str
        :return:
        """
        qDebug("\n(VFK) Loading vfk layer {}".format(vfkLayerName))
        if vfkLayerName in self.__mLoadedLayers:
            qDebug(
                "\n(VFK) Vfk layer {} is already loaded".format(vfkLayerName))
            return

        composedURI = self.__mDataSourceName + "|layername=" + vfkLayerName
        layer = QgsVectorLayer(composedURI, vfkLayerName, "ogr")
        if not layer.isValid():
            qDebug("\n(VFK) Layer failed to load!")

        self.__mLoadedLayers[vfkLayerName] = layer.id()

        try:
            self.__setSymbology(layer)
        except VFKWarning as e:
            QMessageBox.information(self, 'Load Style', '{}'.format(e), QMessageBox.Ok)

        QgsMapLayerRegistry.instance().addMapLayer(layer)

    def __unLoadVfkLayer(self, vfkLayerName):
        """

        :type vfkLayerName: str
        :return:
        """
        qDebug("\n(VFK) Unloading vfk layer {}".format(vfkLayerName))

        if vfkLayerName not in self.__mLoadedLayers:
            qDebug(
                "\n(VFK) Vfk layer {} is already unloaded".format(vfkLayerName))
            return

        QgsMapLayerRegistry.instance().removeMapLayer(
            self.__mLoadedLayers[vfkLayerName])
        del self.__mLoadedLayers[vfkLayerName]

    def __setSymbology(self, layer):
        """

        :type layer: QgsVectorLayer
        :return:
        """
        name = layer.name()
        symbologyFile = ''

        if name == 'PAR':
            symbologyFile = ':/parStyle.qml'
        elif name == 'BUD':
            symbologyFile = ':/budStyle.qml'

        errorMsg, resultFlag = layer.loadNamedStyle(symbologyFile)

        if not resultFlag:
            raise VFKWarning(u'Load style: {}'.format(errorMsg))

        layer.triggerRepaint()
        self.emit(SIGNAL("refreshLegend"), layer)

    def __openDatabase(self, dbPath):
        """

        :type dbPath: str
        :return:
        """
        qDebug("\n(VFK) Open DB: {}".format(dbPath))
        if not QSqlDatabase.isDriverAvailable('QSQLITE'):
            raise VFKError(u'Databázový ovladač QSQLITE není dostupný.')

        connectionName = QUuid.createUuid().toString()
        db = QSqlDatabase.addDatabase("QSQLITE", connectionName)
        db.setDatabaseName(dbPath)
        if not db.open():
            raise VFKError(u'Nepodařilo se otevřít databázi. ')

        self.setProperty("connectionName", connectionName)

    def loadVfkFile(self, fileName):
        """

        :type fileName: str
        :return:
        """
        label_text = fileName.split('/')
        label_text = '...' + label_text[-2] + '/' + label_text[-1]

        # overwrite database
        if fileName == self.__fileName[0]:
            if self.overwriteCheckBox.isChecked():
                qDebug('\n (VFK) Database will be overwritten')
                os.environ['OGR_VFK_DB_OVERWRITE'] = '1'

        if self.__mOgrDataSource:
            self.__mOgrDataSource.Destroy()
            self.__mOgrDataSource = None

        QgsApplication.registerOgrDrivers()

        self.progressBar.setRange(0, 1)
        self.progressBar.setValue(0)

        QgsApplication.processEvents()

        #os.environ['OGR_VFK_DB_READ_ALL_BLOCKS'] = 'NO'
        self.labelLoading.setText(
            u'Načítám soubor {} (může nějaký čas trvat...)'.format(label_text))

        QgsApplication.processEvents()

        self.__mOgrDataSource = ogr.Open(
            fileName, 0)   # 0 - datasource is open in read-only mode

        if not self.__mOgrDataSource:
            raise VFKError(
                u"Nelze otevřít VFK soubor '{}' jako platný OGR datasource.".format(fileName))

        t_par = self.__mOgrDataSource.GetLayerByName('PAR')

        if t_par == None:
            self.__mOgrDataSource = None
            # Build Parcels
            parcels = VFKParBuilder(fileName)
            parcels.build_all_par()
            # Build Buildings
            buildings = VFKBudBuilder(fileName)
            buildings.build_all_bud()
            self.labelLoading.setText(
                u'Data byla načtena pomocí rozšíření, které neobsažené bloky PAR a BUD sestavilo z neúplného VFK souboru.')
            self.__mOgrDataSource = ogr.Open(os.environ['OGR_VFK_DB_NAME'], 0) 
            self.__mDataSourceName = os.environ['OGR_VFK_DB_NAME']

        #What kind of data were loaded? full/incomplete
        if not self.__mOgrDataSource.GetLayerByName('JED'):
            self.full_data = False

        if self.full_data is False:
            self.actionBack.setEnabled(False)
            self.actionBack.setToolTip(u'Zpět-nefunkční pro neúplná data')
            self.actionZpracujZmeny.setEnabled(False)
            self.actionZpracujZmeny.setToolTip(u'Zpracování změn pro neúplná data není možné')
            self.actionForward.setEnabled(False)
            self.actionForward.setToolTip(u'Vpřed-nefunkční pro neúplná data')
            self.actionSelectBudInMap.setEnabled(False)
            self.actionSelectBudInMap.setToolTip(u'Výběr budov v mapě není možný u neúplných dat')
            self.actionCuzkPage.setEnabled(False)
            self.actionCuzkPage.setToolTip(u'V neúplných datech nelze nahlížet do katastru nemovitostí')
            self.actionShowInfoaboutSelection.setEnabled(False)
            self.actionShowInfoaboutSelection.setToolTip(u'Informace o výběru pro neúplná data nefungují')
            self.actionShowHelpPage.setEnabled(False)
            self.actionShowHelpPage.setToolTip(u'Nápověda není k dispozici pro neúplná data')
            self.actionExportLatex.setEnabled(False)
            self.actionExportHtml.setEnabled(False)

        layers_names = []

        layerCount = self.__mOgrDataSource.GetLayerCount()

        for i in xrange(layerCount):
            layers_names.append(
                self.__mOgrDataSource.GetLayer(i).GetLayerDefn().GetName())

        if ('PAR' not in layers_names or 'BUD' not in layers_names) and len(self.__vfkLineEdits) == 1:
            self.__dataWithoutParBud()
            self.labelLoading.setText(
                u'Data nemohla být načtena. Vstupní soubor neobsahuje bloky PAR a BUD.')
            QgsApplication.processEvents()
            return

        # load all layers
        self.progressBar.setRange(0, layerCount - 1)
        for i in xrange(layerCount):
            self.progressBar.setValue(i)
            theLayerName = self.__mOgrDataSource.GetLayer(
                i).GetLayerDefn().GetName()
            self.labelLoading.setText(
               u"VFK data {}/{}: {}".format(i + 1, layerCount, theLayerName))
            QgsApplication.processEvents()
            self.__mOgrDataSource.GetLayer(i).GetFeatureCount(True)
            time.sleep(0.02)

        self.labelLoading.setText(
            u'Soubor {} úspěšně načten.'.format(label_text))

        os.environ['OGR_VFK_DB_OVERWRITE'] = '0'
        self.__mOgrDataSource.Destroy()
        self.__mOgrDataSource = None

    def __selectedIds(self, layer):
        """

        :type layer: QgsVectorLayer
        :return:
        """
        ids = []
        flist = layer.selectedFeatures()

        for it in flist:
            f = QgsFeature(it)
            ids.append(str(f.attribute("ID")))
        return ids

    def showInfoAboutSelection(self):
        layers = ["PAR", "BUD"]
        layerIds = {}
        for layer in layers:
            if layer in self.__mLoadedLayers:
                id = str(self.__mLoadedLayers[layer])
                vectorLayer = QgsMapLayerRegistry.instance().mapLayer(id)
                layerIds[layer] = self.__selectedIds(vectorLayer)

        self.vfkBrowser.showInfoAboutSelection(
            layerIds["PAR"], layerIds["BUD"])

    def showParInMap(self, ids):
        """

        :type ids: list
        :return:
        """
        if self.actionShowInfoaboutSelection.isChecked():
            self.setSelectionChangedConnected(False)
            self.showInMap(ids, "PAR")
            self.setSelectionChangedConnected(True)
        else:
            self.showInMap(ids, "PAR")

    def showBudInMap(self, ids):
        """

        :type ids: list
        :return:
        """
        if self.actionShowInfoaboutSelection.isChecked():
            self.setSelectionChangedConnected(False)
            self.showInMap(ids, "BUD")
            self.setSelectionChangedConnected(True)
        else:
            self.showInMap(ids, "BUD")

    def showOnCuzk(self):
        x = self.vfkBrowser.currentDefinitionPoint().first.split(".")[0]
        y = self.vfkBrowser.currentDefinitionPoint().second.split(".")[0]

        url = "http://nahlizenidokn.cuzk.cz/MapaIdentifikace.aspx?&x=-{}&y=-{}".format(
            y, x)
        QDesktopServices.openUrl(QUrl(url, QUrl.TolerantMode))

    def switchToImport(self):
        self.actionImport.trigger()

    def switchToSearch(self, searchType):
        """
        :type searchType: int
        """
        self.actionVyhledavani.trigger()
        self.searchCombo.setCurrentIndex(searchType)
        self.searchFormMainControls.searchForms.setCurrentIndex(searchType)

    def switchToChanges(self):
        """

        """
        self.actionZpracujZmeny.trigger()

    def succesfullExport(self, export_format):
        """

        :type export_format: str
        :return:
        """
        QMessageBox.information(
            self, u'Export', u"Export do formátu {} proběhl úspěšně.".format(
                export_format),
                                QMessageBox.Ok)

    def __dataWithoutParBud(self):
        """

        :type export_format: str
        :return:
        """
        QMessageBox.warning(self, u'Upozornění', u"Zvolený VFK soubor neobsahuje vrstvy s geometrií (PAR, BUD), proto nemohou "
                            u"být pomocí VFK Pluginu načtena. Data je možné načíst v QGIS pomocí volby "
                            u"'Načíst vektorovou vrstvu.'", QMessageBox.Ok)

    def __addRowToGridLayout(self):
        if len(self.__vfkLineEdits) >= 5:
            self.__maximumLineEditsReached()
            return

        # update label
        self.label.setText('VFK soubory:')

        # new layout
        horizontalLayout = QtGui.QHBoxLayout()

        # create new objects
        self.__browseButtons['browseButton_{}'.format(
            len(self.__vfkLineEdits) + 1)] = QtGui.QPushButton(u"Procházet")
        self.__vfkLineEdits['vfkLineEdit_{}'.format(
            len(self.__vfkLineEdits) + 1)] = QtGui.QLineEdit()

        horizontalLayout.addWidget(self.__vfkLineEdits[
                                   'vfkLineEdit_{}'.format(len(self.__vfkLineEdits))])
        horizontalLayout.addWidget(self.__browseButtons[
                                   'browseButton_{}'.format(len(self.__vfkLineEdits))])

        # number of lines in gridLayout
        rows_count = self.gridLayout_12.rowCount(
        )  # count of rows in gridLayout

        # export objects from gridLayout
        item_label = self.gridLayout_12.itemAtPosition(rows_count - 3, 0)
        item_par = self.gridLayout_12.itemAtPosition(rows_count - 3, 1)
        item_bud = self.gridLayout_12.itemAtPosition(rows_count - 2, 1)
        item_settings = self.gridLayout_12.itemAtPosition(rows_count - 1, 0)
        item_rewrite_db = self.gridLayout_12.itemAtPosition(rows_count - 1, 1)

        # remove objects from gridLayout
        self.gridLayout_12.removeItem(
            self.gridLayout_12.itemAtPosition(rows_count - 3, 0))
        self.gridLayout_12.removeItem(
            self.gridLayout_12.itemAtPosition(rows_count - 3, 1))
        self.gridLayout_12.removeItem(
            self.gridLayout_12.itemAtPosition(rows_count - 2, 1))
        self.gridLayout_12.removeItem(
            self.gridLayout_12.itemAtPosition(rows_count - 1, 0))
        self.gridLayout_12.removeItem(
            self.gridLayout_12.itemAtPosition(rows_count - 1, 1))

        # re-build gridLayout
        self.gridLayout_12.addLayout(horizontalLayout, rows_count - 3, 1)
        self.gridLayout_12.addItem(item_label, rows_count - 2, 0)
        self.gridLayout_12.addItem(item_par, rows_count - 2, 1)
        self.gridLayout_12.addItem(item_bud, rows_count - 1, 1)
        self.gridLayout_12.addItem(item_settings, rows_count, 0)
        self.gridLayout_12.addItem(item_rewrite_db, rows_count, 1)

        self.__browseButtons['browseButton_{}'.format(len(self.__vfkLineEdits))].clicked.\
            connect(lambda: self.browseButton_clicked(
                int('{}'.format(len(self.__vfkLineEdits)))))

    def __maximumLineEditsReached(self):
        QMessageBox.information(self, u'Upozornění', u"Byl dosažen maximální počet ({}) VFK souboru pro zpracování."
                                                     u"\nNačítání dalších souborů není povoleno!".
                                format(self.lineEditsCount), QMessageBox.Ok)

    def browseDb_clicked(self, database_type):
        """
        Method run dialog for select database in widget with changes.
        According to pushButton name will fill in relevant lineEdit.
        :type database_type: str
        """
        title = u'Vyber databázi'
        settings = QSettings()
        lastUsedDir = str(settings.value('/UI/' + "lastVectorFileFilter" + "Dir", "."))

        if database_type == 'mainDb':
            self.__databases[database_type] = QFileDialog.getOpenFileName(self, title, lastUsedDir, u'Datábaze (*.db)')
            if not self.__databases[database_type]:
                return
            self.le_mainDb.setText(self.__databases[database_type])

        elif database_type == 'amendmentDb':
            self.__databases[database_type] = QFileDialog.getOpenFileName(self, title, lastUsedDir, u'Datábaze (*.db)')
            if not self.__databases[database_type]:
                return
            self.le_amendmentDb.setText(self.__databases[database_type])

        elif database_type == 'exportDb':
            title = u'Zadej jméno výstupní databáze'
            self.__databases[database_type] = QFileDialog.getSaveFileName(self, u"Jméno výstupní databáze",
                                                                          ".db", u"Databáze (*.db)")
            if not self.__databases[database_type]:
                return
            self.le_exportDb.setText(self.__databases[database_type])

        if len(self.__databases) == 3:
            self.pb_applyChanges.setEnabled(True)

    def applyChanges(self):
        """
        Method
        :return:
        """
        self.changes_instance.run(self.__databases['mainDb'],
                                  self.__databases['amendmentDb'],
                                  self.__databases['exportDb'])

    def __updateProgressBarChanges(self, iteration, table_name):
        """
        :type iteration: int
        :type table_name: str
        """
        self.progressBar_changes.setValue(iteration)
        self.l_status.setText(u'Aplikuji změny na tabulku {}...'.format(table_name))
        QgsApplication.processEvents()

    def __setRangeProgressBarChanges(self, max_range):
        """
        :type max_range: int
        """
        self.progressBar_changes.setRange(0, max_range)
        self.progressBar_changes.setValue(0)

    def __changesApplied(self):
        """
        """
        time.sleep(1)
        self.l_status.setText(u'Změny byly úspěšně aplikovány.')
        QgsApplication.processEvents()

    def __changesPreprocessingDatabase(self):
        """
        """
        self.l_status.setText(u'Připravuji výstupní databázi...')
        QgsApplication.processEvents()

    def __checkIfAmendmentFile(self, file_name):
        """

        :param file_name: Name of the input file
        :type file_name: str
        :return: bool
        """
        if file_name.endswith(".vfk"):
            with open(file_name, 'r') as f:
                for line in f:

                    line_splited = str(line).split(';')

                    if line_splited[0] == '&HZMENY':
                        if line_splited[1] == '1':
                            return True
                        else:
                            return False
        else:
            print 'database'
            # TODO: dopsat kontrolu, zda se jedna o stavovou, nebo zmenovou databazi

    def radioButtonValue(self):
        """
        Check which radio button is checked
        """
        self.vfkFileLineEdit.setText('')
        self.__fileName = []
        self.loadVfkButton.setEnabled(False)

        if self.rb_file.isChecked():
            self.__source_for_data = 'file'
            self.pb_nextFile.show()
            self.label.setText('VFK soubor:')
        elif self.rb_directory.isChecked():
            self.__source_for_data = 'directory'
            self.pb_nextFile.hide()
            self.label.setText(u'Adresář:')

            # delete
            if len(self.__browseButtons) > 1:
                for i, button in enumerate(self.__browseButtons):
                    if i > 0:
                        self.__browseButtons[button].hide()

            if len(self.__vfkLineEdits) > 1:
                for i, le in enumerate(self.__vfkLineEdits):
                    if i > 0:
                        self.__vfkLineEdits[le].hide()

    def __findVFKFilesInDirectory(self, dir_path):
        """
        Finds all files with extension '.vfk' in given directory including subdirectories
        :param dir_path: Path to directory.
        :type dir_path: str
        :return: List of VFK files
        """
        file_paths = []
        for root, dirs, files in os.walk(dir_path):
            for file in files:
                if file.endswith(".db"):
                #if file.endswith(".vfk"):
                    file_paths.append(os.path.join(root, file))

        return file_paths


    def __createToolbarsAndConnect(self):

        actionGroup = QActionGroup(self)
        actionGroup.addAction(self.actionImport)
        actionGroup.addAction(self.actionVyhledavani)
        actionGroup.addAction(self.actionZpracujZmeny)

        # QSignalMapper
        self.signalMapper = QSignalMapper(self)

        # connect to 'clicked' on all buttons
        self.connect(self.actionImport, SIGNAL(
            "triggered()"), self.signalMapper, SLOT("map()"))
        self.connect(self.actionVyhledavani, SIGNAL(
            "triggered()"), self.signalMapper, SLOT("map()"))
        self.connect(self.actionZpracujZmeny, SIGNAL(
            "triggered()"), self.signalMapper, SLOT("map()"))

        # setMapping on each button to the QStackedWidget index we'd like to
        # switch to
        self.signalMapper.setMapping(self.actionImport, 0)
        self.signalMapper.setMapping(self.actionVyhledavani, 2)
        self.signalMapper.setMapping(self.actionZpracujZmeny, 1)

        # connect mapper to stackedWidget
        self.connect(self.signalMapper, SIGNAL("mapped(int)"),
                     self.stackedWidget, SLOT("setCurrentIndex(int)"))
        self.actionImport.trigger()

        self.connect(self.vfkBrowser, SIGNAL(
            "switchToPanelImport"), self.switchToImport)
        self.connect(self.vfkBrowser, SIGNAL(
            "switchToPanelSearch"), self.switchToSearch)
        self.connect(self.vfkBrowser, SIGNAL(
            "switchToPanelChanges"), self.switchToChanges)

        # Browser toolbar
        # ---------------
        self.__mBrowserToolbar = QToolBar(self)
        self.connect(self.actionBack, SIGNAL(
            "triggered()"), self.vfkBrowser.goBack)
        self.connect(self.actionForward, SIGNAL(
            "triggered()"), self.vfkBrowser.goForth)

        self.connect(self.actionSelectBudInMap,
                     SIGNAL("triggered()"), self.selectBudInMap)
        self.connect(self.actionSelectParInMap,
                     SIGNAL("triggered()"), self.selectParInMap)
        self.connect(self.actionCuzkPage,
                     SIGNAL("triggered()"), self.showOnCuzk)

        self.connect(self.actionExportLatex,
                     SIGNAL("triggered()"), self.latexExport)
        self.connect(self.actionExportHtml,
                     SIGNAL("triggered()"), self.htmlExport)

        self.connect(self.actionShowInfoaboutSelection, SIGNAL(
            "toggled(bool)"), self.setSelectionChangedConnected)
        self.connect(self.actionShowHelpPage, SIGNAL(
            "triggered()"), self.vfkBrowser.showHelpPage)

        self.loadVfkButton.clicked.connect(self.loadVfkButton_clicked)

        self.__browseButtons['browseButton_1'] = self.browseButton
        self.__browseButtons['browseButton_1'].clicked.connect(
            lambda: self.browseButton_clicked(int('{}'.format(len(self.__vfkLineEdits)))))

        self.__vfkLineEdits['vfkLineEdit_1'] = self.vfkFileLineEdit

        bt = QToolButton(self.__mBrowserToolbar)
        bt.setPopupMode(QToolButton.InstantPopup)
        bt.setText("Export ")

        menu = QMenu(bt)
        menu.addAction(self.actionExportLatex)
        menu.addAction(self.actionExportHtml)
        bt.setMenu(menu)

        # add actions to toolbar icons
        self.__mBrowserToolbar.addAction(self.actionImport)
        self.__mBrowserToolbar.addAction(self.actionVyhledavani)
        self.__mBrowserToolbar.addAction(self.actionZpracujZmeny)
        self.__mBrowserToolbar.addSeparator()
        self.__mBrowserToolbar.addAction(self.actionBack)
        self.__mBrowserToolbar.addAction(self.actionForward)
        self.__mBrowserToolbar.addAction(self.actionSelectParInMap)
        self.__mBrowserToolbar.addAction(self.actionSelectBudInMap)
        self.__mBrowserToolbar.addAction(self.actionCuzkPage)
        self.__mBrowserToolbar.addSeparator()
        self.__mBrowserToolbar.addAction(self.actionShowInfoaboutSelection)
        self.__mBrowserToolbar.addSeparator()
        self.__mBrowserToolbar.addWidget(bt)
        self.__mBrowserToolbar.addSeparator()
        self.__mBrowserToolbar.addAction(self.actionShowHelpPage)

        self.rightWidgetLayout.insertWidget(0, self.__mBrowserToolbar)

        # connect signals from vfkbrowser when changing history
        self.connect(self.vfkBrowser, SIGNAL(
            "currentParIdsChanged"), self.actionSelectParInMap.setEnabled)
        self.connect(self.vfkBrowser, SIGNAL("currentBudIdsChanged"),
                     self.actionSelectBudInMap.setEnabled)
        self.connect(self.vfkBrowser, SIGNAL(
            "historyBefore"), self.actionBack.setEnabled)
        self.connect(self.vfkBrowser, SIGNAL(
            "historyAfter"), self.actionForward.setEnabled)
        self.connect(self.vfkBrowser, SIGNAL(
            "definitionPointAvailable"), self.actionCuzkPage.setEnabled)

        # add toolTips
        self.pb_nextFile.setToolTip(u'Přidej další soubor VFK')
        self.parCheckBox.setToolTip(u'Načti vrstvu parcel')
        self.budCheckBox.setToolTip(u'Načti vrstvu budov')

        # add new VFK file
        self.pb_nextFile.clicked.connect(self.__addRowToGridLayout)

        # widget apply changes
        self.pb_mainDb.clicked.connect(
            lambda: self.browseDb_clicked('mainDb'))
        self.pb_amendmentDb.clicked.connect(
            lambda: self.browseDb_clicked('amendmentDb'))
        self.pb_exportDb.clicked.connect(
            lambda: self.browseDb_clicked('exportDb'))

        self.pb_applyChanges.clicked.connect(self.applyChanges)
        self.pb_applyChanges.setEnabled(False)

        self.connect(self.changes_instance, SIGNAL("maxRangeProgressBar"), self.__setRangeProgressBarChanges)
        self.connect(self.changes_instance, SIGNAL("updateStatus"), self.__updateProgressBarChanges)
        self.connect(self.changes_instance, SIGNAL("finishedStatus"), self.__changesApplied)
        self.connect(self.changes_instance, SIGNAL("preprocessingDatabase"), self.__changesPreprocessingDatabase)

        # connect radio boxes
        self.rb_file.clicked.connect(self.radioButtonValue)
        self.rb_directory.clicked.connect(self.radioButtonValue)
예제 #21
0
class MainWindow(QMainWindow, Ui_MainWindow):
    """Where all the action happens."""

    def __init__(self, settings):
        super(MainWindow, self).__init__()
        self.settings = settings
        self.setupUi(self)

        # MainWindow is a collection of widgets in their respective docks.
        # We make DockArea our central widget
        self.dock_area = DockArea()
        self.setCentralWidget(self.dock_area)

        self.createDocks()
        self.initAfterCreatingDockWidgets()
        self.loadSettings()

        self.connectSignalsToSlots()

        # all signals in place, send out the first image
        # self.image_browser.populateAndEmitImageInfo()
        self.image_browser.initialEmit()
        self.roi_editor_h.initialEmit()
        self.roi_editor_v.initialEmit()
        self.roi_editor_int.initialEmit()
        self.roi_editor_err.initialEmit()

        self.loadPlugins()

    def loadPlugins(self):
        """Looks for all plugins and creates menu entries, signals and
           slots for them."""
        self.pluginSignalMapper = QSignalMapper(self)
        for p in plugin_list:
            click_action = QAction(p.name, self)
            self.menuPlugins.addAction(click_action)
            self.connect(click_action, SIGNAL("triggered()"),
                         self.pluginSignalMapper, SLOT("map()"))
            self.pluginSignalMapper.setMapping(click_action, QString(p.name))
        self.connect(self.pluginSignalMapper,
                     SIGNAL("mapped(const QString &)"),
                     self.image_browser.handlePluginClicked)


    def createDocks(self):
        """Create all dock widgets and add them to DockArea."""
        self.image_view = ImageView(self.settings, self)
        self.image_browser = ImageBrowser(self.settings, self)
        self.fitter = Fitter(self.settings, self)

        self.roi_editor_h = RoiEditor(self.settings,
                                      self.image_view, self, name='ROIH',
                                      pen=(1, 9), axis=1)
        self.roi_editor_v = RoiEditor(self.settings,
                                      self.image_view, self, name='ROIV',
                                      pen=(1, 1), axis=0)
        self.roi_editor_int = RoiEditor(self.settings,
                                        self.image_view, self, name='ROI Int',
                                        pen=(1, 2), axis=1)
        self.roi_editor_err = RoiEditor(self.settings,
                                        self.image_view, self, name='ROI Err',
                                        pen=(1, 3), axis=1)
        self.roi_plot_h = Plot1d(parent=self, title='ROI H')
        self.roi_plot_v = Plot1d(parent=self, title='ROI V')

        self.analyzer = Analyzer(self.settings, parent=self)

        # Create docks for all widgets
        self.dock_image_view = Dock('Image View', widget=self.image_view)
        self.dock_image_browser = Dock('Image Browser',
                                       widget=self.image_browser)
        self.dock_fitter = Dock('Fitter', widget=self.fitter)
        self.dock_roi_h = Dock('ROIH', widget=self.roi_editor_h)
        self.dock_roi_v = Dock('ROIV', widget=self.roi_editor_v)
        self.dock_roi_int = Dock('ROI Int', widget=self.roi_editor_int)
        self.dock_roi_err = Dock('ROI Err', widget=self.roi_editor_err)

        self.dock_roi_plot_h = Dock('ROIH Plot', widget=self.roi_plot_h)
        self.dock_roi_plot_v = Dock('ROIV Plot', widget=self.roi_plot_v)
        self.dock_analyzer = Dock('Analyze', widget=self.analyzer)

        self.dock_area.addDock(self.dock_image_view, position='top')
        self.dock_area.addDock(self.dock_image_browser, position='right',
                               relativeTo=self.dock_image_view)
        self.dock_area.addDock(self.dock_fitter, position='left',
                               relativeTo=self.dock_image_view)
        self.dock_area.addDock(self.dock_roi_h, position='bottom',
                               relativeTo=self.dock_fitter)
        self.dock_area.addDock(self.dock_roi_v, position='below',
                               relativeTo=self.dock_roi_h)
        self.dock_area.addDock(self.dock_roi_int, position='below',
                               relativeTo=self.dock_roi_v)
        self.dock_area.addDock(self.dock_roi_err, position='below',
                               relativeTo=self.dock_roi_int)
        self.dock_area.addDock(self.dock_roi_plot_h, position='below',
                               relativeTo=self.dock_image_view)
        self.dock_area.addDock(self.dock_roi_plot_v, position='right',
                               relativeTo=self.dock_roi_plot_h)
        self.dock_area.addDock(self.dock_analyzer, position='top',
                               relativeTo=self.dock_image_browser)

    def initAfterCreatingDockWidgets(self):
        self.setWindowTitle(self.image_browser.current_directory)

    def connectSignalsToSlots(self):
        self.actionOpen_Directory.triggered.connect(self.image_browser.handleOpenDirectoryAction)
        self.actionDark_File.triggered.connect(self.image_browser.handleDarkFileAction)
        self.actionRefresh.triggered.connect(self.image_browser.handleRefreshAction)
        self.actionSave.triggered.connect(self.image_browser.handleSaveAnalysis)

        self.image_browser.windowTitleChanged.connect(self.setWindowTitle)
        # self.image_browser.imageChanged.connect(self.image_view.handleImageChanged)
        self.image_browser.imageChanged.connect(self.fitter.handleImageChanged)
        self.image_browser.imageChanged.connect(self.analyzer.handleImageChanged)

        self.roi_editor_int.roiChanged.connect(self.image_browser.handleRoiChanged)
        self.roi_editor_err.roiChanged.connect(self.image_browser.handleROIErrChanged)
        self.roi_editor_h.roiChanged.connect(self.image_browser.handleROIHChanged)
        self.roi_editor_v.roiChanged.connect(self.image_browser.handleROIVChanged)

        self.roi_editor_h.roiChanged.connect(self.fitter.handleROIHChanged)
        self.roi_editor_h.roiChanged.connect(self.analyzer.handleROIHChanged)
        self.roi_editor_v.roiChanged.connect(self.fitter.handleROIVChanged)
        self.roi_editor_v.roiChanged.connect(self.analyzer.handleROIVChanged)
        self.roi_editor_int.roiChanged.connect(self.fitter.handleROIIntChanged)
        self.roi_editor_int.roiChanged.connect(self.analyzer.handleROIIntChanged)
        self.roi_editor_err.roiChanged.connect(self.fitter.handleROIErrChanged)
        self.roi_editor_err.roiChanged.connect(self.analyzer.handleROIErrChanged)

        self.image_view.doubleClicked.connect(self.roi_editor_h.centerROI)
        self.image_view.doubleClicked.connect(self.roi_editor_v.centerROI)

        self.fitter.imageChanged.connect(self.image_view.handleImageChanged)
        self.fitter.horDataChanged.connect(self.roi_plot_h.handleDataChanged)
        self.fitter.verDataChanged.connect(self.roi_plot_v.handleDataChanged)
        self.fitter.doneFitting.connect(self.analyzer.handleDoneFitting)

    def loadSettings(self):
        """Load window state from self.settings"""

        self.settings.beginGroup('mainwindow')
        geometry = self.settings.value('geometry').toByteArray()
        state = self.settings.value('windowstate').toByteArray()
        dock_string = str(self.settings.value('dockstate').toString())
        if dock_string is not "":
            dock_state = eval(dock_string)
            self.dock_area.restoreState(dock_state)
        self.settings.endGroup()

        self.restoreGeometry(geometry)
        self.restoreState(state)

    def saveSettings(self):
        """Save window state to self.settings."""
        self.settings.beginGroup('mainwindow')
        self.settings.setValue('geometry', self.saveGeometry())
        self.settings.setValue('windowstate', self.saveState())
        dock_state = self.dock_area.saveState()
        # dock_state returned here is a python dictionary. Coundn't find a good
        # way to save dicts in QSettings, hence just using representation
        # of it.
        self.settings.setValue('dockstate', repr(dock_state))
        self.settings.endGroup()

    def closeEvent(self, event):
        self.saveSettings()
        self.image_browser.saveSettings()
        self.roi_editor_int.saveSettings()
        self.roi_editor_err.saveSettings()
        self.roi_editor_v.saveSettings()
        self.roi_editor_h.saveSettings()
        self.analyzer.saveSettings()
        super(MainWindow, self).closeEvent(event)

    def setWindowTitle(self, newTitle=''):
        """Prepend IP-BEC to all window titles."""
        title = 'IP-BEC: ' + newTitle
        super(MainWindow, self).setWindowTitle(title)
예제 #22
0
class CanvasScene(QGraphicsScene):
    """
    A Graphics Scene for displaying an :class:`~.scheme.Scheme` instance.
    """

    #: Signal emitted when a :class:`NodeItem` has been added to the scene.
    node_item_added = Signal(object)

    #: Signal emitted when a :class:`NodeItem` has been removed from the
    #: scene.
    node_item_removed = Signal(object)

    #: Signal emitted when a new :class:`LinkItem` has been added to the
    #: scene.
    link_item_added = Signal(object)

    #: Signal emitted when a :class:`LinkItem` has been removed.
    link_item_removed = Signal(object)

    #: Signal emitted when a :class:`Annotation` item has been added.
    annotation_added = Signal(object)

    #: Signal emitted when a :class:`Annotation` item has been removed.
    annotation_removed = Signal(object)

    #: Signal emitted when the position of a :class:`NodeItem` has changed.
    node_item_position_changed = Signal(object, QPointF)

    #: Signal emitted when an :class:`NodeItem` has been double clicked.
    node_item_double_clicked = Signal(object)

    #: An node item has been activated (clicked)
    node_item_activated = Signal(object)

    #: An node item has been hovered
    node_item_hovered = Signal(object)

    #: Link item has been hovered
    link_item_hovered = Signal(object)

    def __init__(self, *args, **kwargs):
        QGraphicsScene.__init__(self, *args, **kwargs)

        self.scheme = None
        self.registry = None

        # All node items
        self.__node_items = []
        # Mapping from SchemeNodes to canvas items
        self.__item_for_node = {}
        # All link items
        self.__link_items = []
        # Mapping from SchemeLinks to canvas items.
        self.__item_for_link = {}

        # All annotation items
        self.__annotation_items = []
        # Mapping from SchemeAnnotations to canvas items.
        self.__item_for_annotation = {}

        # Is the scene editable
        self.editable = True

        # Anchor Layout
        self.__anchor_layout = AnchorLayout()
        self.addItem(self.__anchor_layout)

        self.__channel_names_visible = True
        self.__node_animation_enabled = True

        self.user_interaction_handler = None

        self.activated_mapper = QSignalMapper(self)
        self.activated_mapper.mapped[QObject].connect(
            lambda node: self.node_item_activated.emit(node)
        )
        self.hovered_mapper = QSignalMapper(self)
        self.hovered_mapper.mapped[QObject].connect(
            lambda node: self.node_item_hovered.emit(node)
        )
        self.position_change_mapper = QSignalMapper(self)
        self.position_change_mapper.mapped[QObject].connect(
            self._on_position_change
        )
        log.info("'%s' intitialized." % self)

    def clear_scene(self):
        """
        Clear (reset) the scene.
        """
        if self.scheme is not None:
            self.scheme.node_added.disconnect(self.add_node)
            self.scheme.node_removed.disconnect(self.remove_node)

            self.scheme.link_added.disconnect(self.add_link)
            self.scheme.link_removed.disconnect(self.remove_link)

            self.scheme.annotation_added.disconnect(self.add_annotation)
            self.scheme.annotation_removed.disconnect(self.remove_annotation)

            self.scheme.node_state_changed.disconnect(
                self.on_widget_state_change
            )
            self.scheme.channel_state_changed.disconnect(
                self.on_link_state_change
            )

            # Remove all items to make sure all signals from scheme items
            # to canvas items are disconnected.

            for annot in self.scheme.annotations:
                if annot in self.__item_for_annotation:
                    self.remove_annotation(annot)

            for link in self.scheme.links:
                if link in self.__item_for_link:
                    self.remove_link(link)

            for node in self.scheme.nodes:
                if node in self.__item_for_node:
                    self.remove_node(node)

        self.scheme = None
        self.__node_items = []
        self.__item_for_node = {}
        self.__link_items = []
        self.__item_for_link = {}
        self.__annotation_items = []
        self.__item_for_annotation = {}

        self.__anchor_layout.deleteLater()

        self.user_interaction_handler = None

        self.clear()
        log.info("'%s' cleared." % self)

    def set_scheme(self, scheme):
        """
        Set the scheme to display. Populates the scene with nodes and links
        already in the scheme. Any further change to the scheme will be
        reflected in the scene.

        Parameters
        ----------
        scheme : :class:`~.scheme.Scheme`

        """
        if self.scheme is not None:
            # Clear the old scheme
            self.clear_scene()

        log.info("Setting scheme '%s' on '%s'" % (scheme, self))

        self.scheme = scheme
        if self.scheme is not None:
            self.scheme.node_added.connect(self.add_node)
            self.scheme.node_removed.connect(self.remove_node)

            self.scheme.link_added.connect(self.add_link)
            self.scheme.link_removed.connect(self.remove_link)

            self.scheme.annotation_added.connect(self.add_annotation)
            self.scheme.annotation_removed.connect(self.remove_annotation)

            self.scheme.node_state_changed.connect(
                self.on_widget_state_change
            )
            self.scheme.channel_state_changed.connect(
                self.on_link_state_change
            )

            self.scheme.topology_changed.connect(self.on_scheme_change)

        for node in scheme.nodes:
            self.add_node(node)

        for link in scheme.links:
            self.add_link(link)

        for annot in scheme.annotations:
            self.add_annotation(annot)

    def set_registry(self, registry):
        """
        Set the widget registry.
        """
        # TODO: Remove/Deprecate. Is used only to get the category/background
        # color. That should be part of the SchemeNode/WidgetDescription.
        log.info("Setting registry '%s on '%s'." % (registry, self))
        self.registry = registry

    def set_anchor_layout(self, layout):
        """
        Set an :class:`~.layout.AnchorLayout`
        """
        if self.__anchor_layout != layout:
            if self.__anchor_layout:
                self.__anchor_layout.deleteLater()
                self.__anchor_layout = None

            self.__anchor_layout = layout

    def anchor_layout(self):
        """
        Return the anchor layout instance.
        """
        return self.__anchor_layout

    def set_channel_names_visible(self, visible):
        """
        Set the channel names visibility.
        """
        self.__channel_names_visible = visible
        for link in self.__link_items:
            link.setChannelNamesVisible(visible)

    def channel_names_visible(self):
        """
        Return the channel names visibility state.
        """
        return self.__channel_names_visible

    def set_node_animation_enabled(self, enabled):
        """
        Set node animation enabled state.
        """
        if self.__node_animation_enabled != enabled:
            self.__node_animation_enabled = enabled

            for node in self.__node_items:
                node.setAnimationEnabled(enabled)

    def add_node_item(self, item):
        """
        Add a :class:`.NodeItem` instance to the scene.
        """
        if item in self.__node_items:
            raise ValueError("%r is already in the scene." % item)

        if item.pos().isNull():
            if self.__node_items:
                pos = self.__node_items[-1].pos() + QPointF(150, 0)
            else:
                pos = QPointF(150, 150)

            item.setPos(pos)

        item.setFont(self.font())

        # Set signal mappings
        self.activated_mapper.setMapping(item, item)
        item.activated.connect(self.activated_mapper.map)

        self.hovered_mapper.setMapping(item, item)
        item.hovered.connect(self.hovered_mapper.map)

        self.position_change_mapper.setMapping(item, item)
        item.positionChanged.connect(self.position_change_mapper.map)

        self.addItem(item)

        self.__node_items.append(item)

        self.node_item_added.emit(item)

        log.info("Added item '%s' to '%s'" % (item, self))
        return item

    def add_node(self, node):
        """
        Add and return a default constructed :class:`.NodeItem` for a
        :class:`SchemeNode` instance `node`. If the `node` is already in
        the scene do nothing and just return its item.

        """
        if node in self.__item_for_node:
            # Already added
            return self.__item_for_node[node]

        item = self.new_node_item(node.description)

        if node.position:
            pos = QPointF(*node.position)
            item.setPos(pos)

        item.setTitle(node.title)
        item.setProcessingState(node.processing_state)
        item.setProgress(node.progress)

        for message in node.state_messages():
            item.setStateMessage(message)

        item.setStatusMessage(node.status_message())

        self.__item_for_node[node] = item

        node.position_changed.connect(self.__on_node_pos_changed)
        node.title_changed.connect(item.setTitle)
        node.progress_changed.connect(item.setProgress)
        node.processing_state_changed.connect(item.setProcessingState)
        node.state_message_changed.connect(item.setStateMessage)
        node.status_message_changed.connect(item.setStatusMessage)

        return self.add_node_item(item)

    def new_node_item(self, widget_desc, category_desc=None):
        """
        Construct an new :class:`.NodeItem` from a `WidgetDescription`.
        Optionally also set `CategoryDescription`.

        """
        item = items.NodeItem()
        item.setWidgetDescription(widget_desc)

        if category_desc is None and self.registry and widget_desc.category:
            category_desc = self.registry.category(widget_desc.category)

        if category_desc is None and self.registry is not None:
            try:
                category_desc = self.registry.category(widget_desc.category)
            except KeyError:
                pass

        if category_desc is not None:
            item.setWidgetCategory(category_desc)

        item.setAnimationEnabled(self.__node_animation_enabled)
        return item

    def remove_node_item(self, item):
        """
        Remove `item` (:class:`.NodeItem`) from the scene.
        """
        self.activated_mapper.removeMappings(item)
        self.hovered_mapper.removeMappings(item)
        self.position_change_mapper.removeMappings(item)

        item.hide()
        self.removeItem(item)
        self.__node_items.remove(item)

        self.node_item_removed.emit(item)

        log.info("Removed item '%s' from '%s'" % (item, self))

    def remove_node(self, node):
        """
        Remove the :class:`.NodeItem` instance that was previously
        constructed for a :class:`SchemeNode` `node` using the `add_node`
        method.

        """
        item = self.__item_for_node.pop(node)

        node.position_changed.disconnect(self.__on_node_pos_changed)
        node.title_changed.disconnect(item.setTitle)
        node.progress_changed.disconnect(item.setProgress)
        node.processing_state_changed.disconnect(item.setProcessingState)
        node.state_message_changed.disconnect(item.setStateMessage)

        self.remove_node_item(item)

    def node_items(self):
        """
        Return all :class:`.NodeItem` instances in the scene.
        """
        return list(self.__node_items)

    def add_link_item(self, item):
        """
        Add a link (:class:`.LinkItem`) to the scene.
        """
        if item.scene() is not self:
            self.addItem(item)

        item.setFont(self.font())
        self.__link_items.append(item)

        self.link_item_added.emit(item)

        log.info("Added link %r -> %r to '%s'" % \
                 (item.sourceItem.title(), item.sinkItem.title(), self))

        self.__anchor_layout.invalidateLink(item)

        return item

    def add_link(self, scheme_link):
        """
        Create and add a :class:`.LinkItem` instance for a
        :class:`SchemeLink` instance. If the link is already in the scene
        do nothing and just return its :class:`.LinkItem`.

        """
        if scheme_link in self.__item_for_link:
            return self.__item_for_link[scheme_link]

        source = self.__item_for_node[scheme_link.source_node]
        sink = self.__item_for_node[scheme_link.sink_node]

        item = self.new_link_item(source, scheme_link.source_channel,
                                  sink, scheme_link.sink_channel)

        item.setEnabled(scheme_link.enabled)
        scheme_link.enabled_changed.connect(item.setEnabled)

        if scheme_link.is_dynamic():
            item.setDynamic(True)
            item.setDynamicEnabled(scheme_link.dynamic_enabled)
            scheme_link.dynamic_enabled_changed.connect(item.setDynamicEnabled)

        item.setRuntimeState(scheme_link.runtime_state())
        scheme_link.state_changed.connect(item.setRuntimeState)

        self.add_link_item(item)
        self.__item_for_link[scheme_link] = item
        return item

    def new_link_item(self, source_item, source_channel,
                      sink_item, sink_channel):
        """
        Construct and return a new :class:`.LinkItem`
        """
        item = items.LinkItem()
        item.setSourceItem(source_item)
        item.setSinkItem(sink_item)

        def channel_name(channel):
            if isinstance(channel, str):
                return channel
            else:
                return channel.name

        source_name = channel_name(source_channel)
        sink_name = channel_name(sink_channel)

        fmt = "<b>{0}</b>&nbsp; \u2192 &nbsp;<b>{1}</b>"
        item.setToolTip(
            fmt.format(escape(source_name),
                       escape(sink_name))
        )

        item.setSourceName(source_name)
        item.setSinkName(sink_name)
        item.setChannelNamesVisible(self.__channel_names_visible)

        return item

    def remove_link_item(self, item):
        """
        Remove a link (:class:`.LinkItem`) from the scene.
        """
        # Invalidate the anchor layout.
        self.__anchor_layout.invalidateAnchorItem(
            item.sourceItem.outputAnchorItem
        )
        self.__anchor_layout.invalidateAnchorItem(
            item.sinkItem.inputAnchorItem
        )

        self.__link_items.remove(item)

        # Remove the anchor points.
        item.removeLink()
        self.removeItem(item)

        self.link_item_removed.emit(item)

        log.info("Removed link '%s' from '%s'" % (item, self))

        return item

    def remove_link(self, scheme_link):
        """
        Remove a :class:`.LinkItem` instance that was previously constructed
        for a :class:`SchemeLink` instance `link` using the `add_link` method.

        """
        item = self.__item_for_link.pop(scheme_link)
        scheme_link.enabled_changed.disconnect(item.setEnabled)

        if scheme_link.is_dynamic():
            scheme_link.dynamic_enabled_changed.disconnect(
                item.setDynamicEnabled
            )
        scheme_link.state_changed.disconnect(item.setRuntimeState)
        self.remove_link_item(item)

    def link_items(self):
        """
        Return all :class:`.LinkItem`\s in the scene.
        """
        return list(self.__link_items)

    def add_annotation_item(self, annotation):
        """
        Add an :class:`.Annotation` item to the scene.
        """
        self.__annotation_items.append(annotation)
        self.addItem(annotation)
        self.annotation_added.emit(annotation)
        return annotation

    def add_annotation(self, scheme_annot):
        """
        Create a new item for :class:`SchemeAnnotation` and add it
        to the scene. If the `scheme_annot` is already in the scene do
        nothing and just return its item.

        """
        if scheme_annot in self.__item_for_annotation:
            # Already added
            return self.__item_for_annotation[scheme_annot]

        if isinstance(scheme_annot, scheme.SchemeTextAnnotation):
            item = items.TextAnnotation()
            item.setPlainText(scheme_annot.text)
            x, y, w, h = scheme_annot.rect
            item.setPos(x, y)
            item.resize(w, h)
            item.setTextInteractionFlags(Qt.TextEditorInteraction)

            font = font_from_dict(scheme_annot.font, item.font())
            item.setFont(font)
            scheme_annot.text_changed.connect(item.setPlainText)

        elif isinstance(scheme_annot, scheme.SchemeArrowAnnotation):
            item = items.ArrowAnnotation()
            start, end = scheme_annot.start_pos, scheme_annot.end_pos
            item.setLine(QLineF(QPointF(*start), QPointF(*end)))
            item.setColor(QColor(scheme_annot.color))

        scheme_annot.geometry_changed.connect(
            self.__on_scheme_annot_geometry_change
        )

        self.add_annotation_item(item)
        self.__item_for_annotation[scheme_annot] = item

        return item

    def remove_annotation_item(self, annotation):
        """
        Remove an :class:`.Annotation` instance from the scene.

        """
        self.__annotation_items.remove(annotation)
        self.removeItem(annotation)
        self.annotation_removed.emit(annotation)

    def remove_annotation(self, scheme_annotation):
        """
        Remove an :class:`.Annotation` instance that was previously added
        using :func:`add_anotation`.

        """
        item = self.__item_for_annotation.pop(scheme_annotation)

        scheme_annotation.geometry_changed.disconnect(
            self.__on_scheme_annot_geometry_change
        )

        if isinstance(scheme_annotation, scheme.SchemeTextAnnotation):
            scheme_annotation.text_changed.disconnect(
                item.setPlainText
            )

        self.remove_annotation_item(item)

    def annotation_items(self):
        """
        Return all :class:`.Annotation` items in the scene.
        """
        return self.__annotation_items

    def item_for_annotation(self, scheme_annotation):
        return self.__item_for_annotation[scheme_annotation]

    def annotation_for_item(self, item):
        rev = dict(reversed(item) \
                   for item in self.__item_for_annotation.items())
        return rev[item]

    def commit_scheme_node(self, node):
        """
        Commit the `node` into the scheme.
        """
        if not self.editable:
            raise Exception("Scheme not editable.")

        if node not in self.__item_for_node:
            raise ValueError("No 'NodeItem' for node.")

        item = self.__item_for_node[node]

        try:
            self.scheme.add_node(node)
        except Exception:
            log.error("An error occurred while committing node '%s'",
                      node, exc_info=True)
            # Cleanup (remove the node item)
            self.remove_node_item(item)
            raise

        log.info("Commited node '%s' from '%s' to '%s'" % \
                 (node, self, self.scheme))

    def commit_scheme_link(self, link):
        """
        Commit a scheme link.
        """
        if not self.editable:
            raise Exception("Scheme not editable")

        if link not in self.__item_for_link:
            raise ValueError("No 'LinkItem' for link.")

        self.scheme.add_link(link)
        log.info("Commited link '%s' from '%s' to '%s'" % \
                 (link, self, self.scheme))

    def node_for_item(self, item):
        """
        Return the `SchemeNode` for the `item`.
        """
        rev = dict([(v, k) for k, v in self.__item_for_node.items()])
        return rev[item]

    def item_for_node(self, node):
        """
        Return the :class:`NodeItem` instance for a :class:`SchemeNode`.
        """
        return self.__item_for_node[node]

    def link_for_item(self, item):
        """
        Return the `SchemeLink for `item` (:class:`LinkItem`).
        """
        rev = dict([(v, k) for k, v in self.__item_for_link.items()])
        return rev[item]

    def item_for_link(self, link):
        """
        Return the :class:`LinkItem` for a :class:`SchemeLink`
        """
        return self.__item_for_link[link]

    def selected_node_items(self):
        """
        Return the selected :class:`NodeItem`'s.
        """
        return [item for item in self.__node_items if item.isSelected()]

    def selected_annotation_items(self):
        """
        Return the selected :class:`Annotation`'s
        """
        return [item for item in self.__annotation_items if item.isSelected()]

    def node_links(self, node_item):
        """
        Return all links from the `node_item` (:class:`NodeItem`).
        """
        return self.node_output_links(node_item) + \
               self.node_input_links(node_item)

    def node_output_links(self, node_item):
        """
        Return a list of all output links from `node_item`.
        """
        return [link for link in self.__link_items
                if link.sourceItem == node_item]

    def node_input_links(self, node_item):
        """
        Return a list of all input links for `node_item`.
        """
        return [link for link in self.__link_items
                if link.sinkItem == node_item]

    def neighbor_nodes(self, node_item):
        """
        Return a list of `node_item`'s (class:`NodeItem`) neighbor nodes.
        """
        neighbors = list(map(attrgetter("sourceItem"),
                             self.node_input_links(node_item)))

        neighbors.extend(map(attrgetter("sinkItem"),
                             self.node_output_links(node_item)))
        return neighbors

    def on_widget_state_change(self, widget, state):
        pass

    def on_link_state_change(self, link, state):
        pass

    def on_scheme_change(self, ):
        pass

    def _on_position_change(self, item):
        # Invalidate the anchor point layout and schedule a layout.
        self.__anchor_layout.invalidateNode(item)

        self.node_item_position_changed.emit(item, item.pos())

    def __on_node_pos_changed(self, pos):
        node = self.sender()
        item = self.__item_for_node[node]
        item.setPos(*pos)

    def __on_scheme_annot_geometry_change(self):
        annot = self.sender()
        item = self.__item_for_annotation[annot]
        if isinstance(annot, scheme.SchemeTextAnnotation):
            item.setGeometry(QRectF(*annot.rect))
        elif isinstance(annot, scheme.SchemeArrowAnnotation):
            p1 = item.mapFromScene(QPointF(*annot.start_pos))
            p2 = item.mapFromScene(QPointF(*annot.end_pos))
            item.setLine(QLineF(p1, p2))
        else:
            pass

    def item_at(self, pos, type_or_tuple=None, buttons=0):
        """Return the item at `pos` that is an instance of the specified
        type (`type_or_tuple`). If `buttons` (`Qt.MouseButtons`) is given
        only return the item if it is the top level item that would
        accept any of the buttons (`QGraphicsItem.acceptedMouseButtons`).

        """
        rect = QRectF(pos, QSizeF(1, 1))
        items = self.items(rect)

        if buttons:
            items = itertools.dropwhile(
                lambda item: not item.acceptedMouseButtons() & buttons,
                items
            )
            items = list(items)[:1]

        if type_or_tuple:
            items = [i for i in items if isinstance(i, type_or_tuple)]

        return items[0] if items else None

    if PYQT_VERSION < 0x40900:
        # For QGraphicsObject subclasses items, itemAt ... return a
        # QGraphicsItem wrapper instance and not the actual class instance.
        def itemAt(self, *args, **kwargs):
            item = QGraphicsScene.itemAt(self, *args, **kwargs)
            return toGraphicsObjectIfPossible(item)

        def items(self, *args, **kwargs):
            items = QGraphicsScene.items(self, *args, **kwargs)
            return list(map(toGraphicsObjectIfPossible, items))

        def selectedItems(self, *args, **kwargs):
            return list(map(toGraphicsObjectIfPossible,
                       QGraphicsScene.selectedItems(self, *args, **kwargs)))

        def collidingItems(self, *args, **kwargs):
            return list(map(toGraphicsObjectIfPossible,
                       QGraphicsScene.collidingItems(self, *args, **kwargs)))

        def focusItem(self, *args, **kwargs):
            item = QGraphicsScene.focusItem(self, *args, **kwargs)
            return toGraphicsObjectIfPossible(item)

        def mouseGrabberItem(self, *args, **kwargs):
            item = QGraphicsScene.mouseGrabberItem(self, *args, **kwargs)
            return toGraphicsObjectIfPossible(item)

    def mousePressEvent(self, event):
        if self.user_interaction_handler and \
                self.user_interaction_handler.mousePressEvent(event):
            return

        # Right (context) click on the node item. If the widget is not
        # in the current selection then select the widget (only the widget).
        # Else simply return and let customContextMenuReqested signal
        # handle it
        shape_item = self.item_at(event.scenePos(), items.NodeItem)
        if shape_item and event.button() == Qt.RightButton and \
                shape_item.flags() & QGraphicsItem.ItemIsSelectable:
            if not shape_item.isSelected():
                self.clearSelection()
                shape_item.setSelected(True)

        return QGraphicsScene.mousePressEvent(self, event)

    def mouseMoveEvent(self, event):
        if self.user_interaction_handler and \
                self.user_interaction_handler.mouseMoveEvent(event):
            return

        return QGraphicsScene.mouseMoveEvent(self, event)

    def mouseReleaseEvent(self, event):
        if self.user_interaction_handler and \
                self.user_interaction_handler.mouseReleaseEvent(event):
            return
        return QGraphicsScene.mouseReleaseEvent(self, event)

    def mouseDoubleClickEvent(self, event):
        if self.user_interaction_handler and \
                self.user_interaction_handler.mouseDoubleClickEvent(event):
            return

        return QGraphicsScene.mouseDoubleClickEvent(self, event)

    def keyPressEvent(self, event):
        if self.user_interaction_handler and \
                self.user_interaction_handler.keyPressEvent(event):
            return
        return QGraphicsScene.keyPressEvent(self, event)

    def keyReleaseEvent(self, event):
        if self.user_interaction_handler and \
                self.user_interaction_handler.keyReleaseEvent(event):
            return
        return QGraphicsScene.keyReleaseEvent(self, event)

    def set_user_interaction_handler(self, handler):
        if self.user_interaction_handler and \
                not self.user_interaction_handler.isFinished():
            self.user_interaction_handler.cancel()

        log.info("Setting interaction '%s' to '%s'" % (handler, self))

        self.user_interaction_handler = handler
        if handler:
            handler.start()

    def event(self, event):
        # TODO: change the base class of Node/LinkItem to QGraphicsWidget.
        # It already handles font changes.
        if event.type() == QEvent.FontChange:
            self.__update_font()

        return QGraphicsScene.event(self, event)

    def __update_font(self):
        font = self.font()
        for item in self.__node_items + self.__link_items:
            item.setFont(font)

    def __str__(self):
        return "%s(objectName=%r, ...)" % \
                (type(self).__name__, str(self.objectName()))
예제 #23
0
class ExportToPdfForm(QtGui.QWidget):

    stop_signal = pyqtSignal()

    def __init__(self):
        QtGui.QWidget.__init__(self)

        self._log = logging.getLogger(ConfigStatic.logger_name)
        self._log.info('passed')

        self.ui = Ui_ExportToPdf()
        self.ui.setupUi(self)

        self.reportlab_imported = REPORTLAB_IMPORTED
        self.cprint = Print()

        self.btnSave = self.ui.buttonBox.button(Qt.QDialogButtonBox.Save)
        self.btnCancel = self.ui.buttonBox.button(Qt.QDialogButtonBox.Cancel)

        # self.ui.rbPaperEU.clicked.connect(self.slotik)
        # self.ui.rbPaperUS.clicked.connect(self.slotik)

        self.signal_mapper = QSignalMapper(self)

        # self.connect(self.ui.rbPaperEU, QtCore.SIGNAL("clicked()"), signal_mapper, QtCore.SLOT("map()"))
        # self.connect(self.ui.rbPaperEU, QtCore.SIGNAL("clicked()"), self.signal_mapper, QtCore.SLOT("map()"))

        self.signal_mapper.setMapping(self.ui.rbPaperEU, EnumPaperFormat.EU)
        self.signal_mapper.setMapping(self.ui.rbPaperUS, EnumPaperFormat.US)

        self.ui.rbPaperEU.clicked.connect(self.signal_mapper.map)
        self.ui.rbPaperUS.clicked.connect(self.signal_mapper.map)

        self.signal_mapper.mapped[QtCore.QString].connect(
            self.slot_paper_format)

        # self.signal_mapper.mapped.connect(self.slot_paper_format)
        # self.signal_mapper.connect(QtCore.SIGNAL("mapped()"), QtCore.SLOT("clicked()"))

        # connection
        self.ui.btnBrowse.clicked.connect(self._open_dir_source)
        self.ui.btnSelectAll.clicked.connect(self._select_all)
        self.ui.btnUnselect.clicked.connect(self._unselect_all)
        self.btnSave.clicked.connect(self.save)
        self.btnCancel.clicked.connect(self.close)
        self.ui.lnFileName.textChanged.connect(self.line_edit_changed)

        self.ui.lnFileName.setText(os.path.expanduser("~") + os.path.sep)

        self._progress_dialog = QtGui.QProgressDialog()
        self._progress_dialog.setLabelText("Exporting CTG file to pdf ...")
        self._progress_dialog.setWindowModality(QtCore.Qt.WindowModal)
        self._progress_dialog.close()

        self._progress_dialog.canceled.connect(self._stop_progress)
        self.cprint.nr_pages_processed.connect(self._update_progress)

        self.msgBoxError = QtGui.QMessageBox()
        self.msgBoxError.setText("The output file name is empy!")
        self.msgBoxError.setDefaultButton(QtGui.QMessageBox.Close)
        self.msgBoxError.setIcon(QtGui.QMessageBox.Critical)

        self.msgbox_unsaved = QtGui.QMessageBox()
        self.msgbox_unsaved.setText("The file already exists.")
        self.msgbox_unsaved.setInformativeText("Overwrite?")
        self.msgbox_unsaved.setStandardButtons(QtGui.QMessageBox.Ok
                                               | QtGui.QMessageBox.Cancel)
        self.msgbox_unsaved.setDefaultButton(QtGui.QMessageBox.Ok)
        self.msgbox_unsaved.setIcon(QtGui.QMessageBox.Warning)

        e = EnumAnnType
        self._dict_ann = {
            self.ui.cbBasal: e.basal,
            self.ui.cbBaseline: e.baseline,
            self.ui.cbRecovery: e.recovery,
            self.ui.cbNoRecovery: e.no_recovery,
            self.ui.cbExcessiveUA: e.excessive_ua,
            self.ui.cbNote: e.note,
            self.ui.cbMark: e.ellipsenote,
            self.ui.cbFloatingBaseline: e.floating_baseline,
            self.ui.cbAccel: e.acceleration,
            self.ui.cbDecel: e.deceleration,
            self.ui.cbUA: e.uterine_contraction
        }

        self.ui.lbStatus.setText("(saving to pdf might take a while)")

    @pyqtSlot(QtCore.QString)
    def slot_paper_format(self, sformat):
        # print sformat
        self.set_paper_format(sformat)

    def _open_dir_source(self):

        dir1, dummy = os.path.split(str(self.ui.lnFileName.text()))
        file_name = QtGui.QFileDialog.getSaveFileName(
            self, 'Select a filename for exporting', dir1)

        if file_name == "":
            return -1
        else:
            file_name = str(file_name)
            self.ui.lnFileName.setText(file_name)

        return 0

    def line_edit_changed(self):
        self.btnSave.setEnabled(True)

    def get_file_to_save(self):
        """
        Split the path, check if file exist.
        Default: Save to USER HOME directory
        """

        file_name = str(self.ui.lnFileName.text())
        path, file_name = os.path.split(file_name)

        if file_name == "":
            self.msgBoxError.exec_()
            return -1

        if not file_name.endswith(".pdf"):
            file_name += '.pdf'

        if path == "":
            file_name = os.path.join(os.path.expanduser('~'), file_name)
        else:
            file_name = os.path.join(path, file_name)

        return file_name

    def set_paper_format(self, pformat):
        """Interface method to the Print() class"""

        if pformat == EnumPaperFormat.EU:
            self.ui.rbPaperEU.setChecked(True)
            self.cprint.set_paper_format(pformat)
        else:
            self.ui.rbPaperUS.setChecked(True)
            self.cprint.set_paper_format(pformat)

    def set_signals(self, fhr, toco, fs, timestamp):
        """Interface method to the Print() class"""
        self.cprint.set_signals(fhr, toco, fs, timestamp)

    def set_annotations(self, ann):
        """Interface method to the Print() class"""
        self.cprint.set_annotations(ann)

    def set_record_name(self, s):
        """Interface method to the Print() class"""
        self.cprint.set_record_name(s)

    def save(self):

        file_name = self.get_file_to_save()

        if file_name == -1:
            return -1

        if os.path.exists(file_name):
            if self.msgbox_unsaved.exec_() == QtGui.QMessageBox.Cancel:
                return

        self.btnSave.setEnabled(False)
        self.btnCancel.setEnabled(False)
        App.processEvents()

        try:
            self._progress_dialog.close()
            self._progress_dialog.setRange(
                0, self.cprint.number_of_pages_to_print())
            self._progress_dialog.setValue(0)
            self._progress_dialog.open()

            self.cprint.set_ann_to_print(self._get_ann_to_print())
            self.cprint.set_file_name(file_name)
            self.cprint.print_()

        except IOError, e:
            self._progress_dialog.close()
            self.msgBoxError.setText(str(e))
            self.msgBoxError.exec_()
            self.btnSave.setEnabled(True)
            self.btnCancel.setEnabled(True)
            return -1

        self._progress_dialog.close()
        s = "PDF saved to: " + file_name
        self.ui.lbStatus.setText(s)
        self._log.info(s)
        App.processEvents()

        time.sleep(2)

        self.ui.lbStatus.setText("")
        self.btnSave.setEnabled(True)
        self.btnCancel.setEnabled(True)
        self.close()
예제 #24
0
class ToolGrid(QFrame):
    """
    A widget containing a grid of actions/buttons.

    Actions can be added using standard :ref:`QWidget.addAction(QAction)`
    and :ref:`QWidget.insertAction(int, QAction)` methods.

    Parameters
    ----------
    parent : :class:`QWidget`
        Parent widget.
    columns : int
        Number of columns in the grid layout.
    buttonSize : :class:`QSize`, optional
        Size of tool buttons in the grid.
    iconSize : :class:`QSize`, optional
        Size of icons in the buttons.
    toolButtonStyle : :class:`Qt.ToolButtonStyle`
        Tool button style.

    """

    actionTriggered = Signal(QAction)
    actionHovered = Signal(QAction)

    def __init__(self, parent=None, columns=4, buttonSize=None,
                 iconSize=None, toolButtonStyle=Qt.ToolButtonTextUnderIcon):
        QFrame.__init__(self, parent)

        if buttonSize is not None:
            buttonSize = QSize(buttonSize)

        if iconSize is not None:
            iconSize = QSize(iconSize)

        self.__columns = columns
        self.__buttonSize = buttonSize or QSize(50, 50)
        self.__iconSize = iconSize or QSize(26, 26)
        self.__toolButtonStyle = toolButtonStyle

        self.__gridSlots = []

        self.__buttonListener = ToolButtonEventListener(self)
        self.__buttonListener.buttonRightClicked.connect(
                self.__onButtonRightClick)

        self.__buttonListener.buttonEnter.connect(
                self.__onButtonEnter)

        self.__mapper = QSignalMapper()
        self.__mapper.mapped[QObject].connect(self.__onClicked)

        self.__setupUi()

    def __setupUi(self):
        layout = QGridLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.setSizeConstraint(QGridLayout.SetFixedSize)
        self.setLayout(layout)
        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding)

    def setButtonSize(self, size):
        """
        Set the button size.
        """
        if self.__buttonSize != size:
            self.__buttonSize = size
            for slot in self.__gridSlots:
                slot.button.setFixedSize(size)

    def buttonSize(self):
        """
        Return the button size.
        """
        return QSize(self.__buttonSize)

    def setIconSize(self, size):
        """
        Set the button icon size.
        """
        if self.__iconSize != size:
            self.__iconSize = size
            for slot in self.__gridSlots:
                slot.button.setIconSize(size)

    def iconSize(self):
        """
        Return the icon size
        """
        return QSize(self.__iconSize)

    def setToolButtonStyle(self, style):
        """
        Set the tool button style.
        """
        if self.__toolButtonStyle != style:
            self.__toolButtonStyle = style
            for slot in self.__gridSlots:
                slot.button.setToolButtonStyle(style)

    def toolButtonStyle(self):
        """
        Return the tool button style.
        """
        return self.__toolButtonStyle

    def setColumnCount(self, columns):
        """
        Set the number of button/action columns.
        """
        if self.__columns != columns:
            self.__columns = columns
            self.__relayout()

    def columns(self):
        """
        Return the number of columns in the grid.
        """
        return self.__columns

    def clear(self):
        """
        Clear all actions/buttons.
        """
        for slot in reversed(list(self.__gridSlots)):
            self.removeAction(slot.action)
        self.__gridSlots = []

    def insertAction(self, before, action):
        """
        Insert a new action at the position currently occupied
        by `before` (can also be an index).

        Parameters
        ----------
        before : :class:`QAction` or int
            Position where the `action` should be inserted.
        action : :class:`QAction`
            Action to insert

        """
        if isinstance(before, int):
            actions = list(self.actions())
            if len(actions) == 0 or before >= len(actions):
                # Insert as the first action or the last action.
                return self.addAction(action)

            before = actions[before]

        return QFrame.insertAction(self, before, action)

    def setActions(self, actions):
        """
        Clear the grid and add `actions`.
        """
        self.clear()

        for action in actions:
            self.addAction(action)

    def buttonForAction(self, action):
        """
        Return the :class:`QToolButton` instance button for `action`.
        """
        actions = [slot.action for slot in self.__gridSlots]
        index = actions.index(action)
        return self.__gridSlots[index].button

    def createButtonForAction(self, action):
        """
        Create and return a :class:`QToolButton` for action.
        """
        button = _ToolGridButton(self)
        button.setDefaultAction(action)

        if self.__buttonSize.isValid():
            button.setFixedSize(self.__buttonSize)
        if self.__iconSize.isValid():
            button.setIconSize(self.__iconSize)

        button.setToolButtonStyle(self.__toolButtonStyle)
        button.setProperty("tool-grid-button", qtcompat.qwrap(True))
        return button

    def count(self):
        """
        Return the number of buttons/actions in the grid.
        """
        return len(self.__gridSlots)

    def actionEvent(self, event):
        QFrame.actionEvent(self, event)

        if event.type() == QEvent.ActionAdded:
            # Note: the action is already in the self.actions() list.
            actions = list(self.actions())
            index = actions.index(event.action())
            self.__insertActionButton(index, event.action())

        elif event.type() == QEvent.ActionRemoved:
            self.__removeActionButton(event.action())

    def __insertActionButton(self, index, action):
        """Create a button for the action and add it to the layout
        at index.

        """
        self.__shiftGrid(index, 1)
        button = self.createButtonForAction(action)

        row = index / self.__columns
        column = index % self.__columns

        self.layout().addWidget(
            button, row, column,
            Qt.AlignLeft | Qt.AlignTop
        )

        self.__gridSlots.insert(
            index, _ToolGridSlot(button, action, row, column)
        )

        self.__mapper.setMapping(button, action)
        button.clicked.connect(self.__mapper.map)
        button.installEventFilter(self.__buttonListener)
        button.installEventFilter(self)

    def __removeActionButton(self, action):
        """Remove the button for the action from the layout and delete it.
        """
        actions = [slot.action for slot in self.__gridSlots]
        index = actions.index(action)
        slot = self.__gridSlots.pop(index)

        slot.button.removeEventFilter(self.__buttonListener)
        slot.button.removeEventFilter(self)
        self.__mapper.removeMappings(slot.button)

        self.layout().removeWidget(slot.button)
        self.__shiftGrid(index + 1, -1)

        slot.button.deleteLater()

    def __shiftGrid(self, start, count=1):
        """Shift all buttons starting at index `start` by `count` cells.
        """
        button_count = self.layout().count()
        direction = 1 if count >= 0 else -1
        if direction == 1:
            start, end = button_count - 1, start - 1
        else:
            start, end = start, button_count

        for index in range(start, end, -direction):
            item = self.layout().itemAtPosition(index / self.__columns,
                                                index % self.__columns)
            if item:
                button = item.widget()
                new_index = index + count
                self.layout().addWidget(button, new_index / self.__columns,
                                        new_index % self.__columns,
                                        Qt.AlignLeft | Qt.AlignTop)

    def __relayout(self):
        """Relayout the buttons.
        """
        for i in reversed(range(self.layout().count())):
            self.layout().takeAt(i)

        self.__gridSlots = [_ToolGridSlot(slot.button, slot.action,
                                          i / self.__columns,
                                          i % self.__columns)
                            for i, slot in enumerate(self.__gridSlots)]

        for slot in self.__gridSlots:
            self.layout().addWidget(slot.button, slot.row, slot.column,
                                    Qt.AlignLeft | Qt.AlignTop)

    def __indexOf(self, button):
        """Return the index of button widget.
        """
        buttons = [slot.button for slot in self.__gridSlots]
        return buttons.index(button)

    def __onButtonRightClick(self, button):
        pass

    def __onButtonEnter(self, button):
        action = button.defaultAction()
        self.actionHovered.emit(action)

    def __onClicked(self, action):
        self.actionTriggered.emit(action)

    def paintEvent(self, event):
        return utils.StyledWidget_paintEvent(self, event)

    def eventFilter(self, obj, event):
        etype = event.type()
        if etype == QEvent.KeyPress and obj.hasFocus():
            key = event.key()
            if key in [Qt.Key_Up, Qt.Key_Down, Qt.Key_Left, Qt.Key_Right]:
                if self.__focusMove(obj, key):
                    event.accept()
                    return True

        return QFrame.eventFilter(self, obj, event)

    def __focusMove(self, focus, key):
        assert(focus is self.focusWidget())
        try:
            index = self.__indexOf(focus)
        except IndexError:
            return False

        if key == Qt.Key_Down:
            index += self.__columns
        elif key == Qt.Key_Up:
            index -= self.__columns
        elif key == Qt.Key_Left:
            index -= 1
        elif key == Qt.Key_Right:
            index += 1

        if index >= 0 and index < self.count():
            button = self.__gridSlots[index].button
            button.setFocus(Qt.TabFocusReason)
            return True
        else:
            return False
 def contextMenuEvent(self, event):
   menu     = QtGui.QMenu()
   last_pos = event.pos()
   cursor   = self.cursorForPosition(last_pos)
   pos      = cursor.positionInBlock()
   line     = cursor.blockNumber()
   
   word = self.error_at_pos(line, pos)
   
   if not word == None:
     suggestions = self.highlighter.checker.suggest(word[0])
     
     if len(suggestions) > 0:
       suggestion_mapper = QSignalMapper(self)
       
       actions = []
       
       # Generate our suggestions, packing all the data we need to make the replacement.
       for i, suggestion in enumerate(suggestions):
         if i == MAX_SUGGESTIONS:
           break
         
         actions.append(QtGui.QAction(suggestion, None))
         
         # We can only send strings with the signal mapper, so pickle our data.
         data = pickle.dumps((word[0], line, word[1], suggestion))
         data = QtCore.QString.fromAscii(data)
         
         self.connect(actions[-1], QtCore.SIGNAL("triggered()"), suggestion_mapper, QtCore.SLOT("map()"))
         suggestion_mapper.setMapping(actions[-1], data)
       
       self.connect(suggestion_mapper, QtCore.SIGNAL("mapped(QString)"), self.__replace)
       menu.addActions(actions)
     else:
       action = QtGui.QAction("(No spelling suggestions)", None)
       action.setDisabled(True)
       menu.addAction(action)
       
     menu.addSeparator()
     
     add_mapper    = QSignalMapper(self)
     ignore_mapper = QSignalMapper(self)
     add_action = QtGui.QAction("Add to Dictionary", None)
     ignore_action = QtGui.QAction("Ignore All", None)
     
     self.connect(add_action, QtCore.SIGNAL("triggered()"), add_mapper, QtCore.SLOT("map()"))
     add_mapper.setMapping(add_action, word[0])
     self.connect(add_mapper, QtCore.SIGNAL("mapped(QString)"), self.__add)
     
     self.connect(ignore_action, QtCore.SIGNAL("triggered()"), ignore_mapper, QtCore.SLOT("map()"))
     ignore_mapper.setMapping(ignore_action, word[0])
     self.connect(ignore_mapper, QtCore.SIGNAL("mapped(QString)"), self.__ignore)
     
     menu.addAction(add_action)
     menu.addAction(ignore_action)
     menu.addSeparator()
   
   default_menu = self.createStandardContextMenu()
   menu.addActions(default_menu.actions())
   
   menu.exec_(event.globalPos())
예제 #26
0
class DocumentViewManager(QMainWindow):
    """
    MDI area for displaying supporting documents within a given context e.g.
    supporting documents for a specific household based on the lifetime of the
    'SourceDocumentManager' instance.
    """
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.setWindowFlags(Qt.Window)

        self._mdi_area = QMdiArea()
        self._mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self._mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.setCentralWidget(self._mdi_area)
        # set the size of mid_area and DocumentViewManager based on the
        # screen size.
        screen = QDesktopWidget().availableGeometry()
        self._mdi_area.resize(screen.width() - 30, screen.height() - 80)
        self.resize(self._mdi_area.size())
        self._mdi_area.subWindowActivated.connect(self.update_actions)
        self._viewer_mapper = QSignalMapper(self)
        self._viewer_mapper.mapped[QWidget].connect(self.set_active_sub_window)

        win_title = QApplication.translate(
            "DocumentViewManager",
            "Document Viewer"
        )
        self.setWindowTitle(win_title)
        self.setUnifiedTitleAndToolBarOnMac(True)
        self.statusBar().showMessage(
            QApplication.translate(
                "DocumentViewManager",
                "Ready"
            )
        )
        self._doc_viewers = {}

        self._create_menu_actions()
        self.update_actions()

    def center(self):
        """
        Move the Document viewer to the center of the screen.
        """
        # Get the current screens' dimensions...
        screen = QDesktopWidget().availableGeometry()
        # ... and get this windows' dimensions
        mdi_area_size = self.frameGeometry()
        # The horizontal position
        hpos = (screen.width() - mdi_area_size.width()) / 2
        # vertical position
        vpos = (screen.height() - mdi_area_size.height()) / 2
        # repositions the window
        self.move(hpos, vpos)

    def _create_menu_actions(self):
        self._window_menu = self.menuBar().addMenu(
            QApplication.translate(
                "DocumentViewManager","&Windows"))

        self._close_act = QAction(
            QApplication.translate("DocumentViewManager",
                                                         "Cl&ose"), self)
        self._close_act.setStatusTip(
            QApplication.translate("DocumentViewManager",
                                   "Close the active document viewer"))
        self._close_act.triggered.connect(self._mdi_area.closeActiveSubWindow)

        self._close_all_act = QAction(QApplication.translate(
            "DocumentViewManager",
            "Close &All"), self
        )
        self._close_all_act.setStatusTip(
            QApplication.translate("DocumentViewManager",
                                   "Close all the document viewers")
        )
        self._close_all_act.triggered.connect(
            self._mdi_area.closeAllSubWindows
        )

        self._tile_act = QAction(QApplication.translate(
            "DocumentViewManager",
            "&Tile"), self
        )
        self._tile_act.setStatusTip(
            QApplication.translate("DocumentViewManager",
                                   "Tile the document viewers"))
        self._tile_act.triggered.connect(self.tile_windows)

        self._cascade_act = QAction(QApplication.translate(
            "DocumentViewManager",
            "&Cascade"), self)
        self._cascade_act.setStatusTip(QApplication.translate(
            "DocumentViewManager",
            "Cascade the document viewers"))
        self._cascade_act.triggered.connect(self.cascade_windows)

        self._next_act = QAction(QApplication.translate(
            "DocumentViewManager",
            "Ne&xt"), self)
        self._next_act.setStatusTip(
            QApplication.translate(
                "DocumentViewManager",
                "Move the focus to the next document viewer"
            )
        )
        self._next_act.triggered.connect(self._mdi_area.activateNextSubWindow)

        self._previous_act = QAction(QApplication.translate(
            "DocumentViewManager",
            "Pre&vious"), self)
        self._previous_act.setStatusTip(
            QApplication.translate(
                "DocumentViewManager",
                "Move the focus to the previous document viewer"
            )
        )
        self._previous_act.triggered.connect(
            self._mdi_area.activatePreviousSubWindow
        )

        self._separator_act = QAction(self)
        self._separator_act.setSeparator(True)

        self.update_window_menu()
        self._window_menu.aboutToShow.connect(self.update_window_menu)

    def cascade_windows(self):
        #Cascade document windows
        self._mdi_area.cascadeSubWindows()

    def tile_windows(self):
        #Arrange document windows to occupy the available space in mdi area
        self._mdi_area.tileSubWindows()

    def update_actions(self):
        if self._mdi_area.activeSubWindow():
            has_mdi_child = True

        else:
            has_mdi_child = False

        self._close_act.setEnabled(has_mdi_child)
        self._close_all_act.setEnabled(has_mdi_child)
        self._tile_act.setEnabled(has_mdi_child)
        self._cascade_act.setEnabled(has_mdi_child)
        self._previous_act.setEnabled(has_mdi_child)
        self._next_act.setEnabled(has_mdi_child)
        self._separator_act.setVisible(has_mdi_child)

    def update_window_menu(self):
        self._window_menu.clear()
        self._window_menu.addAction(self._close_act)
        self._window_menu.addAction(self._close_all_act)
        self._window_menu.addSeparator()
        self._window_menu.addAction(self._tile_act)
        self._window_menu.addAction(self._cascade_act)
        self._window_menu.addSeparator()
        self._window_menu.addAction(self._next_act)
        self._window_menu.addAction(self._previous_act)
        self._window_menu.addAction(self._separator_act)

        windows = self._mdi_area.subWindowList()
        self._separator_act.setVisible(len(windows) != 0)

        for i, window in enumerate(windows):
            text = "%d. %s" % (i + 1, window.windowTitle())

            win_action = self._window_menu.addAction(text)
            win_action.setCheckable(True)
            win_action.setChecked(window is self._mdi_area.activeSubWindow())
            win_action.triggered.connect(self._viewer_mapper.map)
            self._viewer_mapper.setMapping(win_action, window)

    def load_viewer(self, document_widget, visible=True):
        """
        Open a new instance of the viewer or activate an existing one if the
        document had been previously loaded.
        :param document_widget: Contains all the necessary information required
        to load the specific document.
        :type document_widget: DocumentWidget
        :param visible: True to show the view manager after the viewer has
        been loaded, otherwise it will be the responsibility of the caller to
        enable visibility.
        :type visible: bool
        :returns: True if the document was successfully loaded, else False.
        :rtype: bool
        """
        doc_identifier = document_widget.file_identifier()

        if doc_identifier in self._doc_viewers:

            doc_sw = self._doc_viewers[doc_identifier]

            self._mdi_area.setActiveSubWindow(doc_sw)
            doc_sw.showNormal()

        else:
            doc_viewer = self._create_viewer(document_widget)

            abs_doc_path = self.absolute_document_path(document_widget)

            if not QFile.exists(abs_doc_path):
                msg = QApplication.translate(
                    "DocumentViewManager",
                    "The selected document does not exist."
                    "\nPlease check the supporting documents' "
                    "repository setting."
                )
                QMessageBox.critical(
                    self,
                    QApplication.translate(
                        "DocumentViewManager","Invalid Document"
                    ),
                    msg
                )

                return False

            doc_viewer.load_document(abs_doc_path)

            self._doc_viewers[doc_identifier] = doc_viewer

            self._mdi_area.addSubWindow(doc_viewer)

            doc_viewer.show()

        if not self.isVisible() and visible:
            self.setVisible(True)

        if self.isMinimized():
            self.showNormal()

        self.center()

        return True

    def set_active_sub_window(self, viewer):
        if viewer:
            self._mdi_area.setActiveSubWindow(viewer)

    def absolute_document_path(self, document_widget):
        """
        Build the absolute document path using info from the document widget.
        :param document_widget: Instance of document widget.
        :return: Absolute path of the supporting document.
        :rtype: str
        """
        abs_path = ''

        file_manager = document_widget.fileManager
        if not file_manager is None:
            network_repository = file_manager.networkPath
            file_id = document_widget.file_identifier()
            source_entity = document_widget.doc_source_entity()
            profile_name = current_profile().name
            doc_type = document_widget.doc_type_value().lower().replace(
                ' ', '_'
            )
            file_name, file_extension = guess_extension(
                document_widget.displayName()
            )

            abs_path = network_repository + "/" +profile_name + '/' +\
                       unicode(source_entity) + "/" + unicode(doc_type) + "/" +\
                       unicode(file_id) + unicode(file_extension)

        return abs_path

    def reset(self):
        """
        Removes all document viewers in the view area.
        The QCloseEvent sent to each sub-window will decrement the register.
        """
        self._mdi_area.closeAllSubWindows()

    def _create_viewer(self, document_widget):
        """
        Creates a new instance of a document viewer.
        :param document_widget: Contains all
        the necessary information required
        to load the specific document.
        :return: Document viewer object
        :rtype: DocumentViewer
        """
        doc_viewer = DocumentViewer(
            self._mdi_area, document_widget.file_identifier()
        )
        doc_viewer.setAttribute(Qt.WA_DeleteOnClose)
        doc_viewer.setWindowTitle(
            document_widget.displayName()
        )

        # TODO: Incorporate logic for determining
        # TODO: viewer based on document type
        ph_viewer = PhotoViewer()

        # v_layout = QVBoxLayout()
        # v_layout.addWidget(ph_viewer)
        # doc_viewer.setLayout(v_layout)

        doc_viewer.set_view_widget(ph_viewer)

        doc_viewer.closed.connect(self._on_viewer_closed)

        return doc_viewer

    def remove_viewer(self, viewer_id):
        """
        Close and remove the viewer with the specified viewer ID.
        """
        if viewer_id in self._doc_viewers:
            viewer = self._doc_viewers[viewer_id]
            self._mdi_area.setActiveSubWindow(viewer)
            self._mdi_area.closeActiveSubWindow()

        self._on_viewer_closed(viewer_id)

    def _on_viewer_closed(self,file_id):
        """
        Slot raised when a document viewer is closed.
        """
        if file_id in self._doc_viewers:
            del self._doc_viewers[file_id]
예제 #27
0
파일: K800iRecover.py 프로젝트: kzwkt/dff
class K800IRec(QWidget, mfso):
    def __init__(self):
        mfso.__init__(self, "K800i-Recover")
        self.name = "K800i-Recover"
        self.icon = None
        self.__disown__()

    def start(self, args):
        self.vfs = vfs.vfs()
        self.dumpnumber = 1
        try:
            self.nor = args['nor'].value()
            self.nand = args['nand'].value()
        except IndexError:
            return
        try:
            self.spareSize = args["spare-size"].value()
        except IndexError:
            self.spareSize = 16
        try:
            self.pageSize = args["page-size"].value()
        except IndexError:
            self.pageSize = 512
        self.k800n = Node("k800-base")
        self.k800n.__disown__()
        self.boot = SEBootBlock(self.nor, self.pageSize)
        self.blockSize = self.boot.blockSize
        self.nandClean = SpareNode(self, self.nand, "nandfs", self.pageSize,
                                   self.spareSize, self.k800n)
        self.norFs = NorFs(self, self.k800n, self.nor, "norfs", self.boot)
        self.fullFs = FullFs(self, self.k800n, self.norFs, self.nandClean,
                             "fullfs", self.boot)
        self.gdfs = GDFS(self, self.k800n, self.nor, "gdfs", self.boot)
        self.firmware = Firmware(self, self.k800n, self.nor, "firmware",
                                 self.boot.norfsoffset)

        self.tables = Tables(self.fullFs, self.blockSize)
        self.registerTree(self.nand, self.k800n)

    def createDump(self):
        text, ok = QInputDialog.getText(self, "Create dump", "dump name:",
                                        QLineEdit.Normal,
                                        "k800-restore-" + str(self.dumpnumber))
        if ok and text != "":
            if (self.vfs.getnode(self.nand.absolute() + "/" +
                                 str(text)) == None):
                self.dumpnumber += 1
                newroot = Node(str(text))
                newroot.__disown__()
                for id in range(0, len(self.tables.tablesIdWriteMap) - 1):
                    write = int(
                        str(self.gtable.cellWidget(id, 0).currentText()), 16)
                    self.tables.map[id] = self.tables.tablesIdWriteMap[id][
                        write]
                virtual = VirtualMap(self, newroot, self.fullFs, self.tables,
                                     "virtual", self.blockSize)
                separt = SEPartitionBlock(virtual, self.boot.partitionblock,
                                          self.blockSize)
                self.createPart(separt, newroot, virtual)
                self.registerTree(self.nand, newroot)
            else:
                box = QMessageBox(QMessageBox.Warning, "Error",
                                  "Error node already exists",
                                  QMessageBox.NoButton, self)
                box.exec_()
                self.createDump()

    def createPart(self, separt, newroot, virtual):
        for part in separt.partTable:
            if part.start > 0:
                p = Partition(self, newroot, virtual, part, self.blockSize)

    def g_display(self):
        QWidget.__init__(self, None)
        self.layout = QVBoxLayout(self)
        self.hlayout = QSplitter(self)
        self.layout.insertWidget(0, self.hlayout)
        self.layout.setStretchFactor(self.hlayout, 1)
        self.gTable()
        self.viewTable()

        self.button = QPushButton("&Create dump")
        self.connect(self.button, SIGNAL("clicked()"), self.createDump)
        self.layout.addWidget(self.button)

    def viewTable(self):
        self.vtable = QTableWidget()
        self.vtable.setColumnCount(20)
        self.vtable.setRowCount(48)
        self.hlayout.addWidget(self.vtable)

    def viewTableUpdate(self, id):
        write = int(str(self.gtable.cellWidget(id, 0).currentText()), 16)
        t = self.tables.tablesIdWriteMap[id][write]
        l = t.blockList
        for x in xrange(0, len(t.blockList[0])):
            block = t.blockList[0][x]
            c = ((x) % 20)
            r = ((x) / 20)
            item = QTableWidgetItem(QString(hex(block)))
            tipBlock = (id * 960) + x
            item.setToolTip(QString(hex(tipBlock)))
            item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.vtable.setItem(r, c, item)

    def gTable(self):
        self.gtable = QTableWidget()
        self.gtable.setColumnCount(1)
        self.gtable.setRowCount(len(self.tables.tablesIdWriteMap))
        self.gtable.setHorizontalHeaderItem(
            0, QTableWidgetItem(QString("version")))
        self.hlayout.addWidget(self.gtable)
        self.sigMapper = QSignalMapper(self)
        for id in self.tables.tablesIdWriteMap:
            wlist = self.tables.tablesIdWriteMap[id]
            cbox = QComboBox(self.gtable)
            self.connect(cbox, SIGNAL("activated(QString)"), self.sigMapper,
                         SLOT("map()"))
            self.sigMapper.setMapping(cbox, id)
            l = []
            for write in wlist:
                l.append(write)
            l.sort()
            l.reverse()
            for write in l:
                cbox.addItem(QString(hex(write)))
            self.gtable.setCellWidget(id, 0, cbox)
            self.gtable.setVerticalHeaderItem(
                id, QTableWidgetItem(QString(hex(id))))
        self.connect(self.sigMapper, SIGNAL("mapped(int)"),
                     self.viewTableUpdate)
        self.gtable.setMaximumWidth(
            self.gtable.columnWidth(0) +
            self.gtable.verticalHeader().sectionSize(0) + 30)

    def updateWidget(self):
        pass
예제 #28
0
    def _create_connections(self):
        """ Creates connections in MainWindow """

        # TODO rewrite the connection to a modern style
        # Menu: File
        self.connect(self.ui.actionQuit, QtCore.SIGNAL('triggered()'),
                     QtCore.SLOT('close()'))
        self.connect(self.ui.actionOpen, QtCore.SIGNAL('triggered()'),
                     self._open_file)
        self.connect(self.ui.actionOpen_folder, QtCore.SIGNAL('triggered()'),
                     self._open_folder_dialog)

        # Menu: TOOLS
        self.connect(self.ui.actionConvert_files, QtCore.SIGNAL('triggered()'),
                     self._show_convert_files)
        self.connect(self.ui.actionDownload_CTU_UHB_data,
                     QtCore.SIGNAL('triggered()'), self._show_download_db)
        self.connect(self.ui.actionAttribute_selection,
                     QtCore.SIGNAL('triggered()'),
                     self._show_attribute_selection)
        self.connect(self.ui.actionExport_to_PDF, QtCore.SIGNAL('triggered()'),
                     self._export_to_pdf)
        # self.connect(self.ui.actionSet_Clear_Baseline, QtCore.SIGNAL('triggered()'), self._show_set_clear_bsln)
        self.connect(self.ui.actionSent_annotations,
                     QtCore.SIGNAL('triggered()'), self._show_sent_annotations)

        group_paper = QtGui.QActionGroup(self)
        self.ui.actionEU.setActionGroup(group_paper)
        self.ui.actionUS.setActionGroup(group_paper)
        self.connect(self.ui.actionEU, QtCore.SIGNAL('triggered()'),
                     self._set_paper)
        self.connect(self.ui.actionUS, QtCore.SIGNAL('triggered()'),
                     self._set_paper)
        ''' create dictionary in the following format: action: name '''
        e = EnumAnnType
        pui = self.ui
        dactions = {
            pui.actionCursor: e.select,
            pui.actionBasal: e.basal,
            pui.actionBaseline: e.baseline,
            pui.actionRecovery: e.recovery,
            pui.actionNo_recovery: e.no_recovery,
            pui.actionExcessive_UA: e.excessive_ua,
            pui.actionEllipse: e.ellipse,
            pui.actionEllipseNote: e.ellipsenote,
            pui.actionNote: e.note,
            pui.actionEvaluationNote: e.evaluation_note
        }

        if DEBUG_FIGO_ANN:
            dactions[pui.actionFloating_Baseline] = e.floating_baseline
            dactions[pui.actionAcceleration] = e.acceleration
            dactions[pui.actionDeceleration] = e.deceleration
            dactions[pui.actionUA] = e.uterine_contraction

        group_ann = QtGui.QActionGroup(self)
        for action in dactions.iterkeys():
            if isinstance(action, QtGui.QAction):  # just to check the type
                action.setActionGroup(group_ann)

        # self.ui.actionBasal.triggered.connect(self._debug_slot)
        # self.ui.actionBaseline.triggered.connect(self._set_annotation_action)

        signal_mapper = QSignalMapper(self)

        # signal_mapper.setMapping(self.ui.actionBaseline, 'baseline')
        # self.connect(self.ui.actionBaseline, QtCore.SIGNAL('triggered()'), signal_mapper.map)
        # self.connect(signal_mapper, QtCore.SIGNAL('mapped()'), self._set_annotation_action)

        # for d in dactions.iteritems():
        for action, name in dactions.iteritems():
            signal_mapper.setMapping(action, name)

            if isinstance(action, QtGui.QAction):  # just to check the type
                action.triggered.connect(signal_mapper.map)
                # self.connect(action, QtCore.SIGNAL('triggered()'), signal_mapper.map)

        signal_mapper.mapped[QtCore.QString].connect(self._ann_set_action)
        # self.connect(self.ui.actionUS, QtCore.SIGNAL('triggered()'), self._set_paper)
        # self.connect(signal_mapper, QtCore.SIGNAL('mapped()'), self._set_annotation_action)

        # signal_mapper = QtCore.QSignalMapper(self)
        #
        # # for d in dactions.iteritems():
        # for action, name in dactions.iteritems():
        #     signal_mapper.setMapping(action, name)
        #
        #     if isinstance(action, QtGui.QAction):  # just to check the type
        #         # action.triggered.connect(signal_mapper.map)
        #         self.connect(action, QtCore.SIGNAL('triggered()'), signal_mapper.map)
        #
        # # signal_mapper.mapped[QtCore.QString].connect(self._set_annotation_action)
        # # self.connect(self.ui.actionUS, QtCore.SIGNAL('triggered()'), self._set_paper)
        # self.connect(signal_mapper, QtCore.SIGNAL('mapped()'), self._set_annotation_action)

        # self.ui.actionAnnShowHide.triggered.connect(self._show_ann_show_hide)
        self.connect(self.ui.actionSave, QtCore.SIGNAL('triggered()'),
                     self._toolbar_ann_save)
        self.connect(self.ui.actionDelete, QtCore.SIGNAL('triggered()'),
                     self._toolbar_ann_delete)

        self.ui.actionCaliper.triggered.connect(self._caliper_set)
        self.ui.actionCaliperFHR.triggered.connect(self._caliper_set)
        self.ui.actionCaliperTOCO.triggered.connect(self._caliper_set)

        self.ui.actionFIGO_acc_dec.triggered.connect(
            self._caliper_set_figo_acc_dec)
        self.ui.actionFIGO_UA.triggered.connect(self._caliper_set_figo_ua)
        self.ui.actionCaliperReset.triggered.connect(self._caliper_reset)

        # Menu: View
        self.connect(self.ui.actionClinical_information,
                     QtCore.SIGNAL('triggered()'), self._dock_clin_info_toggle)
        self.connect(self.ui.dockClinInfo,
                     QtCore.SIGNAL("visibilityChanged(bool)"),
                     self._dock_clin_info_visibility)
        self.connect(self.ui.actionData_browser, QtCore.SIGNAL('triggered()'),
                     self._dock_databrowse_toggle)
        self.connect(self.ui.dockDataBrowser,
                     QtCore.SIGNAL("visibilityChanged(bool)"),
                     self._dock_databrowse_visibility)
        self.connect(self.ui.actionAnnToolbarVisible,
                     QtCore.SIGNAL('triggered()'), self._toolbar_ann_toggle)
        self.connect(self.ui.toolBar, QtCore.SIGNAL('visibilityChanged(bool)'),
                     self._toolbar_ann_visibility)
        self.connect(self.ui.actionAnnToolbarAlign_right,
                     QtCore.SIGNAL('triggered()'), self._toolbar_align)

        # Menu: Help
        self.connect(self.ui.actionAbout, QtCore.SIGNAL('triggered()'),
                     self._show_about)

        # SIGNALS
        self.ui.PlotWidget.fhrPlot.signal_ann_changed.connect(
            self._toolbar_ann_changed)
        self.ui.PlotWidget.tocoPlot.signal_ann_changed.connect(
            self._toolbar_ann_changed)
        self._attSelectForm.signal_sel_att_changed.connect(
            self._update_data_browser)

        if DEBUG_TOOLS is True:
            self.ui.actionDebug_CalibSignal.triggered.connect(
                self.plot_calibration_signal)

            if self._dataBrowserWidget.bDebugStageIICorrect:
                self._dataBrowserWidget.debug_stageI_signal.connect(
                    self.debug_plot_stage1)
예제 #29
0
class DocumentViewManager(QMainWindow):
    """
    MDI area for displaying supporting documents within a given context e.g.
    supporting documents for a specific household based on the lifetime of the
    'SourceDocumentManager' instance.
    """
    def __init__(self, parent = None):
        QMainWindow.__init__(self, parent)
        self.setWindowFlags(Qt.Window)

        self._mdi_area = QMdiArea()
        self.setCentralWidget(self._mdi_area)

        self._mdi_area.subWindowActivated.connect(self.update_actions)
        self._viewer_mapper = QSignalMapper(self)
        self._viewer_mapper.mapped[QWidget].connect(self.set_active_sub_window)

        win_title = QApplication.translate("DocumentViewManager","Document Viewer")
        self.setWindowTitle(win_title)
        self.setUnifiedTitleAndToolBarOnMac(True)
        self.statusBar().showMessage(QApplication.translate("DocumentViewManager","Ready"))

        self._doc_viewers = {}

        self._create_menu_actions()
        self.update_actions()

    def _create_menu_actions(self):
        self._window_menu = self.menuBar().addMenu(QApplication.translate("DocumentViewManager","&Windows"))

        self._close_act = QAction(QApplication.translate("DocumentViewManager",
                                                         "Cl&ose"), self)
        self._close_act.setStatusTip(QApplication.translate("DocumentViewManager",
                                                            "Close the active document viewer"))
        self._close_act.triggered.connect(self._mdi_area.closeActiveSubWindow)

        self._close_all_act = QAction(QApplication.translate("DocumentViewManager",
                                                         "Close &All"), self)
        self._close_all_act.setStatusTip(QApplication.translate("DocumentViewManager",
                                                            "Close all the document viewers"))
        self._close_all_act.triggered.connect(self._mdi_area.closeAllSubWindows)

        self._tile_act = QAction(QApplication.translate("DocumentViewManager",
                                                         "&Tile"), self)
        self._tile_act.setStatusTip(QApplication.translate("DocumentViewManager",
                                                            "Tile the document viewers"))
        self._tile_act.triggered.connect(self._mdi_area.tileSubWindows)

        self._cascade_act = QAction(QApplication.translate("DocumentViewManager",
                                                         "&Cascade"), self)
        self._cascade_act.setStatusTip(QApplication.translate("DocumentViewManager",
                                                            "Cascade the document viewers"))
        self._cascade_act.triggered.connect(self._mdi_area.cascadeSubWindows)

        self._next_act = QAction(QApplication.translate("DocumentViewManager",
                                                         "Ne&xt"), self)
        self._next_act.setStatusTip(QApplication.translate("DocumentViewManager",
                                                            "Move the focus to the next document viewer"))
        self._next_act.triggered.connect(self._mdi_area.activateNextSubWindow)

        self._previous_act = QAction(QApplication.translate("DocumentViewManager",
                                                         "Pre&vious"), self)
        self._previous_act.setStatusTip(QApplication.translate("DocumentViewManager",
                                                            "Move the focus to the previous document viewer"))
        self._previous_act.triggered.connect(self._mdi_area.activatePreviousSubWindow)

        self._separator_act = QAction(self)
        self._separator_act.setSeparator(True)

        self.update_window_menu()
        self._window_menu.aboutToShow.connect(self.update_window_menu)

    def update_actions(self):
        if self._mdi_area.activeSubWindow():
            has_mdi_child = True

        else:
            has_mdi_child = False

        self._close_act.setEnabled(has_mdi_child)
        self._close_all_act.setEnabled(has_mdi_child)
        self._tile_act.setEnabled(has_mdi_child)
        self._cascade_act.setEnabled(has_mdi_child)
        self._previous_act.setEnabled(has_mdi_child)
        self._next_act.setEnabled(has_mdi_child)
        self._separator_act.setVisible(has_mdi_child)

    def update_window_menu(self):
        self._window_menu.clear()
        self._window_menu.addAction(self._close_act)
        self._window_menu.addAction(self._close_all_act)
        self._window_menu.addSeparator()
        self._window_menu.addAction(self._tile_act)
        self._window_menu.addAction(self._cascade_act)
        self._window_menu.addSeparator()
        self._window_menu.addAction(self._next_act)
        self._window_menu.addAction(self._previous_act)
        self._window_menu.addAction(self._separator_act)

        windows = self._mdi_area.subWindowList()
        self._separator_act.setVisible(len(windows) != 0)

        for i, window in enumerate(windows):
            text = "%d. %s" % (i + 1, window.windowTitle())

            win_action = self._window_menu.addAction(text)
            win_action.setCheckable(True)
            win_action.setChecked(window is self._mdi_area.activeSubWindow())
            win_action.triggered.connect(self._viewer_mapper.map)
            self._viewer_mapper.setMapping(win_action, window)

    def load_viewer(self, document_widget):
        """
        Open a new instance of the viewer or activate an existing one if the
        document had been previously loaded.
        :param document_widget: Contains all the necessary information required
        to load the specific document.
        """
        doc_identifier = document_widget.file_identifier()

        if doc_identifier in self._doc_viewers:
            doc_sw = self._doc_viewers[doc_identifier]
            self._mdi_area.setActiveSubWindow(doc_sw)
            doc_sw.showNormal()

        else:
            doc_viewer = self._create_viewer(document_widget)
            abs_doc_path = self.absolute_document_path(document_widget)

            if not QFile.exists(abs_doc_path):
                msg = QApplication.translate("DocumentViewManager",
                                             "The selected document does not exist."
                                             "\nPlease check the supporting documents' "
                                             "repository setting.")
                QMessageBox.critical(self, QApplication.translate("DocumentViewManager","Invalid Document"),
                                     msg)

                return

            doc_viewer.load_document(abs_doc_path)

            self._doc_viewers[doc_identifier] = doc_viewer

            self._mdi_area.addSubWindow(doc_viewer)
            doc_viewer.show()

        if not self.isVisible():
            self.setVisible(True)

        if self.isMinimized():
            self.showNormal()

    def set_active_sub_window(self, viewer):
        if viewer:
            self._mdi_area.setActiveSubWindow(viewer)

    def absolute_document_path(self,document_widget):
        """
        Build the absolute document path using info from the document widget.
        :param document_widget: Instance of document widget.
        :return: Absolute path of the supporting document.
        :rtype: str
        """
        abs_path = ""

        file_manager = document_widget.fileManager
        if not file_manager is None:
            network_repository = file_manager.networkPath
            file_id = document_widget.file_identifier()
            doc_type = "%d"%(document_widget.documentType())
            file_name, file_extension = guess_extension(document_widget.displayName())

            abs_path = network_repository + "/" + doc_type + "/" +\
                       file_id + file_extension

        return abs_path

    def reset(self):
        """
        Removes all document viewers in the view area.
        The QCloseEvent sent to each sub-window will decrement the register.
        """
        self._mdi_area.closeAllSubWindows()

    def _create_viewer(self, document_widget):
        """
        Creates a new instance of a document viewer.
        :param document_widget: Contains all the necessary information required
        to load the specific document.
        :return: Document viewer object
        :rtype: DocumentViewer
        """
        doc_viewer = DocumentViewer(self._mdi_area, document_widget.file_identifier())
        doc_viewer.setAttribute(Qt.WA_DeleteOnClose)
        doc_viewer.setWindowTitle(document_widget.displayName())

        #TODO: Incorporate logic for determining viewer based on document type
        ph_viewer = PhotoViewer()
        doc_viewer.set_view_widget(ph_viewer)

        doc_viewer.closed.connect(self._on_viewer_closed)

        return doc_viewer

    def remove_viewer(self, viewer_id):
        """
        Close and remove the viewer with the specified viewer ID.
        """
        if viewer_id in self._doc_viewers:
            viewer = self._doc_viewers[viewer_id]
            self._mdi_area.setActiveSubWindow(viewer)
            self._mdi_area.closeActiveSubWindow()

        self._on_viewer_closed(viewer_id)

    def _on_viewer_closed(self,file_id):
        """
        Slot raised when a document viewer is closed.
        """
        if file_id in self._doc_viewers:
            del self._doc_viewers[file_id]
예제 #30
0
파일: collaggr.py 프로젝트: StyXman/satyr
class CollectionAggregator (SatyrObject):
    def __init__ (self, parent, collections=None, songs=None, busName=None, busPath=None):
        SatyrObject.__init__ (self, parent, busName, busPath)

        self.configValues= (
            ('collsNo', int, 0),
            )
        self.loadConfig ()

        self.signalMapper= QSignalMapper ()
        self.collections= []

        if songs is None:
            self.songs= []
            self.count= 0
        else:
            self.songs= songs
            self.count= len (songs)

        # if collections is not None we it means the may have changed
        if self.collsNo>0 and collections is None:
            logger.info ("loading collections from config", self.collsNo)
            for index in xrange (self.collsNo):
                collection= Collection (self, busName=busName, busPath="/collection_%04d" % index)
                self.append (collection)
        else:
            if collections is not None:
                for collection in collections:
                    self.append (collection)

        self.signalMapper.mapped.connect (self.addSongs)

    def append (self, collection):
        logger.debug ("adding collection", collection)
        collection.loadOrScan ()
        self.collections.append (collection)
        self.collsNo= len (self.collections)

        index= len (self.collections)-1
        collection.newSongs.connect (self.signalMapper.map)
        self.signalMapper.setMapping (collection, index)
        collection.scanFinished.connect (self.updateIndexes)
        self.updateIndexes ()

    def indexToCollection (self, index):
        """Selects the collection that contains the index"""
        prevCollection= self.collections[0]
        for collection in self.collections[1:]:
            # print index, collection.offset
            if index < collection.offset:
                break
            prevCollection= collection

        return prevCollection

    def indexToCollectionIndex (self, index):
        """Converts a global index to a index in a collection"""
        collection= self.indexToCollection (index)
        collectionIndex= index-collection.offset

        return collection, collectionIndex

    def songForIndex (self, index):
        if len (self.songs)==0:
            # we're not a queue PLM, so we use the collections
            collection, collectionIndex= self.indexToCollectionIndex (index)
            song= collection.songs[collectionIndex]
        else:
            song= self.songs[index]

        return song

    def indexForSong (self, song):
        logger.debug ("PLM.indexForSong", song)
        index= None
        if len (self.songs)>0:
            index= self.songs.index (song)
        else:
            collection= song.coll
            collectionIndex= collection.indexForSong (song)
            if collectionIndex is not None:
                index= collection.offset+collectionIndex

        return index

    def addSongs (self, collNo):
        collection= self.collections[collNo]
        # BUG? shouldn't we call updateIndexes?
        # HINT: we call it when scanFinished()
        self.count+= len (collection.newSongs_)

    def updateIndexes (self):
        # recalculate the count and the offsets
        # only if we don't hols the songs ourselves
        if len (self.songs)==0:
            # HINT: yes, self.count==offset, but the semantic is different
            # otherwise the update of offsets will not be so clear
            self.count= 0
            offset= 0

            for collection in self.collections:
                collection.offset= offset
                offset+= collection.count
                self.count+= collection.count

        logger.debug ("PLM: count:", self.count)
예제 #31
0
파일: myterm.py 프로젝트: gamesun/MyTerm
class MainWindow(QMainWindow, Ui_MainWindow):
    """docstring for MainWindow."""
    def __init__(self, parent=None):
        super(MainWindow, self).__init__()
        self._csvFilePath = ""
        self.serialport = serial.Serial()
        self.receiver_thread = readerThread(self)
        self.receiver_thread.setPort(self.serialport)
        self._localEcho = None

        self.setupUi(self)
        self.setCorner(Qt.TopLeftCorner, Qt.LeftDockWidgetArea)
        self.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea)
        font = QtGui.QFont()
        font.setFamily(EDITOR_FONT)
        font.setPointSize(10)
        self.txtEdtOutput.setFont(font)
        self.txtEdtInput.setFont(font)
        self.quickSendTable.setFont(font)
        if UI_FONT is not None:
            font = QtGui.QFont()
            font.setFamily(UI_FONT)
            font.setPointSize(9)
            self.dockWidget_PortConfig.setFont(font)
            self.dockWidget_SendHex.setFont(font)
            self.dockWidget_QuickSend.setFont(font)
        self.setupFlatUi()
        self.onEnumPorts()

        icon = QtGui.QIcon(":/icon.ico")
        self.setWindowIcon(icon)
        self.actionAbout.setIcon(icon)

        icon = QtGui.QIcon(":/qt_logo_16.ico")
        self.actionAbout_Qt.setIcon(icon)

        self._viewGroup = QActionGroup(self)
        self._viewGroup.addAction(self.actionAscii)
        self._viewGroup.addAction(self.actionHex_lowercase)
        self._viewGroup.addAction(self.actionHEX_UPPERCASE)
        self._viewGroup.setExclusive(True)

        # bind events
        self.actionOpen_Cmd_File.triggered.connect(self.openCSV)
        self.actionSave_Log.triggered.connect(self.onSaveLog)
        self.actionExit.triggered.connect(self.onExit)

        self.actionOpen.triggered.connect(self.openPort)
        self.actionClose.triggered.connect(self.closePort)

        self.actionPort_Config_Panel.triggered.connect(self.onTogglePrtCfgPnl)
        self.actionQuick_Send_Panel.triggered.connect(self.onToggleQckSndPnl)
        self.actionSend_Hex_Panel.triggered.connect(self.onToggleHexPnl)
        self.dockWidget_PortConfig.visibilityChanged.connect(self.onVisiblePrtCfgPnl)
        self.dockWidget_QuickSend.visibilityChanged.connect(self.onVisibleQckSndPnl)
        self.dockWidget_SendHex.visibilityChanged.connect(self.onVisibleHexPnl)
        self.actionLocal_Echo.triggered.connect(self.onLocalEcho)
        self.actionAlways_On_Top.triggered.connect(self.onAlwaysOnTop)

        self.actionAscii.triggered.connect(self.onViewChanged)
        self.actionHex_lowercase.triggered.connect(self.onViewChanged)
        self.actionHEX_UPPERCASE.triggered.connect(self.onViewChanged)

        self.actionAbout.triggered.connect(self.onAbout)
        self.actionAbout_Qt.triggered.connect(self.onAboutQt)

        self.btnOpen.clicked.connect(self.onOpen)
        self.btnClear.clicked.connect(self.onClear)
        self.btnSaveLog.clicked.connect(self.onSaveLog)
        self.btnEnumPorts.clicked.connect(self.onEnumPorts)
        self.btnSendHex.clicked.connect(self.sendHex)

        self.receiver_thread.read.connect(self.receive)
        self.receiver_thread.exception.connect(self.readerExcept)
        self._signalMap = QSignalMapper(self)
        self._signalMap.mapped[int].connect(self.tableClick)

        # initial action
        self.actionHEX_UPPERCASE.setChecked(True)
        self.receiver_thread.setViewMode(VIEWMODE_HEX_UPPERCASE)
        self.initQuickSend()
        self.restoreLayout()
        self.moveScreenCenter()
        self.syncMenu()
        
        if self.isMaximized():
            self.setMaximizeButton("restore")
        else:
            self.setMaximizeButton("maximize")
            
        self.LoadSettings()

    def setupFlatUi(self):
        self._dragPos = self.pos()
        self._isDragging = False
        self.setMouseTracking(True)
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setStyleSheet("""
            QWidget {
                background-color:#99d9ea;
                /*background-image: url(:/background.png);*/
                outline: 1px solid #0057ff;
            }
            QLabel {
                color:#202020;
                font-size:13px;
                font-family:Century;
            }
            
            QComboBox {
                color:#202020;
                font-size:13px;
                font-family:Century Schoolbook;
            }
            QComboBox {
                border: none;
                padding: 1px 18px 1px 3px;
            }
            QComboBox:editable {
                background: white;
            }
            QComboBox:!editable, QComboBox::drop-down:editable {
                background: #62c7e0;
            }
            QComboBox:!editable:hover, QComboBox::drop-down:editable:hover {
                background: #c7eaf3;
            }
            QComboBox:!editable:pressed, QComboBox::drop-down:editable:pressed {
                background: #35b6d7;
            }
            QComboBox:on {
                padding-top: 3px;
                padding-left: 4px;
            }
            QComboBox::drop-down {
                subcontrol-origin: padding;
                subcontrol-position: top right;
                width: 16px;
                border: none;
            }
            QComboBox::down-arrow {
                image: url(:/downarrow.png);
            }
            QComboBox::down-arrow:on {
                image: url(:/uparrow.png);
            }
            
            QGroupBox {
                color:#202020;
                font-size:12px;
                font-family:Century Schoolbook;
                border: 1px solid gray;
                margin-top: 15px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                subcontrol-position: top left;
                left:5px;
                top:3px;
            }
            
            QCheckBox {
                color:#202020;
                spacing: 5px;
                font-size:12px;
                font-family:Century Schoolbook;
            }
            QCheckBox::indicator:unchecked {
                image: url(:/checkbox_unchecked.png);
            }

            QCheckBox::indicator:unchecked:hover {
                image: url(:/checkbox_unchecked_hover.png);
            }

            QCheckBox::indicator:unchecked:pressed {
                image: url(:/checkbox_unchecked_pressed.png);
            }

            QCheckBox::indicator:checked {
                image: url(:/checkbox_checked.png);
            }

            QCheckBox::indicator:checked:hover {
                image: url(:/checkbox_checked_hover.png);
            }

            QCheckBox::indicator:checked:pressed {
                image: url(:/checkbox_checked_pressed.png);
            }
            
            QScrollBar:horizontal {
                background-color:#99d9ea;
                border: none;
                height: 15px;
                margin: 0px 20px 0 20px;
            }
            QScrollBar::handle:horizontal {
                background: #61b9e1;
                min-width: 20px;
            }
            QScrollBar::add-line:horizontal {
                image: url(:/rightarrow.png);
                border: none;
                background: #7ecfe4;
                width: 20px;
                subcontrol-position: right;
                subcontrol-origin: margin;
            }
            QScrollBar::sub-line:horizontal {
                image: url(:/leftarrow.png);
                border: none;
                background: #7ecfe4;
                width: 20px;
                subcontrol-position: left;
                subcontrol-origin: margin;
            }
            
            QScrollBar:vertical {
                background-color:#99d9ea;
                border: none;
                width: 15px;
                margin: 20px 0px 20px 0px;
            }
            QScrollBar::handle::vertical {
                background: #61b9e1;
                min-height: 20px;
            }
            QScrollBar::add-line::vertical {
                image: url(:/downarrow.png);
                border: none;
                background: #7ecfe4;
                height: 20px;
                subcontrol-position: bottom;
                subcontrol-origin: margin;
            }
            QScrollBar::sub-line::vertical {
                image: url(:/uparrow.png);
                border: none;
                background: #7ecfe4;
                height: 20px;
                subcontrol-position: top;
                subcontrol-origin: margin;
            }
            
            QTableView {
                background-color: white;
                /*selection-background-color: #FF92BB;*/
                border: 1px solid #eeeeee;
                color: #2f2f2f;
            }
            QTableView::focus {
                /*border: 1px solid #2a7fff;*/
            }
            QTableView QTableCornerButton::section {
                border: none;
                border-right: 1px solid #eeeeee;
                border-bottom: 1px solid #eeeeee;
                background-color: #8ae6d2;
            }
            QTableView QWidget {
                background-color: white;
            }
            QTableView::item:focus {
                border: 1px red;
                background-color: transparent;
                color: #2f2f2f;
            }
            QHeaderView::section {
                border: none;
                border-right: 1px solid #eeeeee;
                border-bottom: 1px solid #eeeeee;
                padding-left: 2px;
                padding-right: 2px;
                color: #444444;
                background-color: #8ae6d2;
            }
            QTextEdit {
                background-color:white;
                color:#2f2f2f;
                border: 1px solid white;
            }
            QTextEdit::focus {
                border: 1px solid #2a7fff;
            }
            
            QPushButton {
                background-color:#30a7b8;
                border:none;
                color:#ffffff;
                font-size:14px;
                font-family:Century Schoolbook;
            }
            QPushButton:hover {
                background-color:#51c0d1;
            }
            QPushButton:pressed {
                background-color:#3a9ecc;
            }
            
            QMenuBar {
                color: #2f2f2f;
            }
            QMenuBar::item {
                background-color: transparent;
                margin: 8px 0px 0px 0px;
                padding: 1px 8px 1px 8px;
                height: 15px;
            }
            QMenuBar::item:selected {
                background: #51c0d1;
            }
            QMenuBar::item:pressed {
                
            }
            QMenu {
                color: #2f2f2f;
            }
            QMenu {
                margin: 2px;
            }
            QMenu::item {
                padding: 2px 25px 2px 21px;
                border: 1px solid transparent;
            }
            QMenu::item:selected {
                background: #51c0d1;
            }
            QMenu::icon {
                background: transparent;
                border: 2px inset transparent;
            }

            QDockWidget {
                font-size:13px;
                font-family:Century;
                color: #202020;
                titlebar-close-icon: none;
                titlebar-normal-icon: none;
            }
            QDockWidget::title {
                margin: 0;
                padding: 2px;
                subcontrol-origin: content;
                subcontrol-position: right top;
                text-align: left;
                background: #67baed;
                
            }
            QDockWidget::float-button {
                max-width: 12px;
                max-height: 12px;
                background-color:transparent;
                border:none;
                image: url(:/restore_inactive.png);
            }
            QDockWidget::float-button:hover {
                background-color:#227582;
                image: url(:/restore_active.png);
            }
            QDockWidget::float-button:pressed {
                padding: 0;
                background-color:#14464e;
                image: url(:/restore_active.png);
            }
            QDockWidget::close-button {
                max-width: 12px;
                max-height: 12px;
                background-color:transparent;
                border:none;
                image: url(:/close_inactive.png);
            }
            QDockWidget::close-button:hover {
                background-color:#ea5e00;
                image: url(:/close_active.png);
            }
            QDockWidget::close-button:pressed {
                background-color:#994005;
                image: url(:/close_active.png);
                padding: 0;
            }
            
        """)
        self.dockWidgetContents.setStyleSheet("""
            QPushButton {
                min-height:23px;
            }
        """)
        self.dockWidget_QuickSend.setStyleSheet("""
            QPushButton {
                background-color:#27b798;
                font-family:Consolas;
                font-size:12px;
                min-width:46px;
            }
            QPushButton:hover {
                background-color:#3bd5b4;
            }
            QPushButton:pressed {
                background-color:#1d8770;
            }
        """)
        self.dockWidgetContents_2.setStyleSheet("""
            QPushButton {
                min-height:23px;
                min-width:50px;
            }
        """)

        w = self.frameGeometry().width()
        self._minBtn = QPushButton(self)
        self._minBtn.setGeometry(w-103,0,28,24)
        self._minBtn.clicked.connect(self.onMinimize)
        self._minBtn.setStyleSheet("""
            QPushButton {
                background-color:transparent;
                border:none;
                outline: none;
                image: url(:/minimize_inactive.png);
            }
            QPushButton:hover {
                background-color:#227582;
                image: url(:/minimize_active.png);
            }
            QPushButton:pressed {
                background-color:#14464e;
                image: url(:/minimize_active.png);
            }
        """)
        
        self._maxBtn = QPushButton(self)
        self._maxBtn.setGeometry(w-74,0,28,24)
        self._maxBtn.clicked.connect(self.onMaximize)
        self.setMaximizeButton("maximize")
        
        self._closeBtn = QPushButton(self)
        self._closeBtn.setGeometry(w-45,0,36,24)
        self._closeBtn.clicked.connect(self.onExit)
        self._closeBtn.setStyleSheet("""
            QPushButton {
                background-color:transparent;
                border:none;
                outline: none;
                image: url(:/close_inactive.png);
            }
            QPushButton:hover {
                background-color:#ea5e00;
                image: url(:/close_active.png);
            }
            QPushButton:pressed {
                background-color:#994005;
                image: url(:/close_active.png);
            }
        """)

    def resizeEvent(self, event):
        w = event.size().width()
        self._minBtn.move(w-103,0)
        self._maxBtn.move(w-74,0)
        self._closeBtn.move(w-45,0)

    def onMinimize(self):
        self.showMinimized()
    
    def isMaximized(self):
        return ((self.windowState() == Qt.WindowMaximized))
    
    def onMaximize(self):
        if self.isMaximized():
            self.showNormal()
            self.setMaximizeButton("maximize")
        else:
            self.showMaximized()
            self.setMaximizeButton("restore")
    
    def setMaximizeButton(self, style):
        if "maximize" == style:
            self._maxBtn.setStyleSheet("""
                QPushButton {
                    background-color:transparent;
                    border:none;
                    outline: none;
                    image: url(:/maximize_inactive.png);
                }
                QPushButton:hover {
                    background-color:#227582;
                    image: url(:/maximize_active.png);
                }
                QPushButton:pressed {
                    background-color:#14464e;
                    image: url(:/maximize_active.png);
                }
            """)
        elif "restore" == style:
            self._maxBtn.setStyleSheet("""
                QPushButton {
                    background-color:transparent;
                    border:none;
                    outline: none;
                    image: url(:/restore_inactive.png);
                }
                QPushButton:hover {
                    background-color:#227582;
                    image: url(:/restore_active.png);
                }
                QPushButton:pressed {
                    background-color:#14464e;
                    image: url(:/restore_active.png);
                }
            """)
    
    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self._isDragging = True
            self._dragPos = event.globalPos() - self.pos()
        event.accept()
        
    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.LeftButton and self._isDragging and not self.isMaximized():
            self.move(event.globalPos() - self._dragPos)
        event.accept()

    def mouseReleaseEvent(self, event):
        self._isDragging = False
        event.accept()

    def SaveSettings(self):
        root = ET.Element("MyTerm")
        GUISettings = ET.SubElement(root, "GUISettings")

        PortCfg = ET.SubElement(GUISettings, "PortConfig")
        ET.SubElement(PortCfg, "port").text = self.cmbPort.currentText()
        ET.SubElement(PortCfg, "baudrate").text = self.cmbBaudRate.currentText()
        ET.SubElement(PortCfg, "databits").text = self.cmbDataBits.currentText()
        ET.SubElement(PortCfg, "parity").text = self.cmbParity.currentText()
        ET.SubElement(PortCfg, "stopbits").text = self.cmbStopBits.currentText()
        ET.SubElement(PortCfg, "rtscts").text = self.chkRTSCTS.isChecked() and "on" or "off"
        ET.SubElement(PortCfg, "xonxoff").text = self.chkXonXoff.isChecked() and "on" or "off"

        View = ET.SubElement(GUISettings, "View")
        ET.SubElement(View, "LocalEcho").text = self.actionLocal_Echo.isChecked() and "on" or "off"
        ET.SubElement(View, "ReceiveView").text = self._viewGroup.checkedAction().text()

        with open(get_config_path('settings.xml'), 'w') as f:
            f.write('<?xml version="1.0" encoding="UTF-8"?>\n')
            f.write(ET.tostring(root, encoding='utf-8', pretty_print=True).decode("utf-8"))

    def LoadSettings(self):
        if os.path.isfile(get_config_path("settings.xml")):
            with open(get_config_path("settings.xml"), 'r') as f:
                tree = safeET.parse(f)

            port = tree.findtext('GUISettings/PortConfig/port', default='')
            if port != '':
                self.cmbPort.setProperty("currentText", port)

            baudrate = tree.findtext('GUISettings/PortConfig/baudrate', default='38400')
            if baudrate != '':
                self.cmbBaudRate.setProperty("currentText", baudrate)

            databits = tree.findtext('GUISettings/PortConfig/databits', default='8')
            id = self.cmbDataBits.findText(databits)
            if id >= 0:
                self.cmbDataBits.setCurrentIndex(id)

            parity = tree.findtext('GUISettings/PortConfig/parity', default='None')
            id = self.cmbParity.findText(parity)
            if id >= 0:
                self.cmbParity.setCurrentIndex(id)

            stopbits = tree.findtext('GUISettings/PortConfig/stopbits', default='1')
            id = self.cmbStopBits.findText(stopbits)
            if id >= 0:
                self.cmbStopBits.setCurrentIndex(id)

            rtscts = tree.findtext('GUISettings/PortConfig/rtscts', default='off')
            if 'on' == rtscts:
                self.chkRTSCTS.setChecked(True)
            else:
                self.chkRTSCTS.setChecked(False)

            xonxoff = tree.findtext('GUISettings/PortConfig/xonxoff', default='off')
            if 'on' == xonxoff:
                self.chkXonXoff.setChecked(True)
            else:
                self.chkXonXoff.setChecked(False)

            LocalEcho = tree.findtext('GUISettings/View/LocalEcho', default='off')
            if 'on' == LocalEcho:
                self.actionLocal_Echo.setChecked(True)
                self._localEcho = True
            else:
                self.actionLocal_Echo.setChecked(False)
                self._localEcho = False

            ReceiveView = tree.findtext('GUISettings/View/ReceiveView', default='HEX(UPPERCASE)')
            if 'Ascii' in ReceiveView:
                self.actionAscii.setChecked(True)
            elif 'lowercase' in ReceiveView:
                self.actionHex_lowercase.setChecked(True)
            elif 'UPPERCASE' in ReceiveView:
                self.actionHEX_UPPERCASE.setChecked(True)

    def closeEvent(self, event):
        self.saveLayout()
        self.saveCSV()
        self.SaveSettings()
        event.accept()

    def tableClick(self, row):
        self.sendTableRow(row)

    def initQuickSend(self):
        #self.quickSendTable.horizontalHeader().setDefaultSectionSize(40)
        #self.quickSendTable.horizontalHeader().setMinimumSectionSize(25)
        self.quickSendTable.setRowCount(50)
        self.quickSendTable.setColumnCount(20)

        for row in range(50):
            item = QPushButton(str("Send"))
            item.clicked.connect(self._signalMap.map)
            self._signalMap.setMapping(item, row)
            self.quickSendTable.setCellWidget(row, 0, item)
            self.quickSendTable.setRowHeight(row, 20)

        if os.path.isfile(get_config_path('QckSndBckup.csv')):
            self.loadCSV(get_config_path('QckSndBckup.csv'))

        self.quickSendTable.resizeColumnsToContents()

    def openCSV(self):
        fileName = QFileDialog.getOpenFileName(self, "Select a file",
            os.getcwd(), "CSV Files (*.csv)")
        if fileName:
            self.loadCSV(fileName, notifyExcept = True)

    def saveCSV(self):
        # scan table
        rows = self.quickSendTable.rowCount()
        cols = self.quickSendTable.columnCount()

        tmp_data = [[self.quickSendTable.item(row, col) is not None
                    and self.quickSendTable.item(row, col).text() or ''
                    for col in range(1, cols)] for row in range(rows)]

        data = []
        # delete trailing blanks
        for row in tmp_data:
            for idx, d in enumerate(row[::-1]):
                if '' != d:
                    break
            new_row = row[:len(row) - idx]
            data.append(new_row)

        #import pprint
        #pprint.pprint(data, width=120, compact=True)

        # write to file
        with open(get_config_path('QckSndBckup.csv'), 'w') as csvfile:
            csvwriter = csv.writer(csvfile, delimiter=',', lineterminator='\n')
            csvwriter.writerows(data)

    def loadCSV(self, path, notifyExcept = False):
        data = []
        set_rows = 0
        set_cols = 0
        try:
            with open(path) as csvfile:
                csvData = csv.reader(csvfile)
                for row in csvData:
                    data.append(row)
                    set_rows = set_rows + 1
                    if len(row) > set_cols:
                        set_cols = len(row)
        except IOError as e:
            print("({})".format(e))
            if notifyExcept:
                QMessageBox.critical(self, "Open failed", str(e), QMessageBox.Close)
            return

        rows = self.quickSendTable.rowCount()
        cols = self.quickSendTable.columnCount()
        # clear table
        for col in range(cols):
            for row in range(rows):
                self.quickSendTable.setItem(row, col, QTableWidgetItem(""))

        self._csvFilePath = path
        if (cols - 1) < set_cols:   # first colume is used by the "send" buttons.
            cols = set_cols + 10
            self.quickSendTable.setColumnCount(cols)
        if rows < set_rows:
            rows = set_rows + 20
            self.quickSendTable.setRowCount(rows)

        for row, rowdat in enumerate(data):
            if len(rowdat) > 0:
                for col, cell in enumerate(rowdat, 1):
                    self.quickSendTable.setItem(row, col, QTableWidgetItem(str(cell)))

        self.quickSendTable.resizeColumnsToContents()
        #self.quickSendTable.resizeRowsToContents()

    def sendTableRow(self, row):
        cols = self.quickSendTable.columnCount()
        try:
            data = ['0' + self.quickSendTable.item(row, col).text()
                for col in range(1, cols)
                if self.quickSendTable.item(row, col) is not None
                    and self.quickSendTable.item(row, col).text() is not '']
        except:
            print("Exception in get table data(row = %d)" % (row + 1))
        else:
            tmp = [d[-2] + d[-1] for d in data if len(d) >= 2]
            for t in tmp:
                if not is_hex(t):
                    QMessageBox.critical(self, "Error",
                        "'%s' is not hexadecimal." % (t), QMessageBox.Close)
                    return

            h = [int(t, 16) for t in tmp]
            self.transmitHex(h)

    def sendHex(self):
        hexStr = self.txtEdtInput.toPlainText()
        hexStr = ''.join(hexStr.split(" "))

        hexarray = []
        for i in range(0, len(hexStr), 2):
            hexarray.append(int(hexStr[i:i+2], 16))

        self.transmitHex(hexarray)

    def readerExcept(self, e):
        self.closePort()
        QMessageBox.critical(self, "Read failed", str(e), QMessageBox.Close)

    def timestamp(self):
        return datetime.datetime.now().time().isoformat()[:-3]

    def receive(self, data):
        self.appendOutputText("\n%s R<-:%s" % (self.timestamp(), data))

    def appendOutputText(self, data, color=Qt.black):
        # the qEditText's "append" methon will add a unnecessary newline.
        # self.txtEdtOutput.append(data.decode('utf-8'))

        tc=self.txtEdtOutput.textColor()
        self.txtEdtOutput.moveCursor(QtGui.QTextCursor.End)
        self.txtEdtOutput.setTextColor(QtGui.QColor(color))
        self.txtEdtOutput.insertPlainText(data)
        self.txtEdtOutput.moveCursor(QtGui.QTextCursor.End)
        self.txtEdtOutput.setTextColor(tc)

    def transmitHex(self, hexarray):
        if len(hexarray) > 0:
            byteArray = bytearray(hexarray)
            if self.serialport.isOpen():
                try:
                    self.serialport.write(byteArray)
                except serial.SerialException as e:
                    print("Exception in transmitHex(%s)" % repr(hexarray))
                    QMessageBox.critical(self, "Exception in transmitHex", str(e),
                        QMessageBox.Close)
                else:
                    # self.txCount += len( b )
                    # self.frame.statusbar.SetStatusText('Tx:%d' % self.txCount, 2)

                    text = ''.join(['%02X ' % i for i in hexarray])
                    self.appendOutputText("\n%s T->:%s" % (self.timestamp(), text),
                        Qt.blue)

    def GetPort(self):
        return self.cmbPort.currentText()

    def GetDataBits(self):
        s = self.cmbDataBits.currentText()
        if s == '5':
            return serial.FIVEBITS
        elif s == '6':
            return serial.SIXBITS
        elif s == '7':
            return serial.SEVENBITS
        elif s == '8':
            return serial.EIGHTBITS

    def GetParity(self):
        s = self.cmbParity.currentText()
        if s == 'None':
            return serial.PARITY_NONE
        elif s == 'Even':
            return serial.PARITY_EVEN
        elif s == 'Odd':
            return serial.PARITY_ODD
        elif s == 'Mark':
            return serial.PARITY_MARK
        elif s == 'Space':
            return serial.PARITY_SPACE

    def GetStopBits(self):
        s = self.cmbStopBits.currentText()
        if s == '1':
            return serial.STOPBITS_ONE
        elif s == '1.5':
            return serial.STOPBITS_ONE_POINT_FIVE
        elif s == '2':
            return serial.STOPBITS_TWO

    def openPort(self):
        if self.serialport.isOpen():
            return

        _port = self.GetPort()
        if '' == _port:
            QMessageBox.information(self, "Invalid parameters", "Port is empty.")
            return

        _baudrate = self.cmbBaudRate.currentText()
        if '' == _baudrate:
            QMessageBox.information(self, "Invalid parameters", "Baudrate is empty.")
            return

        self.serialport.port     = _port
        self.serialport.baudrate = _baudrate
        self.serialport.bytesize = self.GetDataBits()
        self.serialport.stopbits = self.GetStopBits()
        self.serialport.parity   = self.GetParity()
        self.serialport.rtscts   = self.chkRTSCTS.isChecked()
        self.serialport.xonxoff  = self.chkXonXoff.isChecked()
        # self.serialport.timeout  = THREAD_TIMEOUT
        # self.serialport.writeTimeout = SERIAL_WRITE_TIMEOUT
        try:
            self.serialport.open()
        except serial.SerialException as e:
            QMessageBox.critical(self, "Could not open serial port", str(e),
                QMessageBox.Close)
        else:
            self._start_reader()
            self.setWindowTitle("%s on %s [%s, %s%s%s%s%s]" % (
                appInfo.title,
                self.serialport.portstr,
                self.serialport.baudrate,
                self.serialport.bytesize,
                self.serialport.parity,
                self.serialport.stopbits,
                self.serialport.rtscts and ' RTS/CTS' or '',
                self.serialport.xonxoff and ' Xon/Xoff' or '',
                )
            )
            pal = self.btnOpen.palette()
            pal.setColor(QtGui.QPalette.Button, QtGui.QColor(0,0xff,0x7f))
            self.btnOpen.setAutoFillBackground(True)
            self.btnOpen.setPalette(pal)
            self.btnOpen.setText('Close')
            self.btnOpen.update()

    def closePort(self):
        if self.serialport.isOpen():
            self._stop_reader()
            self.serialport.close()
            self.setWindowTitle(appInfo.title)
            pal = self.btnOpen.style().standardPalette()
            self.btnOpen.setAutoFillBackground(True)
            self.btnOpen.setPalette(pal)
            self.btnOpen.setText('Open')
            self.btnOpen.update()

    def _start_reader(self):
        """Start reader thread"""
        self.receiver_thread.start()

    def _stop_reader(self):
        """Stop reader thread only, wait for clean exit of thread"""
        self.receiver_thread.join()

    def onTogglePrtCfgPnl(self):
        if self.actionPort_Config_Panel.isChecked():
            self.dockWidget_PortConfig.show()
        else:
            self.dockWidget_PortConfig.hide()

    def onToggleQckSndPnl(self):
        if self.actionQuick_Send_Panel.isChecked():
            self.dockWidget_QuickSend.show()
        else:
            self.dockWidget_QuickSend.hide()

    def onToggleHexPnl(self):
        if self.actionSend_Hex_Panel.isChecked():
            self.dockWidget_SendHex.show()
        else:
            self.dockWidget_SendHex.hide()

    def onVisiblePrtCfgPnl(self, visible):
        self.actionPort_Config_Panel.setChecked(visible)

    def onVisibleQckSndPnl(self, visible):
        self.actionQuick_Send_Panel.setChecked(visible)

    def onVisibleHexPnl(self, visible):
        self.actionSend_Hex_Panel.setChecked(visible)

    def onLocalEcho(self):
        self._localEcho = self.actionLocal_Echo.isChecked()

    def onAlwaysOnTop(self):
        if self.actionAlways_On_Top.isChecked():
            style = self.windowFlags()
            self.setWindowFlags(style|Qt.WindowStaysOnTopHint)
            self.show()
        else:
            style = self.windowFlags()
            self.setWindowFlags(style & ~Qt.WindowStaysOnTopHint)
            self.show()

    def onOpen(self):
        if self.serialport.isOpen():
            self.closePort()
        else:
            self.openPort()

    def onClear(self):
        self.txtEdtOutput.clear()

    def onSaveLog(self):
        fileName = QFileDialog.getSaveFileName(self, "Save as", os.getcwd(),
            "Log files (*.log);;Text files (*.txt);;All files (*.*)")
        if fileName:
            import codecs
            f = codecs.open(fileName, 'w', 'utf-8')
            f.write(self.txtEdtOutput.toPlainText())
            f.close()

    def moveScreenCenter(self):
        w = self.frameGeometry().width()
        h = self.frameGeometry().height()
        desktop = QDesktopWidget()
        screenW = desktop.screen().width()
        screenH = desktop.screen().height()
        self.setGeometry((screenW-w)/2, (screenH-h)/2, w, h)

    def onEnumPorts(self):
        for p in enum_ports():
            self.cmbPort.addItem(p)
        # self.cmbPort.update()

    def onAbout(self):
        q = QWidget()
        icon = QtGui.QIcon(":/icon.ico")
        q.setWindowIcon(icon)
        QMessageBox.about(q, "About MyTerm", appInfo.aboutme)

    def onAboutQt(self):
        QMessageBox.aboutQt(None)

    def onExit(self):
        if self.serialport.isOpen():
            self.closePort()
        self.close()

    def restoreLayout(self):
        if os.path.isfile(get_config_path("layout.dat")):
            try:
                f=open(get_config_path("layout.dat"), 'rb')
                geometry, state=pickle.load(f)
                self.restoreGeometry(geometry)
                self.restoreState(state)
            except Exception as e:
                print("Exception on restoreLayout, {}".format(e))
        else:
            try:
                f=QFile(':/default_layout.dat')
                f.open(QIODevice.ReadOnly)
                geometry, state=pickle.loads(f.readAll())
                self.restoreGeometry(geometry)
                self.restoreState(state)
            except Exception as e:
                print("Exception on restoreLayout, {}".format(e))

    def saveLayout(self):
        with open(get_config_path("layout.dat"), 'wb') as f:
            pickle.dump((self.saveGeometry(), self.saveState()), f)

    def syncMenu(self):
        self.actionPort_Config_Panel.setChecked(not self.dockWidget_PortConfig.isHidden())
        self.actionQuick_Send_Panel.setChecked(not self.dockWidget_QuickSend.isHidden())
        self.actionSend_Hex_Panel.setChecked(not self.dockWidget_SendHex.isHidden())

    def onViewChanged(self):
        checked = self._viewGroup.checkedAction()
        if checked is None:
            self.actionHEX_UPPERCASE.setChecked(True)
            self.receiver_thread.setViewMode(VIEWMODE_HEX_UPPERCASE)
        else:
            if 'Ascii' in checked.text():
                self.receiver_thread.setViewMode(VIEWMODE_ASCII)
            elif 'lowercase' in checked.text():
                self.receiver_thread.setViewMode(VIEWMODE_HEX_LOWERCASE)
            elif 'UPPERCASE' in checked.text():
                self.receiver_thread.setViewMode(VIEWMODE_HEX_UPPERCASE)
    def contextMenuEvent(self, event):
        menu = QtGui.QMenu()
        last_pos = event.pos()
        cursor = self.cursorForPosition(last_pos)
        pos = cursor.positionInBlock()
        line = cursor.blockNumber()

        word = self.error_at_pos(line, pos)

        if not word == None:
            suggestions = self.highlighter.checker.suggest(word[0])

            if len(suggestions) > 0:
                suggestion_mapper = QSignalMapper(self)

                actions = []

                # Generate our suggestions, packing all the data we need to make the replacement.
                for i, suggestion in enumerate(suggestions):
                    if i == MAX_SUGGESTIONS:
                        break

                    actions.append(QtGui.QAction(suggestion, None))

                    # We can only send strings with the signal mapper, so pickle our data.
                    data = pickle.dumps((word[0], line, word[1], suggestion))
                    data = QtCore.QString.fromAscii(data)

                    self.connect(actions[-1], QtCore.SIGNAL("triggered()"),
                                 suggestion_mapper, QtCore.SLOT("map()"))
                    suggestion_mapper.setMapping(actions[-1], data)

                self.connect(suggestion_mapper,
                             QtCore.SIGNAL("mapped(QString)"), self.__replace)
                menu.addActions(actions)
            else:
                action = QtGui.QAction("(No spelling suggestions)", None)
                action.setDisabled(True)
                menu.addAction(action)

            menu.addSeparator()

            add_mapper = QSignalMapper(self)
            ignore_mapper = QSignalMapper(self)
            add_action = QtGui.QAction("Add to Dictionary", None)
            ignore_action = QtGui.QAction("Ignore All", None)

            self.connect(add_action, QtCore.SIGNAL("triggered()"), add_mapper,
                         QtCore.SLOT("map()"))
            add_mapper.setMapping(add_action, word[0])
            self.connect(add_mapper, QtCore.SIGNAL("mapped(QString)"),
                         self.__add)

            self.connect(ignore_action, QtCore.SIGNAL("triggered()"),
                         ignore_mapper, QtCore.SLOT("map()"))
            ignore_mapper.setMapping(ignore_action, word[0])
            self.connect(ignore_mapper, QtCore.SIGNAL("mapped(QString)"),
                         self.__ignore)

            menu.addAction(add_action)
            menu.addAction(ignore_action)
            menu.addSeparator()

        default_menu = self.createStandardContextMenu()
        menu.addActions(default_menu.actions())

        menu.exec_(event.globalPos())
예제 #33
0
class TratadorInterface(QMainWindow, Ui_InterfaceQt, Observer):
    
    erro_vertice_existe = pyqtSignal()
    erro_vertice_nao_existe = pyqtSignal(str)
    
    def __init__(self):
        Observer.__init__(self)
        QMainWindow.__init__(self)
        super(TratadorInterface, self).__init__(self)
        self.setupUi(self)
        self.erro_vertice_existe.connect(self.verticeExiste)
        self.erro_vertice_nao_existe.connect(self.verticeNaoExiste)
        self.eh_funcao.setEnabled(False)
        self.funcao_edit.setEnabled(False)
        self.grafo = None
        self.signalMapper = QSignalMapper(self)
        
    
    @pyqtSlot()
    def on_adicionar_vertice_button_clicked(self):
        if not self.grafo:
            nome = str(self.nome_edit.text())
            if nome == '':
                QMessageBox(self).critical(self, 'ERRO', 'O Gravo deve ter um nome!', buttons=QMessageBox.Ok)
                return
            self.grafo = AlgoritmosGrafoNO(nome)
            self.observe(self.grafo)
        self.limparInferencias()
        vertices = str(self.vertices_edit.text())
        vertices = vertices.split(',')
        if len(vertices) > 0:
            self.signalMapper = QSignalMapper(self)
            for vertice in vertices:
                vertice = vertice.strip()
                if vertice != '':
                    cont = self.tabela_adjacencia.rowCount()
                    item = QTableWidgetItem(vertice)
                    self.tabela_adjacencia.insertColumn(cont)
                    self.tabela_adjacencia.insertRow(cont)
                    self.tabela_adjacencia.setHorizontalHeaderItem(cont,item)
                    self.tabela_adjacencia.setVerticalHeaderItem(cont,item)
                    self.grafo.adicionarVertice(vertice)

                for x in xrange(self.tabela_adjacencia.rowCount()):
                    comboV = QComboBox(self)
                    comboH = QComboBox(self)
                    
                    comboV.addItems(['0','1'])
                    comboH.addItems(['0','1'])
                    comboV.setSizePolicy(QSizePolicy().Minimum, QSizePolicy().Minimum)
                    comboH.setSizePolicy(QSizePolicy().Minimum, QSizePolicy().Minimum)
                    self.tabela_adjacencia.setCellWidget(cont,x,comboH)
                    self.tabela_adjacencia.setCellWidget(x,cont,comboV)
                        
            for x in xrange(self.tabela_adjacencia.rowCount()):
                for y in xrange(self.tabela_adjacencia.rowCount()):
                    item = self.tabela_adjacencia.cellWidget(x, y)
                    self.connect(item, SIGNAL('currentIndexChanged(int)'),self.signalMapper, SLOT('map()'))
                    self.signalMapper.setMapping(item, '{0};{1}'.format(x,y))
                    self.connect(self.signalMapper, SIGNAL('mapped(QString)'), self.valorAlterado)
                    
            self.tabela_adjacencia.resizeColumnsToContents()
            self.tabela_adjacencia.resizeRowsToContents()
            self.fecho_origem.addItems(vertices)
            self.informacoes_vertice.addItems(vertices)
            self.busca_destino.addItems(vertices)
            self.busca_origem.addItems(vertices)
            self.remover_vertice_combo.addItems(vertices)
            self.ordem_resultado.setText(str(self.grafo.obterOrdem()))
            self.gerar_arestas_button.setEnabled(True)
            self.vertices_edit.clear()
    
    @pyqtSlot()            
    def on_remover_vertice_button_clicked(self):
        QMessageBox.critical(self, 'ERRO INTERNO', u'Ação não disponívl devido à um erro interno do QT', buttons=QMessageBox.Ok, defaultButton=QMessageBox.NoButton)

        '''
        vertice = self.remover_vertice_combo.currentText()
        colunas = self.tabela_adjacencia.columnCount()
        for x in xrange(colunas):
            if vertice == str(self.tabela_adjacencia.horizontalHeaderItem(x).text()):
                for y in xrange(self.tabela_adjacencia.columnCount()):
                    self.tabela_adjacencia.removeCellWidget(y,x)
                self.tabela_adjacencia.removeColumn(x)
                self.fecho_origem.removeItem(self.fecho_origem.findText(vertice))
                self.informacoes_vertice.removeItem(self.informacoes_vertice.findText(vertice))
                self.busca_destino.removeItem(self.busca_destino.findText(vertice))
                self.busca_origem.removeItem(self.busca_origem.findText(vertice))
                self.remover_vertice_combo.removeItem(self.remover_vertice_combo.findText(vertice))
                self.ordem_resultado.setText(str(self.grafo.obterOrdem()))
                break
        '''
           
    @pyqtSlot()
    def on_gerar_arestas_button_clicked(self):
        self.limparInferencias()
        self.grafo.limpar()
        n_linhas = self.tabela_adjacencia.rowCount()
        n_colunas = self.tabela_adjacencia.columnCount()
        for x in xrange(n_linhas):
            for y in xrange(x,n_colunas):
                item = self.tabela_adjacencia.cellWidget(x, y)
                if item.currentText() == '1':
                    v1 = str(self.tabela_adjacencia.horizontalHeaderItem(x).text())
                    v2 = str(self.tabela_adjacencia.verticalHeaderItem(y).text())
                    valor = None
                    if self.valorar_aresta.isChecked():
                        ok = False
                        while not ok:
                            valor,ok = QInputDialog().getText(self, 'Dados para a aresta {0}-{1} (separados por ",")'.format(v1,v2), 
                                                           'Dados:', mode=QLineEdit.Normal, 
                                                           text=QString())
                            valor = str(valor)
                            if not ok:
                                sair = QMessageBox.question(self, 'Cancelar?', u'deseja cancelar a criação do grafo?',
                                                            buttons=QMessageBox.Yes | QMessageBox.No, defaultButton=QMessageBox.NoButton)
                                
                                if sair == QMessageBox.Yes:
                                    self.grafo.limpar()
                                    return
                            elif valor == '':
                                atribuir_zero = QMessageBox.question(self, 'Valor em branco!', u'Atribuit valor "0" à aresta?', 
                                                                     buttons=QMessageBox.Yes | QMessageBox.No, defaultButton=QMessageBox.NoButton)
                                if atribuir_zero:
                                    valor = '0'
                                else:
                                    valor = None
                            valor = valor.split(',')
                            valor = {string.split(':')[0]:string.split(':')[1] for string in valor}
                            for dado in valor:
                                if valor[dado].isdigit():
                                    valor[dado] = int(valor[dado])
                                else:
                                    try:
                                        valor[dado] = float(valor[dado])
                                    except ValueError:
                                        pass
                    
                    self.grafo.adicionarAresta(v1, v2, valor)
        self.eh_conexo.setChecked(self.grafo.ehConexo())
        self.eh_arvore.setChecked(self.grafo.ehArvore())
        self.eh_regular.setChecked(self.grafo.ehRegular())
        self.eh_completo.setChecked(self.grafo.ehCompleto())
    
    @pyqtSlot()
    def on_busca_profundidade_button_clicked(self):
        self.busca_resultado.clear()
        verticeInicial = str(self.busca_origem.currentText())
        verticeFinal = str(self.busca_destino.currentText())
        resultados = self.grafo.buscaProfundidade(verticeInicial, verticeFinal)
        for resultado in resultados:
            caminho = str([vertice.obterNome() for vertice in resultado]) + '\n'
            item = QListWidgetItem(caminho)
            self.busca_resultado.addItem(item)
    @pyqtSlot()
    def on_informacoes_button_clicked(self):
        self.busca_resultado.clear()
        vertice = str(self.informacoes_vertice.currentText())
        '''Mostrar grau'''
        grau = self.grafo.obterGrau(vertice)
        self.busca_resultado.addItem(QListWidgetItem('Grau: ' + str(grau)))
        
        '''Mostrar adjacentes e info das arestas'''
        adjacentes = self.grafo.obterAdjacentes(vertice)
        self.busca_resultado.addItem(QListWidgetItem('Adjacentes:'))
        for adjacente in adjacentes:
            texto_valor = ''
            valor = self.grafo.obterAresta(vertice, adjacente.obterNome())
            texto = '\t'+adjacente.obterNome()
            if valor:
                for x in valor:
                    texto_valor +=  str(x) + ' : ' + str(valor[x]) + ', '
                texto += '-> ' + texto_valor[:-2]
            self.busca_resultado.addItem(QListWidgetItem(texto))
    
    @pyqtSlot('QString')
    def on_fecho_origem_activated(self, vertice):
        fecho = self.grafo.fechoTransitivo(str(vertice), set())
        resultado = ''
        while fecho:
            resultado += fecho.pop() + ', '
        self.fecho_resultado.setText(resultado[:-2])
        
    @pyqtSlot('bool')
    def on_valorar_aresta_toggled(self,marcado):
        if not marcado:
            self.eh_funcao.setChecked(marcado)
            self.funcao_edit.setEnabled(marcado)
        self.eh_funcao.setEnabled(marcado)
        
    @pyqtSlot('bool')
    def on_eh_funcao_toggled(self,marcado):
        self.funcao_edit.setEnabled(marcado)
    
    @pyqtSlot('QString')
    def on_nome_edit_textChanged(self, texto):
        if texto == '':
            self.adicionar_vertice_button.setEnabled(False)
        elif len(self.vertices_edit.text()) > 0:
            self.adicionar_vertice_button.setEnabled(True)

    @pyqtSlot('QString')
    def on_vertices_edit_textChanged(self, texto):
        if texto == '':
            self.adicionar_vertice_button.setEnabled(False)
        elif len(self.nome_edit.text()) > 0:
            self.adicionar_vertice_button.setEnabled(True)
                
    def processEvent(self, notificador, evento, *args):
        if notificador == self.grafo:
            if evento == 'VerticeNaoExiste':
                self.erro_vertice_nao_existe.emit(args[0])
            elif evento == 'VerticeJahExiste':
                self.erro_vertice_existe.emit()
    
    def limparItens(self):
        self.fecho_origem.clear()
        self.informacoes_vertice.clear()
        self.busca_destino.clear()
        self.busca_origem.clear()
        self.ordem_resultado.setText('')
        
    def limparInferencias(self):
        self.eh_conexo.setChecked(False)
        self.eh_arvore.setChecked(False)
        self.eh_regular.setChecked(False)
        self.eh_completo.setChecked(False)
        
    def verticeExiste(self):
        QMessageBox(self).critical(self, 'ERRO!', u'Existe mais de um vértice com o mesmo nome!', buttons=QMessageBox.Ok)
        self.limparInferencias()
        self.limparItens()
        self.tabela_adjacencia.setRowCount(0)
        self.tabela_adjacencia.setColumnCount(0)
    
    def verticeNaoExiste(self,e):
        QMessageBox(self).critical(self, 'ERRO', e, buttons=QMessageBox.Ok)
    
    @pyqtSlot('QString')
    def valorAlterado(self,string):
        string = str(string)
        pos = string.split(';')
        item2 = self.tabela_adjacencia.cellWidget(int(pos[1]), int(pos[0]))
        item1 = self.tabela_adjacencia.cellWidget(int(pos[0]), int(pos[1]))
        item2.setCurrentIndex(item1.currentIndex())
예제 #34
0
class Panel(QDialog):
    # A list of two-sized tuples (QWidget's name, model field name).
    FIELDS = []

    def __init__(self, parent=None):
        # The flags we pass are that so we don't get the "What's this" button in the title bar
        QDialog.__init__(self, parent, Qt.WindowTitleHint | Qt.WindowSystemMenuHint)
        self._widget2ModelAttr = {}

    def _changeComboBoxItems(self, comboBox, newItems):
        # When a combo box's items are changed, its currentIndex changed with a currentIndexChanged
        # signal, and if that signal results in the model being updated, it messes the model.
        # We thus have to disconnect the combo box's signal before changing the items.
        if comboBox in self._widget2ModelAttr:
            comboBox.currentIndexChanged.disconnect(self.comboBoxCurrentIndexChanged)
        index = comboBox.currentIndex()
        comboBox.clear()
        comboBox.addItems(newItems)
        comboBox.setCurrentIndex(index)
        if comboBox in self._widget2ModelAttr:
            comboBox.currentIndexChanged.connect(self.comboBoxCurrentIndexChanged)

    def _connectSignals(self):
        self._signalMapper = QSignalMapper()
        for widgetName, modelAttr in self.FIELDS:
            widget = getattr(self, widgetName)
            self._widget2ModelAttr[widget] = modelAttr
            self._signalMapper.setMapping(widget, widget)
            if isinstance(widget, QComboBox):
                widget.currentIndexChanged.connect(self._signalMapper.map)
            elif isinstance(widget, QSpinBox):
                widget.valueChanged.connect(self._signalMapper.map)
            elif isinstance(widget, QLineEdit):
                widget.editingFinished.connect(self._signalMapper.map)
            elif isinstance(widget, QPlainTextEdit):
                widget.textChanged.connect(self._signalMapper.map)
            elif isinstance(widget, QCheckBox):
                widget.stateChanged.connect(self._signalMapper.map)
        self._signalMapper.mapped[QWidget].connect(self.widgetChanged)

    def _loadFields(self):
        for widgetName, modelAttr in self.FIELDS:
            widget = getattr(self, widgetName)
            value = getattr(self.model, modelAttr)
            if isinstance(widget, QComboBox):
                widget.setCurrentIndex(value)
            elif isinstance(widget, QSpinBox):
                widget.setValue(value)
            elif isinstance(widget, QLineEdit):
                widget.setText(value)
            elif isinstance(widget, QPlainTextEdit):
                widget.setPlainText(value)
            elif isinstance(widget, QCheckBox):
                widget.setChecked(value)

    def _saveFields(self):
        pass

    def accept(self):
        # The setFocus() call is to force the last edited field to "commit". When the save button
        # is clicked, accept() is called before the last field to have focus has a chance to emit
        # its edition signal.
        self.setFocus()
        self.model.save()
        QDialog.accept(self)

    #--- Event Handlers
    def widgetChanged(self, sender):
        modelAttr = self._widget2ModelAttr[sender]
        if isinstance(sender, QComboBox):
            newvalue = sender.currentIndex()
        elif isinstance(sender, QSpinBox):
            newvalue = sender.value()
        elif isinstance(sender, QLineEdit):
            newvalue = sender.text()
        elif isinstance(sender, QPlainTextEdit):
            newvalue = sender.toPlainText()
        elif isinstance(sender, QCheckBox):
            newvalue = sender.isChecked()
        setattr(self.model, modelAttr, newvalue)

    #--- model --> view
    def pre_load(self):
        pass

    def pre_save(self):
        self._saveFields()

    def post_load(self):
        if not self._widget2ModelAttr: # signal not connected yet
            self._connectSignals()
        self._loadFields()
        self.show()
        # For initial text edits to have their text selected, we *have to* first select the dialog,
        # then setFocus on it with qt.TabFocusReason. Don't ask, I don't know why either...
        self.setFocus()
        focus = self.nextInFocusChain()
        while focus.focusPolicy() == Qt.NoFocus:
            focus = focus.nextInFocusChain()
        focus.setFocus(Qt.TabFocusReason)
예제 #35
0
파일: toolbox.py 프로젝트: yisuax11/orange2
class ToolBox(QFrame):
    """
    A tool box widget.
    """
    # Emitted when a tab is toggled.
    tabToogled = Signal(int, bool)

    def setExclusive(self, exclusive):
        """
        Set exclusive tabs (only one tab can be open at a time).
        """
        if self.__exclusive != exclusive:
            self.__exclusive = exclusive
            self.__tabActionGroup.setExclusive(exclusive)
            checked = self.__tabActionGroup.checkedAction()
            if checked is None:
                # The action group can be out of sync with the actions state
                # when switching between exclusive states.
                actions_checked = [
                    page.action for page in self.__pages
                    if page.action.isChecked()
                ]
                if actions_checked:
                    checked = actions_checked[0]

            # Trigger/toggle remaining open pages
            if exclusive and checked is not None:
                for page in self.__pages:
                    if checked != page.action and page.action.isChecked():
                        page.action.trigger()

    def exclusive(self):
        """
        Are the tabs in the toolbox exclusive.
        """
        return self.__exclusive

    exclusive_ = Property(bool,
                          fget=exclusive,
                          fset=setExclusive,
                          designable=True,
                          doc="Exclusive tabs")

    def __init__(self, parent=None, **kwargs):
        QFrame.__init__(self, parent, **kwargs)

        self.__pages = []
        self.__tabButtonHeight = -1
        self.__tabIconSize = QSize()
        self.__exclusive = False
        self.__setupUi()

    def __setupUi(self):
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)

        # Scroll area for the contents.
        self.__scrollArea = \
                _ToolBoxScrollArea(self, objectName="toolbox-scroll-area")

        self.__scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.__scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.__scrollArea.setSizePolicy(QSizePolicy.MinimumExpanding,
                                        QSizePolicy.MinimumExpanding)
        self.__scrollArea.setFrameStyle(QScrollArea.NoFrame)
        self.__scrollArea.setWidgetResizable(True)

        # A widget with all of the contents.
        # The tabs/contents are placed in the layout inside this widget
        self.__contents = QWidget(self.__scrollArea,
                                  objectName="toolbox-contents")

        # The layout where all the tab/pages are placed
        self.__contentsLayout = QVBoxLayout()
        self.__contentsLayout.setContentsMargins(0, 0, 0, 0)
        self.__contentsLayout.setSizeConstraint(QVBoxLayout.SetMinAndMaxSize)
        self.__contentsLayout.setSpacing(0)

        self.__contents.setLayout(self.__contentsLayout)

        self.__scrollArea.setWidget(self.__contents)

        layout.addWidget(self.__scrollArea)

        self.setLayout(layout)
        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding)

        self.__tabActionGroup = \
                QActionGroup(self, objectName="toolbox-tab-action-group")

        self.__tabActionGroup.setExclusive(self.__exclusive)

        self.__actionMapper = QSignalMapper(self)
        self.__actionMapper.mapped[QObject].connect(self.__onTabActionToogled)

    def setTabButtonHeight(self, height):
        """
        Set the tab button height.
        """
        if self.__tabButtonHeight != height:
            self.__tabButtonHeight = height
            for page in self.__pages:
                page.button.setFixedHeight(height)

    def tabButtonHeight(self):
        """
        Return the tab button height.
        """
        return self.__tabButtonHeight

    def setTabIconSize(self, size):
        """
        Set the tab button icon size.
        """
        if self.__tabIconSize != size:
            self.__tabIconSize = size
            for page in self.__pages:
                page.button.setIconSize(size)

    def tabIconSize(self):
        """
        Return the tab icon size.
        """
        return self.__tabIconSize

    def tabButton(self, index):
        """
        Return the tab button at `index`
        """
        return self.__pages[index].button

    def tabAction(self, index):
        """
        Return open/close action for the tab at `index`.
        """
        return self.__pages[index].action

    def addItem(self, widget, text, icon=None, toolTip=None):
        """
        Append the `widget` in a new tab and return its index.

        Parameters
        ----------
        widget : :class:`QWidget`
            A widget to be inserted. The toolbox takes ownership
            of the widget.

        text : str
            Name/title of the new tab.

        icon : :class:`QIcon`, optional
            An icon for the tab button.

        toolTip : str, optional
            Tool tip for the tab button.

        """
        return self.insertItem(self.count(), widget, text, icon, toolTip)

    def insertItem(self, index, widget, text, icon=None, toolTip=None):
        """
        Insert the `widget` in a new tab at position `index`.

        See also
        --------
        ToolBox.addItem

        """
        button = self.createTabButton(widget, text, icon, toolTip)

        self.__contentsLayout.insertWidget(index * 2, button)
        self.__contentsLayout.insertWidget(index * 2 + 1, widget)

        widget.hide()

        page = _ToolBoxPage(index, widget, button.defaultAction(), button)
        self.__pages.insert(index, page)

        for i in range(index + 1, self.count()):
            self.__pages[i] = self.__pages[i]._replace(index=i)

        self.__updatePositions()

        # Show (open) the first tab.
        if self.count() == 1 and index == 0:
            page.action.trigger()

        self.__updateSelected()

        self.updateGeometry()
        return index

    def removeItem(self, index):
        """
        Remove the widget at `index`.

        .. note:: The widget hidden but is is not deleted.

        """
        self.__contentsLayout.takeAt(2 * index + 1)
        self.__contentsLayout.takeAt(2 * index)
        page = self.__pages.pop(index)

        # Update the page indexes
        for i in range(index, self.count()):
            self.__pages[i] = self.__pages[i]._replace(index=i)

        page.button.deleteLater()

        # Hide the widget and reparent to self
        # This follows QToolBox.removeItem
        page.widget.hide()
        page.widget.setParent(self)

        self.__updatePositions()
        self.__updateSelected()

        self.updateGeometry()

    def count(self):
        """
        Return the number of widgets inserted in the toolbox.
        """
        return len(self.__pages)

    def widget(self, index):
        """
        Return the widget at `index`.
        """
        return self.__pages[index].widget

    def createTabButton(self, widget, text, icon=None, toolTip=None):
        """
        Create the tab button for `widget`.
        """
        action = QAction(text, self)
        action.setCheckable(True)

        if icon:
            action.setIcon(icon)

        if toolTip:
            action.setToolTip(toolTip)
        self.__tabActionGroup.addAction(action)
        self.__actionMapper.setMapping(action, action)
        action.toggled.connect(self.__actionMapper.map)

        button = ToolBoxTabButton(self, objectName="toolbox-tab-button")
        button.setDefaultAction(action)
        button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)

        if self.__tabIconSize.isValid():
            button.setIconSize(self.__tabIconSize)

        if self.__tabButtonHeight > 0:
            button.setFixedHeight(self.__tabButtonHeight)

        return button

    def ensureWidgetVisible(self, child, xmargin=50, ymargin=50):
        """
        Scroll the contents so child widget instance is visible inside
        the viewport.

        """
        self.__scrollArea.ensureWidgetVisible(child, xmargin, ymargin)

    def sizeHint(self):
        hint = self.__contentsLayout.sizeHint()

        if self.count():
            # Compute max width of hidden widgets also.
            scroll = self.__scrollArea
            scroll_w = scroll.verticalScrollBar().sizeHint().width()
            frame_w = self.frameWidth() * 2 + scroll.frameWidth() * 2
            max_w = max([p.widget.sizeHint().width() for p in self.__pages])
            hint = QSize(
                max(max_w, hint.width()) + scroll_w + frame_w, hint.height())

        return QSize(200, 200).expandedTo(hint)

    def __onTabActionToogled(self, action):
        page = find(self.__pages, action, key=attrgetter("action"))
        on = action.isChecked()
        page.widget.setVisible(on)
        index = page.index

        if index > 0:
            # Update the `previous` tab buttons style hints
            previous = self.__pages[index - 1].button
            flag = QStyleOptionToolBoxV2.NextIsSelected
            if on:
                previous.selected |= flag
            else:
                previous.selected &= ~flag

            previous.update()

        if index < self.count() - 1:
            next = self.__pages[index + 1].button
            flag = QStyleOptionToolBoxV2.PreviousIsSelected
            if on:
                next.selected |= flag
            else:
                next.selected &= ~flag

            next.update()

        self.tabToogled.emit(index, on)

        self.__contentsLayout.invalidate()

    def __updateSelected(self):
        """Update the tab buttons selected style flags.
        """
        if self.count() == 0:
            return

        opt = QStyleOptionToolBoxV2

        def update(button, next_sel, prev_sel):
            if next_sel:
                button.selected |= opt.NextIsSelected
            else:
                button.selected &= ~opt.NextIsSelected

            if prev_sel:
                button.selected |= opt.PreviousIsSelected
            else:
                button.selected &= ~opt.PreviousIsSelected

            button.update()

        if self.count() == 1:
            update(self.__pages[0].button, False, False)
        elif self.count() >= 2:
            pages = self.__pages
            for i in range(1, self.count() - 1):
                update(pages[i].button, pages[i + 1].action.isChecked(),
                       pages[i - 1].action.isChecked())

    def __updatePositions(self):
        """Update the tab buttons position style flags.
        """
        if self.count() == 0:
            return
        elif self.count() == 1:
            self.__pages[0].button.position = QStyleOptionToolBoxV2.OnlyOneTab
        else:
            self.__pages[0].button.position = QStyleOptionToolBoxV2.Beginning
            self.__pages[-1].button.position = QStyleOptionToolBoxV2.End
            for p in self.__pages[1:-1]:
                p.button.position = QStyleOptionToolBoxV2.Middle

        for p in self.__pages:
            p.button.update()
예제 #36
0
파일: complex.py 프로젝트: StyXman/satyr
class QPlayListModel (QAbstractTableModel):
    def __init__ (self, collaggr=None, songs=None, view=None):
        QAbstractTableModel.__init__ (self, view)
        # TODO: different delegate for editing tags: one with completion
        self.view_= view
        self.playlist= view.playlist
        self.edited= False

        if songs is None:
            self.collaggr= collaggr
            self.collections= self.collaggr.collections

            self.signalMapper= QSignalMapper ()
            for collNo, collection in enumerate (self.collections):
                collection.newSongs.connect (self.signalMapper.map)
                self.signalMapper.setMapping (collection, collNo)

            self.signalMapper.mapped.connect (self.addRows)
        else:
            self.collaggr= CollectionAggregator (self, songs=songs)

        self.attrNames= ('artist', 'year', 'collection', 'diskno', 'album', 'trackno', 'title', 'length', 'filepath')

        self.headers= (u'Artist', u'Year', u'Collection', u'CD', u'Album', u'Track', u'Title', u'Length', u'Path')
        # FIXME: kinda hacky
        self.fontMetrics= QFontMetrics (KGlobalSettings.generalFont ())
        # FIXME: (even more) hackish
        self.columnWidths= ("M"*15, "M"*4, "M"*15, "M"*3, "M"*20, "M"*3, "M"*25, "M"*5, "M"*200)
        logger.debug ("QPLM: ", self)

    def data (self, modelIndex, role):
        # TODO: allow to enter to edit mode in filepath but don't save any changes
        # so we can copy the path, that is...
        if modelIndex.isValid () and modelIndex.row ()<self.collaggr.count:
            song= self.collaggr.songForIndex (modelIndex.row ())

            if role==Qt.DisplayRole or role==Qt.EditRole:
                attr= self.attrNames [modelIndex.column ()]
                rawData= song[attr]
                if attr=='filepath':
                    # filenames as they are
                    rawData= QFile.decodeName (rawData)
                elif attr=='title' and role!=Qt.EditRole:
                    # don't (even try to) add the [#] to the title
                    try:
                        queueIndex= self.playlist.indexQueue.index (modelIndex.row ())
                        # make it show as starting in 1, otherwise it's confusing
                        rawData= "[%d] %s" % (queueIndex+1, rawData)
                    except ValueError:
                        pass
                data= QVariant (rawData)

            elif role==Qt.SizeHintRole:
                size= self.fontMetrics.size (Qt.TextSingleLine, self.columnWidths[modelIndex.column ()])
                data= QVariant (size)

            elif role==Qt.BackgroundRole and self.view_.modelIndex is not None:
                if modelIndex.row ()==self.view_.modelIndex.row ():
                    # highlight the current song
                    # must return a QBrush
                    data= QVariant (QApplication.palette ().dark ())
                else:
                    try:
                        queueIndex= self.playlist.indexQueue.index (modelIndex.row ())
                        data= QVariant (QApplication.palette ().mid ())
                    except ValueError:
                        data= QVariant ()

            elif role==Qt.ForegroundRole and self.view_.modelIndex is not None:
                if modelIndex.row ()==self.view_.modelIndex.row ():
                    # highlight the current song
                    # must return a QBrush
                    data= QVariant (QApplication.palette ().brightText ())
                else:
                    data= QVariant ()

            else:
                data= QVariant ()
        else:
            data= QVariant ()

        return data

    def flags (self, modelIndex):
        ans= QAbstractTableModel.flags (self, modelIndex)
        if modelIndex.column ()<7: # length or filepath are not editable
            ans= ans|Qt.ItemIsEditable|Qt.ItemIsEditable

        return ans

    def match (self, start, role, value, hits=1, flags=None):
        # when you press a key on an uneditable cell, QTableView tries to search
        # calling this function for matching. we already have a way for searching
        # and it loads the metadata of all the songs anyways
        # so we disable it by constantly returning an empty list
        return []

    def setData (self, modelIndex, variant, role=Qt.EditRole):
        # not length or filepath and editing
        if modelIndex.column ()<7 and role==Qt.EditRole:
            logger.debug ("QPLM.setData()", modelIndex.row (), modelIndex.column(), role, ">%s<", unicode (variant.toString ()))
            song= self.collaggr.songForIndex (modelIndex.row ())
            attr= self.attrNames[modelIndex.column ()]
            try:
                song[attr]= unicode (variant.toString ())
                # TODO: make a list of dirty songs and commit them later
                song.saveMetadata ()
            except TagWriteError:
                # it failed
                ans= False
            else:
                self.edited= True
                self.dataChanged.emit (modelIndex, modelIndex)
                ans= True
        else:
            ans= QAbstractTableModel.setData (self, modelIndex, variant, role)

        logger.debug ("QPLM.setData():", modelIndex.row(), modelIndex.column (), role, ans)
        return ans

    def headerData (self, section, direction, role=Qt.DisplayRole):
        if direction==Qt.Horizontal and role==Qt.DisplayRole:
            data= QVariant (self.headers[section])
        elif direction==Qt.Vertical:
            if role==Qt.SizeHintRole:
                # TODO: support bold fonts
                # again, hacky. 5 for enough witdh for 5 digits
                size= self.fontMetrics.size (Qt.TextSingleLine, "M"*5)
                data= QVariant (size)
            elif role==Qt.TextAlignmentRole:
                data= QVariant (Qt.AlignRight|Qt.AlignVCenter)
            else:
                data= QAbstractTableModel.headerData (self, section, direction, role)
        else:
            data= QAbstractTableModel.headerData (self, section, direction, role)

        return data

    def addRows (self, collNo):
        collection= self.collections[collNo]

        for index, filepath in collection.newSongs_:
            self.beginInsertRows (QModelIndex (), index, index)
            self.endInsertRows ()

            modelIndex= self.index (index, 0)
            self.dataChanged.emit (modelIndex, modelIndex)

    def dirtyRow (self, index):
        logger.debug ("QLMP.dirtyRow():", index)
        columns= self.columnCount ()
        start= self.index (index, 0)
        end=   self.index (index, columns)
        self.edited= False
        self.dataChanged.emit (start, end)

    def rowCount (self, parent=None):
        return self.collaggr.count

    def columnCount (self, parent=None):
        return len (self.attrNames)
예제 #37
0
def main(icon_spec):
    app = QApplication(sys.argv)

    main_window = QMainWindow()

    def sigint_handler(*args):
        main_window.close()
    signal.signal(signal.SIGINT, sigint_handler)
    # the timer enables triggering the sigint_handler
    signal_timer = QTimer()
    signal_timer.start(100)
    signal_timer.timeout.connect(lambda: None)

    tool_bar = QToolBar()
    main_window.addToolBar(Qt.TopToolBarArea, tool_bar)

    table_view = QTableView()
    table_view.setSelectionBehavior(QAbstractItemView.SelectRows)
    table_view.setSelectionMode(QAbstractItemView.SingleSelection)
    table_view.setSortingEnabled(True)
    main_window.setCentralWidget(table_view)

    proxy_model = QSortFilterProxyModel()
    proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive)
    proxy_model.setFilterKeyColumn(1)
    table_view.setModel(proxy_model)
    proxy_model.layoutChanged.connect(table_view.resizeRowsToContents)

    item_model = QStandardItemModel()
    proxy_model.setSourceModel(item_model)

    # get all icons and their available sizes
    QIcon.setThemeName("gnome")
    icons = []
    all_sizes = set([])
    for context, icon_names in icon_spec:
        for icon_name in icon_names:
            icon = QIcon.fromTheme(icon_name)
            sizes = []
            for size in icon.availableSizes():
                size = (size.width(), size.height())
                sizes.append(size)
                all_sizes.add(size)
            sizes.sort()
            icons.append({
                'context': context,
                'icon_name': icon_name,
                'icon': icon,
                'sizes': sizes,
            })
    all_sizes = list(all_sizes)
    all_sizes.sort()

    # input field for filter
    def filter_changed(value):
        proxy_model.setFilterRegExp(value)
        table_view.resizeRowsToContents()
    filter_line_edit = QLineEdit()
    filter_line_edit.setMaximumWidth(200)
    filter_line_edit.setPlaceholderText('Filter name')
    filter_line_edit.setToolTip('Filter name optionally using regular expressions (' + QKeySequence(QKeySequence.Find).toString() + ')')
    filter_line_edit.textChanged.connect(filter_changed)
    tool_bar.addWidget(filter_line_edit)

    # actions to toggle visibility of available sizes/columns 
    def action_toggled(index):
        column = 2 + index
        table_view.setColumnHidden(column, not table_view.isColumnHidden(column))
        table_view.resizeColumnsToContents()
        table_view.resizeRowsToContents()
    signal_mapper = QSignalMapper()
    for i, size in enumerate(all_sizes):
        action = QAction('%dx%d' % size, tool_bar)
        action.setCheckable(True)
        action.setChecked(True)
        tool_bar.addAction(action)
        action.toggled.connect(signal_mapper.map)
        signal_mapper.setMapping(action, i)
        # set tool tip and handle key sequence
        tool_tip = 'Toggle visibility of column'
        if i < 10:
            digit = ('%d' % (i + 1))[-1]
            tool_tip += ' (%s)' % QKeySequence('Ctrl+%s' % digit).toString()
        action.setToolTip(tool_tip)
    signal_mapper.mapped.connect(action_toggled)

    # label columns
    header_labels = ['context', 'name']
    for width, height in all_sizes:
        header_labels.append('%dx%d' % (width, height))
    item_model.setColumnCount(len(header_labels))
    item_model.setHorizontalHeaderLabels(header_labels)

    # fill rows
    item_model.setRowCount(len(icons))
    for row, icon_data in enumerate(icons):
        # context
        item = QStandardItem(icon_data['context'])
        item.setFlags(item.flags() ^ Qt.ItemIsEditable)
        item_model.setItem(row, 0, item)
        # icon name
        item = QStandardItem(icon_data['icon_name'])
        item.setFlags(item.flags() ^ Qt.ItemIsEditable)
        item_model.setItem(row, 1, item)
        for index_in_all_sizes, size in enumerate(all_sizes):
            column = 2 + index_in_all_sizes
            if size in icon_data['sizes']:
                # icon as pixmap to keep specific size
                item = QStandardItem('')
                pixmap = icon_data['icon'].pixmap(size[0], size[1])
                item.setData(pixmap, Qt.DecorationRole)
                item.setFlags(item.flags() ^ Qt.ItemIsEditable)
                item_model.setItem(row, column, item)
            else:
                # single space to be sortable against icons
                item = QStandardItem(' ')
                item.setFlags(item.flags() ^ Qt.ItemIsEditable)
                item_model.setItem(row, column, item)

    table_view.resizeColumnsToContents()
    # manually set row heights because resizeRowsToContents is not working properly
    for row, icon_data in enumerate(icons):
        if len(icon_data['sizes']) > 0:
            max_size = icon_data['sizes'][-1]
            table_view.setRowHeight(row, max_size[1])

    # enable focus find (ctrl+f) and toggle columns (ctrl+NUM)
    def main_window_keyPressEvent(self, event, old_keyPressEvent=QMainWindow.keyPressEvent):
        if event.matches(QKeySequence.Find):
            filter_line_edit.setFocus()
            return
        if event.modifiers() == Qt.ControlModifier and event.key() >= Qt.Key_0 and event.key() <= Qt.Key_9:
            index = event.key() - Qt.Key_1
            if event.key() == Qt.Key_0:
                index += 10
            action = signal_mapper.mapping(index)
            if action:
                action.toggle()
                return
        old_keyPressEvent(self, event)
    main_window.keyPressEvent = new.instancemethod(main_window_keyPressEvent, table_view, None)

    # enable copy (ctrl+c) name of icon to clipboard
    def table_view_keyPressEvent(self, event, old_keyPressEvent=QTableView.keyPressEvent):
        if event.matches(QKeySequence.Copy):
            selection_model = self.selectionModel()
            if selection_model.hasSelection():
                index = selection_model.selectedRows()[0]
                source_index = self.model().mapToSource(index)
                item = self.model().sourceModel().item(source_index.row(), 1)
                icon_name = item.data(Qt.EditRole)
                app.clipboard().setText(icon_name.toString())
                return
        old_keyPressEvent(self, event)
    table_view.keyPressEvent = new.instancemethod(table_view_keyPressEvent, table_view, None)
    print 'Icon Theme: ', QIcon.themeName()

    print 'Theme Search Paths:'
    for item in QIcon.themeSearchPaths():
        print item
    main_window.showMaximized()
    return app.exec_()
예제 #38
0
class TrackingWindow(QMainWindow):
    """
    Main window of the application.

    This class is responsible for the global data structures too.

    :IVariables:
        undo_stack : `QUndoStack`
            Undo stack. All actions that can be undone should be pushed on the stack.
        _project : `project.Project`
            Project object managing the loaded data
        _data : `tracking_data.TrackingData`
            Data object keeping track of points and cells
        toolGroup : `QActionGroup`
            Group of actions to be enabled only when actions can be taken on images
        previousSelAct : `QActionGroup`
            Actions enabled when points are selected in the previous pane
        currentSelAct : `QActionGroup`
            Actions enabled when points are selected in the current pane
        projectAct : `QActionGroup`
            Actions to enable once a project is loaded
        _previousScene : `tracking_scene.TrackingScene`
            Object managing the previous pane
        _currentScene : `tracking_scene.LinkedTrackingScene`
            Object managing the current pane
    """
    def __init__(self, *args, **kwords):
        QMainWindow.__init__(self, *args)
        self.undo_stack = QUndoStack(self)
        self.ui = Ui_TrackingWindow()
        self.ui.setupUi(self)
        self._project = None
        self._data = None
        self.toolGroup = QActionGroup(self)
        self.toolGroup.addAction(self.ui.actionAdd_point)
        self.toolGroup.addAction(self.ui.action_Move_point)
        self.toolGroup.addAction(self.ui.actionAdd_cell)
        self.toolGroup.addAction(self.ui.actionRemove_cell)
        self.toolGroup.addAction(self.ui.action_Pan)
        self.toolGroup.addAction(self.ui.actionZoom_out)
        self.toolGroup.addAction(self.ui.actionZoom_in)
        self.previousSelAct = QActionGroup(self)
        self.previousSelAct.addAction(self.ui.actionCopy_selection_from_Previous)
        self.previousSelAct.addAction(self.ui.actionDelete_Previous)
        self.previousSelAct.setEnabled(False)
        self.currentSelAct = QActionGroup(self)
        self.currentSelAct.addAction(self.ui.actionCopy_selection_from_Current)
        self.currentSelAct.addAction(self.ui.actionDelete_Current)
        self.currentSelAct.setEnabled(False)
        self.projectAct = QActionGroup(self)
        self.projectAct.addAction(self.ui.action_Next_image)
        self.projectAct.addAction(self.ui.action_Previous_image)
        self.projectAct.addAction(self.ui.actionAdd_point)
        self.projectAct.addAction(self.ui.action_Move_point)
        self.projectAct.addAction(self.ui.action_Pan)
        self.projectAct.addAction(self.ui.actionAdd_cell)
        self.projectAct.addAction(self.ui.actionRemove_cell)
        self.projectAct.addAction(self.ui.action_Change_data_file)
        self.projectAct.addAction(self.ui.actionNew_data_file)
        self.projectAct.addAction(self.ui.actionZoom_out)
        self.projectAct.addAction(self.ui.actionZoom_in)
        self.projectAct.addAction(self.ui.actionSave_as)
        self.projectAct.addAction(self.ui.action_Fit)
        self.projectAct.addAction(self.ui.actionZoom_100)
        self.projectAct.addAction(self.ui.actionMerge_points)
        self.projectAct.addAction(self.ui.actionCopy_from_previous)
        self.projectAct.addAction(self.ui.actionCopy_from_current)
        self.projectAct.addAction(self.ui.actionReset_alignment)
        self.projectAct.addAction(self.ui.actionAlign_images)
        self.projectAct.addAction(self.ui.actionSelectPreviousAll)
        self.projectAct.addAction(self.ui.actionSelectPreviousNew)
        self.projectAct.addAction(self.ui.actionSelectPreviousNone)
        self.projectAct.addAction(self.ui.actionSelectPreviousNon_associated)
        self.projectAct.addAction(self.ui.actionSelectPreviousAssociated)
        self.projectAct.addAction(self.ui.actionSelectPreviousInvert)
        self.projectAct.addAction(self.ui.actionSelectCurrentAll)
        self.projectAct.addAction(self.ui.actionSelectCurrentNew)
        self.projectAct.addAction(self.ui.actionSelectCurrentNone)
        self.projectAct.addAction(self.ui.actionSelectCurrentNon_associated)
        self.projectAct.addAction(self.ui.actionSelectCurrentAssociated)
        self.projectAct.addAction(self.ui.actionSelectCurrentInvert)
        self.projectAct.addAction(self.ui.actionEdit_timing)
        self.projectAct.addAction(self.ui.actionEdit_scales)
        self.projectAct.addAction(self.ui.actionCompute_growth)
        self.projectAct.addAction(self.ui.actionClean_cells)
        self.projectAct.addAction(self.ui.actionGotoCell)

        self.projectAct.setEnabled(False)

        current_sel_actions = [self.ui.actionSelectCurrentAll,
                               self.ui.actionSelectCurrentNew,
                               self.ui.actionSelectCurrentNone,
                               self.ui.actionSelectCurrentInvert,
                               '-',
                               self.ui.actionSelectCurrentNon_associated,
                               self.ui.actionSelectCurrentAssociated,
                               self.ui.actionCopy_selection_from_Previous
                               ]

        previous_sel_actions = [self.ui.actionSelectPreviousAll,
                                self.ui.actionSelectPreviousNew,
                                self.ui.actionSelectPreviousNone,
                                self.ui.actionSelectPreviousInvert,
                                '-',
                                self.ui.actionSelectPreviousNon_associated,
                                self.ui.actionSelectPreviousAssociated,
                                self.ui.actionCopy_selection_from_Current
                                ]

        self._previousScene = TrackingScene(self.undo_stack, self.ui.actionDelete_Previous,
                                            previous_sel_actions, self)
        self._currentScene = LinkedTrackingScene(self._previousScene, self.undo_stack,
                                                 self.ui.actionDelete_Current, current_sel_actions,
                                                 self)
        self._previousScene.hasSelectionChanged.connect(self.previousSelAct.setEnabled)
        self._currentScene.hasSelectionChanged.connect(self.currentSelAct.setEnabled)
        self._previousScene.realSceneSizeChanged.connect(self.sceneSizeChanged)
        self._currentScene.realSceneSizeChanged.connect(self.sceneSizeChanged)
        self._previousScene.zoomIn[QPointF].connect(self.zoomIn)
        self._currentScene.zoomIn.connect(self.zoomIn)
        self._previousScene.zoomOut[QPointF].connect(self.zoomOut)
        self._currentScene.zoomOut.connect(self.zoomOut)
        self.ui.previousData.setScene(self._previousScene)
        self.ui.currentData.setScene(self._currentScene)
        self.ui.previousData.setDragMode(QGraphicsView.ScrollHandDrag)
        self.ui.currentData.setDragMode(QGraphicsView.ScrollHandDrag)
        #self.ui.previousData.setCacheMode(QGraphicsView.CacheBackground)
        #self.ui.currentData.setCacheMode(QGraphicsView.CacheBackground)

# Redefine shortcuts to standard key sequences
        self.ui.action_Save.setShortcut(QKeySequence.Save)
        self.ui.actionSave_as.setShortcut(QKeySequence.SaveAs)
        self.ui.action_Open_project.setShortcut(QKeySequence.Open)
        self.ui.action_Undo.setShortcut(QKeySequence.Undo)
        self.ui.action_Redo.setShortcut(QKeySequence.Redo)
        self.ui.action_Next_image.setShortcut(QKeySequence.Forward)
        self.ui.action_Previous_image.setShortcut(QKeySequence.Back)

# Connecting undo stack signals
        self.ui.action_Undo.triggered.connect(self.undo)
        self.ui.action_Redo.triggered.connect(self.redo)
        self.undo_stack.canRedoChanged[bool].connect(self.ui.action_Redo.setEnabled)
        self.undo_stack.canUndoChanged[bool].connect(self.ui.action_Undo.setEnabled)
        self.undo_stack.redoTextChanged["const QString&"].connect(self.changeRedoText)
        self.undo_stack.undoTextChanged["const QString&"].connect(self.changeUndoText)
        self.undo_stack.cleanChanged[bool].connect(self.ui.action_Save.setDisabled)

#        link_icon = QIcon()
#        pix = QPixmap(":/icons/link.png")
#        link_icon.addPixmap(pix, QIcon.Normal, QIcon.On)
#        pix = QPixmap(":/icons/link_broken.png")
#        link_icon.addPixmap(pix, QIcon.Normal, QIcon.Off)
#        self.link_icon = link_icon
#        #self.ui.linkViews.setIconSize(QSize(64,32))
#        self.ui.linkViews.setIcon(link_icon)

        self._recent_projects_menu = QMenu(self)
        self.ui.actionRecent_projects.setMenu(self._recent_projects_menu)

        self._recent_projects_act = []
        self._projects_mapper = QSignalMapper(self)
        self._projects_mapper.mapped[int].connect(self.loadRecentProject)

        self.param_dlg = None

# Setting up the status bar
        bar = self.statusBar()
# Adding current directory
        cur_dir = QLabel("")
        bar.addPermanentWidget(cur_dir)
        self._current_dir_label = cur_dir
# Adding up zoom
        zoom = QLabel("")
        bar.addPermanentWidget(zoom)
        self._zoom_label = zoom
        self.changeZoom(1)

        self.loadConfig()

        parameters.instance.renderingChanged.connect(self.changeRendering)
        self.changeRendering()

    def changeRendering(self):
        if parameters.instance.use_OpenGL:
            self.ui.previousData.setViewport(QGLWidget(QGLFormat(QGL.SampleBuffers)))
            self.ui.currentData.setViewport(QGLWidget(QGLFormat(QGL.SampleBuffers)))
        else:
            self.ui.previousData.setViewport(QWidget())
            self.ui.currentData.setViewport(QWidget())

    def undo(self):
        self.undo_stack.undo()

    def redo(self):
        self.undo_stack.redo()

    def changeRedoText(self, text):
        self.ui.action_Redo.setText(text)
        self.ui.action_Redo.setToolTip(text)
        self.ui.action_Redo.setStatusTip(text)

    def changeUndoText(self, text):
        self.ui.action_Undo.setText(text)
        self.ui.action_Undo.setToolTip(text)
        self.ui.action_Undo.setStatusTip(text)

    def closeEvent(self, event):
        self.saveConfig()
        if not self.ensure_save_data("Exiting whith unsaved data",
                                     "The last modifications you made were not saved."
                                     " Are you sure you want to exit?"):
            event.ignore()
            return
        QMainWindow.closeEvent(self, event)
        #sys.exit(0)

    def loadConfig(self):
        params = parameters.instance
        self.ui.action_Show_vector.setChecked(params.show_vectors)
        self.ui.linkViews.setChecked(params.link_views)
        self.ui.action_Show_template.setChecked(parameters.instance.show_template)
        self.ui.actionShow_id.setChecked(parameters.instance.show_id)
        self.ui.action_Estimate_position.setChecked(parameters.instance.estimate)
        self.updateRecentFiles()
        params.recentProjectsChange.connect(self.updateRecentFiles)

    def updateRecentFiles(self):
        for a in self._recent_projects_act:
            self._projects_mapper.removeMappings(a)
        del self._recent_projects_act[:]
        menu = self._recent_projects_menu
        menu.clear()
        recent_projects = parameters.instance.recent_projects
        for i, p in enumerate(recent_projects):
            act = QAction(self)
            act.setText("&{0:d} {1}".format(i + 1, p))
            self._recent_projects_act.append(act)
            act.triggered.connect(self._projects_mapper.map)
            self._projects_mapper.setMapping(act, i)
            menu.addAction(act)

    def saveConfig(self):
        parameters.instance.save()

    def check_for_data(self):
        if self._project is None:
            QMessageBox.critical(self, "No project loaded",
                                 "You have to load a project before performing this operation")
            return False
        return True

    def loadRecentProject(self, i):
        if self.ensure_save_data("Leaving unsaved data",
                                 "The last modifications you made were not saved."
                                 " Are you sure you want to change project?"):
            self.loadProject(parameters.instance.recent_projects[i])

    @pyqtSignature("")
    def on_action_Open_project_triggered(self):
        if self.ensure_save_data("Leaving unsaved data",
                                 "The last modifications you made were not saved."
                                 " Are you sure you want to change project?"):
            dir_ = QFileDialog.getExistingDirectory(self, "Select a project directory",
                                                    parameters.instance._last_dir)
            if dir_:
                self.loadProject(dir_)

    def loadProject(self, dir_):
        dir_ = path(dir_)
        project = Project(dir_)
        if project.valid:
            self._project = project
        else:
            create = QMessageBox.question(self, "Invalid project directory",
                                          "This directory does not contain a valid project. Turn into a directory?",
                                          QMessageBox.No, QMessageBox.Yes)
            if create == QMessageBox.No:
                return
            project.create()
            self._project = project
        self._project.use()
        parameters.instance.add_recent_project(dir_)
        parameters.instance._last_dir = dir_
        if self._data is not None:
            _data = self._data
            _data.saved.disconnect(self.undo_stack.setClean)
        try:
            #self._project.load()
            self.load_data()
            _data = self._project.data
            _data.saved.connect(self.undo_stack.setClean)
            self._project.changedDataFile.connect(self.dataFileChanged)
            self._data = _data
            self._previousScene.changeDataManager(self._data)
            self._currentScene.changeDataManager(self._data)
            self.initFromData()
            self.projectAct.setEnabled(True)
        except TrackingDataException as ex:
            showException(self, "Error while loaded data", ex)

    def dataFileChanged(self, new_file):
        if new_file is None:
            self._current_dir_label.setText("")
        else:
            self._current_dir_label.setText(new_file)

    def initFromData(self):
        """
        Initialize the interface using the current data
        """
        self.ui.previousState.clear()
        self.ui.currentState.clear()
        for name in self._data.images_name:
            self.ui.previousState.addItem(name)
            self.ui.currentState.addItem(name)
        self.ui.previousState.setCurrentIndex(0)
        self.ui.currentState.setCurrentIndex(1)
        self._previousScene.changeImage(self._data.image_path(self._data.images_name[0]))
        self._currentScene.changeImage(self._data.image_path(self._data.images_name[1]))
        self.dataFileChanged(self._project.data_file)

    @pyqtSignature("int")
    def on_previousState_currentIndexChanged(self, index):
        #print "Previous image loaded: %s" % self._data.images[index]
        self.changeScene(self._previousScene, index)
        self._currentScene.changeImage(None)

    @pyqtSignature("int")
    def on_currentState_currentIndexChanged(self, index):
        #print "Current image loaded: %s" % self._data.images[index]
        self.changeScene(self._currentScene, index)

    def changeScene(self, scene, index):
        """
        Set the scene to use the image number index.
        """
        scene.changeImage(self._data.image_path(self._data.images_name[index]))

    @pyqtSignature("")
    def on_action_Save_triggered(self):
        self.save_data()

    @pyqtSignature("")
    def on_actionSave_as_triggered(self):
        fn = QFileDialog.getSaveFileName(self, "Select a data file to save in", self._project.data_dir,
                                               "CSV Files (*.csv);;All files (*.*)")
        if fn:
            self.save_data(path(fn))

    def save_data(self, data_file=None):
        if self._data is None:
            raise TrackingDataException("Trying to save data when none have been loaded")
        try:
            self._project.save(data_file)
            return True
        except TrackingDataException as ex:
            showException(self, "Error while saving data", ex)
            return False

    def load_data(self, **opts):
        if self._project is None:
            raise TrackingDataException("Trying to load data when no project have been loaded")
        try:
            if self._project.load(**opts):
                log_debug("Data file was corrected. Need saving.")
                self.ui.action_Save.setEnabled(True)
            else:
                log_debug("Data file is clean.")
                self.ui.action_Save.setEnabled(False)
            return True
        except TrackingDataException as ex:
            showException(self, "Error while loading data", ex)
            return False
        except RetryTrackingDataException as ex:
            if retryException(self, "Problem while loading data", ex):
                new_opts = dict(opts)
                new_opts.update(ex.method_args)
                return self.load_data(**new_opts)
            return False

    def ensure_save_data(self, title, reason):
        if self._data is not None and not self.undo_stack.isClean():
            button = QMessageBox.warning(self, title, reason, QMessageBox.Yes | QMessageBox.Save | QMessageBox.Cancel)
            if button == QMessageBox.Save:
                return self.save_data()
            elif button == QMessageBox.Cancel:
                return False
            self.undo_stack.clear()
        return True

    @pyqtSignature("")
    def on_action_Change_data_file_triggered(self):
        if self.ensure_save_data("Leaving unsaved data", "The last modifications you made were not saved."
                                 " Are you sure you want to change the current data file?"):
            fn = QFileDialog.getOpenFileName(self, "Select a data file to load", self._project.data_dir,
                                                   "CSV Files (*.csv);;All files (*.*)")
            if fn:
                self._project.data_file = str(fn)
                if self.load_data():
                    self._previousScene.resetNewPoints()
                    self._currentScene.resetNewPoints()

    @pyqtSignature("bool")
    def on_action_Show_vector_toggled(self, value):
        parameters.instance.show_vector = value
        self._currentScene.showVector(value)

    @pyqtSignature("bool")
    def on_action_Show_template_toggled(self, value):
        parameters.instance.show_template = value

    @pyqtSignature("bool")
    def on_actionShow_id_toggled(self, value):
        parameters.instance.show_id = value

    @pyqtSignature("")
    def on_action_Next_image_triggered(self):
        cur = self.ui.currentState.currentIndex()
        pre = self.ui.previousState.currentIndex()
        l = len(self._data.images_name)
        if cur < l-1 and pre < l-1:
            self.ui.previousState.setCurrentIndex(pre+1)
            self.ui.currentState.setCurrentIndex(cur+1)

    @pyqtSignature("")
    def on_action_Previous_image_triggered(self):
        cur = self.ui.currentState.currentIndex()
        pre = self.ui.previousState.currentIndex()
        if cur > 0 and pre > 0:
            self.ui.previousState.setCurrentIndex(pre-1)
            self.ui.currentState.setCurrentIndex(cur-1)

    @pyqtSignature("")
    def on_copyToPrevious_clicked(self):
        self._currentScene.copyFromLinked(self._previousScene)

    @pyqtSignature("")
    def on_copyToCurrent_clicked(self):
        self._previousScene.copyToLinked(self._currentScene)

    @pyqtSignature("bool")
    def on_action_Estimate_position_toggled(self, value):
        parameters.instance.estimate = value

#  @pyqtSignature("")
#  def on_action_Undo_triggered(self):
#    print "Undo"

#  @pyqtSignature("")
#  def on_action_Redo_triggered(self):
#    print "Redo"

    @pyqtSignature("bool")
    def on_action_Parameters_toggled(self, value):
        if value:
            from .parametersdlg import ParametersDlg
            self._previousScene.showTemplates()
            self._currentScene.showTemplates()
            #tracking_scene.saveParameters()
            parameters.instance.save()
            max_size = max(self._currentScene.width(), self._currentScene.height(),
                           self._previousScene.width(), self._previousScene.height(), 400)
            self.param_dlg = ParametersDlg(max_size, self)
            self.param_dlg.setModal(False)
            self.ui.action_Pan.setChecked(True)
            self.ui.actionAdd_point.setEnabled(False)
            self.ui.action_Move_point.setEnabled(False)
            self.ui.actionAdd_cell.setEnabled(False)
            self.ui.actionRemove_cell.setEnabled(False)
            self.ui.action_Undo.setEnabled(False)
            self.ui.action_Redo.setEnabled(False)
            self.ui.action_Open_project.setEnabled(False)
            self.ui.actionRecent_projects.setEnabled(False)
            self.ui.action_Change_data_file.setEnabled(False)
            self.ui.copyToCurrent.setEnabled(False)
            self.ui.copyToPrevious.setEnabled(False)
            self.param_dlg.finished[int].connect(self.closeParam)
            self.param_dlg.show()
        elif self.param_dlg:
            self.param_dlg.accept()

    def closeParam(self, value):
        if value == QDialog.Rejected:
            parameters.instance.load()
        self.ui.actionAdd_point.setEnabled(True)
        self.ui.action_Move_point.setEnabled(True)
        self.ui.actionAdd_cell.setEnabled(True)
        self.ui.actionRemove_cell.setEnabled(True)
        self.ui.action_Undo.setEnabled(True)
        self.ui.action_Redo.setEnabled(True)
        self.ui.action_Open_project.setEnabled(True)
        self.ui.actionRecent_projects.setEnabled(True)
        self.ui.action_Change_data_file.setEnabled(True)
        self.ui.copyToCurrent.setEnabled(True)
        self.ui.copyToPrevious.setEnabled(True)
        self._previousScene.showTemplates(False)
        self._currentScene.showTemplates(False)
        self._previousScene.update()
        self._currentScene.update()
        self.param_dlg = None
        self.ui.action_Parameters.setChecked(False)

    @pyqtSignature("bool")
    def on_actionZoom_in_toggled(self, value):
        if value:
            self._previousScene.mode = TrackingScene.ZoomIn
            self._currentScene.mode = TrackingScene.ZoomIn

    @pyqtSignature("bool")
    def on_actionZoom_out_toggled(self, value):
        if value:
            self._previousScene.mode = TrackingScene.ZoomOut
            self._currentScene.mode = TrackingScene.ZoomOut

    #def resizeEvent(self, event):
    #    self.ensureZoomFit()

    def ensureZoomFit(self):
        if self._data:
            prev_rect = self._previousScene.sceneRect()
            cur_rect = self._currentScene.sceneRect()
            prev_wnd = QRectF(self.ui.previousData.childrenRect())
            cur_wnd = QRectF(self.ui.currentData.childrenRect())
            prev_matrix = self.ui.previousData.matrix()
            cur_matrix = self.ui.currentData.matrix()

            prev_mapped_rect = prev_matrix.mapRect(prev_rect)
            cur_mapped_rect = cur_matrix.mapRect(cur_rect)

            if (prev_mapped_rect.width() < prev_wnd.width() or
                    prev_mapped_rect.height() < prev_wnd.height() or
                    cur_mapped_rect.width() < cur_wnd.width() or
                    cur_mapped_rect.height() < cur_wnd.height()):
                self.on_action_Fit_triggered()

    @pyqtSignature("")
    def on_action_Fit_triggered(self):
        prev_rect = self._previousScene.sceneRect()
        cur_rect = self._currentScene.sceneRect()
        prev_wnd = self.ui.previousData.childrenRect()
        cur_wnd = self.ui.currentData.childrenRect()

        prev_sw = prev_wnd.width() / prev_rect.width()
        prev_sh = prev_wnd.height() / prev_rect.height()

        cur_sw = cur_wnd.width() / cur_rect.width()
        cur_sh = cur_wnd.height() / cur_rect.height()

        s = max(prev_sw, prev_sh, cur_sw, cur_sh)

        self.ui.previousData.resetMatrix()
        self.ui.previousData.scale(s, s)
        self.ui.currentData.resetMatrix()
        self.ui.currentData.scale(s, s)
        self.changeZoom(s)

    def zoomOut(self, point=None):
        self.ui.currentData.scale(0.5, 0.5)
        self.ui.previousData.scale(0.5, 0.5)
        self.changeZoom(self.ui.previousData.matrix().m11())
        if point is not None:
            self.ui.previousData.centerOn(point)
            self.ui.currentData.centerOn(point)
        #self.ensureZoomFit()

    def zoomIn(self, point=None):
        self.ui.currentData.scale(2, 2)
        self.ui.previousData.scale(2, 2)
        self.changeZoom(self.ui.previousData.matrix().m11())
        if point is not None:
            self.ui.previousData.centerOn(point)
            self.ui.currentData.centerOn(point)

    def changeZoom(self, zoom):
        self._zoom_label.setText("Zoom: %.5g%%" % (100*zoom))

    @pyqtSignature("")
    def on_actionZoom_100_triggered(self):
        self.ui.previousData.resetMatrix()
        self.ui.currentData.resetMatrix()
        self.changeZoom(1)

    @pyqtSignature("bool")
    def on_actionAdd_point_toggled(self, value):
        if value:
            self._previousScene.mode = TrackingScene.Add
            self._currentScene.mode = TrackingScene.Add

    @pyqtSignature("bool")
    def on_actionAdd_cell_toggled(self, value):
        if value:
            self._previousScene.mode = TrackingScene.AddCell
            self._currentScene.mode = TrackingScene.AddCell

    @pyqtSignature("bool")
    def on_actionRemove_cell_toggled(self, value):
        if value:
            self._previousScene.mode = TrackingScene.RemoveCell
            self._currentScene.mode = TrackingScene.RemoveCell

    @pyqtSignature("bool")
    def on_action_Move_point_toggled(self, value):
        if value:
            self._previousScene.mode = TrackingScene.Move
            self._currentScene.mode = TrackingScene.Move

    @pyqtSignature("bool")
    def on_action_Pan_toggled(self, value):
        if value:
            self._previousScene.mode = TrackingScene.Pan
            self._currentScene.mode = TrackingScene.Pan

    @pyqtSignature("bool")
    def on_linkViews_toggled(self, value):
        parameters.instance.link_views = value
        phor = self.ui.previousData.horizontalScrollBar()
        pver = self.ui.previousData.verticalScrollBar()
        chor = self.ui.currentData.horizontalScrollBar()
        cver = self.ui.currentData.verticalScrollBar()
        if value:
            phor.valueChanged[int].connect(chor.setValue)
            pver.valueChanged[int].connect(cver.setValue)
            chor.valueChanged[int].connect(phor.setValue)
            cver.valueChanged[int].connect(pver.setValue)
            self._previousScene.templatePosChange.connect(self._currentScene.setTemplatePos)
            self._currentScene.templatePosChange.connect(self._previousScene.setTemplatePos)
            phor.setValue(chor.value())
            pver.setValue(cver.value())
        else:
            phor.valueChanged[int].disconnect(chor.setValue)
            pver.valueChanged[int].disconnect(cver.setValue)
            chor.valueChanged[int].disconnect(phor.setValue)
            cver.valueChanged[int].disconnect(pver.setValue)
            self._previousScene.templatePosChange.disconnect(self._currentScene.setTemplatePos)
            self._currentScene.templatePosChange.disconnect(self._previousScene.setTemplatePos)

    def copyFrom(self, start, items):
        if parameters.instance.estimate:
            dlg = createForm('copy_progress.ui', None)
            dlg.buttonBox.clicked["QAbstractButton*"].connect(self.cancelCopy)
            params = parameters.instance
            ts = params.template_size
            ss = params.search_size
            fs = params.filter_size
            self.copy_thread = algo.FindInAll(self._data, start, items, ts, ss, fs, self)
            dlg.imageProgress.setMaximum(self.copy_thread.num_images)
            self.copy_thread.start()
            self.copy_dlg = dlg
            dlg.exec_()
        else:
            algo.copyFromImage(self._data, start, items, self.undo_stack)

    def cancelCopy(self, *args):
        self.copy_thread.stop = True
        dlg = self.copy_dlg
        dlg.buttonBox.clicked['QAbstractButton*)'].disconnect(self.cancelCopy)
        self._previousScene.changeImage(None)
        self._currentScene.changeImage(None)

    def event(self, event):
        if isinstance(event, algo.NextImage):
            dlg = self.copy_dlg
            if dlg is not None:
                dlg.imageProgress.setValue(event.currentImage)
                dlg.pointProgress.setMaximum(event.nbPoints)
                dlg.pointProgress.setValue(0)
            return True
        elif isinstance(event, algo.NextPoint):
            dlg = self.copy_dlg
            if dlg is not None:
                dlg.pointProgress.setValue(event.currentPoint)
            return True
        elif isinstance(event, algo.FoundAll):
            dlg = self.copy_dlg
            if dlg is not None:
                self.cancelCopy()
                dlg.accept()
            return True
        elif isinstance(event, algo.Aborted):
            dlg = self.copy_dlg
            if dlg is not None:
                self.cancelCopy()
                dlg.accept()
            return True
        return QMainWindow.event(self, event)

    def itemsToCopy(self, scene):
        items = scene.getSelectedIds()
        if items:
            answer = QMessageBox.question(self, "Copy of points",
                                          "Some points were selected in the previous data window."
                                          " Do you want to copy only these point on the successive images?",
                                          QMessageBox.Yes, QMessageBox.No)
            if answer == QMessageBox.Yes:
                return items
        return scene.getAllIds()

    @pyqtSignature("")
    def on_actionCopy_from_previous_triggered(self):
        items = self.itemsToCopy(self._previousScene)
        if items:
            self.copyFrom(self.ui.previousState.currentIndex(), items)

    @pyqtSignature("")
    def on_actionCopy_from_current_triggered(self):
        items = self.itemsToCopy(self._currentScene)
        if items:
            self.copyFrom(self.ui.currentState.currentIndex(), items)

    @pyqtSignature("")
    def on_actionSelectPreviousAll_triggered(self):
        self._previousScene.selectAll()

    @pyqtSignature("")
    def on_actionSelectPreviousNew_triggered(self):
        self._previousScene.selectNew()

    @pyqtSignature("")
    def on_actionSelectPreviousNone_triggered(self):
        self._previousScene.selectNone()

    @pyqtSignature("")
    def on_actionSelectPreviousNon_associated_triggered(self):
        self._previousScene.selectNonAssociated()

    @pyqtSignature("")
    def on_actionSelectPreviousAssociated_triggered(self):
        self._previousScene.selectAssociated()

    @pyqtSignature("")
    def on_actionSelectPreviousInvert_triggered(self):
        self._previousScene.selectInvert()

    @pyqtSignature("")
    def on_actionSelectCurrentAll_triggered(self):
        self._currentScene.selectAll()

    @pyqtSignature("")
    def on_actionSelectCurrentNew_triggered(self):
        self._currentScene.selectNew()

    @pyqtSignature("")
    def on_actionSelectCurrentNone_triggered(self):
        self._currentScene.selectNone()

    @pyqtSignature("")
    def on_actionSelectCurrentNon_associated_triggered(self):
        self._currentScene.selectNonAssociated()

    @pyqtSignature("")
    def on_actionSelectCurrentAssociated_triggered(self):
        self._currentScene.selectAssociated()

    @pyqtSignature("")
    def on_actionSelectCurrentInvert_triggered(self):
        self._currentScene.selectInvert()

    def whichDelete(self):
        """
        Returns a function deleting what the user wants
        """
        dlg = createForm("deletedlg.ui", None)
        ret = dlg.exec_()
        if ret:
            if dlg.inAllImages.isChecked():
                return TrackingScene.deleteInAllImages
            if dlg.toImage.isChecked():
                return TrackingScene.deleteToImage
            if dlg.fromImage.isChecked():
                return TrackingScene.deleteFromImage
        return lambda x: None

    @pyqtSignature("")
    def on_actionDelete_Previous_triggered(self):
        del_fct = self.whichDelete()
        del_fct(self._previousScene)

    @pyqtSignature("")
    def on_actionDelete_Current_triggered(self):
        del_fct = self.whichDelete()
        del_fct(self._currentScene)

    @pyqtSignature("")
    def on_actionMerge_points_triggered(self):
        if self._previousScene.mode == TrackingScene.AddCell:
            old_cell = self._previousScene.selected_cell
            new_cell = self._currentScene.selected_cell
            if old_cell is None or new_cell is None:
                QMessageBox.critical(self, "Cannot merge cells",
                                     "You have to select exactly one cell in the old state "
                                     "and one in the new state to merge them.")
                return
            try:
                if old_cell != new_cell:
                    self.undo_stack.push(MergeCells(self._data, self._previousScene.image_name, old_cell, new_cell))
                else:
                    self.undo_stack.push(SplitCells(self._data, self._previousScene.image_name, old_cell, new_cell))
            except AssertionError as error:
                QMessageBox.critical(self, "Cannot merge the cells", str(error))
        else:
            old_pts = self._previousScene.getSelectedIds()
            new_pts = self._currentScene.getSelectedIds()
            if len(old_pts) != 1 or len(new_pts) != 1:
                QMessageBox.critical(self, "Cannot merge points",
                                     "You have to select exactly one point in the old state "
                                     "and one in the new state to link them.")
                return
            try:
                if old_pts != new_pts:
                    self.undo_stack.push(ChangePointsId(self._data, self._previousScene.image_name, old_pts, new_pts))
                else:
                    log_debug("Splitting point of id %d" % old_pts[0])
                    self.undo_stack.push(SplitPointsId(self._data, self._previousScene.image_name, old_pts))
            except AssertionError as error:
                QMessageBox.critical(self, "Cannot merge the points", str(error))

    @pyqtSignature("")
    def on_actionCopy_selection_from_Current_triggered(self):
        cur_sel = self._currentScene.getSelectedIds()
        self._previousScene.setSelectedIds(cur_sel)

    @pyqtSignature("")
    def on_actionCopy_selection_from_Previous_triggered(self):
        cur_sel = self._previousScene.getSelectedIds()
        self._currentScene.setSelectedIds(cur_sel)

    @pyqtSignature("")
    def on_actionNew_data_file_triggered(self):
        if self.ensure_save_data("Leaving unsaved data", "The last modifications you made were not saved."
                                 " Are you sure you want to change the current data file?"):
            fn = QFileDialog.getSaveFileName(self, "Select a new data file to create", self._project.data_dir,
                                                   "CSV Files (*.csv);;All files (*.*)")
            if fn:
                fn = path(fn)
                if fn.exists():
                    button = QMessageBox.question(self, "Erasing existing file",
                                                  "Are you sure yo want to empty the file '%s' ?" % fn,
                                                  QMessageBox.Yes, QMessageBox.No)
                    if button == QMessageBox.No:
                        return
                    fn.remove()
                self._data.clear()
                self._previousScene.resetNewPoints()
                self._currentScene.resetNewPoints()
                self._project.data_file = fn
                self.initFromData()
                log_debug("Data file = %s" % (self._project.data_file,))

    @pyqtSignature("")
    def on_actionAbout_triggered(self):
        dlg = QMessageBox(self)
        dlg.setWindowTitle("About Point Tracker")
        dlg.setIconPixmap(self.windowIcon().pixmap(64, 64))
        #dlg.setTextFormat(Qt.RichText)

        dlg.setText("""Point Tracker Tool version %s rev %s
Developper: Pierre Barbier de Reuille <*****@*****.**>
Copyright 2008
""" % (__version__, __revision__))

        img_read = ", ".join(str(s) for s in QImageReader.supportedImageFormats())
        img_write = ", ".join(str(s) for s in QImageWriter.supportedImageFormats())

        dlg.setDetailedText("""Supported image formats:
  - For reading: %s

  - For writing: %s
""" % (img_read, img_write))
        dlg.exec_()

    @pyqtSignature("")
    def on_actionAbout_Qt_triggered(self):
        QMessageBox.aboutQt(self, "About Qt")

    @pyqtSignature("")
    def on_actionReset_alignment_triggered(self):
        self.undo_stack.push(ResetAlignment(self._data))

    @pyqtSignature("")
    def on_actionAlign_images_triggered(self):
        fn = QFileDialog.getOpenFileName(self, "Select a data file for alignment", self._project.data_dir,
                                               "CSV Files (*.csv);;All files (*.*)")
        if fn:
            d = self._data.copy()
            fn = path(fn)
            try:
                d.load(fn)
            except TrackingDataException as ex:
                showException(self, "Error while loading data file", ex)
                return
            if d._last_pt_id > 0:
                dlg = AlignmentDlg(d._last_pt_id+1, self)
                if dlg.exec_():
                    ref = dlg.ui.referencePoint.currentText()
                    try:
                        ref = int(ref)
                    except ValueError:
                        ref = str(ref)
                    if dlg.ui.twoPointsRotation.isChecked():
                        r1 = int(dlg.ui.rotationPt1.currentText())
                        r2 = int(dlg.ui.rotationPt2.currentText())
                        rotation = ("TwoPoint", r1, r2)
                    else:
                        rotation = None
                else:
                    return
            else:
                ref = 0
                rotation = None
            try:
                shifts, angles = algo.alignImages(self._data, d, ref, rotation)
                self.undo_stack.push(AlignImages(self._data, shifts, angles))
            except algo.AlgoException as ex:
                showException(self, "Error while aligning images", ex)

    def sceneSizeChanged(self):
        previous_rect = self._previousScene.real_scene_rect
        current_rect = self._currentScene.real_scene_rect
        rect = previous_rect | current_rect
        self._previousScene.setSceneRect(rect)
        self._currentScene.setSceneRect(rect)

    @pyqtSignature("")
    def on_actionEdit_timing_triggered(self):
        data = self._data
        dlg = TimeEditDlg(data.images_name, data.images_time, [data.image_path(n) for n in data.images_name], self)
        self.current_dlg = dlg
        if dlg.exec_() == QDialog.Accepted:
            self.undo_stack.push(ChangeTiming(data, [t for n, t in dlg.model]))
        del self.current_dlg

    @pyqtSignature("")
    def on_actionEdit_scales_triggered(self):
        data = self._data
        dlg = EditResDlg(data.images_name, data.images_scale, [data.image_path(n) for n in data.images_name], self)
        self.current_dlg = dlg
        if dlg.exec_() == QDialog.Accepted:
            self.undo_stack.push(ChangeScales(data, [sc for n, sc in dlg.model]))
        del self.current_dlg

    @pyqtSignature("")
    def on_actionCompute_growth_triggered(self):
        data = self._data
        dlg = GrowthComputationDlg(data, self)
        self.current_dlg = dlg
        dlg.exec_()
        del self.current_dlg

    @pyqtSignature("")
    def on_actionPlot_growth_triggered(self):
        data = self._data
        dlg = PlottingDlg(data, self)
        self.current_dlg = dlg
        dlg.exec_()
        del self.current_dlg

    @pyqtSignature("")
    def on_actionClean_cells_triggered(self):
        self.undo_stack.push(CleanCells(self._data))

    @pyqtSignature("")
    def on_actionGotoCell_triggered(self):
        cells = [str(cid) for cid in self._data.cells]
        selected, ok = QInputDialog.getItem(self, "Goto cell", "Select the cell to go to", cells, 0)
        if ok:
            cid = int(selected)
            self.ui.actionAdd_cell.setChecked(True)
            data = self._data
            if cid not in data.cells:
                return
            ls = data.cells_lifespan[cid]
            prev_pos = self._previousScene.current_data._current_index
            cur_pos = self._currentScene.current_data._current_index
            full_poly = data.cells[cid]
            poly = [pid for pid in full_poly if pid in data[prev_pos]]
            #log_debug("Cell %d on time %d: %s" % (cid, prev_pos, poly))
            if prev_pos < ls.start or prev_pos >= ls.end or not poly:
                for i in range(*ls.slice().indices(len(data))):
                    poly = [pid for pid in full_poly if pid in data[i]]
                    if poly:
                        log_debug("Found cell %d on image %d with polygon %s" % (cid, i, poly))
                        new_prev_pos = i
                        break
                else:
                    log_debug("Cell %d found nowhere in range %s!!!" % (cid, ls.slice()))
            else:
                new_prev_pos = prev_pos
            new_cur_pos = min(max(cur_pos + new_prev_pos - prev_pos, 0), len(data))
            self.ui.previousState.setCurrentIndex(new_prev_pos)
            self.ui.currentState.setCurrentIndex(new_cur_pos)
            self._previousScene.current_cell = cid
            self._currentScene.current_cell = cid
            prev_data = self._previousScene.current_data
            poly = data.cells[cid]
            prev_poly = QPolygonF([prev_data[ptid] for ptid in poly if ptid in prev_data])
            prev_bbox = prev_poly.boundingRect()
            log_debug("Previous bounding box = %dx%d+%d+%d" % (prev_bbox.width(), prev_bbox.height(),
                                                               prev_bbox.left(), prev_bbox.top()))
            self.ui.previousData.ensureVisible(prev_bbox)

    @pyqtSignature("")
    def on_actionGotoPoint_triggered(self):
        data = self._data
        points = [str(pid) for pid in data.cell_points]
        selected, ok = QInputDialog.getItem(self, "Goto point", "Select the point to go to", points, 0)
        if ok:
            pid = int(selected)
            self.ui.action_Move_point.setChecked(True)
            if pid not in data.cell_points:
                return
            prev_pos = self._previousScene.current_data._current_index
            cur_pos = self._currentScene.current_data._current_index
            prev_data = self._previousScene.current_data
            if not pid in prev_data:
                closest = -1
                best_dist = len(data)+1
                for img_data in data:
                    if pid in img_data:
                        dist = abs(img_data._current_index - prev_pos)
                        if dist < best_dist:
                            best_dist = dist
                            closest = img_data._current_index
                new_prev_pos = closest
            else:
                new_prev_pos = prev_pos
            new_cur_pos = min(max(cur_pos + new_prev_pos - prev_pos, 0), len(data))
            self.ui.previousState.setCurrentIndex(new_prev_pos)
            self.ui.currentState.setCurrentIndex(new_cur_pos)
            self._previousScene.setSelectedIds([pid])
            self._currentScene.setSelectedIds([pid])
            self.ui.previousData.centerOn(self._previousScene.current_data[pid])
예제 #39
0
class ToolBox(QFrame):
    """
    A tool box widget.
    """
    # Emitted when a tab is toggled.
    tabToogled = Signal(int, bool)

    def setExclusive(self, exclusive):
        """
        Set exclusive tabs (only one tab can be open at a time).
        """
        if self.__exclusive != exclusive:
            self.__exclusive = exclusive
            self.__tabActionGroup.setExclusive(exclusive)
            checked = self.__tabActionGroup.checkedAction()
            if checked is None:
                # The action group can be out of sync with the actions state
                # when switching between exclusive states.
                actions_checked = [page.action for page in self.__pages
                                   if page.action.isChecked()]
                if actions_checked:
                    checked = actions_checked[0]

            # Trigger/toggle remaining open pages
            if exclusive and checked is not None:
                for page in self.__pages:
                    if checked != page.action and page.action.isChecked():
                        page.action.trigger()

    def exclusive(self):
        """
        Are the tabs in the toolbox exclusive.
        """
        return self.__exclusive

    exclusive_ = Property(bool,
                          fget=exclusive,
                          fset=setExclusive,
                          designable=True,
                          doc="Exclusive tabs")

    def __init__(self, parent=None, **kwargs):
        QFrame.__init__(self, parent, **kwargs)

        self.__pages = []
        self.__tabButtonHeight = -1
        self.__tabIconSize = QSize()
        self.__exclusive = False
        self.__setupUi()

    def __setupUi(self):
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)

        # Scroll area for the contents.
        self.__scrollArea = \
                _ToolBoxScrollArea(self, objectName="toolbox-scroll-area")

        self.__scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.__scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.__scrollArea.setSizePolicy(QSizePolicy.MinimumExpanding,
                                        QSizePolicy.MinimumExpanding)
        self.__scrollArea.setFrameStyle(QScrollArea.NoFrame)
        self.__scrollArea.setWidgetResizable(True)

        # A widget with all of the contents.
        # The tabs/contents are placed in the layout inside this widget
        self.__contents = QWidget(self.__scrollArea,
                                  objectName="toolbox-contents")

        # The layout where all the tab/pages are placed
        self.__contentsLayout = QVBoxLayout()
        self.__contentsLayout.setContentsMargins(0, 0, 0, 0)
        self.__contentsLayout.setSizeConstraint(QVBoxLayout.SetMinAndMaxSize)
        self.__contentsLayout.setSpacing(0)

        self.__contents.setLayout(self.__contentsLayout)

        self.__scrollArea.setWidget(self.__contents)

        layout.addWidget(self.__scrollArea)

        self.setLayout(layout)
        self.setSizePolicy(QSizePolicy.Fixed,
                           QSizePolicy.MinimumExpanding)

        self.__tabActionGroup = \
                QActionGroup(self, objectName="toolbox-tab-action-group")

        self.__tabActionGroup.setExclusive(self.__exclusive)

        self.__actionMapper = QSignalMapper(self)
        self.__actionMapper.mapped[QObject].connect(self.__onTabActionToogled)

    def setTabButtonHeight(self, height):
        """
        Set the tab button height.
        """
        if self.__tabButtonHeight != height:
            self.__tabButtonHeight = height
            for page in self.__pages:
                page.button.setFixedHeight(height)

    def tabButtonHeight(self):
        """
        Return the tab button height.
        """
        return self.__tabButtonHeight

    def setTabIconSize(self, size):
        """
        Set the tab button icon size.
        """
        if self.__tabIconSize != size:
            self.__tabIconSize = size
            for page in self.__pages:
                page.button.setIconSize(size)

    def tabIconSize(self):
        """
        Return the tab icon size.
        """
        return self.__tabIconSize

    def tabButton(self, index):
        """
        Return the tab button at `index`
        """
        return self.__pages[index].button

    def tabAction(self, index):
        """
        Return open/close action for the tab at `index`.
        """
        return self.__pages[index].action

    def addItem(self, widget, text, icon=None, toolTip=None):
        """
        Append the `widget` in a new tab and return its index.

        Parameters
        ----------
        widget : :class:`QWidget`
            A widget to be inserted. The toolbox takes ownership
            of the widget.

        text : str
            Name/title of the new tab.

        icon : :class:`QIcon`, optional
            An icon for the tab button.

        toolTip : str, optional
            Tool tip for the tab button.

        """
        return self.insertItem(self.count(), widget, text, icon, toolTip)

    def insertItem(self, index, widget, text, icon=None, toolTip=None):
        """
        Insert the `widget` in a new tab at position `index`.

        See also
        --------
        ToolBox.addItem

        """
        button = self.createTabButton(widget, text, icon, toolTip)

        self.__contentsLayout.insertWidget(index * 2, button)
        self.__contentsLayout.insertWidget(index * 2 + 1, widget)

        widget.hide()

        page = _ToolBoxPage(index, widget, button.defaultAction(), button)
        self.__pages.insert(index, page)

        for i in range(index + 1, self.count()):
            self.__pages[i] = self.__pages[i]._replace(index=i)

        self.__updatePositions()

        # Show (open) the first tab.
        if self.count() == 1 and index == 0:
            page.action.trigger()

        self.__updateSelected()

        self.updateGeometry()
        return index

    def removeItem(self, index):
        """
        Remove the widget at `index`.

        .. note:: The widget hidden but is is not deleted.

        """
        self.__contentsLayout.takeAt(2 * index + 1)
        self.__contentsLayout.takeAt(2 * index)
        page = self.__pages.pop(index)

        # Update the page indexes
        for i in range(index, self.count()):
            self.__pages[i] = self.__pages[i]._replace(index=i)

        page.button.deleteLater()

        # Hide the widget and reparent to self
        # This follows QToolBox.removeItem
        page.widget.hide()
        page.widget.setParent(self)

        self.__updatePositions()
        self.__updateSelected()

        self.updateGeometry()

    def count(self):
        """
        Return the number of widgets inserted in the toolbox.
        """
        return len(self.__pages)

    def widget(self, index):
        """
        Return the widget at `index`.
        """
        return self.__pages[index].widget

    def createTabButton(self, widget, text, icon=None, toolTip=None):
        """
        Create the tab button for `widget`.
        """
        action = QAction(text, self)
        action.setCheckable(True)

        if icon:
            action.setIcon(icon)

        if toolTip:
            action.setToolTip(toolTip)
        self.__tabActionGroup.addAction(action)
        self.__actionMapper.setMapping(action, action)
        action.toggled.connect(self.__actionMapper.map)

        button = ToolBoxTabButton(self, objectName="toolbox-tab-button")
        button.setDefaultAction(action)
        button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        button.setSizePolicy(QSizePolicy.Expanding,
                             QSizePolicy.Fixed)

        if self.__tabIconSize.isValid():
            button.setIconSize(self.__tabIconSize)

        if self.__tabButtonHeight > 0:
            button.setFixedHeight(self.__tabButtonHeight)

        return button

    def ensureWidgetVisible(self, child, xmargin=50, ymargin=50):
        """
        Scroll the contents so child widget instance is visible inside
        the viewport.

        """
        self.__scrollArea.ensureWidgetVisible(child, xmargin, ymargin)

    def sizeHint(self):
        hint = self.__contentsLayout.sizeHint()

        if self.count():
            # Compute max width of hidden widgets also.
            scroll = self.__scrollArea
            scroll_w = scroll.verticalScrollBar().sizeHint().width()
            frame_w = self.frameWidth() * 2 + scroll.frameWidth() * 2
            max_w = max([p.widget.sizeHint().width() for p in self.__pages])
            hint = QSize(max(max_w, hint.width()) + scroll_w + frame_w,
                         hint.height())

        return QSize(200, 200).expandedTo(hint)

    def __onTabActionToogled(self, action):
        page = find(self.__pages, action, key=attrgetter("action"))
        on = action.isChecked()
        page.widget.setVisible(on)
        index = page.index

        if index > 0:
            # Update the `previous` tab buttons style hints
            previous = self.__pages[index - 1].button
            flag = QStyleOptionToolBoxV2.NextIsSelected
            if on:
                previous.selected |= flag
            else:
                previous.selected &= ~flag

            previous.update()

        if index < self.count() - 1:
            next = self.__pages[index + 1].button
            flag = QStyleOptionToolBoxV2.PreviousIsSelected
            if on:
                next.selected |= flag
            else:
                next.selected &= ~flag

            next.update()

        self.tabToogled.emit(index, on)

        self.__contentsLayout.invalidate()

    def __updateSelected(self):
        """Update the tab buttons selected style flags.
        """
        if self.count() == 0:
            return

        opt = QStyleOptionToolBoxV2

        def update(button, next_sel, prev_sel):
            if next_sel:
                button.selected |= opt.NextIsSelected
            else:
                button.selected &= ~opt.NextIsSelected

            if prev_sel:
                button.selected |= opt.PreviousIsSelected
            else:
                button.selected &= ~ opt.PreviousIsSelected

            button.update()

        if self.count() == 1:
            update(self.__pages[0].button, False, False)
        elif self.count() >= 2:
            pages = self.__pages
            for i in range(1, self.count() - 1):
                update(pages[i].button,
                       pages[i + 1].action.isChecked(),
                       pages[i - 1].action.isChecked())

    def __updatePositions(self):
        """Update the tab buttons position style flags.
        """
        if self.count() == 0:
            return
        elif self.count() == 1:
            self.__pages[0].button.position = QStyleOptionToolBoxV2.OnlyOneTab
        else:
            self.__pages[0].button.position = QStyleOptionToolBoxV2.Beginning
            self.__pages[-1].button.position = QStyleOptionToolBoxV2.End
            for p in self.__pages[1:-1]:
                p.button.position = QStyleOptionToolBoxV2.Middle

        for p in self.__pages:
            p.button.update()
예제 #40
0
class Context(QDialog):
    """
    Context dialog. Responsoble for viewing full thread.
    
    There are a "bug" here - I have to duplicate some functions from main
    module due to architecture fail. I will fix that, soon.
    
    Parameters:
    @auth - connector instance for communication with statusnet installation
    @conversation_id - conversation id. Nuff said.
    @settings - QTDenter settings
    @self.version - QTDenter self.version
    """
    def __init__(self, auth, conversation_id, settings, version, parent = None):
        QDialog.__init__(self, parent)
        self.ui = cg.Ui_Dialog()
        self.ui.setupUi(self)
        
        self.settings = settings
        self.auth = auth
        self.version = version
        
        # Initialize signal mappers for buttons and lists for buttons pointers
        self.context_buttons_mapper = QSignalMapper(self)
        self.context_buttons_list = []
        self.destroy_buttons_mapper = QSignalMapper(self)
        self.destroy_buttons_list = []
        self.redent_buttons_mapper = QSignalMapper(self)
        self.redent_buttons_list = []
        self.like_buttons_mapper = QSignalMapper(self)
        self.like_buttons_list = []
        self.dentid_buttons_mapper = QSignalMapper(self)
        self.dentid_buttons_list = []
        
        self.ui.context.setSortingEnabled(True)
        self.ui.context.sortByColumn(2, Qt.DescendingOrder)
        for column in range(2, 6):
            self.ui.context.setColumnHidden(column, True)
        self.ui.context.setColumnWidth(0, 65)
        self.ui.context.itemActivated.connect(self.reply_to_dent)

        conversation = self.auth.get_conversation(conversation_id)
        
        self.list_handler = list_handler.List_Handler(self.callback)
        
        self.list_item = list_item.list_item()
        self.list_handler.add_data("conversation", conversation, self.settings["server"])
        self.connect_buttons()
        
    def connect_buttons(self):
        print "Connecting buttons"
        for item in self.context_buttons_list:
            self.context_buttons_mapper.setMapping(item[0], item[1])
            item[0].clicked.connect(self.context_buttons_mapper.map)
            
        for item in self.destroy_buttons_list:
            self.destroy_buttons_mapper.setMapping(item[0], item[1])
            item[0].clicked.connect(self.destroy_buttons_mapper.map)
            
        for item in self.redent_buttons_list:
            self.redent_buttons_mapper.setMapping(item[0], item[1])
            item[0].clicked.connect(self.redent_buttons_mapper.map)
            
        for item in self.like_buttons_list:
            self.like_buttons_mapper.setMapping(item[0], item[1])
            item[0].clicked.connect(self.like_buttons_mapper.map)
            
        for item in self.dentid_buttons_list:
            self.dentid_buttons_mapper.setMapping(item[0], item[1])
            item[0].clicked.connect(self.dentid_buttons_mapper.map)
            
        self.destroy_buttons_mapper.mapped.connect(self.delete_dent)
        self.redent_buttons_mapper.mapped.connect(self.redent_dent)
        self.like_buttons_mapper.mapped.connect(self.like_dent)
        self.dentid_buttons_mapper.mapped.connect(self.go_to_dent)
        
    def callback(self, dent_type, data, server):
        """
        List callback
        """
        if dent_type == "end":
            pass
        else:
            self.add_dent(data, dent_type)
        
    def add_dent(self, data, dent_type):
        """
        Add dent to list widget
        """
        item_data = self.list_item.process_item(data, self.settings["last_dent_id"], self.settings["user"], self.settings["server"], dent_type)

        item = item_data[0]
        avatar_widget = item_data[1]
        post_widget = item_data[2]
        
        destroy_button = avatar_widget.findChild(QPushButton, "destroy_button_" + str(data["id"]))
        dentid_button = post_widget.findChild(QPushButton, "dentid_button_" + str(data["id"]))
        redent_button = post_widget.findChild(QPushButton, "redent_button_" + str(data["id"]))
        like_button = post_widget.findChild(QPushButton, "like_button_" + str(data["id"]))
        
        # Adding buttons pointers to list for later mapping
        self.destroy_buttons_list.append([destroy_button, data["id"]])
        self.dentid_buttons_list.append([dentid_button, data["id"]])
        self.redent_buttons_list.append([redent_button, data["id"]])
        self.like_buttons_list.append([like_button, data["id"]])
        
        # If current dent is not self-posted - hide "Delete" button.
        if not data["nickname"] == self.settings["user"]:
            destroy_button.hide()
        else:
            redent_button.hide()
        
        if data["retweeted"]:
            redent_button.hide()
        elif data["nickname"] != self.settings["user"] and data["retweeted"]:
            redent_button.show()
        
        self.ui.context.addTopLevelItem(item)
        self.ui.context.setItemWidget(item, 0, avatar_widget)
        self.ui.context.setItemWidget(item, 1, post_widget)

    def like_dent(self, dent_id):
        """
        Like dent button callback
        """
        # Search for item that contain pressed "Like" button, get dent id,
        # and send a request to connector for dent like.
        try:
            root = self.ui.context.invisibleRootItem()
            count = root.childCount()
            for index in range(count):
                item = root.child(index)
                if item.text(2).split(":")[0] == str(dent_id):
                    btn = self.ui.context.findChild(QPushButton, "like_button_" + str(dent_id))
                    btn.setText("...")
                    if item.text(3) == "not":
                        data = self.auth.favoritize_dent(dent_id, self.version)
                        if data["favorited"]:
                            item.setText(3, "favorited")
                            btn.setText("X")
                        else:
                            btn.setText(u"\u2665")
                        break
                    else:
                        data = self.auth.defavoritize_dent(dent_id, self.version)
                        if not data["favorited"]:
                            item.setText(3, "not")
                            btn.setText(u"\u2665")
                        else:
                            btn.setText("X")
                        break
        except:
            QMessageBox.critical(self, "QTDenter - Choose dent first!", "You have to choose dent")
        
    def redent_dent(self, dent_id):
        """
        Redent dent button callback
        """
        # Search for item that contain pressed "Redent" button, get dent id,
        # and send a request to connector for dent redenting.
        try:
            root = self.ui.context.invisibleRootItem()
            count = root.childCount()
            for index in range(count):
                item = root.child(index)
                if item.text(2).split(":")[0] == str(dent_id):
                    print "FOUND!"
                    data = self.auth.redent_dent(dent_id, self.version)
                    if data != "FAIL":
                        btn = self.ui.context.findChild(QPushButton, "redent_button_" + str(dent_id))
                        btn.hide()
                        
                    break
        except:
            QMessageBox.critical(self, "QTDenter - Choose dent first!", "You have to choose dent")
        
    def delete_dent(self, dent_id):
        """
        Delete dent button callback
        """
        # Search for item that contain pressed "Delete" button, get dent id,
        # and send a request to connector for dent deletion.
        try:
            root = self.ui.context.invisibleRootItem()
            count = root.childCount()
            for index in range(count):
                item = root.child(index)
                if item.text(2).split(":")[0] == str(dent_id):
                    data = self.auth.delete_dent(dent_id)
                    print data
                    if data == "OK":
                        index = self.ui.context.indexOfTopLevelItem(item)
                        self.ui.context.takeTopLevelItem(index)
                    break
        except:
            QMessageBox.critical(self, "QTDenter - Choose dent first!", "You have to choose dent")
            
    def reply_to_dent(self):
        """
        Reply to dent
        """
        try:
            dent_id = self.ui.context.currentItem().text(2).split(":")[0]
            to_username = self.ui.context.currentItem().text(2).split(":")[1]
            dent_text = self.ui.context.currentItem().text(4)
            params = {}
            params["type"] = "reply"
            params["reply_to_id"] = dent_id
            params["nickname"] = to_username
            params["text"] = dent_text
            newpostD = new_post.New_Post(self.settings["messageLength"], params, self.new_post_callback)
            newpostD.exec_()
        except:
            QMessageBox.critical(self, "QTDenter - Choose dent first!", "You have to choose dent")

    def go_to_dent(self, dent_id):
        """
        Callback for ID button
        """
        try:
            root = self.ui.context.invisibleRootItem()
            count = root.childCount()
            for index in range(count):
                item = root.child(index)
                if item.text(2).split(":")[0] == str(dent_id):
                    server_address = self.settings["server"]
                    if self.settings["useSecureConnection"] == 1:
                        server_address = "https://" + server_address
                    else:
                        server_address = "http://" + server_address
        
                    QDesktopServices.openUrl(QUrl(server_address + "/notice/" + str(dent_id)))
        except:
            QMessageBox.critical(self, "QTDenter - Choose dent first!", "You have to choose dent")
        
    def send_reply(self, data):
        """
        Send reply
        """
        data = self.auth.send_reply(data, self.version)
        self.list_handler.add_data("home", [data])
            
    def new_post_callback(self, type, data):
        """
        Callback for new post dialog.
        """
        if type == "send_reply":
            self.send_reply(data)
예제 #41
0
class MainApp(QDockWidget, QMainWindow, Ui_MainApp):
    # signals
    goBack = pyqtSignal()
    searchOpsubByName = pyqtSignal(str)
    enableSearch = pyqtSignal(bool)
    refreshLegend = pyqtSignal(QgsMapLayer)
    ogrDatasourceLoaded = pyqtSignal(bool)

    class VfkLayer(object):
        Par = 0
        Bud = 1

    def __init__(self, iface):
        QDockWidget.__init__(self, iface.mainWindow())
        self.setupUi(self)
        self.iface = iface

        # variables
        self.__mLastVfkFile = []
        self.__mOgrDataSource = None
        self.__mDataSourceName = ''
        self.__fileName = []
        self.__mLoadedLayers = {}
        self.__mDefaultPalette = self.vfkFileLineEdit.palette()

        # new lineEdits variables
        self.lineEditsCount = 1

        self.__browseButtons = {}
        self.__vfkLineEdits = {}

        # data will be load from source according to checked radiobox
        self.__source_for_data = 'file'

        # apply changes into main database
        self.__databases = {}
        # self.pb_applyChanges.setEnabled(False)
        self.changes_instance = ApplyChanges()

        # Connect ui with functions
        self.__createToolbarsAndConnect()

        # check GDAL version
        self.__gdal_version = int(gdal.VersionInfo())

        if self.__gdal_version < 2020000:
            self.actionZpracujZmeny.setEnabled(False)
            self.pb_nextFile.setEnabled(False)
            self.pb_nextFile.setToolTip(
                u'Není možné načíst více souborů, verze GDAL je nižší než 2.2.0.')
            self.actionZpracujZmeny.setToolTip(u'Zpracování změn není povoleno, verze GDAL je nižší než 2.2.0.')
            self.groupBox.setEnabled(False)

        # settings
        self.loadVfkButton.setDisabled(True)

        self.searchFormMainControls = SearchFormController.MainControls()
        self.searchFormMainControls.formCombobox = self.searchCombo
        self.searchFormMainControls.searchForms = self.searchForms
        self.searchFormMainControls.searchButton = self.searchButton

        self.searchForms = SearchFormController.SearchForms()
        self.searchForms.vlastnici = self.vlastniciSearchForm
        self.searchForms.parcely = self.parcelySearchForm
        self.searchForms.budovy = self.budovySearchForm
        self.searchForms.jednotky = self.jednotkySearchForm

        # search form controller
        self.__mSearchController = SearchFormController(
            self.searchFormMainControls, self.searchForms, self)

        self.connect(self.__mSearchController, SIGNAL(
            "actionTriggered(QUrl)"), self.vfkBrowser.processAction)
        self.connect(
            self, SIGNAL("enableSearch"), self.searchButton.setEnabled)

        self.connect(self.vfkBrowser, SIGNAL("showParcely"), self.showParInMap)
        self.connect(self.vfkBrowser, SIGNAL("showBudovy"), self.showBudInMap)

        # connect lineEdits and returnPressed action
        self.connect(self.vfkFileLineEdit, SIGNAL(
            "returnPressed()"), self.loadVfkButton_clicked)
        self.connect(self.vlastniciSearchForm.ui.jmenoLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)
        self.connect(self.vlastniciSearchForm.ui.rcIcoLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)
        self.connect(self.vlastniciSearchForm.ui.lvVlastniciLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)

        self.connect(self.parcelySearchForm.ui.parCisloLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)
        self.connect(self.parcelySearchForm.ui.lvParcelyLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)

        self.connect(self.budovySearchForm.ui.cisloDomovniLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)
        self.connect(self.budovySearchForm.ui.naParceleLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)
        self.connect(self.budovySearchForm.ui.lvBudovyLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)

        self.connect(self.jednotkySearchForm.ui.mCisloJednotkyLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)
        self.connect(self.jednotkySearchForm.ui.mCisloDomovniLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)
        self.connect(self.jednotkySearchForm.ui.mNaParceleLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)
        self.connect(self.jednotkySearchForm.ui.mLvJednotkyLineEdit,
                     SIGNAL("returnPressed()"), self.__mSearchController.search)

        self.vfkBrowser.showHelpPage()

    def browseButton_clicked(self, browseButton_id=1):
        """
        :param browseButton_id: ID of clicked browse button.
        :return:
        """
        title = u'Načti soubor VFK'
        settings = QSettings()
        lastUsedDir = ''

        if self.__source_for_data == 'file':
            ext = '*.vfk'
            if self.__gdal_version >= 2020000:
                ext += ' *.db'
            loaded_file = QFileDialog.getOpenFileName(
                self, title, lastUsedDir, u'Soubory podporované ovladačem VFK GDAL ({})'.format(ext))

            if not loaded_file:
                return
            else:
                self.__fileName.append(loaded_file)
                if browseButton_id == 1:
                    self.vfkFileLineEdit.setText(self.__fileName[0])
                else:
                    self.__vfkLineEdits['vfkLineEdit_{}'.format(len(self.__vfkLineEdits))].setText(
                        self.__fileName[browseButton_id - 1])
        elif self.__source_for_data == 'directory':
            loaded_file = QFileDialog.getExistingDirectory(self, u"Vyberte adresář s daty VFK")
            if not loaded_file:
                return
            else:
                self.__fileName = []
                self.__fileName.append(loaded_file)
                self.vfkFileLineEdit.setText(self.__fileName[0])
        else:
            qDebug('(VFK) Not valid source')

        self.loadVfkButton.setEnabled(True)

    def browserGoBack(self):
        self.vfkBrowser.goBack()

    def browserGoForward(self):
        self.vfkBrowser.goForth()

    def selectParInMap(self):
        self.showInMap(self.vfkBrowser.currentParIds(), "PAR")

    def selectBudInMap(self):
        self.showInMap(self.vfkBrowser.currentBudIds(), "BUD")

    def latexExport(self):
        fileName = QFileDialog.getSaveFileName(
            self, u"Jméno exportovaného souboru", ".tex", "LaTeX (*.tex)")
        if fileName:
            export_succesfull = self.vfkBrowser.exportDocument(
                self.vfkBrowser.currentUrl(), fileName, self.vfkBrowser.ExportFormat.Latex)
            if export_succesfull:
                self.succesfullExport("LaTeX")

    def htmlExport(self):
        fileName = QFileDialog.getSaveFileName(
            self, u"Jméno exportovaného souboru", ".html", "HTML (*.html)")
        if fileName:
            export_succesfull = self.vfkBrowser.exportDocument(
                self.vfkBrowser.currentUrl(), fileName, self.vfkBrowser.ExportFormat.Html)
            if export_succesfull:
                self.succesfullExport("HTML")

    def setSelectionChangedConnected(self, connected):
        """

        :type connected: bool
        :return:
        """
        for layer in self.__mLoadedLayers:
            id = self.__mLoadedLayers[layer]
            vectorLayer = QgsMapLayerRegistry.instance().mapLayer(id)

            if connected:
                self.connect(
                    vectorLayer, SIGNAL("selectionChanged()"), self.showInfoAboutSelection)
            else:
                self.disconnect(
                    vectorLayer, SIGNAL("selectionChanged()"), self.showInfoAboutSelection)

    def showInMap(self, ids, layerName):
        """

        :type ids: list
        :type layerName: str
        :return:
        """
        if layerName in self.__mLoadedLayers:
            id = self.__mLoadedLayers[layerName]
            vectorLayer = QgsMapLayerRegistry.instance().mapLayer(id)
            searchString = "ID IN ({})".format(", ".join(ids))
            error = ''
            fIds = self.__search(vectorLayer, searchString, error)
            if error:
                qDebug('\n (VFK) ERROR in showInMap: {}'.format(error))
                return
            else:
                vectorLayer.setSelectedFeatures(fIds)

    def __search(self, layer, searchString, error):
        """

        :type layer: QgsVectorLayer
        :type searchString: str
        :type error: str
        :return:
        """
        # parse search string and build parsed tree
        search = QgsExpression(searchString)
        rect = QgsRectangle()
        fIds = []

        if search.hasParserError():
            error += "Parsing error:" + search.parserErrorString()
            return fIds
        if not search.prepare(layer.pendingFields()):
            error + "Evaluation error:" + search.evalErrorString()

        layer.select(rect, False)
        fit = QgsFeatureIterator(layer.getFeatures())
        f = QgsFeature()

        while fit.nextFeature(f):

            if search.evaluate(f):
                fIds.append(f.id())
            # check if there were errors during evaluating
            if search.hasEvalError():
                qDebug('\n (VFK) Evaluate error: {}'.format(error))
                break

        return fIds

    def loadVfkButton_clicked(self):
        """
        After click method starts loading all inserted files
        """
        # check the source of data
        if self.__source_for_data == 'directory':
            dir_path = self.__fileName[0]
            self.__fileName = self.__findVFKFilesInDirectory(dir_path)

        # check if first file is amendment
        amendment_file = self.__checkIfAmendmentFile(self.__fileName[0])

        # prepare name for database
        if amendment_file:
            new_database_name = '{}_zmeny.db'.format(os.path.basename(self.__fileName[0]).split('.')[0])
        else:
            new_database_name = '{}_stav.db'.format(os.path.basename(self.__fileName[0]).split('.')[0])

        os.environ['OGR_VFK_DB_NAME'] = os.path.join(
            os.path.dirname(os.path.dirname(self.__fileName[0])), new_database_name)
        self.__mDataSourceName = self.__fileName[0]     # os.environ['OGR_VFK_DB_NAME']

        QgsApplication.processEvents()

        self.importThread = OpenThread(self.__fileName)
        self.importThread.working.connect(self.runLoadingLayer)
        if not self.importThread.isRunning():
            self.importThread.start()

    def runLoadingLayer(self, fileName):
        """

        :return:
        """
        if fileName not in self.__mLastVfkFile:

            self.labelLoading.setText(
                u'Načítám data do SQLite databáze (může nějaký čas trvat...)')

            try:
                self.loadVfkFile(fileName)
            except VFKError as e:
                QMessageBox.critical(
                    self, u'Chyba', u'{}'.format(e), QMessageBox.Ok)
                self.emit(SIGNAL("enableSearch"), False)
                return

            self.__mLastVfkFile.append(fileName)
            self.importThread.nextLayer = False

            if fileName == self.__fileName[-1]:
                self.loadingLayersFinished()

    def loadingLayersFinished(self):
        """

        :return:
        """
        try:
            self.__openDatabase(
                os.environ['OGR_VFK_DB_NAME'])  # self.__mDataSourceName)
        except VFKError as e:
            QMessageBox.critical(
                self, u'Chyba', u'{}'.format(e), QMessageBox.Ok)
            self.emit(SIGNAL("enableSearch"), False)
            return

        self.vfkBrowser.setConnectionName(self.property("connectionName"))
        self.__mSearchController.setConnectionName(
            self.property("connectionName"))

        self.emit(SIGNAL("enableSearch"), True)
        self.__mLoadedLayers.clear()

        if self.parCheckBox.isChecked():
            self.__loadVfkLayer('PAR')
        else:
            self.__unLoadVfkLayer('PAR')

        if self.budCheckBox.isChecked():
            self.__loadVfkLayer('BUD')
        else:
            self.__unLoadVfkLayer('BUD')

        self.labelLoading.setText(u'Načítání souborů VFK bylo dokončeno.')

    def vfkFileLineEdit_textChanged(self, arg1):
        """

        :type arg1: str
        :return:
        """
        info = QFileInfo(arg1)

        if info.isFile():
            self.loadVfkButton.setEnabled(True)
            self.vfkFileLineEdit.setPalette(self.__mDefaultPalette)
        else:
            self.loadVfkButton.setEnabled(False)
            pal = QPalette(self.vfkFileLineEdit.palette())
            pal.setColor(QPalette.text(), Qt.red)
            self.vfkFileLineEdit.setPalette(pal)

    def __loadVfkLayer(self, vfkLayerName):
        """

        :type vfkLayerName: str
        :return:
        """
        qDebug("\n(VFK) Loading vfk layer {}".format(vfkLayerName))
        if vfkLayerName in self.__mLoadedLayers:
            qDebug(
                "\n(VFK) Vfk layer {} is already loaded".format(vfkLayerName))
            return

        composedURI = self.__mDataSourceName + "|layername=" + vfkLayerName
        layer = QgsVectorLayer(composedURI, vfkLayerName, "ogr")
        if not layer.isValid():
            qDebug("\n(VFK) Layer failed to load!")

        self.__mLoadedLayers[vfkLayerName] = layer.id()

        try:
            self.__setSymbology(layer)
        except VFKWarning as e:
            QMessageBox.information(self, 'Load Style', e, QMessageBox.Ok)

        QgsMapLayerRegistry.instance().addMapLayer(layer)

    def __unLoadVfkLayer(self, vfkLayerName):
        """

        :type vfkLayerName: str
        :return:
        """
        qDebug("\n(VFK) Unloading vfk layer {}".format(vfkLayerName))

        if vfkLayerName not in self.__mLoadedLayers:
            qDebug(
                "\n(VFK) Vfk layer {} is already unloaded".format(vfkLayerName))
            return

        QgsMapLayerRegistry.instance().removeMapLayer(
            self.__mLoadedLayers[vfkLayerName])
        del self.__mLoadedLayers[vfkLayerName]

    def __setSymbology(self, layer):
        """

        :type layer: QgsVectorLayer
        :return:
        """
        name = layer.name()
        symbologyFile = ''

        if name == 'PAR':
            symbologyFile = ':/parStyle.qml'
        elif name == 'BUD':
            symbologyFile = ':/budStyle.qml'

        errorMsg, resultFlag = layer.loadNamedStyle(symbologyFile)

        if not resultFlag:
            raise VFKWarning(u'Load style: {}'.format(errorMsg))

        layer.triggerRepaint()
        self.emit(SIGNAL("refreshLegend"), layer)

    def __openDatabase(self, dbPath):
        """

        :type dbPath: str
        :return:
        """
        qDebug("\n(VFK) Open DB: {}".format(dbPath))
        if not QSqlDatabase.isDriverAvailable('QSQLITE'):
            raise VFKError(u'Databázový ovladač QSQLITE není dostupný.')

        connectionName = QUuid.createUuid().toString()
        db = QSqlDatabase.addDatabase("QSQLITE", connectionName)
        db.setDatabaseName(dbPath)
        if not db.open():
            raise VFKError(u'Nepodařilo se otevřít databázi. ')

        self.setProperty("connectionName", connectionName)

    def loadVfkFile(self, fileName):
        """

        :type fileName: str
        :return:
        """
        label_text = fileName.split('/')
        label_text = '...' + label_text[-2] + '/' + label_text[-1]

        # overwrite database
        if fileName == self.__fileName[0]:
            if self.overwriteCheckBox.isChecked():
                qDebug('\n (VFK) Database will be overwritten')
                os.environ['OGR_VFK_DB_OVERWRITE'] = '1'

        if self.__mOgrDataSource:
            self.__mOgrDataSource.Destroy()
            self.__mOgrDataSource = None

        QgsApplication.registerOgrDrivers()

        self.progressBar.setRange(0, 1)
        self.progressBar.setValue(0)

        QgsApplication.processEvents()

        #os.environ['OGR_VFK_DB_READ_ALL_BLOCKS'] = 'NO'
        self.labelLoading.setText(
            u'Načítám soubor {} (může nějaký čas trvat...)'.format(label_text))

        QgsApplication.processEvents()

        self.__mOgrDataSource = ogr.Open(
            fileName, 0)   # 0 - datasource is open in read-only mode

        if not self.__mOgrDataSource:
            raise VFKError(
                u"Nelze otevřít VFK soubor '{}' jako platný OGR datasource.".format(fileName))

        layerCount = self.__mOgrDataSource.GetLayerCount()

        layers_names = []

        for i in xrange(layerCount):
            layers_names.append(
                self.__mOgrDataSource.GetLayer(i).GetLayerDefn().GetName())

        if ('PAR' not in layers_names or 'BUD' not in layers_names) and len(self.__vfkLineEdits) == 1:
            self.__dataWithoutParBud()
            self.labelLoading.setText(
                u'Data nemohla být načtena. Vstupní soubor neobsahuje bloky PAR a BUD.')
            QgsApplication.processEvents()
            return

        # load all layers
        self.progressBar.setRange(0, layerCount - 1)
        for i in xrange(layerCount):
            self.progressBar.setValue(i)
            theLayerName = self.__mOgrDataSource.GetLayer(
                i).GetLayerDefn().GetName()
            self.labelLoading.setText(
                u"VFK data {}/{}: {}".format(i + 1, layerCount, theLayerName))
            QgsApplication.processEvents()
            self.__mOgrDataSource.GetLayer(i).GetFeatureCount(True)
            time.sleep(0.02)

        self.labelLoading.setText(
            u'Soubor {} úspěšně načten.'.format(label_text))

        os.environ['OGR_VFK_DB_OVERWRITE'] = '0'
        self.__mOgrDataSource.Destroy()
        self.__mOgrDataSource = None

    def __selectedIds(self, layer):
        """

        :type layer: QgsVectorLayer
        :return:
        """
        ids = []
        flist = layer.selectedFeatures()

        for it in flist:
            f = QgsFeature(it)
            ids.append(str(f.attribute("ID")))
        return ids

    def showInfoAboutSelection(self):
        layers = ["PAR", "BUD"]
        layerIds = {}
        for layer in layers:
            if layer in self.__mLoadedLayers:
                id = str(self.__mLoadedLayers[layer])
                vectorLayer = QgsMapLayerRegistry.instance().mapLayer(id)
                layerIds[layer] = self.__selectedIds(vectorLayer)

        self.vfkBrowser.showInfoAboutSelection(
            layerIds["PAR"], layerIds["BUD"])

    def showParInMap(self, ids):
        """

        :type ids: list
        :return:
        """
        if self.actionShowInfoaboutSelection.isChecked():
            self.setSelectionChangedConnected(False)
            self.showInMap(ids, "PAR")
            self.setSelectionChangedConnected(True)
        else:
            self.showInMap(ids, "PAR")

    def showBudInMap(self, ids):
        """

        :type ids: list
        :return:
        """
        if self.actionShowInfoaboutSelection.isChecked():
            self.setSelectionChangedConnected(False)
            self.showInMap(ids, "BUD")
            self.setSelectionChangedConnected(True)
        else:
            self.showInMap(ids, "BUD")

    def showOnCuzk(self):
        x = self.vfkBrowser.currentDefinitionPoint().first.split(".")[0]
        y = self.vfkBrowser.currentDefinitionPoint().second.split(".")[0]

        url = "http://nahlizenidokn.cuzk.cz/MapaIdentifikace.aspx?&x=-{}&y=-{}".format(
            y, x)
        QDesktopServices.openUrl(QUrl(url, QUrl.TolerantMode))

    def switchToImport(self):
        self.actionImport.trigger()

    def switchToSearch(self, searchType):
        """
        :type searchType: int
        """
        self.actionVyhledavani.trigger()
        self.searchCombo.setCurrentIndex(searchType)
        self.searchFormMainControls.searchForms.setCurrentIndex(searchType)

    def switchToChanges(self):
        """

        """
        self.actionZpracujZmeny.trigger()

    def succesfullExport(self, export_format):
        """

        :type export_format: str
        :return:
        """
        QMessageBox.information(
            self, u'Export', u"Export do formátu {} proběhl úspěšně.".format(
                export_format),
                                QMessageBox.Ok)

    def __dataWithoutParBud(self):
        """

        :type export_format: str
        :return:
        """
        QMessageBox.warning(self, u'Upozornění', u"Zvolený VFK soubor neobsahuje vrstvy s geometrií (PAR, BUD), proto nemohou "
                            u"být pomocí VFK Pluginu načtena. Data je možné načíst v QGIS pomocí volby "
                            u"'Načíst vektorovou vrstvu.'", QMessageBox.Ok)

    def __addRowToGridLayout(self):
        if len(self.__vfkLineEdits) >= 5:
            self.__maximumLineEditsReached()
            return

        # update label
        self.label.setText('VFK soubory:')

        # new layout
        horizontalLayout = QtGui.QHBoxLayout()

        # create new objects
        self.__browseButtons['browseButton_{}'.format(
            len(self.__vfkLineEdits) + 1)] = QtGui.QPushButton(u"Procházet")
        self.__vfkLineEdits['vfkLineEdit_{}'.format(
            len(self.__vfkLineEdits) + 1)] = QtGui.QLineEdit()

        horizontalLayout.addWidget(self.__vfkLineEdits[
                                   'vfkLineEdit_{}'.format(len(self.__vfkLineEdits))])
        horizontalLayout.addWidget(self.__browseButtons[
                                   'browseButton_{}'.format(len(self.__vfkLineEdits))])

        # number of lines in gridLayout
        rows_count = self.gridLayout_12.rowCount(
        )  # count of rows in gridLayout

        # export objects from gridLayout
        item_label = self.gridLayout_12.itemAtPosition(rows_count - 3, 0)
        item_par = self.gridLayout_12.itemAtPosition(rows_count - 3, 1)
        item_bud = self.gridLayout_12.itemAtPosition(rows_count - 2, 1)
        item_settings = self.gridLayout_12.itemAtPosition(rows_count - 1, 0)
        item_rewrite_db = self.gridLayout_12.itemAtPosition(rows_count - 1, 1)

        # remove objects from gridLayout
        self.gridLayout_12.removeItem(
            self.gridLayout_12.itemAtPosition(rows_count - 3, 0))
        self.gridLayout_12.removeItem(
            self.gridLayout_12.itemAtPosition(rows_count - 3, 1))
        self.gridLayout_12.removeItem(
            self.gridLayout_12.itemAtPosition(rows_count - 2, 1))
        self.gridLayout_12.removeItem(
            self.gridLayout_12.itemAtPosition(rows_count - 1, 0))
        self.gridLayout_12.removeItem(
            self.gridLayout_12.itemAtPosition(rows_count - 1, 1))

        # re-build gridLayout
        self.gridLayout_12.addLayout(horizontalLayout, rows_count - 3, 1)
        self.gridLayout_12.addItem(item_label, rows_count - 2, 0)
        self.gridLayout_12.addItem(item_par, rows_count - 2, 1)
        self.gridLayout_12.addItem(item_bud, rows_count - 1, 1)
        self.gridLayout_12.addItem(item_settings, rows_count, 0)
        self.gridLayout_12.addItem(item_rewrite_db, rows_count, 1)

        self.__browseButtons['browseButton_{}'.format(len(self.__vfkLineEdits))].clicked.\
            connect(lambda: self.browseButton_clicked(
                int('{}'.format(len(self.__vfkLineEdits)))))

    def __maximumLineEditsReached(self):
        QMessageBox.information(self, u'Upozornění', u"Byl dosažen maximální počet ({}) VFK souboru pro zpracování."
                                                     u"\nNačítání dalších souborů není povoleno!".
                                format(self.lineEditsCount), QMessageBox.Ok)

    def browseDb_clicked(self, database_type):
        """
        Method run dialog for select database in widget with changes.
        According to pushButton name will fill in relevant lineEdit.
        :type database_type: str
        """
        title = u'Vyber databázi'
        settings = QSettings()
        lastUsedDir = str(settings.value('/UI/' + "lastVectorFileFilter" + "Dir", "."))

        if database_type == 'mainDb':
            self.__databases[database_type] = QFileDialog.getOpenFileName(self, title, lastUsedDir, u'Datábaze (*.db)')
            if not self.__databases[database_type]:
                return
            self.le_mainDb.setText(self.__databases[database_type])

        elif database_type == 'amendmentDb':
            self.__databases[database_type] = QFileDialog.getOpenFileName(self, title, lastUsedDir, u'Datábaze (*.db)')
            if not self.__databases[database_type]:
                return
            self.le_amendmentDb.setText(self.__databases[database_type])

        elif database_type == 'exportDb':
            title = u'Zadej jméno výstupní databáze'
            self.__databases[database_type] = QFileDialog.getSaveFileName(self, u"Jméno výstupní databáze",
                                                                          ".db", u"Databáze (*.db)")
            if not self.__databases[database_type]:
                return
            self.le_exportDb.setText(self.__databases[database_type])

        if len(self.__databases) == 3:
            self.pb_applyChanges.setEnabled(True)

    def applyChanges(self):
        """
        Method
        :return:
        """
        self.changes_instance.run(self.__databases['mainDb'],
                                  self.__databases['amendmentDb'],
                                  self.__databases['exportDb'])

    def __updateProgressBarChanges(self, iteration, table_name):
        """
        :type iteration: int
        :type table_name: str
        """
        self.progressBar_changes.setValue(iteration)
        self.l_status.setText(u'Aplikuji změny na tabulku {}...'.format(table_name))
        QgsApplication.processEvents()

    def __setRangeProgressBarChanges(self, max_range):
        """
        :type max_range: int
        """
        self.progressBar_changes.setRange(0, max_range)
        self.progressBar_changes.setValue(0)

    def __changesApplied(self):
        """
        """
        time.sleep(1)
        self.l_status.setText(u'Změny byly úspěšně aplikovány.')
        QgsApplication.processEvents()

    def __changesPreprocessingDatabase(self):
        """
        """
        self.l_status.setText(u'Připravuji výstupní databázi...')
        QgsApplication.processEvents()

    def __checkIfAmendmentFile(self, file_name):
        """

        :param file_name: Name of the input file
        :type file_name: str
        :return: bool
        """
        if file_name.endswith(".vfk"):
            with open(file_name, 'r') as f:
                for line in f:

                    line_splited = str(line).split(';')

                    if line_splited[0] == '&HZMENY':
                        if line_splited[1] == '1':
                            return True
                        else:
                            return False
        else:
            print 'database'
            # TODO: dopsat kontrolu, zda se jedna o stavovou, nebo zmenovou databazi

    def radioButtonValue(self):
        """
        Check which radio button is checked
        """
        self.vfkFileLineEdit.setText('')
        self.__fileName = []
        self.loadVfkButton.setEnabled(False)

        if self.rb_file.isChecked():
            self.__source_for_data = 'file'
            self.pb_nextFile.show()
            self.label.setText('VFK soubor:')
        elif self.rb_directory.isChecked():
            self.__source_for_data = 'directory'
            self.pb_nextFile.hide()
            self.label.setText(u'Adresář:')

            # delete
            if len(self.__browseButtons) > 1:
                for i, button in enumerate(self.__browseButtons):
                    if i > 0:
                        self.__browseButtons[button].hide()

            if len(self.__vfkLineEdits) > 1:
                for i, le in enumerate(self.__vfkLineEdits):
                    if i > 0:
                        self.__vfkLineEdits[le].hide()

    def __findVFKFilesInDirectory(self, dir_path):
        """
        Finds all files with extension '.vfk' in given directory including subdirectories
        :param dir_path: Path to directory.
        :type dir_path: str
        :return: List of VFK files
        """
        file_paths = []
        for root, dirs, files in os.walk(dir_path):
            for file in files:
                if file.endswith(".vfk"):
                    file_paths.append(os.path.join(root, file))

        return file_paths


    def __createToolbarsAndConnect(self):

        actionGroup = QActionGroup(self)
        actionGroup.addAction(self.actionImport)
        actionGroup.addAction(self.actionVyhledavani)
        actionGroup.addAction(self.actionZpracujZmeny)

        # QSignalMapper
        self.signalMapper = QSignalMapper(self)

        # connect to 'clicked' on all buttons
        self.connect(self.actionImport, SIGNAL(
            "triggered()"), self.signalMapper, SLOT("map()"))
        self.connect(self.actionVyhledavani, SIGNAL(
            "triggered()"), self.signalMapper, SLOT("map()"))
        self.connect(self.actionZpracujZmeny, SIGNAL(
            "triggered()"), self.signalMapper, SLOT("map()"))

        # setMapping on each button to the QStackedWidget index we'd like to
        # switch to
        self.signalMapper.setMapping(self.actionImport, 0)
        self.signalMapper.setMapping(self.actionVyhledavani, 2)
        self.signalMapper.setMapping(self.actionZpracujZmeny, 1)

        # connect mapper to stackedWidget
        self.connect(self.signalMapper, SIGNAL("mapped(int)"),
                     self.stackedWidget, SLOT("setCurrentIndex(int)"))
        self.actionImport.trigger()

        self.connect(self.vfkBrowser, SIGNAL(
            "switchToPanelImport"), self.switchToImport)
        self.connect(self.vfkBrowser, SIGNAL(
            "switchToPanelSearch"), self.switchToSearch)
        self.connect(self.vfkBrowser, SIGNAL(
            "switchToPanelChanges"), self.switchToChanges)

        # Browser toolbar
        # ---------------
        self.__mBrowserToolbar = QToolBar(self)
        self.connect(self.actionBack, SIGNAL(
            "triggered()"), self.vfkBrowser.goBack)
        self.connect(self.actionForward, SIGNAL(
            "triggered()"), self.vfkBrowser.goForth)

        self.connect(self.actionSelectBudInMap,
                     SIGNAL("triggered()"), self.selectBudInMap)
        self.connect(self.actionSelectParInMap,
                     SIGNAL("triggered()"), self.selectParInMap)
        self.connect(self.actionCuzkPage,
                     SIGNAL("triggered()"), self.showOnCuzk)

        self.connect(self.actionExportLatex,
                     SIGNAL("triggered()"), self.latexExport)
        self.connect(self.actionExportHtml,
                     SIGNAL("triggered()"), self.htmlExport)

        self.connect(self.actionShowInfoaboutSelection, SIGNAL(
            "toggled(bool)"), self.setSelectionChangedConnected)
        self.connect(self.actionShowHelpPage, SIGNAL(
            "triggered()"), self.vfkBrowser.showHelpPage)

        self.loadVfkButton.clicked.connect(self.loadVfkButton_clicked)

        self.__browseButtons['browseButton_1'] = self.browseButton
        self.__browseButtons['browseButton_1'].clicked.connect(
            lambda: self.browseButton_clicked(int('{}'.format(len(self.__vfkLineEdits)))))

        self.__vfkLineEdits['vfkLineEdit_1'] = self.vfkFileLineEdit

        bt = QToolButton(self.__mBrowserToolbar)
        bt.setPopupMode(QToolButton.InstantPopup)
        bt.setText("Export ")

        menu = QMenu(bt)
        menu.addAction(self.actionExportLatex)
        menu.addAction(self.actionExportHtml)
        bt.setMenu(menu)

        # add actions to toolbar icons
        self.__mBrowserToolbar.addAction(self.actionImport)
        self.__mBrowserToolbar.addAction(self.actionVyhledavani)
        self.__mBrowserToolbar.addAction(self.actionZpracujZmeny)
        self.__mBrowserToolbar.addSeparator()
        self.__mBrowserToolbar.addAction(self.actionBack)
        self.__mBrowserToolbar.addAction(self.actionForward)
        self.__mBrowserToolbar.addAction(self.actionSelectParInMap)
        self.__mBrowserToolbar.addAction(self.actionSelectBudInMap)
        self.__mBrowserToolbar.addAction(self.actionCuzkPage)
        self.__mBrowserToolbar.addSeparator()
        self.__mBrowserToolbar.addAction(self.actionShowInfoaboutSelection)
        self.__mBrowserToolbar.addSeparator()
        self.__mBrowserToolbar.addWidget(bt)
        self.__mBrowserToolbar.addSeparator()
        self.__mBrowserToolbar.addAction(self.actionShowHelpPage)

        self.rightWidgetLayout.insertWidget(0, self.__mBrowserToolbar)

        # connect signals from vfkbrowser when changing history
        self.connect(self.vfkBrowser, SIGNAL(
            "currentParIdsChanged"), self.actionSelectParInMap.setEnabled)
        self.connect(self.vfkBrowser, SIGNAL("currentBudIdsChanged"),
                     self.actionSelectBudInMap.setEnabled)
        self.connect(self.vfkBrowser, SIGNAL(
            "historyBefore"), self.actionBack.setEnabled)
        self.connect(self.vfkBrowser, SIGNAL(
            "historyAfter"), self.actionForward.setEnabled)
        self.connect(self.vfkBrowser, SIGNAL(
            "definitionPointAvailable"), self.actionCuzkPage.setEnabled)

        # add toolTips
        self.pb_nextFile.setToolTip(u'Přidej další soubor VFK')
        self.parCheckBox.setToolTip(u'Načti vrstvu parcel')
        self.budCheckBox.setToolTip(u'Načti vrstvu budov')

        # add new VFK file
        self.pb_nextFile.clicked.connect(self.__addRowToGridLayout)

        # widget apply changes
        self.pb_mainDb.clicked.connect(
            lambda: self.browseDb_clicked('mainDb'))
        self.pb_amendmentDb.clicked.connect(
            lambda: self.browseDb_clicked('amendmentDb'))
        self.pb_exportDb.clicked.connect(
            lambda: self.browseDb_clicked('exportDb'))

        self.pb_applyChanges.clicked.connect(self.applyChanges)
        self.pb_applyChanges.setEnabled(False)

        self.connect(self.changes_instance, SIGNAL("maxRangeProgressBar"), self.__setRangeProgressBarChanges)
        self.connect(self.changes_instance, SIGNAL("updateStatus"), self.__updateProgressBarChanges)
        self.connect(self.changes_instance, SIGNAL("finishedStatus"), self.__changesApplied)
        self.connect(self.changes_instance, SIGNAL("preprocessingDatabase"), self.__changesPreprocessingDatabase)

        # connect radio boxes
        self.rb_file.clicked.connect(self.radioButtonValue)
        self.rb_directory.clicked.connect(self.radioButtonValue)
예제 #42
0
class MainWindowStart(QMainWindow, MainWindow_Pro.Ui_MainWindow):
    def __init__(self, parent=None):
        super(MainWindowStart, self).__init__(parent)
        # Mappers for connecting buttons and labels
        self.myMapper = QSignalMapper(self)
        self.myMapper_StyleSheet = QSignalMapper(self)
        # Load UI
        self.setupUi(self)

        self.regex_edits = QRegExp(r"(^[0]+$|^$)")
        self._filter = Filter()
        self.filename = QString()
        self.edit1_delayh.installEventFilter(self._filter)
        self.sizeLabel = QLabel()
        self.sizeLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        self.statusBar1.addPermanentWidget(self.sizeLabel)
        self.statusBar1.setSizeGripEnabled(False)

        self.create_connections()
        self.assign_shortcuts()

        self.create_tool_bar()
        self.update_devices_list()
        # self.button_stop.clicked.connect(self.stop_all)
        # List of valve pushbuttons
        self.valve_list = [
            self.valve1, self.valve2, self.valve3, self.valve4, self.valve5,
            self.valve6, self.valve7, self.valve8
        ]

        # GroupBoxes for grouping labels and buttons on each row, used for applying StyleSheets
        self.group_boxes = [
            self.groupbox1, self.groupbox2, self.groupbox3, self.groupbox4,
            self.groupbox5, self.groupbox6, self.groupbox7, self.groupbox8
        ]
        # List of lineEdits
        self.lineEdits_list = [
            (self.edit1_delayh, self.edit1_delaym, self.edit1_delays,
             self.edit1_onh, self.edit1_onm, self.edit1_ons, self.edit1_offh,
             self.edit1_offm, self.edit1_offs, self.edit1_totalh,
             self.edit1_totalm, self.edit1_totals),
            (self.edit2_delayh, self.edit2_delaym, self.edit2_delays,
             self.edit2_onh, self.edit2_onm, self.edit2_ons, self.edit2_offh,
             self.edit2_offm, self.edit2_offs, self.edit2_totalh,
             self.edit2_totalm, self.edit2_totals),
            (self.edit3_delayh, self.edit3_delaym, self.edit3_delays,
             self.edit3_onh, self.edit3_onm, self.edit3_ons, self.edit3_offh,
             self.edit3_offm, self.edit3_offs, self.edit3_totalh,
             self.edit3_totalm, self.edit3_totals),
            (self.edit4_delayh, self.edit4_delaym, self.edit4_delays,
             self.edit4_onh, self.edit4_onm, self.edit4_ons, self.edit4_offh,
             self.edit4_offm, self.edit4_offs, self.edit4_totalh,
             self.edit4_totalm, self.edit4_totals),
            (self.edit5_delayh, self.edit5_delaym, self.edit5_delays,
             self.edit5_onh, self.edit5_onm, self.edit5_ons, self.edit5_offh,
             self.edit5_offm, self.edit5_offs, self.edit5_totalh,
             self.edit5_totalm, self.edit5_totals),
            (self.edit6_delayh, self.edit6_delaym, self.edit6_delays,
             self.edit6_onh, self.edit6_onm, self.edit6_ons, self.edit6_offh,
             self.edit6_offm, self.edit6_offs, self.edit6_totalh,
             self.edit6_totalm, self.edit6_totals),
            (self.edit7_delayh, self.edit7_delaym, self.edit7_delays,
             self.edit7_onh, self.edit7_onm, self.edit7_ons, self.edit7_offh,
             self.edit7_offm, self.edit7_offs, self.edit7_totalh,
             self.edit7_totalm, self.edit7_totals),
            (self.edit8_delayh, self.edit8_delaym, self.edit8_delays,
             self.edit8_onh, self.edit8_onm, self.edit8_ons, self.edit8_offh,
             self.edit8_offm, self.edit8_offs, self.edit8_totalh,
             self.edit8_totalm, self.edit8_totals)
        ]

        for index, editLabels in enumerate(self.lineEdits_list, 1):

            for index2, lineedits in enumerate(editLabels, 0):
                # Apply mapper (GUIObject, objectIndex)
                self.myMapper_StyleSheet.setMapping(
                    self.lineEdits_list[index - 1][index2], index - 1)
                # Connect mapper to signal
                (self.lineEdits_list[index - 1][index2]).textChanged.connect(
                    self.myMapper_StyleSheet.map)
                # Set event Filter, for detecting when Focus changes
                self.lineEdits_list[index - 1][index2].installEventFilter(
                    self._filter)
            # Set Mappers for buttons (1..8)
            self.myMapper.setMapping(self.valve_list[index - 1], index)
            # Connect mapper to signal for detecting clicks on buttons
            (self.valve_list[index - 1]).clicked.connect(self.myMapper.map)
        # Connect to signal for enabling labelEdits
        self.myMapper.mapped['int'].connect(self.enable_fields)
        # Connect to signal for changing color of groupbox used for visual indication
        self.myMapper_StyleSheet.mapped['int'].connect(self.valve_color_status)

    # Create Keyboard Shortcuts
    def assign_shortcuts(self):
        self.actionArchivo_Nuevo.setShortcut(QKeySequence.New)
        self.action_Abrir.setShortcut(QKeySequence.Open)
        self.action_Guardar.setShortcut(QKeySequence.Save)
        self.actionGuardar_Como.setShortcut(QKeySequence.SaveAs)
        self.action_Limpiar.setShortcut('Ctrl+L')
        self.actionVAL_508_Ayuda.setShortcut(QKeySequence.HelpContents)
        self.action_Salir.setShortcut(QKeySequence.Close)
        # self.actionPreferencias.setShortcut(QKeySequence.Preferences)

        self.action_Detener_USB.setShortcut('Ctrl+Shift+C')
        self.action_Ejecutar.setShortcut('Ctrl+Shift+X')
        self.action_Para_Valvulas.setShortcut('Ctrl+Shift+P')

    # Create connections to signals
    def create_connections(self):
        self.actionArchivo_Nuevo.triggered.connect(self.new_file)
        self.action_Abrir.triggered.connect(self.open_file)
        self.action_Guardar.triggered.connect(self.save_file)
        self.actionGuardar_Como.triggered.connect(self.save_file_as)
        self.action_Limpiar.triggered.connect(self.clean_fields)
        self.action_Salir.triggered.connect(self.close)
        self.actionVAL_508_Ayuda.triggered.connect(self.show_help)
        self.actionAcerca_de_VAL_508.triggered.connect(self.show_about)

        self.action_Detener_USB.triggered.connect(self.stop_usb)
        self.action_Ejecutar.triggered.connect(self.execute)
        self.action_Para_Valvulas.triggered.connect(self.stop_all)

    # Creation of About Dialog
    def show_about(self):
        about = aboutdialog.AboutDialog(self)
        about.show()

    # Creation of Help Form
    def show_help(self):
        form = helpform.HelpForm('Help.html', self)
        form.show()

    def new_file(self):
        self.filename = QString()
        self.clean_fields()

    # Close connection to arduino before closing Program
    def closeEvent(self, QCloseEvent):
        try:
            self.thread_connection.serial_connection.close()
            logging.debug("Thread running and killed at closing program")
        except AttributeError:
            logging.debug("Thread was not running when closing program OK")

    def clean_fields(self):
        for index, editLabels in enumerate(self.lineEdits_list, 1):
            for index2, lineedits in enumerate(editLabels, 0):
                self.lineEdits_list[index - 1][index2].setText('0')

    def save_file_as(self):
        filename_copy = self.filename
        logging.info("Current filename: %s" % self.filename)
        my_home = os.path.expanduser('~')
        self.filename = QFileDialog.getSaveFileName(
            self, self.tr('Guardar como'),
            os.path.join(my_home, "archivo.txt"), "", "",
            QFileDialog.DontUseNativeDialog)
        logging.info("Filename to save: %s" % self.filename)
        if not self.filename.isNull():
            if self.filename.endsWith(QString('.txt')):
                self.write_data_to_file('w')
            else:
                self.filename.append(QString('.txt'))
                messageBox = QMessageBox(self)
                messageBox.setStyleSheet(
                    'QMessageBox QLabel {font: bold 14pt "Cantarell";}')
                messageBox.setWindowTitle(self.tr('Advertencia'))
                messageBox.setText(
                    self.tr(u"El archivo ya existe, ¿Reemplazar?"))
                messageBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
                messageBox.setIconPixmap(QPixmap(':/broken_file.png'))
                if messageBox.exec_() == QMessageBox.Yes:
                    self.write_data_to_file('w')
                else:
                    try:
                        while True:
                            self.filename = QFileDialog.getSaveFileName(
                                self, self.tr('Guardar como'),
                                os.path.join(my_home, "archivo.txt"), "", "",
                                QFileDialog.DontUseNativeDialog)
                            if self.filename.isNull():
                                raise Saved_Canceled()
                            else:
                                messageBox = QMessageBox(self)
                                messageBox.setStyleSheet(
                                    'QMessageBox QLabel {font: bold 14pt "Cantarell";}'
                                )
                                messageBox.setWindowTitle(
                                    self.tr('Advertencia'))
                                messageBox.setText(
                                    self.tr(
                                        u"El archivo ya existe, ¿Reemplazar?"))
                                messageBox.setStandardButtons(QMessageBox.Yes
                                                              | QMessageBox.No)
                                messageBox.setIconPixmap(
                                    QPixmap(':/broken_file.png'))
                                if messageBox.exec_() == QMessageBox.Yes:
                                    self.write_data_to_file('w')
                                    raise Saved_Accepted()
                    except Saved_Canceled:
                        self.filename = filename_copy
                    except Saved_Accepted:
                        pass
        logging.info("Current filename after operation: %s" % self.filename)

    def save_file(self):
        if self.filename.isNull():
            self.save_file_as()
        else:
            self.write_data_to_file('w')

    # Colect data for arduino
    def execute(self):
        string_data = ''
        list_strings = []
        if str(self.arduino_combobox.currentText()):
            self.statusBar1.showMessage(self.tr('Conectando...'))
            # Gather all  the contents of each row of valves and create a list of lists with them
            for elem_edit in self.lineEdits_list:
                # delay
                string_data = string_data + str(
                    ((int(elem_edit[0].text()) * 3600) +
                     (int(elem_edit[1].text()) * 60) +
                     (int(elem_edit[2].text()))) * 1000) + ';'
                # ON
                string_data = string_data + str(
                    ((int(elem_edit[3].text()) * 3600) +
                     (int(elem_edit[4].text()) * 60) +
                     (int(elem_edit[5].text()))) * 1000) + ';'
                # OFF
                string_data = string_data + str(
                    ((int(elem_edit[6].text()) * 3600) +
                     (int(elem_edit[7].text()) * 60) +
                     (int(elem_edit[8].text()))) * 1000) + ';'
                # Total
                string_data = string_data + str(
                    ((int(elem_edit[9].text()) * 3600) +
                     (int(elem_edit[10].text()) * 60) +
                     (int(elem_edit[11].text()))) * 1000) + ';'

                list_strings.append(string_data)
                string_data = ''

            # Start QThread for communicating with arduino
            self.thread_connection = Arduino_Communication(
                str(self.arduino_combobox.currentText()), list_strings)
            self.thread_connection.start()
            self.action_Ejecutar.setEnabled(False)
            self.action_Para_Valvulas.setEnabled(False)

            # Connect to current QThread instance in order to know the status of it's termination
            # This line used only when stopping current task
            self.thread_connection.finished.connect(self.finished_thread)
            self.thread_connection.connection_exit_status.connect(
                self.finished_thread)
        else:
            messageBox = QMessageBox(self)
            messageBox.setStyleSheet(
                'QMessageBox QLabel {font: bold 14pt "Cantarell";}')
            messageBox.setWindowTitle(self.tr('Advertencia'))
            messageBox.setText(self.tr("Arduino no seleccionado"))
            messageBox.setStandardButtons(QMessageBox.Ok)
            messageBox.setIconPixmap(QPixmap(':/usb_error.png'))
            messageBox.exec_()

    # Inform QThread to stop sending data to arduino
    def stop_usb(self):
        if str(self.arduino_combobox.currentText()):
            try:
                self.statusBar1.showMessage(self.tr(u'Conexión detenida'))
                if self.thread_connection.isRunning():
                    mutex.lock()
                    self.thread_connection.kill_serial = True
                    mutex.unlock()
            except AttributeError:
                logging.debug("Thread not running \'disconnected! \'")
        else:
            messageBox = QMessageBox(self)
            messageBox.setStyleSheet(
                'QMessageBox QLabel {font: bold 14pt "Cantarell";}')
            messageBox.setWindowTitle(self.tr('Advertencia'))
            messageBox.setText(self.tr("Arduino no seleccionado"))
            messageBox.setStandardButtons(QMessageBox.Ok)
            messageBox.setIconPixmap(QPixmap(':/usb_error.png'))
            messageBox.exec_()

    def enable_fields(self, index):
        hours_reg = QRegExp(r"0*[0-9]{1,3}")
        sec_reg = QRegExp(r"(0*[0-9])|(0*[0-5][0-9])")
        for counter, line_edit in enumerate(self.lineEdits_list[index - 1]):
            line_edit.setEnabled(self.valve_list[index - 1].isChecked())
            if counter % 3 == 0:
                line_edit.setValidator(QRegExpValidator(hours_reg, self))
            else:
                line_edit.setValidator(QRegExpValidator(sec_reg, self))

    def valve_color_status(self, index):
        logging.info("Checking color from valve button")
        for edit in self.lineEdits_list[index]:
            if edit.text().contains(self.regex_edits):
                self.group_boxes[index].setStyleSheet('''QGroupBox {
                                                      border: 2px solid;
                                                      border-color: rgba(255, 255, 255, 0);}'''
                                                      )
            else:
                self.group_boxes[index].setStyleSheet(
                    '''QGroupBox {background-color: rgba(103, 255, 126, 150);
                                                      border: 2px solid;
                                                      border-color: rgba(255, 255, 255, 255);}'''
                )
                break

    def create_tool_bar(self):
        self.label_arduino = QLabel(self.tr('Dispositivos: '))
        self.toolBar.addWidget(self.label_arduino)

        self.arduino_combobox = QComboBox()
        self.arduino_combobox.setToolTip(self.tr('Seleccionar Arduino'))
        self.arduino_combobox.setFocusPolicy(Qt.NoFocus)

        # Update List of Arduino devices
        self.reload = QAction(QIcon(":/reload.png"), self.tr("&Refrescar"),
                              self)
        self.reload.setShortcut(QKeySequence.Refresh)
        self.reload.setToolTip(self.tr('Refrescar Dispositivos'))
        self.reload.triggered.connect(self.update_devices_list)

        self.toolBar.addWidget(self.arduino_combobox)
        self.toolBar.addAction(self.reload)

    # Update current usb devices connected to PC
    def update_devices_list(self):
        device_list = serial.tools.list_ports.comports()
        current_arduino = self.arduino_combobox.currentText()
        self.arduino_combobox.clear()
        for device_index, device in enumerate(sorted(device_list)):
            self.arduino_combobox.addItem(device.device)
            if device.device == current_arduino:
                self.arduino_combobox.setCurrentIndex(device_index)

    # Stop current arduino task
    def stop_all(self):
        if str(self.arduino_combobox.currentText()):
            self.thread_connection = Arduino_Communication(
                str(self.arduino_combobox.currentText()))
            self.thread_connection.start()
            self.action_Ejecutar.setEnabled(False)
            self.action_Para_Valvulas.setEnabled(False)
            self.action_Detener_USB.setEnabled(False)
            self.thread_connection.finished.connect(self.finished_thread)
            self.thread_connection.connection_exit_status.connect(
                self.finished_thread)
        else:
            messageBox = QMessageBox(self)
            messageBox.setStyleSheet(
                'QMessageBox QLabel {font: bold 14pt "Cantarell";}')
            messageBox.setWindowTitle(self.tr('Advertencia'))
            messageBox.setText(self.tr("Arduino no seleccionado"))
            messageBox.setStandardButtons(QMessageBox.Ok)
            messageBox.setIconPixmap(QPixmap(':/usb_error.png'))
            messageBox.exec_()

    def open_file(self):
        try:
            my_home = os.path.expanduser('~')
            file_name = QFileDialog.getOpenFileName(
                self, self.tr('Abrir archivo'), my_home, '*.txt', '*.txt',
                QFileDialog.DontUseNativeDialog)
            logging.warning("file_name type: %s" % type(file_name))
            list_values = []
            if not file_name.isNull():
                with open(file_name) as fp:
                    for line in fp:
                        list_values.extend([line.replace('\n', '')])
                logging.info("List Content: %s" % list_values)
                count = 0
                for elems in self.lineEdits_list:
                    for inner_elem in elems:
                        if not unicode(list_values[count]).isdigit():
                            raise Uncompatible_Data()
                        inner_elem.setText(list_values[count])
                        count = count + 1
                self.filename = file_name
        except (IOError, OSError):
            messageBox = QMessageBox(self)
            messageBox.setStyleSheet(
                'QMessageBox QLabel {font: bold 14pt "Cantarell";}')
            messageBox.setWindowTitle(self.tr('Error'))
            messageBox.setText(self.tr('No se pudo abrir el archivo'))
            messageBox.setStandardButtons(QMessageBox.Ok)
            messageBox.setIconPixmap(QPixmap(':/broken_file.png'))
            messageBox.exec_()
        except (IndexError, Uncompatible_Data):
            messageBox = QMessageBox(self)
            messageBox.setStyleSheet(
                'QMessageBox QLabel {font: bold 14pt "Cantarell";}')
            messageBox.setWindowTitle(self.tr('Error'))
            messageBox.setText(self.tr('Formato Incompatible'))
            messageBox.setStandardButtons(QMessageBox.Ok)
            messageBox.setIconPixmap(QPixmap(':/broken_file.png'))
            messageBox.exec_()

    # Inform the user if we were able to send data successfully to arduino
    def finished_thread(self, error=None, message=''):
        if error == 'error':
            messageBox = QMessageBox(self)
            messageBox.setStyleSheet(
                'QMessageBox QLabel {font: bold 14pt "Cantarell";}')
            messageBox.setWindowTitle(self.tr('Error'))
            messageBox.setText(message)
            messageBox.setStandardButtons(QMessageBox.Ok)
            messageBox.setIconPixmap(QPixmap(':/usb_error.png'))
            messageBox.exec_()
            return
        elif error == 'success':
            messageBox = QMessageBox(self)
            messageBox.setStyleSheet(
                'QMessageBox QLabel {font: bold 14pt "Cantarell";}')
            messageBox.setWindowTitle(self.tr(u'Éxito'))
            messageBox.setText(message)
            messageBox.setStandardButtons(QMessageBox.Ok)
            messageBox.setIconPixmap(QPixmap(':/usb_success.png'))
            messageBox.exec_()
            return
        elif error == 'stopped':
            messageBox = QMessageBox(self)
            messageBox.setStyleSheet(
                'QMessageBox QLabel {font: bold 14pt "Cantarell";}')
            messageBox.setWindowTitle(self.tr(u'Éxito'))
            messageBox.setText(message)
            messageBox.setStandardButtons(QMessageBox.Ok)
            messageBox.setIconPixmap(QPixmap(':/success_general.png'))
            messageBox.exec_()
            return
        self.action_Ejecutar.setEnabled(True)
        self.action_Para_Valvulas.setEnabled(True)
        self.action_Detener_USB.setEnabled(True)
        self.statusBar1.showMessage(self.tr('Finalizado'))

    # Save data to disk
    def write_data_to_file(self, open_mode):
        progressDialog = QProgressDialog()
        progressDialog.setModal(True)
        progressDialog.setLabelText(self.tr('Guardando...'))
        progressDialog.setMaximum(8)
        progressDialog.setCancelButton(None)
        progressDialog.show()

        try:
            # File is closed automatically even on error
            with open(unicode(self.filename), open_mode) as file_obj:
                for count, elem_edit in enumerate(self.lineEdits_list, 1):
                    file_obj.write(''.join([str(elem_edit[0].text()), '\n']))
                    file_obj.write(''.join([str(elem_edit[1].text()), '\n']))
                    file_obj.write(''.join([str(elem_edit[2].text()), '\n']))
                    file_obj.write(''.join([str(elem_edit[3].text()), '\n']))
                    file_obj.write(''.join([str(elem_edit[4].text()), '\n']))
                    file_obj.write(''.join([str(elem_edit[5].text()), '\n']))
                    file_obj.write(''.join([str(elem_edit[6].text()), '\n']))
                    file_obj.write(''.join([str(elem_edit[7].text()), '\n']))
                    file_obj.write(''.join([str(elem_edit[8].text()), '\n']))
                    file_obj.write(''.join([str(elem_edit[9].text()), '\n']))
                    file_obj.write(''.join([str(elem_edit[10].text()), '\n']))
                    file_obj.write(''.join([str(elem_edit[11].text()), '\n']))
                    progressDialog.setValue(count)
        except (IOError, OSError):
            progressDialog.close()
            messageBox = QMessageBox(self)
            messageBox.setStyleSheet(
                'QMessageBox QLabel {font: bold 14pt "Cantarell";}')
            messageBox.setWindowTitle(self.tr('Error'))
            messageBox.setText(self.tr('Error al guardar'))
            messageBox.setStandardButtons(QMessageBox.Ok)
            messageBox.setIcon(QMessageBox.Critical)
            messageBox.exec_()

        else:
            self.statusBar1.showMessage(self.tr('Guardado'), 3000)
예제 #43
0
class CaseSelectionWidget(QWidget):

    caseSelectionChanged = pyqtSignal()

    def __init__(self, current_case):
        QWidget.__init__(self)

        self.__model = PlotCaseModel()

        self.__signal_mapper = QSignalMapper(self)
        self.__case_selectors = {}
        self.__case_selectors_order = []

        layout = QVBoxLayout()

        add_button_layout = QHBoxLayout()
        button = QPushButton(util.resourceIcon("ide/small/add"), "Add case to plot")
        button.clicked.connect(self.addCaseSelector)

        add_button_layout.addStretch()
        add_button_layout.addWidget(button)
        add_button_layout.addStretch()

        layout.addLayout(add_button_layout)

        self.__case_layout = QVBoxLayout()
        self.__case_layout.setMargin(0)
        layout.addLayout(self.__case_layout)

        self.addCaseSelector(disabled=True, current_case=current_case)
        layout.addStretch()

        self.setLayout(layout)

        self.__signal_mapper.mapped[QWidget].connect(self.removeWidget)


    def __caseName(self, widget):
        """ @rtype: str """
        return str(self.__case_selectors[widget].currentText())

    def getPlotCaseNames(self):
        if self.__model.rowCount() == 0:
            return []

        return [self.__caseName(widget) for widget in self.__case_selectors_order]


    def addCaseSelector(self, disabled=False, current_case=None):
        if len(self.__case_selectors_order) == 5:
            return

        widget = QWidget()

        layout = QHBoxLayout()
        layout.setMargin(0)
        widget.setLayout(layout)

        combo = QComboBox()
        combo.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLengthWithIcon)
        combo.setMinimumContentsLength(20)
        combo.setModel(self.__model)

        if current_case is not None:
            index = 0
            for item in self.__model:
                if item == current_case:
                    combo.setCurrentIndex(index)
                    break
                index += 1

        combo.currentIndexChanged.connect(self.caseSelectionChanged.emit)



        layout.addWidget(combo, 1)

        button = QToolButton()
        button.setAutoRaise(True)
        button.setDisabled(disabled)
        button.setIcon(util.resourceIcon("ide/small/delete"))
        button.clicked.connect(self.__signal_mapper.map)

        layout.addWidget(button)

        self.__case_selectors[widget] = combo
        self.__case_selectors_order.append(widget)
        self.__signal_mapper.setMapping(button, widget)

        self.__case_layout.addWidget(widget)

        self.caseSelectionChanged.emit()



    def removeWidget(self, widget):
        self.__case_layout.removeWidget(widget)
        del self.__case_selectors[widget]
        self.__case_selectors_order.remove(widget)
        widget.setParent(None)
        self.caseSelectionChanged.emit()
class CaseSelectionWidget(QWidget):

    caseSelectionChanged = pyqtSignal()

    def __init__(self, current_case):
        QWidget.__init__(self)

        self.__model = PlotCaseModel()

        self.__signal_mapper = QSignalMapper(self)
        self.__case_selectors = {}
        self.__case_selectors_order = []

        layout = QVBoxLayout()

        add_button_layout = QHBoxLayout()
        self.__add_case_button = QToolButton()
        self.__add_case_button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.__add_case_button.setText("Add case to plot")
        self.__add_case_button.setIcon(resourceIcon("ide/small/add"))
        self.__add_case_button.clicked.connect(self.addCaseSelector)

        add_button_layout.addStretch()
        add_button_layout.addWidget(self.__add_case_button)
        add_button_layout.addStretch()

        layout.addLayout(add_button_layout)

        self.__case_layout = QVBoxLayout()
        self.__case_layout.setMargin(0)
        layout.addLayout(self.__case_layout)

        self.addCaseSelector(disabled=True, current_case=current_case)
        layout.addStretch()

        self.setLayout(layout)

        self.__signal_mapper.mapped[QWidget].connect(self.removeWidget)

    def __caseName(self, widget):
        """ @rtype: str """
        return str(self.__case_selectors[widget].currentText())

    def getPlotCaseNames(self):
        if self.__model.rowCount() == 0:
            return []

        return [
            self.__caseName(widget) for widget in self.__case_selectors_order
        ]

    def checkCaseCount(self):
        state = True
        if len(self.__case_selectors_order) == 5:
            state = False

        self.__add_case_button.setEnabled(state)

    def addCaseSelector(self, disabled=False, current_case=None):
        widget = QWidget()

        layout = QHBoxLayout()
        layout.setMargin(0)
        widget.setLayout(layout)

        combo = QComboBox()
        combo.setSizeAdjustPolicy(
            QComboBox.AdjustToMinimumContentsLengthWithIcon)
        combo.setMinimumContentsLength(20)
        combo.setModel(self.__model)

        if current_case is not None:
            index = 0
            for item in self.__model:
                if item == current_case:
                    combo.setCurrentIndex(index)
                    break
                index += 1

        combo.currentIndexChanged.connect(self.caseSelectionChanged.emit)

        layout.addWidget(combo, 1)

        button = QToolButton()
        button.setAutoRaise(True)
        button.setDisabled(disabled)
        button.setIcon(resourceIcon("ide/small/delete"))
        button.clicked.connect(self.__signal_mapper.map)

        layout.addWidget(button)

        self.__case_selectors[widget] = combo
        self.__case_selectors_order.append(widget)
        self.__signal_mapper.setMapping(button, widget)

        self.__case_layout.addWidget(widget)

        self.checkCaseCount()
        self.caseSelectionChanged.emit()

    def removeWidget(self, widget):
        self.__case_layout.removeWidget(widget)
        del self.__case_selectors[widget]
        self.__case_selectors_order.remove(widget)
        widget.setParent(None)
        self.caseSelectionChanged.emit()

        self.checkCaseCount()
    def __init__(self, parent, tester):
        """Construct a new dockwindow following the tester """
        self.tester = tester

        QtGui.QDockWidget.__init__(self, tester.testname, parent)
        self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        if not self.widget():
            self.setWidget(QtGui.QWidget(self))

        vl = QtGui.QVBoxLayout(self.widget())
        #self.widget().setLayout(vl)

        panel = QtGui.QFrame(self)
        vl.addWidget(panel)

        self.login = QtGui.QLineEdit(self)
        self.login.textEdited.connect(self.check_logpass)
        self.password = QtGui.QLineEdit(self)
        self.password.setEchoMode(QtGui.QLineEdit.Password)
        self.password.textEdited.connect(self.check_logpass)

        self.tests = []

        fl = QtGui.QFormLayout(panel)
        fl.addRow('&Login:'******'&Password:', self.password)
        panel.setLayout(fl)

        panel = QtGui.QFrame(self)
        panel.setFrameShadow(QtGui.QFrame.Sunken)
        panel.setFrameShape(QtGui.QFrame.Panel)
        vl.addWidget(panel)

        vl2 = QtGui.QVBoxLayout(panel)

        signalmapper = QSignalMapper(self)
        signalmapper.mapped[int].connect(self.test)

        for i, test in enumerate(self.tester.tests):
            btn = QtGui.QPushButton("Test {}: {}".format(i + 1, test.name),
                                    panel)
            btn.setStyleSheet(self.btn_default_stylesheet)
            btn.setEnabled(False)
            vl2.addWidget(btn)
            self.tests.append(btn)
            signalmapper.setMapping(btn, i)
            btn.clicked.connect(signalmapper.map)

        panel.setLayout(vl2)

        self.text = QtGui.QLabel(
            "Enter your Coursera login and assignments password and push one of the test buttons above to run the test and submit the results to Coursera."
        )
        self.text.setWordWrap(True)
        vl.addWidget(self.text)
        self.text.setFrameShadow(QtGui.QFrame.Sunken)
        self.text.setFrameShape(QtGui.QFrame.Panel)
        self.text.setMargin(5)

        #vl.setStretch(2,1)
        vl.addStretch(1)
예제 #46
0
class RadioEditor ( BaseEditor ):
    """ Enumeration editor, used for the "custom" style, that displays radio
        buttons.
    """

    #-- Public Methods ---------------------------------------------------------

    def init ( self, parent ):
        """ Finishes initializing the editor by creating the underlying toolkit
            widget.
        """
        super( RadioEditor, self ).init( parent )

        # The control is a grid layout:
        self.control = layout = QGridLayout()
        layout.setSpacing( 0 )
        layout.setMargin( 0 )

        self._mapper = QSignalMapper()
        QObject.connect( self._mapper, SIGNAL( 'mapped(QWidget *)' ),
                         self.update_object )

        self.rebuild_editor()


    def update_object ( self, rb ):
        """ Handles the user clicking one of the custom radio buttons.
        """
        try:
            self.value = rb.value
        except:
            pass


    def update_editor ( self ):
        """ Updates the editor when the object facet changes externally to the
            editor.
        """
        value = self.value
        for i in range( self.control.count() ):
            rb = self.control.itemAt( i ).widget()
            rb.setChecked( rb.value == value )


    def rebuild_editor ( self ):
        """ Rebuilds the contents of the editor whenever the original factory
            object's **values** facet changes.
        """
        # Clear any existing content:
        ### self.clear_layout()

        # Get the current facet value:
        cur_name = self.str_value

        # Create a sizer to manage the radio buttons:
        names   = self.names
        mapping = self.mapping
        n       = len( names )
        cols    = self.factory.cols
        rows    = (n + cols - 1) / cols
        incr    = [ n / cols ] * cols
        rem     = n % cols
        for i in range( cols ):
            incr[i] += (rem > i)

        incr[-1] = -( reduce( lambda x, y: x + y, incr[:-1], 0 ) - 1 )

        # Add the set of all possible choices:
        index = 0

        for i in range( rows ):
            for j in range( cols ):
                if n > 0:
                    name = label = names[ index ]
                    label = self.string_value( label, capitalize )
                    rb = QRadioButton( label )
                    rb.value = mapping[ name ]

                    rb.setChecked( name == cur_name )

                    QObject.connect( rb, SIGNAL( 'clicked()' ), self._mapper,
                                     SLOT( 'map()' ) )
                    self._mapper.setMapping( rb, rb )

                    self.set_tooltip( rb )
                    self.control.addWidget( rb, i, j )

                    index += incr[j]
                    n     -= 1
예제 #47
0
class K800IRec(QWidget, mfso):
    def __init__(self):
       mfso.__init__(self, "K800i-Recover")
       self.name = "K800i-Recover"
       self.icon = None
       self.__disown__()

    def start(self, args):
        self.vfs = vfs.vfs()
        self.dumpnumber = 1
        try :
          self.nor = args['nor'].value()
          self.nand = args['nand'].value()
        except IndexError:
	  return
        try:
          self.spareSize = args["spare-size"].value()
        except IndexError:
          self.spareSize = 16
        try:
          self.pageSize = args["page-size"].value()
        except IndexError:
	  self.pageSize = 512
        self.k800n = Node("k800-base")
        self.k800n.__disown__()
        self.boot = SEBootBlock(self.nor, self.pageSize) 
        self.blockSize = self.boot.blockSize
        self.nandClean = SpareNode(self,  self.nand, "nandfs", self.pageSize, self.spareSize, self.k800n)
        self.norFs = NorFs(self, self.k800n,  self.nor, "norfs", self.boot)
        self.fullFs = FullFs(self, self.k800n, self.norFs, self.nandClean, "fullfs", self.boot)
        self.gdfs = GDFS(self, self.k800n, self.nor, "gdfs", self.boot)
        self.firmware = Firmware(self, self.k800n,  self.nor, "firmware", self.boot.norfsoffset)

        self.tables = Tables(self.fullFs, self.blockSize)
        self.registerTree(self.nand, self.k800n)
 
    def createDump(self):
       text, ok = QInputDialog.getText(self, "Create dump", "dump name:", QLineEdit.Normal, "k800-restore-" + str(self.dumpnumber)) 
       if ok and text != "":
         if  (self.vfs.getnode(self.nand.absolute() + "/" + str(text)) == None):  
           self.dumpnumber += 1
           newroot = Node(str(text))
	   newroot.__disown__()
	   for id in range(0, len(self.tables.tablesIdWriteMap) - 1):
             write = int(str(self.gtable.cellWidget(id, 0).currentText()), 16)
             self.tables.map[id] = self.tables.tablesIdWriteMap[id][write]
           virtual = VirtualMap(self, newroot, self.fullFs, self.tables, "virtual", self.blockSize)
           separt = SEPartitionBlock(virtual, self.boot.partitionblock, self.blockSize)
           self.createPart(separt, newroot, virtual)
           self.registerTree(self.nand, newroot) 
         else :
          box = QMessageBox(QMessageBox.Warning, "Error", "Error node already exists", QMessageBox.NoButton, self)
          box.exec_()
          self.createDump()

    def createPart(self, separt, newroot, virtual):
      for part in separt.partTable:
       if part.start > 0:
         p = Partition(self, newroot, virtual, part, self.blockSize)

    def g_display(self):
      QWidget.__init__(self, None)
      self.layout = QVBoxLayout(self)
      self.hlayout = QSplitter(self)
      self.layout.insertWidget(0, self.hlayout)
      self.layout.setStretchFactor(self.hlayout, 1)
      self.gTable()
      self.viewTable()

      self.button = QPushButton("&Create dump")
      self.connect(self.button, SIGNAL("clicked()"), self.createDump)
      self.layout.addWidget(self.button)
     	

    def viewTable(self):
      self.vtable = QTableWidget() 
      self.vtable.setColumnCount(20)	
      self.vtable.setRowCount(48)
      self.hlayout.addWidget(self.vtable)
 
    def viewTableUpdate(self, id):
      write = int(str(self.gtable.cellWidget(id, 0).currentText()), 16) 
      t = self.tables.tablesIdWriteMap[id][write]
      l = t.blockList
      for x in xrange(0, len(t.blockList[0])):
        block = t.blockList[0][x]
        c = ((x) % 20) 
        r = ((x) / 20) 
        item = QTableWidgetItem(QString(hex(block))) 
        tipBlock = (id * 960) + x
        item.setToolTip(QString(hex(tipBlock)))
        item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        self.vtable.setItem(r ,c,  item)
     

    def gTable(self):
      self.gtable = QTableWidget()
      self.gtable.setColumnCount(1)	
      self.gtable.setRowCount(len(self.tables.tablesIdWriteMap))
      self.gtable.setHorizontalHeaderItem(0, QTableWidgetItem(QString("version")))
      self.hlayout.addWidget(self.gtable)
      self.sigMapper = QSignalMapper(self)
      for id in self.tables.tablesIdWriteMap:
         wlist = self.tables.tablesIdWriteMap[id]
         cbox = QComboBox(self.gtable)
         self.connect(cbox, SIGNAL("activated(QString)"), self.sigMapper, SLOT("map()"))
         self.sigMapper.setMapping(cbox, id)
         l = [] 
         for write in wlist:
           l.append(write)
         l.sort()
         l.reverse()
         for write in l:
	   cbox.addItem(QString(hex(write))) 
	 self.gtable.setCellWidget(id, 0, cbox)
	 self.gtable.setVerticalHeaderItem(id, QTableWidgetItem(QString(hex(id))))
      self.connect(self.sigMapper, SIGNAL("mapped(int)"),  self.viewTableUpdate) 
      self.gtable.setMaximumWidth(self.gtable.columnWidth(0) + self.gtable.verticalHeader().sectionSize(0) + 30)  
 
    def updateWidget(self):
       pass 
예제 #48
0
class ToolGrid(QFrame):
    """
    A widget containing a grid of actions/buttons.

    Actions can be added using standard :func:`QWidget.addAction(QAction)`
    and :func:`QWidget.insertAction(int, QAction)` methods.

    Parameters
    ----------
    parent : :class:`QWidget`
        Parent widget.
    columns : int
        Number of columns in the grid layout.
    buttonSize : :class:`QSize`, optional
        Size of tool buttons in the grid.
    iconSize : :class:`QSize`, optional
        Size of icons in the buttons.
    toolButtonStyle : :class:`Qt.ToolButtonStyle`
        Tool button style.

    """

    actionTriggered = Signal(QAction)
    actionHovered = Signal(QAction)

    def __init__(self,
                 parent=None,
                 columns=4,
                 buttonSize=None,
                 iconSize=None,
                 toolButtonStyle=Qt.ToolButtonTextUnderIcon):
        QFrame.__init__(self, parent)

        if buttonSize is not None:
            buttonSize = QSize(buttonSize)

        if iconSize is not None:
            iconSize = QSize(iconSize)

        self.__columns = columns
        self.__buttonSize = buttonSize or QSize(50, 50)
        self.__iconSize = iconSize or QSize(26, 26)
        self.__toolButtonStyle = toolButtonStyle

        self.__gridSlots = []

        self.__buttonListener = ToolButtonEventListener(self)
        self.__buttonListener.buttonRightClicked.connect(
            self.__onButtonRightClick)

        self.__buttonListener.buttonEnter.connect(self.__onButtonEnter)

        self.__mapper = QSignalMapper()
        self.__mapper.mapped[QObject].connect(self.__onClicked)

        self.__setupUi()

    def __setupUi(self):
        layout = QGridLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.setSizeConstraint(QGridLayout.SetFixedSize)
        self.setLayout(layout)
        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding)

    def setButtonSize(self, size):
        """
        Set the button size.
        """
        if self.__buttonSize != size:
            self.__buttonSize = size
            for slot in self.__gridSlots:
                slot.button.setFixedSize(size)

    def buttonSize(self):
        """
        Return the button size.
        """
        return QSize(self.__buttonSize)

    def setIconSize(self, size):
        """
        Set the button icon size.
        """
        if self.__iconSize != size:
            self.__iconSize = size
            for slot in self.__gridSlots:
                slot.button.setIconSize(size)

    def iconSize(self):
        """
        Return the icon size
        """
        return QSize(self.__iconSize)

    def setToolButtonStyle(self, style):
        """
        Set the tool button style.
        """
        if self.__toolButtonStyle != style:
            self.__toolButtonStyle = style
            for slot in self.__gridSlots:
                slot.button.setToolButtonStyle(style)

    def toolButtonStyle(self):
        """
        Return the tool button style.
        """
        return self.__toolButtonStyle

    def setColumnCount(self, columns):
        """
        Set the number of button/action columns.
        """
        if self.__columns != columns:
            self.__columns = columns
            self.__relayout()

    def columns(self):
        """
        Return the number of columns in the grid.
        """
        return self.__columns

    def clear(self):
        """
        Clear all actions/buttons.
        """
        for slot in reversed(list(self.__gridSlots)):
            self.removeAction(slot.action)
        self.__gridSlots = []

    def insertAction(self, before, action):
        """
        Insert a new action at the position currently occupied
        by `before` (can also be an index).

        Parameters
        ----------
        before : :class:`QAction` or int
            Position where the `action` should be inserted.
        action : :class:`QAction`
            Action to insert

        """
        if isinstance(before, int):
            actions = list(self.actions())
            if len(actions) == 0 or before >= len(actions):
                # Insert as the first action or the last action.
                return self.addAction(action)

            before = actions[before]

        return QFrame.insertAction(self, before, action)

    def setActions(self, actions):
        """
        Clear the grid and add `actions`.
        """
        self.clear()

        for action in actions:
            self.addAction(action)

    def buttonForAction(self, action):
        """
        Return the :class:`QToolButton` instance button for `action`.
        """
        actions = [slot.action for slot in self.__gridSlots]
        index = actions.index(action)
        return self.__gridSlots[index].button

    def createButtonForAction(self, action):
        """
        Create and return a :class:`QToolButton` for action.
        """
        button = _ToolGridButton(self)
        button.setDefaultAction(action)

        if self.__buttonSize.isValid():
            button.setFixedSize(self.__buttonSize)
        if self.__iconSize.isValid():
            button.setIconSize(self.__iconSize)

        button.setToolButtonStyle(self.__toolButtonStyle)
        button.setProperty("tool-grid-button", QVariant(True))
        return button

    def count(self):
        """
        Return the number of buttons/actions in the grid.
        """
        return len(self.__gridSlots)

    def actionEvent(self, event):
        QFrame.actionEvent(self, event)

        if event.type() == QEvent.ActionAdded:
            # Note: the action is already in the self.actions() list.
            actions = list(self.actions())
            index = actions.index(event.action())
            self.__insertActionButton(index, event.action())

        elif event.type() == QEvent.ActionRemoved:
            self.__removeActionButton(event.action())

    def __insertActionButton(self, index, action):
        """Create a button for the action and add it to the layout
        at index.

        """
        self.__shiftGrid(index, 1)
        button = self.createButtonForAction(action)

        row = index / self.__columns
        column = index % self.__columns

        self.layout().addWidget(button, row, column,
                                Qt.AlignLeft | Qt.AlignTop)

        self.__gridSlots.insert(index,
                                _ToolGridSlot(button, action, row, column))

        self.__mapper.setMapping(button, action)
        button.clicked.connect(self.__mapper.map)
        button.installEventFilter(self.__buttonListener)
        button.installEventFilter(self)

    def __removeActionButton(self, action):
        """Remove the button for the action from the layout and delete it.
        """
        actions = [slot.action for slot in self.__gridSlots]
        index = actions.index(action)
        slot = self.__gridSlots.pop(index)

        slot.button.removeEventFilter(self.__buttonListener)
        slot.button.removeEventFilter(self)
        self.__mapper.removeMappings(slot.button)

        self.layout().removeWidget(slot.button)
        self.__shiftGrid(index + 1, -1)

        slot.button.deleteLater()

    def __shiftGrid(self, start, count=1):
        """Shift all buttons starting at index `start` by `count` cells.
        """
        button_count = self.layout().count()
        direction = 1 if count >= 0 else -1
        if direction == 1:
            start, end = button_count - 1, start - 1
        else:
            start, end = start, button_count

        for index in range(start, end, -direction):
            item = self.layout().itemAtPosition(index / self.__columns,
                                                index % self.__columns)
            if item:
                button = item.widget()
                new_index = index + count
                self.layout().addWidget(button, new_index / self.__columns,
                                        new_index % self.__columns,
                                        Qt.AlignLeft | Qt.AlignTop)

    def __relayout(self):
        """Relayout the buttons.
        """
        for i in reversed(range(self.layout().count())):
            self.layout().takeAt(i)

        self.__gridSlots = [
            _ToolGridSlot(slot.button, slot.action, i / self.__columns,
                          i % self.__columns)
            for i, slot in enumerate(self.__gridSlots)
        ]

        for slot in self.__gridSlots:
            self.layout().addWidget(slot.button, slot.row, slot.column,
                                    Qt.AlignLeft | Qt.AlignTop)

    def __indexOf(self, button):
        """Return the index of button widget.
        """
        buttons = [slot.button for slot in self.__gridSlots]
        return buttons.index(button)

    def __onButtonRightClick(self, button):
        pass

    def __onButtonEnter(self, button):
        action = button.defaultAction()
        self.actionHovered.emit(action)

    def __onClicked(self, action):
        self.actionTriggered.emit(action)

    def paintEvent(self, event):
        return utils.StyledWidget_paintEvent(self, event)

    def eventFilter(self, obj, event):
        etype = event.type()
        if etype == QEvent.KeyPress and obj.hasFocus():
            key = event.key()
            if key in [Qt.Key_Up, Qt.Key_Down, Qt.Key_Left, Qt.Key_Right]:
                if self.__focusMove(obj, key):
                    event.accept()
                    return True

        return QFrame.eventFilter(self, obj, event)

    def __focusMove(self, focus, key):
        assert (focus is self.focusWidget())
        try:
            index = self.__indexOf(focus)
        except IndexError:
            return False

        if key == Qt.Key_Down:
            index += self.__columns
        elif key == Qt.Key_Up:
            index -= self.__columns
        elif key == Qt.Key_Left:
            index -= 1
        elif key == Qt.Key_Right:
            index += 1

        if index >= 0 and index < self.count():
            button = self.__gridSlots[index].button
            button.setFocus(Qt.TabFocusReason)
            return True
        else:
            return False