예제 #1
0
class QuickWatch(QToolBar):
    def __init__(self, parent, distributedObjects):
        QToolBar.__init__(self, "QuickWatch")
        self.config = QuickWatchConfig()
        distributedObjects.configStore.registerConfigSet(self.config)

        self.setObjectName("QuickWatch")
        parent.addToolBar(self)
        self.watchedit = QComboBox()
        self.watchedit.setFixedHeight(28)
        self.watchedit.setInsertPolicy(QComboBox.NoInsert)
        self.watchedit.setEditable(True)
        self.watchedit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.addWidget(self.watchedit)
        self.distributedObjects = distributedObjects
        self.addAction(Icons.watch, "Add to Watch", self.addToWatch)
        self.addAction(Icons.datagraph, "Add to Data Graph", self.addToDG)
        self.watchedit.lineEdit().returnPressed.connect(self.returnPressed)

    def __addCurrentText(self):
        text = self.watchedit.lineEdit().text()
        idx = self.watchedit.findText(text)
        if idx == -1:
            self.watchedit.addItem(text)
        self.watchedit.setEditText("")

    def returnPressed(self):
        if self.config.addTo.value == "Watch View":
            self.addToWatch()
        elif self.config.addTo.value == "Data Graph View":
            self.addToDG()

    def addToWatch(self):
        self.distributedObjects.watchModel.addVar(
            self.watchedit.lineEdit().text())
        self.__addCurrentText()

    def addToDG(self):
        self.distributedObjects.datagraphController.addWatch(
            self.watchedit.lineEdit().text())
        self.__addCurrentText()
예제 #2
0
class QuickWatch(QToolBar):
    def __init__(self, parent, distributedObjects):
        QToolBar.__init__(self, "QuickWatch")
        self.config = QuickWatchConfig()
        distributedObjects.configStore.registerConfigSet(self.config)

        self.setObjectName("QuickWatch")
        parent.addToolBar(self)
        self.watchedit = QComboBox()
        self.watchedit.setFixedHeight(28)
        self.watchedit.setInsertPolicy(QComboBox.NoInsert)
        self.watchedit.setEditable(True)
        self.watchedit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.addWidget(self.watchedit)
        self.distributedObjects = distributedObjects
        self.addAction(Icons.watch, "Add to Watch", self.addToWatch)
        self.addAction(Icons.datagraph, "Add to Data Graph", self.addToDG)
        self.watchedit.lineEdit().returnPressed.connect(self.returnPressed)

    def __addCurrentText(self):
        text = self.watchedit.lineEdit().text()
        idx = self.watchedit.findText(text)
        if idx == -1:
            self.watchedit.addItem(text)
        self.watchedit.setEditText("")

    def returnPressed(self):
        if self.config.addTo.value == "Watch View":
            self.addToWatch()
        elif self.config.addTo.value == "Data Graph View":
            self.addToDG()

    def addToWatch(self):
        self.distributedObjects.watchModel.addVar(self.watchedit.lineEdit().text())
        self.__addCurrentText()

    def addToDG(self):
        self.distributedObjects.datagraphController.addWatch(self.watchedit.lineEdit().text())
        self.__addCurrentText()
예제 #3
0
class FindInFilesDialog(QDialog, object):
    """ find in files dialog implementation """

    inProject = 0
    inDirectory = 1
    inOpenFiles = 2

    def __init__(self, where, what="", dirPath="", filters=[], parent=None):

        QDialog.__init__(self, parent)

        mainWindow = GlobalData().mainWindow
        self.editorsManager = mainWindow.editorsManagerWidget.editorsManager

        self.__cancelRequest = False
        self.__inProgress = False
        self.searchRegexp = None
        self.searchResults = []

        # Avoid pylint complains
        self.findCombo = None
        self.caseCheckBox = None
        self.wordCheckBox = None
        self.regexpCheckBox = None
        self.projectRButton = None
        self.openFilesRButton = None
        self.dirRButton = None
        self.dirEditCombo = None
        self.dirSelectButton = None
        self.filterCombo = None
        self.fileLabel = None
        self.progressBar = None
        self.findButton = None

        self.__createLayout()
        self.setWindowTitle("Find in files")

        # Restore the combo box values
        project = GlobalData().project
        if project.fileName != "":
            self.findFilesWhat = project.findFilesWhat
            self.findFilesDirs = project.findFilesDirs
            self.findFilesMasks = project.findFilesMasks
        else:
            settings = Settings()
            self.findFilesWhat = settings.findFilesWhat
            self.findFilesDirs = settings.findFilesDirs
            self.findFilesMasks = settings.findFilesMasks
        self.findCombo.addItems(self.findFilesWhat)
        self.findCombo.setEditText("")
        self.dirEditCombo.addItems(self.findFilesDirs)
        self.dirEditCombo.setEditText("")
        self.filterCombo.addItems(self.findFilesMasks)
        self.filterCombo.setEditText("")

        if where == self.inProject:
            self.setSearchInProject(what, filters)
        elif where == self.inDirectory:
            self.setSearchInDirectory(what, dirPath, filters)
        else:
            self.setSearchInOpenFiles(what, filters)

        return

    def __createLayout(self):
        """ Creates the dialog layout """

        self.resize(600, 300)
        self.setSizeGripEnabled(True)

        verticalLayout = QVBoxLayout(self)
        gridLayout = QGridLayout()

        # Combo box for the text to search
        findLabel = QLabel(self)
        findLabel.setText("Find text:")
        self.findCombo = QComboBox(self)
        self.__tuneCombo(self.findCombo)
        self.findCombo.lineEdit().setToolTip(
            "Regular expression to search for")
        self.findCombo.editTextChanged.connect(self.__someTextChanged)

        gridLayout.addWidget(findLabel, 0, 0, 1, 1)
        gridLayout.addWidget(self.findCombo, 0, 1, 1, 1)
        verticalLayout.addLayout(gridLayout)

        # Check boxes
        horizontalCBLayout = QHBoxLayout()
        self.caseCheckBox = QCheckBox(self)
        self.caseCheckBox.setText("Match &case")
        horizontalCBLayout.addWidget(self.caseCheckBox)
        self.wordCheckBox = QCheckBox(self)
        self.wordCheckBox.setText("Match whole &word")
        horizontalCBLayout.addWidget(self.wordCheckBox)
        self.regexpCheckBox = QCheckBox(self)
        self.regexpCheckBox.setText("Regular &expression")
        horizontalCBLayout.addWidget(self.regexpCheckBox)

        verticalLayout.addLayout(horizontalCBLayout)

        # Files groupbox
        filesGroupbox = QGroupBox(self)
        filesGroupbox.setTitle("Find in")
        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            filesGroupbox.sizePolicy().hasHeightForWidth())
        filesGroupbox.setSizePolicy(sizePolicy)

        gridLayoutFG = QGridLayout(filesGroupbox)
        self.projectRButton = QRadioButton(filesGroupbox)
        self.projectRButton.setText("&Project")
        gridLayoutFG.addWidget(self.projectRButton, 0, 0)
        self.projectRButton.clicked.connect(self.__projectClicked)

        self.openFilesRButton = QRadioButton(filesGroupbox)
        self.openFilesRButton.setText("&Opened files only")
        gridLayoutFG.addWidget(self.openFilesRButton, 1, 0)
        self.openFilesRButton.clicked.connect(self.__openFilesOnlyClicked)

        self.dirRButton = QRadioButton(filesGroupbox)
        self.dirRButton.setText("&Directory tree")
        gridLayoutFG.addWidget(self.dirRButton, 2, 0)
        self.dirRButton.clicked.connect(self.__dirClicked)

        self.dirEditCombo = QComboBox(filesGroupbox)
        self.__tuneCombo(self.dirEditCombo)
        self.dirEditCombo.lineEdit().setToolTip("Directory to search in")
        gridLayoutFG.addWidget(self.dirEditCombo, 2, 1)
        self.dirEditCombo.editTextChanged.connect(self.__someTextChanged)

        self.dirSelectButton = QPushButton(filesGroupbox)
        self.dirSelectButton.setText("...")
        gridLayoutFG.addWidget(self.dirSelectButton, 2, 2)
        self.dirSelectButton.clicked.connect(self.__selectDirClicked)

        filterLabel = QLabel(filesGroupbox)
        filterLabel.setText("Files filter:")
        gridLayoutFG.addWidget(filterLabel, 3, 0)
        self.filterCombo = QComboBox(filesGroupbox)
        self.__tuneCombo(self.filterCombo)
        self.filterCombo.lineEdit().setToolTip("File names regular expression")
        gridLayoutFG.addWidget(self.filterCombo, 3, 1)
        self.filterCombo.editTextChanged.connect(self.__someTextChanged)

        verticalLayout.addWidget(filesGroupbox)

        # File label
        self.fileLabel = FitPathLabel(self)
        self.fileLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed)
        verticalLayout.addWidget(self.fileLabel)

        # Progress bar
        self.progressBar = QProgressBar(self)
        self.progressBar.setValue(0)
        self.progressBar.setOrientation(Qt.Horizontal)
        verticalLayout.addWidget(self.progressBar)

        # Buttons at the bottom
        buttonBox = QDialogButtonBox(self)
        buttonBox.setOrientation(Qt.Horizontal)
        buttonBox.setStandardButtons(QDialogButtonBox.Cancel)
        self.findButton = buttonBox.addButton("Find",
                                              QDialogButtonBox.AcceptRole)
        self.findButton.setDefault(True)
        self.findButton.clicked.connect(self.__process)
        verticalLayout.addWidget(buttonBox)

        buttonBox.rejected.connect(self.__onClose)
        return

    @staticmethod
    def __tuneCombo(comboBox):
        " Sets the common settings for a combo box "
        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth( \
                            comboBox.sizePolicy().hasHeightForWidth() )
        comboBox.setSizePolicy(sizePolicy)
        comboBox.setEditable(True)
        comboBox.setInsertPolicy(QComboBox.InsertAtTop)
        comboBox.setAutoCompletion(False)
        comboBox.setDuplicatesEnabled(False)
        return

    def __onClose(self):
        " Triggered when the close button is clicked "

        self.__cancelRequest = True
        if not self.__inProgress:
            self.close()
        return

    def setSearchInProject(self, what="", filters=[]):
        " Set search ready for the whole project "

        if GlobalData().project.fileName == "":
            # No project loaded, fallback to opened files
            self.setSearchInOpenFiles(what, filters)
            return

        # Select the project radio button
        self.projectRButton.setEnabled(True)
        self.projectRButton.setChecked(True)
        self.dirEditCombo.setEnabled(False)
        self.dirSelectButton.setEnabled(False)

        openedFiles = self.editorsManager.getTextEditors()
        self.openFilesRButton.setEnabled(len(openedFiles) != 0)

        self.setFilters(filters)

        self.findCombo.setEditText(what)
        self.findCombo.lineEdit().selectAll()
        self.findCombo.setFocus()

        # Check searchability
        self.__testSearchability()
        return

    def setSearchInOpenFiles(self, what="", filters=[]):
        " Sets search ready for the opened files "

        openedFiles = self.editorsManager.getTextEditors()
        if len(openedFiles) == 0:
            # No opened files, fallback to search in dir
            self.setSearchInDirectory(what, "", filters)
            return

        # Select the radio buttons
        self.projectRButton.setEnabled(GlobalData().project.fileName != "")
        self.openFilesRButton.setEnabled(True)
        self.openFilesRButton.setChecked(True)
        self.dirEditCombo.setEnabled(False)
        self.dirSelectButton.setEnabled(False)

        self.setFilters(filters)

        self.findCombo.setEditText(what)
        self.findCombo.lineEdit().selectAll()
        self.findCombo.setFocus()

        # Check searchability
        self.__testSearchability()
        return

    def setSearchInDirectory(self, what="", dirPath="", filters=[]):
        " Sets search ready for the given directory "

        # Select radio buttons
        self.projectRButton.setEnabled(GlobalData().project.fileName != "")
        openedFiles = self.editorsManager.getTextEditors()
        self.openFilesRButton.setEnabled(len(openedFiles) != 0)
        self.dirRButton.setEnabled(True)
        self.dirRButton.setChecked(True)
        self.dirEditCombo.setEnabled(True)
        self.dirSelectButton.setEnabled(True)
        self.dirEditCombo.setEditText(dirPath)

        self.setFilters(filters)

        self.findCombo.setEditText(what)
        self.findCombo.lineEdit().selectAll()
        self.findCombo.setFocus()

        # Check searchability
        self.__testSearchability()
        return

    def setFilters(self, filters):
        " Sets up the filters "

        # Set filters if provided
        if filters:
            self.filterCombo.setEditText(";".join(filters))
        else:
            self.filterCombo.setEditText("")
        return

    def __testSearchability(self):
        " Tests the searchability and sets the Find button status "

        startTime = time.time()
        if self.findCombo.currentText().strip() == "":
            self.findButton.setEnabled(False)
            self.findButton.setToolTip("No text to search")
            return

        if self.dirRButton.isChecked():
            dirname = self.dirEditCombo.currentText().strip()
            if dirname == "":
                self.findButton.setEnabled(False)
                self.findButton.setToolTip("No directory path")
                return
            if not os.path.isdir(dirname):
                self.findButton.setEnabled(False)
                self.findButton.setToolTip("Path is not a directory")
                return

        # Now we need to match file names if there is a filter
        filtersText = self.filterCombo.currentText().strip()
        if filtersText == "":
            self.findButton.setEnabled(True)
            self.findButton.setToolTip("Find in files")
            return

        # Need to check the files match
        try:
            filterRe = re.compile(filtersText, re.IGNORECASE)
        except:
            self.findButton.setEnabled(False)
            self.findButton.setToolTip( "Incorrect files " \
                                        "filter regular expression" )
            return

        matched = False
        tooLong = False
        if self.projectRButton.isChecked():
            # Whole project
            for fname in GlobalData().project.filesList:
                if fname.endswith(sep):
                    continue
                matched = filterRe.match(fname)
                if matched:
                    break
                # Check the time, it might took too long
                if time.time() - startTime > 0.1:
                    tooLong = True
                    break

        elif self.openFilesRButton.isChecked():
            # Opened files
            openedFiles = self.editorsManager.getTextEditors()
            for record in openedFiles:
                matched = filterRe.match(record[1])
                if matched:
                    break
                # Check the time, it might took too long
                if time.time() - startTime > 0.1:
                    tooLong = True
                    break

        else:
            # Search in the dir
            if not dirname.endswith(sep):
                dirname += sep
            matched, tooLong = self.__matchInDir(dirname, filterRe, startTime)

        if matched:
            self.findButton.setEnabled(True)
            self.findButton.setToolTip("Find in files")
        else:
            if tooLong:
                self.findButton.setEnabled(True)
                self.findButton.setToolTip("Find in files")
            else:
                self.findButton.setEnabled(False)
                self.findButton.setToolTip("No files matched to search in")
        return

    @staticmethod
    def __matchInDir(path, filterRe, startTime):
        " Provides the 'match' and 'too long' statuses "
        matched = False
        tooLong = False
        for item in os.listdir(path):
            if time.time() - startTime > 0.1:
                tooLong = True
                return matched, tooLong
            if os.path.isdir(path + item):
                dname = path + item + sep
                matched, tooLong = FindInFilesDialog.__matchInDir(
                    dname, filterRe, startTime)
                if matched or tooLong:
                    return matched, tooLong
                continue
            if filterRe.match(path + item):
                matched = True
                return matched, tooLong
        return matched, tooLong

    def __projectClicked(self):
        " project radio button clicked "
        self.dirEditCombo.setEnabled(False)
        self.dirSelectButton.setEnabled(False)
        self.__testSearchability()
        return

    def __openFilesOnlyClicked(self):
        " open files only radio button clicked "
        self.dirEditCombo.setEnabled(False)
        self.dirSelectButton.setEnabled(False)
        self.__testSearchability()
        return

    def __dirClicked(self):
        " dir radio button clicked "
        self.dirEditCombo.setEnabled(True)
        self.dirSelectButton.setEnabled(True)
        self.dirEditCombo.setFocus()
        self.__testSearchability()
        return

    def __someTextChanged(self, text):
        " Text to search, filter or dir name has been changed "
        self.__testSearchability()
        return

    def __selectDirClicked(self):
        " The user selects a directory "
        dirName = QFileDialog.getExistingDirectory(
            self, "Select directory to search in",
            self.dirEditCombo.currentText(),
            QFileDialog.Options(QFileDialog.ShowDirsOnly))

        if dirName:
            self.dirEditCombo.setEditText(os.path.normpath(dirName))
        self.__testSearchability()
        return

    def __projectFiles(self, filterRe):
        " Project files list respecting the mask "
        mainWindow = GlobalData().mainWindow
        files = []
        for fname in GlobalData().project.filesList:
            if fname.endswith(sep):
                continue
            if filterRe is None or filterRe.match(fname):
                widget = mainWindow.getWidgetForFileName(fname)
                if widget is None:
                    # Do not check for broken symlinks
                    if isFileSearchable(fname, False):
                        files.append(ItemToSearchIn(fname, ""))
                else:
                    if widget.getType() in \
                                [ MainWindowTabWidgetBase.PlainTextEditor ]:
                        files.append(ItemToSearchIn(fname, widget.getUUID()))
            QApplication.processEvents()
            if self.__cancelRequest:
                raise Exception("Cancel request")
        return files

    def __openedFiles(self, filterRe):
        " Currently opened editor buffers "

        files = []
        openedFiles = self.editorsManager.getTextEditors()
        for record in openedFiles:
            uuid = record[0]
            fname = record[1]
            if filterRe is None or filterRe.match(fname):
                files.append(ItemToSearchIn(fname, uuid))
            QApplication.processEvents()
            if self.__cancelRequest:
                raise Exception("Cancel request")
        return files

    def __dirFiles(self, path, filterRe, files):
        " Files recursively for the dir "
        for item in os.listdir(path):
            QApplication.processEvents()
            if self.__cancelRequest:
                raise Exception("Cancel request")
            if os.path.isdir(path + item):
                if item in [".svn", ".cvs"]:
                    # It does not make sense to search in revision control dirs
                    continue
                anotherDir, isLoop = resolveLink(path + item)
                if not isLoop:
                    self.__dirFiles(anotherDir + sep, filterRe, files)
                continue
            if not os.path.isfile(path + item):
                continue
            realItem, isLoop = resolveLink(path + item)
            if isLoop:
                continue
            if filterRe is None or filterRe.match(realItem):
                found = False
                for itm in files:
                    if itm.fileName == realItem:
                        found = True
                        break
                if not found:
                    mainWindow = GlobalData().mainWindow
                    widget = mainWindow.getWidgetForFileName(realItem)
                    if widget is None:
                        if isFileSearchable(realItem):
                            files.append(ItemToSearchIn(realItem, ""))
                    else:
                        if widget.getType() in \
                                    [ MainWindowTabWidgetBase.PlainTextEditor ]:
                            files.append(
                                ItemToSearchIn(realItem, widget.getUUID()))
        return

    def __buildFilesList(self):
        " Builds the list of files to search in "
        filtersText = self.filterCombo.currentText().strip()
        if filtersText != "":
            filterRe = re.compile(filtersText, re.IGNORECASE)
        else:
            filterRe = None

        if self.projectRButton.isChecked():
            return self.__projectFiles(filterRe)

        if self.openFilesRButton.isChecked():
            return self.__openedFiles(filterRe)

        dirname = os.path.realpath(self.dirEditCombo.currentText().strip())
        files = []
        self.__dirFiles(dirname + sep, filterRe, files)
        return files

    def __process(self):
        " Search process "

        # Add entries to the combo box if required
        regexpText = self.findCombo.currentText()
        if regexpText in self.findFilesWhat:
            self.findFilesWhat.remove(regexpText)
        self.findFilesWhat.insert(0, regexpText)
        if len(self.findFilesWhat) > 32:
            self.findFilesWhat = self.findFilesWhat[:32]
        self.findCombo.clear()
        self.findCombo.addItems(self.findFilesWhat)

        filtersText = self.filterCombo.currentText().strip()
        if filtersText in self.findFilesMasks:
            self.findFilesMasks.remove(filtersText)
        self.findFilesMasks.insert(0, filtersText)
        if len(self.findFilesMasks) > 32:
            self.findFilesMasks = self.findFilesMasks[:32]
        self.filterCombo.clear()
        self.filterCombo.addItems(self.findFilesMasks)

        if self.dirRButton.isChecked():
            dirText = self.dirEditCombo.currentText().strip()
            if dirText in self.findFilesDirs:
                self.findFilesDirs.remove(dirText)
            self.findFilesDirs.insert(0, dirText)
            if len(self.findFilesDirs) > 32:
                self.findFilesDirs = self.findFilesDirs[:32]
            self.dirEditCombo.clear()
            self.dirEditCombo.addItems(self.findFilesDirs)

        # Save the combo values for further usage
        if GlobalData().project.fileName != "":
            GlobalData().project.setFindInFilesHistory(self.findFilesWhat,
                                                       self.findFilesDirs,
                                                       self.findFilesMasks)
        else:
            Settings().findFilesWhat = self.findFilesWhat
            Settings().findFilesDirs = self.findFilesDirs
            Settings().findFilesMasks = self.findFilesMasks

        self.__inProgress = True
        numberOfMatches = 0
        self.searchResults = []
        self.searchRegexp = None

        # Form the regexp to search
        if not self.regexpCheckBox.isChecked():
            regexpText = re.escape(regexpText)
        if self.wordCheckBox.isChecked():
            regexpText = "\\b%s\\b" % regexpText
        flags = re.UNICODE | re.LOCALE
        if not self.caseCheckBox.isChecked():
            flags |= re.IGNORECASE

        try:
            self.searchRegexp = re.compile(regexpText, flags)
        except:
            logging.error("Invalid search expression")
            self.close()
            return

        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        self.fileLabel.setPath('Building list of files to search in...')
        QApplication.processEvents()
        try:
            files = self.__buildFilesList()
        except Exception, exc:
            if "Cancel request" in str(exc):
                QApplication.restoreOverrideCursor()
                self.close()
                return
            else:
                QApplication.restoreOverrideCursor()
                logging.error(str(exc))
                self.close()
                return
        QApplication.restoreOverrideCursor()
        QApplication.processEvents()

        if len(files) == 0:
            self.fileLabel.setPath('No files to search in')
            return

        self.progressBar.setRange(0, len(files))

        index = 1
        for item in files:

            if self.__cancelRequest:
                self.__inProgress = False
                self.close()
                return

            self.fileLabel.setPath( 'Matches: ' + str( numberOfMatches ) + \
                                    ' Processing: ' + item.fileName )

            item.search(self.searchRegexp)
            found = len(item.matches)
            if found > 0:
                numberOfMatches += found
                self.searchResults.append(item)

            self.progressBar.setValue(index)
            index += 1

            QApplication.processEvents()

        if numberOfMatches == 0:
            if len(files) == 1:
                self.fileLabel.setPath("No matches in 1 file.")
            else:
                self.fileLabel.setPath( "No matches in " + \
                                        str( len( files ) ) + " files." )
            self.__inProgress = False
        else:
            self.close()
        return
예제 #4
0
파일: LDSGUI.py 프로젝트: josephramsay/LDS
class LDSControls(QFrame):
        
    STATIC_IMG = ('error_static.png','linz_static.png','busy_static.png','clean_static.png')
    ANIM_IMG   = ('error.gif','linz.gif','layer.gif','clean.gif')
    
    IMG_SPEED  = 100
    IMG_WIDTH  = 64
    IMG_HEIGHT = 64
    
    MAX_WD = 450
    
    GD_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../../bin/gdal/gdal-data'))
    STATUS = LU.enum('ERROR','IDLE','BUSY','CLEAN')
    
    def __init__(self,parent):
        super(LDSControls, self).__init__()
        self.parent = parent
        self.initConf()
        self.initEPSG()
        self.initUI()
        
    def initConf(self):
        '''Read files in conf dir ending in conf'''
        self.cflist = ConfigInitialiser.getConfFiles()
        #self.imgset = self.STATIC_IMG if ConfigWrapper().readDSProperty('Misc','indicator')=='static' else self.ANIM_IMG
        #self.imgset = self.STATIC_IMG if self.parent.confconn.tp.src.confwrap.readDSProperty('Misc','indicator')=='static' else self.ANIM_IMG
        sep = self.parent.confconn.reg.openEndPoint(self.parent.confconn.SRCNAME,self.parent.confconn.uconf)
        self.imgset = self.STATIC_IMG if sep.confwrap.readDSProperty('Misc','indicator')=='static' else self.ANIM_IMG
        self.parent.confconn.reg.closeEndPoint(self.parent.confconn.SRCNAME)
        
    def initEPSG(self):
        '''Read GDAL EPSG files, splitting by NZ(RSR) and RestOfTheWorld'''

        gcsf = gdal.FindFile('gdal','gcs.csv') 
        if not gcsf:
            gcsf = os.path.join(self.GD_PATH,'gcs.csv')
        pcsf = gdal.FindFile('gdal','pcs.csv') 
        if not pcsf: 
            pcsf = os.path.join(self.GD_PATH,'pcs.csv')
        gcs = ConfigInitialiser.readCSV(gcsf)
        pcs = ConfigInitialiser.readCSV(pcsf)

        self.nzlsr = [(e[0],e[0]+' - '+e[3]) for e in gcs if 'NZGD'     in e[1] or  'RSRGD'     in e[1]] \
                   + [(e[0],e[0]+' - '+e[1]) for e in pcs if 'NZGD'     in e[1] or  'RSRGD'     in e[1]]
        self.rowsr = [(e[0],e[0]+' - '+e[3]) for e in gcs if 'NZGD' not in e[1] and 'RSRGD' not in e[1]] \
                   + [(e[0],e[0]+' - '+e[1]) for e in pcs if 'NZGD' not in e[1] and 'RSRGD' not in e[1]]
                   
                   
    def initUI(self):
        
        # 0      1          2       3       4       5      6    7    8
        #'destname','lgselect','layer','uconf','group','epsg','fd','td','int'
        
        #self.rdest,rlgselect,self.rlayer,ruconf,self.rgroup,repsg,rfd,rtd,rint = readlist 
        
        QToolTip.setFont(QFont('SansSerif', 10))
        
        #labels
        destLabel = QLabel('Destination')
        lgLabel = QLabel('Group/Layer')
        epsgLabel = QLabel('EPSG')
        fromDateLabel = QLabel('From Date')
        toDateLabel = QLabel('To Date')
        confLabel = QLabel('User Config')
        
        self.view = QLabel() 
        self.view.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.view.setAlignment(Qt.AlignCenter)

        self.confcombo = QComboBox(self)
        self.confcombo.setToolTip('Enter your user config name (file) here')
        self.confcombo.addItems(self.cflist)
        self.confcombo.setEditable(False)
        #self.confcombo.currentIndexChanged.connect(self.doLGEditUpdate)
        
        #combos
        self.lgcombo = QComboBox(self)
        self.lgcombo.setMaximumWidth(self.MAX_WD)
        self.lgcombo.setDuplicatesEnabled(False)
        #self.lgcombo.setInsertPolicy(QComboBox.InsertAlphabetically)#?doesnt seem to work
        self.lgcombo.setToolTip('Select either Layer or Group entry')
        self.lgcombo.setEditable(False)
        self.sepindex = None
        #self.updateLGValues()
        
        self.epsgcombo = QComboBox(self)
        self.epsgcombo.setMaximumWidth(self.MAX_WD)
        self.epsgcombo.setToolTip('Setting an EPSG number here determines the output SR of the layer')  
        self.epsgcombo.addItems([i[1] for i in self.nzlsr])
        self.epsgcombo.insertSeparator(len(self.nzlsr))
        self.epsgcombo.addItems([i[1] for i in self.rowsr])
        self.epsgcombo.setEditable(True)
        self.epsgcombo.setEnabled(False)
        
        self.destlist = self.getConfiguredDestinations()
        self.destcombo = QComboBox(self)
        self.destcombo.setToolTip('Choose the desired output type')   
        self.destcombo.setEditable(False)
        self.destcombo.addItems(self.destlist)

        #date selection
        self.fromdateedit = QDateEdit()
        self.fromdateedit.setCalendarPopup(True)
        self.fromdateedit.setEnabled(False)
        
        self.todateedit = QDateEdit()
        self.todateedit.setCalendarPopup(True)
        self.todateedit.setEnabled(False)
        
        #check boxes
        self.epsgenable = QCheckBox()
        self.epsgenable.setCheckState(False)
        self.epsgenable.clicked.connect(self.doEPSGEnable)       
        
        self.fromdateenable = QCheckBox()
        self.fromdateenable.setCheckState(False)
        self.fromdateenable.clicked.connect(self.doFromDateEnable)
        
        self.todateenable = QCheckBox()
        self.todateenable.setCheckState(False) 
        self.todateenable.clicked.connect(self.doToDateEnable)
        
        self.progressbar = QProgressBar()
        self.progressbar.setRange(0,100)
        self.progressbar.setVisible(True)
        self.progressbar.setMinimumWidth(self.MAX_WD)
        
        
        #buttons        
        self.initbutton = QPushButton("waiting")
        self.initbutton.setToolTip('Initialise the Layer Configuration')
        self.initbutton.clicked.connect(self.doInitClickAction)
        
        self.cleanbutton = QPushButton("Clean")
        self.cleanbutton.setToolTip('Clean the selected layer/group from local storage')
        self.cleanbutton.clicked.connect(self.doCleanClickAction)
        
        self.replicatebutton = QPushButton("Replicate")
        self.replicatebutton.setToolTip('Execute selected replication')
        self.replicatebutton.clicked.connect(self.doReplicateClickAction)
        
        self.cancelbutton = QPushButton("Close")
        self.cancelbutton.setToolTip('Close the LDS Replicate application')       
        self.cancelbutton.clicked.connect(self.parent.close)


        #set dialog values using GPR
        self.updateGUIValues(self.parent.gvs)
        
        #set onchange here otherwise we get circular initialisation
        self.destcombo.currentIndexChanged.connect(self.doDestChanged)
        self.confcombo.currentIndexChanged.connect(self.doConfChanged)
        self.lgcombo.currentIndexChanged.connect(self.doLGComboChanged)

        self.setStatus(self.STATUS.IDLE)
        
        #grid
        grid = QGridLayout()
        grid.setSpacing(10)
        
        
        #placement section ------------------------------------
        #---------+---------+--------+---------+--------
        # dest LB |         | dest DD
        # grp LB  |         | grp DD
        # conf LB |         | conf DD
        # epsg L  | epsg CB | epsg DD
        # f dt L  | f dt CB | f dt DD
        # t td L  | t td CB | t td DD
        # icon    |       <- progress ->
        # layer B | <- . -> |repl B  | clean B | close B 
        #---------+---------+--------+---------+--------

        grid.addWidget(destLabel, 1, 0)
        grid.addWidget(self.destcombo, 1, 2)

        #grid.addWidget(layerLabel, 2, 0)
        grid.addWidget(lgLabel, 2, 0)
        grid.addWidget(self.lgcombo, 2, 2)
        
        grid.addWidget(confLabel, 3, 0)
        grid.addWidget(self.confcombo, 3, 2)
        
        #grid.addWidget(groupLabel, 4, 0)
        #grid.addWidget(self.groupEdit, 4, 2)
        
        grid.addWidget(epsgLabel, 5, 0)
        grid.addWidget(self.epsgenable, 5, 1)
        grid.addWidget(self.epsgcombo, 5, 2)

        grid.addWidget(fromDateLabel, 6, 0)
        grid.addWidget(self.fromdateenable, 6, 1)
        grid.addWidget(self.fromdateedit, 6, 2)
        
        grid.addWidget(toDateLabel, 7, 0)
        grid.addWidget(self.todateenable, 7, 1)
        grid.addWidget(self.todateedit, 7, 2)
        
        hbox3 = QHBoxLayout()
        hbox3.addWidget(self.view) 
        hbox3.addStretch(1)
        hbox3.addWidget(self.progressbar)

        #hbox3.addLayout(vbox2)
        #hbox3.addLayout(vbox3)
        
        hbox4 = QHBoxLayout()
        hbox4.addWidget(self.initbutton)
        hbox4.addStretch(1)
        hbox4.addWidget(self.replicatebutton)
        hbox4.addWidget(self.cleanbutton)
        hbox4.addWidget(self.cancelbutton)
        

        vbox = QVBoxLayout()
        #vbox.addStretch(1)
        vbox.addLayout(grid)
        vbox.addLayout(hbox3)
        vbox.addLayout(hbox4)
        
        self.setLayout(vbox)  
       
    #def setProgress(self,pct):
    #    self.progressbar.setValue(pct)
        
    def setStatus(self,status,message='',tooltip=None):
        '''Sets indicator icon and statusbar message'''
        self.parent.statusbar.showMessage(message)
        self.parent.statusbar.setToolTip(tooltip if tooltip else '')

        #progress
        loc = os.path.abspath(os.path.join(IMG_LOC,self.imgset[status]))
        #loc = os.path.abspath(os.path.join(os.path.dirname(__file__),self.parent.IMG_LOC,self.imgset[status]))
        self.progressbar.setVisible(status in (self.STATUS.BUSY, self.STATUS.CLEAN))
        
        #icon
        anim = QMovie(loc, QByteArray(), self)
        anim.setScaledSize(QSize(self.IMG_WIDTH,self.IMG_HEIGHT))
        anim.setCacheMode(QMovie.CacheAll)
        anim.setSpeed(self.IMG_SPEED)
        self.view.clear()
        self.view.setMovie(anim)
        anim.start()

        self.view.repaint()
        QApplication.processEvents(QEventLoop.AllEvents)

    def mainWindowEnable(self,enable=True):
        cons = (self.lgcombo, self.confcombo, self.destcombo, 
                self.initbutton, self.replicatebutton, self.cleanbutton, self.cancelbutton,
                self.epsgenable,self.fromdateenable,self.todateenable,
                self.parent.menubar)
        for c in cons:
            c.setEnabled(enable)
            
        if enable:
            self.epsgcombo.setEnabled(self.epsgenable.checkState())
            self.fromdateedit.setEnabled(self.fromdateenable.checkState())
            self.todateedit.setEnabled(self.todateenable.checkState())
        else:
            self.epsgcombo.setEnabled(False)
            self.fromdateedit.setEnabled(False)
            self.todateedit.setEnabled(False)
   
        QApplication.restoreOverrideCursor() if enable else QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) 

    def refreshLGCombo(self):
        '''Re index LG combobox since a refreshLG call (new dest?) will usually mean new groups'''
        self.lgcombo.clear()
        self.lgcombo.addItems([i[2] for i in self.parent.confconn.lglist])
        #NOTE the separator consumes an index, if not clearing the combobox selectively remove the old sepindex (assumes g preceeds l)
        #if self.sepindex:
        #    self.lgcombo.removeItem(self.sepindex)
        self.sepindex = [i[0] for i in self.parent.confconn.lglist].count(LORG.GROUP)
        self.lgcombo.insertSeparator(self.sepindex)
        
    def updateLGValues(self,uconf,lgval,dest):
        '''Sets the values displayed in the Layer/Group combo'''
        #because we cant seem to sort combobox entries and want groups at the top, clear and re-add
        #TRACE#        
        #pdb.set_trace()
        sf = None
        try:
            self.parent.confconn.initConnections(uconf,lgval,dest)
        except Exception as e:
            sf=1
            ldslog.error('Error Updating UC Values. '+str(e))
            
        if sf:
            self.setStatus(self.STATUS.ERROR,'Error Updating UC Values', str(e))
        else:
            self.setStatus(self.STATUS.IDLE)
            
        self.refreshLGCombo()
        
    def centre(self):
        
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())
        
    
    def gprParameters(self,rdest):
        '''Zip default and GPR values'''
        return [x if LU.assessNone(x) else y for x,y in zip(self.parent.gpr.readsec(rdest),self.parent.DEF_RVALS[1:])]
    
    def getLCE(self,ln):
        '''Read layer parameters'''
        dep = self.parent.confconn.reg.openEndPoint(self.parent.confconn.destname,self.parent.confconn.uconf)
        #sep = self.parent.confconn.reg.openEndPoint('WFS',self.parent.confconn.uconf)
        self.parent.confconn.reg.setupLayerConfig(self.parent.confconn.tp,None,dep,initlc=False)
        lce = dep.getLayerConf().readLayerParameters(ln)
        #self.parent.confconn.reg.closeEndPoint('WFS')
        self.parent.confconn.reg.closeEndPoint(self.parent.confconn.destname)
        sep,dep = None,None
        return lce
    
    
    def doDestChanged(self):
        '''Read the destname parameter and fill dialog with matching GPR values'''
        rdest = str(self.destlist[self.destcombo.currentIndex()])
        rvals = self.gprParameters(rdest)
        self.updateGUIValues([rdest]+rvals)    
        
        
    def doConfChanged(self):
        '''Read the user conf parameter and fill dialog with matching GPR values'''
        rdest = str(self.destlist[self.destcombo.currentIndex()])
        rlg,_,rep,rfd,rtd = self.gprParameters(rdest)
        ruc = str(self.cflist[self.confcombo.currentIndex()])
        self.updateGUIValues((rdest,rlg,ruc,rep,rfd,rtd))
        
        
    def doLGComboChanged(self):
        '''Read the layer/group value and change epsg to layer or gpr match'''
        #get a matching LG entry and test whether its a layer or group
        #lgi = self.parent.confconn.getLayerGroupIndex(self.lgcombo.currentText().toUtf8().data())
        lgi = self.parent.confconn.getLayerGroupIndex(LQ.readWidgetText(self.lgcombo.currentText()))
        #lgi can be none if we init a new group, in which case we use the GPR value
        if lgi:
            lge = self.parent.confconn.lglist[lgi]
            lce = self.getLCE(lge[1]) if lge[0]==LORG.LAYER else None
        else:
            lce = None
        
        #look for filled layer conf epsg OR use prefs stored in gpr
        if lce and LU.assessNone(lce.epsg):
            epsgval = lce.epsg
        else:
            rdest = str(self.destlist[self.destcombo.currentIndex()])
            _,_,epsgval,_,_ = self.gprParameters(rdest)
        epsgindex = [i[0] for i in self.nzlsr+[(0,0)]+self.rowsr].index(epsgval)
        if self.epsgcombo.currentIndex() != epsgindex:
            self.epsgcombo.setCurrentIndex(int(epsgindex))

        
    def updateGUIValues(self,readlist):
        '''Fill dialog values from provided list'''
        #TODO. Remove circular references when setCurrentIndex() triggers do###Changed()
        #Read user input
        rdest,self.rlgval,ruconf,repsg,rfd,rtd = readlist
        
        #--------------------------------------------------------------------
        
        #Destination Menu
        selecteddest = LU.standardiseDriverNames(rdest)
        if selecteddest not in self.destlist:
            self.destlist = self.getConfiguredDestinations()
            self.destcombo.addItem(selecteddest)
        destindex = self.destlist.index(selecteddest) if selecteddest else 0
        
        if self.destcombo.currentIndex() != destindex:
            self.destcombo.setCurrentIndex(destindex)
        
        #InitButton
        self.initbutton.setText('Layer Select')
        
        #Config File
        confindex = 0
        if LU.assessNone(ruconf):
            ruconf = ruconf.split('.')[0]
            if ruconf not in self.cflist:
                self.cflist += [ruconf,]
                self.confcombo.addItem(ruconf)
            confindex = self.cflist.index(ruconf)
            
        if self.confcombo.currentIndex() != confindex:
            self.confcombo.setCurrentIndex(confindex)
        #self.confEdit.setText(ruconf if LU.assessNone(ruconf) else '')
        
        #Layer/Group Selection
        self.updateLGValues(ruconf,self.rlgval,rdest)
        lgindex = None
        if LU.assessNone(self.rlgval):
            #index of list value
            lgindex = self.parent.confconn.getLayerGroupIndex(self.rlgval,col=1)
            
        if LU.assessNone(lgindex):
            #advance by 1 for sep
            lgindex += 1 if lgindex>self.sepindex else 0 
        else:
            #using the separator index sets the combo to blank
            lgindex = self.sepindex
        if self.lgcombo.currentIndex() != lgindex:
            self.lgcombo.setCurrentIndex(lgindex)
        #self.doLGEditUpdate()
        
        #EPSG
        #                                user > layerconf
        #useepsg = LU.precedence(repsg, lce.epsg if lce else None, None)
        epsgindex = [i[0] for i in self.nzlsr+[(None,None)]+self.rowsr].index(repsg)
        if self.epsgcombo.currentIndex() != epsgindex:
            self.epsgcombo.setCurrentIndex(epsgindex)
            
        #epsgedit = self.epsgcombo.lineEdit()
        #epsgedit.setText([e[1] for e in self.nzlsr+self.rowsr if e[0]==repsg][0])
        
        #epsgedit.setText([e for e in self.nzlsr+self.rowsr if re.match('^\s*(\d+).*',e).group(1)==repsg][0])
        
        #To/From Dates
        if LU.assessNone(rfd):
            self.fromdateedit.setDate(QDate(int(rfd[0:4]),int(rfd[5:7]),int(rfd[8:10])))
        else:
            early = DataStore.EARLIEST_INIT_DATE
            self.fromdateedit.setDate(QDate(int(early[0:4]),int(early[5:7]),int(early[8:10])))
            
        if LU.assessNone(rtd):
            self.todateedit.setDate(QDate(int(rtd[0:4]),int(rtd[5:7]),int(rtd[8:10]))) 
        else:
            today = DataStore.getCurrent()
            self.todateedit.setDate(QDate(int(today[0:4]),int(today[5:7]),int(today[8:10])))
            
        #Internal/External CheckBox
