예제 #1
0
파일: orange.py 프로젝트: scroll/orange
class ProgramCache(object):
    def __init__(self, ctx):
        from PyQt4.QtCore import QFileSystemWatcher
        self.ctx = ctx
        self.cache = {}
        self.watcher = QFileSystemWatcher()
        self.watcher.fileChanged.connect(self._file_changed)
        self.options = '-I ' + os.path.join(os.path.dirname(__file__), 'kernels')

    def _file_changed(self, path):
        path = str(path)
        del self.cache[path]
        self.watcher.removePath(path)

    def get(self, path):
        prog = self.cache.get(path, None)
        if not prog:
            src = open(path).read()
            prog = cl.Program(self.ctx, src).build(self.options)
            log.info('%s succesfully compiled' % path)
            self.cache[path] = prog
            self.watcher.addPath(path)
        return prog
예제 #2
0
class ProgramCache(object):
    def __init__(self, ctx):
        from PyQt4.QtCore import QFileSystemWatcher
        self.ctx = ctx
        self.cache = {}
        self.watcher = QFileSystemWatcher()
        self.watcher.fileChanged.connect(self._file_changed)
        self.options = '-I ' + os.path.join(os.path.dirname(__file__),
                                            'kernels')

    def _file_changed(self, path):
        path = str(path)
        del self.cache[path]
        self.watcher.removePath(path)

    def get(self, path):
        prog = self.cache.get(path, None)
        if not prog:
            src = open(path).read()
            prog = cl.Program(self.ctx, src).build(self.options)
            log.info('%s succesfully compiled' % path)
            self.cache[path] = prog
            self.watcher.addPath(path)
        return prog
예제 #3
0
class ProjectWidget(Ui_Form, QWidget):
    SampleWidgetRole = Qt.UserRole + 1
    projectsaved = pyqtSignal()
    projectupdated = pyqtSignal()
    projectloaded = pyqtSignal(object)
    selectlayersupdated = pyqtSignal(list)

    def __init__(self, parent=None):
        super(ProjectWidget, self).__init__(parent)
        self.setupUi(self)
        self.project = None
        self.mapisloaded = False
        self.bar = None
        self.roamapp = None

        menu = QMenu()
        self.canvas.setCanvasColor(Qt.white)

        self.canvas.enableAntiAliasing(True)
        self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor)
        self.canvas.mapRenderer().setLabelingEngine(QgsPalLabeling())

        # self.roamVersionLabel.setText("You are running IntraMaps Roam version {}".format(roam.__version__))

        self.openProjectFolderButton.pressed.connect(self.openprojectfolder)
        self.openinQGISButton.pressed.connect(self.openinqgis)
        self.depolyProjectButton.pressed.connect(self.deploy_project)
        self.depolyInstallProjectButton.pressed.connect(functools.partial(self.deploy_project, True))

        self.filewatcher = QFileSystemWatcher()
        self.filewatcher.fileChanged.connect(self.qgisprojectupdated)

        self.projectupdatedlabel.linkActivated.connect(self.reloadproject)
        self.projectupdatedlabel.hide()

        # self.setpage(4)
        self.currentnode = None
        self.form = None

        qgislocation = r'C:\OSGeo4W\bin\qgis.bat'
        qgislocation = roam.config.settings.setdefault('configmanager', {}) \
            .setdefault('qgislocation', qgislocation)

        self.qgispathEdit.setText(qgislocation)
        self.qgispathEdit.textChanged.connect(self.save_qgis_path) 
        self.filePickerButton.pressed.connect(self.set_qgis_path)

    def set_qgis_path(self):
        path = QFileDialog.getOpenFileName(self, "Select QGIS install file", filter="(*.bat)")
        if not path:
            return
        self.qgispathEdit.setText(path)
        self.save_qgis_path(path)

    def save_qgis_path(self, path):
        roam.config.settings['configmanager'] = {'qgislocation': path}
        roam.config.save()

    def setpage(self, page, node):
        self.currentnode = node

        self.write_config_currentwidget()

        self.stackedWidget.setCurrentIndex(page)
        if self.project:
            print self.project.dump_settings()

        widget = self.stackedWidget.currentWidget()
        if hasattr(widget, "set_project"):
            widget.set_project(self.project, self.currentnode)

    def write_config_currentwidget(self):
        widget = self.stackedWidget.currentWidget()
        if hasattr(widget, "write_config"):
            widget.write_config()

    def deploy_project(self, with_data=False):
        if self.roamapp.sourcerun:
            base = os.path.join(self.roamapp.apppath, "..")
        else:
            base = self.roamapp.apppath

        default = os.path.join(base, "roam_serv")
        path = roam.config.settings.get("publish", {}).get("path", '')
        if not path:
            path = default

        path = os.path.join(path, "projects")

        if not os.path.exists(path):
            os.makedirs(path)

        self._saveproject()
        options = {}

        bundle.bundle_project(self.project, path, options, as_install=with_data)

    def setaboutinfo(self):
        self.versionLabel.setText(roam.__version__)
        self.qgisapiLabel.setText(str(QGis.QGIS_VERSION))

    def checkcapturelayers(self):
        haslayers = self.project.hascapturelayers()
        self.formslayerlabel.setVisible(not haslayers)
        return haslayers


    def selectlayerschanged(self, *args):
        self.formlayers.setSelectLayers(self.project.selectlayers)
        self.checkcapturelayers()
        self.selectlayersupdated.emit(self.project.selectlayers)

    def reloadproject(self, *args):
        self.setproject(self.project)
        self.projectupdated.emit()

    def qgisprojectupdated(self, path):
        self.projectupdatedlabel.show()
        self.projectupdatedlabel.setText("The QGIS project has been updated. <a href='reload'> "
                                         "Click to reload</a>. <b style=\"color:red\">Unsaved data will be lost</b>")

    def openinqgis(self):
        projectfile = self.project.projectfile
        qgislocation = r'C:\OSGeo4W\bin\qgis.bat'
        qgislocation = roam.config.settings.setdefault('configmanager', {}) \
            .setdefault('qgislocation', qgislocation)

        try:
            openqgis(projectfile, qgislocation)
        except OSError:
            self.bar.pushMessage("Looks like I couldn't find QGIS",
                                 "Check qgislocation in roam.config", QgsMessageBar.WARNING)

    def openprojectfolder(self):
        folder = self.project.folder
        openfolder(folder)

    def setproject(self, project, loadqgis=True):
        """
        Set the widgets active project.
        """
        self.mapisloaded = False
        self.filewatcher.removePaths(self.filewatcher.files())
        self.projectupdatedlabel.hide()
        self._closeqgisproject()

        if project.valid:
            self.startsettings = copy.deepcopy(project.settings)
            self.project = project
            self.projectlabel.setText(project.name)
            self.loadqgisproject(project, self.project.projectfile)
            self.filewatcher.addPath(self.project.projectfile)
            self.projectloaded.emit(self.project)

    def loadqgisproject(self, project, projectfile):
        QDir.setCurrent(os.path.dirname(project.projectfile))
        fileinfo = QFileInfo(project.projectfile)
        self.projectLocationLabel.setText("Project File: {}".format(os.path.basename(project.projectfile)))
        QgsProject.instance().read(fileinfo)

    def _closeqgisproject(self):
        if self.canvas.isDrawing():
            return

        self.canvas.freeze(True)
        QgsMapLayerRegistry.instance().removeAllMapLayers()
        self.canvas.freeze(False)

    def loadmap(self):
        if self.mapisloaded:
            return

        # This is a dirty hack to work around the timer that is in QgsMapCanvas in 2.2.
        # Refresh will stop the canvas timer
        # Repaint will redraw the widget.
        # loadmap is only called once per project load so it's safe to do this here.
        self.canvas.refresh()
        self.canvas.repaint()

        parser = roam.projectparser.ProjectParser.fromFile(self.project.projectfile)
        canvasnode = parser.canvasnode
        self.canvas.mapRenderer().readXML(canvasnode)
        self.canvaslayers = parser.canvaslayers()
        self.canvas.setLayerSet(self.canvaslayers)
        self.canvas.updateScale()
        self.canvas.refresh()

        self.mapisloaded = True

    def _saveproject(self):
        """
        Save the project config to disk.
        """
        self.write_config_currentwidget()
        # self.project.dump_settings()
        self.project.save(update_version=True)
        self.projectsaved.emit()
예제 #4
0
class ImageBrowser(QWidget, Ui_ImageBrowser):
    """Widget to browse absorption and reference images"""

    windowTitleChanged = pyqtSignal(str)
    imageChanged = pyqtSignal(dict)

    def __init__(self, settings, parent):
        super(ImageBrowser, self).__init__(parent=parent)
        self.settings = settings
        self.main_window = parent

        self.current_directory = default_image_dir
        self.path_to_dark_file = default_dark_path
        self.path_to_json_db = os.path.join(main_package_dir,
                                            'image_save_info.json')

        self.current_image_info = {}

        self.setupUi(self)
        self.loadSettings()
        self.is_cleaned = False
        self.use_roi_while_cleaning = False

        self.connectSignalsToSlots()

        self.image_list = clt.ImageList()
        self.updateFileList(new_dir=True)
        self.current_image_index = 0

        self.watcher = QFileSystemWatcher(self)
        self.watcher.addPath(self.current_directory)
        self.watcher.directoryChanged.connect(
            self.handleWatcherDirectoryChanged)
        self.updateCommentBox()

    def initialEmit(self):
        self.populateAndEmitImageInfo()

    def populateAndEmitImageInfo(self):
        """Populates current_image_info with all the required information about
        the current image. It then emits the imageChanged signal"""
        d = self.current_image_info
        index = self.imageListCombo.currentIndex()
        d['index'] = index
        d['path_to_abs'] = self.image_list.absorption_files[index]
        d['path_to_ref'] = self.image_list.reference_files[index]
        d['path_to_dark'] = self.path_to_dark_file
        d['abs_image'] = clt.readImageFile(d['path_to_abs'])
        d['ref_image'] = clt.readImageFile(d['path_to_ref'])
        d['dark_image'] = clt.readImageFile(d['path_to_dark'])

        modified_time = time.ctime(os.path.getmtime(d['path_to_abs']))
        self.fileDateTime.setText(str(modified_time))

        if self.is_cleaned and self.useCleanedCheck.checkState() == 2:
            ref_image = self.clean_ref_images[index]
        else:
            ref_image = d['ref_image']
        d['div_image'] = clt.dividedImage(d['abs_image'], ref_image,
                                          d['dark_image'],
                                          od_minmax=self.getODMinMax(),
                                          correct_od_saturation=self.getODSaturationParms(),
                                          correct_saturation=self.getSaturationParms())
        d['image_type'] = self.getImageType()
        key = d['path_to_abs']
        if key not in self.global_save_info:
            self.global_save_info[key] = {}
        d['save_info'] = self.global_save_info[key]
        # d['save_info']['comment'] = str(self.commentTextEdit.toPlainText())

        self.imageChanged.emit(d)

    def getImageType(self):
        imtype = str(self.imageTypeCombo.currentText())
        imcode = {'Absorption': 'abs_image', 'Reference': 'ref_image',
                  'Divided': 'div_image', 'Dark': 'dark_image'}
        return imcode[imtype]

    def handleImageIndexValueChanged(self, new_index):
        """Slot: called when the user changes the current index."""
        # just update imageList. handleImageListIndexChanged will take care of
        # the rest
        self.imageListCombo.setCurrentIndex(new_index)

    def handleImageListIndexChanged(self, new_index):
        """Slot: called when the user changes the current image in the combo
        box."""
        # we need to update imageIndexSpin, but also want to avoid recursive
        # updates. Hence we disconnect slots before updating.

        self.saveImageInfo()

        self.imageIndexSpin.valueChanged.disconnect(
            self.handleImageIndexValueChanged)
        self.imageIndexSpin.setValue(new_index)
        self.imageIndexSpin.valueChanged.connect(
            self.handleImageIndexValueChanged)

        self.current_image_index = new_index
        self.updateCommentBox()
        self.populateAndEmitImageInfo()

    def saveImageInfo(self):
        """Get save_info contents of current_image_info and save it in
        global_save_info.

        TODO: write better description of this function.
        """
        comment = str(self.commentTextEdit.toPlainText())
        print(self.current_image_index)
        key = self.image_list.absorption_files[self.current_image_index]
        # if key not in self.global_save_info:
        #     self.global_save_info[key] = {}
        self.global_save_info[key] = self.current_image_info['save_info']
        self.global_save_info[key]['comment'] = comment

    def updateCommentBox(self):
        """Updates comment box to display comment for the current image."""
        key = self.image_list.absorption_files[self.current_image_index]
        if key not in self.global_save_info:
            self.commentTextEdit.setPlainText('')
        else:
            if 'comment' not in self.global_save_info[key]:
                self.global_save_info[key]['comment'] = ''
            comment = self.global_save_info[key]['comment']
            self.commentTextEdit.setPlainText(comment)

    def connectSignalsToSlots(self):
        self.imageIndexSpin.valueChanged.connect(
            self.handleImageIndexValueChanged)
        self.imageListCombo.currentIndexChanged.connect(
            self.handleImageListIndexChanged)

    def updateFileList(self, new_dir=False):
        """Updates image_list to reflect files in current_directory.

        Pass new_dir=True if current_directory has changed.

        If an error occured, gives the user the option to select a different
        directory."""
        done = False
        while not done:
            try:
                if new_dir:
                    self.image_list.updateFileList(self.current_directory)
                else:
                    self.image_list.updateFileList()
            except clt.ImageListError as err:
                info = (' Would you like to open a different directory?'
                        ' Press cancel to quit')
                pressed = QMessageBox.question(self, 'Error opening directory',
                                               str(err) + info,
                                               QMessageBox.No |
                                               QMessageBox.Yes |
                                               QMessageBox.Cancel)
                if pressed == QMessageBox.Yes:
                    self.setCurrentDirectory(self.openDirectoryDialog())
                    new_dir = True
                elif pressed == QMessageBox.No:
                    done = True
                elif pressed == QMessageBox.Cancel:
                    # user pressed cancel, quit!
                    done = True
                    self.main_window.close()
                    # TODO: find graceful way to quit
            else:
                # file updating was successful
                done = True

                if new_dir is False:
                    # This means we are just refreshing the current directory
                    # we probably want to keep the current index
                    previous_text = self.imageListCombo.currentText()

                # disconnect slots before adding items
                self.imageListCombo.currentIndexChanged.disconnect(
                    self.handleImageListIndexChanged)
                self.imageIndexSpin.valueChanged.disconnect(
                    self.handleImageIndexValueChanged)

                # update image List combo
                self.imageListCombo.clear()
                self.imageListCombo.addItems(self.image_list.short_names)

                # update image index
                max_index = self.image_list.n_images - 1
                self.imageIndexSpin.setMaximum(max_index)
                labelString = 'of' + str(max_index)
                self.maxImageIndexLabel.setText(labelString)

                if new_dir is False:
                    # find if the previous image is still around
                    try:
                        ci = self.image_list.short_names.index(previous_text)
                    except ValueError:
                        # nope, it's not there. set current index to max index
                        ci = max_index
                else:
                    # if we have a new folder, then set the index to 0
                    ci = 0
                self.current_image_index = ci
                self.imageListCombo.setCurrentIndex(ci)
                self.imageIndexSpin.setValue(ci)


                # connect slot again once done adding
                self.imageListCombo.currentIndexChanged.connect(
                    self.handleImageListIndexChanged)
                self.imageIndexSpin.valueChanged.connect(
                    self.handleImageIndexValueChanged)

                self.is_cleaned = False
                self.useCleanedCheck.setCheckState(0)

                self.populateAndEmitImageInfo()
                self.purgeNonExistentEntries()

    def setCurrentDirectory(self, new_directory):
        """Sets the current directory.

        Never change self.current_directory directly. Use this function
        instead."""
        self.current_directory = new_directory
        self.windowTitleChanged.emit(self.current_directory)

    def loadSettings(self):
        self.settings.beginGroup('imagebrowser')
        self.setCurrentDirectory(
            str(self.settings.value('current_directory',
                                    default_image_dir).toString()))
        self.path_to_dark_file = str(
            self.settings.value('path_to_dark_file',
            self.path_to_dark_file).toString())
        self.path_to_json_db = str(self.settings.value(
                                   'path_to_json_db',
                                   './image_save_info.json').toString())

        self.settings.endGroup()
        self.loadJsonFile()

    def loadJsonFile(self):
        info = ('\n\nSomething bad has happened and I am not equipped to '
                'handle it. Please check if ' + self.path_to_json_db +
                ' exists. If it does, then make a backup of this file as '
                'it has a lot of information. Press yes if you would like '
                'to select a new database file. This has to be a valid JSON '
                'file. {} is an empty but valid JSON file. '
                'Press no if you want to '
                'create a new empty database at the same location. '
                'Press cancel if you want me to crash horribly but '
                'without touching the database file.')
        done = False
        while not done:
            try:
                with open(self.path_to_json_db, 'r') as f:
                    self.global_save_info = json.loads(f.read())
            except IOError as err:
                (errno, strerror) = err
                # something bad wrong
                msg = str(strerror)
            except ValueError as err:
                msg = str(err)
            except:
                msg = "Unknown error."
            else:
                done = True

            if not done:
                pressed = QMessageBox.critical(self, 'Error opening database',
                                               msg + info, QMessageBox.No |
                                               QMessageBox.Yes |
                                               QMessageBox.Cancel)
                if pressed == QMessageBox.Yes:
                    # file dialog
                    new_path = str(QFileDialog.getOpenFileName(self,
                                   "Select new JSON file",
                                   self.path_to_json_db))
                    self.path_to_json_db = new_path
                    done = False
                elif pressed == QMessageBox.No:
                    self.global_save_info = {}
                    done = True
                elif pressed == QMessageBox.Cancel:
                    done = True
                    self.global_save_info = {}
                    self.path_to_json_db = './temp_crash_json'
                    self.main_window.close()

    def saveSettings(self):
        self.saveImageInfo()
        self.settings.beginGroup('imagebrowser')
        self.settings.setValue('current_directory', self.current_directory)
        self.settings.setValue('path_to_dark_file', self.path_to_dark_file)
        self.settings.setValue('path_to_json_db', self.path_to_json_db)
        self.settings.endGroup()
        json_file_as_string = json.dumps(self.global_save_info, indent=4,
                                         separators=(',', ': '))
        with open(self.path_to_json_db, 'w') as f:
            f.write(json_file_as_string)

    def openDirectoryDialog(self):
        """Opens a dialog to select and new directory and returns path to
        selected directory."""
        return str(QFileDialog.getExistingDirectory(self, "Open Directory",
                   self.current_directory, QFileDialog.ShowDirsOnly))

    def handleOpenDirectoryAction(self):
        """Called when the user clicks the Open Directory button."""

        new_directory = self.openDirectoryDialog()

        if new_directory is not '' and new_directory != self.current_directory:
            self.watcher.removePath(self.current_directory)
            self.setCurrentDirectory(new_directory)
            self.updateFileList(new_dir=True)
            self.watcher.addPath(self.current_directory)

    def handleDarkFileAction(self):
        """Called when the user clicks the Dark File menu option."""
        new_path_to_dark_file = str(QFileDialog.getOpenFileName(self,
                                    "Select dark file",
                                    self.path_to_dark_file))

        if new_path_to_dark_file != '':
            self.path_to_dark_file = new_path_to_dark_file

    def handleRefreshAction(self):
        self.updateFileList(new_dir=False)

    def handleCleanAction(self):
        progress = QProgressDialog('Reading Reference images', 'Abort',
                                   0, 4.0*self.image_list.n_images, self)
        progress.setWindowModality(Qt.WindowModal)
        progress.setMinimumDuration(1)

        if self.readAllRefImages(progress):
            return
        if self.generateBasis(progress):
            return
        if self.readAllAbsImages(progress):
            return
        if self.generateCleanRefs(progress):
            return

        progress.setValue(4.0*self.image_list.n_images)
        self.populateAndEmitImageInfo()

    def readAllRefImages(self, progress):
        progress.setLabelText('Reading Reference Images')
        self.ref_images = []
        for path_to_ref in self.image_list.reference_files:
            if progress.wasCanceled():
                return 1
            progress.setValue(progress.value() + 1)
            im = clt.normalize(clt.readImageFile(path_to_ref))
            self.ref_images.append(im)

    def generateBasis(self, progress):
        progress.setLabelText('Generating basis vectors')
        self.basis = []
        for b in clt.generateBasis(self.ref_images):
            progress.setValue(progress.value() + 1)
            if progress.wasCanceled():
                return 1
            self.basis.append(b)

    def readAllAbsImages(self, progress):
        progress.setLabelText('Reading absorption images')
        self.abs_images = []
        for path_to_abs in self.image_list.absorption_files:
            progress.setValue(progress.value() + 1)
            if progress.wasCanceled():
                return 1
            im = clt.readImageFile(path_to_abs)
            self.abs_images.append(im)

    def generateCleanRefs(self, progress):
        progress.setLabelText('Generating clean reference images')
        self.clean_ref_images = []

        abs_shape = self.abs_images[0].shape
        # TODO: insert code to get actual mask
        mask = self.getROIMask(abs_shape)
        self.is_cleaned = False
        for im in clt.generateCleanRefs(self.abs_images, self.basis, mask):
            progress.setValue(progress.value() + 1)
            if progress.wasCanceled():
                return 1
            self.clean_ref_images.append(im)
        else:
            self.is_cleaned = True

    def handleUseCleanedAction(self, state):
        if self.is_cleaned is False and state == 2:
            self.handleCleanAction()
        self.populateAndEmitImageInfo()

    def handleUseRoiWhileCleaningAction(self, state):
        self.use_roi_while_cleaning = bool(state)

    def handleImageTypeChanged(self, new_state_string):
        self.populateAndEmitImageInfo()

    def odMinMaxStateChanged(self, new_state):
        print(new_state)

    def correctSaturationStateChanged(self, new_state):
        print(new_state)

    def handleWatcherDirectoryChanged(self, newDir):
        try:
            # see if updating the list would generate any errors
            clt.ImageList(self.current_directory)
        except clt.ImageListError:
            # if they do, then we probably are in the middle of a refresh
            # process, do nothing.
            return
        else:
            self.updateFileList(new_dir=False)

    def getODMinMax(self):
        """Return a tuple with min and max OD values read from the UI.
        returns None if odMinMax check button is not checked."""
        if self.odMinMaxCheck.checkState() == 0:
            return None
        else:
            return (self.odMinSpin.value(), self.odMaxSpin.value())

    def getSaturationParms(self):
        if self.correctSaturationCheckBox.checkState() == 0:
            return None
        else:
            gamma = 6.0666  # MHz
            return (self.satPixCountsSpin.value(), 2.0*self.detuningSpin.value()/gamma)

    def getODSaturationParms(self):
        if self.correctODSaturationCheckBox.checkState() == 0:
            return None
        else:
            return float(self.odSatSpin.value())


    def handleRoiChanged(self, new_roi):
        """Slot: Changes ROI used for cleaning images."""
        self.cleaning_roi = new_roi

    def handleROIHChanged(self, new_roi):
        self.roi_h = new_roi

    def handleROIVChanged(self, new_roi):
        self.roi_v = new_roi

    def getROIMask(self, abs_shape):
        mask = np.ones(abs_shape)
        if self.use_roi_while_cleaning:
            roi = self.cleaning_roi[0]  # 0th component is roi list
            mask[roi[0]:roi[2], roi[1]:roi[3]] = 0.0
        return mask

    def purgeNonExistentEntries(self):
        """Scan JSON database for files in current directory. If database has
        entries for which there are no files, then delete those entries."""
        for key in self.global_save_info.keys():
            if path.dirname(key) == self.current_directory:
                if not path.isfile(key):
                    # remove entry if this file does not exis
                    del self.global_save_info[key]
                    print('deleting: ' + key)

    def handleSaveAnalysis(self):
        save_dialog = SaveDialog(self.settings, self.global_save_info, self.image_list)
        save_dialog.exec_()

    def handleMakeMovie(self):
        print('handle')
        image_list = []
        for index in range(self.image_list.n_images):
            d = {}
            d['abs_image'] = clt.readImageFile(self.image_list.absorption_files[index])
            d['ref_image'] = clt.readImageFile(self.image_list.reference_files[index])
            d['dark_image'] = clt.readImageFile(self.path_to_dark_file)

            if self.is_cleaned and self.useCleanedCheck.checkState() == 2:
                ref_image = self.clean_ref_images[index]
            else:
                ref_image = d['ref_image']
            d['div_image'] = clt.dividedImage(d['abs_image'], ref_image,
                                              d['dark_image'],
                                              od_minmax=self.getODMinMax(),
                                              correct_od_saturation=self.getODSaturationParms(),
                                              correct_saturation=self.getSaturationParms())
            d['image_type'] = self.getImageType()

            image_in_roi = getSubImage(d['div_image'], self.cleaning_roi)
            image_list.append(image_in_roi)

        fig = plt.figure()
        ax = fig.add_subplot(111)
        ax.set_aspect('equal')
        ax.get_xaxis().set_visible(False)
        ax.get_yaxis().set_visible(False)

        im = ax.imshow(image_list[0], cmap='gray', interpolation='nearest')
        im.set_clim([0, 1])
        fig.set_size_inches([5, 5])

        plt.tight_layout()

        def update_img(n):
            im.set_data(image_list[n])
            return im

        #legend(loc=0)
        ani = animation.FuncAnimation(fig, update_img, len(image_list),
                                      interval=30)
        #writer = animation.writers['gif'](fps=10)

        ani.save('demo.gif')

    def handlePluginClicked(self, plugin_name):
        for p in plugin_list:
            if p.name == plugin_name:
                print("found")
                plugin_dialog = p(self.settings, self.makePluginDataPackage())
                plugin_dialog.exec_()

    def makePluginDataPackage(self):
        """Returns a dict with all the data a plugin would need."""
        data_dict = {}
        data_dict['roi_int'] = self.cleaning_roi
        data_dict['roi_h'] = self.roi_h
        data_dict['roi_v'] = self.roi_v

        abs_images = [clt.readImageFile(p) for p in
                      self.image_list.absorption_files]
        ref_images = [clt.readImageFile(p) for p in
                      self.image_list.reference_files]
        dark_image = clt.readImageFile(self.path_to_dark_file)

        if self.is_cleaned and self.useCleanedCheck.checkState() == 2:
            use_refs = self.clean_ref_images
        else:
            use_refs = ref_images

        sat_od_parms = self.getODSaturationParms()
        corr_sat_parms = self.getSaturationParms()
        div_images = [clt.dividedImage(ai, ri, dark_image,
                                       od_minmax=self.getODMinMax(),
                                       correct_od_saturation=sat_od_parms,
                                       correct_saturation=corr_sat_parms)
                      for ai, ri in zip(abs_images, use_refs)]
        data_dict['abs_images'] = abs_images
        data_dict['ref_images'] = use_refs
        data_dict['dark_image'] = dark_image
        data_dict['div_images'] = div_images
        return data_dict
예제 #5
0
파일: document.py 프로젝트: vi/enki
class _FileWatcher(QObject):
    """File watcher.

    QFileSystemWatcher notifies client about any change (file access mode, modification date, etc.)
    But, we need signal, only after file contents had been changed
    """
    modified = pyqtSignal(bool)
    removed = pyqtSignal(bool)

    def __init__(self, path):
        QObject.__init__(self)
        self._contents = None
        self._watcher = QFileSystemWatcher()
        self._timer = None
        self._path = path

        self._lastEmittedModifiedStatus = None
        self._lastEmittedRemovedStatus = None

        self.setPath(path)
        self.enable()


    def __del__(self):
        self._stopTimer()

    def enable(self):
        """Enable signals from the watcher
        """
        self._watcher.fileChanged.connect(self._onFileChanged)

    def disable(self):
        """Disable signals from the watcher
        """
        self._watcher.fileChanged.disconnect(self._onFileChanged)
        self._stopTimer()

    def setContents(self, contents):
        """Set file contents. Watcher uses it to compare old and new contents of the file.
        """
        self._contents = contents
        # Qt File watcher may work incorrectly, if file was not existing, when it started
        if not self._watcher.files():
            self.setPath(self._path)
        self._lastEmittedModifiedStatus = None
        self._lastEmittedRemovedStatus = None

    def setPath(self, path):
        """Path had been changed or file had been created. Set new path
        """
        if self._watcher.files():
            self._watcher.removePaths(self._watcher.files())
        if path is not None and os.path.isfile(path):
            self._watcher.addPath(path)
        self._path = path
        self._lastEmittedModifiedStatus = None
        self._lastEmittedRemovedStatus = None

    def _emitModifiedStatus(self):
        """Emit self.modified signal with right status
        """
        isModified = self._contents != self._safeRead(self._path)
        if isModified != self._lastEmittedModifiedStatus:
            self.modified.emit(isModified)
            self._lastEmittedModifiedStatus = isModified

    def _emitRemovedStatus(self, isRemoved):
        """Emit 'removed', if status changed"""
        if isRemoved != self._lastEmittedRemovedStatus:
            self._lastEmittedRemovedStatus = isRemoved
            self.removed.emit(isRemoved)

    def _onFileChanged(self):
        """File changed. Emit own signal, if contents changed
        """
        if os.path.exists(self._path):
            self._emitModifiedStatus()
        else:
            self._emitRemovedStatus(True)

        # Sometimes QFileSystemWatcher emits only 1 signal for 2 modifications
        # Check once more later
        self._startTimer()

    def _startTimer(self):
        """Init a timer.
        It is used for monitoring file after deletion.
        Git removes file, than restores it.
        """
        if self._timer is None:
            self._timer = QTimer()
            self._timer.setInterval(500)
            self._timer.timeout.connect(self._onCheckIfDeletedTimer)
        self._timer.start()

    def _stopTimer(self):
        """Stop timer, if exists
        """
        if self._timer is not None:
            self._timer.stop()

    def _onCheckIfDeletedTimer(self):
        """Check, if file has been restored
        """
        if os.path.exists(self._path):
            self.setPath(self._path)  # restart Qt file watcher after file has been restored
            self._stopTimer()
            self._emitRemovedStatus(False)
            self._emitModifiedStatus()

    def _safeRead(self, path):
        """Read file. Ignore exceptions
        """
        try:
            with open(path, 'rb') as file:
                return file.read()
        except (OSError, IOError):
            return None