#        if LU.assessNone(rint):
#            self.internalTrigger.setChecked(rint.lower()==DataStore.CONF_INT)
#        else:
#            self.internalTrigger.setChecked(DataStore.DEFAULT_CONF==DataStore.CONF_INT)
        
        
    def getConfiguredDestinations(self):
        defml = ['',]+DataStore.DRIVER_NAMES.values()
        return [d for d in self.parent.gpr.getDestinations() if d in defml]
        
    def doEPSGEnable(self):
        self.epsgcombo.setEnabled(self.epsgenable.isChecked())
        
    def doFromDateEnable(self):
        self.fromdateedit.setEnabled(self.fromdateenable.isChecked())
          
    def doToDateEnable(self):
        self.todateedit.setEnabled(self.todateenable.isChecked())  
          
    def readParameters(self):
        '''Read values out of dialogs'''
        destination = LU.assessNone(str(self.destlist[self.destcombo.currentIndex()]))
        #lgindex = self.parent.confconn.getLayerGroupIndex(self.lgcombo.currentText().toUtf8().data())
        lgindex = self.parent.confconn.getLayerGroupIndex(LQ.readWidgetText(self.lgcombo.currentText()))
        #NB need to test for None explicitly since zero is a valid index
        lgval = self.parent.confconn.lglist[lgindex][1] if LU.assessNone(lgindex) else None       
        #uconf = LU.standardiseUserConfigName(str(self.confcombo.lineEdit().text()))
        #uconf = str(self.confcombo.lineEdit().text())
        uconf = str(self.cflist[self.confcombo.currentIndex()])
        ee = self.epsgenable.isChecked()
        epsg = None if ee is False else re.match('^\s*(\d+).*',str(self.epsgcombo.lineEdit().text())).group(1)
        fe = self.fromdateenable.isChecked()
        te = self.todateenable.isChecked()
        fd = None if fe is False else str(self.fromdateedit.date().toString('yyyy-MM-dd'))
        td = None if te is False else str(self.todateedit.date().toString('yyyy-MM-dd'))
        
        return destination,lgval,uconf,epsg,fe,te,fd,td
    
    def doInitClickAction(self):
        '''Initialise the LC on LC-button-click, action'''
        try:
            try:
                self.setStatus(self.STATUS.BUSY,'Opening Layer-Config Editor')  
                self.progressbar.setValue(0)
                self.parent.runLayerConfigAction()
            finally:
                self.setStatus(self.STATUS.IDLE,'Ready')
        except Exception as e:
            self.setStatus(self.STATUS.ERROR,'Error in Layer-Config',str(sys.exc_info()))#e))
        
    def doCleanClickAction(self):
        '''Set clean anim and run clean'''
        #lgo = self.lgcombo.currentText().toUtf8().data()
        lgo = LQ.readWidgetText(self.lgcombo.currentText())
        
        try:
            self.setStatus(self.STATUS.CLEAN,'Running Clean '+lgo)
            self.progressbar.setValue(0)
            self.runReplicationScript(True)
        except Exception as e:
            self.setStatus(self.STATUS.ERROR,'Failed Clean of '+lgo,str(sys.exc_info()))#e))
        
    def doReplicateClickAction(self):
        '''Set busy anim and run repl'''
        lgo = self.lgcombo.currentText()#.toUtf8().data()#only used for error messages
        try:
            self.setStatus(self.STATUS.BUSY,'Running Replicate '+lgo)
            self.progressbar.setValue(0)
            self.runReplicationScript(False)
        except Exception as e:
            self.setStatus(self.STATUS.ERROR,'Failed Replication of '+lgo,str(sys.exc_info()))#e))

    def runReplicationScript(self,clean=False):
        '''Run the layer/group repliction script'''
        destination,lgval,uconf,epsg,fe,te,fd,td = self.readParameters()
        uconf_path = LU.standardiseUserConfigName(uconf)
        destination_path = LU.standardiseLayerConfigName(destination)
        destination_driver = LU.standardiseDriverNames(destination)

        if not os.path.exists(uconf_path):
            self.userConfMessage(uconf_path)
            return
        elif not MainFileReader(uconf_path).hasSection(destination_driver):
            self.userConfMessage(uconf_path,destination_driver)
            return
        #-----------------------------------------------------
        #'destname','layer','uconf','group','epsg','fd','td','int'
     
        self.parent.gpr.write((destination_driver,lgval,uconf,epsg,fd,td))        
        ldslog.info(u'dest={0}, lg={1}, conf={2}, epsg={3}'.format(destination_driver,lgval,uconf,epsg))
        ldslog.info('fd={0}, td={1}, fe={2}, te={3}'.format(fd,td,fe,te))
        lgindex = self.parent.confconn.getLayerGroupIndex(lgval,col=1)
        #lorg = self.parent.confconn.lglist[lgindex][0]
        #----------don't need lorg in TP anymore but it is useful for sorting/counting groups
        #self.parent.confconn.tp.setLayerOrGroup(lorg)
        self.parent.confconn.tp.setLayerGroupValue(lgval)
        if self.fromdateenable.isChecked(): self.parent.confconn.tp.setFromDate(fd)
        if self.todateenable.isChecked(): self.parent.confconn.tp.setToDate(td)
        self.parent.confconn.tp.setUserConf(uconf)
        if self.epsgenable: self.parent.confconn.tp.setEPSG(epsg)
        
        #because clean state persists in TP
        if clean:
            self.parent.confconn.tp.setCleanConfig()
        else:
            self.parent.confconn.tp.clearCleanConfig()
        #(re)initialise the data source since uconf may have changed
        #>>#self.parent.confconn.tp.src = self.parent.confconn.initSourceWrapper()
        #--------------------------
        ###ep = self.parent.confconn.reg.openEndPoint(self.parent.confconn.destname,self.parent.confconn.uconf)
        
        ###self.parent.confconn.reg.closeEndPoint(self.parent.confconn.destname)
        ###ep = None
        #Open ProcessRunner and run with TP(proc)/self(gui) instances
        #HACK temp add of dest_drv to PR call
        try:
            #TODO. Test for valid LC first
            self.tpr = ProcessRunner(self,destination_driver)
        except Exception as e:
            ldslog.error('Cannot create ProcessRunner {}. NB Possible missing Layer Config {}'.format(str(e),destination_path))
            self.layerConfMessage(destination_path)
            return
        #If PR has been successfully created we must vave a valid dst    
        self.tpr.start()
        
    def quitProcessRunner(self):
        self.tpr.join()
        self.tpr.quit()
        self.trp = None

        
    def userConfMessage(self,uconf,secname=None):
        ucans = QMessageBox.warning(self, 'User Config Missing/Incomplete', 
                                'Specified User-Config file, '+str(uconf)+' does not exist' if secname is None else 'User-Config file does not contain '+str(secname)+' section', 
                                'Back','Initialise User Config')
        if not ucans:
            #Retry
            ldslog.warn('Retry specifying UC')
            #self.confcombo.setCurrentIndex(0)
            return
        #Init
        ldslog.warn('Reset User Config Wizard')
        self.parent.runWizardAction()


    def layerConfMessage(self,dest):
        lcans = QMessageBox.warning(self, 'Layer Config Missing', 
                                'Required Layer-Config file, '+str(dest)+' does not exist', 
                                'Back','Run Layer Select')
        if not lcans:
            #Retry
            ldslog.warn('Retry specifying LC')
            #self.destcombo.setCurrentIndex(0)
            return
        #Init
        ldslog.warn('Reset Layer Config')
        self.doInitClickAction()
예제 #5
0
class GearEntriesFrame(QFrame):
  read_block = pyqtSignal(int, int)  # start_addr, num_bytes
  poke_block = pyqtSignal(int, QByteArray, bool)  # start_addr, raw_bytes, is_ascii
  log = pyqtSignal(str, str)  # msg, color

  MISSING_SKILL_NAME = '[NOT IN DB]'

  def __init__(self, addr_start, addr_end, class_label, skill2id, id2skill, parent=None):
    super(GearEntriesFrame, self).__init__(parent)
    self.addr_start = addr_start
    self.addr_end = addr_end
    self.max_num_slots = (addr_end - addr_start) / 6 / 4 + 1
    self.class_label = class_label
    self.skill2id = skill2id
    self.id2skill = id2skill

    self.skill_names = self.skill2id.keys()
    self.skill_names.sort()

    self.slots_cache = []  # list of raw 6*4 bytes
    self.slots_txt = None
    self.cur_slot_idx = -1
    self.cur_slot_bytes = None

    self.lbl_label = QLabel(self.class_label, self)

    self.btn_read_slots = QPushButton(' Cache Slots', self)
    self.btn_read_slots.setIcon(QIcon('img/flaticon/data110.png'))
    self.btn_read_slots.setStyleSheet('background-color: white')
    self.btn_read_slots.clicked.connect(self.onReadSlots)

    self.cmb_slots = QComboBox(self)
    self.cmb_slots.setToolTip('')
    self.cmb_slots.setStyleSheet('background-color: white')
    self.cmb_slots.currentIndexChanged[str].connect(self.onChangeSlot)
    self.cmb_slots.setDisabled(True)

    self.btn_read = QPushButton(self)
    self.btn_read.setIcon(QIcon('img/flaticon/open135.png'))
    self.btn_read.setToolTip('Read item slot value from memory')
    self.btn_read.setStyleSheet('background-color: white')
    self.btn_read.clicked.connect(self.onReadSlot)

    self.txt_raw = QLineEdit(self)
    self.txt_raw.setPlaceholderText('Raw hex data')
    self.txt_raw.setMaxLength(8 * 6 + 5)
    self.txt_raw.editingFinished.connect(self.onChangeRaw)

    self.btn_poke = QPushButton(self)
    self.btn_poke.setIcon(QIcon('img/flaticon/draw39.png'))
    self.btn_poke.setToolTip('Poke new value for item slot')
    self.btn_poke.setStyleSheet('background-color: white')
    self.btn_poke.clicked.connect(self.onPokeSlot)

    self.cmb_skills_a = QComboBox(self)
    self.cmb_skills_a.setEditable(True)
    self.cmb_skills_a.addItems(self.skill_names)
    self.cmb_skills_a.lineEdit().setText(GearEntriesFrame.MISSING_SKILL_NAME)
    self.cmb_skills_a.currentIndexChanged[str].connect(self.onChangeSkillA)

    self.cmb_skills_b = QComboBox(self)
    self.cmb_skills_b.setEditable(True)
    self.cmb_skills_b.addItems(self.skill_names)
    self.cmb_skills_b.lineEdit().setText(GearEntriesFrame.MISSING_SKILL_NAME)
    self.cmb_skills_b.currentIndexChanged[str].connect(self.onChangeSkillB)

    self.cmb_skills_c = QComboBox(self)
    self.cmb_skills_c.setEditable(True)
    self.cmb_skills_c.addItems(self.skill_names)
    self.cmb_skills_c.lineEdit().setText(GearEntriesFrame.MISSING_SKILL_NAME)
    self.cmb_skills_c.currentIndexChanged[str].connect(self.onChangeSkillC)

    incr_labels = []
    for incr in xrange(16):
      incr_labels.append('+%d' % incr)

    self.cmb_incr_a = QComboBox(self)
    self.cmb_incr_a.addItems(incr_labels)
    self.cmb_incr_a.currentIndexChanged[int].connect(self.onChangeIncrA)

    self.cmb_incr_b = QComboBox(self)
    self.cmb_incr_b.addItems(incr_labels)
    self.cmb_incr_b.currentIndexChanged[int].connect(self.onChangeIncrB)

    self.cmb_incr_c = QComboBox(self)
    self.cmb_incr_c.addItems(incr_labels)
    self.cmb_incr_c.currentIndexChanged[int].connect(self.onChangeIncrC)

    slot_labels = []
    for incr in xrange(4):
      slot_labels.append('%d Augment Slots' % incr)

    self.cmb_augments = QComboBox(self)
    self.cmb_augments.addItems(slot_labels)
    self.cmb_augments.currentIndexChanged[int].connect(self.onChangeAugments)

    self.layout = QGridLayout(self)
    self.layout.addWidget(self.lbl_label, 0, 0)
    self.layout.addWidget(self.btn_read_slots, 0, 1)
    self.layout.addWidget(self.cmb_slots, 0, 2)
    self.layout.addWidget(self.btn_read, 0, 3)
    self.layout.addWidget(self.txt_raw, 1, 0, 1, 3)
    self.layout.addWidget(self.btn_poke, 1, 3)
    self.layout.addWidget(self.cmb_skills_a, 2, 0)
    self.layout.addWidget(self.cmb_incr_a, 2, 1)
    self.layout.addWidget(self.cmb_skills_b, 3, 0)
    self.layout.addWidget(self.cmb_incr_b, 3, 1)
    self.layout.addWidget(self.cmb_skills_c, 4, 0)
    self.layout.addWidget(self.cmb_incr_c, 4, 1)
    self.layout.addWidget(self.cmb_augments, 2, 2)

    self.layout.setColumnStretch(0, 7)
    self.layout.setColumnStretch(1, 3)
    self.layout.setColumnStretch(2, 3)
    self.layout.setColumnStretch(3, 1)
    self.layout.setContentsMargins(0, 2, 0, 2)

    icon_height = self.lbl_label.height() * 8 / 15
    icon_size = QSize(icon_height, icon_height)
    self.btn_read_slots.setIconSize(icon_size)
    self.btn_read.setIconSize(icon_size)
    self.btn_poke.setIconSize(icon_size)
    btn_size = QSize(icon_height * 1.5, icon_height * 1.5)
    self.btn_read.setFixedSize(btn_size)
    self.btn_poke.setFixedSize(btn_size)

    self.updateUI()

  def updateUI(self):
    # Disable editing if cache missing
    if not (0 <= self.cur_slot_idx < len(self.slots_cache)) or self.cur_slot_bytes is None:
      self.cur_slot_idx = -1
      self.cur_slot_bytes = None
      self.cmb_slots.setDisabled(True)
      self.cmb_skills_a.setDisabled(True)
      self.cmb_skills_b.setDisabled(True)
      self.cmb_skills_c.setDisabled(True)
      self.cmb_incr_a.setDisabled(True)
      self.cmb_incr_b.setDisabled(True)
      self.cmb_incr_c.setDisabled(True)
      self.cmb_augments.setDisabled(True)
      self.txt_raw.setDisabled(True)
      return
    else:
      self.cmb_slots.setDisabled(False)
      self.cmb_skills_a.setDisabled(False)
      self.cmb_skills_b.setDisabled(False)
      self.cmb_skills_c.setDisabled(False)
      self.cmb_incr_a.setDisabled(False)
      self.cmb_incr_b.setDisabled(False)
      self.cmb_incr_c.setDisabled(False)
      self.cmb_augments.setDisabled(False)
      self.txt_raw.setDisabled(False)

    # Validate current slot's raw bytes
    try:
      (gear_id_bytes, index, post_index, skill_a_id, skill_a_incr, skill_b_id, skill_b_incr,
       skill_c_id, skill_c_incr, augment_a_id, augment_b_id, augment_c_id) = parse_gear_bytes(self.cur_slot_bytes)
      cur_slot_words = struct.unpack('>IIIIII', self.cur_slot_bytes)
      num_augments = (augment_a_id != 0xFFFF) + (augment_b_id != 0xFFFF) + (augment_c_id != 0xFFFF)
    except ValueError, e:
      self.log.emit('Gear parsing failed: %s' % e.what(), 'red')
      return

    # Update UI
    self.txt_raw.setDisabled(False)
    self.txt_raw.editingFinished.disconnect()
    self.txt_raw.setText('%08X %08X %08X %08X %08X %08X' % cur_slot_words)
    self.txt_raw.editingFinished.connect(self.onChangeRaw)

    self.cmb_skills_a.setDisabled(False)
    self.cmb_skills_a.currentIndexChanged[str].disconnect()
    try:
      skill_name = self.id2skill[skill_a_id]
      skill_idx = self.skill_names.index(skill_name)
      self.cmb_skills_a.setCurrentIndex(skill_idx)
    except (KeyError, ValueError):
      self.cmb_skills_a.lineEdit().setText(GearEntriesFrame.MISSING_SKILL_NAME)
    self.cmb_skills_a.currentIndexChanged[str].connect(self.onChangeSkillA)

    self.cmb_skills_b.setDisabled(False)
    self.cmb_skills_b.currentIndexChanged[str].disconnect()
    try:
      skill_name = self.id2skill[skill_b_id]
      skill_idx = self.skill_names.index(skill_name)
      self.cmb_skills_b.setCurrentIndex(skill_idx)
    except (KeyError, ValueError):
      self.cmb_skills_b.lineEdit().setText(GearEntriesFrame.MISSING_SKILL_NAME)
    self.cmb_skills_b.currentIndexChanged[str].connect(self.onChangeSkillB)

    self.cmb_skills_c.setDisabled(False)
    self.cmb_skills_c.currentIndexChanged[str].disconnect()
    try:
      skill_name = self.id2skill[skill_c_id]
      skill_idx = self.skill_names.index(skill_name)
      self.cmb_skills_c.setCurrentIndex(skill_idx)
    except (KeyError, ValueError):
      self.cmb_skills_c.lineEdit().setText(GearEntriesFrame.MISSING_SKILL_NAME)
    self.cmb_skills_c.currentIndexChanged[str].connect(self.onChangeSkillC)

    self.cmb_incr_a.setDisabled(False)
    self.cmb_incr_a.currentIndexChanged[int].disconnect()
    self.cmb_incr_a.setCurrentIndex(skill_a_incr)
    self.cmb_incr_a.currentIndexChanged[int].connect(self.onChangeIncrA)

    self.cmb_incr_b.setDisabled(False)
    self.cmb_incr_b.currentIndexChanged[int].disconnect()
    self.cmb_incr_b.setCurrentIndex(skill_b_incr)
    self.cmb_incr_b.currentIndexChanged[int].connect(self.onChangeIncrB)

    self.cmb_incr_c.setDisabled(False)
    self.cmb_incr_c.currentIndexChanged[int].disconnect()
    self.cmb_incr_c.setCurrentIndex(skill_c_incr)
    self.cmb_incr_c.currentIndexChanged[int].connect(self.onChangeIncrC)

    self.cmb_augments.setDisabled(False)
    self.cmb_augments.currentIndexChanged[int].disconnect()
    self.cmb_augments.setCurrentIndex(num_augments)
    self.cmb_augments.currentIndexChanged[int].connect(self.onChangeAugments)