예제 #6
0
class PreferencesWindow(BaseWindow):
    def __init__(self, *args, **kwargs):
        super(PreferencesWindow, self).__init__(layoutCls=QVBoxLayout,
                                                *args,
                                                **kwargs)
        self.setWindowTitle("Settings")
        self.setWindowIcon(QIcon(SETTINGS['icon']))
        self._add_steam_box()
        self._add_dota_box()
        self._add_additional_prefs()
        self._add_log_box()

        self._add_log_watcher()
        Settings().signals.changed.connect(self.update_path)

    def _add_log_watcher(self):
        self.watcher = QFileSystemWatcher()
        self.watcher.addPath(abspath(log.file_name))
        self.watcher.fileChanged.connect(self.update_log)

    def show(self):
        self.update_log(abspath(log.file_name))
        return super(PreferencesWindow, self).show()

    def update_path(self, setting_key, new_value):
        if setting_key == "dota_path":
            self.dota_path.setText(new_value)
        elif setting_key == "steam_path":
            self.steam_path.setText(new_value)

    def _add_steam_box(self):
        box = QGroupBox("Steam Location")
        box.setLayout(QHBoxLayout(box))

        self.steam_path = QLineEdit(box)
        self.steam_path.setReadOnly(True)
        self.steam_path.setText(Settings().get("steam_path"))

        change_btn = QPushButton("Change...", box)
        change_btn.clicked.connect(self.change_steam_path)

        box.layout().addWidget(self.steam_path)
        box.layout().addWidget(change_btn)

        self.layout().addWidget(box)

    def _add_dota_box(self):
        box = QGroupBox("Dota Location")
        box.setLayout(QHBoxLayout(box))

        self.dota_path = QLineEdit(box)
        self.dota_path.setReadOnly(True)
        self.dota_path.setText(Settings().get("dota_path"))

        change_btn = QPushButton("Change...", box)
        change_btn.clicked.connect(self.change_dota_path)

        box.layout().addWidget(self.dota_path)
        box.layout().addWidget(change_btn)

        self.layout().addWidget(box)

    def change_steam_path(self):
        self._change_path("steam_path", is_steam_path_valid)

    def change_dota_path(self):
        self._change_path("dota_path", is_dota_path_valid)

    def _change_path(self, path_key, is_valid):
        new_folder = str(
            QFileDialog.getExistingDirectory(
                parent=self,
                caption="Select new path",
                directory=Settings().get(path_key)))
        if new_folder and exists(new_folder):
            if is_valid(new_folder):
                Settings().set(path_key, new_folder)
            else:
                from d2mp.ui import Message
                Message.critical(
                    "Path is not valid",
                    "Path was not saved in settings.\nPlease select a directory with the right executable in it!"
                )

    def _add_additional_prefs(self):
        box = QGroupBox("Additional Preferences")
        box.setLayout(QHBoxLayout(box))

        log_btn = QPushButton("View Log", box)
        log_btn.clicked.connect(self.open_log_file)
        reset_btn = QPushButton("Reset Settings", box)
        reset_btn.clicked.connect(Settings().reset)

        box.layout().addWidget(log_btn)
        box.layout().addWidget(reset_btn)

        self.layout().addWidget(box)

    def _add_log_box(self):
        box = QGroupBox("Application log")
        box.setLayout(QHBoxLayout(box))

        self.log_area = QTextBrowser(box)
        self.log_area.setLineWrapMode(QTextEdit.NoWrap)

        box.layout().addWidget(self.log_area)
        self.layout().addWidget(box)

    def open_log_file(self):
        log.INFO("TODO: open file in standard editor")
        print(abspath(log.file_name))

    def update_log(self, log_file):
        content = ""
        for line in open(log_file):
            if "========= new programm start =========" in line:
                content = ""
            else:
                content += line[37:]
        if content:
            self.log_area.setText(content)
예제 #7
0
class ProjectWidget(Ui_Form, QWidget):
    SampleWidgetRole = Qt.UserRole + 1
    projectsaved = pyqtSignal()
    projectupdated = pyqtSignal()
    projectloaded = pyqtSignal(object)
    selectlayersupdated = pyqtSignal(list)

    def __init__(self, parent=None):
        super(ProjectWidget, self).__init__(parent)
        self.setupUi(self)
        self.project = None
        self.mapisloaded = False
        self.bar = None
        self.roamapp = None

        menu = QMenu()
        self.canvas.setCanvasColor(Qt.white)

        self.canvas.enableAntiAliasing(True)
        self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor)
        self.canvas.mapRenderer().setLabelingEngine(QgsPalLabeling())

        # self.roamVersionLabel.setText("You are running IntraMaps Roam version {}".format(roam.__version__))

        self.openProjectFolderButton.pressed.connect(self.openprojectfolder)
        self.openinQGISButton.pressed.connect(self.openinqgis)
        self.depolyProjectButton.pressed.connect(self.deploy_project)
        self.depolyInstallProjectButton.pressed.connect(
            functools.partial(self.deploy_project, True))

        self.filewatcher = QFileSystemWatcher()
        self.filewatcher.fileChanged.connect(self.qgisprojectupdated)

        self.projectupdatedlabel.linkActivated.connect(self.reloadproject)
        self.projectupdatedlabel.hide()

        # self.setpage(4)
        self.currentnode = None
        self.form = None

        qgislocation = r'C:\OSGeo4W\bin\qgis.bat'
        qgislocation = roam.config.settings.setdefault('configmanager', {}) \
            .setdefault('qgislocation', qgislocation)

        self.qgispathEdit.setText(qgislocation)
        self.qgispathEdit.textChanged.connect(self.save_qgis_path)
        self.filePickerButton.pressed.connect(self.set_qgis_path)

    def set_qgis_path(self):
        path = QFileDialog.getOpenFileName(self,
                                           "Select QGIS install file",
                                           filter="(*.bat)")
        if not path:
            return
        self.qgispathEdit.setText(path)
        self.save_qgis_path(path)

    def save_qgis_path(self, path):
        roam.config.settings['configmanager'] = {'qgislocation': path}
        roam.config.save()

    def setpage(self, page, node):
        self.currentnode = node

        self.write_config_currentwidget()

        self.stackedWidget.setCurrentIndex(page)
        if self.project:
            print self.project.dump_settings()

        widget = self.stackedWidget.currentWidget()
        if hasattr(widget, "set_project"):
            widget.set_project(self.project, self.currentnode)

    def write_config_currentwidget(self):
        widget = self.stackedWidget.currentWidget()
        if hasattr(widget, "write_config"):
            widget.write_config()

    def deploy_project(self, with_data=False):
        if self.roamapp.sourcerun:
            base = os.path.join(self.roamapp.apppath, "..")
        else:
            base = self.roamapp.apppath

        default = os.path.join(base, "roam_serv")
        path = roam.config.settings.get("publish", {}).get("path", '')
        if not path:
            path = default

        path = os.path.join(path, "projects")

        if not os.path.exists(path):
            os.makedirs(path)

        self._saveproject()
        options = {}

        bundle.bundle_project(self.project,
                              path,
                              options,
                              as_install=with_data)

    def setaboutinfo(self):
        self.versionLabel.setText(roam.__version__)
        self.qgisapiLabel.setText(str(QGis.QGIS_VERSION))

    def checkcapturelayers(self):
        haslayers = self.project.hascapturelayers()
        self.formslayerlabel.setVisible(not haslayers)
        return haslayers

    def selectlayerschanged(self, *args):
        self.formlayers.setSelectLayers(self.project.selectlayers)
        self.checkcapturelayers()
        self.selectlayersupdated.emit(self.project.selectlayers)

    def reloadproject(self, *args):
        self.setproject(self.project)
        self.projectupdated.emit()

    def qgisprojectupdated(self, path):
        self.projectupdatedlabel.show()
        self.projectupdatedlabel.setText(
            "The QGIS project has been updated. <a href='reload'> "
            "Click to reload</a>. <b style=\"color:red\">Unsaved data will be lost</b>"
        )

    def openinqgis(self):
        projectfile = self.project.projectfile
        qgislocation = r'C:\OSGeo4W\bin\qgis.bat'
        qgislocation = roam.config.settings.setdefault('configmanager', {}) \
            .setdefault('qgislocation', qgislocation)

        try:
            openqgis(projectfile, qgislocation)
        except OSError:
            self.bar.pushMessage("Looks like I couldn't find QGIS",
                                 "Check qgislocation in roam.config",
                                 QgsMessageBar.WARNING)

    def openprojectfolder(self):
        folder = self.project.folder
        openfolder(folder)

    def setproject(self, project, loadqgis=True):
        """
        Set the widgets active project.
        """
        self.mapisloaded = False
        self.filewatcher.removePaths(self.filewatcher.files())
        self.projectupdatedlabel.hide()
        self._closeqgisproject()

        if project.valid:
            self.startsettings = copy.deepcopy(project.settings)
            self.project = project
            self.projectlabel.setText(project.name)
            self.loadqgisproject(project, self.project.projectfile)
            self.filewatcher.addPath(self.project.projectfile)
            self.projectloaded.emit(self.project)

    def loadqgisproject(self, project, projectfile):
        QDir.setCurrent(os.path.dirname(project.projectfile))
        fileinfo = QFileInfo(project.projectfile)
        self.projectLocationLabel.setText("Project File: {}".format(
            os.path.basename(project.projectfile)))
        QgsProject.instance().read(fileinfo)

    def _closeqgisproject(self):
        if self.canvas.isDrawing():
            return

        self.canvas.freeze(True)
        QgsMapLayerRegistry.instance().removeAllMapLayers()
        self.canvas.freeze(False)

    def loadmap(self):
        if self.mapisloaded:
            return

        # This is a dirty hack to work around the timer that is in QgsMapCanvas in 2.2.
        # Refresh will stop the canvas timer
        # Repaint will redraw the widget.
        # loadmap is only called once per project load so it's safe to do this here.
        self.canvas.refresh()
        self.canvas.repaint()

        parser = roam.projectparser.ProjectParser.fromFile(
            self.project.projectfile)
        canvasnode = parser.canvasnode
        self.canvas.mapRenderer().readXML(canvasnode)
        self.canvaslayers = parser.canvaslayers()
        self.canvas.setLayerSet(self.canvaslayers)
        self.canvas.updateScale()
        self.canvas.refresh()

        self.mapisloaded = True

    def _saveproject(self):
        """
        Save the project config to disk.
        """
        self.write_config_currentwidget()
        # self.project.dump_settings()
        self.project.save(update_version=True)
        self.projectsaved.emit()
예제 #8
0
class SingleApplication(QApplication):
    def __init__(self, *args):
        QApplication.__init__(self, *args)
        self._memory = QSharedMemory(self)
        self._memory.setKey("d2mp")
        if self._memory.attach():
            self._running = True
        else:
            self._running = False
            if not self._memory.create(1):
                raise RuntimeError(self._memory.errorString().toLocal8Bit().data())

    def is_running(self):
        return self._running
    
    def exec_(self):
        self._create_tray_icon()
        self._create_mod_manager()
        self._start_file_watcher()
        self._create_socket()
        Settings()
        
        return super(SingleApplication, self).exec_()
    def _create_mod_manager(self):
        self.manager = ModManager()
        self.manager.mod_game_info()
        self.manager.signals.message.connect(self.show_message_from_mod_manager)
        self.manager.signals.error.connect(self.show_error_from_mod_manager)
    
    def _create_socket(self):    
        self.socket = ConnectionManager()
        
        self.manager.signals.contact_server.connect(self.socket.send)
        
        self.socket.message.connect(self.show_message_from_socket)
        self.socket.error.connect(self.show_error_from_socket)
        
        
    @property
    def _watcher_file_name(self):
        return "d2mp.pid"
    
    def _start_file_watcher(self):
        self.watcher = QFileSystemWatcher()
        self.watcher_file_path =  join(abspath("."), self._watcher_file_name)
        log.DEBUG("creating watcher file: %s" %(self.watcher_file_path))
        write_to_file(self.watcher_file_path, "Delete this file to shutdown D2MP\n")
        self.watcher.addPath(abspath("."))
        self.watcher.directoryChanged.connect(self._watcher_changed_callback)
    
    def _watcher_changed_callback(self, val):
        if self._watcher_file_name not in os.listdir(val): 
            secs = 3
            self.show_message("Shutdown", "Watcher file was deleted. D2MP will shotdown in %d seconds." %(secs))
            sleep(secs)
            self.exit()
    
    def _create_tray_icon(self):
        self.tray = QSystemTrayIcon(self)
        self.tray.setToolTip("D2Moddin Manager")
        self.tray.setIcon(QIcon(SETTINGS['icon']))
        traymenu = QMenu()
        traymenu.addAction("Restart", self.restart)
        traymenu.addAction("Uninstall", self.uninstall)
        traymenu.addAction("Preferences", UIManager().open_preferences)
        traymenu.addAction("Show mod list", self.show_mod_list)
        traymenu.addSeparator()

        traymenu.addAction("Exit", self.exit)
    
        self.tray.setContextMenu(traymenu)
        self.tray.show()
    
    def restart(self):
        python = sys.executable
        args = set(sys.argv)
        args.add("restart")
        os.execl(python, python, *list(sys.argv))
        self.exit()
    
    def uninstall(self):
        ModManager().delete_mods()
#         ModManager().uninstall_d2mp()
        self.exit()
    
    def exit(self):
        # do some cleanup
        return super(SingleApplication, self).exit()
    
    def show_mod_list(self):
        self.show_message("Mod List", ModManager().mod_names_as_string())
    
    def show_message_from_socket(self, message):
        self.show_message("Server message", message)
        
    def show_error_from_socket(self, message):
        self.show_message("Server error", message, QSystemTrayIcon.Critical)
        
    def show_message_from_mod_manager(self, message):
        self.show_message("ModManager message", message)
        
    def show_error_from_mod_manager(self, message):
        self.show_message("ModManager error", message, QSystemTrayIcon.Critical)
        
    def show_message(self, title, message, icon = QSystemTrayIcon.Information):
        self.tray.showMessage(title, message, icon)
예제 #9
0
class OpenedFileView(QObject):
    MARGIN_NUMBERS, MARGIN_MARKER_BP, MARGIN_MARKER_TP, MARGIN_MARKER_EXEC, \
    MARGIN_MARKER_EXEC_SIGNAL, MARGIN_MARKER_STACK = range(6)

    def __init__(self, distributedObjects, filename):
        QObject.__init__(self)
        filename = str(filename)
        self.distributedObjects = distributedObjects
        self.debugController = self.distributedObjects.debugController
        self.breakpointController = self.distributedObjects.breakpointController
        self.tracepointController = self.distributedObjects.tracepointController
        self.signalProxy = self.distributedObjects.signalProxy
        self.filename = filename
        self.lastContexMenuLine = 0
        self.markerBp = QPixmap(":/markers/bp.png")
        self.markerTp = QPixmap(":/markers/tp.png")
        self.markerExec = QPixmap(":/markers/exec_pos.png")
        self.markerExecSignal = QPixmap(":/markers/exec_pos_signal.png")
        self.shown = False

        self.FileWatcher = QFileSystemWatcher()
        self.FileWatcher.addPath(self.filename)
        self.FileWatcher.fileChanged.connect(self.fileChanged)

        self.tab = QtGui.QWidget()
        self.gridLayout = QtGui.QGridLayout(self.tab)
        self.gridLayout.setMargin(0)
        self.edit = Qsci.QsciScintilla(self.tab)
        self.font = QFont("DejaVu Sans Mono", 10)
        self.font.setStyleHint(QFont.TypeWriter)
        self.lexer = Qsci.QsciLexerCPP()
        self.lexer.setFont(self.font)

        self.edit.setToolTip("")
        self.edit.setWhatsThis("")
        self.edit.setTabWidth(4)
        self.edit.setLexer(self.lexer)
        self.edit.setWhitespaceVisibility(Qsci.QsciScintilla.WsVisible)
        self.edit.setIndentationGuides(True)
        self.edit.setMarginLineNumbers(self.MARGIN_NUMBERS, True)
        # set sensitivity
        self.edit.setMarginSensitivity(self.MARGIN_NUMBERS, True)
        self.edit.setMarginSensitivity(self.MARGIN_MARKER_BP, True)
        self.edit.setMarginSensitivity(self.MARGIN_MARKER_TP, True)
        # define symbol
        self.edit.markerDefine(self.markerBp, self.MARGIN_MARKER_BP)
        self.edit.markerDefine(self.markerTp, self.MARGIN_MARKER_TP)
        self.edit.markerDefine(self.markerExec, self.MARGIN_MARKER_EXEC)
        self.edit.markerDefine(self.markerExecSignal, self.MARGIN_MARKER_EXEC_SIGNAL)
        self.edit.markerDefine(Qsci.QsciScintilla.Background, self.MARGIN_MARKER_STACK)
        # define width and mask to show margin
        self.edit.setMarginWidth(self.MARGIN_MARKER_BP, 10)
        self.edit.setMarginMarkerMask(self.MARGIN_MARKER_BP, 1 << self.MARGIN_MARKER_BP)
        self.edit.setMarginWidth(self.MARGIN_MARKER_TP, 10)
        self.edit.setMarginMarkerMask(self.MARGIN_MARKER_TP, 1 << self.MARGIN_MARKER_TP)
        self.edit.setMarginWidth(self.MARGIN_MARKER_EXEC, 10)
        self.edit.setMarginMarkerMask(self.MARGIN_MARKER_EXEC,
                1 << self.MARGIN_MARKER_EXEC |
                1 << self.MARGIN_MARKER_EXEC_SIGNAL)
        self.edit.setMarginWidth(self.MARGIN_MARKER_STACK, 0)
        self.edit.setMarkerBackgroundColor(QColor(Qt.yellow), self.MARGIN_MARKER_STACK)
        self.edit.setMarginMarkerMask(self.MARGIN_MARKER_STACK, 1 << self.MARGIN_MARKER_STACK)
        # ...
        self.edit.setReadOnly(False)
        self.gridLayout.addWidget(self.edit, 0, 0, 1, 1)

        self.breakpoints = []

        if not (QtCore.QFile.exists(filename)):
            logging.error("could not open file", filename)
        self.file_ = QtCore.QFile(filename)
        self.file_.open(QtCore.QIODevice.ReadOnly | QtCore.QIODevice.Text)
        self.edit.read(self.file_)
        self.file_.close()

        self.changed = False
        self.edit.modificationChanged.connect(self.__setFileModified)

        self.setMarginWidthByLineNumbers()
        self.edit.SendScintilla(Qsci.QsciScintilla.SCI_SETMOUSEDWELLTIME, 500)

        # override scintillas context menu with our own
        self.edit.SendScintilla(Qsci.QsciScintilla.SCI_USEPOPUP, 0)
        self.edit.setContextMenuPolicy(Qt.CustomContextMenu)
        self.edit.customContextMenuRequested.connect(self.showContextMenu)

        self.edit.marginClicked.connect(self.marginClicked)
        self.edit.SCN_DOUBLECLICK.connect(self.editDoubleClicked)
        self.edit.SCN_DWELLSTART.connect(self.dwellStart)
        self.edit.SCN_DWELLEND.connect(self.dwellEnd)

        # initially, read all breakpoints and tracepoints from the model
        self.getBreakpointsFromModel()
        self.getTracepointsFromModel()

        _model = self.breakpointController.model()
        _model.rowsInserted.connect(self.getBreakpointsFromModel)
        _model.rowsRemoved.connect(self.getBreakpointsFromModel)
        _model = self.tracepointController.model()
        _model.rowsInserted.connect(self.getTracepointsFromModel)
        _model.rowsRemoved.connect(self.getTracepointsFromModel)

        act = self.distributedObjects.actions
        act.ToggleTrace.triggered.connect(self.toggleTracepoint)

    def fileChanged(self):
        logging.warning("Source file %s modified. Recompile executable for \
                correct debugging.", self.filename)

    def saveFile(self):
        ''' Save source file '''
        if (QtCore.QFile.exists(self.filename)):
            f = open(self.filename, 'w')
            f.write(self.edit.text())
            f.close()
            self.file_.open(QtCore.QIODevice.ReadOnly | QtCore.QIODevice.Text)
            self.edit.read(self.file_)
            self.file_.close()
            self.__setFileModified(False)

    def __setFileModified(self, modified):
        ''' Method called whenever current file is marked as modified '''
        self.distributedObjects.signalProxy.emitFileModified(self.filename, modified)

    def dwellStart(self, pos, x, y):
        if self.edit.frameGeometry().contains(x, y):
            name = self.getWordOrSelectionFromPosition(pos)
            val = self.debugController.evaluateExpression(name.strip())
            if val != None:
                name = cgi.escape(name)
                val = cgi.escape(val)
                QToolTip.showText(self.edit.mapToGlobal(QtCore.QPoint(x, y)),
                        "<b>" + name + "</b> = " + val, self.edit,
                        QtCore.QRect())

    def dwellEnd(self, position, x, y):
        QToolTip.showText(self.edit.mapToGlobal(QtCore.QPoint(x, y)), "",
                self.edit, QtCore.QRect())

    def showContextMenu(self, point):
        scipos = self.edit.SendScintilla(
                Qsci.QsciScintilla.SCI_POSITIONFROMPOINT, point.x(), point.y())
        point = self.edit.mapToGlobal(point)
        exp = self.getWordOrSelectionFromPosition(scipos)
        # self.edit.lineIndexFromPosition(..) returns tupel. first of tupel is line
        self.lastContexMenuLine = int(self.edit.lineIndexFromPosition(scipos)[0])

        listOfTracepoints = self.tracepointController.getTracepointsFromModel()

        self.subPopupMenu = QtGui.QMenu(self.edit)
        self.subPopupMenu.setTitle("Add variable " + exp + " to...")

        for tp in listOfTracepoints:
            # dynamic actions, not in actiony.py Action class
            self.subPopupMenu.addAction(self.distributedObjects.actions.getAddToTracepointAction(exp, tp.name, tp.addVar))

        self.popupMenu = QtGui.QMenu(self.edit)
        # add watch and toggle breakpoint to menu
        self.popupMenu.addAction(self.distributedObjects.actions.getAddToWatchAction(exp, self.signalProxy.addWatch))
        self.popupMenu.addAction(self.distributedObjects.actions.ToggleTrace)
        self.popupMenu.addAction(self.distributedObjects.actions.getAddToDatagraphAction(exp, self.distributedObjects.datagraphController.addWatch))
        # add separator and self.subPopupMenu
        self.popupMenu.addSeparator()
        self.popupMenu.addMenu(self.subPopupMenu)
        self.popupMenu.popup(point)

    def isPositionInsideSelection(self, position):
        lf, cf, lt, ct = self.edit.getSelection()
        pl, pc = self.edit.lineIndexFromPosition(position)

        if lf < pl and pl < lt:
            return True
        elif lf == pl and pl < lt:
            return True if cf <= pc else False
        elif lf < pl and pl == lt:
            return True if pc <= ct else False
        elif lf == pl and pl == lt:
            return True if (cf <= pc and pc <= ct) else False
        else:
            return False

    def getWordOrSelectionFromPosition(self, position):
        if self.isPositionInsideSelection(position):
            return str(self.edit.selectedText())
        else:
            return self.getWordFromPosition(position)

    def getWordFromPosition(self, position):
        line, col = self.edit.lineIndexFromPosition(position)
        s = str(self.edit.text(line))
        start = col
        end = col

        r = re.compile(r'[\w\d_]')
        while start >= 0:
            if not r.match(s[start]):
                break
            start -= 1
        start += 1
        while end < len(s):
            if not r.match(s[end]):
                break
            end += 1
        return s[start:end]

    def editDoubleClicked(self, position, line, modifiers):
        w = self.getWordFromPosition(position)
        self.signalProxy.addWatch(str(w))

    def showExecutionPosition(self, line):
        self.edit.markerAdd(line, self.MARGIN_MARKER_EXEC)
        self.showLine(line)

    def showSignalPosition(self, line):
        self.edit.markerAdd(line, self.MARGIN_MARKER_EXEC_SIGNAL)
        self.showLine(line)

    def showLine(self, line):
        self.edit.setCursorPosition(line, 1)
        self.edit.ensureLineVisible(line)

    def clearExecutionPositionMarkers(self):
        self.edit.markerDeleteAll(self.MARGIN_MARKER_EXEC)

    def setMarginWidthByLineNumbers(self):
        self.edit.setMarginWidth(0, ceil(log(self.edit.lines(), 10)) * 10 + 5)

    def marginClicked(self, margin, line, state):
        # if breakpoint should be toggled
        if margin == self.MARGIN_NUMBERS or margin == self.MARGIN_MARKER_BP:
            self.toggleBreakpointWithLine(line)
        elif margin == self.MARGIN_MARKER_TP:
            self.toggleTracepointWithLine(line)

    def toggleBreakpointWithLine(self, line):
        self.breakpointController.toggleBreakpoint(self.filename, line + 1)

    def toggleTracepointWithLine(self, line):
        self.tracepointController.toggleTracepoint(self.filename, line + 1)

    def toggleTracepoint(self):
        self.toggleTracepointWithLine(self.lastContexMenuLine)

    def getBreakpointsFromModel(self, parent=None, start=None, end=None):
        """Get breakpoints from model."""
        # TODO: don't reload all breakpoints, just the one referenced by parent/start/end
        self.edit.markerDeleteAll(self.MARGIN_MARKER_BP)
        for bp in self.breakpointController.getBreakpointsFromModel():
            if bp.fullname == self.filename:
                self.edit.markerAdd(int(bp.line) - 1, self.MARGIN_MARKER_BP)

    def getTracepointsFromModel(self):
        """Get tracepoints from model."""
        self.edit.markerDeleteAll(self.MARGIN_MARKER_TP)
        for tp in self.tracepointController.getTracepointsFromModel():
            if tp.fullname == self.filename:
                self.edit.markerAdd(int(tp.line) - 1, self.MARGIN_MARKER_TP)
예제 #10
0
class NFile(QObject):
    """
    SIGNALS:
    @askForSaveFileClosing(QString)
    @fileClosing(QString)
    @fileChanged()
    @willDelete(PyQt_PyObject, PyQt_PyObject)
    @willOverWrite(PyQt_PyObject, QString, QString)
    @willMove(Qt_PyQtObject, QString, QString)
    @willSave(QString, QString)
    @savedAsNewFile(PyQt_PyObject, QString, QString)
    @gotAPath(PyQt_PyObject)
    @willAttachToExistingFile(PyQt_PyObject, QString)
    """

    def __init__(self, path=None):
        """
        """
        self._file_path = path
        self.__created = False
        self.__watcher = None
        self.__mtime = None
        super(NFile, self).__init__()
        if not self._exists():
            self.__created = True

    @property
    def file_name(self):
        """"Returns filename of nfile"""
        file_name = None
        if self._file_path is None:
            file_name = translations.TR_NEW_DOCUMENT
        else:
            file_name = get_basename(self._file_path)
        return file_name

    @property
    def display_name(self):
        """Returns a pretty name to be displayed by tabs"""
        display_name = self.file_name
        if not self._file_path is None and not self.has_write_permission():
            display_name += translations.TR_READ_ONLY
        return display_name

    @property
    def is_new_file(self):
        return self.__created

    def file_ext(self):
        """"Returns extension of nfile"""
        if self._file_path is None:
            return ''
        return get_file_extension(self._file_path)

    @property
    def file_path(self):
        """"Returns file path of nfile"""
        return self._file_path

    def start_watching(self):
        """Create a file system watcher and connect its fileChanged
        SIGNAL to our _file_changed SLOT"""
        if not self.__watcher:
            self.__watcher = QFileSystemWatcher(self)
            self.connect(self.__watcher,
                         SIGNAL("fileChanged(const QString&)"),
                         self._file_changed)
        if self._file_path is not None:
            self.__mtime = os.path.getmtime(self._file_path)
            self.__watcher.addPath(self._file_path)

    def _file_changed(self, path):
        current_mtime = os.path.getmtime(self._file_path)
        if current_mtime != self.__mtime:
            self.__mtime = current_mtime
            self.emit(SIGNAL("fileChanged()"))

    def has_write_permission(self):
        if not self._exists():
            return True
        return os.access(self._file_path, os.W_OK)

    def _exists(self):
        """
        Check if we have been created with a path and if such path exists
        In case there is no path, we are most likely a new file.
        """
        file_exists = False
        if self._file_path and os.path.exists(self._file_path):
            file_exists = True
        return file_exists

    def attach_to_path(self, new_path):
        if os.path.exists(new_path):
            signal_handler = SignalFlowControl()
            self.emit(
                SIGNAL("willAttachToExistingFile(PyQt_PyObject, QString)"),
                signal_handler,
                new_path)
            if signal_handler.stopped():
                    return
        self._file_path = new_path
        self.emit(SIGNAL("gotAPath(PyQt_PyObject)"), self)
        return self._file_path

    def create(self):
        if self.__created:
            self.save("")
        self.__created = False

    def save(self, content, path=None):
        """
        Write a temprorary file with .tnj extension and copy it over the
        original one.
        .nsf = Ninja Swap File
        #FIXME: Where to locate addExtension, does not fit here
        """
        new_path = False
        if path:
            self.attach_to_path(path)
            new_path = True

        save_path = self._file_path

        if not save_path:
            raise NinjaNoFileNameException("I am asked to write a "
                                           "file but no one told me where")
        swap_save_path = "%s.nsp" % save_path

        # If we have a file system watcher, remove the file path
        # from its watch list until we are done making changes.
        if self.__watcher:
            self.__watcher.removePath(save_path)

        flags = QIODevice.WriteOnly | QIODevice.Truncate
        f = QFile(swap_save_path)
        if settings.use_platform_specific_eol():
            flags |= QIODevice.Text

        if not f.open(flags):
            raise NinjaIOException(f.errorString())

        stream = QTextStream(f)
        encoding = get_file_encoding(content)
        if encoding:
            stream.setCodec(encoding)

        encoded_stream = stream.codec().fromUnicode(content)
        f.write(encoded_stream)
        f.flush()
        f.close()
        #SIGNAL: Will save (temp, definitive) to warn folder to do something
        self.emit(SIGNAL("willSave(QString, QString)"),
                  swap_save_path, save_path)
        self.__mtime = os.path.getmtime(swap_save_path)
        shutil.move(swap_save_path, save_path)
        self.reset_state()

        # If we have a file system watcher, add the saved path back
        # to its watch list, otherwise create a watcher and start
        # watching
        if self.__watcher:
            if new_path:
                self.__watcher.removePath(self.__watcher.files()[0])
                self.__watcher.addPath(self._file_path)
            else:
                self.__watcher.addPath(save_path)
        else:
            self.start_watching()
        return self

    def reset_state(self):
        """
        #FIXE: to have a ref to changed I need to have the doc here
        """
        self.__created = False

    def read(self, path=None):
        """
        Read the file or fail
        """
        open_path = path and path or self._file_path
        self._file_path = open_path
        if not self._file_path:
            raise NinjaNoFileNameException("I am asked to read a "
                                           "file but no one told me from where")
        try:
            with open(open_path, 'rU') as f:
                content = f.read()
        except IOError as reason:
            raise NinjaIOException(reason)
        return content

    def move(self, new_path):
        """
        Phisically move the file
        """
        if self._exists():
            signal_handler = SignalFlowControl()
            #SIGNALL: WILL MOVE TO, to warn folder to exist
            self.emit(SIGNAL("willMove(Qt_PyQtObject, QString, QString)"),
                      signal_handler,
                      self._file_path,
                      new_path)
            if signal_handler.stopped():
                return
            if os.path.exists(new_path):
                signal_handler = SignalFlowControl()
                self.emit(
                    SIGNAL("willOverWrite(PyQt_PyObject, QString, QString)"),
                    signal_handler, self._file_path, new_path)
                if signal_handler.stopped():
                    return
            if self.__watcher:
                self.__watcher.removePath(self._file_path)
            shutil.move(self._file_path, new_path)
            if self.__watcher:
                self.__watcher.addPath(new_path)
        self._file_path = new_path
        return

    def copy(self, new_path):
        """
        Copy the file to a new path
        """
        if self._exists():
            signal_handler = SignalFlowControl()
            #SIGNALL: WILL COPY TO, to warn folder to exist
            self.emit(SIGNAL("willCopyTo(Qt_PyQtObject, QString, QString)"),
                      signal_handler, self._file_path, new_path)
            if signal_handler.stopped():
                return
            if os.path.exists(new_path):
                signal_handler = SignalFlowControl()
                self.emit(
                    SIGNAL("willOverWrite(PyQt_PyObject, QString, QString)"),
                    signal_handler, self._file_path, new_path)
                if signal_handler.stopped():
                    return

            shutil.copy(self._file_path, new_path)

    def delete(self, force=False):
        """
        This deletes the object and closes the file.
        """
        #if created but exists this file migth to someone else
        self.close()
        if ((not self.__created) or force) and self._exists():
            DEBUG("Deleting our own NFile %s" % self._file_path)
            signal_handler = SignalFlowControl()
            self.emit(SIGNAL("willDelete(PyQt_PyObject, PyQt_PyObject)"),
                      signal_handler, self)
            if not signal_handler.stopped():
                if self.__watcher:
                    self.__watcher.removePath(self._file_path)
                os.remove(self._file_path)

    def close(self, force_close=False):
        """
        Lets let people know we are going down so they can act upon
        As you can see close does nothing but let everyone know that we are
        not saved yet
        """
        DEBUG("About to close NFile")
        self.emit(SIGNAL("fileClosing(QString, bool)"),
                  self._file_path, force_close)

    def remove_watcher(self):
        if self.__watcher:
            self.__watcher.removePath(self._file_path)
예제 #11
0
파일: object_file.py 프로젝트: Garjy/edis
class EdisFile(QObject):
    """ Representación de un objeto archivo """

    def __init__(self, filename=''):
        QObject.__init__(self)
        self._is_new = True
        if not filename:
            self._filename = "Untitled"
        else:
            self._filename = filename
            self._is_new = False
        self._last_modification = None
        self._system_watcher = None

    @property
    def filename(self):
        return self._filename

    @property
    def is_new(self):
        return self._is_new

    def read(self):
        """ Itenta leer el contenido del archivo, si ocurre un error se lanza
        una excepción.
        """

        try:
            with open(self.filename, mode='r') as f:
                content = f.read()
            return content
        except IOError as reason:
            raise exceptions.EdisIOError(reason)

    def write(self, content, new_filename=''):
        """ Escribe los datos en el archivo """

        DEBUG("Saving file...")
        # Por defecto, si el archivo no tiene extensión se agrega .c
        ext = os.path.splitext(new_filename)
        if not ext[-1]:
            new_filename += '.c'
        if self.is_new:
            self._filename = new_filename
            self._is_new = False
        _file = QFile(self.filename)
        if not _file.open(QIODevice.WriteOnly | QIODevice.Truncate):
            raise exceptions.EdisIOError
        out_file = QTextStream(_file)
        out_file << content
        if self._system_watcher is not None:
            if self._filename is not None:
                archivos = self._system_watcher.files()
                self._system_watcher.removePath(archivos[0])
        else:
            self.run_system_watcher()

    def run_system_watcher(self):
        """ Inicializa el control de monitoreo para modificaciones """

        if self._system_watcher is None:
            self._system_watcher = QFileSystemWatcher()
            self.connect(self._system_watcher,
                         SIGNAL("fileChanged(const QString&)"),
                         self._on_file_changed)
        self._last_modification = os.lstat(self.filename).st_mtime
        self._system_watcher.addPath(self.filename)
        DEBUG("Watching {0}".format(self.filename))

    def stop_system_watcher(self):
        if self._system_watcher is not None:
            self._system_watcher.removePath(self.filename)
            DEBUG("Stoping watching {0}".format(self.filename))

    def _on_file_changed(self, filename):
        mtime = os.lstat(filename).st_mtime
        if mtime != self._last_modification:
            # Actualizo la última modificación
            self._last_modification = mtime
            self.emit(SIGNAL("fileChanged(PyQt_PyObject)"), self)
예제 #12
0
isDone = True
ct = 0


def watch():
    global isDone, ct
    if isDone:
        isDone = False
        ct += 1
        iface.mapCanvas().refresh()
        iface.mapCanvas().saveAsImage("/tmp/image{0:04d}.png".format(ct))
        isDone = True


watcher = QFileSystemWatcher()
watcher.addPath('/tmp/wavefront_edges_progress.wkt')
watcher.fileChanged.connect(watch)

from PyQt4.QtCore import QFileSystemWatcher
watcher = QFileSystemWatcher()
watcher.addPath('/tmp/rays.wkt')
watcher.fileChanged.connect(iface.mapCanvas().refresh)

from PyQt4.QtCore import QFileSystemWatcher
watcher = QFileSystemWatcher()
iface.mapCanvas().setCachingEnabled(False)
isDone = True


def watch():
    global isDone
예제 #13
0
class _FileWatcher(QObject):
    """File watcher.
    
    QFileSystemWatcher notifies client about any change (file access mode, modification date, etc.)
    But, we need signal, only after file contents had been changed
    """
    modified = pyqtSignal(bool)
    removed = pyqtSignal(bool)

    def __init__(self, path):
        QObject.__init__(self)
        self._contents = None
        self._watcher = QFileSystemWatcher()
        self._timer = None
        self._path = path
        self.setPath(path)
        self.enable()

    def __del__(self):
        self._stopTimer()

    def enable(self):
        """Enable signals from the watcher
        """
        self._watcher.fileChanged.connect(self._onFileChanged)

    def disable(self):
        """Disable signals from the watcher
        """
        self._watcher.fileChanged.disconnect(self._onFileChanged)
        self._stopTimer()

    def setContents(self, contents):
        """Set file contents. Watcher uses it to compare old and new contents of the file.
        """
        self._contents = contents
        # Qt File watcher may work incorrectly, if file was not existing, when it started
        self.setPath(self._path)

    def setPath(self, path):
        """Path had been changed or file had been created. Set new path
        """
        if self._watcher.files():
            self._watcher.removePaths(self._watcher.files())
        if path is not None and os.path.isfile(path):
            self._watcher.addPath(path)
        self._path = path

    def _emitModifiedStatus(self):
        """Emit self.modified signal with right status
        """
        isModified = self._contents != self._safeRead(self._path)
        self.modified.emit(isModified)

    def _onFileChanged(self):
        """File changed. Emit own signal, if contents changed
        """
        if os.path.exists(self._path):
            self._emitModifiedStatus()
        else:
            self.removed.emit(True)
            self._startTimer()

    def _startTimer(self):
        """Init a timer.
        It is used for monitoring file after deletion.
        Git removes file, than restores it.
        """
        if self._timer is None:
            self._timer = QTimer()
            self._timer.setInterval(500)
            self._timer.timeout.connect(self._onCheckIfDeletedTimer)
        self._timer.start()

    def _stopTimer(self):
        """Stop timer, if exists
        """
        if self._timer is not None:
            self._timer.stop()

    def _onCheckIfDeletedTimer(self):
        """Check, if file has been restored
        """
        if os.path.exists(self._path):
            self.removed.emit(False)
            self._emitModifiedStatus()
            self.setPath(
                self._path
            )  # restart Qt file watcher after file has been restored
            self._stopTimer()

    def _safeRead(self, path):
        """Read file. Ignore exceptions
        """
        try:
            with open(path, 'rb') as file:
                return file.read()
        except (OSError, IOError):
            return None
예제 #14
0
class ScanPage(QWizardPage):
    scanError = pyqtSignal() # name
    scanSuccess = pyqtSignal()
    path = "qtui/ui/page3_scan.ui"

    def __init__(self, project, parentW=None):
        super(ScanPage, self).__init__()
        self.ui = uic.loadUi(join(os.environ['AUTOEXAM_FOLDER'], self.path), self)
        self.project = project
        self.scan_thread = None
        self.ui.treeWidget.currentItemChanged.connect(self.change_tree_item)
        self.ui.openCameraButton.clicked.connect(self.open_camera)
        self.question_item_to_question = {}

        self.exams = []
        self.order = None
        self.results = None
        self.parentWizard = parentW
        self.scanError.connect(self.on_scan_error)
        self.scanSuccess.connect(self.on_scan_success)
        self.scanning = False

    def initializePage(self):
        super(ScanPage, self).initializePage()
        # api.add_scan_event_subscriber(self)

        self.open_camera()

        if os.path.exists(ORDER_FILE_PATH):
            self.order = scanresults.parse(ORDER_FILE_PATH)

        self.watcher = QFileSystemWatcher()
        self.watcher.fileChanged.connect(self.on_scan_file_change)

        if os.path.exists(TESTS_RESULTS_FILE_PATH):
            self.results = scanresults.parse(TESTS_RESULTS_FILE_PATH)
            self.update_question_tree_widget()
        else:
            with open(TESTS_RESULTS_FILE_PATH,'w') as f:
                f.write('{}')
            self.results = scanresults.parse(TESTS_RESULTS_FILE_PATH)

        # TODO: Check why this doesn't always work
        self.watcher.addPath(TESTS_RESULTS_FILE_PATH)
        self.last_load_time = time.time()

    def update_question_tree_widget(self):
        tree = self.ui.treeWidget
        tree.clear()

        for i in range(len(self.order)):
            incomplete_test = False
            exam_item = QTreeWidgetItem(tree, ['Exam %d' % i])
            for j in range(len(self.order[i].questions)):
                question_item = QTreeWidgetItem(exam_item, ['Question %d' % (j + 1)])
                if i in self.results:
                    question_item.question = self.project.questions[self.order[i].questions[j].id - 1]
                else:
                    incomplete_test = True
            if incomplete_test:
                exam_item.setBackground(0, QBrush(Qt.lightGray))

        first_exam_item = tree.topLevelItem(0)
        first_exam_item.setExpanded(True)
        tree.setCurrentItem(
            tree.itemBelow(first_exam_item))


    def validatePage(self):

        if self.scanning:
            self.show_modal_message(
            'Please close the scanner window first by pressing \'q\'')
            return False
        # TODO: Warning validation here!!!
        if self.results is not None:
            scanresults.dump(self.results, TESTS_RESULTS_FILE_PATH, overwrite=False)
            self.parentWizard.results = self.results
            return True
        else:
            self.show_modal_message('There are still no results to save')
            return False

    def cleanupPage(self):
        super(ScanPage, self).cleanupPage()
        # api.remove_scan_event_subscriber(self)

        # TODO: Do proper shutdown
        # self.scan_thread.__stop()

    # def on_scan_event(self, report):
    #     if report.success:
    #         print 'successful report: ', report
    #         self.process_report(report)
    #     else:
    #         print 'failed report: ', report

    def on_scan_file_change(self, filename):
        if time.time() - self.last_load_time > 2:
            try:
                print('Reloading results...')
                self.results = scanresults.parse(TESTS_RESULTS_FILE_PATH)
                self.update_question_tree_widget()
                self.last_load_time = time.time()
                print('Results reloaded')
            except:
                print('Could not load results.')
        else:
            # print('Ignoring repetitive filewatcher event (this is normal)')
            pass


    # def process_report(self, report):
    #     current = self.ui.treeWidget.topLevelItem(report.test.id)
    #
    #     if current:
    #         if len(report.test.warnings) == 0:
    #             current.setForeground(0, ok_color)
    #         elif len(report.test.warnings) > 0:
    #             current.setForeground(0, warn_color)

    def change_tree_item(self):
        currentItem = self.ui.treeWidget.currentItem()
        if currentItem is not None:
            self.cleanupPanel()
            self.current_item = currentItem
            if currentItem.parent() is not None:  # i.e. it is a question
                self.update_question_panel_with_question()
            else:
                self.update_question_panel_with_exam()

    def update_question_panel_with_question(self):
        current_exam_item = self.current_item.parent()
        exam_no = self.ui.treeWidget.indexOfTopLevelItem(current_exam_item)
        question_no = current_exam_item.indexOfChild(self.current_item)
        question_info = self.project.questions[self.order[exam_no].questions[question_no].id - 1]

        self.ui.questionTextLabel.setText(question_info.text)

        # self.ui.questionDataLayout.addWidget(QLabel(question_info.text))

        order_info = self.order[exam_no].questions[question_no].order

        for answer_no in order_info:
            answer = question_info.answers[answer_no]
            answer_text = answer.text
            answer_text += ' (x)' if answer.valid else ''
            question_answer_check = QCheckBox(answer_text)
            question_answer_check.setChecked(
                self.is_answer_checked(exam_no, question_no, answer_no))
            question_answer_check.stateChanged.connect(
                self.update_current_question_state)
            self.ui.questionDataLayout.addWidget(question_answer_check)

    def update_question_panel_with_exam(self):
        current_exam_item = self.current_item
        exam_no = self.ui.treeWidget.indexOfTopLevelItem(current_exam_item)

        if self.results is not None:
            if exam_no in self.results:
                question_text_label = QLabel('TODO: Put here the warnings...')
            else:
                question_text_label = QLabel('This exam has not been scanned!')
        else:
            print('results null! please check!')

        self.ui.questionDataLayout.addWidget(question_text_label)

    def is_answer_checked(self, exam_no, question_no, answer_no):
        # print 'is_answer_checked'
        # print(exam_no,question_no,answer_no)

        try:
            results_data = self.results
            exam_data = results_data[exam_no]

            question_data = exam_data.questions[question_no]
            return answer_no in question_data.answers
        except:
            # print('answer not scanned exception')
            return False

    def update_current_question_state(self, state):
        current_exam_item = self.current_item.parent()

        exam_no = self.ui.treeWidget.indexOfTopLevelItem(current_exam_item)
        question_no = current_exam_item.indexOfChild(self.current_item)

        results_data = self.results

        if exam_no in results_data:
            question_data = results_data[exam_no].questions[question_no]

            order_data = self.order[exam_no].questions[question_no].order
            self.synchronize_answers_with_model(order_data,question_data)

        else:
            # If we get here, we're trying to manually enter info for an
            # exam that has not been scanned yet.
            # We should create an entry in results_data similar
            confirm_edit = QMessageBox.question(None, "Manual input?", "Do you want to manually enter this test's results?", QMessageBox.Yes | QMessageBox.No )
            regen = confirm_edit == QMessageBox.Yes
            if confirm_edit:
                self.results[exam_no] = self.order[exam_no]
                question_data = results_data[exam_no].questions[question_no]
                order_data = self.order[exam_no].questions[question_no].order
                self.synchronize_answers_with_model(order_data,question_data)

                current_exam_item.setBackground(0, QBrush(Qt.white))

    def synchronize_answers_with_model(self, order_data, question_data):
        for i in range(len(order_data)):
            checked = self.ui.questionDataLayout.itemAt(i).widget().isChecked()
            question_idx = order_data[i]
            if checked and question_idx not in question_data.answers:
                question_data.answers.append(question_idx)
            elif not checked and question_idx in question_data.answers:
                question_data.answers.remove(question_idx)

    def cleanupPage(self):
        self.watcher.fileChanged.disconnect(self.on_scan_file_change)
        del self.watcher

    def cleanupPanel(self):
        for i in reversed(range(self.ui.questionDataLayout.count())):
            elem = self.ui.questionDataLayout.itemAt(i)
            if not elem:
                break
            elem.widget().deleteLater()

    def start_scan(self):

        class _args:
            outfile = TESTS_RESULTS_FILE_PATH
            cameras = [self.ui.cameraIndexSpin.value()] #TODO: UN-WIRE THIS !!!!
            folder = IMAGES_FOLDER
            time = None
            autowrite = True
            poll = None
            debug = False

        self.scanning = True

        ok = api.scan(_args()) # TODO: Fill dictionary properly
        if ok:
            self.scanSuccess.emit()
        else:
            self.scanError.emit()

        self.scanning = False

    def open_camera(self):
        self.ui.openCameraButton.setEnabled(False)

        self.scan_thread = Thread(target=self.start_scan)
        self.scan_thread.setDaemon(True)
        self.scan_thread.start()
        # self.start_scan()

    def on_scan_error(self):
        # self.parentWizard.back()
        self.show_modal_message(
        "There was an error in the scanning process.\
        Please, check if the right camera is selected and try again.")
        self.ui.openCameraButton.setEnabled(True)

    def show_modal_message(self, msg):
        """
        Shows a modal MessageBox displaying the argument string
        """
        msgBox = QMessageBox()
        msgBox.setText(msg)
        msgBox.setModal(True)
        msgBox.setIcon(QMessageBox.Warning)
        msgBox.exec_()

    def on_scan_success(self):
        """
        Reloads results one last time in case the filewatcher doesn't work
        """

        # Do another dump first to store manually entered results
        scanresults.dump(self.results, TESTS_RESULTS_FILE_PATH)

        # Reload merged results
        self.results = scanresults.parse(TESTS_RESULTS_FILE_PATH)

        # Update UI
        self.update_question_tree_widget()

        # Reenable the button in case someone needs to scan again
        self.ui.openCameraButton.setEnabled(True)

        # Msg for debugging purposes
        print 'All OK with the scanning!'
예제 #15
0
class ProjectWidget(Ui_Form, QWidget):
    SampleWidgetRole = Qt.UserRole + 1
    projectsaved = pyqtSignal()
    projectupdated = pyqtSignal(object)
    projectloaded = pyqtSignal(object)
    selectlayersupdated = pyqtSignal(list)

    def __init__(self, parent=None):
        super(ProjectWidget, self).__init__(parent)
        self.setupUi(self)
        self.project = None
        self.bar = None
        self.roamapp = None

        menu = QMenu()

        # self.roamVersionLabel.setText("You are running IntraMaps Roam version {}".format(roam.__version__))

        self.openProjectFolderButton.pressed.connect(self.openprojectfolder)
        self.openinQGISButton.pressed.connect(self.openinqgis)
        self.depolyProjectButton.pressed.connect(self.deploy_project)
        self.depolyInstallProjectButton.pressed.connect(functools.partial(self.deploy_project, True))

        self.filewatcher = QFileSystemWatcher()
        self.filewatcher.fileChanged.connect(self.qgisprojectupdated)

        self.projectupdatedlabel.linkActivated.connect(self.reloadproject)
        self.projectupdatedlabel.hide()

        # self.setpage(4)
        self.currentnode = None
        self.form = None

        qgislocation = r'C:\OSGeo4W\bin\qgis.bat'
        qgislocation = roam.config.settings.setdefault('configmanager', {}) \
            .get('qgislocation', "")

        self.qgispathEdit.setText(qgislocation)
        self.qgispathEdit.textChanged.connect(self.save_qgis_path) 
        self.filePickerButton.pressed.connect(self.set_qgis_path)

        self.connect_page_events()

        QgsProject.instance().readProject.connect(self.projectLoaded)

    def connect_page_events(self):
        """
        Connect the events from all the pages back to here
        """
        for index in range(self.stackedWidget.count()):
            widget = self.stackedWidget.widget(index)
            if hasattr(widget, "raiseMessage"):
                widget.raiseMessage.connect(self.bar.pushMessage)

    def set_qgis_path(self):
        """
        Set the location of the QGIS install.  We need the path to be able to create Roam
        projects
        """
        path = QFileDialog.getOpenFileName(self, "Select QGIS install file", filter="(*.bat)")
        if not path:
            return
        self.qgispathEdit.setText(path)
        self.save_qgis_path(path)

    def save_qgis_path(self, path):
        """
        Save the QGIS path back to the Roam config.
        """
        roam.config.settings['configmanager'] = {'qgislocation': path}
        roam.config.save()

    def setpage(self, page, node, refreshingProject=False):
        """
        Set the current page in the config manager.  We pass the project into the current
        page so that it knows what the project is.
        """
        self.currentnode = node

        if not refreshingProject and self.project:
            self.write_config_currentwidget()
        else:
            roam.utils.info("Reloading project. Not saving current config values")

        self.unload_current_widget()

        # Set the new widget for the selected page
        self.stackedWidget.setCurrentIndex(page)

        widget = self.stackedWidget.currentWidget()
        print "New widget {}".format(widget.objectName())
        if hasattr(widget, "set_project"):
            widget.set_project(self.project, self.currentnode)

    def unload_current_widget(self):
        print "NOTIFY!!"
        widget = self.stackedWidget.currentWidget()
        print widget
        if hasattr(widget, "unload_project"):
            widget.unload_project()

    def write_config_currentwidget(self):
        """
        Call the write config command on the current widget.
        """
        widget = self.stackedWidget.currentWidget()
        if hasattr(widget, "write_config"):
            roam.utils.debug("Write config for {} in project {}".format(widget.objectName(), self.project.name))
            widget.write_config()

    def deploy_project(self, with_data=False):
        """
        Run the step to deploy a project. Projects are deplyed as a bundled zip of the project folder.
        """
        if self.roamapp.sourcerun:
            base = os.path.join(self.roamapp.apppath, "..")
        else:
            base = self.roamapp.apppath

        default = os.path.join(base, "roam_serv")
        path = roam.config.settings.get("publish", {}).get("path", '')
        if not path:
            path = default

        path = os.path.join(path, "projects")

        if not os.path.exists(path):
            os.makedirs(path)

        self._saveproject(update_version=True, reset_save_point=True)
        options = {}

        bundle.bundle_project(self.project, path, options, as_install=with_data)

    def setaboutinfo(self):
        """
        Set the current about info on the widget
        """
        self.versionLabel.setText(roam.__version__)
        self.qgisapiLabel.setText(unicode(QGis.QGIS_VERSION))

    def selectlayerschanged(self, *args):
        """
        Run the updates when the selection layers have changed
        """
        self.formlayers.setSelectLayers(self.project.selectlayers)
        self.selectlayersupdated.emit(self.project.selectlayers)

    def reloadproject(self, *args):
        """
        Reload the project. At the moment this will drop any unsaved changes to the config.
        Note: Should look at making sure it doesn't do that because it's not really needed.
        """
        self.projectupdated.emit(self.project)
        # self.setproject(self.project)

    def qgisprojectupdated(self, path):
        """
        Show a message when the QGIS project file has been updated.
        """
        self.projectupdatedlabel.show()
        self.projectupdatedlabel.setText("The QGIS project has been updated. <a href='reload'> "
                                         "Click to reload</a>. <b style=\"color:red\">Unsaved data will be lost</b>")

    def openinqgis(self):
        """
        Open a QGIS session for the user to config the project layers.
        """
        try:
            openqgis(self.project.projectfile)
        except OSError:
            self.bar.pushMessage("Looks like I couldn't find QGIS",
                                 "Check qgislocation in roam.config", QgsMessageBar.WARNING)

    def openprojectfolder(self):
        """
        Open the project folder in the file manager for the OS.
        """
        folder = self.project.folder
        openfolder(folder)

    def setproject(self, project):
        """
        Set the widgets active project.
        """
        self.unload_current_widget()

        if self.project:
            savelast = QMessageBox.question(self,
                                    "Save Current Project",
                                    "Save {}?".format(self.project.name),
                                    QMessageBox.Save | QMessageBox.No)
            if savelast == QMessageBox.Accepted:
                self._saveproject()

        self.filewatcher.removePaths(self.filewatcher.files())
        self.projectupdatedlabel.hide()
        self._closeqgisproject()

        if project.valid:
            self.startsettings = copy.deepcopy(project.settings)
            self.project = project
            self.projectlabel.setText(project.name)
            self.loadqgisproject(project, self.project.projectfile)

    def projectLoaded(self):
        self.filewatcher.addPath(self.project.projectfile)
        self.projectloaded.emit(self.project)

    def loadqgisproject(self, project, projectfile):
        QDir.setCurrent(os.path.dirname(project.projectfile))
        fileinfo = QFileInfo(project.projectfile)

        # No idea why we have to set this each time.  Maybe QGIS deletes it for
        # some reason.
        self.badLayerHandler = BadLayerHandler(callback=self.missing_layers)
        QgsProject.instance().setBadLayerHandler(self.badLayerHandler)
        QgsProject.instance().read(fileinfo)

    def missing_layers(self, missinglayers):
        """
        Handle any and show any missing layers.
        """
        self.project.missing_layers = missinglayers

    def _closeqgisproject(self):
        """
        Close the current QGIS project and clean up after..
        """
        QGIS.close_project()

    def _saveproject(self, update_version=False, reset_save_point=False):
        """
        Save the project config to disk.
        """
        if not self.project:
            return

        roam.utils.info("Saving project: {}".format(self.project.name))

        self.write_config_currentwidget()
        # self.project.dump_settings()
        self.project.save(update_version=update_version, reset_save_point=reset_save_point)
        self.filewatcher.removePaths(self.filewatcher.files())
        QgsProject.instance().write()
        self.filewatcher.addPath(self.project.projectfile)
        self.projectsaved.emit()
예제 #16
0
class ScanPage(QWizardPage):
    scanError = pyqtSignal()  # name
    scanSuccess = pyqtSignal()
    path = "qtui/ui/page3_scan.ui"

    def __init__(self, project, parentW=None):
        super(ScanPage, self).__init__()
        self.ui = uic.loadUi(join(os.environ['AUTOEXAM_FOLDER'], self.path),
                             self)
        self.project = project
        self.scan_thread = None
        self.ui.treeWidget.currentItemChanged.connect(self.change_tree_item)
        self.ui.openCameraButton.clicked.connect(self.open_camera)
        self.question_item_to_question = {}

        self.exams = []
        self.order = None
        self.results = None
        self.parentWizard = parentW
        self.scanError.connect(self.on_scan_error)
        self.scanSuccess.connect(self.on_scan_success)
        self.scanning = False

    def initializePage(self):
        super(ScanPage, self).initializePage()
        # api.add_scan_event_subscriber(self)

        self.open_camera()

        if os.path.exists(ORDER_FILE_PATH):
            self.order = scanresults.parse(ORDER_FILE_PATH)

        self.watcher = QFileSystemWatcher()
        self.watcher.fileChanged.connect(self.on_scan_file_change)

        if os.path.exists(TESTS_RESULTS_FILE_PATH):
            self.results = scanresults.parse(TESTS_RESULTS_FILE_PATH)
            self.update_question_tree_widget()
        else:
            with open(TESTS_RESULTS_FILE_PATH, 'w') as f:
                f.write('{}')
            self.results = scanresults.parse(TESTS_RESULTS_FILE_PATH)

        # TODO: Check why this doesn't always work
        self.watcher.addPath(TESTS_RESULTS_FILE_PATH)
        self.last_load_time = time.time()

    def update_question_tree_widget(self):
        tree = self.ui.treeWidget
        tree.clear()

        for i in range(len(self.order)):
            incomplete_test = False
            exam_item = QTreeWidgetItem(tree, ['Exam %d' % i])
            for j in range(len(self.order[i].questions)):
                question_item = QTreeWidgetItem(exam_item,
                                                ['Question %d' % (j + 1)])
                if i in self.results:
                    question_item.question = self.project.questions[
                        self.order[i].questions[j].id - 1]
                else:
                    incomplete_test = True
            if incomplete_test:
                exam_item.setBackground(0, QBrush(Qt.lightGray))

        first_exam_item = tree.topLevelItem(0)
        first_exam_item.setExpanded(True)
        tree.setCurrentItem(tree.itemBelow(first_exam_item))

    def validatePage(self):

        if self.scanning:
            self.show_modal_message(
                'Please close the scanner window first by pressing \'q\'')
            return False
        # TODO: Warning validation here!!!
        if self.results is not None:
            scanresults.dump(self.results,
                             TESTS_RESULTS_FILE_PATH,
                             overwrite=False)
            self.parentWizard.results = self.results
            return True
        else:
            self.show_modal_message('There are still no results to save')
            return False

    def cleanupPage(self):
        super(ScanPage, self).cleanupPage()
        # api.remove_scan_event_subscriber(self)

        # TODO: Do proper shutdown
        # self.scan_thread.__stop()

    # def on_scan_event(self, report):
    #     if report.success:
    #         print 'successful report: ', report
    #         self.process_report(report)
    #     else:
    #         print 'failed report: ', report

    def on_scan_file_change(self, filename):
        if time.time() - self.last_load_time > 2:
            try:
                print('Reloading results...')
                self.results = scanresults.parse(TESTS_RESULTS_FILE_PATH)
                self.update_question_tree_widget()
                self.last_load_time = time.time()
                print('Results reloaded')
            except:
                print('Could not load results.')
        else:
            # print('Ignoring repetitive filewatcher event (this is normal)')
            pass

    # def process_report(self, report):
    #     current = self.ui.treeWidget.topLevelItem(report.test.id)
    #
    #     if current:
    #         if len(report.test.warnings) == 0:
    #             current.setForeground(0, ok_color)
    #         elif len(report.test.warnings) > 0:
    #             current.setForeground(0, warn_color)

    def change_tree_item(self):
        currentItem = self.ui.treeWidget.currentItem()
        if currentItem is not None:
            self.cleanupPanel()
            self.current_item = currentItem
            if currentItem.parent() is not None:  # i.e. it is a question
                self.update_question_panel_with_question()
            else:
                self.update_question_panel_with_exam()

    def update_question_panel_with_question(self):
        current_exam_item = self.current_item.parent()
        exam_no = self.ui.treeWidget.indexOfTopLevelItem(current_exam_item)
        question_no = current_exam_item.indexOfChild(self.current_item)
        question_info = self.project.questions[
            self.order[exam_no].questions[question_no].id - 1]

        self.ui.questionTextLabel.setText(question_info.text)

        # self.ui.questionDataLayout.addWidget(QLabel(question_info.text))

        order_info = self.order[exam_no].questions[question_no].order

        for answer_no in order_info:
            answer = question_info.answers[answer_no]
            answer_text = answer.text
            answer_text += ' (x)' if answer.valid else ''
            question_answer_check = QCheckBox(answer_text)
            question_answer_check.setChecked(
                self.is_answer_checked(exam_no, question_no, answer_no))
            question_answer_check.stateChanged.connect(
                self.update_current_question_state)
            self.ui.questionDataLayout.addWidget(question_answer_check)

    def update_question_panel_with_exam(self):
        current_exam_item = self.current_item
        exam_no = self.ui.treeWidget.indexOfTopLevelItem(current_exam_item)

        if self.results is not None:
            if exam_no in self.results:
                question_text_label = QLabel('TODO: Put here the warnings...')
            else:
                question_text_label = QLabel('This exam has not been scanned!')
        else:
            print('results null! please check!')

        self.ui.questionDataLayout.addWidget(question_text_label)

    def is_answer_checked(self, exam_no, question_no, answer_no):
        # print 'is_answer_checked'
        # print(exam_no,question_no,answer_no)

        try:
            results_data = self.results
            exam_data = results_data[exam_no]

            question_data = exam_data.questions[question_no]
            return answer_no in question_data.answers
        except:
            # print('answer not scanned exception')
            return False

    def update_current_question_state(self, state):
        current_exam_item = self.current_item.parent()

        exam_no = self.ui.treeWidget.indexOfTopLevelItem(current_exam_item)
        question_no = current_exam_item.indexOfChild(self.current_item)

        results_data = self.results

        if exam_no in results_data:
            question_data = results_data[exam_no].questions[question_no]

            order_data = self.order[exam_no].questions[question_no].order
            self.synchronize_answers_with_model(order_data, question_data)

        else:
            # If we get here, we're trying to manually enter info for an
            # exam that has not been scanned yet.
            # We should create an entry in results_data similar
            confirm_edit = QMessageBox.question(
                None, "Manual input?",
                "Do you want to manually enter this test's results?",
                QMessageBox.Yes | QMessageBox.No)
            regen = confirm_edit == QMessageBox.Yes
            if confirm_edit:
                self.results[exam_no] = self.order[exam_no]
                question_data = results_data[exam_no].questions[question_no]
                order_data = self.order[exam_no].questions[question_no].order
                self.synchronize_answers_with_model(order_data, question_data)

                current_exam_item.setBackground(0, QBrush(Qt.white))

    def synchronize_answers_with_model(self, order_data, question_data):
        for i in range(len(order_data)):
            checked = self.ui.questionDataLayout.itemAt(i).widget().isChecked()
            question_idx = order_data[i]
            if checked and question_idx not in question_data.answers:
                question_data.answers.append(question_idx)
            elif not checked and question_idx in question_data.answers:
                question_data.answers.remove(question_idx)

    def cleanupPage(self):
        self.watcher.fileChanged.disconnect(self.on_scan_file_change)
        del self.watcher

    def cleanupPanel(self):
        for i in reversed(range(self.ui.questionDataLayout.count())):
            elem = self.ui.questionDataLayout.itemAt(i)
            if not elem:
                break
            elem.widget().deleteLater()

    def start_scan(self):
        class _args:
            outfile = TESTS_RESULTS_FILE_PATH
            cameras = [self.ui.cameraIndexSpin.value()
                       ]  #TODO: UN-WIRE THIS !!!!
            folder = IMAGES_FOLDER
            time = None
            autowrite = True
            poll = None
            debug = False

        self.scanning = True

        ok = api.scan(_args())  # TODO: Fill dictionary properly
        if ok:
            self.scanSuccess.emit()
        else:
            self.scanError.emit()

        self.scanning = False

    def open_camera(self):
        self.ui.openCameraButton.setEnabled(False)

        self.scan_thread = Thread(target=self.start_scan)
        self.scan_thread.setDaemon(True)
        self.scan_thread.start()
        # self.start_scan()

    def on_scan_error(self):
        # self.parentWizard.back()
        self.show_modal_message("There was an error in the scanning process.\
        Please, check if the right camera is selected and try again.")
        self.ui.openCameraButton.setEnabled(True)

    def show_modal_message(self, msg):
        """
        Shows a modal MessageBox displaying the argument string
        """
        msgBox = QMessageBox()
        msgBox.setText(msg)
        msgBox.setModal(True)
        msgBox.setIcon(QMessageBox.Warning)
        msgBox.exec_()

    def on_scan_success(self):
        """
        Reloads results one last time in case the filewatcher doesn't work
        """

        # Do another dump first to store manually entered results
        scanresults.dump(self.results, TESTS_RESULTS_FILE_PATH)

        # Reload merged results
        self.results = scanresults.parse(TESTS_RESULTS_FILE_PATH)

        # Update UI
        self.update_question_tree_widget()

        # Reenable the button in case someone needs to scan again
        self.ui.openCameraButton.setEnabled(True)

        # Msg for debugging purposes
        print 'All OK with the scanning!'