예제 #6
0
class ItemEntriesFrame(QFrame):
    read_block = pyqtSignal(int, int)  # start_addr, num_bytes
    poke_block = pyqtSignal(int, QByteArray,
                            bool)  # start_addr, raw_bytes, is_ascii
    log = pyqtSignal(str, str)  # msg, color

    MISSING_ITEM_NAME = '[NOT IN DB]'

    def __init__(self,
                 type_val,
                 addr_start,
                 addr_end,
                 label,
                 id2name,
                 idx2id,
                 names,
                 parent=None):
        super(ItemEntriesFrame, self).__init__(parent)
        self.type_val = type_val
        self.code_offset = 0
        self.addr_start = addr_start
        self.addr_end = addr_end
        self.max_num_slots = (addr_end - addr_start) / 3 / 4 + 1
        self.label = label
        self.id2name = id2name
        self.idx2id = idx2id
        self.names = names

        self.slots_cache = [
        ]  # tuple: (addr_hex, addr_val, slot_number, cur_val)
        self.cur_slot_idx = -1

        self.lbl_label = QLabel(self.label, self)

        self.btn_read_slots = QPushButton(' Cache Slots', self)
        self.btn_read_slots.setIcon(QIcon('img/flaticon/data110.png'))
        self.btn_read_slots.setStyleSheet('background-color: white')
        self.btn_read_slots.clicked.connect(self.onReadSlots)

        self.btn_search_cache = QPushButton(' Search ID', self)
        self.btn_search_cache.setIcon(QIcon('img/flaticon/magnifier13.png'))
        self.btn_search_cache.setToolTip(
            'Find slot in cache with specified item ID')
        self.btn_search_cache.setStyleSheet('background-color: white')
        self.btn_search_cache.clicked.connect(self.onSearchCacheForID)

        self.btn_read = QPushButton(self)
        self.btn_read.setIcon(QIcon('img/flaticon/open135.png'))
        self.btn_read.setToolTip('Read item slot value from memory')
        self.btn_read.setStyleSheet('background-color: white')
        self.btn_read.clicked.connect(self.onReadSlot)

        self.cmb_slots = QComboBox(self)
        self.cmb_slots.setStyleSheet('background-color: white')
        self.cmb_slots.currentIndexChanged[int].connect(self.onChangeSlot)
        self.cmb_slots.setDisabled(True)

        self.cmb_names = QComboBox(self)
        self.cmb_names.setEditable(True)
        self.cmb_names.addItems(self.names)
        self.cmb_names.lineEdit().setText(ItemEntriesFrame.MISSING_ITEM_NAME)
        self.cmb_names.currentIndexChanged[int].connect(self.fetchID)

        self.txt_id = QLineEdit(self)
        self.txt_id.setPlaceholderText('ID (hex)')
        self.txt_id.setMaxLength(3)
        self.txt_id.textChanged.connect(self.fetchName)

        self.btn_rename = QPushButton(' Fix Name', self)
        self.btn_rename.setIcon(QIcon('img/flaticon/cloud-storage3.png'))
        self.btn_rename.setToolTip(
            'Add/Correct Item Name for %s (type: %02X)' %
            (self.label, type_val))
        self.btn_rename.setStyleSheet('background-color: white')
        self.btn_rename.clicked.connect(self.onRename)

        self.txt_amount = QLineEdit(self)
        self.txt_amount.setPlaceholderText('Amount')
        self.txt_amount.setMaxLength(3)

        self.btn_poke = QPushButton(self)
        self.btn_poke.setIcon(QIcon('img/flaticon/draw39.png'))
        self.btn_poke.setToolTip('Poke new value for item slot')
        self.btn_poke.setStyleSheet('background-color: white')
        self.btn_poke.clicked.connect(self.onPokeSlot)

        self.layout = QGridLayout(self)
        self.layout.addWidget(self.lbl_label, 0, 0)
        self.layout.addWidget(self.btn_read_slots, 0, 1)
        self.layout.addWidget(self.btn_search_cache, 0, 2)
        self.layout.addWidget(self.cmb_slots, 0, 3)
        self.layout.addWidget(self.btn_read, 0, 4)
        self.layout.addWidget(self.cmb_names, 1, 0)
        self.layout.addWidget(self.txt_id, 1, 1)
        self.layout.addWidget(self.btn_rename, 1, 2)
        self.layout.addWidget(self.txt_amount, 1, 3)
        self.layout.addWidget(self.btn_poke, 1, 4)

        self.layout.setColumnStretch(0, 7)
        self.layout.setColumnStretch(1, 3)
        self.layout.setColumnStretch(2, 3)
        self.layout.setColumnStretch(3, 3)
        self.layout.setColumnStretch(4, 1)
        self.layout.setContentsMargins(0, 2, 0, 2)

        icon_height = self.lbl_label.height() * 8 / 15
        icon_size = QSize(icon_height, icon_height)
        self.btn_read_slots.setIconSize(icon_size)
        self.btn_search_cache.setIconSize(icon_size)
        self.btn_rename.setIconSize(icon_size)
        self.btn_read.setIconSize(icon_size)
        self.btn_poke.setIconSize(icon_size)
        btn_size = QSize(icon_height * 1.5, icon_height * 1.5)
        self.btn_read.setFixedSize(btn_size)
        self.btn_poke.setFixedSize(btn_size)

        self.updateUI()

    def updateUI(self):
        # Disable editing if cache missing
        if not (0 <= self.cur_slot_idx < len(self.slots_cache)):
            self.cmb_names.lineEdit().setText(
                ItemEntriesFrame.MISSING_ITEM_NAME)
            self.cmb_names.setDisabled(True)
            self.txt_id.setText('')
            self.txt_id.setDisabled(True)
            return

        # Validate current value
        cur_val = self.slots_cache[self.cur_slot_idx][3]
        (type_val, id_val, amount) = parse_item_word(cur_val)
        if type_val != self.type_val and type_val != 0:
            cur_addr = self.slots_cache[
                self.cur_slot_idx][1] + self.code_offset
            self.log.emit(
                'Cache error: val(0x%08X)=%08X, type (%02X) != expected (%02X)'
                % (cur_addr, cur_val, type_val, self.type_val), 'red')
            return

        # Update UI
        self.txt_id.setDisabled(False)
        self.txt_id.setText('%03X' % id_val)
        self.cmb_names.setDisabled(False)
        self.fetchName()
        self.txt_amount.setText(str(amount))

    def setAlternateBGColor(self):
        self.setStyleSheet(
            'ItemEntriesFrame { background-color:rgb(248,248,248) }')

    @pyqtSlot(int)
    def onSetCodeOffset(self, signed_offset):
        self.code_offset = signed_offset

    @pyqtSlot()
    def onRename(self):
        id_txt = str(self.txt_id.text())
        if len(id_txt) != 3:
            return
        dialog = FixItemNameDialog('%02X' % self.type_val, id_txt, self)
        dialog.log.connect(self.log)
        dialog.exec_()

    @pyqtSlot()
    def onReadSlots(self):
        self.read_block.emit(self.addr_start + self.code_offset,
                             self.max_num_slots * 4 * 3)

    @pyqtSlot(int, int, QByteArray)
    def onBlockRead(self, addr_start_w_code_offset, num_bytes, raw_bytes):
        # Determine whether block has cache or single slot
        if (addr_start_w_code_offset == (self.addr_start + self.code_offset)
            ) and (num_bytes == self.max_num_slots * 4 * 3):
            self.onCacheRead(addr_start_w_code_offset, raw_bytes)
        elif num_bytes == 4:  # Assume read slot
            # Ignore if no cache
            if not (0 <= self.cur_slot_idx < len(self.slots_cache)):
                return
            self.onSlotRead(addr_start_w_code_offset, raw_bytes)

    def onCacheRead(self, addr_start_w_code_offset, raw_bytes):
        slot_bytes = str(raw_bytes)
        self.slots_cache = []
        slots_txt = []
        for slot_i in xrange(self.max_num_slots):
            byte_offset = slot_i * 3 * 4
            cur_slot_bytes = slot_bytes[byte_offset:(byte_offset + 4)]
            if len(cur_slot_bytes) != 4:
                continue
            cur_slot_val = struct.unpack('>I', cur_slot_bytes)[0]
            (type_val, id_val, amount) = parse_item_word(cur_slot_val)
            if type_val == 0 or amount == 0:
                continue
            elif type_val != self.type_val:
                self.log.emit(
                    'val(%08X)=%08X, type_val(%02X) unexpected(%02X)' %
                    (addr_start_w_code_offset + byte_offset, cur_slot_val,
                     type_val, self.type_val), 'red')
                continue
            else:
                addr_val = addr_start_w_code_offset + byte_offset - self.code_offset  # remove code_offset since it may change later
                addr_hex = '%08X' % addr_val
                slot_number = slot_i + 1
                self.slots_cache.append(
                    (addr_hex, addr_val, slot_number, cur_slot_val))
                slots_txt.append('Slot %03d' % slot_number)

        # Update UI
        self.log.emit(
            'Found %d %s slots in memory' %
            (len(self.slots_cache), self.label), 'black')
        self.cmb_slots.clear()
        if len(self.slots_cache) <= 0:
            self.cmb_slots.setDisabled(True)
            self.cur_slot_idx = -1
        else:
            self.cmb_slots.setDisabled(False)
            self.cmb_slots.addItems(slots_txt)
            self.cur_slot_idx = 0
        self.updateUI()

    def onSlotRead(self, addr_word_w_code_offset, raw_bytes):
        addr_cur_slot = self.slots_cache[
            self.cur_slot_idx][1] + self.code_offset
        if addr_word_w_code_offset == addr_cur_slot:
            cur_cache = self.slots_cache[self.cur_slot_idx]
            new_val = struct.unpack('>I', str(raw_bytes))[0]
            new_cache = (cur_cache[0], cur_cache[1], cur_cache[2], new_val)
            self.slots_cache[self.cur_slot_idx] = new_cache
            self.updateUI()
        else:  # Update cached value of other slots
            addr_first_slot = self.slots_cache[0][1] + self.code_offset
            addr_last_slot = self.slots_cache[-1][1] + self.code_offset
            if (addr_first_slot <= addr_word_w_code_offset <= addr_last_slot) and \
               ((addr_word_w_code_offset - addr_first_slot) % 12 == 0):
                for slot_i in xrange(len(self.slots_cache)):
                    addr_cur_slot = self.slots_cache[slot_i][
                        1] + self.code_offset
                    if addr_word_w_code_offset == addr_cur_slot:
                        cur_cache = self.slots_cache[slot_i]
                        new_val = struct.unpack('>I', str(raw_bytes))[0]
                        new_cache = (cur_cache[0], cur_cache[1], cur_cache[2],
                                     new_val)
                        self.slots_cache[slot_i] = new_cache
                        return

    @pyqtSlot()
    def onSearchCacheForID(self):
        # Stop if no cache
        if not (0 <= self.cur_slot_idx < len(self.slots_cache)):
            self.log.emit('Must cache slots before searching', 'red')
            return

        # Fetch and validate target ID
        try:
            target_id_val = int(str(self.txt_id.text()), 16)
            if target_id_val < 0 or target_id_val > Item.MAX_ID_VAL:
                self.log.emit('Item ID out of [0, 0x%03X] range' %
                              Item.MAX_ID_VAL)
                return
        except ValueError:
            self.log.emit('Failed to parse item ID, expecting XXX', 'red')
            return

        # Search ID in cache
        for cand_slot_idx in xrange(len(self.slots_cache)):
            (type_val, id_val,
             amount) = parse_item_word(self.slots_cache[cand_slot_idx][3])
            if id_val == target_id_val and type_val == self.type_val:
                self.cur_slot_idx = cand_slot_idx
                self.cmb_slots.setCurrentIndex(self.cur_slot_idx)
                self.updateUI()
                return
        self.log.emit(
            'Did not find ID=%03X in %d cached %s slots' %
            (target_id_val, len(self.slots_cache), self.label), 'red')

    @pyqtSlot(int)
    def onChangeSlot(self, new_slot_idx):
        # Validate new_slot_idx
        if not (0 <= new_slot_idx < len(self.slots_cache)):
            return

        # Update slot idx and read value from memory
        self.cur_slot_idx = new_slot_idx
        self.cmb_names.lineEdit().setText(ItemEntriesFrame.MISSING_ITEM_NAME)
        self.cmb_names.setDisabled(True)
        self.txt_id.setText('')
        self.txt_id.setDisabled(True)
        self.onReadSlot()

    @pyqtSlot()
    def fetchName(self):
        try:
            id_val = int(str(self.txt_id.text()), 16)
        except ValueError:
            return
        name = self.MISSING_ITEM_NAME
        if id_val in self.id2name:
            name = self.id2name[id_val]
        self.cmb_names.lineEdit().setText(name)

    @pyqtSlot(int)
    def fetchID(self, cmb_idx):
        if cmb_idx < 0 or cmb_idx >= len(self.idx2id):
            self.txt_id.setText('')
        else:
            self.txt_id.setText(self.idx2id[cmb_idx])

    @pyqtSlot()
    def onReadSlot(self):
        try:
            if not (0 <= self.cur_slot_idx < len(self.slots_cache)):
                raise ValueError('must cache slots before reading')
            addr_cur_slot = self.slots_cache[
                self.cur_slot_idx][1] + self.code_offset
            self.read_block.emit(addr_cur_slot, 4)
        except ValueError, e:
            cur_slot_num = self.slots_cache[self.cur_slot_idx][2]
            self.log.emit(
                'READ %s Slot %03d failed: %s' %
                (self.label, cur_slot_num, str(e)), 'red')
        except BaseException, e:
            cur_slot_num = self.slots_cache[self.cur_slot_idx][2]
            self.log.emit(
                'READ %s Slot %03d failed: %s' %
                (self.label, cur_slot_num, str(e)), 'red')
            traceback.print_exc()
예제 #7
0
class FindInFilesDialog( QDialog, object ):
    """ find in files dialog implementation """

    inProject = 0
    inDirectory = 1
    inOpenFiles = 2

    def __init__( self, where, what = "",
                  dirPath = "", filters = [],
                  parent = None ):

        QDialog.__init__( self, parent )

        mainWindow = GlobalData().mainWindow
        self.editorsManager = mainWindow.editorsManagerWidget.editorsManager

        self.__cancelRequest = False
        self.__inProgress = False
        self.searchRegexp = None
        self.searchResults = []

        # Avoid pylint complains
        self.findCombo = None
        self.caseCheckBox = None
        self.wordCheckBox = None
        self.regexpCheckBox = None
        self.projectRButton = None
        self.openFilesRButton = None
        self.dirRButton = None
        self.dirEditCombo = None
        self.dirSelectButton = None
        self.filterCombo = None
        self.fileLabel = None
        self.progressBar = None
        self.findButton = None

        self.__createLayout()
        self.setWindowTitle( "Find in files" )

        # Restore the combo box values
        project = GlobalData().project
        if project.fileName != "":
            self.findFilesWhat = project.findFilesWhat
            self.findFilesDirs = project.findFilesDirs
            self.findFilesMasks = project.findFilesMasks
        else:
            settings = Settings()
            self.findFilesWhat = settings.findFilesWhat
            self.findFilesDirs = settings.findFilesDirs
            self.findFilesMasks = settings.findFilesMasks
        self.findCombo.addItems( self.findFilesWhat )
        self.findCombo.setEditText( "" )
        self.dirEditCombo.addItems( self.findFilesDirs )
        self.dirEditCombo.setEditText( "" )
        self.filterCombo.addItems( self.findFilesMasks )
        self.filterCombo.setEditText( "" )

        if where == self.inProject:
            self.setSearchInProject( what, filters )
        elif where == self.inDirectory:
            self.setSearchInDirectory( what, dirPath, filters )
        else:
            self.setSearchInOpenFiles( what, filters )

        return

    def __createLayout( self ):
        """ Creates the dialog layout """

        self.resize( 600, 300 )
        self.setSizeGripEnabled( True )

        verticalLayout = QVBoxLayout( self )
        gridLayout = QGridLayout()

        # Combo box for the text to search
        findLabel = QLabel( self )
        findLabel.setText( "Find text:" )
        self.findCombo = QComboBox( self )
        self.__tuneCombo( self.findCombo )
        self.findCombo.lineEdit().setToolTip( "Regular expression to search for" )
        self.findCombo.editTextChanged.connect( self.__someTextChanged )

        gridLayout.addWidget( findLabel, 0, 0, 1, 1 )
        gridLayout.addWidget( self.findCombo, 0, 1, 1, 1 )
        verticalLayout.addLayout( gridLayout )

        # Check boxes
        horizontalCBLayout = QHBoxLayout()
        self.caseCheckBox = QCheckBox( self )
        self.caseCheckBox.setText( "Match &case" )
        horizontalCBLayout.addWidget( self.caseCheckBox )
        self.wordCheckBox = QCheckBox( self )
        self.wordCheckBox.setText( "Match whole &word" )
        horizontalCBLayout.addWidget( self.wordCheckBox )
        self.regexpCheckBox = QCheckBox( self )
        self.regexpCheckBox.setText( "Regular &expression" )
        horizontalCBLayout.addWidget( self.regexpCheckBox )

        verticalLayout.addLayout( horizontalCBLayout )

        # Files groupbox
        filesGroupbox = QGroupBox( self )
        filesGroupbox.setTitle( "Find in" )
        sizePolicy = QSizePolicy( QSizePolicy.Expanding, QSizePolicy.Preferred )
        sizePolicy.setHorizontalStretch( 0 )
        sizePolicy.setVerticalStretch( 0 )
        sizePolicy.setHeightForWidth(
                        filesGroupbox.sizePolicy().hasHeightForWidth() )
        filesGroupbox.setSizePolicy( sizePolicy )

        gridLayoutFG = QGridLayout( filesGroupbox )
        self.projectRButton = QRadioButton( filesGroupbox )
        self.projectRButton.setText( "&Project" )
        gridLayoutFG.addWidget( self.projectRButton, 0, 0 )
        self.projectRButton.clicked.connect( self.__projectClicked )

        self.openFilesRButton = QRadioButton( filesGroupbox )
        self.openFilesRButton.setText( "&Opened files only" )
        gridLayoutFG.addWidget( self.openFilesRButton, 1, 0 )
        self.openFilesRButton.clicked.connect( self.__openFilesOnlyClicked )

        self.dirRButton = QRadioButton( filesGroupbox )
        self.dirRButton.setText( "&Directory tree" )
        gridLayoutFG.addWidget( self.dirRButton, 2, 0 )
        self.dirRButton.clicked.connect( self.__dirClicked )

        self.dirEditCombo = QComboBox( filesGroupbox )
        self.__tuneCombo( self.dirEditCombo )
        self.dirEditCombo.lineEdit().setToolTip( "Directory to search in" )
        gridLayoutFG.addWidget( self.dirEditCombo, 2, 1 )
        self.dirEditCombo.editTextChanged.connect( self.__someTextChanged )

        self.dirSelectButton = QPushButton( filesGroupbox )
        self.dirSelectButton.setText( "..." )
        gridLayoutFG.addWidget( self.dirSelectButton, 2, 2 )
        self.dirSelectButton.clicked.connect( self.__selectDirClicked )

        filterLabel = QLabel( filesGroupbox )
        filterLabel.setText( "Files filter:" )
        gridLayoutFG.addWidget( filterLabel, 3, 0 )
        self.filterCombo = QComboBox( filesGroupbox )
        self.__tuneCombo( self.filterCombo )
        self.filterCombo.lineEdit().setToolTip( "File names regular expression" )
        gridLayoutFG.addWidget( self.filterCombo, 3, 1 )
        self.filterCombo.editTextChanged.connect( self.__someTextChanged )

        verticalLayout.addWidget( filesGroupbox )

        # File label
        self.fileLabel = FitPathLabel( self )
        self.fileLabel.setSizePolicy( QSizePolicy.Ignored, QSizePolicy.Fixed )
        verticalLayout.addWidget( self.fileLabel )

        # Progress bar
        self.progressBar = QProgressBar( self )
        self.progressBar.setValue( 0 )
        self.progressBar.setOrientation( Qt.Horizontal )
        verticalLayout.addWidget( self.progressBar )

        # Buttons at the bottom
        buttonBox = QDialogButtonBox( self )
        buttonBox.setOrientation( Qt.Horizontal )
        buttonBox.setStandardButtons( QDialogButtonBox.Cancel )
        self.findButton = buttonBox.addButton( "Find",
                                               QDialogButtonBox.AcceptRole )
        self.findButton.setDefault( True )
        self.findButton.clicked.connect( self.__process )
        verticalLayout.addWidget( buttonBox )

        buttonBox.rejected.connect( self.__onClose )
        return

    @staticmethod
    def __tuneCombo( comboBox ):
        " Sets the common settings for a combo box "
        sizePolicy = QSizePolicy( QSizePolicy.Expanding, QSizePolicy.Fixed )
        sizePolicy.setHorizontalStretch( 0 )
        sizePolicy.setVerticalStretch( 0 )
        sizePolicy.setHeightForWidth( \
                            comboBox.sizePolicy().hasHeightForWidth() )
        comboBox.setSizePolicy( sizePolicy )
        comboBox.setEditable( True )
        comboBox.setInsertPolicy( QComboBox.InsertAtTop )
        comboBox.setAutoCompletion( False )
        comboBox.setDuplicatesEnabled( False )
        return

    def __onClose( self ):
        " Triggered when the close button is clicked "

        self.__cancelRequest = True
        if not self.__inProgress:
            self.close()
        return

    def setSearchInProject( self, what = "", filters = [] ):
        " Set search ready for the whole project "

        if GlobalData().project.fileName == "":
            # No project loaded, fallback to opened files
            self.setSearchInOpenFiles( what, filters )
            return

        # Select the project radio button
        self.projectRButton.setEnabled( True )
        self.projectRButton.setChecked( True )
        self.dirEditCombo.setEnabled( False )
        self.dirSelectButton.setEnabled( False )

        openedFiles = self.editorsManager.getTextEditors()
        self.openFilesRButton.setEnabled( len( openedFiles ) != 0 )

        self.setFilters( filters )

        self.findCombo.setEditText( what )
        self.findCombo.lineEdit().selectAll()
        self.findCombo.setFocus()

        # Check searchability
        self.__testSearchability()
        return


    def setSearchInOpenFiles( self, what = "", filters = [] ):
        " Sets search ready for the opened files "

        openedFiles = self.editorsManager.getTextEditors()
        if len( openedFiles ) == 0:
            # No opened files, fallback to search in dir
            self.setSearchInDirectory( what, "", filters )
            return

        # Select the radio buttons
        self.projectRButton.setEnabled( GlobalData().project.fileName != "" )
        self.openFilesRButton.setEnabled( True )
        self.openFilesRButton.setChecked( True )
        self.dirEditCombo.setEnabled( False )
        self.dirSelectButton.setEnabled( False )

        self.setFilters( filters )

        self.findCombo.setEditText( what )
        self.findCombo.lineEdit().selectAll()
        self.findCombo.setFocus()

        # Check searchability
        self.__testSearchability()
        return


    def setSearchInDirectory( self, what = "", dirPath = "", filters = [] ):
        " Sets search ready for the given directory "

        # Select radio buttons
        self.projectRButton.setEnabled( GlobalData().project.fileName != "" )
        openedFiles = self.editorsManager.getTextEditors()
        self.openFilesRButton.setEnabled( len( openedFiles ) != 0 )
        self.dirRButton.setEnabled( True )
        self.dirRButton.setChecked( True )
        self.dirEditCombo.setEnabled( True )
        self.dirSelectButton.setEnabled( True )
        self.dirEditCombo.setEditText( dirPath )

        self.setFilters( filters )

        self.findCombo.setEditText( what )
        self.findCombo.lineEdit().selectAll()
        self.findCombo.setFocus()

        # Check searchability
        self.__testSearchability()
        return


    def setFilters( self, filters ):
        " Sets up the filters "

        # Set filters if provided
        if filters:
            self.filterCombo.setEditText( ";".join( filters ) )
        else:
            self.filterCombo.setEditText( "" )
        return

    def __testSearchability( self ):
        " Tests the searchability and sets the Find button status "

        startTime = time.time()
        if self.findCombo.currentText().strip() == "":
            self.findButton.setEnabled( False )
            self.findButton.setToolTip( "No text to search" )
            return

        if self.dirRButton.isChecked():
            dirname = self.dirEditCombo.currentText().strip()
            if dirname == "":
                self.findButton.setEnabled( False )
                self.findButton.setToolTip( "No directory path" )
                return
            if not os.path.isdir( dirname ):
                self.findButton.setEnabled( False )
                self.findButton.setToolTip( "Path is not a directory" )
                return

        # Now we need to match file names if there is a filter
        filtersText = self.filterCombo.currentText().strip()
        if filtersText == "":
            self.findButton.setEnabled( True )
            self.findButton.setToolTip( "Find in files" )
            return

        # Need to check the files match
        try:
            filterRe = re.compile( filtersText, re.IGNORECASE )
        except:
            self.findButton.setEnabled( False )
            self.findButton.setToolTip( "Incorrect files " \
                                        "filter regular expression" )
            return

        matched = False
        tooLong = False
        if self.projectRButton.isChecked():
            # Whole project
            for fname in GlobalData().project.filesList:
                if fname.endswith( sep ):
                    continue
                matched = filterRe.match( fname )
                if matched:
                    break
                # Check the time, it might took too long
                if time.time() - startTime > 0.1:
                    tooLong = True
                    break

        elif self.openFilesRButton.isChecked():
            # Opened files
            openedFiles = self.editorsManager.getTextEditors()
            for record in openedFiles:
                matched = filterRe.match( record[ 1 ] )
                if matched:
                    break
                # Check the time, it might took too long
                if time.time() - startTime > 0.1:
                    tooLong = True
                    break

        else:
            # Search in the dir
            if not dirname.endswith( sep ):
                dirname += sep
            matched, tooLong = self.__matchInDir( dirname, filterRe, startTime )

        if matched:
            self.findButton.setEnabled( True )
            self.findButton.setToolTip( "Find in files" )
        else:
            if tooLong:
                self.findButton.setEnabled( True )
                self.findButton.setToolTip( "Find in files" )
            else:
                self.findButton.setEnabled( False )
                self.findButton.setToolTip( "No files matched to search in" )
        return

    @staticmethod
    def __matchInDir( path, filterRe, startTime ):
        " Provides the 'match' and 'too long' statuses "
        matched = False
        tooLong = False
        for item in os.listdir( path ):
            if time.time() - startTime > 0.1:
                tooLong = True
                return matched, tooLong
            if os.path.isdir( path + item ):
                dname = path + item + sep
                matched, tooLong = FindInFilesDialog.__matchInDir( dname,
                                                                   filterRe,
                                                                   startTime )
                if matched or tooLong:
                    return matched, tooLong
                continue
            if filterRe.match( path + item ):
                matched = True
                return matched, tooLong
        return matched, tooLong

    def __projectClicked( self ):
        " project radio button clicked "
        self.dirEditCombo.setEnabled( False )
        self.dirSelectButton.setEnabled( False )
        self.__testSearchability()
        return

    def __openFilesOnlyClicked( self ):
        " open files only radio button clicked "
        self.dirEditCombo.setEnabled( False )
        self.dirSelectButton.setEnabled( False )
        self.__testSearchability()
        return

    def __dirClicked( self ):
        " dir radio button clicked "
        self.dirEditCombo.setEnabled( True )
        self.dirSelectButton.setEnabled( True )
        self.dirEditCombo.setFocus()
        self.__testSearchability()
        return

    def __someTextChanged( self, text ):
        " Text to search, filter or dir name has been changed "
        self.__testSearchability()
        return

    def __selectDirClicked( self ):
        " The user selects a directory "
        dirName = QFileDialog.getExistingDirectory( self,
                    "Select directory to search in",
                    self.dirEditCombo.currentText(),
                    QFileDialog.Options( QFileDialog.ShowDirsOnly ) )

        if dirName:
            self.dirEditCombo.setEditText( os.path.normpath( dirName ) )
        self.__testSearchability()
        return


    def __projectFiles( self, filterRe ):
        " Project files list respecting the mask "
        mainWindow = GlobalData().mainWindow
        files = []
        for fname in GlobalData().project.filesList:
            if fname.endswith( sep ):
                continue
            if filterRe is None or filterRe.match( fname ):
                widget = mainWindow.getWidgetForFileName( fname )
                if widget is None:
                    # Do not check for broken symlinks
                    if isFileSearchable( fname, False ):
                        files.append( ItemToSearchIn( fname, "" ) )
                else:
                    if widget.getType() in \
                                [ MainWindowTabWidgetBase.PlainTextEditor ]:
                        files.append( ItemToSearchIn( fname,
                                                      widget.getUUID() ) )
            QApplication.processEvents()
            if self.__cancelRequest:
                raise Exception( "Cancel request" )
        return files

    def __openedFiles( self, filterRe ):
        " Currently opened editor buffers "

        files = []
        openedFiles = self.editorsManager.getTextEditors()
        for record in openedFiles:
            uuid = record[ 0 ]
            fname = record[ 1 ]
            if filterRe is None or filterRe.match( fname ):
                files.append( ItemToSearchIn( fname, uuid ) )
            QApplication.processEvents()
            if self.__cancelRequest:
                raise Exception( "Cancel request" )
        return files

    def __dirFiles( self, path, filterRe, files ):
        " Files recursively for the dir "
        for item in os.listdir( path ):
            QApplication.processEvents()
            if self.__cancelRequest:
                raise Exception( "Cancel request" )
            if os.path.isdir( path + item ):
                if item in [ ".svn", ".cvs" ]:
                    # It does not make sense to search in revision control dirs
                    continue
                anotherDir, isLoop = resolveLink( path + item )
                if not isLoop:
                    self.__dirFiles( anotherDir + sep,
                                     filterRe, files )
                continue
            if not os.path.isfile( path + item ):
                continue
            realItem, isLoop = resolveLink( path + item )
            if isLoop:
                continue
            if filterRe is None or filterRe.match( realItem ):
                found = False
                for itm in files:
                    if itm.fileName == realItem:
                        found = True
                        break
                if not found:
                    mainWindow = GlobalData().mainWindow
                    widget = mainWindow.getWidgetForFileName( realItem )
                    if widget is None:
                        if isFileSearchable( realItem ):
                            files.append( ItemToSearchIn( realItem, "" ) )
                    else:
                        if widget.getType() in \
                                    [ MainWindowTabWidgetBase.PlainTextEditor ]:
                            files.append( ItemToSearchIn( realItem,
                                                          widget.getUUID() ) )
        return


    def __buildFilesList( self ):
        " Builds the list of files to search in "
        filtersText = self.filterCombo.currentText().strip()
        if filtersText != "":
            filterRe = re.compile( filtersText, re.IGNORECASE )
        else:
            filterRe = None

        if self.projectRButton.isChecked():
            return self.__projectFiles( filterRe )

        if self.openFilesRButton.isChecked():
            return self.__openedFiles( filterRe )

        dirname = os.path.realpath( self.dirEditCombo.currentText().strip() )
        files = []
        self.__dirFiles( dirname + sep, filterRe, files )
        return files


    def __process( self ):
        " Search process "

        # Add entries to the combo box if required
        regexpText = self.findCombo.currentText()
        if regexpText in self.findFilesWhat:
            self.findFilesWhat.remove( regexpText )
        self.findFilesWhat.insert( 0, regexpText )
        if len( self.findFilesWhat ) > 32:
            self.findFilesWhat = self.findFilesWhat[ : 32 ]
        self.findCombo.clear()
        self.findCombo.addItems( self.findFilesWhat )

        filtersText = self.filterCombo.currentText().strip()
        if filtersText in self.findFilesMasks:
            self.findFilesMasks.remove( filtersText )
        self.findFilesMasks.insert( 0, filtersText )
        if len( self.findFilesMasks ) > 32:
            self.findFilesMasks = self.findFilesMasks[ : 32 ]
        self.filterCombo.clear()
        self.filterCombo.addItems( self.findFilesMasks )

        if self.dirRButton.isChecked():
            dirText = self.dirEditCombo.currentText().strip()
            if dirText in self.findFilesDirs:
                self.findFilesDirs.remove( dirText )
            self.findFilesDirs.insert( 0, dirText )
            if len( self.findFilesDirs ) > 32:
                self.findFilesDirs = self.findFilesDirs[ : 32 ]
            self.dirEditCombo.clear()
            self.dirEditCombo.addItems( self.findFilesDirs )

        # Save the combo values for further usage
        if GlobalData().project.fileName != "":
            GlobalData().project.setFindInFilesHistory( self.findFilesWhat,
                                                        self.findFilesDirs,
                                                        self.findFilesMasks )
        else:
            Settings().findFilesWhat = self.findFilesWhat
            Settings().findFilesDirs = self.findFilesDirs
            Settings().findFilesMasks = self.findFilesMasks


        self.__inProgress = True
        numberOfMatches = 0
        self.searchResults = []
        self.searchRegexp = None

        # Form the regexp to search
        if not self.regexpCheckBox.isChecked():
            regexpText = re.escape( regexpText )
        if self.wordCheckBox.isChecked():
            regexpText = "\\b%s\\b" % regexpText
        flags = re.UNICODE | re.LOCALE
        if not self.caseCheckBox.isChecked():
            flags |= re.IGNORECASE

        try:
            self.searchRegexp = re.compile( regexpText, flags )
        except:
            logging.error( "Invalid search expression" )
            self.close()
            return


        QApplication.setOverrideCursor( QCursor( Qt.WaitCursor ) )
        self.fileLabel.setPath( 'Building list of files to search in...' )
        QApplication.processEvents()
        try:
            files = self.__buildFilesList()
        except Exception, exc:
            if "Cancel request" in str( exc ):
                QApplication.restoreOverrideCursor()
                self.close()
                return
            else:
                QApplication.restoreOverrideCursor()
                logging.error( str( exc ) )
                self.close()
                return
        QApplication.restoreOverrideCursor()
        QApplication.processEvents()

        if len( files ) == 0:
            self.fileLabel.setPath( 'No files to search in' )
            return

        self.progressBar.setRange( 0, len( files ) )

        index = 1
        for item in files:

            if self.__cancelRequest:
                self.__inProgress = False
                self.close()
                return

            self.fileLabel.setPath( 'Matches: ' + str( numberOfMatches ) + \
                                    ' Processing: ' + item.fileName )

            item.search( self.searchRegexp )
            found = len( item.matches )
            if found > 0:
                numberOfMatches += found
                self.searchResults.append( item )

            self.progressBar.setValue( index )
            index += 1

            QApplication.processEvents()

        if numberOfMatches == 0:
            if len( files ) == 1:
                self.fileLabel.setPath( "No matches in 1 file." )
            else:
                self.fileLabel.setPath( "No matches in " + \
                                        str( len( files ) ) + " files." )
            self.__inProgress = False
        else:
            self.close()
        return
예제 #8
0
class Dialog(QDialog):
    def __init__(self, parent=None):
        super(Dialog, self).__init__(parent)
        self._filename = None
        self._page = None
        self._rect = None
        self.imageViewer = widgets.imageviewer.ImageViewer()
        self.dpiLabel = QLabel()
        self.dpiCombo = QComboBox(insertPolicy=QComboBox.NoInsert, editable=True)
        self.dpiCombo.lineEdit().setCompleter(None)
        self.dpiCombo.setValidator(QDoubleValidator(10.0, 1200.0, 4, self.dpiCombo))
        self.dpiCombo.addItems([format(i) for i in (72, 100, 200, 300, 600, 1200)])
        
        self.colorButton = widgets.colorbutton.ColorButton()
        self.colorButton.setColor(QColor(Qt.white))
        self.crop = QCheckBox()
        self.antialias = QCheckBox(checked=True)
        self.dragfile = QPushButton(icons.get("image-x-generic"), None, None)
        self.fileDragger = FileDragger(self.dragfile)
        self.buttons = QDialogButtonBox(QDialogButtonBox.Close)
        self.copyButton = self.buttons.addButton('', QDialogButtonBox.ApplyRole)
        self.copyButton.setIcon(icons.get('edit-copy'))
        self.saveButton = self.buttons.addButton('', QDialogButtonBox.ApplyRole)
        self.saveButton.setIcon(icons.get('document-save'))
        
        layout = QVBoxLayout()
        self.setLayout(layout)
        
        layout.addWidget(self.imageViewer)
        
        controls = QHBoxLayout()
        layout.addLayout(controls)
        controls.addWidget(self.dpiLabel)
        controls.addWidget(self.dpiCombo)
        controls.addWidget(self.colorButton)
        controls.addWidget(self.crop)
        controls.addWidget(self.antialias)
        controls.addStretch()
        controls.addWidget(self.dragfile)
        layout.addWidget(widgets.Separator())
        layout.addWidget(self.buttons)

        app.translateUI(self)
        self.readSettings()
        self.finished.connect(self.writeSettings)
        self.dpiCombo.editTextChanged.connect(self.drawImage)
        self.colorButton.colorChanged.connect(self.drawImage)
        self.antialias.toggled.connect(self.drawImage)
        self.crop.toggled.connect(self.cropImage)
        self.buttons.rejected.connect(self.reject)
        self.copyButton.clicked.connect(self.copyToClipboard)
        self.saveButton.clicked.connect(self.saveAs)
        qutil.saveDialogSize(self, "copy_image/dialog/size", QSize(480, 320))
    
    def translateUI(self):
        self.setCaption()
        self.dpiLabel.setText(_("DPI:"))
        self.colorButton.setToolTip(_("Paper Color"))
        self.crop.setText(_("Auto-crop"))
        self.antialias.setText(_("Antialias"))
        self.dragfile.setText(_("Drag"))
        self.dragfile.setToolTip(_("Drag the image as a PNG file."))
        self.copyButton.setText(_("&Copy to Clipboard"))
        self.saveButton.setText(_("&Save As..."))
        self.imageViewer.setWhatsThis(_(
            #xgettext:no-python-format
            "<p>\n"
            "Clicking toggles the display between 100% size and window size. "
            "Drag to copy the image to another application. "
            "Drag with Ctrl (or {command}) to scroll a large image.\n"
            "</p>\n"
            "<p>\n"
            "You can also drag the small picture icon in the bottom right, "
            "which drags the actual file on disk, e.g. to an e-mail message.\n"
            "</p>").format(command="\u2318"))
        
    def readSettings(self):
        s = QSettings()
        s.beginGroup('copy_image')
        self.dpiCombo.setEditText(s.value("dpi", "100", type("")))
        self.colorButton.setColor(s.value("papercolor", QColor(Qt.white), QColor))
        self.crop.setChecked(s.value("autocrop", False, bool))
        self.antialias.setChecked(s.value("antialias", True, bool))
    
    def writeSettings(self):
        s = QSettings()
        s.beginGroup('copy_image')
        s.setValue("dpi", self.dpiCombo.currentText())
        s.setValue("papercolor", self.colorButton.color())
        s.setValue("autocrop", self.crop.isChecked())
        s.setValue("antialias", self.antialias.isChecked())
    
    def setCaption(self):
        if self._filename:
            filename = os.path.basename(self._filename)
        else:
            filename = _("<unknown>")
        title = _("Image from {filename}").format(filename = filename)
        self.setWindowTitle(app.caption(title))
        
    def setPage(self, page, rect):
        self._page = page
        self._rect = rect
        self._filename = documents.filename(page.document())
        self.fileDragger.basename = os.path.splitext(os.path.basename(self._filename))[0]
        self.setCaption()
        self.drawImage()

    def drawImage(self):
        dpi = float(self.dpiCombo.currentText() or '100')
        dpi = max(dpi, self.dpiCombo.validator().bottom())
        dpi = min(dpi, self.dpiCombo.validator().top())
        options = qpopplerview.RenderOptions()
        options.setPaperColor(self.colorButton.color())
        if self.antialias.isChecked():
            if popplerqt4:
                options.setRenderHint(
                    popplerqt4.Poppler.Document.Antialiasing |
                    popplerqt4.Poppler.Document.TextAntialiasing)
        else:
            options.setRenderHint(0)
        self._image = self._page.image(self._rect, dpi, dpi, options)
        self.cropImage()
    
    def cropImage(self):
        image = self._image
        if self.crop.isChecked():
            image = image.copy(autoCropRect(image))
        self.imageViewer.setImage(image)
        self.fileDragger.setImage(image)
    
    def copyToClipboard(self):
        QApplication.clipboard().setImage(self.imageViewer.image())

    def saveAs(self):
        if self._filename and not self.imageViewer.image().isNull():
            filename = os.path.splitext(self._filename)[0] + ".png"
        else:
            filename = 'image.png'
        filename = QFileDialog.getSaveFileName(self,
            _("Save Image As"), filename)
        if filename:
            if not self.imageViewer.image().save(filename):
                QMessageBox.critical(self, _("Error"), _(
                    "Could not save the image."))
            else:
                self.fileDragger.currentFile = filename
예제 #9
0
class FindReplace(QWidget):
    """
    Find replace widget bar
    """
    NbReplaced = pyqtSignal(int)

    def __init__(self, parent=None):
        """
        This class provides an graphical interface to find and replace text

        @param parent: 
        @type parent:
        """
        QWidget.__init__(self, parent)
        self.editor = None
        self.styleEdit = {
            False: "background-color:rgb(255, 175, 90);",
            True: ""
        }

        self.createButton()
        self.createWidgets()
        self.createConnections()

    def showEnhanced(self, textSelected=''):
        """
        Show enhanced (focus and text selected)
        """
        self.show()
        if len(textSelected): self.edit.setEditText(textSelected)
        self.edit.setFocus()
        self.edit.lineEdit().selectAll()

    def createButton(self):
        """
        create qt buttons
        Buttons defined:
         * Previous
         * Next
         * Replace
        """
        self.previousButton = QtHelper.createButton(
            self,
            text=self.tr("Find Previous"),
            triggered=self.findPrevious,
            icon=QIcon(":/find_previous.png"))
        self.nextButton = QtHelper.createButton(self,
                                                text=self.tr("Find Next"),
                                                triggered=self.findNext,
                                                icon=QIcon(":/find_next.png"))
        self.replaceButton = QtHelper.createButton(self,
                                                   text=self.tr("Replace..."),
                                                   triggered=self.replaceFind,
                                                   icon=QIcon(":/replace.png"))

    def createWidgets(self):
        """
        QtWidgets creation

        QHBoxLayout
         -------------------------------------------.....
        | QLabel: QLineEdit QButton QButton QCheckBox |
         -------------------------------------------.....

        ....--------------------------------------
             QLabel: QLineEdit QButton QCheckBox |
        ....--------------------------------------
        """
        glayout = QGridLayout()

        self.edit = QLineEditMore(parent=self)
        self.edit.setEditable(1)
        self.edit.setMaxCount(10)
        self.edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.edit.lineEdit().setPlaceholderText("Search text in your test?")
        self.edit.setMinimumWidth(200)

        self.replaceEdit = QComboBox(self)
        self.replaceEdit.setEditable(1)
        self.replaceEdit.setMaxCount(10)
        self.replaceEdit.setSizePolicy(QSizePolicy.Expanding,
                                       QSizePolicy.Fixed)
        self.replaceEdit.lineEdit().setPlaceholderText(
            "Replace the text with?")
        self.replaceEdit.setMinimumWidth(200)

        self.caseCheck = QCheckBox(self.tr("Case Sensitive"))
        self.caseCheck.setChecked(
            QtHelper.str2bool(Settings.instance().readValue(
                key='Editor/find-case-sensitive')))
        self.caseWordCheck = QCheckBox(self.tr("Whole word only"))
        self.caseWordCheck.setChecked(
            QtHelper.str2bool(
                Settings.instance().readValue(key='Editor/find-whole-word')))
        self.allCheck = QCheckBox(self.tr("All occurences"))
        self.allCheck.setChecked(
            QtHelper.str2bool(
                Settings.instance().readValue(key='Editor/replace-all')))
        self.caseRegexpCheck = QCheckBox(self.tr("Regular expression"))
        self.caseRegexpCheck.setChecked(
            QtHelper.str2bool(
                Settings.instance().readValue(key='Editor/find-regexp')))
        self.caseWrapCheck = QCheckBox(self.tr("Wrap at the end"))
        self.caseWrapCheck.setChecked(
            QtHelper.str2bool(
                Settings.instance().readValue(key='Editor/find-wrap')))

        glayout.addWidget(self.edit, 0, 1)
        glayout.addWidget(self.nextButton, 0, 3)
        glayout.addWidget(self.previousButton, 0, 2)
        glayout.addWidget(self.caseCheck, 2, 2)
        glayout.addWidget(self.caseWrapCheck, 2, 3)
        glayout.addWidget(self.caseWordCheck, 3, 2)
        glayout.addWidget(self.caseRegexpCheck, 3, 3)

        glayout.addWidget(self.replaceEdit, 1, 1)
        glayout.addWidget(self.replaceButton, 1, 2)
        glayout.addWidget(self.allCheck, 1, 3)

        self.previousButton.setDisabled(True)
        self.nextButton.setDisabled(True)

        self.setLayout(glayout)

        flags = Qt.WindowFlags()
        flags |= Qt.Window
        flags |= Qt.WindowTitleHint
        flags |= Qt.WindowCloseButtonHint
        flags |= Qt.MSWindowsFixedSizeDialogHint
        self.setWindowFlags(flags)

        self.setWindowIcon(QIcon(":/main.png"))
        self.setWindowTitle("Find And Replace")

    def createConnections(self):
        """
        create qt connection
        """
        self.edit.editTextChanged.connect(self.textHasChanged)
        self.edit.EnterPressed.connect(self.returnPressed)
        self.caseCheck.stateChanged.connect(self.find)

    def returnPressed(self):
        """
        Return key pressed
        Find next in this case
        """
        self.findNext()

    def setEditor(self, editor):
        """
        Set the target to find the text

        @param editor: 
        @type editor:   
        """
        self.editor = editor

    def textHasChanged(self, txt):
        """
        Find text has changed
        """
        text = self.edit.currentText()
        if len(text) > 0:
            self.previousButton.setEnabled(True)
            self.nextButton.setEnabled(True)
            self.find(changed=True, forward=True)
        else:
            self.previousButton.setDisabled(True)
            self.nextButton.setDisabled(True)

    def updateComboBox(self):
        """
        Update combobox
        """
        comboUpdated = False
        for i in range(self.edit.count()):
            if self.edit.itemText(i) == self.edit.currentText():
                comboUpdated = True
        if not comboUpdated:
            self.edit.addItem(self.edit.currentText())

        comboUpdated = False
        for i in range(self.replaceEdit.count()):
            if self.replaceEdit.itemText(i) == self.replaceEdit.currentText():
                comboUpdated = True
        if not comboUpdated:
            self.replaceEdit.addItem(self.replaceEdit.currentText())

    def clearText(self):
        """
        Clear all QlineEdit
        """
        self.edit.setStyleSheet("")

        self.edit.clearEditText()

        self.replaceEdit.clearEditText()

    def findPrevious(self):
        """
        Find previous occurence
        """
        # update combobox
        self.updateComboBox()

        # find previous
        self.find(changed=False, forward=False)

    def findNext(self, line=-1, index=-1):
        """
        Find next occurence
        """
        # update combobox
        self.updateComboBox()

        return self.find(changed=False, forward=True, line=line, index=index)

    def find(self, changed=True, forward=True, line=-1, index=-1):
        """
        Call the find function

        @param changed: 
        @type changed: boolean  

        @param forward: 
        @type forward: boolean      
        """
        text = self.edit.currentText()
        if len(text) == 0:
            self.edit.setStyleSheet("")
            return None
        else:
            found = self.editor.findText(
                text,
                changed,
                forward,
                case=self.caseCheck.isChecked(),
                words=self.caseWordCheck.isChecked(),
                regexp=self.caseRegexpCheck.isChecked(),
                wraps=self.caseWrapCheck.isChecked(),
                line=line,
                index=index)
            self.edit.setStyleSheet(self.styleEdit[found])
            return found

    def replaceFind(self):
        """
        Replace and find
        """
        if (self.editor is None): return

        replaceText = self.replaceEdit.currentText()
        searchText = self.edit.currentText()
        if not self.caseCheck.isChecked(): searchText = searchText.lower()
        current = -1
        nbReplaced = 0

        # find the first occurence from the beginning of the doc or not
        if not self.allCheck.isChecked():
            detected = self.findNext()
        else:
            detected = self.findNext(line=0, index=0)
        (line, _) = self.editor.getCursorPosition()

        while detected:
            previous = current
            selectedText = self.editor.selectedText()

            # normalize the text in lower case if the case sensitive is not activated
            if not self.caseCheck.isChecked():
                selectedText = selectedText.lower()

            # replace the selection
            if self.editor.hasSelectedText() and selectedText == searchText:
                self.editor.replace(replaceText)
                nbReplaced += 1

            # find the next occurence of the word
            detected = self.findNext()
            (current, _) = self.editor.getCursorPosition()

            # all doc readed ? break the loop
            if previous > current: break
            if current == line and previous != -1: break

            # just execute one replace
            if not self.allCheck.isChecked(): break

        self.allCheck.setCheckState(Qt.Unchecked)
        self.NbReplaced.emit(nbReplaced)
예제 #10
0
class GearEntriesFrame(QFrame):
    read_block = pyqtSignal(int, int)  # start_addr, num_bytes
    poke_block = pyqtSignal(int, QByteArray,
                            bool)  # start_addr, raw_bytes, is_ascii
    log = pyqtSignal(str, str)  # msg, color

    MISSING_SKILL_NAME = '[NOT IN DB]'

    def __init__(self,
                 addr_start,
                 addr_end,
                 class_label,
                 skill2id,
                 id2skill,
                 parent=None):
        super(GearEntriesFrame, self).__init__(parent)
        self.code_offset = 0
        self.addr_start = addr_start
        self.addr_end = addr_end
        self.max_num_slots = (addr_end - addr_start) / 6 / 4 + 1
        self.class_label = class_label
        self.skill2id = skill2id
        self.id2skill = id2skill

        self.skill_names = self.skill2id.keys()
        self.skill_names.sort()

        self.slots_cache = []  # list of raw 6*4 bytes
        self.slots_txt = None
        self.cur_slot_idx = -1
        self.cur_slot_bytes = None

        self.lbl_label = QLabel(self.class_label, self)

        self.btn_read_slots = QPushButton(' Cache Slots', self)
        self.btn_read_slots.setIcon(QIcon('img/flaticon/data110.png'))
        self.btn_read_slots.setStyleSheet('background-color: white')
        self.btn_read_slots.clicked.connect(self.onReadSlots)

        self.cmb_slots = QComboBox(self)
        self.cmb_slots.setStyleSheet('background-color: white')
        self.cmb_slots.currentIndexChanged[str].connect(self.onChangeSlot)
        self.cmb_slots.setDisabled(True)

        self.btn_read = QPushButton(self)
        self.btn_read.setIcon(QIcon('img/flaticon/open135.png'))
        self.btn_read.setToolTip('Read item slot value from memory')
        self.btn_read.setStyleSheet('background-color: white')
        self.btn_read.clicked.connect(self.onReadSlot)

        self.txt_raw = QLineEdit(self)
        self.txt_raw.setPlaceholderText('Raw hex data')
        self.txt_raw.setMaxLength(8 * 6 + 5)
        self.txt_raw.editingFinished.connect(self.onChangeRaw)

        self.btn_poke = QPushButton(self)
        self.btn_poke.setIcon(QIcon('img/flaticon/draw39.png'))
        self.btn_poke.setToolTip('Poke new value for item slot')
        self.btn_poke.setStyleSheet('background-color: white')
        self.btn_poke.clicked.connect(self.onPokeSlot)

        self.cmb_skills_a = QComboBox(self)
        self.cmb_skills_a.setEditable(True)
        self.cmb_skills_a.addItems(self.skill_names)
        self.cmb_skills_a.lineEdit().setText(
            GearEntriesFrame.MISSING_SKILL_NAME)
        self.cmb_skills_a.currentIndexChanged[str].connect(self.onChangeSkillA)

        self.cmb_skills_b = QComboBox(self)
        self.cmb_skills_b.setEditable(True)
        self.cmb_skills_b.addItems(self.skill_names)
        self.cmb_skills_b.lineEdit().setText(
            GearEntriesFrame.MISSING_SKILL_NAME)
        self.cmb_skills_b.currentIndexChanged[str].connect(self.onChangeSkillB)

        self.cmb_skills_c = QComboBox(self)
        self.cmb_skills_c.setEditable(True)
        self.cmb_skills_c.addItems(self.skill_names)
        self.cmb_skills_c.lineEdit().setText(
            GearEntriesFrame.MISSING_SKILL_NAME)
        self.cmb_skills_c.currentIndexChanged[str].connect(self.onChangeSkillC)

        incr_labels = []
        for incr in xrange(16):
            incr_labels.append('+%d' % incr)

        self.cmb_incr_a = QComboBox(self)
        self.cmb_incr_a.addItems(incr_labels)
        self.cmb_incr_a.currentIndexChanged[int].connect(self.onChangeIncrA)

        self.cmb_incr_b = QComboBox(self)
        self.cmb_incr_b.addItems(incr_labels)
        self.cmb_incr_b.currentIndexChanged[int].connect(self.onChangeIncrB)

        self.cmb_incr_c = QComboBox(self)
        self.cmb_incr_c.addItems(incr_labels)
        self.cmb_incr_c.currentIndexChanged[int].connect(self.onChangeIncrC)

        slot_labels = []
        for incr in xrange(4):
            slot_labels.append('%d Augment Slots' % incr)

        self.cmb_augments = QComboBox(self)
        self.cmb_augments.addItems(slot_labels)
        self.cmb_augments.currentIndexChanged[int].connect(
            self.onChangeAugments)

        self.layout = QGridLayout(self)
        self.layout.addWidget(self.lbl_label, 0, 0)
        self.layout.addWidget(self.btn_read_slots, 0, 1)
        self.layout.addWidget(self.cmb_slots, 0, 2)
        self.layout.addWidget(self.btn_read, 0, 3)
        self.layout.addWidget(self.txt_raw, 1, 0, 1, 3)
        self.layout.addWidget(self.btn_poke, 1, 3)
        self.layout.addWidget(self.cmb_skills_a, 2, 0)
        self.layout.addWidget(self.cmb_incr_a, 2, 1)
        self.layout.addWidget(self.cmb_skills_b, 3, 0)
        self.layout.addWidget(self.cmb_incr_b, 3, 1)
        self.layout.addWidget(self.cmb_skills_c, 4, 0)
        self.layout.addWidget(self.cmb_incr_c, 4, 1)
        self.layout.addWidget(self.cmb_augments, 2, 2)

        self.layout.setColumnStretch(0, 7)
        self.layout.setColumnStretch(1, 3)
        self.layout.setColumnStretch(2, 3)
        self.layout.setColumnStretch(3, 1)
        self.layout.setContentsMargins(0, 2, 0, 2)

        icon_height = self.lbl_label.height() * 8 / 15
        icon_size = QSize(icon_height, icon_height)
        self.btn_read_slots.setIconSize(icon_size)
        self.btn_read.setIconSize(icon_size)
        self.btn_poke.setIconSize(icon_size)
        btn_size = QSize(icon_height * 1.5, icon_height * 1.5)
        self.btn_read.setFixedSize(btn_size)
        self.btn_poke.setFixedSize(btn_size)

        self.updateUI()

    def updateUI(self):
        # Disable editing if cache missing
        if not (0 <= self.cur_slot_idx < len(
                self.slots_cache)) or self.cur_slot_bytes is None:
            self.cur_slot_idx = -1
            self.cur_slot_bytes = None
            self.cmb_slots.setDisabled(True)
            self.cmb_skills_a.setDisabled(True)
            self.cmb_skills_b.setDisabled(True)
            self.cmb_skills_c.setDisabled(True)
            self.cmb_incr_a.setDisabled(True)
            self.cmb_incr_b.setDisabled(True)
            self.cmb_incr_c.setDisabled(True)
            self.cmb_augments.setDisabled(True)
            self.txt_raw.setDisabled(True)
            return
        else:
            self.cmb_slots.setDisabled(False)
            self.cmb_skills_a.setDisabled(False)
            self.cmb_skills_b.setDisabled(False)
            self.cmb_skills_c.setDisabled(False)
            self.cmb_incr_a.setDisabled(False)
            self.cmb_incr_b.setDisabled(False)
            self.cmb_incr_c.setDisabled(False)
            self.cmb_augments.setDisabled(False)
            self.txt_raw.setDisabled(False)

        # Validate current slot's raw bytes
        try:
            (gear_id_bytes, index, post_index, skill_a_id, skill_a_incr,
             skill_b_id, skill_b_incr, skill_c_id, skill_c_incr, augment_a_id,
             augment_b_id,
             augment_c_id) = parse_gear_bytes(self.cur_slot_bytes)
            cur_slot_words = struct.unpack('>IIIIII', self.cur_slot_bytes)
            num_augments = (augment_a_id != 0xFFFF) + (
                augment_b_id != 0xFFFF) + (augment_c_id != 0xFFFF)
        except ValueError, e:
            self.log.emit('Gear parsing failed: %s' % e.what(), 'red')
            return

        # Update UI
        self.txt_raw.setDisabled(False)
        self.txt_raw.editingFinished.disconnect()
        self.txt_raw.setText('%08X %08X %08X %08X %08X %08X' % cur_slot_words)
        self.txt_raw.editingFinished.connect(self.onChangeRaw)

        self.cmb_skills_a.setDisabled(False)
        self.cmb_skills_a.currentIndexChanged[str].disconnect()
        try:
            skill_name = self.id2skill[skill_a_id]
            skill_idx = self.skill_names.index(skill_name)
            self.cmb_skills_a.setCurrentIndex(skill_idx)
        except (KeyError, ValueError):
            self.cmb_skills_a.lineEdit().setText(
                GearEntriesFrame.MISSING_SKILL_NAME)
        self.cmb_skills_a.currentIndexChanged[str].connect(self.onChangeSkillA)

        self.cmb_skills_b.setDisabled(False)
        self.cmb_skills_b.currentIndexChanged[str].disconnect()
        try:
            skill_name = self.id2skill[skill_b_id]
            skill_idx = self.skill_names.index(skill_name)
            self.cmb_skills_b.setCurrentIndex(skill_idx)
        except (KeyError, ValueError):
            self.cmb_skills_b.lineEdit().setText(
                GearEntriesFrame.MISSING_SKILL_NAME)
        self.cmb_skills_b.currentIndexChanged[str].connect(self.onChangeSkillB)

        self.cmb_skills_c.setDisabled(False)
        self.cmb_skills_c.currentIndexChanged[str].disconnect()
        try:
            skill_name = self.id2skill[skill_c_id]
            skill_idx = self.skill_names.index(skill_name)
            self.cmb_skills_c.setCurrentIndex(skill_idx)
        except (KeyError, ValueError):
            self.cmb_skills_c.lineEdit().setText(
                GearEntriesFrame.MISSING_SKILL_NAME)
        self.cmb_skills_c.currentIndexChanged[str].connect(self.onChangeSkillC)

        self.cmb_incr_a.setDisabled(False)
        self.cmb_incr_a.currentIndexChanged[int].disconnect()
        self.cmb_incr_a.setCurrentIndex(skill_a_incr)
        self.cmb_incr_a.currentIndexChanged[int].connect(self.onChangeIncrA)

        self.cmb_incr_b.setDisabled(False)
        self.cmb_incr_b.currentIndexChanged[int].disconnect()
        self.cmb_incr_b.setCurrentIndex(skill_b_incr)
        self.cmb_incr_b.currentIndexChanged[int].connect(self.onChangeIncrB)

        self.cmb_incr_c.setDisabled(False)
        self.cmb_incr_c.currentIndexChanged[int].disconnect()
        self.cmb_incr_c.setCurrentIndex(skill_c_incr)
        self.cmb_incr_c.currentIndexChanged[int].connect(self.onChangeIncrC)

        self.cmb_augments.setDisabled(False)
        self.cmb_augments.currentIndexChanged[int].disconnect()
        self.cmb_augments.setCurrentIndex(num_augments)
        self.cmb_augments.currentIndexChanged[int].connect(
            self.onChangeAugments)
예제 #11
0
class Printing(preferences.Group):
    def __init__(self, page):
        super(Printing, self).__init__(page)

        layout = QGridLayout(spacing=1)
        self.setLayout(layout)

        self.messageLabel = QLabel(wordWrap=True)
        self.printCommandLabel = QLabel()
        self.printCommand = widgets.urlrequester.UrlRequester()
        self.printCommand.setFileMode(QFileDialog.ExistingFile)
        self.printCommand.changed.connect(page.changed)
        self.printDialogCheck = QCheckBox(toggled=page.changed)
        self.resolutionLabel = QLabel()
        self.resolution = QComboBox(editable=True,
                                    editTextChanged=page.changed)
        self.resolution.addItems("300 600 1200".split())
        self.resolution.lineEdit().setInputMask("9000")

        layout.addWidget(self.messageLabel, 0, 0, 1, 2)
        layout.addWidget(self.printCommandLabel, 1, 0)
        layout.addWidget(self.printCommand, 1, 1)
        layout.addWidget(self.printDialogCheck, 2, 0, 1, 2)
        layout.addWidget(self.resolutionLabel, 3, 0)
        layout.addWidget(self.resolution, 3, 1)

        app.translateUI(self)

    def translateUI(self):
        self.setTitle(_("Printing Music"))
        self.messageLabel.setText(
            _("Here you can enter a command to print a PDF or PostScript file. "
              "See the Help page for more information about printing music."))
        self.printCommandLabel.setText(_("Printing command:"))
        self.printCommand.setToolTip('<qt>' + _(
            "The printing command is used to print a PostScript or PDF file. "
            "On Linux you don't need this, but on Windows and Mac OS X you can "
            "provide a command to avoid that PDF documents are being printed "
            "using raster images, which is less optimal.\n"
            "<code>$pdf</code> gets replaced with the PDF filename, or alternatively, "
            "<code>$ps</code> is replaced with the PostScript filename. "
            "<code>$printer</code> is replaced with the printer's name to use."
        ))
        self.printDialogCheck.setText(_("Use Frescobaldi's print dialog"))
        self.printDialogCheck.setToolTip('<qt>' + _(
            "If enabled, Frescobaldi will show the print dialog and create a "
            "PDF or PostScript document containing only the selected pages "
            "to print. Otherwise, the command is called directly and is expected "
            "to show a print dialog itself."))
        self.resolutionLabel.setText(_("Resolution:"))
        self.resolution.setToolTip(
            _("Set the resolution if Frescobaldi prints using raster images."))

    def loadSettings(self):
        s = QSettings()
        s.beginGroup("helper_applications")
        self.printCommand.setPath(s.value("printcommand", "", type("")))
        self.printDialogCheck.setChecked(
            s.value("printcommand/dialog", False, bool))
        with qutil.signalsBlocked(self.resolution):
            self.resolution.setEditText(
                format(s.value("printcommand/dpi", 300, int)))

    def saveSettings(self):
        s = QSettings()
        s.beginGroup("helper_applications")
        s.setValue("printcommand", self.printCommand.path())
        s.setValue("printcommand/dialog", self.printDialogCheck.isChecked())
        s.setValue("printcommand/dpi", int(self.resolution.currentText()))