예제 #17
0
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        """ init UI """
        QMainWindow.__init__(self, parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.ui.actionSaveSession.setEnabled(False)

        self.distributedObjects = DistributedObjects(self)

        self.act = self.distributedObjects.actions
        self.debugController = self.distributedObjects.debugController
        self.settings = self.distributedObjects.settings
        self.signalproxy = self.distributedObjects.signalProxy
        self.pluginloader = PluginLoader(self.distributedObjects)
        self.editorController = self.distributedObjects.editorController

        self.act = self.distributedObjects.actions
        # init RecentFileHandler
        self.recentFileHandler = RecentFileHandler(self, self.ui.menuRecentlyUsedFiles, self.distributedObjects)
        self.debugController.executableOpened.connect(self.recentFileHandler.addToRecentFiles)
        self.debugController.executableOpened.connect(self.__observeWorkingBinary)
        self.debugController.executableOpened.connect(self.showExecutableName)
        self.debugController.executableOpened.connect(self.disableButtons)
        # signal proxy
        self.signalproxy.inferiorIsRunning.connect(self.targetStartedRunning)
        self.signalproxy.inferiorStoppedNormally.connect(self.targetStopped)
        self.signalproxy.inferiorReceivedSignal.connect(self.targetStopped)
        self.signalproxy.inferiorHasExited.connect(self.targetExited)

        # Plugin Loader
        self.pluginloader.insertPluginAction.connect(self.addPluginAction)

        self.ui.actionSavePlugins.triggered.connect(self.showSavePluginsDialog)
        self.ui.actionLoadPlugins.triggered.connect(self.showLoadPluginsDialog)

        # Add editor to main window.
        self.ui.gridLayout.addWidget(self.distributedObjects.editorController.editor_view, 0, 0, 1, 1)

        self.pluginloader.addAvailablePlugins()

        self.setWindowFilePath("<none>")
        self.setupUi()
        self.readSettings()

        self.quickwatch = QuickWatch(self, self.distributedObjects)

        self.binaryName = None
        self.fileWatcher = QFileSystemWatcher()
        self.fileWatcher.fileChanged.connect(self.__binaryChanged)

        self.__runWithArgumentsMenu = None
        self.__argumentsEdit = None
        self.__makeRunWithArgumentsMenu()

    def __makeRunWithArgumentsMenu(self):
        self.__runWithArgumentsMenu = QMenu(self)
        self.__argumentsEdit = QLineEdit()
        self.__argumentsEdit.returnPressed.connect(self.__runWithArgumentsTriggered)

        hl = QHBoxLayout(self.__runWithArgumentsMenu)
        hl.addWidget(QLabel("Run with arguments:"))
        hl.addWidget(self.__argumentsEdit)

        w = QWidget(self.__runWithArgumentsMenu)
        w.setLayout(hl)

        wa = QWidgetAction(self.__runWithArgumentsMenu)
        wa.setDefaultWidget(w)
        self.__runWithArgumentsMenu.addAction(wa)

        self.act.Run.setMenu(self.__runWithArgumentsMenu)

    def __runTriggered(self):
        self.debugController.run()

    def __runWithArgumentsTriggered(self):
        self.__runWithArgumentsMenu.close()
        self.debugController.run(str(self.__argumentsEdit.text()))

    def setupUi(self):
        self.__initActions()
        self.ui.statusLabel = QLabel()
        self.ui.statusLabel.setText("Not running")
        self.ui.statusbar.addPermanentWidget(self.ui.statusLabel)
        self.ui.statusIcon = QLabel()
        self.ui.statusIcon.setPixmap(QPixmap(":/icons/images/22x22/not_running.png"))
        self.ui.statusbar.addPermanentWidget(self.ui.statusIcon)

    def __initActions(self):
        self.disableButtons()
        self.act.Record.setCheckable(True)
        self.act.ReverseNext.setEnabled(False)
        self.act.ReverseStep.setEnabled(False)
        self.act.SaveFile.setEnabled(False)
        self.act.Beautify.setCheckable(True)
        self.act.Beautify.setChecked(True)
        # debug actions
        self.ui.menuDebug.addAction(self.act.Run)
        self.ui.menuDebug.addAction(self.act.Continue)
        self.ui.menuDebug.addAction(self.act.Interrupt)
        self.ui.menuDebug.addAction(self.act.Next)
        self.ui.menuDebug.addAction(self.act.Step)
        self.ui.menuDebug.addAction(self.act.Finish)
        self.ui.menuDebug.addAction(self.act.RunToCursor)
        self.ui.menuDebug.addAction(self.act.Record)
        self.ui.menuDebug.addAction(self.act.ReverseNext)
        self.ui.menuDebug.addAction(self.act.ReverseStep)
        self.ui.menuDebug.addAction(self.act.Beautify)

        # file actions
        self.ui.menuFile.insertAction(self.ui.actionSaveSession, self.act.OpenMenu)
        self.ui.menuFile.addAction(self.act.SaveFile)
        self.ui.menuFile.addAction(self.act.Exit)

        # add them to menubar and also menuView to respect order
        self.ui.menubar.addAction(self.ui.menuFile.menuAction())
        self.ui.menubar.addAction(self.ui.menuView.menuAction())
        self.ui.menubar.addAction(self.ui.menuDebug.menuAction())
        self.ui.menubar.addAction(self.ui.menuHelp.menuAction())
        # now make toolbar actions
        self.act.Open.setMenu(self.ui.menuRecentlyUsedFiles)
        self.ui.Main.addAction(self.act.Open)
        self.ui.Main.addAction(self.act.SaveFile)
        self.ui.Main.addSeparator()
        self.ui.Main.addAction(self.act.Run)
        self.ui.Main.addAction(self.act.Continue)
        self.ui.Main.addAction(self.act.Interrupt)
        self.ui.Main.addAction(self.act.Next)
        self.ui.Main.addAction(self.act.Step)
        self.ui.Main.addAction(self.act.Record)
        self.ui.Main.addAction(self.act.ReverseNext)
        self.ui.Main.addAction(self.act.ReverseStep)
        self.ui.Main.addAction(self.act.Finish)
        self.ui.Main.addAction(self.act.RunToCursor)
        self.ui.Main.addAction(self.act.Beautify)

        self.ui.Main.addSeparator()
        self.ui.Main.addAction(self.act.Exit)
        # connect actions
        self.__connectActions()

    def __connectActions(self):
        # file menu
        self.act.Open.triggered.connect(self.showOpenExecutableDialog)
        self.act.OpenMenu.triggered.connect(self.showOpenExecutableDialog)
        self.act.Exit.triggered.connect(self.close)
        self.act.SaveFile.triggered.connect(self.signalproxy.emitSaveCurrentFile)
        # debug menu

        self.act.Run.triggered.connect(self.__runTriggered)

        self.act.Next.triggered.connect(self.debugController.next_)
        self.act.Step.triggered.connect(self.debugController.step)
        self.act.Continue.triggered.connect(self.debugController.cont)
        self.act.Record.triggered.connect(self.toggleRecord)
        self.act.ReverseStep.triggered.connect(self.debugController.reverse_step)
        self.act.ReverseNext.triggered.connect(self.debugController.reverse_next)
        self.act.Beautify.triggered.connect(self.__askBeautify)

        self.act.Interrupt.triggered.connect(self.debugController.interrupt)
        self.act.Finish.triggered.connect(self.debugController.finish)
        self.act.RunToCursor.triggered.connect(self.debugController.inferiorUntil)

        self.ui.actionRestoreSession.triggered.connect(self.distributedObjects.sessionManager.showRestoreSessionDialog)
        self.ui.actionSaveSession.triggered.connect(self.distributedObjects.sessionManager.showSaveSessionDialog)
        self.ui.actionConfigure.triggered.connect(self.distributedObjects.configStore.edit)

    def insertDockWidget(self, widget, name, area, addToggleViewAction):
        d = AlertableDockWidget(name, self)
        d.setObjectName(name)
        d.setWidget(widget)

        self.addDockWidget(area, d)
        if addToggleViewAction:
            self.ui.menuShow_View.addAction(d.toggleViewAction())

        return d

    def insertStatusbarWidget(self, widget):
        w = QFrame()
        w.setLayout(QHBoxLayout())
        w.layout().addWidget(widget)
        f = QFrame()
        f.setFrameStyle(QFrame.Plain | QFrame.VLine)
        w.layout().addWidget(f)

        self.ui.statusbar.insertPermanentWidget(0, w)
        return w

    def removeStatusbarWidget(self, widget):
        self.ui.statusbar.removeWidget(widget)

    def addPluginAction(self, Action):
        """ show plugin as menu entry """
        self.ui.menuPlugins.addAction(Action)

    def showOpenExecutableDialog(self):
        filename = str(QFileDialog.getOpenFileName(self, "Open Executable", self.recentFileHandler.getDirOfLastFile()))
        if filename != "":
            self.debugController.openExecutable(filename)

    def showLoadPluginsDialog(self):
        dialog = QFileDialog()
        dialog.setNameFilter("*.xml")
        filename = str(dialog.getOpenFileName(self, "Load plugin configuration"))
        if filename != "":
            self.pluginloader.getActivePlugins(filename)

    def showSavePluginsDialog(self):
        dialog = QFileDialog()
        dialog.setNameFilter("*.xml")
        filename = str(dialog.getSaveFileName(self, "Save plugin configuration"))
        if filename != "":
            self.pluginloader.savePluginInfo(filename)

    def showExecutableName(self, filename):
        self.ui.actionSaveSession.setEnabled(True)  # enable saving session
        self.setWindowFilePath(filename)

    def targetStartedRunning(self):
        self.ui.statusLabel.setText("Running")
        self.ui.statusIcon.setPixmap(QPixmap(":/icons/images/22x22/running.png"))
        self.disableButtons()
        self.act.Interrupt.setEnabled(True)

    def targetStopped(self, rec):
        self.ui.statusLabel.setText("Stopped")
        self.ui.statusIcon.setPixmap(QPixmap(":/icons/images/22x22/stopped.png"))
        self.enableButtons()
        self.act.Interrupt.setEnabled(False)

    def targetExited(self):
        self.ui.statusLabel.setText("Not running")
        self.disableButtons()
        self.ui.statusIcon.setPixmap(QPixmap(":/icons/images/22x22/not_running.png"))

    def closeEvent(self, event):
        if not self.distributedObjects.editorController.closeOpenedFiles():
            event.ignore()  # closing source files may be canceled by user
        else:
            self.settings.setValue("geometry", self.saveGeometry())
            self.settings.setValue("windowState", self.saveState())
            QMainWindow.closeEvent(self, event)
            self.pluginloader.savePluginInfo()

    def readSettings(self):
        self.restoreGeometry(self.settings.value("geometry").toByteArray())
        self.restoreState(self.settings.value("windowState").toByteArray())

    def toggleRecord(self, check):
        if check:
            self.debugController.record_start()
            self.act.ReverseNext.setEnabled(True)
            self.act.ReverseStep.setEnabled(True)
        else:
            self.debugController.record_stop()
            self.act.ReverseNext.setEnabled(False)
            self.act.ReverseStep.setEnabled(False)

    def enableButtons(self):
        self.act.Continue.setEnabled(True)
        self.act.Interrupt.setEnabled(True)
        self.act.Next.setEnabled(True)
        self.act.Step.setEnabled(True)
        self.act.Finish.setEnabled(True)
        self.act.RunToCursor.setEnabled(True)
        self.act.Record.setEnabled(True)
        self.act.Beautify.setEnabled(True)

    def disableButtons(self):
        self.act.Continue.setEnabled(False)
        self.act.Interrupt.setEnabled(False)
        self.act.Next.setEnabled(False)
        self.act.Step.setEnabled(False)
        self.act.Finish.setEnabled(False)
        self.act.RunToCursor.setEnabled(False)
        self.act.Record.setChecked(False)
        self.act.Record.setEnabled(False)
        self.act.Beautify.setEnabled(False)

    def __observeWorkingBinary(self, filename):
        """ Private Method to Observe Debugged Binary """
        if self.binaryName != None:
            self.fileWatcher.removePath(self.binaryName)
        self.fileWatcher.addPath(filename)
        self.binaryName = filename

    def __binaryChanged(self):
        """ Slot for FileWatcher - Using QtMessagebox for interaction"""
        box = QtGui.QMessageBox()
        if (
            box.question(self, "Binary Changed!", "Reload File?", QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
            == QtGui.QMessageBox.Yes
        ):
            self.debugController.openExecutable(self.binaryName)
        else:
            self.fileWatcher.removePath(self.binaryName)
            self.fileWatcher.addPath(self.binaryName)

    def __askBeautify(self):
        """ Warn user before Beautify - Using QtMessagebox for interaction"""
        box = QtGui.QMessageBox()
        if (
            box.question(
                self,
                "Warning!",
                "This deletes the objects in watch and datagraphview.",
                QtGui.QMessageBox.Ok,
                QtGui.QMessageBox.Cancel,
            )
            == QtGui.QMessageBox.Ok
        ):
            self.debugController.beautify()
        else:
            self.act.Beautify.toggle()
예제 #18
0
class DebugController(QObject):
    executableOpened = pyqtSignal('PyQt_PyObject')

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

        self.ptyhandler = PtyHandler()

        self.do = do

        self.connector = self.do.gdb_connector
        self.signalProxy = self.do.signalProxy

        self.executableName = None
        self.lastCmdWasStep = False

        self.ptyhandler.start()
        self.connector.start()

        self.connector.reader.asyncRecordReceived.connect(self.handleAsyncRecord, Qt.QueuedConnection)

        self.__config = DebugConfig()
        self.do.configStore.registerConfigSet(self.__config)
        self.__config.itemsHaveChanged.connect(self.updateConfig)

        self.__binaryWatcher = QFileSystemWatcher()
        self.__binaryWatcher.fileChanged.connect(self.__binaryChanged)

    def __reloadAction(self):
        a = QAction("Reload", self)
        a.triggered.connect(lambda: self.openExecutable(self.executableName))
        return a

    def __binaryChanged(self):
        """ Slot for FileWatcher - Using QtMessagebox for interaction"""
        logging.warning("The executable was changed on the disc. Please reload the file.",
                        extra={"actions": [self.__reloadAction()]})

    def updateConfig(self):
        if self.executableName:
            logging.warning("Configuration changed. Please reload executable for changes to take effect!",
                            extra={"actions": [self.__reloadAction()]})

    def openExecutable(self, filename):
        # make sure we only open absolute paths, otherwise eg. RecentFileHandler
        # will not know _where_ the file was we opened and store different
        # relative paths for the same file
        filename = os.path.abspath(filename)

        if not os.path.exists(filename):
            logging.error("File %s was not found.", filename)
            return

        if self.do.editorController.closeOpenedFiles():  # closing source files may be canceled by user
            if self.executableName is not None:
                # clear variables, tracepoints, watches,... by connecting to this signal
                self.signalProxy.cleanupModels.emit()
                self.__binaryWatcher.removePath(self.executableName)

            self.connector.changeWorkingDirectory(os.path.dirname(filename))
            self.connector.openFile(filename)
            if self.__config.breakAtMain.value:
                self.do.breakpointModel.insertBreakpoint("main", None)
            self.executableOpened.emit(filename)
            self.executableName = filename
            self.__binaryWatcher.addPath(self.executableName)

    def run(self, args=None):
        self.connector.setTty(self.ptyhandler.ptyname)
        if not args:
            args = ""
        self.connector.setArgs(args)
        try:
            self.connector.run()
            self.lastCmdWasStep = False
            self.signalProxy.runClicked.emit()
        except GdbError:
            pass

    def setRecord(self, state):
        if state:
            self.connector.record_start()
        else:
            self.connector.record_stop()
        self.signalProxy.recordStateChanged.emit(state)

    def next_(self):
        self.connector.next_()
        self.lastCmdWasStep = True

    def reverse_next(self):
        self.connector.next_(True)
        self.lastCmdWasStep = True

    def step(self):
        self.connector.step()
        self.lastCmdWasStep = True

    def reverse_step(self):
        self.connector.step(True)
        self.lastCmdWasStep = True

    def cont(self):
        self.connector.cont()
        self.lastCmdWasStep = False

    def interrupt(self):
        self.connector.interrupt()
        self.lastCmdWasStep = False

    def finish(self):
        self.connector.finish()
        self.lastCmdWasStep = False

    def reverse_finish(self):
        self.connector.finish(True)
        self.lastCmdWasStep = False

    def evaluateExpression(self, exp):
        if exp == "":
            return None
        exp = exp.replace('"', '\"')
        return self.connector.evaluate("\"" + exp + "\"")

    def executeCliCommand(self, cmd):
        return self.connector.executeCliCommand(cmd)

    def handleAsyncRecord(self, rec):
        if rec.type_ == GdbOutput.EXEC_ASYN and rec.class_ == GdbOutput.STOPPED:
            self.handleStoppedRecord(rec)
        elif rec.type_ == GdbOutput.EXEC_ASYN and rec.class_ == GdbOutput.RUNNING:
            self.signalProxy.inferiorIsRunning.emit(rec)
        elif rec.type_ == GdbOutput.NOTIFY_ASYN and rec.class_ == GdbOutput.THREAD_CREATED:
            self.signalProxy.threadCreated.emit(rec)
        elif rec.type_ == GdbOutput.NOTIFY_ASYN and rec.class_ == GdbOutput.THREAD_EXITED:
            self.signalProxy.threadExited.emit(rec)
        elif rec.type_ == GdbOutput.NOTIFY_ASYN and rec.class_ == GdbOutput.BREAKPOINT_MODIFIED:
            self.signalProxy.breakpointModified.emit(rec)

    def handleStoppedRecord(self, rec):
        # With reverse debugging, some stopped records might not contain a
        # reason. Predefine it as None, since all unknown reasons will be
        # handled as the inferior having stopped normally.
        fields = ["reason", "frame", "signal-name", "signal-meaning", "bkptno", "wpt", "value"]
        field = defaultdict(None)

        for r in rec.results:
            if r.dest in fields:
                field[r.dest] = r.src

        if field["reason"] in ['exited-normally', 'exited']:
            self.signalProxy.inferiorHasExited.emit(rec)
        elif field["reason"] == 'breakpoint-hit':
            # Ok, we're kind of frantically trying to cover all bases here. We
            # cannot simply check for file:line combination reported in the
            # stopped message, since breakpoints may be set to a certain line
            # (which GDB also reports back as the line where the breakpoint is
            # really located), but one of the following lines may be reported in
            # the stopped message (eg., if the breakpoint is set to a function
            # header, the line reported here will be the first line of the
            # function's body).

            # Therefore, we're checking what was hit using the reported
            # breakpoint number. However, if the user sets both a breakpoint and
            # a tracepoint in the same line, only one number will be reported
            # here, but we need to handle both. Therefore, check the location
            # where what we found was supposed to be, and check if something
            # else was supposed to be there too. This still might be a problem
            # (eg. setting a breakpoint and a tracepoint in the line following
            # the breakpoint, both of which would cause the program to suspend
            # on yet another line), but that's about as good as our guessing
            # currently gets.
            tp = self.do.tracepointController.model().breakpointByNumber(int(field["bkptno"]))
            bp = self.do.breakpointModel.breakpointByNumber(int(field["bkptno"]))
            assert tp or bp  # either a TP or a BP must have been hit

            # now that we have one, check if the other is here too
            if bp and not tp:
                tp = self.do.tracepointController.model().breakpointByLocation(bp.fullname, bp.line)
            elif tp and not bp:
                bp = self.do.breakpointModel.breakpointByLocation(tp.fullname, tp.line)

            if tp:
                # this will cause the variable pool to update all variables
                self.do.signalProxy.tracepointOccurred.emit()
                tp.recordData()

            if self.lastCmdWasStep or bp:
                self.signalProxy.inferiorStoppedNormally.emit(rec)
                self.lastCmdWasStep = False
            else:
                assert tp  # if this was not a breakpoint, it must have been a tracepoint
                self.connector.cont()
        elif field["reason"] == "signal-received":
            logging.warning("Inferior received signal <b>%s</b> (%s) at <b>%s:%s</b>.", field["signal-name"], field["signal-meaning"], field["frame"].file, field["frame"].line)
            self.signalProxy.inferiorReceivedSignal.emit(rec)
        elif field["reason"] == "watchpoint-trigger":
            logging.warning("Watchpoint %s on expression <b>%s</b> triggered; old value: %s, new value: %s.", field["wpt"].number, self.do.breakpointModel.breakpointByNumber(field["wpt"].number).where, field["value"].old, field["value"].new)
            self.signalProxy.inferiorStoppedNormally.emit(rec)
        else:
            self.signalProxy.inferiorStoppedNormally.emit(rec)

    def executePythonCode(self, code):
        exec(code, {'do': self.do})

    def inferiorUntil(self):
        current_opened_file = self.do.editorController.editor_view.getCurrentOpenedFile()
        line, _ = current_opened_file.getCursorPosition()
        self.connector.until(current_opened_file.filename, line + 1)
        self.lastCmdWasStep = False

    def getExecutableName(self):
        return self.executableName

    def getStackDepth(self):
        return self.connector.getStackDepth()

    def selectStackFrame(self, exp):
        return self.connector.selectStackFrame(exp)
예제 #19
0
class MainWindow(QMainWindow):
    workingDirectory = ''
    fileNames = []
    supportedExtensions = QStringList(('*.txt','*.csv'))
    def __init__(self):
        QMainWindow.__init__(self)

        self.settings = QSettings("greyltc", "batch-iv-analysis")

        self.rows = 0 #keep track of how many rows there are in the table

        self.cols = OrderedDict()

        thisKey = 'plotBtn'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'Draw Plot'
        self.cols[thisKey].tooltip = 'Click this button to draw a plot for that row'        

        thisKey = 'exportBtn'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'Export'
        self.cols[thisKey].tooltip = 'Click this button to export\ninterpolated data points from fits'        

        thisKey = 'file'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'File'
        self.cols[thisKey].tooltip = 'File name\nHover to see header from data file'

        thisKey = 'pce'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'PCE\n[%]'
        self.cols[thisKey].tooltip = 'Power conversion efficiency as found from spline fit\nHover for value from characteristic equation fit'

        thisKey = 'pmax'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'P_max\n[mW/cm^2]'
        self.cols[thisKey].tooltip = 'Maximum power density as found from spline fit\nHover for value from characteristic equation fit'

        thisKey = 'jsc'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'J_sc\n[mA/cm^2]'
        self.cols[thisKey].tooltip = 'Short-circuit current density as found from spline fit\nHover for value from characteristic equation fit'

        thisKey = 'voc'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'V_oc\n[mV]'
        self.cols[thisKey].tooltip = 'Open-circuit voltage as found from spline fit\nHover for value from characteristic equation fit'

        thisKey = 'ff'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'FF'
        self.cols[thisKey].tooltip = 'Fill factor as found from spline fit\nHover for value from characteristic equation fit'

        thisKey = 'rs'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'R_s\n[ohm*cm^2]'
        self.cols[thisKey].tooltip = 'Specific series resistance as found from characteristic equation fit\nHover for 95% confidence interval'

        thisKey = 'rsh'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'R_sh\n[ohm*cm^2]'
        self.cols[thisKey].tooltip = 'Specific shunt resistance as found from characteristic equation fit\nHover for 95% confidence interval'

        thisKey = 'jph'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'J_ph\n[mA/cm^2]'
        self.cols[thisKey].tooltip = 'Photogenerated current density as found from characteristic equation fit\nHover for 95% confidence interval'

        thisKey = 'j0'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'J_0\n[nA/cm^2]'
        self.cols[thisKey].tooltip = 'Reverse saturation current density as found from characteristic equation fit\nHover for 95% confidence interval'

        thisKey = 'n'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'n'
        self.cols[thisKey].tooltip = 'Diode ideality factor as found from characteristic equation fit\nHover for 95% confidence interval'

        thisKey = 'Vmax'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'V_max\n[mV]'
        self.cols[thisKey].tooltip = 'Voltage at maximum power point as found from spline fit\nHover for value from characteristic equation fit'

        thisKey = 'area'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'Area\n[cm^2]'
        self.cols[thisKey].tooltip = 'Device area'

        thisKey = 'pmax2'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'P_max\n[mW]'
        self.cols[thisKey].tooltip = 'Maximum power as found from spline fit\nHover for value from characteristic equation fit'

        thisKey = 'isc'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'I_sc\n[mA]'
        self.cols[thisKey].tooltip = 'Short-circuit current as found from characteristic equation fit\nHover for 95% confidence interval'

        thisKey = 'iph'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'I_ph\n[mA]'
        self.cols[thisKey].tooltip = 'Photogenerated current as found from characteristic equation fit\nHover for 95% confidence interval'

        thisKey = 'i0'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'I_0\n[nA]'
        self.cols[thisKey].tooltip = 'Reverse saturation current as found from characteristic equation fit\nHover for 95% confidence interval'

        thisKey = 'rs2'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'R_s\n[ohm]'
        self.cols[thisKey].tooltip = 'Series resistance as found from characteristic equation fit\nHover for 95% confidence interval'

        thisKey = 'rsh2'
        self.cols[thisKey] = col()
        self.cols[thisKey].header = 'R_sh\n[ohm]'
        self.cols[thisKey].tooltip = 'Shunt resistance as found from characteristic equation fit\nHover for 95% confidence interval'		


        #how long status messages show for
        self.messageDuration = 2500#ms

        # Set up the user interface from Designer.
        self.ui = Ui_batch_iv_analysis()
        self.ui.setupUi(self)

        #insert cols
        for item in self.cols:
            blankItem = QTableWidgetItem()
            thisCol = self.cols.keys().index(item)
            self.ui.tableWidget.insertColumn(thisCol)
            blankItem.setToolTip(self.cols[item].tooltip)
            blankItem.setText(self.cols[item].header)
            self.ui.tableWidget.setHorizontalHeaderItem(thisCol,blankItem)
        
        #file system watcher
        self.watcher = QFileSystemWatcher(self)
        self.watcher.directoryChanged.connect(self.handleWatchUpdate)

        #connect signals generated by gui elements to proper functions 
        self.ui.actionOpen.triggered.connect(self.openCall)
        self.ui.actionEnable_Watching.triggered.connect(self.watchCall)
        self.ui.actionSave.triggered.connect(self.handleSave)
        self.ui.actionWatch_2.triggered.connect(self.handleWatchAction)
        

        self.ui.actionClear_Table.triggered.connect(self.clearTableCall)

    def exportInterp(self,row):
        thisGraphData = self.ui.tableWidget.item(row,self.cols.keys().index('plotBtn')).data(Qt.UserRole).toPyObject()
        fitX = thisGraphData[QString(u'fitX')]
        modelY = thisGraphData[QString(u'modelY')]
        splineY = thisGraphData[QString(u'splineY')]
        a = np.asarray([fitX, modelY, splineY])
        a = np.transpose(a)
        destinationFolder = os.path.join(self.workingDirectory,'exports')
        QDestinationFolder = QDir(destinationFolder)
        if not QDestinationFolder.exists():
            QDir().mkdir(destinationFolder)
        saveFile = os.path.join(destinationFolder,str(self.ui.tableWidget.item(row,self.cols.keys().index('file')).text())+'.csv')
        header = 'Voltage [V],CharEqn Current [mA/cm^2],Spline Current [mA/cm^2]'
        try:
            np.savetxt(saveFile, a, delimiter=",",header=header)
            self.ui.statusbar.showMessage("Exported " + saveFile,5000)
        except:
            self.ui.statusbar.showMessage("Could not export " + saveFile,self.messageDuration)
        


    def handleButton(self):
        btn = self.sender()
        #kinda hacky:
        row = self.ui.tableWidget.indexAt(btn.pos()).row()
        col = self.ui.tableWidget.indexAt(btn.pos()).column()
        if col == 0:
            self.rowGraph(row)
        if col == 1:
            self.exportInterp(row)


    def rowGraph(self,row):
        thisGraphData = self.ui.tableWidget.item(row,self.cols.keys().index('plotBtn')).data(Qt.UserRole).toPyObject()
        filename = str(self.ui.tableWidget.item(row,self.cols.keys().index('file')).text())
        
        v = thisGraphData[QString(u'v')]
        i = thisGraphData[QString(u'i')]
        if not thisGraphData[QString(u'vsTime')]:
            plt.plot(v, i, c='b', marker='o', ls="None",label='I-V Data')
            plt.scatter(thisGraphData[QString(u'Vmax')], thisGraphData[QString(u'Imax')], c='g',marker='x',s=100)
            plt.scatter(thisGraphData[QString(u'Voc')], 0, c='g',marker='x',s=100)
            plt.scatter(0, thisGraphData[QString(u'Isc')], c='g',marker='x',s=100)
            fitX = thisGraphData[QString(u'fitX')]
            modelY = thisGraphData[QString(u'modelY')]
            splineY = thisGraphData[QString(u'splineY')]
            if not np.isnan(modelY[0]):
                plt.plot(fitX, modelY,c='k', label='CharEqn Best Fit')
            plt.plot(fitX, splineY,c='g', label='Spline Fit')
            plt.autoscale(axis='x', tight=True)
            plt.grid(b=True)
            ax = plt.gca()
            handles, labels = ax.get_legend_handles_labels()
            ax.legend(handles, labels, loc=3)
    
            plt.annotate(
                thisGraphData[QString(u'Voc')].__format__('0.4f')+ ' V', 
                xy = (thisGraphData[QString(u'Voc')], 0), xytext = (40, 20),
                textcoords = 'offset points', ha = 'right', va = 'bottom',
                bbox = dict(boxstyle = 'round,pad=0.5', fc = 'yellow', alpha = 0.5),
                arrowprops = dict(arrowstyle = '->', connectionstyle = 'arc3,rad=0'))
    
            plt.annotate(
                float(thisGraphData[QString(u'Isc')]).__format__('0.4f') + ' mA/cm^2', 
                xy = (0,thisGraphData[QString(u'Isc')]), xytext = (40, 20),
                textcoords = 'offset points', ha = 'right', va = 'bottom',
                bbox = dict(boxstyle = 'round,pad=0.5', fc = 'yellow', alpha = 0.5),
                arrowprops = dict(arrowstyle = '->', connectionstyle = 'arc3,rad=0'))
    
            plt.annotate(
                float(thisGraphData[QString(u'Imax')]*thisGraphData[QString(u'Vmax')]).__format__('0.4f') + '% @(' + float(thisGraphData[QString(u'Vmax')]).__format__('0.4f') + ',' + float(thisGraphData[QString(u'Imax')]).__format__('0.4f') + ')', 
                xy = (thisGraphData[QString(u'Vmax')],thisGraphData[QString(u'Imax')]), xytext = (80, 40),
                textcoords = 'offset points', ha = 'right', va = 'bottom',
                bbox = dict(boxstyle = 'round,pad=0.5', fc = 'yellow', alpha = 0.5),
                arrowprops = dict(arrowstyle = '->', connectionstyle = 'arc3,rad=0'))		
    
            plt.ylabel('Current [mA/cm^2]')
            plt.xlabel('Voltage [V]')
        else: #vs time
            time = thisGraphData[QString(u'time')]
            
            fig, ax1 = plt.subplots()
            ax1.plot(time, v, 'b-',label='Voltage [V]')
            ax1.set_xlabel('Time [s]')
            # Make the y-axis label and tick labels match the line color.
            ax1.set_ylabel('Voltage [V]', color='b')
            for tl in ax1.get_yticklabels():
                tl.set_color('b')
            #fdsf
            ax2 = ax1.twinx()
            ax2.plot(time, i, 'r-')
            ax2.set_ylabel('Current [mA/cm^2]', color='r')
            for tl in ax2.get_yticklabels():
                tl.set_color('r')            
        
        plt.title(filename)
        plt.draw()
        plt.show()

    def handleSave(self):
        if self.settings.contains('lastFolder'):
            saveDir = self.settings.value('lastFolder').toString()
        else:
            saveDir = '.'        
        path = QFileDialog.getSaveFileName(self, caption='Save File', directory=saveDir)
        if not str(path[0]) == '':
            with open(unicode(path), 'wb') as stream:
                writer = csv.writer(stream)
                rowdata = []
                for column in range(self.ui.tableWidget.columnCount()):
                    item = self.ui.tableWidget.horizontalHeaderItem(column)
                    if item is not None:
                        rowdata.append(unicode(item.text()).encode('utf8').replace('\n',' '))
                    else:
                        rowdata.append('')
                writer.writerow(rowdata)                
                for row in range(self.ui.tableWidget.rowCount()):
                    rowdata = []
                    for column in range(self.ui.tableWidget.columnCount()):
                        item = self.ui.tableWidget.item(row, column)
                        if item is not None:
                            rowdata.append(unicode(item.text()).encode('utf8'))
                        else:
                            rowdata.append('')
                    writer.writerow(rowdata)
                stream.close()

    def clearTableCall(self):
        for ii in range(self.rows):
            self.ui.tableWidget.removeRow(0)
        self.ui.tableWidget.clearContents()
        self.rows = 0
        self.fileNames = []

    def processFile(self,fullPath):
        fileName, fileExtension = os.path.splitext(fullPath)
        fileName = os.path.basename(fullPath)
        self.fileNames.append(fileName)
        if fileExtension == '.csv':
            delimiter = ','
        else:
            delimiter = None

        self.ui.statusbar.showMessage("processing: "+ fileName,2500)
        
        #wait here for the file to be completely written to disk and closed before trying to read it

        fi = QFileInfo(fullPath)
        while (not fi.isWritable()):
                time.sleep(0.001)
                fi.refresh()
        
        fp = open(fullPath, mode='r')
        fileBuffer = fp.read()
        fp.close()
        first10 = fileBuffer[0:10]
        nMcHeaderLines = 25 #number of header lines in mcgehee IV file format
        isMcFile = False #true if this is a McGehee iv file format
        if (not first10.__contains__('#')) and (first10.__contains__('/')) and (first10.__contains__('\t')):#the first line is not a comment
            #the first 8 chars do not contain comment symbol and do contain / and a tab, it's safe to assume mcgehee iv file format
            isMcFile = True
            #comment out the first 25 rows here
            fileBuffer = '#'+fileBuffer
            fileBuffer = fileBuffer.replace('\n', '\n#',nMcHeaderLines-1)

        splitBuffer = fileBuffer.splitlines(True)
        
        
        area = 1
        noArea = True
        vsTime = False #this is not an i,v vs t data file
        #extract header lines and search for area
        header = []
        for line in splitBuffer:
            if line.startswith('#'):
                header.append(line)
                if line.__contains__('Area'):
                    area = float(line.split(' ')[3])
                    noArea = False
                if line.__contains__('I&V vs t'):
                    if float(line.split(' ')[5]) == 1:
                        vsTime = True
            else:
                break
        
        outputScaleFactor = np.array(1000/area) #for converstion to [mA/cm^2]

        tempFile = QTemporaryFile()
        tempFile.open()
        tempFile.writeData(fileBuffer)
        tempFile.flush()

        #read in data
        try:
            data = np.loadtxt(str(tempFile.fileName()),delimiter=delimiter)
            VV = data[:,0]
            II = data[:,1]
            if vsTime:
                time = data[:,2]
        except:
            self.ui.statusbar.showMessage('Could not read' + fileName +'. Prepend # to all non-data lines and try again',2500)
            return
        tempFile.close()
        tempFile.remove()

        
        if isMcFile: #convert to amps
            II = II/1000*area

        if not vsTime:
            #sort data by ascending voltage
            newOrder = VV.argsort()
            VV=VV[newOrder]
            II=II[newOrder]
            #remove duplicate voltage entries
            VV, indices = np.unique(VV, return_index =True)
            II = II[indices]
        else:
            #sort data by ascending time
            newOrder = time.argsort()
            VV=VV[newOrder]
            II=II[newOrder]
            time=time[newOrder]
            time=time-time[0]#start time at t=0

        #catch and fix flipped current sign:
        if II[0] < II[-1]:
            II = II * -1       

        indexInQuad1 = np.logical_and(VV>0,II>0)
        if any(indexInQuad1): #enters statement if there is at least one datapoint in quadrant 1
            isDarkCurve = False
        else:
            self.ui.statusbar.showMessage("Dark curve detected",500)
            isDarkCurve = True
        
        #put items in table
        self.ui.tableWidget.insertRow(self.rows)
        for ii in range(len(self.cols)):
            self.ui.tableWidget.setItem(self.rows,ii,QTableWidgetItem())        
        
        if not vsTime:
            fitParams, fitCovariance, infodict, errmsg, ier = self.bestEffortFit(VV,II)
        
            #print errmsg
    
            I0_fit = fitParams[0]
            Iph_fit = fitParams[1]
            Rs_fit = fitParams[2]
            Rsh_fit = fitParams[3]
            n_fit = fitParams[4]
    
            
            #0 -> LS-straight line
            #1 -> cubic spline interpolant
            smoothingParameter = 1-2e-6
            iFitSpline = SmoothSpline(VV, II, p=smoothingParameter)
    
            def cellModel(voltageIn):
                #voltageIn = np.array(voltageIn)
                return vectorizedCurrent(voltageIn, I0_fit, Iph_fit, Rs_fit, Rsh_fit, n_fit)
    
            def invCellPowerSpline(voltageIn):
                if voltageIn < 0:
                    return 0
                else:
                    return -1*voltageIn*iFitSpline(voltageIn)
    
            def invCellPowerModel(voltageIn):
                if voltageIn < 0:
                    return 0
                else:
                    return -1*voltageIn*cellModel(voltageIn)
    
            if not isDarkCurve:
                VVq1 = VV[indexInQuad1]
                IIq1 = II[indexInQuad1]
                vMaxGuess = VVq1[np.array(VVq1*IIq1).argmax()]
                powerSearchResults = optimize.minimize(invCellPowerSpline,vMaxGuess)
                #catch a failed max power search:
                if not powerSearchResults.status == 0:
                    print "power search exit code = " + str(powerSearchResults.status)
                    print powerSearchResults.message
                    vMax = nan
                    iMax = nan
                    pMax = nan
                else:
                    vMax = powerSearchResults.x[0]
                    iMax = iFitSpline([vMax])[0]
                    pMax = vMax*iMax                
    
                #only do this stuff if the char eqn fit was good
                if ier < 5:
                    powerSearchResults_charEqn = optimize.minimize(invCellPowerModel,vMaxGuess)
                    #catch a failed max power search:
                    if not powerSearchResults_charEqn.status == 0:
                        print "power search exit code = " + str(powerSearchResults_charEqn.status)
                        print powerSearchResults_charEqn.message
                        vMax_charEqn = nan
                    else:
                        vMax_charEqn = powerSearchResults_charEqn.x[0]
                    #dude
                    try:
                        Voc_nn_charEqn=optimize.brentq(cellModel, VV[0], VV[-1])
                    except:
                        Voc_nn_charEqn = nan
                else:
                    Voc_nn_charEqn = nan
                    vMax_charEqn = nan
    
    
                try:
                    Voc_nn = optimize.brentq(iFitSpline, VV[0], VV[-1])
                except:
                    Voc_nn = nan
    
            else:
                Voc_nn = nan
                vMax = nan
                iMax = nan
                pMax = nan
                Voc_nn_charEqn = nan
                vMax_charEqn = nan
                iMax_charEqn = nan
                pMax_charEqn = nan
    
    
    
            if ier < 5:
                dontFindBounds = False
                iMax_charEqn = cellModel([vMax_charEqn])[0]
                pMax_charEqn = vMax_charEqn*iMax_charEqn
                Isc_nn_charEqn = cellModel(0)
                FF_charEqn = pMax_charEqn/(Voc_nn_charEqn*Isc_nn_charEqn)
            else:
                dontFindBounds = True
                iMax_charEqn = nan
                pMax_charEqn = nan
                Isc_nn_charEqn = nan
                FF_charEqn = nan
    
            #there is a maddening bug in SmoothingSpline: it can't evaluate 0 alone, so I have to do this:
            try:
                Isc_nn = iFitSpline([0,1e-55])[0]
            except:
                Isc_nn = nan
    
            FF = pMax/(Voc_nn*Isc_nn)
    
            if (ier != 7) and (ier != 6) and (not dontFindBounds) and (type(fitCovariance) is not float):
                #error estimation:
                alpha = 0.05 # 95% confidence interval = 100*(1-alpha)
    
                nn = len(VV)    # number of data points
                p = len(fitParams) # number of parameters
    
                dof = max(0, nn - p) # number of degrees of freedom
    
                # student-t value for the dof and confidence level
                tval = t.ppf(1.0-alpha/2., dof) 
    
                lowers = []
                uppers = []
                #calculate 95% confidence interval
                for a, p,var in zip(range(nn), fitParams, np.diag(fitCovariance)):
                    sigma = var**0.5
                    lower = p - sigma*tval
                    upper = p + sigma*tval
                    lowers.append(lower)
                    uppers.append(upper)
    
            else:
                uppers = [nan,nan,nan,nan,nan]
                lowers = [nan,nan,nan,nan,nan]
    
            plotPoints = 1000
            fitX = np.linspace(VV[0],VV[-1],plotPoints)
            
            if ier < 5:
                modelY = cellModel(fitX)*outputScaleFactor
            else:
                modelY = np.empty(plotPoints)*nan
            splineY = iFitSpline(fitX)*outputScaleFactor
            graphData = {'vsTime':vsTime,'origRow':self.rows,'fitX':fitX,'modelY':modelY,'splineY':splineY,'i':II*outputScaleFactor,'v':VV,'Voc':Voc_nn,'Isc':Isc_nn*outputScaleFactor,'Vmax':vMax,'Imax':iMax*outputScaleFactor}		
    
            #export button
            exportBtn = QPushButton(self.ui.tableWidget)
            exportBtn.setText('Export')
            exportBtn.clicked.connect(self.handleButton)
            self.ui.tableWidget.setCellWidget(self.rows,self.cols.keys().index('exportBtn'), exportBtn)
              
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('pce')).setData(Qt.DisplayRole,round(pMax/area*1e3,3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('pce')).setToolTip(str(round(pMax_charEqn/area*1e3,3)))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('pmax')).setData(Qt.DisplayRole,round(pMax/area*1e3,3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('pmax')).setToolTip(str(round(pMax_charEqn/area*1e3,3)))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('jsc')).setData(Qt.DisplayRole,round(Isc_nn/area*1e3,3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('jsc')).setToolTip(str(round(Isc_nn_charEqn/area*1e3,3)))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('voc')).setData(Qt.DisplayRole,round(Voc_nn*1e3,3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('voc')).setToolTip(str(round(Voc_nn_charEqn*1e3,3)))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('ff')).setData(Qt.DisplayRole,round(FF,3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('ff')).setToolTip(str(round(FF_charEqn,3)))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('rs')).setData(Qt.DisplayRole,round(Rs_fit*area,3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('rs')).setToolTip('[{0}  {1}]'.format(lowers[2]*area, uppers[2]*area))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('rsh')).setData(Qt.DisplayRole,round(Rsh_fit*area,3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('rsh')).setToolTip('[{0}  {1}]'.format(lowers[3]*area, uppers[3]*area))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('jph')).setData(Qt.DisplayRole,round(Iph_fit/area*1e3,3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('jph')).setToolTip('[{0}  {1}]'.format(lowers[1]/area*1e3, uppers[1]/area*1e3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('j0')).setData(Qt.DisplayRole,round(I0_fit/area*1e9,3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('j0')).setToolTip('[{0}  {1}]'.format(lowers[0]/area*1e9, uppers[0]/area*1e9))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('n')).setData(Qt.DisplayRole,round(n_fit,3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('n')).setToolTip('[{0}  {1}]'.format(lowers[4], uppers[4]))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('Vmax')).setData(Qt.DisplayRole,round(vMax*1e3,3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('Vmax')).setToolTip(str(round(vMax_charEqn*1e3,3)))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('area')).setData(Qt.DisplayRole,round(area,3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('pmax2')).setData(Qt.DisplayRole,round(pMax*1e3,3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('pmax2')).setToolTip(str(round(pMax_charEqn*1e3,3)))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('isc')).setData(Qt.DisplayRole,round(Isc_nn*1e3,3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('isc')).setToolTip(str(round(Isc_nn_charEqn*1e3,3)))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('iph')).setData(Qt.DisplayRole,round(Iph_fit*1e3,3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('iph')).setToolTip('[{0}  {1}]'.format(lowers[1]*1e3, uppers[1]*1e3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('i0')).setData(Qt.DisplayRole,round(I0_fit*1e9,3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('i0')).setToolTip('[{0}  {1}]'.format(lowers[0]*1e9, uppers[0]*1e9))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('rs2')).setData(Qt.DisplayRole,round(Rs_fit,3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('rs2')).setToolTip('[{0}  {1}]'.format(lowers[2], uppers[2]))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('rsh2')).setData(Qt.DisplayRole,round(Rsh_fit,3))
            self.ui.tableWidget.item(self.rows,self.cols.keys().index('rsh2')).setToolTip('[{0}  {1}]'.format(lowers[3], uppers[3]))
        
        else:#vs time
            graphData = {'vsTime':vsTime,'origRow':self.rows,'time':time,'i':II*outputScaleFactor,'v':VV}

        #file name
        self.ui.tableWidget.item(self.rows,self.cols.keys().index('file')).setText(fileName)
        self.ui.tableWidget.item(self.rows,self.cols.keys().index('file')).setToolTip(''.join(header))          
        
        #plot button
        plotBtn = QPushButton(self.ui.tableWidget)
        plotBtn.setText('Plot')
        plotBtn.clicked.connect(self.handleButton)
        self.ui.tableWidget.setCellWidget(self.rows,self.cols.keys().index('plotBtn'), plotBtn)
        self.ui.tableWidget.item(self.rows,self.cols.keys().index('plotBtn')).setData(Qt.UserRole,graphData)
        
        self.ui.tableWidget.resizeColumnsToContents()
        self.rows = self.rows + 1


    def bestEffortFit(self,VV,II):

        #splineTestVV=np.linspace(VV[0],VV[-1],1000)
        #splineTestII=iFitSpline(splineTestVV)
        #p1, = plt.plot(splineTestVV,splineTestII)
        #p3, = plt.plot(VV,II,ls='None',marker='o', label='Data')
        #plt.draw()
        #plt.show()            

        #data point selection:
        #lowest voltage (might be same as Isc)
        V_start_n = VV[0]
        I_start_n = II[0]

        #highest voltage
        V_end_n = VV[-1]
        I_end_n = II[-1]

        #Isc
        iFit = interpolate.interp1d(VV,II)
        V_sc_n = 0
        try:
            I_sc_n = float(iFit(V_sc_n))
        except:
            return([[nan,nan,nan,nan,nan], [nan,nan,nan,nan,nan], nan, "hard fail", 10])

        #mpp
        VVcalc = VV-VV[0]
        IIcalc = II-min(II)
        Pvirtual= np.array(VVcalc*IIcalc)
        vMaxIndex = Pvirtual.argmax()
        V_vmpp_n = VV[vMaxIndex]
        I_vmpp_n = II[vMaxIndex]

        #Vp: half way in voltage between vMpp and the start of the dataset:
        V_vp_n = (V_vmpp_n-V_start_n)/2 +V_start_n
        try:
            I_vp_n = float(iFit(V_vp_n))
        except:
            return([[nan,nan,nan,nan,nan], [nan,nan,nan,nan,nan], nan, "hard fail", 10])

        #Ip: half way in current between vMpp and the end of the dataset:
        I_ip_n = (I_vmpp_n-I_end_n)/2 + I_end_n
        iFit2 = interpolate.interp1d(VV,II-I_ip_n)
        try:
            V_ip_n = optimize.brentq(iFit2, VV[0], VV[-1])
        except:
            return([[nan,nan,nan,nan,nan], [nan,nan,nan,nan,nan], nan, "hard fail", 10])

        diaplayAllGuesses = False
        def evaluateGuessPlot(dataX, dataY, myguess):
            myguess = [float(x) for x in myguess]
            print "myguess:"
            print myguess
            vv=np.linspace(min(dataX),max(dataX),1000)
            ii=vectorizedCurrent(vv,myguess[0],myguess[1],myguess[2],myguess[3],myguess[4])
            plt.title('Guess and raw data')
            plt.plot(vv,ii)
            plt.scatter(dataX,dataY)
            plt.grid(b=True)
            plt.draw()
            plt.show()

        #phase 1 guesses:
        I_L_initial_guess = I_sc_n
        R_sh_initial_guess = 1e6

        #compute intellegent guesses for Iph, Rsh by forcing the curve through several data points and numerically solving the resulting system of eqns
        newRhs = rhs - I
        aLine = Rsh*V+Iph-I
        eqnSys1 = aLine.subs([(V,V_start_n),(I,I_start_n)])
        eqnSys2 = aLine.subs([(V,V_vp_n),(I,I_vp_n)])

        eqnSys = (eqnSys1,eqnSys2)

        try:
            nGuessSln = sympy.nsolve(eqnSys,(Iph,Rsh),(I_L_initial_guess,R_sh_initial_guess),maxsteps=10000)
        except:
            return([[nan,nan,nan,nan,nan], [nan,nan,nan,nan,nan], nan, "hard fail", 10])


        I_L_guess = nGuessSln[0]
        R_sh_guess = -1*1/nGuessSln[1]
        R_s_guess = -1*(V_end_n-V_ip_n)/(I_end_n-I_ip_n)
        n_initial_guess = 2 #TODO: maybe a more intelegant guess for n can be found using http://pvcdrom.pveducation.org/CHARACT/IDEALITY.HTM
        I0_initial_guess = eyeNot[0].evalf(subs={Vth:thermalVoltage,Rs:R_s_guess,Rsh:R_sh_guess,Iph:I_L_guess,n:n_initial_guess,I:I_ip_n,V:V_ip_n})                         

        initial_guess = [I0_initial_guess, I_L_guess, R_s_guess, R_sh_guess, n_initial_guess]
        if diaplayAllGuesses:
            evaluateGuessPlot(VV, II, initial_guess)
            
        # let's try the fit now, if it works great, we're done, otherwise we can continue
        #try:
            #guess = initial_guess
            #fitParams, fitCovariance, infodict, errmsg, ier = optimize.curve_fit(optimizeThis, VV, II,p0=guess,full_output = True,xtol=1e-13,ftol=1e-15)
            #return(fitParams, fitCovariance, infodict, errmsg, ier)
        #except:
            #pass        

        #refine guesses for I0 and Rs by forcing the curve through several data points and numerically solving the resulting system of eqns
        eqnSys1 = newRhs.subs([(Vth,thermalVoltage),(Iph,I_L_guess),(V,V_ip_n),(I,I_ip_n),(n,n_initial_guess),(Rsh,R_sh_guess)])
        eqnSys2 = newRhs.subs([(Vth,thermalVoltage),(Iph,I_L_guess),(V,V_end_n),(I,I_end_n),(n,n_initial_guess),(Rsh,R_sh_guess)])
        eqnSys = (eqnSys1,eqnSys2)
        
        try:
            nGuessSln = sympy.nsolve(eqnSys,(I0,Rs),(I0_initial_guess,R_s_guess),maxsteps=10000)
        except:
            return([[nan,nan,nan,nan,nan], [nan,nan,nan,nan,nan], nan, "hard fail", 10])

        I0_guess = nGuessSln[0]
        R_s_guess = nGuessSln[1]
        
        #Rs_initial_guess = RsEqn[0].evalf(subs={I0:I0_initial_guess,Vth:thermalVoltage,Rsh:R_sh_guess,Iph:I_L_guess,n:n_initial_guess,I:I_end_n,V:V_end_n})
        #I0_guess = I0_initial_guess
        #R_s_guess = Rs_initial_guess
        
        guess = [I0_guess, I_L_guess, R_s_guess, R_sh_guess, n_initial_guess]
        if diaplayAllGuesses:
            evaluateGuessPlot(VV, II, guess)
            
        #nidf

        #give 5x weight to data around mpp
        #nP = II*VV
        #maxIndex = np.argmax(nP)
        #weights = np.ones(len(II))
        #halfRange = (V_ip_n-VV[vMaxIndex])/2
        #upperTarget = VV[vMaxIndex] + halfRange
        #lowerTarget = VV[vMaxIndex] - halfRange
        #lowerTarget = 0
        #upperTarget = V_oc_n
        #lowerI = np.argmin(abs(VV-lowerTarget))
        #upperI = np.argmin(abs(VV-upperTarget))
        #weights[range(lowerI,upperI)] = 3
        #weights[maxnpi] = 10
        #todo: play with setting up "key points"

        guess = [float(x) for x in guess]

        #odrMod = odr.Model(odrThing)
        #myData = odr.Data(VV,II)
        #myodr = odr.ODR(myData, odrMod, beta0=guess,maxit=5000,sstol=1e-20,partol=1e-20)#
        #myoutput = myodr.run()
        #myoutput.pprint()
        #see http://docs.scipy.org/doc/external/odrpack_guide.pdf


        try:
            #myoutput = myodr.run()
            #fitParams = myoutput.beta
            #print myoutput.stopreason
            #print myoutput.info
            #ier = 1
            fitParams, fitCovariance, infodict, errmsg, ier = optimize.curve_fit(optimizeThis, VV, II,p0=guess,full_output = True,xtol=1e-13,ftol=1e-15)
            #fitParams, fitCovariance, infodict, errmsg, ier = optimize.leastsq(func=residual, args=(VV, II, np.ones(len(II))),x0=guess,full_output=1,xtol=1e-12,ftol=1e-14)#,xtol=1e-12,ftol=1e-14,maxfev=12000
            #fitParams, fitCovariance, infodict, errmsg, ier = optimize.leastsq(func=residual, args=(VV, II, weights),x0=fitParams,full_output=1,ftol=1e-15,xtol=0)#,xtol=1e-12,ftol=1e-14            
        
            alwaysShowRecap = False
            if  alwaysShowRecap:
                vv=np.linspace(VV[0],VV[-1],1000)
                print "fit:"
                print fitParams                
                print "guess:"
                print guess
                print ier
                print errmsg
                ii=vectorizedCurrent(vv,guess[0],guess[1],guess[2],guess[3],guess[4])
                ii2=vectorizedCurrent(vv,fitParams[0],fitParams[1],fitParams[2],fitParams[3],fitParams[4])
                plt.title('Fit analysis')
                p1, = plt.plot(vv,ii, label='Guess',ls='--')
                p2, = plt.plot(vv,ii2, label='Fit')
                p3, = plt.plot(VV,II,ls='None',marker='o', label='Data')
                #p4, = plt.plot(VV[range(lowerI,upperI)],II[range(lowerI,upperI)],ls="None",marker='o', label='5x Weight Data')
                ax = plt.gca()
                handles, labels = ax.get_legend_handles_labels()
                ax.legend(handles, labels, loc=3)
                plt.grid(b=True)
                plt.draw()
                plt.show()
            return(fitParams, fitCovariance, infodict, errmsg, ier)
        except:
            return([[nan,nan,nan,nan,nan], [nan,nan,nan,nan,nan], nan, "hard fail", 10])



    def openCall(self):
        #remember the last path th user opened
        if self.settings.contains('lastFolder'):
            openDir = self.settings.value('lastFolder').toString()
        else:
            openDir = '.'

        fileNames = QFileDialog.getOpenFileNamesAndFilter(directory = openDir, caption="Select one or more files to open", filter = '(*.txt *.csv);;Folders (*)')       
        #fileNames = QFileDialog.getExistingDirectory(directory = openDir, caption="Select one or more files to open")       
        
        if len(fileNames[0])>0:#check if user clicked cancel
            self.workingDirectory = os.path.dirname(str(fileNames[0][0]))
            self.settings.setValue('lastFolder',self.workingDirectory)
            for fullPath in fileNames[0]:
                fullPath = str(fullPath)
                self.processFile(fullPath)
        
            if self.ui.actionEnable_Watching.isChecked():
                watchedDirs = self.watcher.directories()
                self.watcher.removePaths(watchedDirs)
                self.watcher.addPath(self.workingDirectory)
                self.handleWatchUpdate(self.workingDirectory)
    
    #user chose file --> watch
    def handleWatchAction(self):
        #remember the last path th user opened
        if self.settings.contains('lastFolder'):
            openDir = self.settings.value('lastFolder').toString()
        else:
            openDir = '.'
        
        myDir = QFileDialog.getExistingDirectory(directory = openDir, caption="Select folder to watch")
        
        if len(myDir)>0:#check if user clicked cancel
            self.workingDirectory = str(myDir)
            self.settings.setValue('lastFolder',self.workingDirectory)
            self.ui.actionEnable_Watching.setChecked(True)
            watchedDirs = self.watcher.directories()
            self.watcher.removePaths(watchedDirs)
            self.watcher.addPath(self.workingDirectory)
            self.handleWatchUpdate(self.workingDirectory)
    
    #user toggeled Tools --> Enable Watching
    def watchCall(self):
        watchedDirs = self.watcher.directories()
        self.watcher.removePaths(watchedDirs)
        if self.ui.actionEnable_Watching.isChecked():
            if (self.workingDirectory != ''):
                self.watcher.addPath(self.workingDirectory)
                self.handleWatchUpdate(self.workingDirectory)
            
    def handleWatchUpdate(self,path):
        myDir = QDir(path)
        myDir.setNameFilters(self.supportedExtensions)
        allFilesNow = myDir.entryList()
        allFilesNow = list(allFilesNow)
        allFilesNow = [str(item) for item in allFilesNow]

        differentFiles = list(set(allFilesNow) ^ set(self.fileNames))
        if differentFiles != []:
            for aFile in differentFiles:
                if self.fileNames.__contains__(aFile):
                    #TODO: delete the file from the table
                    self.ui.statusbar.showMessage('Removed' + aFile,2500)
                else:
                    #process the new file
                    self.processFile(os.path.join(self.workingDirectory,aFile))
예제 #20
0
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        """ init UI """
        QMainWindow.__init__(self, parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.ui.actionSaveSession.setEnabled(False)

        self.distributedObjects = DistributedObjects()

        self.act = self.distributedObjects.actions
        self.debugController = self.distributedObjects.debugController
        self.settings = self.debugController.settings
        self.signalproxy = self.distributedObjects.signalProxy
        self.pluginloader = PluginLoader(self.distributedObjects)
        self.editorController = self.distributedObjects.editorController

        self.act = self.distributedObjects.actions
        # init RecentFileHandler
        self.recentFileHandler = RecentFileHandler(self, self.ui.menuRecentlyUsedFiles, self.distributedObjects)
        self.debugController.executableOpened.connect(self.recentFileHandler.addToRecentFiles)
        self.debugController.executableOpened.connect(self.__observeWorkingBinary)
        self.debugController.executableOpened.connect(self.showExecutableName)
        self.debugController.executableOpened.connect(self.disableButtons)
        # signal proxy
        self.signalproxy.inferiorIsRunning.connect(self.targetStartedRunning, Qt.QueuedConnection)
        self.signalproxy.inferiorStoppedNormally.connect(self.targetStopped, Qt.QueuedConnection)
        self.signalproxy.inferiorReceivedSignal.connect(self.targetStopped, Qt.QueuedConnection)
        self.signalproxy.inferiorHasExited.connect(self.targetExited, Qt.QueuedConnection)

        self.signalproxy.addDockWidget.connect(self.addPluginDockWidget)
        self.signalproxy.removeDockWidget.connect(self.removeDockWidget)

        # Plugin Loader
        self.pluginloader.insertPluginAction.connect(self.addPluginAction)

        self.ui.actionSavePlugins.triggered.connect(self.showSavePluginsDialog)
        self.ui.actionLoadPlugins.triggered.connect(self.showLoadPluginsDialog)

        # Add editor to main window.
        self.ui.gridLayout.addWidget(self.distributedObjects.editorController.editor_view, 0, 0, 1, 1)

        self.pluginloader.addAvailablePlugins()

        # Tell everyone to insert their dock widgets into the main window
        self.signalproxy.emitInsertDockWidgets()

        # get filelist dockwidget
        self.filelist_dockwidget = self.findChild(QDockWidget, "FileListView")

        self.setWindowFilePath("<none>")
        self.setupUi()
        self.createInitialWindowPlacement()
        self.readSettings()

        self.quickwatch = QuickWatch(self, self.distributedObjects)

        self.binaryName = None
        self.fileWatcher = QFileSystemWatcher()
        self.fileWatcher.fileChanged.connect(self.__binaryChanged)

    def setupUi(self):
        self.__initActions()
        self.ui.statusLabel = QLabel()
        self.ui.statusLabel.setText("Not running")
        self.ui.statusbar.addPermanentWidget(self.ui.statusLabel)
        self.ui.statusIcon = QLabel()
        self.ui.statusIcon.setPixmap(QPixmap(":/icons/images/inferior_not_running.png"))
        self.ui.statusbar.addPermanentWidget(self.ui.statusIcon)

    def __initActions(self):
        self.disableButtons()
        self.act.Record.setCheckable(True)
        self.act.ReverseNext.setEnabled(False)
        self.act.ReverseStep.setEnabled(False)
        self.act.SaveFile.setEnabled(False)
        # debug actions
        self.ui.menuDebug.addAction(self.act.Run)
        self.ui.menuDebug.addAction(self.act.Continue)
        self.ui.menuDebug.addAction(self.act.Interrupt)
        self.ui.menuDebug.addAction(self.act.Next)
        self.ui.menuDebug.addAction(self.act.Step)
        self.ui.menuDebug.addAction(self.act.Finish)
        self.ui.menuDebug.addAction(self.act.RunToCursor)
        self.ui.menuDebug.addAction(self.act.Record)
        self.ui.menuDebug.addAction(self.act.ReverseNext)
        self.ui.menuDebug.addAction(self.act.ReverseStep)

        # file actions
        self.ui.menuFile.insertAction(self.ui.actionSaveSession, self.act.OpenMenu)
        self.ui.menuFile.addAction(self.act.SaveFile)
        self.ui.menuFile.addAction(self.act.Exit)

        # add them to menubar and also menuView to respect order
        self.ui.menubar.addAction(self.ui.menuFile.menuAction())
        self.ui.menubar.addAction(self.ui.menuView.menuAction())
        self.ui.menubar.addAction(self.ui.menuDebug.menuAction())
        self.ui.menubar.addAction(self.ui.menuHelp.menuAction())
        # now make toolbar actions
        self.act.Open.setMenu(self.ui.menuRecentlyUsedFiles)
        self.ui.Main.addAction(self.act.Open)
        self.ui.Main.addAction(self.act.SaveFile)
        self.ui.Main.addSeparator()
        self.ui.Main.addAction(self.act.Run)
        self.ui.Main.addAction(self.act.Continue)
        self.ui.Main.addAction(self.act.Interrupt)
        self.ui.Main.addAction(self.act.Next)
        self.ui.Main.addAction(self.act.Step)
        self.ui.Main.addAction(self.act.Record)
        self.ui.Main.addAction(self.act.ReverseNext)
        self.ui.Main.addAction(self.act.ReverseStep)
        self.ui.Main.addAction(self.act.Finish)
        self.ui.Main.addAction(self.act.RunToCursor)

        self.ui.Main.addSeparator()
        self.ui.Main.addAction(self.act.Exit)
        # connect actions
        self.__connectActions()

    def __connectActions(self):
        # file menu
        self.act.Open.triggered.connect(self.showOpenExecutableDialog)
        self.act.OpenMenu.triggered.connect(self.showOpenExecutableDialog)
        self.act.Exit.triggered.connect(self.close)
        self.act.SaveFile.triggered.connect(self.signalproxy.emitSaveCurrentFile)
        # debug menu

        self.act.Run.triggered.connect(self.debugController.run)

        self.act.Next.triggered.connect(self.debugController.next_)
        self.act.Step.triggered.connect(self.debugController.step)
        self.act.Continue.triggered.connect(self.debugController.cont)
        self.act.Record.triggered.connect(self.toggleRecord)
        self.act.ReverseStep.triggered.connect(self.debugController.reverse_step)
        self.act.ReverseNext.triggered.connect(self.debugController.reverse_next)

        self.act.Interrupt.triggered.connect(self.debugController.interrupt)
        self.act.Finish.triggered.connect(self.debugController.finish)
        self.act.RunToCursor.triggered.connect(self.debugController.inferiorUntil)

        self.ui.actionRestoreSession.triggered.connect(self.distributedObjects.sessionManager.showRestoreSessionDialog)
        self.ui.actionSaveSession.triggered.connect(self.distributedObjects.sessionManager.showSaveSessionDialog)

    def addPluginDockWidget(self, area, widget, addToggleViewAction):
        self.addDockWidget(area, widget)
        if addToggleViewAction:
            self.ui.menuShow_View.addAction(widget.toggleViewAction())

    def addPluginAction(self, Action):
        """ show plugin as menu entry """
        self.ui.menuPlugins.addAction(Action)

    def createInitialWindowPlacement(self):
        """
        Saves the window and widget placement after first start of program.
        """
        # check if settings do not exist
        initExists = self.settings.contains("InitialWindowPlacement/geometry")
        if not initExists:
            self.breakpointWidget = self.findChild(QDockWidget, "BreakpointView")
            self.fileListWidget = self.findChild(QDockWidget, "FileListView")
            self.dataGraphWidget = self.findChild(QDockWidget, "DataGraphView")
            self.watchWidget = self.findChild(QDockWidget, "WatchView")
            self.localsWidget = self.findChild(QDockWidget, "LocalsView")
            self.stackWidget = self.findChild(QDockWidget, "StackView")
            self.tracepointWidget = self.findChild(QDockWidget, "TracepointView")
            self.gdbIoWidget = self.findChild(QDockWidget, "GdbIoView")
            self.pyIoWidget = self.findChild(QDockWidget, "PyIoView")
            self.inferiorIoWidget = self.findChild(QDockWidget, "InferiorIoView")

            # tabify widgets to initial state and save settings
            self.tabifyDockWidget(self.fileListWidget, self.dataGraphWidget)
            self.tabifyDockWidget(self.watchWidget, self.localsWidget)
            self.tabifyDockWidget(self.localsWidget, self.stackWidget)
            self.tabifyDockWidget(self.stackWidget, self.breakpointWidget)
            self.tabifyDockWidget(self.breakpointWidget, self.tracepointWidget)
            self.tabifyDockWidget(self.gdbIoWidget, self.pyIoWidget)
            self.tabifyDockWidget(self.pyIoWidget, self.inferiorIoWidget)

            self.settings.setValue("InitialWindowPlacement/geometry", self.saveGeometry())
            self.settings.setValue("InitialWindowPlacement/windowState", self.saveState())

    def restoreInitialWindowPlacement(self):
        """
        Restores the window placement created by
        createInitialWindowPlacement().
        """
        self.restoreGeometry(self.settings.value("InitialWindowPlacement/geometry").toByteArray())
        self.restoreState(self.settings.value("InitialWindowPlacement/windowState").toByteArray())

    def showOpenExecutableDialog(self):
        filename = str(QFileDialog.getOpenFileName(self, "Open Executable", self.recentFileHandler.getDirOfLastFile()))
        if filename != "":
            self.debugController.openExecutable(filename)

    def showLoadPluginsDialog(self):
        dialog = QFileDialog()
        dialog.setNameFilter("*.xml")
        filename = str(dialog.getOpenFileName(self, "Load plugin configuration"))
        if filename != "":
            self.pluginloader.getActivePlugins(filename)

    def showSavePluginsDialog(self):
        dialog = QFileDialog()
        dialog.setNameFilter("*.xml")
        filename = str(dialog.getSaveFileName(self, "Save plugin configuration"))
        if filename != "":
            self.pluginloader.savePluginInfo(filename)

    def showExecutableName(self, filename):
        self.ui.actionSaveSession.setEnabled(True)  # enable saving session
        self.setWindowFilePath(filename)

    def targetStartedRunning(self):
        self.ui.statusLabel.setText("Running")
        self.ui.statusIcon.setPixmap(QPixmap(":/icons/images/inferior_running.png"))
        self.enableButtons()

    def targetStopped(self, rec):
        self.ui.statusLabel.setText("Stopped")
        self.ui.statusIcon.setPixmap(QPixmap(":/icons/images/inferior_stopped.png"))

    def targetExited(self):
        self.ui.statusLabel.setText("Not running")
        self.disableButtons()
        self.ui.statusIcon.setPixmap(QPixmap(":/icons/images/inferior_not_running.png"))

    def closeEvent(self, event):
        if not self.distributedObjects.editorController.closeOpenedFiles():
            event.ignore()  # closing source files may be canceled by user
        else:
            self.settings.setValue("geometry", self.saveGeometry())
            self.settings.setValue("windowState", self.saveState())
            QMainWindow.closeEvent(self, event)
            self.pluginloader.savePluginInfo()

    def readSettings(self):
        self.restoreGeometry(self.settings.value("geometry").toByteArray())
        self.restoreState(self.settings.value("windowState").toByteArray())

    def toggleRecord(self, check):
        if check:
            self.debugController.record_start()
            self.act.ReverseNext.setEnabled(True)
            self.act.ReverseStep.setEnabled(True)
        else:
            self.debugController.record_stop()
            self.act.ReverseNext.setEnabled(False)
            self.act.ReverseStep.setEnabled(False)

    def enableButtons(self):
        self.act.Continue.setEnabled(True)
        self.act.Interrupt.setEnabled(True)
        self.act.Next.setEnabled(True)
        self.act.Step.setEnabled(True)
        self.act.Finish.setEnabled(True)
        self.act.RunToCursor.setEnabled(True)
        self.act.Record.setEnabled(True)

    def disableButtons(self):
        self.act.Continue.setEnabled(False)
        self.act.Interrupt.setEnabled(False)
        self.act.Next.setEnabled(False)
        self.act.Step.setEnabled(False)
        self.act.Finish.setEnabled(False)
        self.act.RunToCursor.setEnabled(False)
        self.act.Record.setChecked(False)
        self.act.Record.setEnabled(False)

    def __observeWorkingBinary(self, filename):
        """ Private Method to Observe Debugged Binary """
        if self.binaryName != None:
            self.fileWatcher.removePath(self.binaryName)
        self.fileWatcher.addPath(filename)
        self.binaryName = filename

    def __binaryChanged(self):
        """ Slot for FileWatcher - Using QtMessagebox for interaction"""
        box = QtGui.QMessageBox()
        if (
            box.question(self, "Binary Changed!", "Reload File?", QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
            == QtGui.QMessageBox.Yes
        ):
            self.debugController.openExecutable(self.binaryName)
        else:
            self.fileWatcher.removePath(self.binaryName)
            self.fileWatcher.addPath(self.binaryName)
예제 #21
0
class NFile(QObject):
    """
    SIGNALS:
    @neverSavedFileClosing(QString)
    @fileClosing(QString)
    @fileChanged()
    @willDelete(PyQt_PyObject, PyQt_PyObject)
    @willOverWrite(PyQt_PyObject, QString, QString)
    @willMove(Qt_PyQtObject, QString, QString)
    @willSave(QString, QString)
    @savedAsNewFile(PyQt_PyObject, QString, QString)
    @gotAPath(PyQt_PyObject)
    @willAttachToExistingFile(PyQt_PyObject, QString)
    """

    def __init__(self, path=None):
        """
        """
        self._file_path = path
        self.__created = False
        self.__watcher = None
        self.__mtime = None
        super(NFile, self).__init__()
        if not self._exists():
            self.__created = True

    @property
    def file_name(self):
        """"Returns filename of nfile"""
        file_name = None
        if self._file_path is None:
            file_name = translations.TR_NEW_DOCUMENT
        else:
            file_name = get_basename(self._file_path)
        return file_name

    @property
    def display_name(self):
        """Returns a pretty name to be displayed by tabs"""
        display_name = self.file_name
        if not self._file_path is None and not self.has_write_permission():
            display_name += translations.TR_READ_ONLY
        return display_name

    @property
    def is_new_file(self):
        return self.__created

    def file_ext(self):
        """"Returns extension of nfile"""
        if self._file_path is None:
            return ''
        return get_file_extension(self._file_path)

    @property
    def file_path(self):
        """"Returns file path of nfile"""
        return self._file_path

    def start_watching(self):
        self.__watcher = QFileSystemWatcher(self)
        self.connect(self.__watcher, SIGNAL("fileChanged(const QString&)"),
            self._file_changed)
        if self._file_path is not None:
            self.__mtime = os.path.getmtime(self._file_path)
            self.__watcher.addPath(self._file_path)

    def _file_changed(self, path):
        current_mtime = os.path.getmtime(self._file_path)
        if current_mtime != self.__mtime:
            self.__mtime = current_mtime
            self.emit(SIGNAL("fileChanged()"))

    def has_write_permission(self):
        if not self._exists():
            return True
        return os.access(self._file_path, os.W_OK)

    def _exists(self):
        """
        Check if we have been created with a path and if such path exists
        In case there is no path, we are most likely a new file.
        """
        file_exists = False
        if self._file_path and os.path.exists(self._file_path):
            file_exists = True
        return file_exists

    def attach_to_path(self, new_path):
        if os.path.exists(new_path):
            signal_handler = SignalFlowControl()
            self.emit(
                    SIGNAL("willAttachToExistingFile(PyQt_PyObject, QString)"),
                    signal_handler, new_path)
            if signal_handler.stopped():
                    return
        self._file_path = new_path
        self.emit(SIGNAL("gotAPath(PyQt_PyObject)"), self)
        return self._file_path

    def create(self):
        if self.__created:
            self.save("")
        self.__created = False

    def save(self, content, path=None):
        """
        Write a temprorary file with .tnj extension and copy it over the
        original one.
        .nsf = Ninja Swap File
        #FIXME: Where to locate addExtension, does not fit here
        """
        new_path = False
        if path:
            self.attach_to_path(path)
            new_path = True

        save_path = self._file_path

        if not save_path:
            raise NinjaNoFileNameException("I am asked to write a "
                                "file but no one told me where")
        swap_save_path = u"%s.nsp" % save_path

        self.__watcher.removePath(save_path)
        flags = QIODevice.WriteOnly | QIODevice.Truncate
        f = QFile(swap_save_path)
        if settings.use_platform_specific_eol():
            flags |= QIODevice.Text

        if not f.open(flags):
            raise NinjaIOException(f.errorString())

        stream = QTextStream(f)
        encoding = get_file_encoding(content)
        if encoding:
            stream.setCodec(encoding)

        encoded_stream = stream.codec().fromUnicode(content)
        f.write(encoded_stream)
        f.flush()
        f.close()
        #SIGNAL: Will save (temp, definitive) to warn folder to do something
        self.emit(SIGNAL("willSave(QString, QString)"), swap_save_path,
                                                        save_path)
        self.__mtime = os.path.getmtime(swap_save_path)
        shutil.move(swap_save_path, save_path)
        self.__watcher.addPath(save_path)
        self.reset_state()
        if self.__watcher and new_path:
            self.__watcher.removePath(self.__watcher.files()[0])
            self.__watcher.addPath(self._file_path)
        return self

    def reset_state(self):
        """
        #FIXE: to have a ref to changed I need to have the doc here
        """
        self.__created = False

    def read(self, path=None):
        """
        Read the file or fail
        """
        open_path = path and path or self._file_path
        self._file_path = open_path
        if not self._file_path:
            raise NinjaNoFileNameException("I am asked to read a "
                                    "file but no one told me from where")
        try:
            with open(open_path, 'rU') as f:
                content = f.read()
        except IOError as reason:
            raise NinjaIOException(reason)
        return content

    def move(self, new_path):
        """
        Phisically move the file
        """
        if self._exists():
            signal_handler = SignalFlowControl()
            #SIGNALL: WILL MOVE TO, to warn folder to exist
            self.emit(SIGNAL("willMove(Qt_PyQtObject, QString, QString)"),
                                                            signal_handler,
                                                            self._file_path,
                                                            new_path)
            if signal_handler.stopped():
                return
            if os.path.exists(new_path):
                signal_handler = SignalFlowControl()
                self.emit(
                    SIGNAL("willOverWrite(PyQt_PyObject, QString, QString)"),
                                    signal_handler, self._file_path, new_path)
                if signal_handler.stopped():
                    return
            if self.__watcher:
                self.__watcher.removePath(self._file_path)
            shutil.move(self._file_path, new_path)
            if self.__watcher:
                self.__watcher.addPath(new_path)
        self._file_path = new_path
        return

    def copy(self, new_path):
        """
        Copy the file to a new path
        """
        if self._exists():
            signal_handler = SignalFlowControl()
            #SIGNALL: WILL COPY TO, to warn folder to exist
            self.emit(SIGNAL("willCopyTo(Qt_PyQtObject, QString, QString)"),
                                                            signal_handler,
                                                            self._file_path,
                                                            new_path)
            if signal_handler.stopped():
                return
            if os.path.exists(new_path):
                signal_handler = SignalFlowControl()
                self.emit(
                    SIGNAL("willOverWrite(PyQt_PyObject, QString, QString)"),
                                    signal_handler, self._file_path, new_path)
                if signal_handler.stopped():
                    return

            shutil.copy(self._file_path, new_path)

    def delete(self, force=False):
        """
        This deletes the object and closes the file.
        """
        #if created but exists this file migth to someone else
        self.close()
        if ((not self.__created) or force) and self._exists():
            DEBUG("Deleting our own NFile %s" % self._file_path)
            signal_handler = SignalFlowControl()
            self.emit(SIGNAL("willDelete(PyQt_PyObject, PyQt_PyObject)"),
                                                        signal_handler, self)
            if not signal_handler.stopped():
                if self.__watcher:
                    self.__watcher.removePath(self._file_path)
                os.remove(self._file_path)

    def close(self, force_close=False):
        """
        Lets let people know we are going down so they can act upon
        As you can see close does nothing but let everyone know that we are
        not saved yet
        """
        DEBUG("About to close NFile")
        if self.__created and not force_close:
            self.emit(SIGNAL("neverSavedFileClosing(QString)"),
                        self._file_path)
        else:
            self.emit(SIGNAL("fileClosing(QString)"), self._file_path)
            if self.__watcher:
                self.__watcher.removePath(self._file_path)
예제 #22
0
class Infoboard(object):
    def __init__(self, schedule_dir):
        self.schedule_dir = schedule_dir
        self.videos = {}
        self.playlist = []

        self.app = QApplication(sys.argv)

        self.view = QDeclarativeView()
        self.view.setSource(QUrl('scene.qml'))
        self.view.setResizeMode(QDeclarativeView.SizeRootObjectToView)

        self.viewRoot = self.view.rootObject()
        self.viewRoot.quit.connect(self.app.quit)
        self.viewRoot.finished.connect(self.show_next)

        self.view.setGeometry(100, 100, 400, 240)
        self.view.showFullScreen()

        self.watcher = QFileSystemWatcher()

    def schedule_dir_changed(self, filename):
        self.process_all_schedules()
        self.playlist = []

    def run(self):
        self.watcher.addPath(self.schedule_dir)
        self.watcher.directoryChanged.connect(self.schedule_dir_changed)
        self.process_all_schedules()
        self.show_next()
        self.app.exec_()

    def process_schedule(self, key, fobj):
        self.videos[key] = []
        for line in fobj:
            line = line.strip()
            if (not line) or line.startswith('#'): continue
            try:
                self.videos[key].append(Video(line))
            except:
                pass

    def process_all_schedules(self):
        for filename in glob.glob(self.schedule_dir + '/*.txt'):
            filename = os.path.abspath(filename)
            with open(filename, 'rb') as f:
                self.process_schedule(filename, f)

    def show_next(self):
        item = self.playlist_next()
        if not item:
          return

        if item.type == 'image':
            self.viewRoot.showImage(item.filename)
            QTimer.singleShot(item.duration * 1000, self.show_next)
        elif item.type == 'video':
            self.viewRoot.showVideo(item.filename)

    def playlist_next(self):
        if len(self.playlist) == 0:
            self.process_all_schedules()
            today = date.today()
            all_videos = []
            for key in sorted(self.videos.keys()):
                all_videos += self.videos[key]
            self.playlist = [video for video in all_videos
                    if video.start_date <= today and video.end_date >= today]
        if len(self.playlist) == 0:
            return None
        return self.playlist.pop(0)
예제 #23
0
class ProjectWidget(Ui_Form, QWidget):
    SampleWidgetRole = Qt.UserRole + 1
    projectsaved = pyqtSignal()
    projectupdated = pyqtSignal()
    projectloaded = pyqtSignal(object)
    selectlayersupdated = pyqtSignal(list)
    projectlocationchanged = pyqtSignal(str)

    def __init__(self, parent=None):
        super(ProjectWidget, self).__init__(parent)
        self.setupUi(self)
        self.project = None
        self.mapisloaded = False
        self.bar = None

        self.canvas.setCanvasColor(Qt.white)
        self.canvas.enableAntiAliasing(True)
        self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor)
        self.canvas.mapRenderer().setLabelingEngine(QgsPalLabeling())

        self.fieldsmodel = QgsFieldModel()
        self.widgetmodel = WidgetsModel()
        self.possiblewidgetsmodel = QStandardItemModel()

        self.formlayersmodel = QgsLayerModel(watchregistry=False)
        self.formlayers = CaptureLayerFilter()
        self.formlayers.setSourceModel(self.formlayersmodel)

        self.selectlayermodel = CaptureLayersModel(watchregistry=False)
        self.selectlayerfilter = LayerTypeFilter()
        self.selectlayerfilter.setSourceModel(self.selectlayermodel)
        self.selectlayermodel.dataChanged.connect(self.selectlayerschanged)

        self.layerCombo.setModel(self.formlayers)
        self.widgetCombo.setModel(self.possiblewidgetsmodel)
        self.selectLayers.setModel(self.selectlayerfilter)
        self.selectLayers_2.setModel(self.selectlayerfilter)
        self.fieldList.setModel(self.fieldsmodel)

        self.widgetlist.setModel(self.widgetmodel)
        self.widgetlist.selectionModel().currentChanged.connect(self.updatecurrentwidget)
        self.widgetmodel.rowsRemoved.connect(self.setwidgetconfigvisiable)
        self.widgetmodel.rowsInserted.connect(self.setwidgetconfigvisiable)
        self.widgetmodel.modelReset.connect(self.setwidgetconfigvisiable)

        self.titleText.textChanged.connect(self.updatetitle)

        QgsProject.instance().readProject.connect(self._readproject)

        self.loadwidgettypes()

        self.addWidgetButton.pressed.connect(self.newwidget)
        self.removeWidgetButton.pressed.connect(self.removewidget)

        self.roamVersionLabel.setText("You are running IntraMaps Roam version {}".format(roam.__version__))

        self.openProjectFolderButton.pressed.connect(self.openprojectfolder)
        self.openinQGISButton.pressed.connect(self.openinqgis)

        self.filewatcher = QFileSystemWatcher()
        self.filewatcher.fileChanged.connect(self.qgisprojectupdated)

        self.formfolderLabel.linkActivated.connect(self.openformfolder)
        self.projectupdatedlabel.linkActivated.connect(self.reloadproject)
        self.projectupdatedlabel.hide()
        self.formtab.currentChanged.connect(self.formtabchanged)

        self.expressionButton.clicked.connect(self.opendefaultexpression)

        self.fieldList.currentIndexChanged.connect(self.updatewidgetname)
        self.fieldwarninglabel.hide()

        for item, data in readonlyvalues:
            self.readonlyCombo.addItem(item, data)

        self.setpage(4)
        self.form = None

        self.projectlocations.currentIndexChanged[str].connect(self.projectlocationchanged.emit)

    def setaboutinfo(self):
        self.versionLabel.setText(roam.__version__)
        self.qgisapiLabel.setText(str(QGis.QGIS_VERSION))

    def checkcapturelayers(self):
        haslayers = self.project.hascapturelayers()
        self.formslayerlabel.setVisible(not haslayers)
        return haslayers

    def opendefaultexpression(self):
        layer = self.currentform.QGISLayer
        dlg = QgsExpressionBuilderDialog(layer, "Create default value expression", self)
        text = self.defaultvalueText.text().strip('[%').strip('%]').strip()
        dlg.setExpressionText(text)
        if dlg.exec_():
            self.defaultvalueText.setText('[% {} %]'.format(dlg.expressionText()))

    def openformfolder(self, url):
        openfolder(url)

    def selectlayerschanged(self, *args):
        self.formlayers.setSelectLayers(self.project.selectlayers)
        self.checkcapturelayers()
        self.selectlayersupdated.emit(self.project.selectlayers)

    def formtabchanged(self, index):
        # preview
        if index == 1:
            self.form.settings['widgets'] = list(self.widgetmodel.widgets())
            self.setformpreview(self.form)

    def setprojectfolders(self, folders):
        for folder in folders:
            self.projectlocations.addItem(folder)

    def setpage(self, page):
        self.stackedWidget.setCurrentIndex(page)

    def reloadproject(self, *args):
        self.setproject(self.project)

    def qgisprojectupdated(self, path):
        self.projectupdatedlabel.show()
        self.projectupdatedlabel.setText("The QGIS project has been updated. <a href='reload'> "
                                         "Click to reload</a>. <b style=\"color:red\">Unsaved data will be lost</b>")

    def openinqgis(self):
        projectfile = self.project.projectfile
        qgislocation = r'C:\OSGeo4W\bin\qgis.bat'
        qgislocation = roam.config.settings.setdefault('configmanager', {}) \
                                        .setdefault('qgislocation', qgislocation)

        try:
            openqgis(projectfile, qgislocation)
        except WindowsError:
            self.bar.pushMessage("Looks like I couldn't find QGIS",
                               "Check qgislocation in settings.config", QgsMessageBar.WARNING)

    def openprojectfolder(self):
        folder = self.project.folder
        openfolder(folder)

    def setwidgetconfigvisiable(self, *args):
        haswidgets = self.widgetmodel.rowCount() > 0
        self.widgetframe.setEnabled(haswidgets)

    def removewidget(self):
        """
        Remove the selected widget from the widgets list
        """
        widget, index = self.currentuserwidget
        if index.isValid():
            self.widgetmodel.removeRow(index.row(), index.parent())

    def newwidget(self):
        """
        Create a new widget.  The default is a list.
        """
        widget = {}
        widget['widget'] = 'List'
        # Grab the first field.
        widget['field'] = self.fieldsmodel.index(0, 0).data(QgsFieldModel.FieldNameRole)
        currentindex = self.widgetlist.currentIndex()
        currentitem = self.widgetmodel.itemFromIndex(currentindex)
        if currentitem and currentitem.iscontainor():
            parent = currentindex
        else:
            parent = currentindex.parent()
        index = self.widgetmodel.addwidget(widget, parent)
        self.widgetlist.setCurrentIndex(index)

    def loadwidgettypes(self):
        self.widgetCombo.blockSignals(True)
        for widgettype in roam.editorwidgets.core.supportedwidgets():
            try:
                configclass = configmanager.editorwidgets.widgetconfigs[widgettype]
            except KeyError:
                continue

            configwidget = configclass()
            item = QStandardItem(widgettype)
            item.setData(configwidget, Qt.UserRole)
            item.setData(widgettype, Qt.UserRole + 1)
            item.setIcon(QIcon(widgeticon(widgettype)))
            self.widgetCombo.model().appendRow(item)
            self.widgetstack.addWidget(configwidget)
        self.widgetCombo.blockSignals(False)

    def usedfields(self):
        """
        Return the list of fields that have been used by the the current form's widgets
        """
        for widget in self.currentform.widgets:
            yield widget['field']

    @property
    def currentform(self):
        """
        Return the current selected form.
        """
        return self.form

    @property
    def currentuserwidget(self):
        """
        Return the selected user widget.
        """
        index = self.widgetlist.currentIndex()
        return index.data(Qt.UserRole), index

    @property
    def currentwidgetconfig(self):
        """
        Return the selected widget in the widget combo.
        """
        index = self.widgetCombo.currentIndex()
        index = self.possiblewidgetsmodel.index(index, 0)
        return index.data(Qt.UserRole), index, index.data(Qt.UserRole + 1)

    def updatewidgetname(self, index):
        # Only change the edit text on name field if it's not already set to something other then the
        # field name.
        field = self.fieldsmodel.index(index, 0).data(QgsFieldModel.FieldNameRole)
        currenttext = self.nameText.text()
        foundfield = self.fieldsmodel.findfield(currenttext)
        if foundfield:
            self.nameText.setText(field)

    def _save_widgetfield(self, index):
        """
        Save the selected field for the current widget.

        Shows a error if the field is already used but will allow
        the user to still set it in the case of extra logic for that field
        in the forms Python logic.
        """
        widget, index = self.currentuserwidget
        row = self.fieldList.currentIndex()
        field = self.fieldsmodel.index(row, 0).data(QgsFieldModel.FieldNameRole)
        showwarning = field in self.usedfields()
        self.fieldwarninglabel.setVisible(showwarning)
        widget['field'] = field
        self.widgetmodel.setData(index, widget, Qt.UserRole)

    def _save_selectedwidget(self, index):
        configwidget, index, widgettype = self.currentwidgetconfig
        widget, index = self.currentuserwidget
        if not widget:
            return

        widget['widget'] = widgettype
        widget['required'] = self.requiredCheck.isChecked()
        widget['config'] = configwidget.getconfig()
        widget['name'] = self.nameText.text()
        widget['read-only-rules'] = [self.readonlyCombo.itemData(self.readonlyCombo.currentIndex())]
        widget['hidden'] = self.hiddenCheck.isChecked()

        self.widgetmodel.setData(index, widget, Qt.UserRole)

    def _save_default(self):
        widget, index = self.currentuserwidget
        default = self.defaultvalueText.text()
        widget['default'] = default
        self.widgetmodel.setData(index, widget, Qt.UserRole)

    def _save_selectionlayers(self, index, layer, value):
        config = self.project.settings

        self.selectlayermodel.dataChanged.emit(index, index)

    def _save_formtype(self, index):
        formtype = self.formtypeCombo.currentText()
        form = self.currentform
        form.settings['type'] = formtype

    def _save_formname(self, text):
        """
        Save the form label to the settings file.
        """
        try:
            form = self.currentform
            if form is None:
                return
            form.settings['label'] = text
            self.projectupdated.emit()
        except IndexError:
            return

    def _save_layer(self, index):
        """
        Save the selected layer to the settings file.
        """
        index = self.formlayers.index(index, 0)
        layer = index.data(Qt.UserRole)
        if not layer:
            return

        form = self.currentform
        if form is None:
            return

        form.settings['layer'] = layer.name()
        self.updatefields(layer)

    def setsplash(self, splash):
        pixmap = QPixmap(splash)
        w = self.splashlabel.width()
        h = self.splashlabel.height()
        self.splashlabel.setPixmap(pixmap.scaled(w,h, Qt.KeepAspectRatio))

    def setproject(self, project, loadqgis=True):
        """
        Set the widgets active project.
        """
        self.disconnectsignals()
        self.mapisloaded = False
        self.filewatcher.removePaths(self.filewatcher.files())
        self.projectupdatedlabel.hide()
        self._closeqgisproject()

        if project.valid:
            self.startsettings = copy.deepcopy(project.settings)
            self.project = project
            self.projectlabel.setText(project.name)
            self.versionText.setText(project.version)
            self.selectlayermodel.config = project.settings
            self.formlayers.setSelectLayers(self.project.selectlayers)
            self.setsplash(project.splash)
            self.loadqgisproject(project, self.project.projectfile)
            self.filewatcher.addPath(self.project.projectfile)
            self.projectloaded.emit(self.project)

    def loadqgisproject(self, project, projectfile):
        QDir.setCurrent(os.path.dirname(project.projectfile))
        fileinfo = QFileInfo(project.projectfile)
        QgsProject.instance().read(fileinfo)

    def _closeqgisproject(self):
        if self.canvas.isDrawing():
            return

        self.canvas.freeze(True)
        self.formlayersmodel.removeall()
        self.selectlayermodel.removeall()
        QgsMapLayerRegistry.instance().removeAllMapLayers()
        self.canvas.freeze(False)

    def loadmap(self):
        if self.mapisloaded:
            return

        # This is a dirty hack to work around the timer that is in QgsMapCanvas in 2.2.
        # Refresh will stop the canvas timer
        # Repaint will redraw the widget.
        # loadmap is only called once per project load so it's safe to do this here.
        self.canvas.refresh()
        self.canvas.repaint()

        parser = roam.projectparser.ProjectParser.fromFile(self.project.projectfile)
        canvasnode = parser.canvasnode
        self.canvas.mapRenderer().readXML(canvasnode)
        self.canvaslayers = parser.canvaslayers()
        self.canvas.setLayerSet(self.canvaslayers)
        self.canvas.updateScale()
        self.canvas.refresh()

        self.mapisloaded = True

    def _readproject(self, doc):
        self.formlayersmodel.refresh()
        self.selectlayermodel.refresh()
        self._updateforproject(self.project)

    def _updateforproject(self, project):
        self.titleText.setText(project.name)
        self.descriptionText.setPlainText(project.description)

    def swapwidgetconfig(self, index):
        widgetconfig, _, _ = self.currentwidgetconfig
        defaultvalue = widgetconfig.defaultvalue
        self.defaultvalueText.setText(defaultvalue)

        self.updatewidgetconfig({})

    def updatetitle(self, text):
        self.project.settings['title'] = text
        self.projectlabel.setText(text)
        self.projectupdated.emit()

    def updatewidgetconfig(self, config):
        widgetconfig, index, widgettype = self.currentwidgetconfig
        self.setconfigwidget(widgetconfig, config)

    def setformpreview(self, form):
        def removewidget():
            item = self.frame_2.layout().itemAt(0)
            if item and item.widget():
                item.widget().setParent(None)

        removewidget()

        featureform = FeatureForm.from_form(form, form.settings, None, {})

        self.frame_2.layout().addWidget(featureform)

    def connectsignals(self):
        self.formLabelText.textChanged.connect(self._save_formname)
        self.layerCombo.currentIndexChanged.connect(self._save_layer)
        self.formtypeCombo.currentIndexChanged.connect(self._save_formtype)

        #widget settings
        self.fieldList.currentIndexChanged.connect(self._save_widgetfield)
        self.requiredCheck.toggled.connect(self._save_selectedwidget)
        self.defaultvalueText.textChanged.connect(self._save_default)
        self.widgetCombo.currentIndexChanged.connect(self._save_selectedwidget)
        self.widgetCombo.currentIndexChanged.connect(self.swapwidgetconfig)
        self.nameText.textChanged.connect(self._save_selectedwidget)
        self.readonlyCombo.currentIndexChanged.connect(self._save_selectedwidget)
        self.hiddenCheck.toggled.connect(self._save_selectedwidget)

    def disconnectsignals(self):
        try:
            self.formLabelText.textChanged.disconnect(self._save_formname)
            self.layerCombo.currentIndexChanged.disconnect(self._save_layer)
            self.formtypeCombo.currentIndexChanged.disconnect(self._save_formtype)

            #widget settings
            self.fieldList.currentIndexChanged.disconnect(self._save_widgetfield)
            self.requiredCheck.toggled.disconnect(self._save_selectedwidget)
            self.defaultvalueText.textChanged.disconnect(self._save_default)
            self.widgetCombo.currentIndexChanged.disconnect(self._save_selectedwidget)
            self.widgetCombo.currentIndexChanged.disconnect(self.swapwidgetconfig)
            self.nameText.textChanged.disconnect(self._save_selectedwidget)
            self.readonlyCombo.currentIndexChanged.disconnect(self._save_selectedwidget)
            self.hiddenCheck.toggled.disconnect(self._save_selectedwidget)
        except TypeError:
            pass

    def setform(self, form):
        """
        Update the UI with the currently selected form.
        """

        def getfirstlayer():
            index = self.formlayers.index(0,0)
            layer = index.data(Qt.UserRole)
            layer = layer.name()
            return layer

        def loadwidgets(widget):
            """
            Load the widgets into widgets model
            """
            self.widgetmodel.clear()
            self.widgetmodel.loadwidgets(form.widgets)

        def findlayer(layername):
            index = self.formlayersmodel.findlayer(layername)
            index = self.formlayers.mapFromSource(index)
            layer = index.data(Qt.UserRole)
            return index, layer


        self.disconnectsignals()

        self.form = form

        settings = form.settings
        label = form.label
        layername = settings.setdefault('layer', getfirstlayer())
        layerindex, layer = findlayer(layername)
        if not layer or not layerindex.isValid():
            return

        formtype = settings.setdefault('type', 'auto')
        widgets = settings.setdefault('widgets', [])

        self.formLabelText.setText(label)
        folderurl = "<a href='{path}'>{name}</a>".format(path=form.folder, name=os.path.basename(form.folder))
        self.formfolderLabel.setText(folderurl)
        self.layerCombo.setCurrentIndex(layerindex.row())
        self.updatefields(layer)

        index = self.formtypeCombo.findText(formtype)
        if index == -1:
            self.formtypeCombo.insertItem(0, formtype)
            self.formtypeCombo.setCurrentIndex(0)
        else:
            self.formtypeCombo.setCurrentIndex(index)

        loadwidgets(widgets)

        # Set the first widget
        index = self.widgetmodel.index(0, 0)
        if index.isValid():
            self.widgetlist.setCurrentIndex(index)
            self.updatecurrentwidget(index, None)

        self.connectsignals()

    def updatefields(self, layer):
        """
        Update the UI with the fields for the selected layer.
        """
        self.fieldsmodel.setLayer(layer)

    def setconfigwidget(self, configwidget, config):
        """
        Set the active config widget.
        """

        try:
            configwidget.widgetdirty.disconnect(self._save_selectedwidget)
        except TypeError:
            pass

        #self.descriptionLabel.setText(configwidget.description)
        self.widgetstack.setCurrentWidget(configwidget)
        configwidget.setconfig(config)

        configwidget.widgetdirty.connect(self._save_selectedwidget)

    def updatecurrentwidget(self, index, _):
        """
        Update the UI with the config for the current selected widget.
        """
        if not index.isValid():
            return

        widget = index.data(Qt.UserRole)
        widgettype = widget['widget']
        field = widget['field']
        required = widget.setdefault('required', False)
        name = widget.setdefault('name', field)
        default = widget.setdefault('default', '')
        readonly = widget.setdefault('read-only-rules', [])
        hidden = widget.setdefault('hidden', False)

        try:
            data = readonly[0]
        except:
            data = 'never'

        self.readonlyCombo.blockSignals(True)
        index = self.readonlyCombo.findData(data)
        self.readonlyCombo.setCurrentIndex(index)
        self.readonlyCombo.blockSignals(False)

        self.defaultvalueText.blockSignals(True)
        if not isinstance(default, dict):
            self.defaultvalueText.setText(default)
        else:
            # TODO Handle the more advanced default values.
            pass
        self.defaultvalueText.blockSignals(False)

        self.nameText.blockSignals(True)
        self.nameText.setText(name)
        self.nameText.blockSignals(False)

        self.requiredCheck.blockSignals(True)
        self.requiredCheck.setChecked(required)
        self.requiredCheck.blockSignals(False)

        self.hiddenCheck.blockSignals(True)
        self.hiddenCheck.setChecked(hidden)
        self.hiddenCheck.blockSignals(False)

        if not field is None:
            self.fieldList.blockSignals(True)
            index = self.fieldList.findData(field.lower(), QgsFieldModel.FieldNameRole)
            if index > -1:
                self.fieldList.setCurrentIndex(index)
            else:
                self.fieldList.setEditText(field)
            self.fieldList.blockSignals(False)

        index = self.widgetCombo.findText(widgettype)
        self.widgetCombo.blockSignals(True)
        if index > -1:
            self.widgetCombo.setCurrentIndex(index)
        self.widgetCombo.blockSignals(False)

        self.updatewidgetconfig(config=widget.setdefault('config', {}))

    def _saveproject(self):
        """
        Save the project config to disk.
        """
        title = self.titleText.text()
        description = self.descriptionText.toPlainText()
        version = str(self.versionText.text())

        settings = self.project.settings
        settings['title'] = title
        settings['description'] = description
        settings['version'] = version

        form = self.currentform
        if form:
            form.settings['widgets'] = list(self.widgetmodel.widgets())
            logger.debug(form.settings)

        self.project.save()
        self.projectsaved.emit()
예제 #24
0
class WebDesktopEntry(xdg.DesktopEntry.DesktopEntry):
    def __init__(self, appid, *args, **kwargs):
        import webplier

        self.appid = appid
        xdg.DesktopEntry.DesktopEntry.__init__(self, *args, **kwargs)

        self.setWindowWidth = partial(xdg.DesktopEntry.DesktopEntry.set, self, 'X-%s-Window-Width' % APP_NAME)
        self.getWindowWidth = partial(xdg.DesktopEntry.DesktopEntry.get, self, 'X-%s-Window-Width' % APP_NAME)
        self.setWindowHeight = partial(xdg.DesktopEntry.DesktopEntry.set, self, 'X-%s-Window-Height' % APP_NAME)
        self.getWindowHeight = partial(xdg.DesktopEntry.DesktopEntry.get, self, 'X-%s-Window-Height' % APP_NAME)

        self.setBaseUrl = partial(xdg.DesktopEntry.DesktopEntry.set, self, 'X-%s-BaseUrl' % APP_NAME)
        self.getBaseUrl = partial(xdg.DesktopEntry.DesktopEntry.get, self, 'X-%s-BaseUrl' % APP_NAME)

        self.setName = partial(xdg.DesktopEntry.DesktopEntry.set, self, 'Name')

        if not self.hasGroup('Desktop Entry'):
            self.addGroup('Desktop Entry')

        pythonPath = 'python2.7'
        for p in os.environ["PATH"].split(os.pathsep):
            if os.path.exists(os.path.join(p, 'python2.7')):
                pythonPath = os.path.join(p, 'python2.7')
                break

        webplierPath = 'plier'
        for p in os.environ["PATH"].split(os.pathsep):
            if os.path.exists(os.path.join(p, 'plier')):
                webplierPath = os.path.join(p, 'plier')
                break

        if not self.get('Exec'):
            self.set('Exec', '%s %s %s' % (pythonPath, webplierPath, self.appid))

        self.set('X-%s-Type' % APP_NAME, 'Webapp')
        self.set('X-%s-AppId' % APP_NAME, appid)

        self.set('StartupWMClass', appid)

        # TODO: Get this working.

        self.watcher = QFileSystemWatcher()
        self.addPath(getPath('%s-%s.desktop' % (APP_NAME.lower(), self.appid)))
        self.watcher.fileChanged.connect(self._refreshFromFilesystem)

        self.onRefresh = None

    def _refreshFromFilesystem(self):
        path = getPath('%s-%s.desktop' % (APP_NAME.lower(), self.appid))
        if not (os.path.exists(path) and len(open(path).read()) > 1):
            return

        self.parse(path)
        if self.onRefresh:
            self.onRefresh()

        self.watcher.removePath(path)
        self.addPath(path)

    def write(self, *args, **kwargs):
        path = getPath('%s-%s.desktop' % (APP_NAME.lower(), self.appid))
        self.watcher.removePath(path)
        v = xdg.DesktopEntry.DesktopEntry.write(self, *args, **kwargs)
        self.addPath(path)
        return v

    @classmethod
    def fromPath(cls, path):
        de = xdg.DesktopEntry.DesktopEntry(path)
        appid = de.get('X-%s-AppId' % APP_NAME)
        return cls(appid, path)

    def addPath(self, path):
        self.watcher.addPath(path)

    @classmethod
    def getAll(cls):
        """
        Returns all found desktop entries that are webapps we created.
        """
        # TODO: Should we hardcode ".desktop"?
        path = getPath()
        filenames = filter(lambda x: x.endswith('.desktop'), os.listdir(path))
        filenames = map(lambda x: os.path.join(path, x), filenames)
        filenames = filter(cls.isWebDesktopEntry, filenames)

        return map(cls.fromPath, filenames)

    @classmethod
    def isWebDesktopEntry(cls, filename):
        de = xdg.DesktopEntry.DesktopEntry(filename)
        if de.hasKey('X-%s-Type' % APP_NAME) and de.get('X-%s-Type' % APP_NAME) == 'Webapp':
            return True
        else:
            return False
예제 #25
0
class Edytor(QMainWindow, Ui_edytor):
    fpath = ""

    def __init__(self):
        super(Edytor,self).__init__()
        self.setupUi(self)
        # Create watcher
        self.watcher = QFileSystemWatcher(self)

        # Register slots
        self.button_close.clicked.connect(self.close)
        self.button_open.clicked.connect(self.file_open)
        self.button_save.clicked.connect(self.file_save)
        self.editor_window.textChanged.connect(self.text_changed)
        self.watcher.fileChanged.connect(self.file_changed)

    @pyqtSlot()
    def file_open(self, fpath=""):
        if self.ask_discard():
            return

        fpath = fpath or QFileDialog.getOpenFileName(self, "Open file...")
        if isfile(fpath):
            # Switch watcher files
            if self.fpath:
                self.watcher.removePath(self.fpath)
            self.watcher.addPath(fpath)

            with open(fpath) as f:
                text = f.read()
            self.editor_window.setText(text)
            # Disable afterwards since `setText` triggers "textChanged" signal
            self.button_save.setEnabled(False)

            # Finally save the path
            self.fpath = fpath

    @pyqtSlot()
    def file_save(self):
        if isfile(self.fpath):
            # Do not trigger fileChanged when saving ourselves
            self.watcher.removePath(self.fpath)

            text = self.editor_window.toPlainText()
            with open(self.fpath, 'w') as f:
                f.write(text)

            self.button_save.setEnabled(False)
            self.watcher.addPath(self.fpath)

    @pyqtSlot()
    def text_changed(self):
        if self.fpath:
            self.button_save.setEnabled(True)

    @pyqtSlot(str)
    def file_changed(self, path):
        res = QMessageBox.question(
            self, "%s - File has been changed" % self.objectName(),
            "The opened document has been modified by another program.\n"
            + "Do you want to reload the file?",
            QMessageBox.Yes | QMessageBox.No
            | (QMessageBox.Save if self.button_save.isEnabled() else 0),
            QMessageBox.Yes
        )
        if res == QMessageBox.Yes:
            self.file_open(self.fpath)
        elif res == QMessageBox.Save:
            self.file_save()

    def ask_discard(self):
        if not self.button_save.isEnabled():
            return

        res = QMessageBox.question(
            self, "%s - Unsaved changes" % self.objectName(),
            "The document has been modified\n"
            + "Do you want to save your changes?",
            QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel,
            QMessageBox.Save
        )
        print(res)
        if res == QMessageBox.Save:
            self.file_save()

        return res == QMessageBox.Cancel

    def closeEvent(self, event):
        # For some reason this is called twice when clicking the "Close" button SOMETIMES
        if self.ask_discard():
            event.ignore()
        else:
            event.accept()
예제 #26
0
class OpenedFileView(QObject):
    MARGIN_NUMBERS, MARGIN_MARKER_FOLD, MARGIN_MARKER_BP, MARGIN_MARKER_TP, MARGIN_MARKER_EXEC, \
    MARGIN_MARKER_EXEC_SIGNAL, MARKER_HIGHLIGHTED_LINE, MARGIN_MARKER_STACK = range(8)

    def __init__(self, distributedObjects, filename, parent):
        QObject.__init__(self, parent)
        filename = str(filename)
        self.distributedObjects = distributedObjects
        self.debugController = self.distributedObjects.debugController
        self.breakpointController = self.distributedObjects.breakpointController
        self.tracepointController = self.distributedObjects.tracepointController
        self.signalProxy = self.distributedObjects.signalProxy
        self.filename = filename
        self.lastContexMenuLine = 0
        self.markerBp = QPixmap(":/markers/bp.png")
        self.markerTp = QPixmap(":/markers/tp.png")
        self.markerExec = QPixmap(":/markers/exec_pos.png")
        self.markerStack = QPixmap(":/markers/stack_pos.png")
        self.markerExecSignal = QPixmap(":/markers/exec_pos_signal.png")
        self.shown = False

        self.FileWatcher = QFileSystemWatcher()
        self.FileWatcher.addPath(self.filename)
        self.FileWatcher.fileChanged.connect(self.fileChanged)

        self.tab = QtGui.QWidget()
        self.gridLayout = QtGui.QGridLayout(self.tab)
        self.gridLayout.setMargin(0)
        self.edit = ScintillaWrapper(self.tab)
        self.font = QFont("DejaVu Sans Mono", 10)
        self.font.setStyleHint(QFont.TypeWriter)
        self.lexer = Qsci.QsciLexerCPP()
        self.lexer.setFont(self.font)

        self.edit.setToolTip("")
        self.edit.setWhatsThis("")
        self.edit.setLexer(self.lexer)
        self.edit.setMarginLineNumbers(self.MARGIN_NUMBERS, True)
        # set sensitivity
        self.edit.setMarginSensitivity(self.MARGIN_NUMBERS, True)
        self.edit.setMarginSensitivity(self.MARGIN_MARKER_BP, True)
        self.edit.setMarginSensitivity(self.MARGIN_MARKER_TP, True)
        # define symbol
        self.edit.markerDefine(self.markerBp, self.MARGIN_MARKER_BP)
        self.edit.markerDefine(self.markerTp, self.MARGIN_MARKER_TP)
        self.edit.markerDefine(self.markerExec, self.MARGIN_MARKER_EXEC)
        self.edit.markerDefine(self.markerStack, self.MARGIN_MARKER_STACK)
        self.edit.markerDefine(self.markerExecSignal, self.MARGIN_MARKER_EXEC_SIGNAL)
        self.edit.markerDefine(Qsci.QsciScintilla.Background, self.MARKER_HIGHLIGHTED_LINE)
        # define width and mask to show margin
        self.edit.setMarginWidth(self.MARGIN_MARKER_BP, 10)
        self.edit.setMarginMarkerMask(self.MARGIN_MARKER_BP, 1 << self.MARGIN_MARKER_BP)
        self.edit.setMarginWidth(self.MARGIN_MARKER_TP, 10)
        self.edit.setMarginMarkerMask(self.MARGIN_MARKER_TP, 1 << self.MARGIN_MARKER_TP)
        self.edit.setMarginWidth(self.MARGIN_MARKER_EXEC, 10)
        self.edit.setMarginMarkerMask(self.MARGIN_MARKER_EXEC,
                1 << self.MARGIN_MARKER_EXEC |
                1 << self.MARGIN_MARKER_EXEC_SIGNAL |
                1 << self.MARGIN_MARKER_STACK)
        self.edit.setMarginWidth(self.MARKER_HIGHLIGHTED_LINE, 0)
        self.edit.setMarginMarkerMask(self.MARKER_HIGHLIGHTED_LINE, 1 << self.MARKER_HIGHLIGHTED_LINE)

        self.INDICATOR_TOOLTIP = self.edit.indicatorDefine(self.edit.BoxIndicator, -1)

        self.edit.setReadOnly(False)
        self.gridLayout.addWidget(self.edit, 0, 0, 1, 1)

        self.breakpoints = []

        if not (QtCore.QFile.exists(filename)):
            logging.error("could not open file", filename)
        self.file_ = QtCore.QFile(filename)
        self.file_.open(QtCore.QIODevice.ReadOnly | QtCore.QIODevice.Text)
        self.edit.read(self.file_)
        self.file_.close()

        self.changed = False
        self.edit.modificationChanged.connect(self.__setFileModified)

        self.setMarginWidthByLineNumbers()
        self.edit.SendScintilla(Qsci.QsciScintilla.SCI_SETMOUSEDWELLTIME, 500)

        # override scintillas context menu with our own
        self.edit.SendScintilla(Qsci.QsciScintilla.SCI_USEPOPUP, 0)
        self.edit.setContextMenuPolicy(Qt.CustomContextMenu)
        self.edit.customContextMenuRequested.connect(self.showContextMenu)

        self.edit.marginClicked.connect(self.marginClicked)
        self.edit.SCN_DOUBLECLICK.connect(self.editDoubleClicked)
        self.edit.dwellStart.connect(self.dwellStart)
        self.edit.dwellEnd.connect(self.dwellEnd)


        # initially, read all breakpoints and tracepoints from the model
        self.getBreakpointsFromModel()
        self.getTracepointsFromModel()

        _model = self.breakpointController.model()
        _model.rowsInserted.connect(self.getBreakpointsFromModel)
        _model.rowsRemoved.connect(self.getBreakpointsFromModel)
        _model = self.tracepointController.model()
        _model.rowsInserted.connect(self.getTracepointsFromModel)
        _model.rowsRemoved.connect(self.getTracepointsFromModel)

        act = self.distributedObjects.actions
        act.ToggleTrace.triggered.connect(self.toggleTracepoint)

        self.distributedObjects.editorController.config.itemsHaveChanged.connect(self.updateConfig)
        self.updateConfig()

        self.__allowToolTip = True
        self.__enableToolTip(True)

    def updateConfig(self):
        qs = Qsci.QsciScintilla
        c = self.distributedObjects.editorController.config
        self.edit.setWhitespaceVisibility(qs.WsVisible if c.showWhiteSpaces.value else qs.WsInvisible)
        self.edit.setIndentationGuides(c.showIndentationGuides.value)
        self.edit.setTabWidth(int(c.tabWidth.value))
        self.edit.setWrapMode(qs.WrapWord if c.wrapLines.value else qs.WrapNone)
        self.edit.setFolding(qs.BoxedTreeFoldStyle if c.folding.value else qs.NoFoldStyle, self.MARGIN_MARKER_FOLD)
        self.lexer.setPaper(QColor(c.backgroundColor.value))
        self.lexer.setColor(QColor(c.identifierColor.value), self.lexer.Identifier)
        self.lexer.setColor(QColor(c.identifierColor.value), self.lexer.Operator)
        self.edit.setCaretForegroundColor(QColor(c.identifierColor.value))
        self.lexer.setColor(QColor(c.keywordColor.value), self.lexer.Keyword)
        self.lexer.setColor(QColor(c.stringColor.value), self.lexer.SingleQuotedString)
        self.lexer.setColor(QColor(c.stringColor.value), self.lexer.DoubleQuotedString)
        self.lexer.setColor(QColor(c.numberColor.value), self.lexer.Number)
        self.lexer.setColor(QColor(c.preprocessorColor.value), self.lexer.PreProcessor)
        self.lexer.setColor(QColor(c.commentColor.value), self.lexer.Comment)
        self.lexer.setColor(QColor(c.commentColor.value), self.lexer.CommentLine)
        self.lexer.setColor(QColor(c.commentColor.value), self.lexer.CommentDoc)
        self.edit.setIndicatorForegroundColor(QColor(c.tooltipIndicatorColor.value))
        self.edit.setMarkerBackgroundColor(QColor(c.highlightColor.value), self.MARKER_HIGHLIGHTED_LINE)

    def fileChanged(self):
        logging.warning("Source file %s modified. Recompile executable for \
                correct debugging.", self.filename)

    def saveFile(self):
        ''' Save source file '''
        if (QtCore.QFile.exists(self.filename)):
            f = open(self.filename, 'w')
            f.write(self.edit.text())
            f.close()
            self.file_.open(QtCore.QIODevice.ReadOnly | QtCore.QIODevice.Text)
            self.edit.read(self.file_)
            self.file_.close()
            self.__setFileModified(False)

    def __setFileModified(self, modified):
        ''' Method called whenever current file is marked as modified '''
        self.distributedObjects.signalProxy.emitFileModified(self.filename, modified)

    def dwellStart(self, pos, x, y):
        if self.__allowToolTip:
            exp, (line, start, end) = self.getWordOrSelectionAndRangeFromPosition(pos)

            # try evaluating the expression before doing anything else: this will return None if the
            # expression is not valid (ie. something that is not a variable)
            if self.debugController.evaluateExpression(exp.strip()) is not None:
                self.edit.fillIndicatorRange(line, start, line, end, self.INDICATOR_TOOLTIP)
                startPos = self.edit.positionFromLineIndex(line, start)
                x = self.edit.SendScintilla(Qsci.QsciScintilla.SCI_POINTXFROMPOSITION, 0, startPos)
                y = self.edit.SendScintilla(Qsci.QsciScintilla.SCI_POINTYFROMPOSITION, 0, startPos)
                self.distributedObjects.toolTipController.showToolTip(exp, QtCore.QPoint(x + 3, y + 3 + self.edit.textHeight(line)), self.edit)

    def dwellEnd(self, position, x, y):
        self.distributedObjects.toolTipController.hideToolTip()
        self.edit.clearIndicatorRange(0, 0, self.edit.lines(), 1, self.INDICATOR_TOOLTIP)

    def showContextMenu(self, point):
        scipos = self.edit.SendScintilla(
                Qsci.QsciScintilla.SCI_POSITIONFROMPOINT, point.x(), point.y())
        point = self.edit.mapToGlobal(point)
        exp, (line, start, end) = self.getWordOrSelectionAndRangeFromPosition(scipos)
        self.edit.fillIndicatorRange(line, start, line, end, self.INDICATOR_TOOLTIP)

        # self.edit.lineIndexFromPosition(..) returns tuple. first element is line
        self.lastContexMenuLine = int(self.edit.lineIndexFromPosition(scipos)[0])

        listOfTracepoints = self.tracepointController.getTracepointsFromModel()

        self.subPopupMenu = QtGui.QMenu(self.edit)
        self.subPopupMenu.setTitle("Add variable " + exp + " to...")

        for tp in listOfTracepoints:
            self.subPopupMenu.addAction(self.distributedObjects.actions.getAddToTracepointAction(exp, tp.name, tp.addVar))

        self.popupMenu = QtGui.QMenu(self.edit)
        self.popupMenu.addAction(self.distributedObjects.actions.getAddToWatchAction(exp, self.signalProxy.addWatch))
        self.popupMenu.addAction(self.distributedObjects.actions.ToggleTrace)
        self.popupMenu.addAction(self.distributedObjects.actions.getAddToDatagraphAction(exp, self.distributedObjects.datagraphController.addWatch))
        self.popupMenu.addSeparator()
        self.popupMenu.addMenu(self.subPopupMenu)
        self.popupMenu.popup(point)

        # disable the tooltips while the menu is shown
        self.__enableToolTip(False)
        self.popupMenu.aboutToHide.connect(lambda: self.__enableToolTip(True))

    def __enableToolTip(self, enable):
        self.__allowToolTip = enable

    def isPositionInsideSelection(self, position):
        lf, cf, lt, ct = self.edit.getSelection()
        pl, pc = self.edit.lineIndexFromPosition(position)

        if lf < pl and pl < lt:
            return True
        elif lf == pl and pl < lt:
            return True if cf <= pc else False
        elif lf < pl and pl == lt:
            return True if pc <= ct else False
        elif lf == pl and pl == lt:
            return True if (cf <= pc and pc <= ct) else False
        else:
            return False

    def getWordOrSelectionAndRangeFromPosition(self, position):
        if self.isPositionInsideSelection(position):
            line, start, lineTo, end = self.edit.getSelection()
            if line != lineTo:
                return ""
        else:
            line, start, end = self.getWordRangeFromPosition(position)
        l = str(self.edit.text(line))
        return l[start:end], (line, start, end)

    def getWordRangeFromPosition(self, position):
        line, col = self.edit.lineIndexFromPosition(position)
        s = str(self.edit.text(line))
        start = col - 1
        end = col

        r = re.compile(r'[\w\d_\.]')    # FIXME: also scan over ->
        while start >= 0:
            if not r.match(s[start]):
                break
            start -= 1
        start += 1
        r = re.compile(r'[\w\d_]')
        while end < len(s):
            if not r.match(s[end]):
                break
            end += 1
        return (line, start, end)

    def editDoubleClicked(self, position, line, modifiers):
        line, start, end = self.getWordRangeFromPosition(position)
        l = str(self.edit.text(line))
        self.signalProxy.addWatch(str(l[start:end]))

    def showExecutionPosition(self, line):
        self.edit.markerAdd(line, self.MARGIN_MARKER_EXEC)
        self.showLine(line)

    def showSignalPosition(self, line):
        self.edit.markerAdd(line, self.MARGIN_MARKER_EXEC_SIGNAL)
        self.showLine(line)

    def showLine(self, line):
        self.edit.setCursorPosition(line, 1)
        self.edit.ensureLineVisible(line)

    def clearExecutionPositionMarkers(self):
        self.edit.markerDeleteAll(self.MARGIN_MARKER_EXEC)

    def setMarginWidthByLineNumbers(self):
        self.edit.setMarginWidth(0, ceil(log(self.edit.lines(), 10)) * 10 + 5)

    def marginClicked(self, margin, line, state):
        # if breakpoint should be toggled
        if margin == self.MARGIN_NUMBERS or margin == self.MARGIN_MARKER_BP:
            self.toggleBreakpointWithLine(line)
        elif margin == self.MARGIN_MARKER_TP:
            self.toggleTracepointWithLine(line)

    def toggleBreakpointWithLine(self, line):
        self.breakpointController.toggleBreakpoint(self.filename, line + 1)

    def toggleTracepointWithLine(self, line):
        self.tracepointController.toggleTracepoint(self.filename, line + 1)

    def toggleTracepoint(self):
        self.toggleTracepointWithLine(self.lastContexMenuLine)

    def getBreakpointsFromModel(self, parent=None, start=None, end=None):
        """Get breakpoints from model."""
        # TODO: don't reload all breakpoints, just the one referenced by parent/start/end
        self.edit.markerDeleteAll(self.MARGIN_MARKER_BP)
        for bp in self.breakpointController.getBreakpointsFromModel():
            if bp.fullname == self.filename:
                self.edit.markerAdd(int(bp.line) - 1, self.MARGIN_MARKER_BP)

    def getTracepointsFromModel(self):
        """Get tracepoints from model."""
        self.edit.markerDeleteAll(self.MARGIN_MARKER_TP)
        for tp in self.tracepointController.getTracepointsFromModel():
            if tp.fullname == self.filename:
                self.edit.markerAdd(int(tp.line) - 1, self.MARGIN_MARKER_TP)

    def highlightLine(self, line):
        self.removeHighlightedLines()
        self.edit.markerAdd(line, self.MARKER_HIGHLIGHTED_LINE)
        QTimer.singleShot(int(self.distributedObjects.editorController.config.highlightingDuration.value),
                          self.removeHighlightedLines)

    def removeHighlightedLines(self):
        self.edit.markerDeleteAll(self.MARKER_HIGHLIGHTED_LINE)
예제 #27
0
class ProjectWidget(Ui_Form, QWidget):
    SampleWidgetRole = Qt.UserRole + 1
    projectsaved = pyqtSignal()
    projectupdated = pyqtSignal(object)
    projectloaded = pyqtSignal(object)
    selectlayersupdated = pyqtSignal(list)

    def __init__(self, parent=None):
        super(ProjectWidget, self).__init__(parent)
        self.setupUi(self)
        self.project = None
        self.bar = None
        self.roamapp = None

        menu = QMenu()

        # self.roamVersionLabel.setText("You are running IntraMaps Roam version {}".format(roam.__version__))

        self.openProjectFolderButton.pressed.connect(self.openprojectfolder)
        self.openinQGISButton.pressed.connect(self.openinqgis)
        self.depolyProjectButton.pressed.connect(self.deploy_project)
        self.depolyInstallProjectButton.pressed.connect(
            functools.partial(self.deploy_project, True))

        self.filewatcher = QFileSystemWatcher()
        self.filewatcher.fileChanged.connect(self.qgisprojectupdated)

        self.projectupdatedlabel.linkActivated.connect(self.reloadproject)
        self.projectupdatedlabel.hide()

        # self.setpage(4)
        self.currentnode = None
        self.form = None

        qgislocation = r'C:\OSGeo4W\bin\qgis.bat'
        qgislocation = roam.config.settings.setdefault('configmanager', {}) \
            .setdefault('qgislocation', qgislocation)

        self.qgispathEdit.setText(qgislocation)
        self.qgispathEdit.textChanged.connect(self.save_qgis_path)
        self.filePickerButton.pressed.connect(self.set_qgis_path)

        self.connect_page_events()

    def connect_page_events(self):
        """
        Connect the events from all the pages back to here
        """
        for index in range(self.stackedWidget.count()):
            widget = self.stackedWidget.widget(index)
            if hasattr(widget, "raiseMessage"):
                widget.raiseMessage.connect(self.bar.pushMessage)

    def set_qgis_path(self):
        """
        Set the location of the QGIS install.  We need the path to be able to create Roam
        projects
        """
        path = QFileDialog.getOpenFileName(self,
                                           "Select QGIS install file",
                                           filter="(*.bat)")
        if not path:
            return
        self.qgispathEdit.setText(path)
        self.save_qgis_path(path)

    def save_qgis_path(self, path):
        """
        Save the QGIS path back to the Roam config.
        """
        roam.config.settings['configmanager'] = {'qgislocation': path}
        roam.config.save()

    def setpage(self, page, node):
        """
        Set the current page in the config manager.  We pass the project into the current
        page so that it knows what the project is.
        """
        self.currentnode = node

        self.write_config_currentwidget()

        self.stackedWidget.setCurrentIndex(page)

        widget = self.stackedWidget.currentWidget()
        if hasattr(widget, "set_project"):
            widget.set_project(self.project, self.currentnode)

    def write_config_currentwidget(self):
        """
        Call the write config command on the current widget.
        """
        widget = self.stackedWidget.currentWidget()
        if hasattr(widget, "write_config"):
            widget.write_config()

    def deploy_project(self, with_data=False):
        """
        Run the step to deploy a project. Projects are deplyed as a bundled zip of the project folder.
        """
        if self.roamapp.sourcerun:
            base = os.path.join(self.roamapp.apppath, "..")
        else:
            base = self.roamapp.apppath

        default = os.path.join(base, "roam_serv")
        path = roam.config.settings.get("publish", {}).get("path", '')
        if not path:
            path = default

        path = os.path.join(path, "projects")

        if not os.path.exists(path):
            os.makedirs(path)

        self._saveproject()
        options = {}

        bundle.bundle_project(self.project,
                              path,
                              options,
                              as_install=with_data)

    def setaboutinfo(self):
        """
        Set the current about info on the widget
        """
        self.versionLabel.setText(roam.__version__)
        self.qgisapiLabel.setText(unicode(QGis.QGIS_VERSION))

    def selectlayerschanged(self, *args):
        """
        Run the updates when the selection layers have changed
        """
        self.formlayers.setSelectLayers(self.project.selectlayers)
        self.selectlayersupdated.emit(self.project.selectlayers)

    def reloadproject(self, *args):
        """
        Reload the project. At the moment this will drop any unsaved changes to the config.
        Note: Should look at making sure it doesn't do that because it's not really needed.
        """
        self.projectupdated.emit(self.project)
        # self.setproject(self.project)

    def qgisprojectupdated(self, path):
        """
        Show a message when the QGIS project file has been updated.
        """
        self.projectupdatedlabel.show()
        self.projectupdatedlabel.setText(
            "The QGIS project has been updated. <a href='reload'> "
            "Click to reload</a>. <b style=\"color:red\">Unsaved data will be lost</b>"
        )

    def openinqgis(self):
        """
        Open a QGIS session for the user to config the project layers.
        """
        try:
            openqgis(self.project.projectfile)
        except OSError:
            self.bar.pushMessage("Looks like I couldn't find QGIS",
                                 "Check qgislocation in roam.config",
                                 QgsMessageBar.WARNING)

    def openprojectfolder(self):
        """
        Open the project folder in the file manager for the OS.
        """
        folder = self.project.folder
        openfolder(folder)

    def setproject(self, project, loadqgis=True):
        """
        Set the widgets active project.
        """
        self.filewatcher.removePaths(self.filewatcher.files())
        self.projectupdatedlabel.hide()
        self._closeqgisproject()

        if project.valid:
            self.startsettings = copy.deepcopy(project.settings)
            self.project = project
            self.projectlabel.setText(project.name)
            self.loadqgisproject(project, self.project.projectfile)
            self.filewatcher.addPath(self.project.projectfile)
            self.projectloaded.emit(self.project)

    def loadqgisproject(self, project, projectfile):
        print("Load QGIS Project!!")
        QDir.setCurrent(os.path.dirname(project.projectfile))
        fileinfo = QFileInfo(project.projectfile)
        # No idea why we have to set this each time.  Maybe QGIS deletes it for
        # some reason.
        self.badLayerHandler = BadLayerHandler(callback=self.missing_layers)
        QgsProject.instance().setBadLayerHandler(self.badLayerHandler)
        QgsProject.instance().read(fileinfo)

    def missing_layers(self, missinglayers):
        """
        Handle any and show any missing layers.
        """
        self.project.missing_layers = missinglayers

    def _closeqgisproject(self):
        """
        Close the current QGIS project and clean up after..
        """
        QGIS.close_project()

    def _saveproject(self):
        """
        Save the project config to disk.
        """
        self.write_config_currentwidget()
        # self.project.dump_settings()
        self.project.save(update_version=True)
        self.filewatcher.removePaths(self.filewatcher.files())
        QgsProject.instance().write()
        self.filewatcher.addPath(self.project.projectfile)
        self.projectsaved.emit()
예제 #28
0
class ProjectWidget(Ui_Form, QWidget):
    SampleWidgetRole = Qt.UserRole + 1
    projectsaved = pyqtSignal()
    projectupdated = pyqtSignal()
    projectloaded = pyqtSignal(object)
    selectlayersupdated = pyqtSignal(list)

    def __init__(self, parent=None):
        super(ProjectWidget, self).__init__(parent)
        self.setupUi(self)
        self.project = None
        self.mapisloaded = False
        self.bar = None

        self.canvas.setCanvasColor(Qt.white)
        self.canvas.enableAntiAliasing(True)
        self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor)
        self.canvas.mapRenderer().setLabelingEngine(QgsPalLabeling())

        self.fieldsmodel = QgsFieldModel()
        self.widgetmodel = WidgetsModel()
        self.possiblewidgetsmodel = QStandardItemModel()

        self.formlayersmodel = QgsLayerModel(watchregistry=False)
        self.formlayers = CaptureLayerFilter()
        self.formlayers.setSourceModel(self.formlayersmodel)

        self.selectlayermodel = CaptureLayersModel(watchregistry=False)
        self.selectlayerfilter = LayerTypeFilter()
        self.selectlayerfilter.setSourceModel(self.selectlayermodel)
        self.selectlayermodel.dataChanged.connect(self.selectlayerschanged)

        self.layerCombo.setModel(self.formlayers)
        self.widgetCombo.setModel(self.possiblewidgetsmodel)
        self.selectLayers.setModel(self.selectlayerfilter)
        self.selectLayers_2.setModel(self.selectlayerfilter)
        self.fieldList.setModel(self.fieldsmodel)

        self.widgetlist.setModel(self.widgetmodel)
        self.widgetlist.selectionModel().currentChanged.connect(self.updatecurrentwidget)
        self.widgetmodel.rowsRemoved.connect(self.setwidgetconfigvisiable)
        self.widgetmodel.rowsInserted.connect(self.setwidgetconfigvisiable)
        self.widgetmodel.modelReset.connect(self.setwidgetconfigvisiable)

        self.titleText.textChanged.connect(self.updatetitle)

        QgsProject.instance().readProject.connect(self._readproject)

        self.loadwidgettypes()

        self.addWidgetButton.pressed.connect(self.newwidget)
        self.removeWidgetButton.pressed.connect(self.removewidget)

        self.roamVersionLabel.setText("You are running IntraMaps Roam version {}".format(roam.__version__))

        self.openProjectFolderButton.pressed.connect(self.openprojectfolder)
        self.openinQGISButton.pressed.connect(self.openinqgis)

        self.filewatcher = QFileSystemWatcher()
        self.filewatcher.fileChanged.connect(self.qgisprojectupdated)

        self.formfolderLabel.linkActivated.connect(self.openformfolder)
        self.projectupdatedlabel.linkActivated.connect(self.reloadproject)
        self.projectupdatedlabel.hide()
        self.formtab.currentChanged.connect(self.formtabchanged)

        self.expressionButton.clicked.connect(self.opendefaultexpression)

        self.fieldList.currentIndexChanged.connect(self.updatewidgetname)
        self.fieldwarninglabel.hide()

        for item, data in readonlyvalues:
            self.readonlyCombo.addItem(item, data)

        self.setpage(4)
        self.form = None

    def setaboutinfo(self):
        self.versionLabel.setText(roam.__version__)
        self.qgisapiLabel.setText(str(QGis.QGIS_VERSION))

    def checkcapturelayers(self):
        haslayers = self.project.hascapturelayers()
        self.formslayerlabel.setVisible(not haslayers)
        return haslayers

    def opendefaultexpression(self):
        layer = self.currentform.QGISLayer
        dlg = QgsExpressionBuilderDialog(layer, "Create default value expression", self)
        text = self.defaultvalueText.text().strip('[%').strip('%]').strip()
        dlg.setExpressionText(text)
        if dlg.exec_():
            self.defaultvalueText.setText('[% {} %]'.format(dlg.expressionText()))

    def openformfolder(self, url):
        openfolder(url)

    def selectlayerschanged(self, *args):
        self.formlayers.setSelectLayers(self.project.selectlayers)
        self.checkcapturelayers()
        self.selectlayersupdated.emit(self.project.selectlayers)

    def formtabchanged(self, index):
        # preview
        if index == 1:
            self.form.settings['widgets'] = list(self.widgetmodel.widgets())
            self.setformpreview(self.form)


    def setpage(self, page):
        self.stackedWidget.setCurrentIndex(page)

    def reloadproject(self, *args):
        self.setproject(self.project)

    def qgisprojectupdated(self, path):
        self.projectupdatedlabel.show()
        self.projectupdatedlabel.setText("The QGIS project has been updated. <a href='reload'> "
                                         "Click to reload</a>. <b style=\"color:red\">Unsaved data will be lost</b>")

    def openinqgis(self):
        projectfile = self.project.projectfile
        qgislocation = r'C:\OSGeo4W\bin\qgis.bat'
        qgislocation = roam.config.settings.setdefault('configmanager', {}) \
                                        .setdefault('qgislocation', qgislocation)

        try:
            openqgis(projectfile, qgislocation)
        except WindowsError:
            self.bar.pushMessage("Looks like I couldn't find QGIS",
                               "Check qgislocation in roam.config", QgsMessageBar.WARNING)

    def openprojectfolder(self):
        folder = self.project.folder
        openfolder(folder)

    def setwidgetconfigvisiable(self, *args):
        haswidgets = self.widgetmodel.rowCount() > 0
        self.widgetframe.setEnabled(haswidgets)

    def removewidget(self):
        """
        Remove the selected widget from the widgets list
        """
        widget, index = self.currentuserwidget
        if index.isValid():
            self.widgetmodel.removeRow(index.row(), index.parent())

    def newwidget(self):
        """
        Create a new widget.  The default is a list.
        """
        widget = {}
        widget['widget'] = 'Text'
        # Grab the first field.
        widget['field'] = self.fieldsmodel.index(0, 0).data(QgsFieldModel.FieldNameRole)
        currentindex = self.widgetlist.currentIndex()
        currentitem = self.widgetmodel.itemFromIndex(currentindex)
        if currentitem and currentitem.iscontainor():
            parent = currentindex
        else:
            parent = currentindex.parent()
        index = self.widgetmodel.addwidget(widget, parent)
        self.widgetlist.setCurrentIndex(index)

    def loadwidgettypes(self):
        self.widgetCombo.blockSignals(True)
        for widgettype in roam.editorwidgets.core.supportedwidgets():
            try:
                configclass = configmanager.editorwidgets.widgetconfigs[widgettype]
            except KeyError:
                continue

            configwidget = configclass()
            item = QStandardItem(widgettype)
            item.setData(configwidget, Qt.UserRole)
            item.setData(widgettype, Qt.UserRole + 1)
            item.setIcon(QIcon(widgeticon(widgettype)))
            self.widgetCombo.model().appendRow(item)
            self.widgetstack.addWidget(configwidget)
        self.widgetCombo.blockSignals(False)

    def usedfields(self):
        """
        Return the list of fields that have been used by the the current form's widgets
        """
        for widget in self.currentform.widgets:
            yield widget['field']

    @property
    def currentform(self):
        """
        Return the current selected form.
        """
        return self.form

    @property
    def currentuserwidget(self):
        """
        Return the selected user widget.
        """
        index = self.widgetlist.currentIndex()
        return index.data(Qt.UserRole), index

    @property
    def currentwidgetconfig(self):
        """
        Return the selected widget in the widget combo.
        """
        index = self.widgetCombo.currentIndex()
        index = self.possiblewidgetsmodel.index(index, 0)
        return index.data(Qt.UserRole), index, index.data(Qt.UserRole + 1)

    def updatewidgetname(self, index):
        # Only change the edit text on name field if it's not already set to something other then the
        # field name.
        field = self.fieldsmodel.index(index, 0).data(QgsFieldModel.FieldNameRole)
        currenttext = self.nameText.text()
        foundfield = self.fieldsmodel.findfield(currenttext)
        if foundfield:
            self.nameText.setText(field)

    def _save_widgetfield(self, index):
        """
        Save the selected field for the current widget.

        Shows a error if the field is already used but will allow
        the user to still set it in the case of extra logic for that field
        in the forms Python logic.
        """
        widget, index = self.currentuserwidget
        row = self.fieldList.currentIndex()
        field = self.fieldsmodel.index(row, 0).data(QgsFieldModel.FieldNameRole)
        showwarning = field in self.usedfields()
        self.fieldwarninglabel.setVisible(showwarning)
        widget['field'] = field
        self.widgetmodel.setData(index, widget, Qt.UserRole)

    def _save_selectedwidget(self, index):
        configwidget, index, widgettype = self.currentwidgetconfig
        widget, index = self.currentuserwidget
        if not widget:
            return

        widget['widget'] = widgettype
        widget['required'] = self.requiredCheck.isChecked()
        widget['config'] = configwidget.getconfig()
        widget['name'] = self.nameText.text()
        widget['read-only-rules'] = [self.readonlyCombo.itemData(self.readonlyCombo.currentIndex())]
        widget['hidden'] = self.hiddenCheck.isChecked()

        self.widgetmodel.setData(index, widget, Qt.UserRole)

    def _save_default(self):
        widget, index = self.currentuserwidget
        default = self.defaultvalueText.text()
        widget['default'] = default
        self.widgetmodel.setData(index, widget, Qt.UserRole)

    def _save_selectionlayers(self, index, layer, value):
        config = self.project.settings

        self.selectlayermodel.dataChanged.emit(index, index)

    def _save_formtype(self, index):
        formtype = self.formtypeCombo.currentText()
        form = self.currentform
        form.settings['type'] = formtype

    def _save_formname(self, text):
        """
        Save the form label to the settings file.
        """
        try:
            form = self.currentform
            if form is None:
                return
            form.settings['label'] = text
            self.projectupdated.emit()
        except IndexError:
            return

    def _save_layer(self, index):
        """
        Save the selected layer to the settings file.
        """
        index = self.formlayers.index(index, 0)
        layer = index.data(Qt.UserRole)
        if not layer:
            return

        form = self.currentform
        if form is None:
            return

        form.settings['layer'] = layer.name()
        self.updatefields(layer)

    def setsplash(self, splash):
        pixmap = QPixmap(splash)
        w = self.splashlabel.width()
        h = self.splashlabel.height()
        self.splashlabel.setPixmap(pixmap.scaled(w,h, Qt.KeepAspectRatio))

    def setproject(self, project, loadqgis=True):
        """
        Set the widgets active project.
        """
        self.disconnectsignals()
        self.mapisloaded = False
        self.filewatcher.removePaths(self.filewatcher.files())
        self.projectupdatedlabel.hide()
        self._closeqgisproject()

        if project.valid:
            self.startsettings = copy.deepcopy(project.settings)
            self.project = project
            self.projectlabel.setText(project.name)
            self.versionText.setText(project.version)
            self.selectlayermodel.config = project.settings
            self.formlayers.setSelectLayers(self.project.selectlayers)
            self.setsplash(project.splash)
            self.loadqgisproject(project, self.project.projectfile)
            self.filewatcher.addPath(self.project.projectfile)
            self.projectloaded.emit(self.project)

    def loadqgisproject(self, project, projectfile):
        QDir.setCurrent(os.path.dirname(project.projectfile))
        fileinfo = QFileInfo(project.projectfile)
        QgsProject.instance().read(fileinfo)

    def _closeqgisproject(self):
        if self.canvas.isDrawing():
            return

        self.canvas.freeze(True)
        self.formlayersmodel.removeall()
        self.selectlayermodel.removeall()
        QgsMapLayerRegistry.instance().removeAllMapLayers()
        self.canvas.freeze(False)

    def loadmap(self):
        if self.mapisloaded:
            return

        # This is a dirty hack to work around the timer that is in QgsMapCanvas in 2.2.
        # Refresh will stop the canvas timer
        # Repaint will redraw the widget.
        # loadmap is only called once per project load so it's safe to do this here.
        self.canvas.refresh()
        self.canvas.repaint()

        parser = roam.projectparser.ProjectParser.fromFile(self.project.projectfile)
        canvasnode = parser.canvasnode
        self.canvas.mapRenderer().readXML(canvasnode)
        self.canvaslayers = parser.canvaslayers()
        self.canvas.setLayerSet(self.canvaslayers)
        self.canvas.updateScale()
        self.canvas.refresh()

        self.mapisloaded = True

    def _readproject(self, doc):
        self.formlayersmodel.refresh()
        self.selectlayermodel.refresh()
        self._updateforproject(self.project)

    def _updateforproject(self, project):
        self.titleText.setText(project.name)
        self.descriptionText.setPlainText(project.description)

    def swapwidgetconfig(self, index):
        widgetconfig, _, _ = self.currentwidgetconfig
        defaultvalue = widgetconfig.defaultvalue
        self.defaultvalueText.setText(defaultvalue)

        self.updatewidgetconfig({})

    def updatetitle(self, text):
        self.project.settings['title'] = text
        self.projectlabel.setText(text)
        self.projectupdated.emit()

    def updatewidgetconfig(self, config):
        widgetconfig, index, widgettype = self.currentwidgetconfig
        self.setconfigwidget(widgetconfig, config)

    def setformpreview(self, form):
        def removewidget():
            item = self.frame_2.layout().itemAt(0)
            if item and item.widget():
                item.widget().setParent(None)

        removewidget()

        featureform = FeatureForm.from_form(form, form.settings, None, {})

        self.frame_2.layout().addWidget(featureform)

    def connectsignals(self):
        self.formLabelText.textChanged.connect(self._save_formname)
        self.layerCombo.currentIndexChanged.connect(self._save_layer)
        self.formtypeCombo.currentIndexChanged.connect(self._save_formtype)

        #widget settings
        self.fieldList.currentIndexChanged.connect(self._save_widgetfield)
        self.requiredCheck.toggled.connect(self._save_selectedwidget)
        self.defaultvalueText.textChanged.connect(self._save_default)
        self.widgetCombo.currentIndexChanged.connect(self._save_selectedwidget)
        self.widgetCombo.currentIndexChanged.connect(self.swapwidgetconfig)
        self.nameText.textChanged.connect(self._save_selectedwidget)
        self.readonlyCombo.currentIndexChanged.connect(self._save_selectedwidget)
        self.hiddenCheck.toggled.connect(self._save_selectedwidget)

    def disconnectsignals(self):
        try:
            self.formLabelText.textChanged.disconnect(self._save_formname)
            self.layerCombo.currentIndexChanged.disconnect(self._save_layer)
            self.formtypeCombo.currentIndexChanged.disconnect(self._save_formtype)

            #widget settings
            self.fieldList.currentIndexChanged.disconnect(self._save_widgetfield)
            self.requiredCheck.toggled.disconnect(self._save_selectedwidget)
            self.defaultvalueText.textChanged.disconnect(self._save_default)
            self.widgetCombo.currentIndexChanged.disconnect(self._save_selectedwidget)
            self.widgetCombo.currentIndexChanged.disconnect(self.swapwidgetconfig)
            self.nameText.textChanged.disconnect(self._save_selectedwidget)
            self.readonlyCombo.currentIndexChanged.disconnect(self._save_selectedwidget)
            self.hiddenCheck.toggled.disconnect(self._save_selectedwidget)
        except TypeError:
            pass

    def setform(self, form):
        """
        Update the UI with the currently selected form.
        """

        def getfirstlayer():
            index = self.formlayers.index(0,0)
            layer = index.data(Qt.UserRole)
            layer = layer.name()
            return layer

        def loadwidgets(widget):
            """
            Load the widgets into widgets model
            """
            self.widgetmodel.clear()
            self.widgetmodel.loadwidgets(form.widgets)

        def findlayer(layername):
            index = self.formlayersmodel.findlayer(layername)
            index = self.formlayers.mapFromSource(index)
            layer = index.data(Qt.UserRole)
            return index, layer


        self.disconnectsignals()

        self.form = form

        settings = form.settings
        label = form.label
        layername = settings.setdefault('layer', getfirstlayer())
        layerindex, layer = findlayer(layername)
        if not layer or not layerindex.isValid():
            return

        formtype = settings.setdefault('type', 'auto')
        widgets = settings.setdefault('widgets', [])

        self.formLabelText.setText(label)
        folderurl = "<a href='{path}'>{name}</a>".format(path=form.folder, name=os.path.basename(form.folder))
        self.formfolderLabel.setText(folderurl)
        self.layerCombo.setCurrentIndex(layerindex.row())
        self.updatefields(layer)

        index = self.formtypeCombo.findText(formtype)
        if index == -1:
            self.formtypeCombo.insertItem(0, formtype)
            self.formtypeCombo.setCurrentIndex(0)
        else:
            self.formtypeCombo.setCurrentIndex(index)

        loadwidgets(widgets)

        # Set the first widget
        index = self.widgetmodel.index(0, 0)
        if index.isValid():
            self.widgetlist.setCurrentIndex(index)
            self.updatecurrentwidget(index, None)

        self.connectsignals()

    def updatefields(self, layer):
        """
        Update the UI with the fields for the selected layer.
        """
        self.fieldsmodel.setLayer(layer)

    def setconfigwidget(self, configwidget, config):
        """
        Set the active config widget.
        """

        try:
            configwidget.widgetdirty.disconnect(self._save_selectedwidget)
        except TypeError:
            pass

        #self.descriptionLabel.setText(configwidget.description)
        self.widgetstack.setCurrentWidget(configwidget)
        configwidget.setconfig(config)

        configwidget.widgetdirty.connect(self._save_selectedwidget)

    def updatecurrentwidget(self, index, _):
        """
        Update the UI with the config for the current selected widget.
        """
        if not index.isValid():
            return

        widget = index.data(Qt.UserRole)
        widgettype = widget['widget']
        field = widget['field']
        required = widget.setdefault('required', False)
        name = widget.setdefault('name', field)
        default = widget.setdefault('default', '')
        readonly = widget.setdefault('read-only-rules', [])
        hidden = widget.setdefault('hidden', False)

        try:
            data = readonly[0]
        except:
            data = 'never'

        self.readonlyCombo.blockSignals(True)
        index = self.readonlyCombo.findData(data)
        self.readonlyCombo.setCurrentIndex(index)
        self.readonlyCombo.blockSignals(False)

        self.defaultvalueText.blockSignals(True)
        if not isinstance(default, dict):
            self.defaultvalueText.setText(default)
            self.defaultvalueText.setEnabled(True)
            self.expressionButton.setEnabled(True)
        else:
            # TODO Handle the more advanced default values.
            self.defaultvalueText.setText("Advanced default set in config")
            self.defaultvalueText.setEnabled(False)
            self.expressionButton.setEnabled(False)
        self.defaultvalueText.blockSignals(False)

        self.nameText.blockSignals(True)
        self.nameText.setText(name)
        self.nameText.blockSignals(False)

        self.requiredCheck.blockSignals(True)
        self.requiredCheck.setChecked(required)
        self.requiredCheck.blockSignals(False)

        self.hiddenCheck.blockSignals(True)
        self.hiddenCheck.setChecked(hidden)
        self.hiddenCheck.blockSignals(False)

        if not field is None:
            self.fieldList.blockSignals(True)
            index = self.fieldList.findData(field.lower(), QgsFieldModel.FieldNameRole)
            if index > -1:
                self.fieldList.setCurrentIndex(index)
            else:
                self.fieldList.setEditText(field)
            self.fieldList.blockSignals(False)

        index = self.widgetCombo.findText(widgettype)
        self.widgetCombo.blockSignals(True)
        if index > -1:
            self.widgetCombo.setCurrentIndex(index)
        self.widgetCombo.blockSignals(False)

        self.updatewidgetconfig(config=widget.setdefault('config', {}))

    def _saveproject(self):
        """
        Save the project config to disk.
        """
        title = self.titleText.text()
        description = self.descriptionText.toPlainText()
        version = str(self.versionText.text())

        settings = self.project.settings
        settings['title'] = title
        settings['description'] = description
        settings['version'] = version

        form = self.currentform
        if form:
            form.settings['widgets'] = list(self.widgetmodel.widgets())
            logger.debug(form.settings)

        self.project.save()
        self.projectsaved.emit()
예제 #29
0
class PreferencesWindow(BaseWindow):
    
    def __init__(self, *args, **kwargs):
        super(PreferencesWindow, self).__init__(layoutCls = QVBoxLayout, *args, **kwargs)
        self.setWindowTitle("Settings")
        self.setWindowIcon(QIcon(SETTINGS['icon']))
        self._add_steam_box()
        self._add_dota_box()
        self._add_additional_prefs()
        self._add_log_box()
        
        self._add_log_watcher()
        Settings().signals.changed.connect(self.update_path)
    
    def _add_log_watcher(self):
        self.watcher = QFileSystemWatcher()
        self.watcher.addPath(abspath(log.file_name))
        self.watcher.fileChanged.connect(self.update_log)
    
    def show(self):
        self.update_log(abspath(log.file_name))
        return super(PreferencesWindow, self).show()
    
    def update_path(self, setting_key, new_value):
        if setting_key == "dota_path":
            self.dota_path.setText(new_value)
        elif setting_key == "steam_path":
            self.steam_path.setText(new_value)
    
    def _add_steam_box(self):
        box = QGroupBox("Steam Location")
        box.setLayout(QHBoxLayout(box))
        
        self.steam_path = QLineEdit(box)
        self.steam_path.setReadOnly(True)
        self.steam_path.setText(Settings().get("steam_path"))

        change_btn = QPushButton("Change...", box)
        change_btn.clicked.connect(self.change_steam_path)
                
        box.layout().addWidget(self.steam_path)
        box.layout().addWidget(change_btn)
        
        self.layout().addWidget(box)
    
    def _add_dota_box(self):
        box = QGroupBox("Dota Location")
        box.setLayout(QHBoxLayout(box))
        
        self.dota_path = QLineEdit(box)
        self.dota_path.setReadOnly(True)
        self.dota_path.setText(Settings().get("dota_path"))
        
        change_btn = QPushButton("Change...", box)
        change_btn.clicked.connect(self.change_dota_path)

        box.layout().addWidget(self.dota_path)
        box.layout().addWidget(change_btn)
        
        self.layout().addWidget(box)
    
    def change_steam_path(self):
        self._change_path("steam_path", is_steam_path_valid)

    def change_dota_path(self):
        self._change_path("dota_path", is_dota_path_valid)
    
    def _change_path(self, path_key, is_valid):
        new_folder = str(QFileDialog.getExistingDirectory(parent=self, caption="Select new path", directory=Settings().get(path_key)) )
        if new_folder and exists(new_folder):
            if is_valid(new_folder):
                Settings().set(path_key, new_folder)
            else:
                from d2mp.ui import Message
                Message.critical("Path is not valid", 
                            "Path was not saved in settings.\nPlease select a directory with the right executable in it!")
    
    def _add_additional_prefs(self):
        box = QGroupBox("Additional Preferences")
        box.setLayout(QHBoxLayout(box))
                
        log_btn = QPushButton("View Log", box)
        log_btn.clicked.connect(self.open_log_file)
        reset_btn = QPushButton("Reset Settings", box)
        reset_btn.clicked.connect(Settings().reset)
                
        box.layout().addWidget(log_btn)
        box.layout().addWidget(reset_btn)
        
        self.layout().addWidget(box)
        
    def _add_log_box(self):
        box = QGroupBox("Application log")
        box.setLayout(QHBoxLayout(box))
        
        self.log_area = QTextBrowser(box)
        self.log_area.setLineWrapMode(QTextEdit.NoWrap)
        
        box.layout().addWidget(self.log_area)
        self.layout().addWidget(box)
    
    def open_log_file(self):
        log.INFO("TODO: open file in standard editor")
        print(abspath(log.file_name))
    
    def update_log(self, log_file):
        content = ""
        for line in open(log_file):
            if "========= new programm start =========" in line:
                content = ""
            else:
                content += line[37:]
        if content:
            self.log_area.setText(content)
예제 #30
0
class EdisFile(QObject):
    """ Representación de un objeto archivo """
    def __init__(self, filename=''):
        QObject.__init__(self)
        self._is_new = True
        if not filename:
            self._filename = "Untitled"
        else:
            self._filename = filename
            self._is_new = False
        self._last_modification = None
        self._system_watcher = None

    @property
    def filename(self):
        return self._filename

    @property
    def is_new(self):
        return self._is_new

    def read(self):
        """ Itenta leer el contenido del archivo, si ocurre un error se lanza
        una excepción.
        """

        try:
            with open(self.filename, mode='r') as f:
                content = f.read()
            return content
        except IOError as reason:
            raise exceptions.EdisIOError(reason)

    def write(self, content, new_filename=''):
        """ Escribe los datos en el archivo """

        DEBUG("Saving file...")
        # Por defecto, si el archivo no tiene extensión se agrega .c
        ext = os.path.splitext(new_filename)
        if not ext[-1]:
            new_filename += '.c'
        if self.is_new:
            self._filename = new_filename
            self._is_new = False
        _file = QFile(self.filename)
        if not _file.open(QIODevice.WriteOnly | QIODevice.Truncate):
            raise exceptions.EdisIOError
        out_file = QTextStream(_file)
        out_file << content
        if self._system_watcher is not None:
            if self._filename is not None:
                archivos = self._system_watcher.files()
                self._system_watcher.removePath(archivos[0])
        else:
            self.run_system_watcher()

    def run_system_watcher(self):
        """ Inicializa el control de monitoreo para modificaciones """

        if self._system_watcher is None:
            self._system_watcher = QFileSystemWatcher()
            self.connect(self._system_watcher,
                         SIGNAL("fileChanged(const QString&)"),
                         self._on_file_changed)
        self._last_modification = os.lstat(self.filename).st_mtime
        self._system_watcher.addPath(self.filename)
        DEBUG("Watching {0}".format(self.filename))

    def stop_system_watcher(self):
        if self._system_watcher is not None:
            self._system_watcher.removePath(self.filename)
            DEBUG("Stoping watching {0}".format(self.filename))

    def _on_file_changed(self, filename):
        mtime = os.lstat(filename).st_mtime
        if mtime != self._last_modification:
            # Actualizo la última modificación
            self._last_modification = mtime
            self.emit(SIGNAL("fileChanged(PyQt_PyObject)"), self)