예제 #12
0
class Widget(QWidget):
    def __init__(self, dockwidget):
        super(Widget, self).__init__(dockwidget)
        self._document = None
        self._fileSelector = QComboBox(editable=True, insertPolicy=QComboBox.NoInsert)
        widgets.drag.ComboDrag(self._fileSelector).role = Qt.UserRole
        self._fileSelector.lineEdit().setReadOnly(True)
        self._fileSelector.lineEdit().setFocusPolicy(Qt.NoFocus)
        self._stopButton = QToolButton()
        self._playButton = QToolButton()
        self._timeSlider = QSlider(Qt.Horizontal, tracking=False,
            singleStep=500, pageStep=5000, invertedControls=True)
        self._display = Display()
        self._tempoFactor = QSlider(Qt.Vertical, minimum=-50, maximum=50,
            singleStep=1, pageStep=5)
        
        grid = QGridLayout(spacing=0)
        self.setLayout(grid)
        
        grid.addWidget(self._fileSelector, 0, 0, 1, 3)
        grid.addWidget(self._stopButton, 1, 0)
        grid.addWidget(self._playButton, 1, 1)
        grid.addWidget(self._timeSlider, 1, 2)
        grid.addWidget(self._display, 2, 0, 1, 3)
        grid.addWidget(self._tempoFactor, 0, 3, 3, 1)
        
        # size policy of combo
        p = self._fileSelector.sizePolicy()
        p.setHorizontalPolicy(QSizePolicy.Ignored)
        self._fileSelector.setSizePolicy(p)
        
        # size policy of combo popup
        p = self._fileSelector.view().sizePolicy()
        p.setHorizontalPolicy(QSizePolicy.MinimumExpanding)
        self._fileSelector.view().setSizePolicy(p)

        self._player = player.Player()
        self._outputCloseTimer = QTimer(interval=60000, singleShot=True,
            timeout=self.closeOutput)
        self._timeSliderTicker = QTimer(interval=200, timeout=self.updateTimeSlider)
        self._fileSelector.activated[int].connect(self.slotFileSelected)
        self._tempoFactor.valueChanged.connect(self.slotTempoChanged)
        self._timeSlider.valueChanged.connect(self.slotTimeSliderChanged)
        self._timeSlider.sliderMoved.connect(self.slotTimeSliderMoved)
        self._player.beat.connect(self.updateDisplayBeat)
        self._player.time.connect(self.updateDisplayTime)
        self._player.stateChanged.connect(self.slotPlayerStateChanged)
        self.slotPlayerStateChanged(False)
        dockwidget.mainwindow().currentDocumentChanged.connect(self.loadResults)
        app.documentLoaded.connect(self.slotUpdatedFiles)
        app.jobFinished.connect(self.slotUpdatedFiles)
        app.aboutToQuit.connect(self.stop)
        midihub.aboutToRestart.connect(self.slotAboutToRestart)
        midihub.settingsChanged.connect(self.clearMidiSettings, -100)
        midihub.settingsChanged.connect(self.readMidiSettings)
        app.documentClosed.connect(self.slotDocumentClosed)
        app.translateUI(self)
        self.readMidiSettings()
        d = dockwidget.mainwindow().currentDocument()
        if d:
            self.loadResults(d)

    def translateUI(self):
        self._tempoFactor.setToolTip(_("Tempo"))
    
    def slotAboutToRestart(self):
        self.stop()
        self._player.set_output(None)
    
    def clearMidiSettings(self):
        """Called first when settings are changed."""
        self.stop()
        self._outputCloseTimer.stop()
        self._player.set_output(None)
        
    def readMidiSettings(self):
        """Called after clearMidiSettings(), and on first init."""
        pass
            
    def openOutput(self):
        """Called when playing starts. Ensures an output port is opened."""
        self._outputCloseTimer.stop()
        if not self._player.output():
            p = QSettings().value("midi/player/output_port", midihub.default_output(), type(""))
            o = midihub.output_by_name(p)
            if o:
                self._player.set_output(output.Output(o))
    
    def closeOutput(self):
        """Called when the output close timer fires. Closes the output."""
        self._player.set_output(None)
        
    def slotPlayerStateChanged(self, playing):
        ac = self.parentWidget().actionCollection
        # setDefaultAction also adds the action
        for b in self._stopButton, self._playButton:
            while b.actions():
                b.removeAction(b.actions()[0])
        if playing:
            self._timeSliderTicker.start()
            self._stopButton.setDefaultAction(ac.midi_stop)
            self._playButton.setDefaultAction(ac.midi_pause)
        else:
            self._timeSliderTicker.stop()
            self.updateTimeSlider()
            self._stopButton.setDefaultAction(ac.midi_restart)
            self._playButton.setDefaultAction(ac.midi_play)
            # close the output if the preference is set
            if QSettings().value("midi/close_outputs", False, bool):
                self._outputCloseTimer.start()
        
    def play(self):
        """Starts the MIDI player, opening an output if necessary."""
        if not self._player.is_playing() and not self._player.has_events():
            self.restart()
        self.openOutput()
        if not self._player.output():
            self._display.statusMessage(_("No output found!"))
        self._player.start()
    
    def stop(self):
        """Stops the MIDI player."""
        self._player.stop()
    
    def restart(self):
        """Restarts the MIDI player.
        
        If another file is in the file selector, or the file was updated,
        the new file is loaded.
        
        """
        self._player.seek(0)
        self.updateTimeSlider()
        self._display.reset()
        if self._document:
            files = midifiles.MidiFiles.instance(self._document)
            index = self._fileSelector.currentIndex()
            if files and (files.song(index) is not self._player.song()):
                self.loadSong(index)
        
    def slotTempoChanged(self, value):
        """Called when the user drags the tempo."""
        # convert -50 to 50 to 0.5 to 2.0
        factor = 2 ** (value / 50.0)
        self._player.set_tempo_factor(factor)
        self._display.setTempo("{0}%".format(int(factor * 100)))
    
    def slotTimeSliderChanged(self, value):
        self._player.seek(value)
        self._display.setTime(value)
        if self._player.song():
            self._display.setBeat(*self._player.song().beat(value)[1:])
    
    def slotTimeSliderMoved(self, value):
        self._display.setTime(value)
        if self._player.song():
            self._display.setBeat(*self._player.song().beat(value)[1:])
    
    def updateTimeSlider(self):
        if not self._timeSlider.isSliderDown():
            with qutil.signalsBlocked(self._timeSlider):
                self._timeSlider.setMaximum(self._player.total_time())
                self._timeSlider.setValue(self._player.current_time())

    def updateDisplayBeat(self, measnum, beat, num, den):
        if not self._timeSlider.isSliderDown():
            self._display.setBeat(measnum, beat, num, den)
    
    def updateDisplayTime(self, time):
        if not self._timeSlider.isSliderDown():
            self._display.setTime(time)
    
    def slotUpdatedFiles(self, document):
        """Called when there are new MIDI files."""
        if document == self.parentWidget().mainwindow().currentDocument():
            self.loadResults(document)
    
    def loadResults(self, document):
        self._document = document
        files = midifiles.MidiFiles.instance(document)
        self._fileSelector.setModel(files.model())
        if files:
            self._fileSelector.setCurrentIndex(files.current)
            if not self._player.is_playing():
                self.loadSong(files.current)
    
    def loadSong(self, index):
        files = midifiles.MidiFiles.instance(self._document)
        self._player.set_song(files.song(index))
        m, s = divmod(self._player.total_time() // 1000, 60)
        name = self._fileSelector.currentText()
        self.updateTimeSlider()
        self._display.reset()
        self._display.statusMessage(
            _("midi lcd screen", "LOADED"), name,
            _("midi lcd screen", "TOTAL"), "{0}:{1:02}".format(m, s))
    
    def slotFileSelected(self, index):
        if self._document:
            self._player.stop()
            files = midifiles.MidiFiles.instance(self._document)
            if files:
                files.current = index
                self.restart()
    
    def slotDocumentClosed(self, document):
        if document == self._document:
            self._document = None
            self._fileSelector.clear()
            self._player.stop()
            self._player.clear()
            self.updateTimeSlider()
            self._display.reset()
예제 #13
0
class LDSControls(QFrame):
        
    STATIC_IMG = ('error_static.png','linz_static.png','busy_static.png','clean_static.png')
    ANIM_IMG   = ('error.gif','linz.gif','layer.gif','clean.gif')
    
    IMG_SPEED  = 100
    IMG_WIDTH  = 64
    IMG_HEIGHT = 64
    
    MAX_WD = 450
    
    GD_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../../bin/gdal/gdal-data'))
    STATUS = LU.enum('ERROR','IDLE','BUSY','CLEAN')
    
    def __init__(self,parent):
        super(LDSControls, self).__init__()
        self.parent = parent
        self.initConf()
        self.initEPSG()
        self.initUI()
        
    def initConf(self):
        '''Read files in conf dir ending in conf'''
        self.cflist = ConfigInitialiser.getConfFiles()
        #self.imgset = self.STATIC_IMG if ConfigWrapper().readDSProperty('Misc','indicator')=='static' else self.ANIM_IMG
        #self.imgset = self.STATIC_IMG if self.parent.confconn.tp.src.confwrap.readDSProperty('Misc','indicator')=='static' else self.ANIM_IMG
        sep = self.parent.confconn.reg.openEndPoint(self.parent.confconn.SRCNAME,self.parent.confconn.uconf)
        self.imgset = self.STATIC_IMG if sep.confwrap.readDSProperty('Misc','indicator')=='static' else self.ANIM_IMG
        self.parent.confconn.reg.closeEndPoint(self.parent.confconn.SRCNAME)
        
    def initEPSG(self):
        '''Read GDAL EPSG files, splitting by NZ(RSR) and RestOfTheWorld'''

        gcsf = gdal.FindFile('gdal','gcs.csv') 
        if not gcsf:
            gcsf = os.path.join(self.GD_PATH,'gcs.csv')
        pcsf = gdal.FindFile('gdal','pcs.csv') 
        if not pcsf: 
            pcsf = os.path.join(self.GD_PATH,'pcs.csv')
        gcs = ConfigInitialiser.readCSV(gcsf)
        pcs = ConfigInitialiser.readCSV(pcsf)

        self.nzlsr = [(e[0],e[0]+' - '+e[3]) for e in gcs if 'NZGD'     in e[1] or  'RSRGD'     in e[1]] \
                   + [(e[0],e[0]+' - '+e[1]) for e in pcs if 'NZGD'     in e[1] or  'RSRGD'     in e[1]]
        self.rowsr = [(e[0],e[0]+' - '+e[3]) for e in gcs if 'NZGD' not in e[1] and 'RSRGD' not in e[1]] \
                   + [(e[0],e[0]+' - '+e[1]) for e in pcs if 'NZGD' not in e[1] and 'RSRGD' not in e[1]]
                   
                   
    def initUI(self):
        
        # 0      1          2       3       4       5      6    7    8
        #'destname','lgselect','layer','uconf','group','epsg','fd','td','int'
        
        #self.rdest,rlgselect,self.rlayer,ruconf,self.rgroup,repsg,rfd,rtd,rint = readlist 
        
        QToolTip.setFont(QFont('SansSerif', 10))
        
        #labels
        destLabel = QLabel('Destination')
        lgLabel = QLabel('Group/Layer')
        epsgLabel = QLabel('EPSG')
        fromDateLabel = QLabel('From Date')
        toDateLabel = QLabel('To Date')
        confLabel = QLabel('User Config')
        
        self.view = QLabel() 
        self.view.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.view.setAlignment(Qt.AlignCenter)

        self.confcombo = QComboBox(self)
        self.confcombo.setToolTip('Enter your user config name (file) here')
        self.confcombo.addItems(self.cflist)
        self.confcombo.setEditable(False)
        #self.confcombo.currentIndexChanged.connect(self.doLGEditUpdate)
        
        #combos
        self.lgcombo = QComboBox(self)
        self.lgcombo.setMaximumWidth(self.MAX_WD)
        self.lgcombo.setDuplicatesEnabled(False)
        #self.lgcombo.setInsertPolicy(QComboBox.InsertAlphabetically)#?doesnt seem to work
        self.lgcombo.setToolTip('Select either Layer or Group entry')
        self.lgcombo.setEditable(False)
        self.sepindex = None
        #self.updateLGValues()
        
        self.epsgcombo = QComboBox(self)
        self.epsgcombo.setMaximumWidth(self.MAX_WD)
        self.epsgcombo.setToolTip('Setting an EPSG number here determines the output SR of the layer')  
        self.epsgcombo.addItems([i[1] for i in self.nzlsr])
        self.epsgcombo.insertSeparator(len(self.nzlsr))
        self.epsgcombo.addItems([i[1] for i in self.rowsr])
        self.epsgcombo.setEditable(True)
        self.epsgcombo.setEnabled(False)
        
        self.destlist = self.getConfiguredDestinations()
        self.destcombo = QComboBox(self)
        self.destcombo.setToolTip('Choose the desired output type')   
        self.destcombo.setEditable(False)
        self.destcombo.addItems(self.destlist)

        #date selection
        self.fromdateedit = QDateEdit()
        self.fromdateedit.setCalendarPopup(True)
        self.fromdateedit.setEnabled(False)
        
        self.todateedit = QDateEdit()
        self.todateedit.setCalendarPopup(True)
        self.todateedit.setEnabled(False)
        
        #check boxes
        self.epsgenable = QCheckBox()
        self.epsgenable.setCheckState(False)
        self.epsgenable.clicked.connect(self.doEPSGEnable)       
        
        self.fromdateenable = QCheckBox()
        self.fromdateenable.setCheckState(False)
        self.fromdateenable.clicked.connect(self.doFromDateEnable)
        
        self.todateenable = QCheckBox()
        self.todateenable.setCheckState(False) 
        self.todateenable.clicked.connect(self.doToDateEnable)
        
        self.progressbar = QProgressBar()
        self.progressbar.setRange(0,100)
        self.progressbar.setVisible(True)
        self.progressbar.setMinimumWidth(self.MAX_WD)
        
        
        #buttons        
        self.initbutton = QPushButton("waiting")
        self.initbutton.setToolTip('Initialise the Layer Configuration')
        self.initbutton.clicked.connect(self.doInitClickAction)
        
        self.cleanbutton = QPushButton("Clean")
        self.cleanbutton.setToolTip('Clean the selected layer/group from local storage')
        self.cleanbutton.clicked.connect(self.doCleanClickAction)
        
        self.replicatebutton = QPushButton("Replicate")
        self.replicatebutton.setToolTip('Execute selected replication')
        self.replicatebutton.clicked.connect(self.doReplicateClickAction)
        
        self.cancelbutton = QPushButton("Close")
        self.cancelbutton.setToolTip('Close the LDS Replicate application')       
        self.cancelbutton.clicked.connect(self.parent.close)


        #set dialog values using GPR
        self.updateGUIValues(self.parent.gvs)
        
        #set onchange here otherwise we get circular initialisation
        self.destcombo.currentIndexChanged.connect(self.doDestChanged)
        self.confcombo.currentIndexChanged.connect(self.doConfChanged)
        self.lgcombo.currentIndexChanged.connect(self.doLGComboChanged)

        self.setStatus(self.STATUS.IDLE)
        
        #grid
        grid = QGridLayout()
        grid.setSpacing(10)
        
        
        #placement section ------------------------------------
        #---------+---------+--------+---------+--------
        # dest LB |         | dest DD
        # grp LB  |         | grp DD
        # conf LB |         | conf DD
        # epsg L  | epsg CB | epsg DD
        # f dt L  | f dt CB | f dt DD
        # t td L  | t td CB | t td DD
        # icon    |       <- progress ->
        # layer B | <- . -> |repl B  | clean B | close B 
        #---------+---------+--------+---------+--------

        grid.addWidget(destLabel, 1, 0)
        grid.addWidget(self.destcombo, 1, 2)

        #grid.addWidget(layerLabel, 2, 0)
        grid.addWidget(lgLabel, 2, 0)
        grid.addWidget(self.lgcombo, 2, 2)
        
        grid.addWidget(confLabel, 3, 0)
        grid.addWidget(self.confcombo, 3, 2)
        
        #grid.addWidget(groupLabel, 4, 0)
        #grid.addWidget(self.groupEdit, 4, 2)
        
        grid.addWidget(epsgLabel, 5, 0)
        grid.addWidget(self.epsgenable, 5, 1)
        grid.addWidget(self.epsgcombo, 5, 2)

        grid.addWidget(fromDateLabel, 6, 0)
        grid.addWidget(self.fromdateenable, 6, 1)
        grid.addWidget(self.fromdateedit, 6, 2)
        
        grid.addWidget(toDateLabel, 7, 0)
        grid.addWidget(self.todateenable, 7, 1)
        grid.addWidget(self.todateedit, 7, 2)
        
        hbox3 = QHBoxLayout()
        hbox3.addWidget(self.view) 
        hbox3.addStretch(1)
        hbox3.addWidget(self.progressbar)

        #hbox3.addLayout(vbox2)
        #hbox3.addLayout(vbox3)
        
        hbox4 = QHBoxLayout()
        hbox4.addWidget(self.initbutton)
        hbox4.addStretch(1)
        hbox4.addWidget(self.replicatebutton)
        hbox4.addWidget(self.cleanbutton)
        hbox4.addWidget(self.cancelbutton)
        

        vbox = QVBoxLayout()
        #vbox.addStretch(1)
        vbox.addLayout(grid)
        vbox.addLayout(hbox3)
        vbox.addLayout(hbox4)
        
        self.setLayout(vbox)  
       
    #def setProgress(self,pct):
    #    self.progressbar.setValue(pct)
        
    def setStatus(self,status,message='',tooltip=None):
        '''Sets indicator icon and statusbar message'''
        self.parent.statusbar.showMessage(message)
        self.parent.statusbar.setToolTip(tooltip if tooltip else '')

        #progress
        loc = os.path.abspath(os.path.join(IMG_LOC,self.imgset[status]))
        #loc = os.path.abspath(os.path.join(os.path.dirname(__file__),self.parent.IMG_LOC,self.imgset[status]))
        self.progressbar.setVisible(status in (self.STATUS.BUSY, self.STATUS.CLEAN))
        
        #icon
        anim = QMovie(loc, QByteArray(), self)
        anim.setScaledSize(QSize(self.IMG_WIDTH,self.IMG_HEIGHT))
        anim.setCacheMode(QMovie.CacheAll)
        anim.setSpeed(self.IMG_SPEED)
        self.view.clear()
        self.view.setMovie(anim)
        anim.start()

        self.view.repaint()
        QApplication.processEvents(QEventLoop.AllEvents)

    def mainWindowEnable(self,enable=True):
        cons = (self.lgcombo, self.confcombo, self.destcombo, 
                self.initbutton, self.replicatebutton, self.cleanbutton, self.cancelbutton,
                self.epsgenable,self.fromdateenable,self.todateenable,
                self.parent.menubar)
        for c in cons:
            c.setEnabled(enable)
            
        if enable:
            self.epsgcombo.setEnabled(self.epsgenable.checkState())
            self.fromdateedit.setEnabled(self.fromdateenable.checkState())
            self.todateedit.setEnabled(self.todateenable.checkState())
        else:
            self.epsgcombo.setEnabled(False)
            self.fromdateedit.setEnabled(False)
            self.todateedit.setEnabled(False)
   
        QApplication.restoreOverrideCursor() if enable else QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) 

    def refreshLGCombo(self):
        '''Re index LG combobox since a refreshLG call (new dest?) will usually mean new groups'''
        self.lgcombo.clear()
        self.lgcombo.addItems([i[2] for i in self.parent.confconn.lglist])
        #NOTE the separator consumes an index, if not clearing the combobox selectively remove the old sepindex (assumes g preceeds l)
        #if self.sepindex:
        #    self.lgcombo.removeItem(self.sepindex)
        self.sepindex = [i[0] for i in self.parent.confconn.lglist].count(LORG.GROUP)
        self.lgcombo.insertSeparator(self.sepindex)
        
    def updateLGValues(self,uconf,lgval,dest):
        '''Sets the values displayed in the Layer/Group combo'''
        #because we cant seem to sort combobox entries and want groups at the top, clear and re-add
        #TRACE#        
        #pdb.set_trace()
        sf = None
        try:
            self.parent.confconn.initConnections(uconf,lgval,dest)
        except Exception as e:
            sf=1
            ldslog.error('Error Updating UC Values. '+str(e))
            
        if sf:
            self.setStatus(self.STATUS.ERROR,'Error Updating UC Values', str(e))
        else:
            self.setStatus(self.STATUS.IDLE)
            
        self.refreshLGCombo()
        
    def centre(self):
        
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())
        
    
    def gprParameters(self,rdest):
        '''Zip default and GPR values'''
        return [x if LU.assessNone(x) else y for x,y in zip(self.parent.gpr.readsec(rdest),self.parent.DEF_RVALS[1:])]
    
    def getLCE(self,ln):
        '''Read layer parameters'''
        dep = self.parent.confconn.reg.openEndPoint(self.parent.confconn.destname,self.parent.confconn.uconf)
        #sep = self.parent.confconn.reg.openEndPoint('WFS',self.parent.confconn.uconf)
        self.parent.confconn.reg.setupLayerConfig(self.parent.confconn.tp,None,dep,initlc=False)
        lce = dep.getLayerConf().readLayerParameters(ln)
        #self.parent.confconn.reg.closeEndPoint('WFS')
        self.parent.confconn.reg.closeEndPoint(self.parent.confconn.destname)
        sep,dep = None,None
        return lce
    
    
    def doDestChanged(self):
        '''Read the destname parameter and fill dialog with matching GPR values'''
        rdest = str(self.destlist[self.destcombo.currentIndex()])
        rvals = self.gprParameters(rdest)
        self.updateGUIValues([rdest]+rvals)    
        
        
    def doConfChanged(self):
        '''Read the user conf parameter and fill dialog with matching GPR values'''
        rdest = str(self.destlist[self.destcombo.currentIndex()])
        rlg,_,rep,rfd,rtd = self.gprParameters(rdest)
        ruc = str(self.cflist[self.confcombo.currentIndex()])
        self.updateGUIValues((rdest,rlg,ruc,rep,rfd,rtd))
        
        
    def doLGComboChanged(self):
        '''Read the layer/group value and change epsg to layer or gpr match'''
        #get a matching LG entry and test whether its a layer or group
        #lgi = self.parent.confconn.getLayerGroupIndex(self.lgcombo.currentText().toUtf8().data())
        lgi = self.parent.confconn.getLayerGroupIndex(LQ.readWidgetText(self.lgcombo.currentText()))
        #lgi can be none if we init a new group, in which case we use the GPR value
        if lgi:
            lge = self.parent.confconn.lglist[lgi]
            lce = self.getLCE(lge[1]) if lge[0]==LORG.LAYER else None
        else:
            lce = None
        
        #look for filled layer conf epsg OR use prefs stored in gpr
        if lce and LU.assessNone(lce.epsg):
            epsgval = lce.epsg
        else:
            rdest = str(self.destlist[self.destcombo.currentIndex()])
            _,_,epsgval,_,_ = self.gprParameters(rdest)
        epsgindex = [i[0] for i in self.nzlsr+[(0,0)]+self.rowsr].index(epsgval)
        if self.epsgcombo.currentIndex() != epsgindex:
            self.epsgcombo.setCurrentIndex(int(epsgindex))

        
    def updateGUIValues(self,readlist):
        '''Fill dialog values from provided list'''
        #TODO. Remove circular references when setCurrentIndex() triggers do###Changed()
        #Read user input
        rdest,self.rlgval,ruconf,repsg,rfd,rtd = readlist
        
        #--------------------------------------------------------------------
        
        #Destination Menu
        selecteddest = LU.standardiseDriverNames(rdest)
        if selecteddest not in self.destlist:
            self.destlist = self.getConfiguredDestinations()
            self.destcombo.addItem(selecteddest)
        destindex = self.destlist.index(selecteddest) if selecteddest else 0
        
        if self.destcombo.currentIndex() != destindex:
            self.destcombo.setCurrentIndex(destindex)
        
        #InitButton
        self.initbutton.setText('Layer Select')
        
        #Config File
        confindex = 0
        if LU.assessNone(ruconf):
            ruconf = ruconf.split('.')[0]
            if ruconf not in self.cflist:
                self.cflist += [ruconf,]
                self.confcombo.addItem(ruconf)
            confindex = self.cflist.index(ruconf)
            
        if self.confcombo.currentIndex() != confindex:
            self.confcombo.setCurrentIndex(confindex)
        #self.confEdit.setText(ruconf if LU.assessNone(ruconf) else '')
        
        #Layer/Group Selection
        self.updateLGValues(ruconf,self.rlgval,rdest)
        lgindex = None
        if LU.assessNone(self.rlgval):
            #index of list value
            lgindex = self.parent.confconn.getLayerGroupIndex(self.rlgval,col=1)
            
        if LU.assessNone(lgindex):
            #advance by 1 for sep
            lgindex += 1 if lgindex>self.sepindex else 0 
        else:
            #using the separator index sets the combo to blank
            lgindex = self.sepindex
        if self.lgcombo.currentIndex() != lgindex:
            self.lgcombo.setCurrentIndex(lgindex)
        #self.doLGEditUpdate()
        
        #EPSG
        #                                user > layerconf
        #useepsg = LU.precedence(repsg, lce.epsg if lce else None, None)
        epsgindex = [i[0] for i in self.nzlsr+[(None,None)]+self.rowsr].index(repsg)
        if self.epsgcombo.currentIndex() != epsgindex:
            self.epsgcombo.setCurrentIndex(epsgindex)
            
        #epsgedit = self.epsgcombo.lineEdit()
        #epsgedit.setText([e[1] for e in self.nzlsr+self.rowsr if e[0]==repsg][0])
        
        #epsgedit.setText([e for e in self.nzlsr+self.rowsr if re.match('^\s*(\d+).*',e).group(1)==repsg][0])
        
        #To/From Dates
        if LU.assessNone(rfd):
            self.fromdateedit.setDate(QDate(int(rfd[0:4]),int(rfd[5:7]),int(rfd[8:10])))
        else:
            early = DataStore.EARLIEST_INIT_DATE
            self.fromdateedit.setDate(QDate(int(early[0:4]),int(early[5:7]),int(early[8:10])))
            
        if LU.assessNone(rtd):
            self.todateedit.setDate(QDate(int(rtd[0:4]),int(rtd[5:7]),int(rtd[8:10]))) 
        else:
            today = DataStore.getCurrent()
            self.todateedit.setDate(QDate(int(today[0:4]),int(today[5:7]),int(today[8:10])))
            
        #Internal/External CheckBox
#        if LU.assessNone(rint):
#            self.internalTrigger.setChecked(rint.lower()==DataStore.CONF_INT)
#        else:
#            self.internalTrigger.setChecked(DataStore.DEFAULT_CONF==DataStore.CONF_INT)
        
        
    def getConfiguredDestinations(self):
        defml = ['',]+DataStore.DRIVER_NAMES.values()
        return [d for d in self.parent.gpr.getDestinations() if d in defml]
        
    def doEPSGEnable(self):
        self.epsgcombo.setEnabled(self.epsgenable.isChecked())
        
    def doFromDateEnable(self):
        self.fromdateedit.setEnabled(self.fromdateenable.isChecked())
          
    def doToDateEnable(self):
        self.todateedit.setEnabled(self.todateenable.isChecked())  
          
    def readParameters(self):
        '''Read values out of dialogs'''
        destination = LU.assessNone(str(self.destlist[self.destcombo.currentIndex()]))
        #lgindex = self.parent.confconn.getLayerGroupIndex(self.lgcombo.currentText().toUtf8().data())
        lgindex = self.parent.confconn.getLayerGroupIndex(LQ.readWidgetText(self.lgcombo.currentText()))
        #NB need to test for None explicitly since zero is a valid index
        lgval = self.parent.confconn.lglist[lgindex][1] if LU.assessNone(lgindex) else None       
        #uconf = LU.standardiseUserConfigName(str(self.confcombo.lineEdit().text()))
        #uconf = str(self.confcombo.lineEdit().text())
        uconf = str(self.cflist[self.confcombo.currentIndex()])
        ee = self.epsgenable.isChecked()
        epsg = None if ee is False else re.match('^\s*(\d+).*',str(self.epsgcombo.lineEdit().text())).group(1)
        fe = self.fromdateenable.isChecked()
        te = self.todateenable.isChecked()
        fd = None if fe is False else str(self.fromdateedit.date().toString('yyyy-MM-dd'))
        td = None if te is False else str(self.todateedit.date().toString('yyyy-MM-dd'))
        
        return destination,lgval,uconf,epsg,fe,te,fd,td
    
    def doInitClickAction(self):
        '''Initialise the LC on LC-button-click, action'''
        try:
            try:
                self.setStatus(self.STATUS.BUSY,'Opening Layer-Config Editor')  
                self.progressbar.setValue(0)
                self.parent.runLayerConfigAction()
            finally:
                self.setStatus(self.STATUS.IDLE,'Ready')
        except Exception as e:
            self.setStatus(self.STATUS.ERROR,'Error in Layer-Config',str(sys.exc_info()))#e))
        
    def doCleanClickAction(self):
        '''Set clean anim and run clean'''
        #lgo = self.lgcombo.currentText().toUtf8().data()
        lgo = LQ.readWidgetText(self.lgcombo.currentText())
        
        try:
            self.setStatus(self.STATUS.CLEAN,'Running Clean '+lgo)
            self.progressbar.setValue(0)
            self.runReplicationScript(True)
        except Exception as e:
            self.setStatus(self.STATUS.ERROR,'Failed Clean of '+lgo,str(sys.exc_info()))#e))
        
    def doReplicateClickAction(self):
        '''Set busy anim and run repl'''
        lgo = self.lgcombo.currentText()#.toUtf8().data()#only used for error messages
        try:
            self.setStatus(self.STATUS.BUSY,'Running Replicate '+lgo)
            self.progressbar.setValue(0)
            self.runReplicationScript(False)
            ldslog.debug('TRPfinish')
        except Exception as e:
            self.setStatus(self.STATUS.ERROR,'Failed Replication of '+lgo,str(sys.exc_info()))#e))

    def runReplicationScript(self,clean=False):
        '''Run the layer/group repliction script'''
        destination,lgval,uconf,epsg,fe,te,fd,td = self.readParameters()
        uconf_path = LU.standardiseUserConfigName(uconf)
        destination_path = LU.standardiseLayerConfigName(destination)
        destination_driver = LU.standardiseDriverNames(destination)

        if not os.path.exists(uconf_path):
            self.userConfMessage(uconf_path)
            return
        elif not MainFileReader(uconf_path).hasSection(destination_driver):
            self.userConfMessage(uconf_path,destination_driver)
            return
        #-----------------------------------------------------
        #'destname','layer','uconf','group','epsg','fd','td','int'
     
        self.parent.gpr.write((destination_driver,lgval,uconf,epsg,fd,td))        
        ldslog.info(u'dest={0}, lg={1}, conf={2}, epsg={3}'.format(destination_driver,lgval,uconf,epsg))
        ldslog.info('fd={0}, td={1}, fe={2}, te={3}'.format(fd,td,fe,te))
        lgindex = self.parent.confconn.getLayerGroupIndex(lgval,col=1)
        #lorg = self.parent.confconn.lglist[lgindex][0]
        #----------don't need lorg in TP anymore but it is useful for sorting/counting groups
        #self.parent.confconn.tp.setLayerOrGroup(lorg)
        self.parent.confconn.tp.setLayerGroupValue(lgval)
        if self.fromdateenable.isChecked(): self.parent.confconn.tp.setFromDate(fd)
        if self.todateenable.isChecked(): self.parent.confconn.tp.setToDate(td)
        self.parent.confconn.tp.setUserConf(uconf)
        if self.epsgenable: self.parent.confconn.tp.setEPSG(epsg)
        
        #because clean state persists in TP
        if clean:
            self.parent.confconn.tp.setCleanConfig()
        else:
            self.parent.confconn.tp.clearCleanConfig()
        #(re)initialise the data source since uconf may have changed
        #>>#self.parent.confconn.tp.src = self.parent.confconn.initSourceWrapper()
        #--------------------------
        ###ep = self.parent.confconn.reg.openEndPoint(self.parent.confconn.destname,self.parent.confconn.uconf)
        
        ###self.parent.confconn.reg.closeEndPoint(self.parent.confconn.destname)
        ###ep = None
        #Open ProcessRunner and run with TP(proc)/self(gui) instances
        #HACK temp add of dest_drv to PR call
        try:
            #TODO. Test for valid LC first
            self.tpr = ProcessRunner(self,destination_driver)
        except Exception as e:
            ldslog.error('Cannot create ProcessRunner {}. NB Possible missing Layer Config {}'.format(str(e),destination_path))
            self.layerConfMessage(destination_path)
            return
        #If PR has been successfully created we must vave a valid dst    
        ldslog.debug('TRPstart')
        self.tpr.start()
        
#     def quitProcessRunner(self):
#         self.tpr.join()
#         self.tpr.quit()
#         self.trp = None

        
    def userConfMessage(self,uconf,secname=None):
        ucans = QMessageBox.warning(self, 'User Config Missing/Incomplete', 
                                'Specified User-Config file, '+str(uconf)+' does not exist' if secname is None else 'User-Config file does not contain '+str(secname)+' section', 
                                'Back','Initialise User Config')
        if not ucans:
            #Retry
            ldslog.warn('Retry specifying UC')
            #self.confcombo.setCurrentIndex(0)
            return
        #Init
        ldslog.warn('Reset User Config Wizard')
        self.parent.runWizardAction()


    def layerConfMessage(self,dest):
        lcans = QMessageBox.warning(self, 'Layer Config Missing', 
                                'Required Layer-Config file, '+str(dest)+' does not exist', 
                                'Back','Run Layer Select')
        if not lcans:
            #Retry
            ldslog.warn('Retry specifying LC')
            #self.destcombo.setCurrentIndex(0)
            return
        #Init
        ldslog.warn('Reset Layer Config')
        self.doInitClickAction()
예제 #14
0
class ItemEntriesFrame(QFrame):
  read_block = pyqtSignal(int, int) # start_addr, num_bytes
  poke_block = pyqtSignal(int, QByteArray, bool) # start_addr, raw_bytes, is_ascii
  log = pyqtSignal(str, str) # msg, color

  MISSING_ITEM_NAME = '[NOT IN DB]'

  def __init__(self, type_val, addr_start, addr_end, label, id2name, idx2id, names, parent=None):
    super(ItemEntriesFrame, self).__init__(parent)
    self.type_val = type_val
    self.addr_start = addr_start
    self.addr_end = addr_end
    self.max_num_slots = (addr_end - addr_start)/3/4 + 1
    self.label = label
    self.id2name = id2name
    self.idx2id = idx2id
    self.names = names

    self.slots_cache = [] # tuple: (addr_hex, addr_val, slot_number, cur_val)
    self.cur_slot_idx = -1

    self.lbl_label = QLabel(self.label, self)

    self.btn_read_slots = QPushButton(' Cache Slots', self)
    self.btn_read_slots.setIcon(QIcon('img/flaticon/data110.png'))
    self.btn_read_slots.setStyleSheet('background-color: white')
    self.btn_read_slots.clicked.connect(self.onReadSlots)

    self.btn_search_cache = QPushButton(' Search ID', self)
    self.btn_search_cache.setIcon(QIcon('img/flaticon/magnifier13.png'))
    self.btn_search_cache.setToolTip('Find slot in cache with specified item ID')
    self.btn_search_cache.setStyleSheet('background-color: white')
    self.btn_search_cache.clicked.connect(self.onSearchCacheForID)

    self.btn_read = QPushButton(self)
    self.btn_read.setIcon(QIcon('img/flaticon/open135.png'))
    self.btn_read.setToolTip('Read item slot value from memory')
    self.btn_read.setStyleSheet('background-color: white')
    self.btn_read.clicked.connect(self.onReadSlot)

    self.cmb_slots = QComboBox(self)
    self.cmb_slots.setToolTip('')
    self.cmb_slots.setStyleSheet('background-color: white')
    self.cmb_slots.currentIndexChanged[int].connect(self.onChangeSlot)
    self.cmb_slots.setDisabled(True)

    self.cmb_names = QComboBox(self)
    self.cmb_names.setEditable(True)
    self.cmb_names.addItems(self.names)
    self.cmb_names.lineEdit().setText(ItemEntriesFrame.MISSING_ITEM_NAME)
    self.cmb_names.currentIndexChanged[int].connect(self.fetchID)

    self.txt_id = QLineEdit(self)
    self.txt_id.setPlaceholderText('ID (hex)')
    self.txt_id.setMaxLength(3)
    self.txt_id.textChanged.connect(self.fetchName)

    self.btn_rename = QPushButton(' Fix Name', self)
    self.btn_rename.setIcon(QIcon('img/flaticon/cloud-storage3.png'))
    self.btn_rename.setToolTip('Add/Correct Item Name for %s (type: %02X)' % (self.label, type_val))
    self.btn_rename.setStyleSheet('background-color: white')
    self.btn_rename.clicked.connect(self.onRename)

    self.txt_amount = QLineEdit(self)
    self.txt_amount.setPlaceholderText('Amount')
    self.txt_amount.setMaxLength(3)

    self.btn_poke = QPushButton(self)
    self.btn_poke.setIcon(QIcon('img/flaticon/draw39.png'))
    self.btn_poke.setToolTip('Poke new value for item slot')
    self.btn_poke.setStyleSheet('background-color: white')
    self.btn_poke.clicked.connect(self.onPokeSlot)

    self.layout = QGridLayout(self)
    self.layout.addWidget(self.lbl_label, 0, 0)
    self.layout.addWidget(self.btn_read_slots, 0, 1)
    self.layout.addWidget(self.btn_search_cache, 0, 2)
    self.layout.addWidget(self.cmb_slots, 0, 3)
    self.layout.addWidget(self.btn_read, 0, 4)
    self.layout.addWidget(self.cmb_names, 1, 0)
    self.layout.addWidget(self.txt_id, 1, 1)
    self.layout.addWidget(self.btn_rename, 1, 2)
    self.layout.addWidget(self.txt_amount, 1, 3)
    self.layout.addWidget(self.btn_poke, 1, 4)

    self.layout.setColumnStretch(0, 7)
    self.layout.setColumnStretch(1, 3)
    self.layout.setColumnStretch(2, 3)
    self.layout.setColumnStretch(3, 3)
    self.layout.setColumnStretch(4, 1)
    self.layout.setContentsMargins(0, 2, 0, 2)

    icon_height = self.lbl_label.height()*8/15
    icon_size = QSize(icon_height, icon_height)
    self.btn_read_slots.setIconSize(icon_size)
    self.btn_search_cache.setIconSize(icon_size)
    self.btn_rename.setIconSize(icon_size)
    self.btn_read.setIconSize(icon_size)
    self.btn_poke.setIconSize(icon_size)
    btn_size = QSize(icon_height*1.5, icon_height*1.5)
    self.btn_read.setFixedSize(btn_size)
    self.btn_poke.setFixedSize(btn_size)

    self.updateUI()

  def updateUI(self):
    # Disable editing if cache missing
    if not (0 <= self.cur_slot_idx < len(self.slots_cache)):
      self.cmb_names.lineEdit().setText(ItemEntriesFrame.MISSING_ITEM_NAME)
      self.cmb_names.setDisabled(True)
      self.txt_id.setText('')
      self.txt_id.setDisabled(True)
      return

    # Validate current value
    cur_val = self.slots_cache[self.cur_slot_idx][3]
    (type_val, id_val, amount) = parse_item_word(cur_val)
    if type_val != self.type_val and type_val != 0:
      self.log.emit('Coding error: val(%s)=%08X, type (%02X) != expected (%02X)' %
                    (self.cur_addr_str, cur_val, type_val, self.type_val), 'red')
      return

    # Update UI
    self.txt_id.setDisabled(False)
    self.txt_id.setText('%03X' % id_val)
    self.cmb_names.setDisabled(False)
    self.fetchName()
    self.txt_amount.setText(str(amount))

  def setAlternateBGColor(self):
    self.setStyleSheet('ItemEntriesFrame { background-color:rgb(248,248,248) }')

  @pyqtSlot()
  def onRename(self):
    id_txt = str(self.txt_id.text())
    if len(id_txt) != 3:
      return
    dialog = FixItemNameDialog('%02X' % self.type_val, id_txt, self)
    dialog.log.connect(self.log)
    dialog.exec_()

  @pyqtSlot()
  def onReadSlots(self):
    self.read_block.emit(self.addr_start, self.max_num_slots*4*3)

  @pyqtSlot(int, int, QByteArray)
  def onBlockRead(self, addr_start, num_bytes, raw_bytes):
    # Determine whether block has cache or single slot
    if addr_start == self.addr_start and num_bytes == self.max_num_slots*4*3:
      self.onCacheRead(addr_start, raw_bytes)
    elif num_bytes == 4: # Assume read slot
      # Ignore if no cache
      if not (0 <= self.cur_slot_idx < len(self.slots_cache)):
        return
      self.onSlotRead(addr_start, raw_bytes)

  def onCacheRead(self, addr_start, raw_bytes):
    slot_bytes = str(raw_bytes)
    self.slots_cache = []
    slots_txt = []
    for slot_i in xrange(self.max_num_slots):
      byte_offset = slot_i*3*4
      cur_slot_bytes = slot_bytes[byte_offset:(byte_offset+4)]
      if len(cur_slot_bytes) != 4:
        continue
      cur_slot_val = struct.unpack('>I', cur_slot_bytes)[0]
      (type_val, id_val, amount) = parse_item_word(cur_slot_val)
      if type_val == 0 or amount == 0:
        continue
      elif type_val != self.type_val:
        self.log.emit('val(%08X)=%08X, type_val(%02X) unexpected(%02X)' %
                      (addr_start+byte_offset, cur_slot_val, type_val, self.type_val),
                      'red')
        continue
      else:
        addr_val = addr_start + byte_offset
        addr_hex = '%08X' % addr_val
        slot_number = slot_i+1
        self.slots_cache.append((addr_hex, addr_val, slot_number, cur_slot_val))
        slots_txt.append('Slot %03d' % slot_number)

    # Update UI
    self.log.emit('Found %d %s slots in memory' % (len(self.slots_cache), self.label), 'black')
    self.cmb_slots.clear()
    if len(self.slots_cache) <= 0:
      self.cmb_slots.setDisabled(True)
      self.cur_slot_idx = -1
    else:
      self.cmb_slots.setDisabled(False)
      self.cmb_slots.addItems(slots_txt)
      self.cur_slot_idx = 0
    self.updateUI()

  def onSlotRead(self, addr_word, raw_bytes):
    addr_cur_slot = self.slots_cache[self.cur_slot_idx][1]
    if addr_word == addr_cur_slot:
      cur_cache = self.slots_cache[self.cur_slot_idx]
      new_val = struct.unpack('>I', str(raw_bytes))[0]
      new_cache = (cur_cache[0], cur_cache[1], cur_cache[2], new_val)
      self.slots_cache[self.cur_slot_idx] = new_cache
      self.updateUI()
    else: # Update cached value of other slots
      addr_first_slot = self.slots_cache[0][1]
      addr_last_slot = self.slots_cache[-1][1]
      if (addr_first_slot <= addr_word <= addr_last_slot) and \
         ((addr_word - addr_first_slot) % 12 == 0):
        for slot_i in xrange(len(self.slots_cache)):
          addr_cur_slot = self.slots_cache[slot_i][1]
          if addr_word == addr_cur_slot:
            cur_cache = self.slots_cache[slot_i]
            new_val = struct.unpack('>I', str(raw_bytes))[0]
            new_cache = (cur_cache[0], cur_cache[1], cur_cache[2], new_val)
            self.slots_cache[slot_i] = new_cache
            return

  @pyqtSlot()
  def onSearchCacheForID(self):
    # Stop if no cache
    if not (0 <= self.cur_slot_idx < len(self.slots_cache)):
      self.log.emit('Must cache slots before searching', 'red')
      return

    # Fetch and validate target ID
    try:
      target_id_val = int(str(self.txt_id.text()), 16)
      if target_id_val < 0 or target_id_val > Item.MAX_ID_VAL:
        self.log.emit('Item ID out of [0, 0x%03X] range' % Item.MAX_ID_VAL)
        return
    except ValueError:
      self.log.emit('Failed to parse item ID, expecting XXX', 'red')
      return

    # Search ID in cache
    for cand_slot_idx in xrange(len(self.slots_cache)):
      (type_val, id_val, amount) = parse_item_word(self.slots_cache[cand_slot_idx][3])
      if id_val == target_id_val and type_val == self.type_val:
        self.cur_slot_idx = cand_slot_idx
        self.cmb_slots.setCurrentIndex(self.cur_slot_idx)
        self.updateUI()
        return
    self.log.emit('Did not find ID=%03X in %d cached %s slots' %
                  (target_id_val, len(self.slots_cache), self.label), 'red')

  @pyqtSlot(int)
  def onChangeSlot(self, new_slot_idx):
    # Validate new_slot_idx
    if not (0 <= new_slot_idx < len(self.slots_cache)):
      return

    # Update slot idx and read value from memory
    self.cur_slot_idx = new_slot_idx
    cur_addr_hex = self.slots_cache[self.cur_slot_idx][0]
    self.cmb_slots.setToolTip(cur_addr_hex)
    self.cmb_names.lineEdit().setText(ItemEntriesFrame.MISSING_ITEM_NAME)
    self.cmb_names.setDisabled(True)
    self.txt_id.setText('')
    self.txt_id.setDisabled(True)
    self.onReadSlot()

  @pyqtSlot()
  def fetchName(self):
    try:
      id_val = int(str(self.txt_id.text()), 16)
    except ValueError:
      return
    name = self.MISSING_ITEM_NAME
    if id_val in self.id2name:
      name = self.id2name[id_val]
    self.cmb_names.lineEdit().setText(name)

  @pyqtSlot(int)
  def fetchID(self, cmb_idx):
    if cmb_idx < 0 or cmb_idx >= len(self.idx2id):
      self.txt_id.setText('')
    else:
      self.txt_id.setText(self.idx2id[cmb_idx])

  @pyqtSlot()
  def onReadSlot(self):
    try:
      if not (0 <= self.cur_slot_idx < len(self.slots_cache)):
        raise ValueError('must cache slots before reading')
      addr_cur_slot = self.slots_cache[self.cur_slot_idx][1]
      self.read_block.emit(addr_cur_slot, 4)
    except ValueError, e:
      cur_slot_num = self.slots_cache[self.cur_slot_idx][2]
      self.log.emit('READ %s Slot %03d failed: %s' % (self.label, cur_slot_num, str(e)), 'red')
    except BaseException, e:
      cur_slot_num = self.slots_cache[self.cur_slot_idx][2]
      self.log.emit('READ %s Slot %03d failed: %s' % (self.label, cur_slot_num, str(e)), 'red')
      traceback.print_exc()
class LayerSelectionPage(QFrame):
    #TODO. Filtering, (visible) row selection, multi selection
    colparams = ((0, 65, 'Name'), (1, 235, 'Title'), (2, 350, 'Keywords'))
    XFER_BW = 40

    def __init__(self, parent=None):
        super(LayerSelectionPage, self).__init__(parent)
        self.parent = parent

        #convenience link
        self.confconn_link = self.parent.parent.confconn

        #flag top prevent read read action on keyword delete. New logic makes this redundant
        #self.keywordbypass = False

        QToolTip.setFont(QFont('SansSerif', 10))

        #label
        filterlabel = QLabel('Filter')
        availablelabel = QLabel('Available Layers')
        selectionlabel = QLabel('Layer Selections')
        keywordlabel = QLabel('Keyword')
        explainlabel = QLabel(
            "Edit Group assignments using this dialog or to simply initialise the Layer-Config just click 'Finish'"
        )

        #selection buttons
        chooseallbutton = QPushButton('>>')
        chooseallbutton.setFixedWidth(self.XFER_BW)
        chooseallbutton.clicked.connect(self.doChooseAllClickAction)

        choosebutton = QPushButton('>')
        choosebutton.setFixedWidth(self.XFER_BW)
        choosebutton.clicked.connect(self.doChooseClickAction)

        rejectbutton = QPushButton('<')
        rejectbutton.setFixedWidth(self.XFER_BW)
        rejectbutton.clicked.connect(self.doRejectClickAction)

        rejectallbutton = QPushButton('<<')
        rejectallbutton.setFixedWidth(self.XFER_BW)
        rejectallbutton.clicked.connect(self.doRejectAllClickAction)

        #operation buttons
        finishbutton = QPushButton('Finish')
        finishbutton.setToolTip('Finish and Close layer selection dialog')
        finishbutton.clicked.connect(self.parent.close)

        resetbutton = QPushButton('Reset')
        resetbutton.font()
        resetbutton.setToolTip(
            'Read Layer from LDS GetCapabilities request. Overwrites current Layer Config'
        )
        resetbutton.clicked.connect(self.doResetClickAction)

        self.available_sfpm = LDSSFPAvailableModel(self)
        self.selection_sfpm = LDSSFPSelectionModel(self)

        self.available_sfpm.setSourceModel(self.parent.available_model)
        self.selection_sfpm.setSourceModel(self.parent.selection_model)

        #textedits
        filteredit = QLineEdit('')
        filteredit.setToolTip(
            'Filter Available-Layers pane (filter operates across Name and Title fields and accepts Regex expressions)'
        )
        filteredit.textChanged.connect(self.available_sfpm.setActiveFilter)

        self.keywordcombo = QComboBox()
        self.keywordcombo.setToolTip(
            'Select or Add a unique identifier to be saved in layer config (keyword)'
        )
        self.keywordcombo.addItems(list(self.confconn_link.assigned))
        self.keywordcombo.setEditable(True)
        self.keywordcombo.activated.connect(self.doKeyComboChangeAction)

        lgindex = self.confconn_link.getLayerGroupIndex(
            self.confconn_link.lgval, col=1)
        lgentry = self.confconn_link.lglist[lgindex] if LU.assessNone(
            lgindex) else None
        #keywordedit = self.keywordcombo.lineEdit().text().toUtf8().data().decode('utf8')# for writing
        #if no entry or layer indicated then blank
        self.keywordcombo.lineEdit().setText(
            '' if lgentry is None or lgentry[0] == LORG.LAYER else
            lgentry[1])  #self.confconn_link.lgval)#TODO. group only

        #header
        headmodel = QStandardItemModel()
        headmodel.setHorizontalHeaderLabels([
            i[2] for i in self.colparams
        ][:self.parent.available_model.columnCount()])

        headview1 = QHeaderView(Qt.Horizontal)
        headview1.setModel(headmodel)
        headview1.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)

        headview2 = QHeaderView(Qt.Horizontal)
        headview2.setModel(headmodel)
        headview2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)

        #table
        self.available = QTableView()
        self.available.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.available.setSelectionMode(QAbstractItemView.MultiSelection)

        self.selection = QTableView()
        self.selection.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.selection.setSelectionMode(QAbstractItemView.MultiSelection)

        #interesting, must set model after selection attributes but before headers else row selections/headers don't work properly
        self.available.setModel(self.available_sfpm)
        self.selection.setModel(self.selection_sfpm)

        self.available.setSortingEnabled(True)
        self.available.setHorizontalHeader(headview1)

        self.selection.setSortingEnabled(True)
        self.selection.setHorizontalHeader(headview2)

        for cp in self.colparams:
            self.available.setColumnWidth(cp[0], cp[1])
            self.selection.setColumnWidth(cp[0], cp[1])

        self.available.verticalHeader().setVisible(False)
        self.available.horizontalHeader().setVisible(True)

        self.selection.verticalHeader().setVisible(False)
        self.selection.horizontalHeader().setVisible(True)

        #layout
        vbox00 = QVBoxLayout()
        vbox00.addWidget(availablelabel)
        vbox00.addWidget(self.available)

        vbox01 = QVBoxLayout()
        vbox01.addWidget(chooseallbutton)
        vbox01.addWidget(choosebutton)
        vbox01.addWidget(rejectbutton)
        vbox01.addWidget(rejectallbutton)

        vbox02 = QVBoxLayout()
        vbox02.addWidget(selectionlabel)
        vbox02.addWidget(self.selection)

        vbox10 = QVBoxLayout()
        vbox10.addWidget(filterlabel)
        vbox10.addWidget(filteredit)

        hbox12 = QHBoxLayout()
        hbox12.addWidget(keywordlabel)
        hbox12.addStretch(1)
        #hbox12.addWidget(inspbutton)
        #hbox12.addWidget(addbutton)
        #hbox12.addWidget(delbutton)

        vbox12 = QVBoxLayout()
        vbox12.addLayout(hbox12)
        vbox12.addWidget(self.keywordcombo)

        #00|01|02
        #10|11|12
        grid0 = QGridLayout()
        grid0.addLayout(vbox00, 1, 0)
        grid0.addLayout(vbox01, 1, 1)
        grid0.addLayout(vbox02, 1, 2)
        grid0.addLayout(vbox10, 0, 0)
        grid0.addLayout(vbox12, 0, 2)

        hbox2 = QHBoxLayout()
        hbox2.addWidget(resetbutton)
        hbox2.addStretch(1)
        hbox2.addWidget(explainlabel)
        hbox2.addWidget(finishbutton)
        #gbox1.setLayout(hbox2)

        vbox3 = QVBoxLayout()
        vbox3.addLayout(grid0)
        #vbox3.addLayout(hbox3)
        #vbox3.addWidget(line0)
        vbox3.addLayout(hbox2)

        self.setLayout(vbox3)

    def doChooseAllClickAction(self):
        '''Moves the lot to Selected'''
        #ktext = LU.recode(self.keywordcombo.lineEdit().text().toUtf8().data())
        ktext = LU.recode(
            LQ.readWidgetText(self.keywordcombo.lineEdit().text()))
        if not self.checkKeyword(ktext): return
        #------------------------------
        self.parent.signalModels(self.parent.STEP.PRE)
        #self.parent.selection_model.mdata += self.parent.available_model.mdata
        self.parent.selection_model.initData(self.confconn_link.complete)
        self.parent.available_model.initData([])
        self.parent.signalModels(self.parent.STEP.POST)
        #------------------------------
        self.parent.writeKeysToLayerConfig(ktext)
        #self.confconn_link.setupAssignedLayerList()
        if self.keywordcombo.findText(ktext) == -1:
            self.keywordcombo.addItem(ktext)

    def doChooseClickAction(self):
        '''Takes available selected and moves to selection'''
        #ktext = LU.recode(self.keywordcombo.lineEdit().text().toUtf8().data())
        ktext = LU.recode(
            LQ.readWidgetText(self.keywordcombo.lineEdit().text()))
        #ktext = str(self.keywordcombo.lineEdit().text())
        if not self.checkKeyword(ktext): return
        #------------------------------
        select = self.available.selectionModel()
        if select.hasSelection():
            self.transferSelectedRows(select.selectedRows(),
                                      self.available_sfpm, self.selection_sfpm)
            #------------------------------
            self.parent.writeKeysToLayerConfig(ktext)
            #self.confconn_link.assigned = self.confconn_link.setupAssignedLayerList()
            # -1 to indicate no index since 0,1,... are valid
            if self.keywordcombo.findText(ktext) == -1:
                self.keywordcombo.addItem(ktext)
        else:
            ldslog.warn('L2R > Transfer action without selection')
        #TRACE#
        #pdb.set_trace()
        self.available.clearSelection()

    def transferSelectedRows(self, indices, from_model, to_model):
        tlist = []
        for proxymodelindex in indices:
            transfer = from_model.getData(proxymodelindex)
            tlist.append((proxymodelindex, transfer), )

        to_model.addData([t[1] for t in tlist])
        from_model.delData([t[0] for t in tlist])
        return tlist

    def doRejectClickAction(self):
        '''Takes available selected and moves to selection'''
        #ktext = LU.recode(self.keywordcombo.lineEdit().text().toUtf8().data())
        ktext = LU.recode(
            LQ.readWidgetText(self.keywordcombo.lineEdit().text()))
        if not self.checkKeyword(ktext): return
        #------------------------------
        select = self.selection.selectionModel()
        if select.hasSelection():
            tlist = self.transferSelectedRows(select.selectedRows(),
                                              self.selection_sfpm,
                                              self.available_sfpm)
            #------------------------------
            kindex = self.keywordcombo.findText(ktext)
            remainder = self.parent.deleteKeysFromLayerConfig(
                [ll[1][0] for ll in tlist], ktext)
            if remainder > 0 and kindex == -1:
                #items+newkey -> add
                self.parent.writeKeysToLayerConfig(ktext)
                self.keywordcombo.addItem(ktext)
            elif remainder == 0 and kindex > -1:
                #empty+oldkey -> del
                self.keywordcombo.removeItem(kindex)
                self.keywordcombo.clearEditText()
        else:
            ldslog.warn('R2L < Transfer action without selection')
        #TRACE#
        #pdb.set_trace()
        self.selection.clearSelection()

    def doRejectAllClickAction(self):
        #ktext = LU.recode(self.keywordcombo.lineEdit().text().toUtf8().data())
        ktext = LU.recode(
            LQ.readWidgetText(self.keywordcombo.lineEdit().text()))
        if not self.checkKeyword(ktext): return
        #------------------------------
        self.parent.deleteKeysFromLayerConfig(
            [ll[0] for ll in self.parent.selection_model.mdata], ktext)
        #------------------------------
        self.parent.signalModels(self.parent.STEP.PRE)
        #self.parent.available_model.mdata += self.parent.selection_model.mdata
        self.parent.available_model.initData(self.confconn_link.complete)
        self.parent.selection_model.initData([])
        self.parent.signalModels(self.parent.STEP.POST)
        #------------------------------
        #self.confconn_link.setupAssignedLayerList()
        #self.keywordbypass = True
        self.keywordcombo.removeItem(self.keywordcombo.findText(ktext))
        self.keywordcombo.clearEditText()

    def doKeyComboChangeAction(self):
        '''Reset the available pane and if there is anything in the keyword box use this to init the selection pane'''
        #HACK
        #if self.keywordbypass:
        #    self.keywordbypass = False
        #    return
        #------------------------------
        #ktext = LU.recode(self.keywordcombo.lineEdit().text().toUtf8().data())
        ktext = LU.recode(
            LQ.readWidgetText(self.keywordcombo.lineEdit().text()))
        #------------------------------
        av_sl = self.parent.splitData(ktext, self.confconn_link.complete)
        #av_sl = self.parent.splitData(ktext,self.confconn_link.complete)
        self.parent.signalModels(self.parent.STEP.PRE)
        self.parent.available_model.initData(av_sl[0])
        self.parent.selection_model.initData(av_sl[1])
        self.parent.signalModels(self.parent.STEP.POST)

    def doResetClickAction(self):
        '''Dumps the LC and rebuilds from a fresh read of the caps doc'''
        #int warning (QWidget parent, QString title, QString text, QString button0Text, QString button1Text = QString(), QString button2Text = QString(), int defaultButtonNumber = 0, int escapeButtonNumber = -1)
        ans = QMessageBox.warning(
            self, "Reset",
            "This action will overwrite your Layer Configuration using the current LDS settings (potentially adding new or removing layers). Continue?",
            "Continue", "Cancel")
        if ans:
            #Cancel
            ldslog.warn('Cancelling Reset operation')
            return
        #Continue
        ldslog.warn('Reset Layer Config')
        self.parent.resetLayers()
        self.keywordcombo.clear()

    def checkKeyword(self, ktext):
        '''Checks keyword isn't null and isn't part of the LDS supplied keywords'''
        if LU.assessNone(ktext) is None:
            QMessageBox.about(self, "Keyword Required",
                              "Please enter a Keyword to assign Layer(s) to")
            return False
        if ktext in self.confconn_link.reserved:
            QMessageBox.about(
                self, "Reserved Keyword",
                "'{}' is a reserved keyword, please select again".format(
                    ktext))
            return False
        return True
    def generate_gui(self):
        self.theme_boxes = []
        self.themes = set([''])
        dp = self.layer.dataProvider()
        fields = list(dp.fields())
        numeric_fields = [field for field in fields
                          if field.typeName() in NUMERIC_FIELD_TYPES]

        themes_list = []
        indicators_list = {}
        if self.project_definition:
            themes = self.project_definition['children'][1]['children']
            for theme in themes:
                themes_list.append(theme['name'])
                for indicator in theme['children']:
                    indicators_list[indicator['field']] = (theme['name'],
                                                           indicator['name'])

            # remove duplicates
            themes_list = list(set(themes_list))
            themes_list.sort()
        themes_list.insert(0, '')

        attribute_label = QLabel('Attribute')
        theme_label = QLabel('Theme')
        name_label = QLabel('Name')

        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
        sizePolicy.setHorizontalStretch(5)
        theme_label.setSizePolicy(sizePolicy)
        name_label.setSizePolicy(sizePolicy)
        self.ui.grid_layout.addWidget(attribute_label, 0, 0)
        self.ui.grid_layout.addWidget(theme_label, 0, 1)
        self.ui.grid_layout.addWidget(name_label, 0, 2)

        for i, field in enumerate(numeric_fields, start=1):
            theme_name = ''
            indicator_name = ''
            if field.name() in indicators_list:
                theme_name = indicators_list[field.name()][0]
                indicator_name = indicators_list[field.name()][1]

            attribute_label = QLabel(field.name())
            attribute_label.setTextInteractionFlags(Qt.TextSelectableByMouse)

            theme = QComboBox()
            theme.setEditable(True)
            theme.setDuplicatesEnabled(False)
            theme.setInsertPolicy(QComboBox.InsertAlphabetically)
            theme.addItems(themes_list)
            current_index = theme.findText(theme_name)
            current_index = current_index if current_index != -1 else 0
            theme.setCurrentIndex(current_index)
            theme.currentIndexChanged.connect(self.check_status)
            theme.lineEdit().editingFinished.connect(self.update_themes)
            self.theme_boxes.append(theme)

            name = QLineEdit(indicator_name)
            name.textChanged.connect(self.check_status)

            self.ui.grid_layout.addWidget(attribute_label, i, 0)
            self.ui.grid_layout.addWidget(theme, i, 1)
            self.ui.grid_layout.addWidget(name, i, 2)

        self.check_status()
예제 #17
0
class QuestionDlg(QDialog):
    def __init__(self, parent=None):
        super(QuestionDlg,self).__init__(parent)
        # self.setStyleSheet("background-image:url('image/panelbg.jpg'); border: 2px; border-radius 2px;")
        # self.createDb()
        # return

        self.db = QSqlDatabase.addDatabase("QSQLITE");  
        self.db.setDatabaseName("studentNew.db")
        if not self.db.open():
            QMessageBox.warning(None, "错误",  "数据库连接失败: %s" % self.db.lastError().text())
            sys.exit(1)

        self.g_curClassName = ""
        self.deleteTmpdata()

        self.setWindowFlags(Qt.CustomizeWindowHint)
        # self.setStyleSheet("border: 2px; border-radius 2px;")
        # self.setWindowFlags(Qt.FramelessWindowHint)
        self.setStyleSheet("background-color: rgba(132, 171, 208, 200);")

        self.tabWidget=QTabWidget(self)
        self.tabWidget.currentChanged.connect(self.changeTab)
        # tabWidget.setTabShape(QTabWidget.Triangular)
        self.tabWidget.setStyleSheet("QTabWidget::pane{border-width:1px;border-color:rgb(48, 104, 151);\
            border-style: outset;background-color: rgb(132, 171, 208);\
            background: transparent;} \
            QTabWidget::tab-bar{border-width:0px;}\
            QTabBar::tab { height: 60px; width: 260px; color:rgb(0, 0, 255); font-size:20px; font-weight:bold;} \
            QTabBar::tab:hover{background:rgb(255,255, 255, 100);} \
            QTabBar::tab:selected{border-color:green;background-color:white;color:green;}")
        # tabWidget.setStyleSheet("QTabBar::tab:hover{background:rgb(255,255, 255, 100);}")
        self.btngroup = QButtonGroup()
        self.popMenu = QMenu(self)
        entry1 = self.popMenu.addAction("正确")
        self.connect(entry1,SIGNAL('triggered()'), lambda : self.answerRight())
        entry2 = self.popMenu.addAction("错误")
        self.connect(entry2,SIGNAL('triggered()'), lambda : self.answerWrong())
        entry3 = self.popMenu.addAction("替换")
        self.connect(entry3,SIGNAL('triggered()'), lambda : self.resetStudent())

        # Create the first tab page.
        self.w1=QWidget()
        self.w1.setAccessibleName("w1tab")
        self.genOneTab()
        
        # Create the second tab page.
        self.w2=QWidget()
        self.w2.setAccessibleName("w2tab")        
        self.genTwoTab()

        self.tabWidget.addTab(self.w1,"")
        self.tabWidget.addTab(self.w2,"班级学生信息管理")
        self.tabWidget.resize(940,700)

        btnclose = QPushButton(self)
        btnclose.setToolTip("关闭")
        btnclose.setText("╳")
        btnclose.setGeometry(915, 5, 20, 20)
        btnclose.setStyleSheet("background-color:rgb(0,100,0); color:rgb(255,255,255)")
        btnclose.clicked.connect(self.close)
        btnMinimized = QPushButton(self)
        btnMinimized.setToolTip("最小化")
        btnMinimized.setText("▁")
        btnMinimized.setGeometry(890, 5, 20, 20)
        btnMinimized.setStyleSheet("background-color:rgb(0,100,0); color:rgb(255,255,255)")
        btnMinimized.clicked.connect(lambda: self.showMinimized())
        self.btnSysMenu = QPushButton(self)
        # self.btnSysMenu.setText("▼")
        self.btnSysMenu.setGeometry(865, 5, 20, 20)
        self.btnSysMenu.setToolTip("系统设置")
        self.btnSysMenu.clicked.connect(lambda: self.showMinimized())
        menufont = QFont("宋体", 10)
        popMenu = QMenu(self)
        entry1 = popMenu.addAction("所有学生提问信息清零")
        entry1.setFont(menufont)
        self.connect(entry1,SIGNAL('triggered()'), self.initStudent)
        entry2 = popMenu.addAction("清除本堂课提问人员")
        entry2.setFont(menufont)
        self.connect(entry2,SIGNAL('triggered()'), self.deleteTmpdata)
        entry3 = popMenu.addAction("关于...")
        entry3.setFont(menufont)
        self.connect(entry3,SIGNAL('triggered()'), self.aboutMe)
        entry4 = popMenu.addAction("导出...")
        entry4.setFont(menufont)
        self.connect(entry4,SIGNAL('triggered()'), self.exportNotice)

        self.btnSysMenu.setMenu(popMenu)
        self.btnSysMenu.setStyleSheet("QPushButton::menu-indicator {image: url('image/sysmenu.png');subcontrol-position: right center;} ")
        # self.btnSysMenu.setStyleSheet("background-color:rgb(0,100,0); color:rgb(255,255,255);")

        authorinfo = QLabel(self.tabWidget)
        # authorinfo.setToolTip("关闭")
        authorinfo.setText("程序设计:汕头市大华路第一小学 赵小娜,有任何问题请反馈至[email protected]。")
        authorinfo.setGeometry(20, 665, 470, 26)
        authorinfo.setFont(QFont('Courier New'))
        authorinfo.setStyleSheet("background-color:rgba(255, 255, 255,160); font-size:12px;border: 1px solid rgb(60,200,255,200);color:rgba(0,0,0,220);border-radius:12px;")

        self.setWindowTitle("课堂随机提问")
        self.setWindowIcon(QIcon("image/start.ico"))
        self.setGeometry(100, 20, 940, 700)

        # self.changeTab()

        screen = QDesktopWidget().screenGeometry()
        size = self.geometry()
        self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2)

        self.btn_start.setMyarg('start')
        
        # print(self.btn_start.getMyarg())

        self.connect(self.btn_start, SIGNAL("clicked()"), self.startChoice)
        # self.connect(self.w1title, SIGNAL("currentIndexChanged(int)"), self.changeTitle)
        # self.connect(self.btn_start2, SIGNAL("clicked()"), self.startChoice)
        # self.connect(self.w2title, SIGNAL("currentIndexChanged(int)"), self.changeTitle)
        self.btngroup.buttonClicked[int].connect(self.btns_click)
        # self.connect(self.btn_start,  SIGNAL("myslot(PyQt_PyObject)"), self.myslot)  
        # self.connect(self.btngroup, SIGNAL("buttonClicked(int)"), lambda:self.btns_click())

    def myslot(self, text):  
        # print(text, self.dict_choices)
        self.g_curbtn = text
        if self.g_curbtn not in self.dict_choices:
            self.btnSysMenu.setFocus()
            return

        # print(self.btngroup.button(int(self.g_curbtn)).parent())
        # print(type(self.btngroup.button(int(self.g_curbtn)).parentWidget()))
        pos = self.btngroup.button(int(self.g_curbtn)).parent().mapToGlobal(self.btngroup.button(int(self.g_curbtn)).pos())
        width = self.btngroup.button(int(self.g_curbtn)).rect().height()
        # print("-----", pos, width)
        pos.setY(pos.y()+width-5)

        indx = 0
        for istate in self.dict_choices[self.g_curbtn]:
            if istate == '1':
                self.popMenu.actions()[indx].setEnabled(True)
            elif istate == '0':
                self.popMenu.actions()[indx].setEnabled(False)
            indx += 1
        self.popMenu.exec_(pos)
        self.btnSysMenu.setFocus()
    # def on_context_menu(self, point):
    #     print(point)
    #     self.popMenu.exec_(self.button.mapToGlobal(point)) 

    def btns_click(self, btnid):
        # curclassname = self.tabWidget.tabText(0)
        query = QSqlQuery(self.db)
        # cur = conn.cursor()
        today = datetime.date.today()
        self.g_curbtn = str(btnid).zfill(2)
        if self.g_curbtn not in self.dict_choices:
            self.btngroup.button(int(self.g_curbtn)).setStyleSheet(stylesheetstr_new)
            query.exec_("select count(*) from tmprecord where stusn='" + str(self.g_curbtn) + "'")
            query.next()            
            if query.value(0) == 0:
                query.prepare("insert into tmprecord (classname, stusn, datequestion) values (:classname, :stusn, :datequestion)")
                query.bindValue(":classname", self.g_curClassName)
                query.bindValue(":stusn", self.g_curbtn)
                query.bindValue(":datequestion", today)
                query.exec_() 
                
            self.dict_choices[self.g_curbtn] = "111"
        else:
            self.btngroup.button(int(self.g_curbtn)).setStyleSheet(stylesheetstr_old)
            self.btngroup.button(int(self.g_curbtn)).setIcon(QIcon())            
            query.exec_("delete from tmprecord where stusn='"+ str(self.g_curbtn) + "'")            
            self.dict_choices.pop(self.g_curbtn)

        self.btnSysMenu.setFocus()

    def exportNotice(self):
        query = QSqlQuery(self.db)
        query.exec_("select stusn, stuname, classname, rightquestions, wrongquestions from student" ) 
        lstInfo = [["学号","姓名", "班级", "回答正确次数", "回答错误次数"]]
        while(query.next()):
            lstInfo.append([query.value(0),query.value(1),query.value(2),query.value(3),query.value(4)])

        from xlwt import Workbook,easyxf
        book = Workbook(encoding='ascii')
            # 'pattern: pattern solid,  fore_colour white;'
        style = easyxf(
            'font: height 280, name 黑体;'
            'align: vertical center, horizontal center;'
            )
        style2 = easyxf('font: height 260, name 仿宋_GB2312, bold True; align: vertical center, horizontal left;')
        style3 = easyxf('font: height 260, name 仿宋_GB2312, bold True; align: vertical center, horizontal left, wrap True;')

        sheet1 = book.add_sheet('学生提问情况汇总',cell_overwrite_ok=True)
        # sheet1.write(0,7,flagtxt, easyxf('font: height 200, name 黑体;align: vertical center, horizontal right;'))
        sheet1.write_merge(0,0,0,4, '学生提问情况汇总表',style)
        sheet1.row(0).height_mismatch = 1
        sheet1.row(0).height = 5*256

        sheet1.col(0).width = 10*256
        sheet1.col(1).width = 25*256
        sheet1.col(2).width = 25*256
        sheet1.col(3).width = 20*256
        sheet1.col(4).width = 20*256
        
        tmprows = 1
        for item in lstInfo:
            stusn               = item[0]
            stuname             = item[1]
            classname           = item[2]
            rightquestions      = item[3]
            wrongquestions      = item[4]

            sheet1.write(tmprows,0,stusn, style2)
            sheet1.write(tmprows,1,stuname, style2)
            sheet1.write(tmprows,2,classname, style2)
            sheet1.write(tmprows,3,rightquestions, style2)
            sheet1.write(tmprows,4,wrongquestions, style2)
            tmprows += 1
        # print(tmprows)
        sheet1.header_str = "".encode()
        sheet1.footer_str = "".encode()

        # book.save('d:/simple.xls')
        # print(QDir.home().dirName() , QDir.homePath ())
        filename = QDir.homePath () + "\学生提问情况汇总表.xls" 
        try:
            book.save(filename)
        except  Exception as e:
            QMessageBox.warning(self, "写入错误", "错误号:"+str(e.errno)+"\n错误描述:"+e.strerror+"\n请关闭已经打开的%s文档!" % filename)
        QMessageBox.about (self, "导出成功", "请查看文档:%s" % filename)

    def aboutMe(self):
        strinfo = """本软件采用python3.4编写,界面采用qt4.8的python绑定。
                    \n版本所有:汕头市大华路第一小学赵小娜老师。
                    \n有任何问题请反馈至[email protected]。
                    """
        QMessageBox.information(None, "关于", strinfo)
        
    def initStudent(self):
        query = QSqlQuery(self.db)
        ret = query.exec_("update student set wrongquestions=0") 
        ret = query.exec_("update student set rightquestions=0")  
        QMessageBox.information(None, "提示", "已清除所有学生的累计提问情况。")


    def deleteTmpdata(self):
        query = QSqlQuery(self.db)
        ret = query.exec_("delete from tmprecord where 1=1" )   
        if self.g_curClassName != "":     
            QMessageBox.information(None, "提示", "已清除本次软件启动后的所有已提问过的学生。")
  
    def changeTab(self):
        # pass
        curtab = self.tabWidget.currentIndex()
        # print(curtab, "-")
        if curtab == 1:  ## when click the second tab page ,then pass.
            return
            
        # cur = conn.cursor()
        query = QSqlQuery(self.db)

        ## if current classname is null, then set current tabpage display the first class of classtable
        if self.g_curClassName == "":
            ret = query.exec_("select classname from classtable")
            query.next()
            self.g_curClassName = query.value(0)
            
        self.tabWidget.setTabText(0, self.g_curClassName)
        # print(1)
        strwhere = " and classname like '" + self.g_curClassName + "' ORDER BY stusn"

        self.g_curbtn = ""
        self.dict_choices = {}
        self.studentSnlst = []

        ## clearn the question data of temp record .
        ret= query.exec_("delete from tmprecord where 1=1")
        ret = query.exec_("select stusn, stuname from student where 1=1 " + strwhere)

        ## now update the global data "self.btngroup"
        for indx in range(0, 56):
            self.btngroup.button(indx+1).setText("")
            self.btngroup.button(indx+1).setMyarg(None)       
            self.btngroup.button(indx+1).setStyleSheet(stylesheetstr_old)
            self.btngroup.button(indx+1).setIcon(QIcon())
            self.btngroup.button(indx+1).setEnabled(False)
            self.studentSnlst.append([indx+1,])

        inum = 0
        while (query.next()): 
            inum += 1            
            self.btngroup.button(inum).setText(query.value(1))
            self.btngroup.button(inum).setMyarg(query.value(0))       
            self.btngroup.button(inum).setStyleSheet(stylesheetstr_old)
            self.btngroup.button(inum).setIcon(QIcon())
            self.btngroup.button(inum).setEnabled(True)

        # print(inum, len(self.btngroup.buttons()))        

        self.group_animation = groupAnimation(self.studentSnlst, self.btngroup)

    def mousePressEvent(self, event):
        # print('a')
        if event.button() == Qt.RightButton:
            QDialog.mousePressEvent(self,event)
            return
        # print(event.sender(), event.button())
        self.offset = event.pos()
        # print(self.offset)

    def mouseMoveEvent(self, event):
        # print('sss')
        if hasattr(self, 'offset'):
            x=event.globalX()
            y=event.globalY()
            x_w = self.offset.x()
            y_w = self.offset.y()
            self.move(x-x_w, y-y_w)
        else:
            pass

    #######======= studentModel ============###############
    def newStudent(self):
        # Calc the missing number:
        row = self.StudentModel.rowCount()

        if row > 0:
            oldNumList = []
            for i in range(0, row):
                oldNumList.append(int(self.StudentModel.index(i,2).data()))
            
            allNumberList = list(range(1, oldNumList[-1]+1))
            for i in range(0, allNumberList[-1]):
                if oldNumList[i] != allNumberList[i]:
                    missingNum = allNumberList[i]
                    break
            if len(oldNumList) == len(allNumberList):
                missingNum = allNumberList[i] +1
        else:
            missingNum = 1

        self.StudentModel.insertRow(row)
        self.StudentView.scrollToBottom()
        self.StudentModel.setData(self.StudentModel.index(row, 1), self.g_curClassName)
        self.StudentModel.setData(self.StudentModel.index(row, 2), str(missingNum).zfill(2))
        self.StudentModel.setData(self.StudentModel.index(row, 4), 0)
        self.StudentModel.setData(self.StudentModel.index(row, 5), 0)

    def removeStudent(self):
        index = self.StudentView.currentIndex()
        row = index.row()             
        if QMessageBox.question(self, "删除确认", "是否要删除当前选中记录?", "确定", "取消") == 0:
            self.StudentModel.removeRows(row, 1)
            self.StudentModel.submitAll()
            self.StudentModel.database().commit()

    def revertStudent(self):
        self.StudentModel.revertAll()
        self.StudentModel.database().rollback()

    def saveStudent(self):
        self.StudentModel.database().transaction()
        if self.StudentModel.submitAll():
            self.StudentModel.database().commit()
            # print("save success!  ->commit")
        else:
            self.StudentModel.revertAll()
            self.StudentModel.database().rollback()

    #######======= classModel ============###############
    def newClass(self):
        row = self.ClassnameModel.rowCount()
        self.ClassnameModel.insertRow(row)

    def removeClass(self):
        index = self.ClassnameView.currentIndex()
        row = index.row()   
        curClassname =  index.sibling(index.row(),0).data()
        strwhere = "classname like '" + curClassname + "'"
        self.StudentModel.setFilter(strwhere)
        self.StudentModel.select()
        # print(self.StudentModel.rowCount(), "----", )
        if QMessageBox.question(self, "删除确认", "删除班级意味着会删除本班所有人员信息。是否要删除当前选中记录?", "确定", "取消") == 0:
            self.ClassnameModel.removeRows(row, 1)
            self.ClassnameModel.submitAll()
            self.ClassnameModel.database().commit()

            self.StudentModel.removeRows(0, self.StudentModel.rowCount())
            self.StudentModel.submitAll()
            self.StudentModel.database().commit()

    def revertClass(self):
        self.ClassnameModel.revertAll()
        self.ClassnameModel.database().rollback()

    def saveClass(self):
        query = QSqlQuery(self.db)

        # record the old class name
        lstOldClassName = {}
        lstOldClassid = []
        query.exec_("select rowid, classname from classtable" )        
        while(query.next()):
            lstOldClassName[query.value(0)] = query.value(1)
            lstOldClassid.append(query.value(0))
        # print(lstOldClassName)

        # Update the class Table
        self.ClassnameModel.database().transaction()
        if self.ClassnameModel.submitAll():
            self.ClassnameModel.database().commit()
            # print("save success!  ->commit")
        else:
            QMessageBox.warning(None, "错误",  "请检查班级中名称,不能出现同名班级!")
            self.ClassnameModel.revertAll()
            self.ClassnameModel.database().rollback()

        # print(lstOldClassid)

        lstNewClassName = {}
        query.exec_("select rowid, classname from classtable where rowid in " + str(tuple(lstOldClassid)) )        
        while(query.next()):
            lstNewClassName[query.value(0)] = query.value(1)            

        # print(lstOldClassName, '=========')
        # print(lstNewClassName, '~~~~~~~~~')

        for i in lstOldClassName:
            oldclassname = lstOldClassName[i]
            newclassname = lstNewClassName[i]
            if oldclassname != newclassname:
                # print(oldclassname, newclassname, '++++++++')
                # print("update student set classname=" + newclassname + " where classname='" + oldclassname + "'")
                query.exec_("update student set classname='" + newclassname + "' where classname='" + oldclassname + "'")
                self.StudentModel.setFilter("classname = '" + newclassname + "'")
                self.StudentModel.select()

        lstClassName = []      
        query.exec_("select classname from classtable" ) 
        while(query.next()):
            lstClassName.append(query.value(0))
        self.StudentView.setItemDelegateForColumn(1,  ComboBoxDelegate(self, lstClassName, self.db))

    def dbclick(self, indx):
        if type(indx.sibling(indx.row(),0).data()) != QPyNullVariant:
            classname = indx.sibling(indx.row(),0).data()
            
            strwhere = "classname like '" + classname + "'"
            self.StudentModel.setFilter(strwhere)
            self.StudentModel.setSort(2, Qt.AscendingOrder)
            self.StudentModel.select()
            
            self.g_curClassName = classname
            self.tabWidget.setTabText(0, self.g_curClassName)

    def dbclick2(self, indx):
        if indx.column() == 2:
            self.StudentView.setEditTriggers(QAbstractItemView.NoEditTriggers)
        else:
            self.StudentView.setEditTriggers(QAbstractItemView.DoubleClicked)
        

    def genTwoTab(self, tabtitle=""):
        # Create the tab title sytle.
        tabtitle = QLabel()
        tabtitle.setFont(QFont('Courier New', 20))
        tabtitle.setText("班级学生信息管理")
        tabtitle.setStyleSheet("border: 1px solid blue; color:rgba(0,0,255, 220);\
            background-color:rgba(201,201,201,60);\
            border-radius: 6px; \
            padding: 1px 18px 1px 20px;\
            min-width: 8em;")
        tabtitle.setMinimumHeight(50);
        titleLayout = QHBoxLayout()
        titleLayout.addWidget(tabtitle)
        titleLayout.setAlignment(tabtitle, Qt.AlignCenter)

       
        # Create the classnameView
        self.ClassnameView = QTableView()
        self.ClassnameModel = QSqlTableModel(self.ClassnameView)
        self.ClassnameModel.setTable("classtable")
        # self.ClassnameModel.setRelation(2, QSqlRelation("mentalmodel", "id", "name"));
        self.ClassnameModel.setEditStrategy(QSqlTableModel.OnManualSubmit)
        self.ClassnameModel.select()

        self.ClassnameModel.setHeaderData(0, Qt.Horizontal, "班级名称")

        # for indx, iheader in enumerate(["classid", "classname"]):
        #     self.ClassnameModel.setHeaderData(indx+1, Qt.Horizontal, iheader)
    
        self.ClassnameView.setModel(self.ClassnameModel)
        # self.ClassnameView.setColumnHidden(0, True)
        # self.ClassnameView.show()
        self.ClassnameView.verticalHeader().setFixedWidth(30)
        self.ClassnameView.verticalHeader().setStyleSheet("color: red;font-size:20px; ");
        self.ClassnameView.setStyleSheet("QTableView{background-color: rgb(250, 250, 200, 0);"  
                    "alternate-background-color: rgb(141, 163, 0);}"
                    "QTableView::item:hover {background-color: rgba(100,200,220,100);} ") 
        self.ClassnameView.setStyleSheet("font-size:16px; ");
        self.ClassnameView.setSelectionMode(QAbstractItemView.SingleSelection)
        # self.ClassnameView.dataChanged.connect(self.dataChanged)

        # self.ClassnameView.setSizePolicy(QSizePolicy.Expanding,     QSizePolicy.Expanding)

        # the second list
        self.StudentView = QTableView()
        self.StudentModel = QSqlTableModel(self.StudentView)
        self.StudentModel.setTable("student")
        # self.StudentModel.setRelation(2, QSqlRelation("mentalmodel", "id", "name"));
        self.StudentModel.setEditStrategy(QSqlTableModel.OnManualSubmit)
        # self.StudentModel.select()

        query = QSqlQuery(self.db)
        strwhere = " 1=1 "
        if self.g_curClassName == "":
            ret = query.exec_("select classname from classtable")
            query.next()
            firstClassName = query.value(0)
            strwhere += " and classname like '" + firstClassName + "'"
            
        self.StudentModel.setFilter(strwhere)
        self.StudentModel.select()

        for indx, iheader in enumerate(["班级名称", "学生编号", "学生姓名", "答对次数", "答错次数"]):
            self.StudentModel.setHeaderData(indx+1, Qt.Horizontal, iheader)
    
        self.StudentView.setModel(self.StudentModel)
        self.StudentView.setColumnHidden(0, True)

        # query = QSqlQuery(self.db)  
        lstClassName = []      
        query.exec_("select classname from classtable" ) 
        while(query.next()):
            lstClassName.append(query.value(0))

        self.StudentView.setItemDelegateForColumn(1,  ComboBoxDelegate(self, lstClassName, self.db))
        # self.StudentView.show()
        self.StudentView.verticalHeader().setFixedWidth(30)
        self.StudentView.verticalHeader().setStyleSheet("color: red;font-size:20px; background-color: rgb(250, 250, 200, 100)");
        self.StudentView.setStyleSheet("QTableView{background-color: rgb(250, 250, 200, 0);"  
                    "alternate-background-color: rgb(141, 163, 250);}"
                    "QTableView::item:hover {background-color: rgba(10,200,100,200);} "
                    ) 
        self.StudentView.setStyleSheet("font-size:16px;")
        self.StudentView.setSelectionMode(QAbstractItemView.SingleSelection)
        self.StudentView.doubleClicked.connect(self.dbclick2)

        btn_lst1_layout = QGridLayout()
        newusrbtn       = QPushButton("新增")
        savebtn         = QPushButton("保存")
        revertbtn       = QPushButton("撤销")
        removebtn       = QPushButton("删除")
        btn_lst1_layout.addWidget(newusrbtn, 0, 0)
        btn_lst1_layout.addWidget(savebtn, 0, 1)
        btn_lst1_layout.addWidget(revertbtn, 1, 0)
        btn_lst1_layout.addWidget(removebtn, 1, 1)

        newusrbtn.clicked.connect(self.newClass)
        savebtn.clicked.connect(self.saveClass)
        revertbtn.clicked.connect(self.revertClass)
        removebtn.clicked.connect(self.removeClass)

        self.ClassnameView.doubleClicked.connect(self.dbclick)
        

        btnbox2 = QDialogButtonBox(Qt.Horizontal)
        newusrbtn2       = QPushButton("新增")
        savebtn2         = QPushButton("保存")
        revertbtn2       = QPushButton("撤销")
        removebtn2       = QPushButton("删除")
        btnbox2.addButton(newusrbtn2, QDialogButtonBox.ActionRole);
        btnbox2.addButton(savebtn2, QDialogButtonBox.ActionRole);
        btnbox2.addButton(revertbtn2, QDialogButtonBox.ActionRole);
        btnbox2.addButton(removebtn2, QDialogButtonBox.ActionRole);

        newusrbtn2.clicked.connect(self.newStudent)
        savebtn2.clicked.connect(self.saveStudent)
        revertbtn2.clicked.connect(self.revertStudent)
        removebtn2.clicked.connect(self.removeStudent)

        # left list layout
        lst_layout_1 = QVBoxLayout()
        lst_layout_1.addWidget(self.ClassnameView)
        lst_layout_1.addLayout(btn_lst1_layout)

        lst_layout_2 = QVBoxLayout()
        lst_layout_2.addWidget(self.StudentView)
        lst_layout_2.addWidget(btnbox2)
        
        lstlayout = QHBoxLayout()
        lstlayout.setMargin(5)
        # lstlayout.addLayout(findbox)
        lstlayout.addLayout(lst_layout_1, 2)
        lstlayout.setMargin(5)
        lstlayout.addLayout(lst_layout_2, 5)
            
        labelClass = QLabel("")
        labelClass.setStyleSheet("background-color:rgba(255, 255, 255,0); color:rgba(0,0,0,0);")
        labelClass.setFixedHeight(40)
        # labelClass.setFixedWidth(100)
        # labelClass.setFont(QFont('宋体', 10))

        bottomlayout = QHBoxLayout()        
        bottomlayout.addWidget(labelClass)        

        tab2layout = QVBoxLayout()
        tab2layout.addLayout(titleLayout)       
        tab2layout.addLayout(lstlayout)
        tab2layout.addLayout(bottomlayout)
        self.w2.setLayout(tab2layout)
        self.w2.setStyleSheet("background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ffffff, stop: 1 #228888);")
      
    def genOneTab(self):

        tabtitle = QLabel()
        # tabtitle.setFixedHeight(40)
        # tabtitle.setFixedWidth(160)        
        self.btn_start = MyButton("开始")
        self.choicenum_text = QComboBox()
        self.choicenum_text.setObjectName('w1combonums')
        # self.w1title.setStyleSheet("background-image:url('image/panelbg.jpg');")
        
        # Set the title style                
        tabtitle.setFont(QFont('Courier New', 20))
        tabtitle.setText("随堂提问演板")
        tabtitle.setStyleSheet("border: 1px solid blue; color:rgba(0,0,255, 220);\
            background-color:rgba(201,201,201,60);\
            border-radius: 6px; \
            padding: 1px 18px 1px 20px;\
            min-width: 8em;")
        tabtitle.setMinimumHeight(50);
        titleLayout = QHBoxLayout()
        titleLayout.addWidget(tabtitle)
        titleLayout.setAlignment(tabtitle, Qt.AlignCenter)
       
        btnlayout = QGridLayout()
        tmpnum = 0
        for inum in range(0,56):
            irow = tmpnum // g_cols
            icol = tmpnum % g_cols
            tmpnum += 1
            btnlayout.setRowMinimumHeight(irow, 80)
            tmpbtn = MyButton("")
            tmpbtn.setMyarg(None)
            # tmpbtn.setFixedHeight(20)
            tmpbtn.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding))
            tmpbtn.setStyleSheet("border: 1px solid rgb(55,55,255,100);background-color: rgba(255,255,255,20);font-size:16px;")
            self.connect(tmpbtn,  SIGNAL("myslot(PyQt_PyObject)"), self.myslot)
            tmpbtn.setAutoDefault(False)
            self.btngroup.addButton(tmpbtn, inum+1) # stusn is from 1 start

            btnlayout.addWidget(tmpbtn, irow, icol)


        self.btn_start.setIcon(QIcon("image/start.png"))
        self.btn_start.setStyleSheet("border: 1px solid yellow;")
        self.btn_start.setFixedHeight(40)
        self.btn_start.setFixedWidth(100)
        self.btn_start.setFont(QFont('宋体', 18))
        # self.choicenum_text.setFixedHeight(45)
        # self.choicenum_text.setFixedWidth(60)

        ## Set the combox number style
        self.choicenum_text.setFont(QFont('Courier New', 20))
        self.choicenum_text.setFixedHeight(45)
        self.choicenum_text.setStyleSheet("border: 2px solid blue; color:red;font-weight:light;font-size:26px;\
            border-radius: 6px; \
            min-width: 2em; ")
        self.choicenum_text.setEditable(True)
        self.choicenum_text.lineEdit().setReadOnly(True);
        self.choicenum_text.lineEdit().setAlignment(Qt.AlignCenter);

        model = self.choicenum_text.model()
        for row in list(range(1, 7)):
            item = QStandardItem(str(row))
            item.setTextAlignment(Qt.AlignCenter)
            item.setForeground(QColor('red'))
            item.setBackground(QColor(0,200,50, 130))
            model.appendRow(item)
        self.choicenum_text.setCurrentIndex(2)
        # self.choicenum_text.setStyleSheet ("QComboBox::drop-down {border-width: 100px;}")
        # self.choicenum_text.setStyleSheet ("QComboBox::down-arrow {image: url(image/downarrow.png);top: 10px;left: 1px;}")

        bottomlayout = QHBoxLayout()
        bottomlayout.setSizeConstraint(QLayout.SetFixedSize)
        bottomlayout.addStretch(10)
        bottomlayout.addWidget(self.btn_start)
        bottomlayout.setSpacing(5)
        bottomlayout.addWidget(self.choicenum_text)

        tab1layout = QVBoxLayout()
        tab1layout.addLayout(titleLayout)       
        tab1layout.addLayout(btnlayout)
        tab1layout.addLayout(bottomlayout)
                
        self.w1.setLayout(tab1layout)
        self.w1.setStyleSheet("background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ffffff, stop: 1 #228888);")
         
    def startChoice(self, usernum="", oldbtn=""): 
        # print(oldbtn, 1)

        if oldbtn == "":
            self.dict_choices = {}

        strwhere = " and classname like '" + self.g_curClassName + "'"

        allstudent = []
        lstrecord = []
        query = QSqlQuery(self.db)        
        query.exec_("select stusn from tmprecord where 1=1 " + strwhere) 
        while(query.next()):
            lstrecord.append(query.value(0))
        # print(lstrecord, 'record', "select stusn from student where stusn not in " + str(tuple(lstrecord)))

        query.exec_("select stusn from student where stusn not in " + str(tuple(lstrecord)) + strwhere)
        while(query.next()):
            allstudent.append(query.value(0))

        if usernum == "":
            nums = int(self.choicenum_text.currentText())
        else:
            nums = usernum
        if nums >= len(allstudent):
            query.exec_("delete from tmprecord where 1=1 " + strwhere) #delete tmp date no today            
            allstudent = []
            query.exec_("select stusn from student where 1=1 " + strwhere)
            while(query.next()):
                allstudent.append(query.value(0))
        
        if oldbtn == "":
            random.seed()
            lstchoices = random.sample(allstudent, nums)
            for ibtn in lstchoices:
                self.dict_choices[ibtn] = "111"

            self.group_animation.start()
            QTimer.singleShot(1200, self.stopmovie)
        else:
            random.seed()
            otherbtn = random.sample(allstudent, 1)[0]
            # self.btngroup.button(int(otherbtn)).setFocus()
            self.dict_choices.pop(oldbtn)
            self.dict_choices[otherbtn] = '111'
            self.stopmovie()
        self.btnSysMenu.setFocus()

    def stopmovie(self):
        self.group_animation.stop()
        for isn in self.studentSnlst:
            # print(isn)
            self.btngroup.button(int(isn[0])).setStyleSheet(stylesheetstr_old)
            self.btngroup.button(int(isn[0])).setIcon(QIcon())
        
        classname = self.tabWidget.tabText(0)
        query = QSqlQuery(self.db)        
        today = datetime.date.today()
        for ibtn in self.dict_choices:
            self.btngroup.button(int(ibtn)).setStyleSheet(stylesheetstr_new)
            query.exec_("select count(*) from tmprecord where stusn='" + str(ibtn) + "'")
            query.next()
            if query.value(0) == 0:               
                query.prepare("insert into tmprecord (classname, stusn, datequestion) values (:classname, :stusn, :datequestion)")
                query.bindValue(":classname", classname)
                query.bindValue(":stusn", ibtn)
                query.bindValue(":datequestion", today)
                query.exec_()
    
    def answerRight(self):
        # print(self.g_curbtn)
        value = self.g_curbtn
        if value not in self.dict_choices:
            return

        self.btngroup.button(int(value)).setIcon(QIcon("image/smile.png"))
        self.btngroup.button(int(value)).setIconSize(QSize(20,20))
        
        query = QSqlQuery(self.db)
        query.exec_("select rightquestions from student where stusn='" + value + "'")
        query.next()
        studentRightQuestions = query.value(0) + 1
        query.exec_("update student set rightquestions=" + str(studentRightQuestions) + " where stusn='" + value + "'")
                
        ###########
        if self.dict_choices[value] == "101":
            query.exec_("select wrongquestions from student where stusn='" + value + "'")
            query.next()
            studentWrongQuestions = query.value(0) - 1
            query.exec_("update student set wrongquestions=" + str(studentWrongQuestions) + " where stusn='" + value + "'")
            
        self.dict_choices[value] = "011"

    def answerWrong(self):
        value = self.g_curbtn
        if value not in self.dict_choices:
            return

        self.btngroup.button(int(value)).setIcon(QIcon("image/cry.png"))
        self.btngroup.button(int(value)).setIconSize(QSize(20,20))
        # self.btngroup.button(int(value)).setStyleSheet("border-image: url(image/ex_stu.png);")
        
        query = QSqlQuery(self.db)
        query.exec_("select wrongquestions from student where stusn='" + value + "'")
        query.next()
        studentWrongQuestions = query.value(0) + 1
        query.exec_("update student set wrongquestions=" + str(studentWrongQuestions) + " where stusn='" + value + "'")
        
        if self.dict_choices[value] == "011":
            query.exec_("select rightquestions from student where stusn='" + value + "'")
            query.next()
            studentRightQuestions = query.value(0) - 1
            query.exec_("update student set rightquestions=" + str(studentRightQuestions) + " where stusn='" + value + "'")
            
        self.dict_choices[value] = "101"

    def resetStudent(self):
        value = self.g_curbtn
        if value not in self.dict_choices:
            return

        query = QSqlQuery(self.db)

        if self.dict_choices[value] == "011":        
            query.exec_("select rightquestions from student where stusn='" + value + "'")
            query.next()
            studentRightQuestions = query.value(0) - 1
            query.exec_("update student set rightquestions=" + str(studentRightQuestions) + " where stusn='" + value + "'")

        if self.dict_choices[value] == "101":
            query.exec_("select wrongquestions from student where stusn='" + value + "'")
            query.next()
            studentWrongQuestions = query.value(0) - 1
            query.exec_("update student set wrongquestions=" + str(studentWrongQuestions) + " where stusn='" + value + "'")
            
        self.startChoice(usernum=1, oldbtn=value)
        # print("---reset___")

        # curmenu.actions()[0].setEnabled(True)
        # curmenu.actions()[1].setEnabled(True)
        # self.choiceOneStudent(value)

    def createDb(self):
        conn = sqlite3.connect("studentNew.db") 
        cur = conn.cursor()
        sqlstr = 'create table student (id integer primary key, \
            classname varchar(20), \
            stusn varchar(20), \
            stuname varchar(20), \
            rightquestions integer, \
            wrongquestions integer, \
            FOREIGN KEY(classname) REFERENCES classtable(classname))'
        # print(sqlstr)

        sqlstr2 = 'create table tmprecord (id integer primary key, \
            classname varchar(20), \
            stusn varchar(20), \
            datequestion date)'

        sqlstr3 = 'create table classtable (classname varchar(20) PRIMARY KEY)'
        
        cur.execute(sqlstr3)
        conn.commit()
        cur.execute(sqlstr2)
        conn.commit()
        cur.execute(sqlstr)
        conn.commit()

        strdelete = "delete from student where 1=1"
        cur.execute(strdelete)
        conn.commit()
        strdelete = "delete from tmprecord where 1=1"
        cur.execute(strdelete)
        conn.commit()
        # print(sqlstr2)

        # cur.execute(sqlstr) 
        # conn.commit()
        # cur.execute(sqlstr2) 
        # conn.commit()

        # insert example data
        strsql = "insert into classtable values (?)"
        cur.execute(strsql, ("三(3)班",))
        conn.commit()
        cur.execute(strsql, ("三(4)班",))
        conn.commit()


        a03lst = ["曾忆谊","赵佳泉","翁文秀","林珑","郑铭洁","林泽思","吴崇霖","陈思嘉","欧阳月孜","郭展羽","詹伟哲","黄佳仪","杨秋霞","周奕子","林楚杰","欧伊涵","许腾誉","陈唯凯","陈树凯","林彦君","张钰佳","高锴","杨博凯","林妙菲","林楚鸿","陈展烯","姚静茵","吴欣桐","范思杰","肖佳","马思广","许一帆","姚奕帆","陈海珣","吴黛莹","吴育桐","肖凯帆","林欣阳","叶茂霖","姚楷臻","陈嘉豪","陈琦","杨子楷","陈炎宏","陈幸仪","杨景畅","罗暖婷","郑馨"]
        a04lst = ["罗恩琪","王可","曾祥威","谢濡婷","温嘉凯","许洁仪","肖欣淇","陈凯佳","林天倩","李乐海","吴文慧","黄文婷","万誉","陈进盛","张裕涵","陈振嘉","王巧玲","林珮琪","陈炜楷","杨健","赵泽锴","张凤临","蔡子丹","陈烨杰","廖妍希","林树超","夏培轩","陈锦森","李星","蔡依婷","姚容创","姚凯扬","沈嘉克","周凡","张玉川","邱金迅","陈菲敏","陈星翰","朱煜楷","郑泽洪","钱剑非","罗奕丰","陈杜炜","林知钦"]
        strsql = "insert into student values (?, ?, ?, ?,?,?)" 
        for i in list(range(0,len(a03lst))):
            cur.execute(strsql, (None, "三(3)班", str(i+1).zfill(2), a03lst[i], 0, 0))
            conn.commit()
        strsql = "insert into student values (?, ?, ?, ?,?,?)" 
        for i in list(range(0,len(a04lst))):
            cur.execute(strsql, (None, "三(4)班", str(i+1).zfill(2), a04lst[i], 0, 0))
            conn.commit()
        cur.close()
예제 #18
0
class GotoLineWidget( QWidget ):
    " goto bar widget "

    maxHistory = 12

    def __init__( self, editorsManager, parent = None ):

        QWidget.__init__( self, parent )
        self.editorsManager = editorsManager

        self.__gotoHistory = []

        # Common graphics items
        closeButton = QToolButton( self )
        closeButton.setToolTip( "Click to close the dialog (ESC)" )
        closeButton.setIcon( PixmapCache().getIcon( "close.png" ) )
        closeButton.clicked.connect( self.hide )

        lineLabel = QLabel( self )
        lineLabel.setText( "Goto line:" )

        self.linenumberEdit = QComboBox( self )
        self.linenumberEdit.setEditable( True )
        self.linenumberEdit.setInsertPolicy( QComboBox.InsertAtTop )
        self.linenumberEdit.setAutoCompletion( False )
        self.linenumberEdit.setDuplicatesEnabled( False )
        sizePolicy = QSizePolicy( QSizePolicy.Expanding, QSizePolicy.Fixed )
        sizePolicy.setHorizontalStretch( 0 )
        sizePolicy.setVerticalStretch( 0 )
        sizePolicy.setHeightForWidth(
                self.linenumberEdit.sizePolicy().hasHeightForWidth() )
        self.linenumberEdit.setSizePolicy( sizePolicy )
        self.validator = QIntValidator( 1, 100000, self )
        self.linenumberEdit.setValidator( self.validator )
        self.linenumberEdit.editTextChanged.connect( self.__onEditTextChanged )
        self.linenumberEdit.lineEdit().returnPressed.connect( self.__onEnter )

        self.goButton = QToolButton( self )
        self.goButton.setToolTip( "Click to jump to the line (ENTER)" )
        self.goButton.setIcon( PixmapCache().getIcon( "gotoline.png" ) )
        self.goButton.setFocusPolicy( Qt.NoFocus )
        self.goButton.setEnabled( False )
        self.goButton.clicked.connect( self.__onGo )

        spacer = QWidget()
        spacer.setFixedWidth( 1 )

        horizontalLayout = QHBoxLayout( self )
        horizontalLayout.setMargin( 0 )

        horizontalLayout.addWidget( closeButton )
        horizontalLayout.addWidget( lineLabel )
        horizontalLayout.addWidget( self.linenumberEdit )
        horizontalLayout.addWidget( self.goButton )
        horizontalLayout.addWidget( spacer )
        return

    def keyPressEvent( self, event ):
        """ Handles the key press events """

        if event.key() == Qt.Key_Escape:
            activeWindow = self.editorsManager.currentWidget()
            if activeWindow:
                activeWindow.setFocus()
            event.accept()
            self.hide()
        return

    def __updateHistory( self, txt ):
        " Updates the combo history "

        while txt in self.__gotoHistory:
            self.__gotoHistory.remove( txt )
        self.__gotoHistory = [ txt ] + self.__gotoHistory
        self.__gotoHistory = self.__gotoHistory[ : GotoLineWidget.maxHistory ]

        self.linenumberEdit.clear()
        self.linenumberEdit.addItems( self.__gotoHistory )
        return

    def show( self ):
        " Overriden show() method "
        self.linenumberEdit.lineEdit().selectAll()
        QWidget.show( self )
        self.activateWindow()
        return

    def setFocus( self ):
        " Overridded setFocus "
        self.linenumberEdit.setFocus()
        return

    def updateStatus( self ):
        " Triggered when the current tab is changed "
        currentWidget = self.editorsManager.currentWidget()
        status = currentWidget.getType() in \
                    [ MainWindowTabWidgetBase.PlainTextEditor,
                      MainWindowTabWidgetBase.VCSAnnotateViewer ]
        self.linenumberEdit.setEnabled( status )
        self.goButton.setEnabled( status and
                                  self.linenumberEdit.currentText() != "" )
        return

    def __onGo( self ):
        " Triggered when the 'Go!' button is clicked "
        if self.linenumberEdit.currentText() == "":
            return

        currentWidget = self.editorsManager.currentWidget()
        if not currentWidget.getType() in \
                    [ MainWindowTabWidgetBase.PlainTextEditor,
                      MainWindowTabWidgetBase.VCSAnnotateViewer ]:
            return

        txt = self.linenumberEdit.currentText()
        self.__updateHistory( txt )
        editor = currentWidget.getEditor()
        line = min( int( txt ), editor.lines() ) - 1

        editor.setCursorPosition( line, 0 )
        editor.ensureLineVisible( line )
        currentWidget.setFocus()
        return

    def __onEditTextChanged( self, text ):
        " Triggered when the text has been changed "
        self.goButton.setEnabled( text != "" )
        return

    def __onEnter( self ):
        " Triggered when 'Enter' or 'Return' is clicked "
        self.__onGo()
        return

    def selectAll( self ):
        " Selects the line edit content "
        self.linenumberEdit.lineEdit().selectAll()
        return
예제 #19
0
class LayerSelectionPage(QFrame):
    #TODO. Filtering, (visible) row selection, multi selection
    colparams = ((0,65,'Name'), (1,235,'Title'), (2,350,'Keywords'))
    XFER_BW = 40
    def __init__(self, parent=None):
        super(LayerSelectionPage, self).__init__(parent)
        self.parent = parent
        
        #convenience link
        self.confconn_link = self.parent.parent.confconn
        
        #flag top prevent read read action on keyword delete. New logic makes this redundant
        #self.keywordbypass = False

        QToolTip.setFont(QFont('SansSerif', 10))
        
        #label
        filterlabel = QLabel('Filter')
        availablelabel = QLabel('Available Layers')
        selectionlabel = QLabel('Layer Selections')
        keywordlabel = QLabel('Keyword')
        explainlabel = QLabel("Edit Group assignments using this dialog or to simply initialise the Layer-Config just click 'Finish'")
        
        #selection buttons
        chooseallbutton = QPushButton('>>')
        chooseallbutton.setFixedWidth(self.XFER_BW)
        chooseallbutton.clicked.connect(self.doChooseAllClickAction)
        
        choosebutton = QPushButton('>')
        choosebutton.setFixedWidth(self.XFER_BW)
        choosebutton.clicked.connect(self.doChooseClickAction)
        
        rejectbutton = QPushButton('<')
        rejectbutton.setFixedWidth(self.XFER_BW)
        rejectbutton.clicked.connect(self.doRejectClickAction)
        
        rejectallbutton = QPushButton('<<')
        rejectallbutton.setFixedWidth(self.XFER_BW)
        rejectallbutton.clicked.connect(self.doRejectAllClickAction)
        
        #operation buttons        
        finishbutton = QPushButton('Finish')
        finishbutton.setToolTip('Finish and Close layer selection dialog')
        finishbutton.clicked.connect(self.parent.close)
        
        resetbutton = QPushButton('Reset')
        resetbutton.font()
        resetbutton.setToolTip('Read Layer from LDS GetCapabilities request. Overwrites current Layer Config')       
        resetbutton.clicked.connect(self.doResetClickAction)
        
        self.available_sfpm = LDSSFPAvailableModel(self)
        self.selection_sfpm = LDSSFPSelectionModel(self)
        
        self.available_sfpm.setSourceModel(self.parent.available_model)
        self.selection_sfpm.setSourceModel(self.parent.selection_model)
        
        #textedits
        filteredit = QLineEdit('')
        filteredit.setToolTip('Filter Available-Layers pane (filter operates across Name and Title fields and accepts Regex expressions)')       
        filteredit.textChanged.connect(self.available_sfpm.setActiveFilter)
        
        self.keywordcombo = QComboBox()
        self.keywordcombo.setToolTip('Select or Add a unique identifier to be saved in layer config (keyword)')
        self.keywordcombo.addItems(list(self.confconn_link.assigned))
        self.keywordcombo.setEditable(True)
        self.keywordcombo.activated.connect(self.doKeyComboChangeAction)
        
        lgindex = self.confconn_link.getLayerGroupIndex(self.confconn_link.lgval,col=1)
        lgentry = self.confconn_link.lglist[lgindex] if LU.assessNone(lgindex) else None
        #keywordedit = self.keywordcombo.lineEdit().text().toUtf8().data().decode('utf8')# for writing
        #if no entry or layer indicated then blank 
        self.keywordcombo.lineEdit().setText('' if lgentry is None or lgentry[0]==LORG.LAYER else lgentry[1])#self.confconn_link.lgval)#TODO. group only
        
        #header
        headmodel = QStandardItemModel()
        headmodel.setHorizontalHeaderLabels([i[2] for i in self.colparams][:self.parent.available_model.columnCount()])
        
        headview1 = QHeaderView(Qt.Horizontal)
        headview1.setModel(headmodel)
        headview1.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) 
        
        headview2 = QHeaderView(Qt.Horizontal)
        headview2.setModel(headmodel)
        headview2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)            

        #table
        self.available = QTableView()
        self.available.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.available.setSelectionMode(QAbstractItemView.MultiSelection)       
        
        self.selection = QTableView()
        self.selection.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.selection.setSelectionMode(QAbstractItemView.MultiSelection)
        
        #interesting, must set model after selection attributes but before headers else row selections/headers don't work properly
        self.available.setModel(self.available_sfpm)
        self.selection.setModel(self.selection_sfpm)
        
        self.available.setSortingEnabled(True)
        self.available.setHorizontalHeader(headview1)
        
        self.selection.setSortingEnabled(True)
        self.selection.setHorizontalHeader(headview2)

        for cp in self.colparams:
            self.available.setColumnWidth(cp[0],cp[1])
            self.selection.setColumnWidth(cp[0],cp[1])

        self.available.verticalHeader().setVisible(False)
        self.available.horizontalHeader().setVisible(True)
        
        self.selection.verticalHeader().setVisible(False)
        self.selection.horizontalHeader().setVisible(True)
        
        
        #layout  
        vbox00 = QVBoxLayout()
        vbox00.addWidget(availablelabel)
        vbox00.addWidget(self.available)
        
        vbox01 = QVBoxLayout()
        vbox01.addWidget(chooseallbutton)
        vbox01.addWidget(choosebutton)
        vbox01.addWidget(rejectbutton)
        vbox01.addWidget(rejectallbutton)
        
        vbox02 = QVBoxLayout()
        vbox02.addWidget(selectionlabel)
        vbox02.addWidget(self.selection)

        
        vbox10 = QVBoxLayout()
        vbox10.addWidget(filterlabel)
        vbox10.addWidget(filteredit)
        
        hbox12 = QHBoxLayout()
        hbox12.addWidget(keywordlabel)
        hbox12.addStretch(1)
        #hbox12.addWidget(inspbutton)
        #hbox12.addWidget(addbutton)
        #hbox12.addWidget(delbutton)
        
        vbox12 = QVBoxLayout()
        vbox12.addLayout(hbox12)
        vbox12.addWidget(self.keywordcombo)
                
        #00|01|02
        #10|11|12
        grid0 = QGridLayout()
        grid0.addLayout(vbox00,1,0)
        grid0.addLayout(vbox01,1,1)
        grid0.addLayout(vbox02,1,2)
        grid0.addLayout(vbox10,0,0)
        grid0.addLayout(vbox12,0,2)
        
        
        hbox2 = QHBoxLayout()
        hbox2.addWidget(resetbutton)
        hbox2.addStretch(1)
        hbox2.addWidget(explainlabel)
        hbox2.addWidget(finishbutton)
        #gbox1.setLayout(hbox2)
        
        
        
        vbox3 = QVBoxLayout()
        vbox3.addLayout(grid0)
        #vbox3.addLayout(hbox3)
        #vbox3.addWidget(line0)
        vbox3.addLayout(hbox2)
        
        self.setLayout(vbox3)


            
    def doChooseAllClickAction(self):
        '''Moves the lot to Selected'''
        #ktext = LU.recode(self.keywordcombo.lineEdit().text().toUtf8().data())
        ktext = LU.recode(LQ.readWidgetText(self.keywordcombo.lineEdit().text()))
        if not self.checkKeyword(ktext): return
        #------------------------------
        self.parent.signalModels(self.parent.STEP.PRE)
        #self.parent.selection_model.mdata += self.parent.available_model.mdata
        self.parent.selection_model.initData(self.confconn_link.complete)
        self.parent.available_model.initData([])
        self.parent.signalModels(self.parent.STEP.POST)
        #------------------------------
        self.parent.writeKeysToLayerConfig(ktext)
        #self.confconn_link.setupAssignedLayerList()
        if self.keywordcombo.findText(ktext) == -1:
            self.keywordcombo.addItem(ktext)
    
    def doChooseClickAction(self):
        '''Takes available selected and moves to selection'''
        #ktext = LU.recode(self.keywordcombo.lineEdit().text().toUtf8().data())
        ktext = LU.recode(LQ.readWidgetText(self.keywordcombo.lineEdit().text()))
        #ktext = str(self.keywordcombo.lineEdit().text())
        if not self.checkKeyword(ktext): return
        #------------------------------
        select = self.available.selectionModel()
        if select.hasSelection():
            self.transferSelectedRows(select.selectedRows(),self.available_sfpm,self.selection_sfpm)
            #------------------------------
            self.parent.writeKeysToLayerConfig(ktext)
            #self.confconn_link.assigned = self.confconn_link.setupAssignedLayerList()
            # -1 to indicate no index since 0,1,... are valid
            if self.keywordcombo.findText(ktext) == -1:
                self.keywordcombo.addItem(ktext)
        else:
            ldslog.warn('L2R > Transfer action without selection')        
        #TRACE#
        #pdb.set_trace()
        self.available.clearSelection()
          
        
    def transferSelectedRows(self,indices,from_model,to_model):
        tlist = []
        for proxymodelindex in indices:
            transfer = from_model.getData(proxymodelindex)
            tlist.append((proxymodelindex,transfer),)

        to_model.addData([t[1] for t in tlist])
        from_model.delData([t[0] for t in tlist])
        return tlist
            
    def doRejectClickAction(self):
        '''Takes available selected and moves to selection'''
        #ktext = LU.recode(self.keywordcombo.lineEdit().text().toUtf8().data())
        ktext = LU.recode(LQ.readWidgetText(self.keywordcombo.lineEdit().text()))
        if not self.checkKeyword(ktext): return
        #------------------------------
        select = self.selection.selectionModel()
        if select.hasSelection():
            tlist = self.transferSelectedRows(select.selectedRows(),self.selection_sfpm,self.available_sfpm)
            #------------------------------
            kindex = self.keywordcombo.findText(ktext)
            remainder = self.parent.deleteKeysFromLayerConfig([ll[1][0] for ll in tlist],ktext)
            if remainder > 0 and kindex == -1:
                #items+newkey -> add
                self.parent.writeKeysToLayerConfig(ktext)
                self.keywordcombo.addItem(ktext)
            elif remainder == 0 and kindex > -1:
                #empty+oldkey -> del
                self.keywordcombo.removeItem(kindex)
                self.keywordcombo.clearEditText()
        else:
            ldslog.warn('R2L < Transfer action without selection')
        #TRACE#
        #pdb.set_trace()
        self.selection.clearSelection()

                
    def doRejectAllClickAction(self):
        #ktext = LU.recode(self.keywordcombo.lineEdit().text().toUtf8().data())
        ktext = LU.recode(LQ.readWidgetText(self.keywordcombo.lineEdit().text()))
        if not self.checkKeyword(ktext): return
        #------------------------------
        self.parent.deleteKeysFromLayerConfig([ll[0] for ll in self.parent.selection_model.mdata],ktext)
        #------------------------------
        self.parent.signalModels(self.parent.STEP.PRE)
        #self.parent.available_model.mdata += self.parent.selection_model.mdata
        self.parent.available_model.initData(self.confconn_link.complete)
        self.parent.selection_model.initData([])
        self.parent.signalModels(self.parent.STEP.POST)        
        #------------------------------
        #self.confconn_link.setupAssignedLayerList()
        #self.keywordbypass = True
        self.keywordcombo.removeItem(self.keywordcombo.findText(ktext))
        self.keywordcombo.clearEditText()
        
    def doKeyComboChangeAction(self):
        '''Reset the available pane and if there is anything in the keyword box use this to init the selection pane'''
        #HACK
        #if self.keywordbypass:
        #    self.keywordbypass = False
        #    return
        #------------------------------
        #ktext = LU.recode(self.keywordcombo.lineEdit().text().toUtf8().data())
        ktext = LU.recode(LQ.readWidgetText(self.keywordcombo.lineEdit().text()))
        #------------------------------
        av_sl = self.parent.splitData(ktext,self.confconn_link.complete)
        #av_sl = self.parent.splitData(ktext,self.confconn_link.complete)
        self.parent.signalModels(self.parent.STEP.PRE)
        self.parent.available_model.initData(av_sl[0])
        self.parent.selection_model.initData(av_sl[1])
        self.parent.signalModels(self.parent.STEP.POST)
    
    def doResetClickAction(self):
        '''Dumps the LC and rebuilds from a fresh read of the caps doc'''
        #int warning (QWidget parent, QString title, QString text, QString button0Text, QString button1Text = QString(), QString button2Text = QString(), int defaultButtonNumber = 0, int escapeButtonNumber = -1)
        ans = QMessageBox.warning(self, "Reset","This action will overwrite your Layer Configuration using the current LDS settings (potentially adding new or removing layers). Continue?","Continue","Cancel")
        if ans:
            #Cancel
            ldslog.warn('Cancelling Reset operation')
            return
        #Continue
        ldslog.warn('Reset Layer Config')
        self.parent.resetLayers()
        self.keywordcombo.clear()

    def checkKeyword(self,ktext):
        '''Checks keyword isn't null and isn't part of the LDS supplied keywords'''
        if LU.assessNone(ktext) is None:
            QMessageBox.about(self, "Keyword Required","Please enter a Keyword to assign Layer(s) to")
            return False
        if ktext in self.confconn_link.reserved:
            QMessageBox.about(self, "Reserved Keyword","'{}' is a reserved keyword, please select again".format(ktext))
            return False
        return True
예제 #20
0
class Widget(QWidget):
    def __init__(self, dockwidget):
        super(Widget, self).__init__(dockwidget)
        self._document = None
        self._fileSelector = QComboBox(editable=True,
                                       insertPolicy=QComboBox.NoInsert)
        widgets.drag.ComboDrag(self._fileSelector).role = Qt.UserRole
        self._fileSelector.lineEdit().setReadOnly(True)
        self._fileSelector.lineEdit().setFocusPolicy(Qt.NoFocus)
        self._stopButton = QToolButton()
        self._playButton = QToolButton()
        self._timeSlider = QSlider(Qt.Horizontal,
                                   tracking=False,
                                   singleStep=500,
                                   pageStep=5000,
                                   invertedControls=True)
        self._display = Display()
        self._tempoFactor = QSlider(Qt.Vertical,
                                    minimum=-50,
                                    maximum=50,
                                    singleStep=1,
                                    pageStep=5)

        grid = QGridLayout(spacing=0)
        self.setLayout(grid)

        grid.addWidget(self._fileSelector, 0, 0, 1, 3)
        grid.addWidget(self._stopButton, 1, 0)
        grid.addWidget(self._playButton, 1, 1)
        grid.addWidget(self._timeSlider, 1, 2)
        grid.addWidget(self._display, 2, 0, 1, 3)
        grid.addWidget(self._tempoFactor, 0, 3, 3, 1)

        # size policy of combo
        p = self._fileSelector.sizePolicy()
        p.setHorizontalPolicy(QSizePolicy.Ignored)
        self._fileSelector.setSizePolicy(p)

        # size policy of combo popup
        p = self._fileSelector.view().sizePolicy()
        p.setHorizontalPolicy(QSizePolicy.MinimumExpanding)
        self._fileSelector.view().setSizePolicy(p)

        self._player = player.Player()
        self._outputCloseTimer = QTimer(interval=60000,
                                        singleShot=True,
                                        timeout=self.closeOutput)
        self._timeSliderTicker = QTimer(interval=200,
                                        timeout=self.updateTimeSlider)
        self._fileSelector.activated[int].connect(self.slotFileSelected)
        self._tempoFactor.valueChanged.connect(self.slotTempoChanged)
        self._timeSlider.valueChanged.connect(self.slotTimeSliderChanged)
        self._timeSlider.sliderMoved.connect(self.slotTimeSliderMoved)
        self._player.beat.connect(self.updateDisplayBeat)
        self._player.time.connect(self.updateDisplayTime)
        self._player.stateChanged.connect(self.slotPlayerStateChanged)
        self.slotPlayerStateChanged(False)
        dockwidget.mainwindow().currentDocumentChanged.connect(
            self.loadResults)
        app.documentLoaded.connect(self.slotUpdatedFiles)
        app.jobFinished.connect(self.slotUpdatedFiles)
        app.aboutToQuit.connect(self.stop)
        midihub.aboutToRestart.connect(self.slotAboutToRestart)
        midihub.settingsChanged.connect(self.clearMidiSettings, -100)
        midihub.settingsChanged.connect(self.readMidiSettings)
        app.documentClosed.connect(self.slotDocumentClosed)
        app.translateUI(self)
        self.readMidiSettings()
        d = dockwidget.mainwindow().currentDocument()
        if d:
            self.loadResults(d)

    def translateUI(self):
        self._tempoFactor.setToolTip(_("Tempo"))

    def slotAboutToRestart(self):
        self.stop()
        self._player.set_output(None)

    def clearMidiSettings(self):
        """Called first when settings are changed."""
        self.stop()
        self._outputCloseTimer.stop()
        self._player.set_output(None)

    def readMidiSettings(self):
        """Called after clearMidiSettings(), and on first init."""
        pass

    def openOutput(self):
        """Called when playing starts. Ensures an output port is opened."""
        self._outputCloseTimer.stop()
        if not self._player.output():
            p = QSettings().value("midi/player/output_port",
                                  midihub.default_output(), type(""))
            o = midihub.output_by_name(p)
            if o:
                self._player.set_output(output.Output(o))

    def closeOutput(self):
        """Called when the output close timer fires. Closes the output."""
        self._player.set_output(None)

    def slotPlayerStateChanged(self, playing):
        ac = self.parentWidget().actionCollection
        # setDefaultAction also adds the action
        for b in self._stopButton, self._playButton:
            while b.actions():
                b.removeAction(b.actions()[0])
        if playing:
            self._timeSliderTicker.start()
            self._stopButton.setDefaultAction(ac.midi_stop)
            self._playButton.setDefaultAction(ac.midi_pause)
        else:
            self._timeSliderTicker.stop()
            self.updateTimeSlider()
            self._stopButton.setDefaultAction(ac.midi_restart)
            self._playButton.setDefaultAction(ac.midi_play)
            # close the output if the preference is set
            if QSettings().value("midi/close_outputs", False, bool):
                self._outputCloseTimer.start()

    def play(self):
        """Starts the MIDI player, opening an output if necessary."""
        if not self._player.is_playing() and not self._player.has_events():
            self.restart()
        self.openOutput()
        if not self._player.output():
            self._display.statusMessage(_("No output found!"))
        self._player.start()

    def stop(self):
        """Stops the MIDI player."""
        self._player.stop()

    def restart(self):
        """Restarts the MIDI player.
        
        If another file is in the file selector, or the file was updated,
        the new file is loaded.
        
        """
        self._player.seek(0)
        self.updateTimeSlider()
        self._display.reset()
        if self._document:
            files = midifiles.MidiFiles.instance(self._document)
            index = self._fileSelector.currentIndex()
            if files and (files.song(index) is not self._player.song()):
                self.loadSong(index)

    def slotTempoChanged(self, value):
        """Called when the user drags the tempo."""
        # convert -50 to 50 to 0.5 to 2.0
        factor = 2**(value / 50.0)
        self._player.set_tempo_factor(factor)
        self._display.setTempo("{0}%".format(int(factor * 100)))

    def slotTimeSliderChanged(self, value):
        self._player.seek(value)
        self._display.setTime(value)
        if self._player.song():
            self._display.setBeat(*self._player.song().beat(value)[1:])

    def slotTimeSliderMoved(self, value):
        self._display.setTime(value)
        if self._player.song():
            self._display.setBeat(*self._player.song().beat(value)[1:])

    def updateTimeSlider(self):
        if not self._timeSlider.isSliderDown():
            with qutil.signalsBlocked(self._timeSlider):
                self._timeSlider.setMaximum(self._player.total_time())
                self._timeSlider.setValue(self._player.current_time())

    def updateDisplayBeat(self, measnum, beat, num, den):
        if not self._timeSlider.isSliderDown():
            self._display.setBeat(measnum, beat, num, den)

    def updateDisplayTime(self, time):
        if not self._timeSlider.isSliderDown():
            self._display.setTime(time)

    def slotUpdatedFiles(self, document):
        """Called when there are new MIDI files."""
        if document == self.parentWidget().mainwindow().currentDocument():
            self.loadResults(document)

    def loadResults(self, document):
        self._document = document
        files = midifiles.MidiFiles.instance(document)
        self._fileSelector.setModel(files.model())
        if files:
            self._fileSelector.setCurrentIndex(files.current)
            if not self._player.is_playing():
                self.loadSong(files.current)

    def loadSong(self, index):
        files = midifiles.MidiFiles.instance(self._document)
        self._player.set_song(files.song(index))
        m, s = divmod(self._player.total_time() // 1000, 60)
        name = self._fileSelector.currentText()
        self.updateTimeSlider()
        self._display.reset()
        self._display.statusMessage(_("midi lcd screen", "LOADED"), name,
                                    _("midi lcd screen", "TOTAL"),
                                    "{0}:{1:02}".format(m, s))

    def slotFileSelected(self, index):
        if self._document:
            self._player.stop()
            files = midifiles.MidiFiles.instance(self._document)
            if files:
                files.current = index
                self.restart()

    def slotDocumentClosed(self, document):
        if document == self._document:
            self._document = None
            self._fileSelector.clear()
            self._player.stop()
            self._player.clear()
            self.updateTimeSlider()
            self._display.reset()
예제 #21
0
class Printing(preferences.Group):
    def __init__(self, page):
        super(Printing, self).__init__(page)
        
        layout = QGridLayout(spacing=1)
        self.setLayout(layout)
        
        self.messageLabel = QLabel(wordWrap=True)
        self.printCommandLabel = QLabel()
        self.printCommand = widgets.urlrequester.UrlRequester()
        self.printCommand.setFileMode(QFileDialog.ExistingFile)
        self.printCommand.changed.connect(page.changed)
        self.printDialogCheck = QCheckBox(toggled=page.changed)
        self.resolutionLabel = QLabel()
        self.resolution = QComboBox(editable=True, editTextChanged=page.changed)
        self.resolution.addItems("300 600 1200".split())
        self.resolution.lineEdit().setInputMask("9000")

        layout.addWidget(self.messageLabel, 0, 0, 1, 2)
        layout.addWidget(self.printCommandLabel, 1, 0)
        layout.addWidget(self.printCommand, 1, 1)
        layout.addWidget(self.printDialogCheck, 2, 0, 1, 2)
        layout.addWidget(self.resolutionLabel, 3, 0)
        layout.addWidget(self.resolution, 3, 1)
        
        app.translateUI(self)
    
    def translateUI(self):
        self.setTitle(_("Printing Music"))
        self.messageLabel.setText(_(
            "Here you can enter a command to print a PDF or PostScript file. "
            "See the Help page for more information about printing music."))
        self.printCommandLabel.setText(_("Printing command:"))
        self.printCommand.setToolTip('<qt>' + _(
            "The printing command is used to print a PostScript or PDF file. "
            "On Linux you don't need this, but on Windows and Mac OS X you can "
            "provide a command to avoid that PDF documents are being printed "
            "using raster images, which is less optimal.\n"
            "<code>$pdf</code> gets replaced with the PDF filename, or alternatively, "
            "<code>$ps</code> is replaced with the PostScript filename. "
            "<code>$printer</code> is replaced with the printer's name to use."))
        self.printDialogCheck.setText(_("Use Frescobaldi's print dialog"))
        self.printDialogCheck.setToolTip('<qt>' + _(
            "If enabled, Frescobaldi will show the print dialog and create a "
            "PDF or PostScript document containing only the selected pages "
            "to print. Otherwise, the command is called directly and is expected "
            "to show a print dialog itself."))
        self.resolutionLabel.setText(_("Resolution:"))
        self.resolution.setToolTip(_(
            "Set the resolution if Frescobaldi prints using raster images."))
    
    def loadSettings(self):
        s = QSettings()
        s.beginGroup("helper_applications")
        self.printCommand.setPath(s.value("printcommand", "", type("")))
        self.printDialogCheck.setChecked(s.value("printcommand/dialog", False, bool))
        with qutil.signalsBlocked(self.resolution):
            self.resolution.setEditText(format(s.value("printcommand/dpi", 300, int)))
    
    def saveSettings(self):
        s= QSettings()
        s.beginGroup("helper_applications")
        s.setValue("printcommand", self.printCommand.path())
        s.setValue("printcommand/dialog", self.printDialogCheck.isChecked())
        s.setValue("printcommand/dpi", int(self.resolution.currentText()))
예제 #22
0
class Dialog(QDialog):
    def __init__(self, parent=None):
        super(Dialog, self).__init__(parent)
        self._filename = None
        self._page = None
        self._rect = None
        self.imageViewer = widgets.imageviewer.ImageViewer()
        self.dpiLabel = QLabel()
        self.dpiCombo = QComboBox(insertPolicy=QComboBox.NoInsert,
                                  editable=True)
        self.dpiCombo.lineEdit().setCompleter(None)
        self.dpiCombo.setValidator(
            QDoubleValidator(10.0, 1200.0, 4, self.dpiCombo))
        self.dpiCombo.addItems(
            [format(i) for i in (72, 100, 200, 300, 600, 1200)])

        self.colorButton = widgets.colorbutton.ColorButton()
        self.colorButton.setColor(QColor(Qt.white))
        self.crop = QCheckBox()
        self.antialias = QCheckBox(checked=True)
        self.dragfile = QPushButton(icons.get("image-x-generic"), None, None)
        self.fileDragger = FileDragger(self.dragfile)
        self.buttons = QDialogButtonBox(QDialogButtonBox.Close)
        self.copyButton = self.buttons.addButton('',
                                                 QDialogButtonBox.ApplyRole)
        self.copyButton.setIcon(icons.get('edit-copy'))
        self.saveButton = self.buttons.addButton('',
                                                 QDialogButtonBox.ApplyRole)
        self.saveButton.setIcon(icons.get('document-save'))

        layout = QVBoxLayout()
        self.setLayout(layout)

        layout.addWidget(self.imageViewer)

        controls = QHBoxLayout()
        layout.addLayout(controls)
        controls.addWidget(self.dpiLabel)
        controls.addWidget(self.dpiCombo)
        controls.addWidget(self.colorButton)
        controls.addWidget(self.crop)
        controls.addWidget(self.antialias)
        controls.addStretch()
        controls.addWidget(self.dragfile)
        layout.addWidget(widgets.Separator())
        layout.addWidget(self.buttons)

        app.translateUI(self)
        self.readSettings()
        self.finished.connect(self.writeSettings)
        self.dpiCombo.editTextChanged.connect(self.drawImage)
        self.colorButton.colorChanged.connect(self.drawImage)
        self.antialias.toggled.connect(self.drawImage)
        self.crop.toggled.connect(self.cropImage)
        self.buttons.rejected.connect(self.reject)
        self.copyButton.clicked.connect(self.copyToClipboard)
        self.saveButton.clicked.connect(self.saveAs)
        qutil.saveDialogSize(self, "copy_image/dialog/size", QSize(480, 320))

    def translateUI(self):
        self.setCaption()
        self.dpiLabel.setText(_("DPI:"))
        self.colorButton.setToolTip(_("Paper Color"))
        self.crop.setText(_("Auto-crop"))
        self.antialias.setText(_("Antialias"))
        self.dragfile.setText(_("Drag"))
        self.dragfile.setToolTip(_("Drag the image as a PNG file."))
        self.copyButton.setText(_("&Copy to Clipboard"))
        self.saveButton.setText(_("&Save As..."))
        self.imageViewer.setWhatsThis(
            _(
                #xgettext:no-python-format
                "<p>\n"
                "Clicking toggles the display between 100% size and window size. "
                "Drag to copy the image to another application. "
                "Drag with Ctrl (or {command}) to scroll a large image.\n"
                "</p>\n"
                "<p>\n"
                "You can also drag the small picture icon in the bottom right, "
                "which drags the actual file on disk, e.g. to an e-mail message.\n"
                "</p>").format(command="\u2318"))

    def readSettings(self):
        s = QSettings()
        s.beginGroup('copy_image')
        self.dpiCombo.setEditText(s.value("dpi", "100", type("")))
        self.colorButton.setColor(
            s.value("papercolor", QColor(Qt.white), QColor))
        self.crop.setChecked(s.value("autocrop", False, bool))
        self.antialias.setChecked(s.value("antialias", True, bool))

    def writeSettings(self):
        s = QSettings()
        s.beginGroup('copy_image')
        s.setValue("dpi", self.dpiCombo.currentText())
        s.setValue("papercolor", self.colorButton.color())
        s.setValue("autocrop", self.crop.isChecked())
        s.setValue("antialias", self.antialias.isChecked())

    def setCaption(self):
        if self._filename:
            filename = os.path.basename(self._filename)
        else:
            filename = _("<unknown>")
        title = _("Image from {filename}").format(filename=filename)
        self.setWindowTitle(app.caption(title))

    def setPage(self, page, rect):
        self._page = page
        self._rect = rect
        self._filename = documents.filename(page.document())
        self.fileDragger.basename = os.path.splitext(
            os.path.basename(self._filename))[0]
        self.setCaption()
        self.drawImage()

    def drawImage(self):
        dpi = float(self.dpiCombo.currentText() or '100')
        dpi = max(dpi, self.dpiCombo.validator().bottom())
        dpi = min(dpi, self.dpiCombo.validator().top())
        options = qpopplerview.RenderOptions()
        options.setPaperColor(self.colorButton.color())
        if self.antialias.isChecked():
            if popplerqt4:
                options.setRenderHint(
                    popplerqt4.Poppler.Document.Antialiasing
                    | popplerqt4.Poppler.Document.TextAntialiasing)
        else:
            options.setRenderHint(0)
        self._image = self._page.image(self._rect, dpi, dpi, options)
        self.cropImage()

    def cropImage(self):
        image = self._image
        if self.crop.isChecked():
            image = image.copy(autoCropRect(image))
        self.imageViewer.setImage(image)
        self.fileDragger.setImage(image)

    def copyToClipboard(self):
        QApplication.clipboard().setImage(self.imageViewer.image())

    def saveAs(self):
        if self._filename and not self.imageViewer.image().isNull():
            filename = os.path.splitext(self._filename)[0] + ".png"
        else:
            filename = 'image.png'
        filename = QFileDialog.getSaveFileName(self, _("Save Image As"),
                                               filename)
        if filename:
            if not self.imageViewer.image().save(filename):
                QMessageBox.critical(self, _("Error"),
                                     _("Could not save the image."))
            else:
                self.fileDragger.currentFile = filename