예제 #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 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
예제 #4
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()
예제 #5
0
class Syncer(QObject):
    def __init__(self, settings):
        QObject.__init__(self)
        self.s = settings
        self.filewatcher = None
        self.excelName = settings.excelName  # the layer name
        self.excelSheetName = settings.excelSheetName
        self.excelKeyName = settings.excelKeyName
        self.excelFkIdx = field_idx_from_name(self.excelName,
                                              self.excelKeyName)
        self.excelPath = layer_from_name(self.excelName).publicSource()
        self.excelKeyName = field_name_from_idx(self.excelName,
                                                self.excelFkIdx)
        # shpfile layer
        self.shpName = settings.shpName
        self.shpKeyName = settings.shpKeyName
        self.shpKeyIdx = field_idx_from_name(self.shpName, self.shpKeyName)
        self.skipLines = settings.skipLines
        layer_from_name(self.shpName).setFeatureFormSuppress(
            QgsVectorLayer.SuppressOn if settings.
            hideDialog else QgsVectorLayer.SuppressOff)

        self.join()
        self.clear_edit_state()
        self.initialSync()

    def join(self):
        # join the shp layer to the excel layer, non cached
        # TODO: Ignore if already joined?
        shpLayer = layer_from_name(self.shpName)
        jinfo = QgsVectorJoinInfo()
        jinfo.joinFieldName = self.excelKeyName
        jinfo.targetFieldName = self.shpKeyName
        jinfo.joinLayerId = layer_from_name(self.excelName).id()
        jinfo.memoryCache = False
        jinfo.prefix = ''
        for jinfo2 in shpLayer.vectorJoins():
            if jinfo2 == jinfo:
                info("Join already exists. Will not create it again")
                return
        info("Adding join between master and slave layers")
        shpLayer.addJoin(jinfo)

    def reload_excel(self):
        path = self.excelPath
        layer = layer_from_name(self.excelName)
        fsize = os.stat(self.excelPath).st_size
        info("fsize " + str(fsize))
        if fsize == 0:
            info("File empty. Won't reload yet")
            return
        layer.dataProvider().forceReload()
        show_message_bar("Excel reloaded from disk.")

    def excel_changed(self):
        info("Excel changed on disk - need to sync")
        self.reload_excel()
        self.update_shp_from_excel()

    def get_max_id(self):

        layer = layer_from_name(self.shpName)
        if layer.dataProvider().featureCount() == 0:
            return 0
        maxVal = layer.maximumValue(self.shpKeyIdx)
        if maxVal == None:
            return 0
        else:
            return maxVal

    def renameIds(self, fidToId):
        layer = layer_from_name(self.shpName)
        layer.startEditing()
        feats = query_layer_for_fids(self.shpName, fidToId.keys())
        for f in feats:
            res = layer.changeAttributeValue(f.id(), self.shpKeyIdx,
                                             fidToId[f.id()])
        layer.commitChanges()

    def added_geom(self, layerId, feats):
        info("added feats " + str(feats))
        layer = layer_from_name(self.shpName)
        maxFk = self.get_max_id()
        for i, _ in enumerate(feats):
            _id = maxFk + i + 1
            feats[i].setAttribute(self.shpKeyName, _id)

        self.shpAdd = feats

    def removed_geom_precommit(self, fids):
        #info("Removed fids"+str(fids))
        fks_to_remove = get_fk_set(self.shpName,
                                   self.shpKeyName,
                                   skipFirst=0,
                                   fids=fids,
                                   useProvider=True)
        self.shpRemove = self.shpRemove.union(fks_to_remove)
        info("feat ids to remove" + str(self.shpRemove))

    def changed_geom(self, layerId, geoms):
        fids = geoms.keys()
        feats = query_layer_for_fids(self.shpName, fids)
        fks_to_change = get_fk_set(self.shpName,
                                   self.shpKeyName,
                                   skipFirst=0,
                                   fids=fids)
        self.shpChange = {k: v for (k, v) in zip(fks_to_change, feats)}
        # info("changed"+str(shpChange))

    def get_ignore_indices(self):
        return [
            field_idx_from_name(self.excelName, field)
            for field in self.s.expressions.keys()
        ]

    def write_feature_to_excel(self, sheet, idx, feat):
        sheet.write(idx, self.excelFkIdx, feat[self.shpKeyName])
        for (fieldName, exp) in self.s.expressions.iteritems():
            fieldIdx = field_idx_from_name(self.excelName, fieldName)
            exp = QgsExpression(exp)
            sheet.write(idx, fieldIdx, exp.evaluate(feat))

    def write_rowvals_to_excel(self, sheet, idx, vals, ignore=None):
        if ignore is None:
            ignore = []
        for i, v in enumerate(vals):
            if i not in ignore:
                sheet.write(idx, i, v)

    def update_excel_programmatically(self):

        status_msgs = []

        rb = open_workbook(self.excelPath, formatting_info=True)
        r_sheet = rb.sheet_by_name(self.excelSheetName)  # read only copy
        wb = xlwt.Workbook()
        w_sheet = wb.add_sheet(self.excelSheetName, cell_overwrite_ok=True)
        write_idx = 0

        for row_index in range(r_sheet.nrows):
            if row_index < self.skipLines:  # copy header and/or dummy lines
                vals = r_sheet.row_values(row_index)
                self.write_rowvals_to_excel(w_sheet, write_idx, vals)
                write_idx += 1
                continue
            # print(r_sheet.cell(row_index,1).value)
            fk = r_sheet.cell(row_index, self.excelFkIdx).value
            if fk in self.shpRemove:
                status_msgs.append("Removing feature with id {}".format(fk))
                continue
            if fk in self.shpChange.keys():
                status_msgs.append(
                    "Syncing geometry change to feature with id {}".format(fk))
                shpf = self.shpChange[fk]
                self.write_feature_to_excel(w_sheet, write_idx, shpf)
                vals = r_sheet.row_values(row_index)
                self.write_rowvals_to_excel(w_sheet,
                                            write_idx,
                                            vals,
                                            ignore=self.get_ignore_indices())
            else:  # else just copy the row
                vals = r_sheet.row_values(row_index)
                self.write_rowvals_to_excel(w_sheet, write_idx, vals)

            write_idx += 1

        fidToId = {}
        for shpf in self.shpAdd:
            status_msgs.append("Adding new feature with id {}".format(
                shpf.attribute(self.shpKeyName)))
            fidToId[shpf.id()] = shpf.attribute(self.shpKeyName)
            self.write_feature_to_excel(w_sheet, write_idx, shpf)
            write_idx += 1

        info('\n'.join(status_msgs))
        wb.save(self.excelPath)
        if status_msgs:
            show_message_bar(status_msgs)
        else:
            show_message_bar("No changes to shapefile to sync.")
        return fidToId

    def clear_edit_state(self):
        info("Clearing edit state")
        self.shpAdd = []
        self.shpChange = {}
        self.shpRemove = Set([])

    def update_excel_from_shp(self):
        info("Will now update excel from edited shapefile")
        info("changing:" + str(self.shpChange))
        info("adding:" + str(self.shpAdd))
        info("removing" + str(self.shpRemove))
        self.deactivateFileWatcher()  # so that we don't sync back and forth
        fidToId = self.update_excel_programmatically()
        # need to alter the ids(not fids) of the new features after, because
        # editing the features after they've been commited doesn't work
        if fidToId:
            self.deactivateShpConnections()
            self.renameIds(fidToId)
            self.activateShpConnections()
        self.reload_excel()
        self.activateFileWatcher()
        self.clear_edit_state()

    def updateShpLayer(self, fksToRemove):
        if not fksToRemove:
            return

        prompt_msg = "Attempt to synchronize between Excel and Shapefile. Shapefile has features with ids: ({}) that don't appear in the Excel. Delete those features from the shapefile? ".format(
            ','.join([str(fk) for fk in fksToRemove]))
        reply = QtGui.QMessageBox.question(iface.mainWindow(), 'Message',
                                           prompt_msg, QtGui.QMessageBox.Yes,
                                           QtGui.QMessageBox.No)

        if reply == QtGui.QMessageBox.Yes:
            layer = layer_from_name(self.shpName)
            feats = [f for f in layer.getFeatures()]
            layer.startEditing()
            for f in feats:
                if f.attribute(self.shpKeyName) in fksToRemove:
                    layer.deleteFeature(f.id())
            layer.commitChanges()
        else:
            return

    def update_shp_from_excel(self):
        excelFks = Set(
            get_fk_set(self.excelName,
                       self.excelKeyName,
                       skipFirst=self.skipLines))
        shpFks = Set(get_fk_set(self.shpName, self.shpKeyName, skipFirst=0))
        # TODO also special warning if shp layer is in edit mode
        info("Keys in excel" + str(excelFks))
        info("Keys in shp" + str(shpFks))
        if shpFks == excelFks:
            info("Excel and Shp layer have the same rows. No update necessary")
            return
        inShpButNotInExcel = shpFks - excelFks
        inExcelButNotInShp = excelFks - shpFks
        if inExcelButNotInShp:
            warn(
                "There are rows in the excel file with no matching geometry {}."
                .format(inExcelButNotInShp))
            # FIXME: if those are added later then they will be added twice..
            # However, having an autoincrement id suggests features would be
            # added first from shp only?

        if inShpButNotInExcel:
            self.updateShpLayer(inShpButNotInExcel)

    def activateFileWatcher(self):
        self.filewatcher = QFileSystemWatcher([self.excelPath])
        self.filewatcher.fileChanged.connect(self.excel_changed)

    def deactivateFileWatcher(self):
        self.filewatcher.fileChanged.disconnect(self.excel_changed)
        self.filewatcher.removePath(self.excelPath)

    def activateShpConnections(self):
        shpLayer = layer_from_name(self.shpName)
        shpLayer.committedFeaturesAdded.connect(self.added_geom)
        shpLayer.featuresDeleted.connect(self.removed_geom_precommit)
        shpLayer.committedGeometriesChanges.connect(self.changed_geom)
        shpLayer.editingStopped.connect(self.update_excel_from_shp)
        shpLayer.beforeRollBack.connect(self.clear_edit_state)

    def deactivateShpConnections(self):
        shpLayer = layer_from_name(self.shpName)
        shpLayer.committedFeaturesAdded.disconnect(self.added_geom)
        # shpLayer.featureAdded.disconnect(added_geom_precommit)
        shpLayer.featuresDeleted.disconnect(self.removed_geom_precommit)
        shpLayer.committedGeometriesChanges.disconnect(self.changed_geom)
        shpLayer.editingStopped.disconnect(self.update_excel_from_shp)
        shpLayer.beforeRollBack.disconnect(self.clear_edit_state)

    def initialSync(self):
        info("Initial Syncing excel to shp")
        self.update_shp_from_excel()
        self.activateFileWatcher()
        self.activateShpConnections()

    def __del__(self):
        self.deactivateFileWatcher()
        self.deactivateShpConnections()
예제 #6
0
class Syncer(QObject):

    def __init__(self, settings):
        QObject.__init__(self)
        self.s = settings
        self.filewatcher = None
        self.excelName = settings.excelName  # the layer name
        self.excelSheetName = settings.excelSheetName
        self.excelKeyName = settings.excelKeyName
        self.excelFkIdx = field_idx_from_name(
            self.excelName, self.excelKeyName)
        self.excelPath = layer_from_name(self.excelName).publicSource()
        self.excelKeyName = field_name_from_idx(
            self.excelName, self.excelFkIdx)
        # shpfile layer
        self.shpName = settings.shpName
        self.shpKeyName = settings.shpKeyName
        self.shpKeyIdx = field_idx_from_name(self.shpName, self.shpKeyName)
        self.skipLines = settings.skipLines
        layer_from_name(self.shpName).setFeatureFormSuppress(QgsVectorLayer.SuppressOn if settings.hideDialog else QgsVectorLayer.SuppressOff)

        self.join()
        self.clear_edit_state()
        self.initialSync()

    def join(self):
        # join the shp layer to the excel layer, non cached
        # TODO: Ignore if already joined?
        shpLayer = layer_from_name(self.shpName)
        jinfo = QgsVectorJoinInfo()
        jinfo.joinFieldName = self.excelKeyName
        jinfo.targetFieldName = self.shpKeyName
        jinfo.joinLayerId = layer_from_name(self.excelName).id()
        jinfo.memoryCache = False
        jinfo.prefix = ''
        for jinfo2 in shpLayer.vectorJoins():
            if jinfo2 == jinfo:
                info("Join already exists. Will not create it again")
                return
        info("Adding join between master and slave layers")
        shpLayer.addJoin(jinfo)

    def reload_excel(self):
        path = self.excelPath
        layer = layer_from_name(self.excelName)
        fsize = os.stat(self.excelPath).st_size
        info("fsize " + str(fsize))
        if fsize == 0:
            info("File empty. Won't reload yet")
            return
        layer.dataProvider().forceReload()
        show_message_bar("Excel reloaded from disk.")

    def excel_changed(self):
        info("Excel changed on disk - need to sync")
        self.reload_excel()
        self.update_shp_from_excel()

    def get_max_id(self):
    
        layer = layer_from_name(self.shpName)
        if layer.dataProvider().featureCount() == 0:
            return 0
        maxVal = layer.maximumValue(self.shpKeyIdx)
        if maxVal == None:
            return 0
        else:
            return maxVal

    def renameIds(self, fidToId):
        layer = layer_from_name(self.shpName)
        layer.startEditing()
        feats = query_layer_for_fids(self.shpName, fidToId.keys())
        for f in feats:
            res = layer.changeAttributeValue(
                f.id(), self.shpKeyIdx, fidToId[f.id()])
        layer.commitChanges()

    def added_geom(self, layerId, feats):
        info("added feats " + str(feats))
        layer = layer_from_name(self.shpName)
        maxFk = self.get_max_id()
        for i, _ in enumerate(feats):
            _id = maxFk + i + 1
            feats[i].setAttribute(self.shpKeyName, _id)

        self.shpAdd = feats

    def removed_geom_precommit(self, fids):
        #info("Removed fids"+str(fids))
        fks_to_remove = get_fk_set(
            self.shpName, self.shpKeyName, skipFirst=0, fids=fids, useProvider=True)
        self.shpRemove = self.shpRemove.union(fks_to_remove)
        info("feat ids to remove" + str(self.shpRemove))

    def changed_geom(self, layerId, geoms):
        fids = geoms.keys()
        feats = query_layer_for_fids(self.shpName, fids)
        fks_to_change = get_fk_set(
            self.shpName, self.shpKeyName, skipFirst=0, fids=fids)
        self.shpChange = {k: v for (k, v) in zip(fks_to_change, feats)}
        # info("changed"+str(shpChange))

    def get_ignore_indices(self):
        return [field_idx_from_name(self.excelName, field) for field in self.s.expressions.keys()]

    def write_feature_to_excel(self, sheet, idx, feat):
        sheet.write(idx, self.excelFkIdx, feat[self.shpKeyName])
        for (fieldName, exp) in self.s.expressions.iteritems():
            fieldIdx = field_idx_from_name(self.excelName, fieldName)
            exp = QgsExpression(exp)
            sheet.write(idx, fieldIdx, exp.evaluate(feat))

    def write_rowvals_to_excel(self, sheet, idx, vals, ignore=None):
        if ignore is None:
            ignore = []
        for i, v in enumerate(vals):
            if i not in ignore:
                sheet.write(idx, i, v)

    def update_excel_programmatically(self):

        status_msgs = []

        rb = open_workbook(self.excelPath, formatting_info=True)
        r_sheet = rb.sheet_by_name(self.excelSheetName)  # read only copy
        wb = xlwt.Workbook()
        w_sheet = wb.add_sheet(self.excelSheetName, cell_overwrite_ok=True)
        write_idx = 0

        for row_index in range(r_sheet.nrows):
            if row_index < self.skipLines:  # copy header and/or dummy lines
                vals = r_sheet.row_values(row_index)
                self.write_rowvals_to_excel(w_sheet, write_idx, vals)
                write_idx += 1
                continue
            # print(r_sheet.cell(row_index,1).value)
            fk = r_sheet.cell(row_index, self.excelFkIdx).value
            if fk in self.shpRemove:
                status_msgs.append("Removing feature with id {}".format(fk))
                continue
            if fk in self.shpChange.keys():
                status_msgs.append(
                    "Syncing geometry change to feature with id {}".format(fk))
                shpf = self.shpChange[fk]
                self.write_feature_to_excel(w_sheet, write_idx, shpf)
                vals = r_sheet.row_values(row_index)
                self.write_rowvals_to_excel(w_sheet, write_idx, vals,
                                            ignore=self.get_ignore_indices())
            else:  # else just copy the row
                vals = r_sheet.row_values(row_index)
                self.write_rowvals_to_excel(w_sheet, write_idx, vals)

            write_idx += 1

        fidToId = {}
        for shpf in self.shpAdd:
            status_msgs.append(
                "Adding new feature with id {}".format(shpf.attribute(self.shpKeyName)))
            fidToId[shpf.id()] = shpf.attribute(self.shpKeyName)
            self.write_feature_to_excel(w_sheet, write_idx, shpf)
            write_idx += 1

        info('\n'.join(status_msgs))
        wb.save(self.excelPath)
        if status_msgs:
            show_message_bar(status_msgs)
        else:
            show_message_bar("No changes to shapefile to sync.")
        return fidToId

    def clear_edit_state(self):
        info("Clearing edit state")
        self.shpAdd = []
        self.shpChange = {}
        self.shpRemove = Set([])

    def update_excel_from_shp(self):
        info("Will now update excel from edited shapefile")
        info("changing:" + str(self.shpChange))
        info("adding:" + str(self.shpAdd))
        info("removing" + str(self.shpRemove))
        self.deactivateFileWatcher()  # so that we don't sync back and forth
        fidToId = self.update_excel_programmatically()
        # need to alter the ids(not fids) of the new features after, because
        # editing the features after they've been commited doesn't work
        if fidToId:
            self.deactivateShpConnections()
            self.renameIds(fidToId)
            self.activateShpConnections()
        self.reload_excel()
        self.activateFileWatcher()
        self.clear_edit_state()

    def updateShpLayer(self, fksToRemove):
        if not fksToRemove:
            return

        prompt_msg = "Attempt to synchronize between Excel and Shapefile. Shapefile has features with ids: ({}) that don't appear in the Excel. Delete those features from the shapefile? ".format(
            ','.join([str(fk) for fk in fksToRemove]))
        reply = QtGui.QMessageBox.question(iface.mainWindow(), 'Message',
                                           prompt_msg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)

        if reply == QtGui.QMessageBox.Yes:
            layer = layer_from_name(self.shpName)
            feats = [f for f in layer.getFeatures()]
            layer.startEditing()
            for f in feats:
                if f.attribute(self.shpKeyName) in fksToRemove:
                    layer.deleteFeature(f.id())
            layer.commitChanges()
        else:
            return

    def update_shp_from_excel(self):
        excelFks = Set(
            get_fk_set(self.excelName, self.excelKeyName, skipFirst=self.skipLines))
        shpFks = Set(get_fk_set(self.shpName, self.shpKeyName, skipFirst=0))
        # TODO also special warning if shp layer is in edit mode
        info("Keys in excel" + str(excelFks))
        info("Keys in shp" + str(shpFks))
        if shpFks == excelFks:
            info("Excel and Shp layer have the same rows. No update necessary")
            return
        inShpButNotInExcel = shpFks - excelFks
        inExcelButNotInShp = excelFks - shpFks
        if inExcelButNotInShp:
            warn("There are rows in the excel file with no matching geometry {}.".format(
                inExcelButNotInShp))
            # FIXME: if those are added later then they will be added twice..
            # However, having an autoincrement id suggests features would be
            # added first from shp only?

        if inShpButNotInExcel:
            self.updateShpLayer(inShpButNotInExcel)

    def activateFileWatcher(self):
        self.filewatcher = QFileSystemWatcher([self.excelPath])
        self.filewatcher.fileChanged.connect(self.excel_changed)

    def deactivateFileWatcher(self):
        self.filewatcher.fileChanged.disconnect(self.excel_changed)
        self.filewatcher.removePath(self.excelPath)

    def activateShpConnections(self):
        shpLayer = layer_from_name(self.shpName)
        shpLayer.committedFeaturesAdded.connect(self.added_geom)
        shpLayer.featuresDeleted.connect(self.removed_geom_precommit)
        shpLayer.committedGeometriesChanges.connect(self.changed_geom)
        shpLayer.editingStopped.connect(self.update_excel_from_shp)
        shpLayer.beforeRollBack.connect(self.clear_edit_state)

    def deactivateShpConnections(self):
        shpLayer = layer_from_name(self.shpName)
        shpLayer.committedFeaturesAdded.disconnect(self.added_geom)
        # shpLayer.featureAdded.disconnect(added_geom_precommit)
        shpLayer.featuresDeleted.disconnect(self.removed_geom_precommit)
        shpLayer.committedGeometriesChanges.disconnect(self.changed_geom)
        shpLayer.editingStopped.disconnect(self.update_excel_from_shp)
        shpLayer.beforeRollBack.disconnect(self.clear_edit_state)

    def initialSync(self):
        info("Initial Syncing excel to shp")
        self.update_shp_from_excel()
        self.activateFileWatcher()
        self.activateShpConnections()

    def __del__(self):
        self.deactivateFileWatcher()
        self.deactivateShpConnections()
예제 #7
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)
예제 #8
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()
예제 #9
0
class TreeProjectsWidget(QTreeWidget):

###############################################################################
# TreeProjectsWidget SIGNALS
###############################################################################

    """
    runProject()
    closeProject(QString)
    addProjectToConsole(QString)
    removeProjectFromConsole(QString)
    """

###############################################################################

    #Extra context menu 'all' indicate a menu for ALL LANGUAGES!
    EXTRA_MENUS = {'all': []}
    images = {
        'py': resources.IMAGES['tree-python'],
        'java': resources.IMAGES['tree-java'],
        'fn': resources.IMAGES['tree-code'],
        'c': resources.IMAGES['tree-code'],
        'cs': resources.IMAGES['tree-code'],
        'jpg': resources.IMAGES['tree-image'],
        'png': resources.IMAGES['tree-image'],
        'html': resources.IMAGES['tree-html'],
        'css': resources.IMAGES['tree-css'],
        'ui': resources.IMAGES['designer']}

    def __init__(self):
        QTreeWidget.__init__(self)

        self.header().setHidden(True)
        self.setSelectionMode(QTreeWidget.SingleSelection)
        self.setAnimated(True)

        self._actualProject = None
        self._projects = {}
        self.__enableCloseNotification = True
        self._fileWatcher = QFileSystemWatcher()

        self.header().setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
        self.header().setResizeMode(0, QHeaderView.ResizeToContents)
        self.header().setStretchLastSection(False)

        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.connect(self, SIGNAL(
            "customContextMenuRequested(const QPoint &)"),
            self._menu_context_tree)
        self.connect(self, SIGNAL("itemClicked(QTreeWidgetItem *, int)"),
            self._open_file)
        self.connect(self._fileWatcher, SIGNAL("directoryChanged(QString)"),
            self._refresh_project_by_path)

    def add_extra_menu(self, menu, lang='all'):
        '''
        Add an extra menu for the given language
        @lang: string with the form 'py', 'php', 'json', etc
        '''
        #remove blanks and replace dots Example(.py => py)
        lang = lang.strip().replace('.', '')
        self.EXTRA_MENUS.setdefault(lang, [])
        self.EXTRA_MENUS[lang].append(menu)

    def _menu_context_tree(self, point):
        index = self.indexAt(point)
        if not index.isValid():
            return

        item = self.itemAt(point)
        handler = None
        menu = QMenu(self)
        if item.isFolder or item.parent() is None:
            action_add_file = menu.addAction(QIcon(resources.IMAGES['new']),
                self.tr("Add New File"))
            self.connect(action_add_file, SIGNAL("triggered()"),
                self._add_new_file)
            action_add_folder = menu.addAction(QIcon(
                resources.IMAGES['openProj']), self.tr("Add New Folder"))
            self.connect(action_add_folder, SIGNAL("triggered()"),
                self._add_new_folder)
            action_create_init = menu.addAction(
                self.tr("Create '__init__' Complete"))
            self.connect(action_create_init, SIGNAL("triggered()"),
                self._create_init)
            if item.isFolder and (item.parent() != None):
                action_remove_folder = menu.addAction(self.tr("Remove Folder"))
                self.connect(action_remove_folder, SIGNAL("triggered()"),
                    self._delete_folder)
        elif not item.isFolder:
            action_rename_file = menu.addAction(self.tr("Rename File"))
            action_move_file = menu.addAction(self.tr("Move File"))
            action_copy_file = menu.addAction(self.tr("Copy File"))
            action_remove_file = menu.addAction(
                self.style().standardIcon(QStyle.SP_DialogCloseButton),
                self.tr("Delete File"))
            self.connect(action_remove_file, SIGNAL("triggered()"),
                self._delete_file)
            self.connect(action_rename_file, SIGNAL("triggered()"),
                self._rename_file)
            self.connect(action_copy_file, SIGNAL("triggered()"),
                self._copy_file)
            self.connect(action_move_file, SIGNAL("triggered()"),
                self._move_file)
            #menu per file language!
            for m in self.EXTRA_MENUS.get(item.lang(), ()):
                menu.addSeparator()
                menu.addMenu(m)
        if item.parent() is None:
            menu.addSeparator()
            actionRunProject = menu.addAction(QIcon(
                resources.IMAGES['play']), self.tr("Run Project"))
            self.connect(actionRunProject, SIGNAL("triggered()"),
                SIGNAL("runProject()"))
            actionMainProject = menu.addAction(self.tr("Set as Main Project"))
            self.connect(actionMainProject, SIGNAL("triggered()"),
                lambda: self.set_default_project(item))
            if item.addedToConsole:
                actionRemoveFromConsole = menu.addAction(
                    self.tr("Remove this Project from the Python Console"))
                self.connect(actionRemoveFromConsole, SIGNAL("triggered()"),
                    self._remove_project_from_console)
            else:
                actionAdd2Console = menu.addAction(
                    self.tr("Add this Project to the Python Console"))
                self.connect(actionAdd2Console, SIGNAL("triggered()"),
                    self._add_project_to_console)
            actionProperties = menu.addAction(QIcon(resources.IMAGES['pref']),
                self.tr("Project Properties"))
            self.connect(actionProperties, SIGNAL("triggered()"),
                self.open_project_properties)
            #get the extra context menu for this projectType
            handler = settings.get_project_type_handler(item.projectType)
#            if handler:
#                for m in handler.get_context_menus():
#                    menu.addSeparator()
#                    menu.addMenu(m)

            menu.addSeparator()
            action_refresh = menu.addAction(
                self.style().standardIcon(QStyle.SP_BrowserReload),
                self.tr("Refresh Project"))
            self.connect(action_refresh, SIGNAL("triggered()"),
                self._refresh_project)
            action_close = menu.addAction(
                self.style().standardIcon(QStyle.SP_DialogCloseButton),
                self.tr("Close Project"))
            self.connect(action_close, SIGNAL("triggered()"),
                self._close_project)

        #menu for all items!
        for m in self.EXTRA_MENUS.get('all', ()):
            menu.addSeparator()
            menu.addMenu(m)

        #menu for the Project Type(if present)
        if handler:
            for m in handler.get_context_menus():
                menu.addSeparator()
                menu.addMenu(m)
        #show the menu!
        menu.exec_(QCursor.pos())

    def _add_project_to_console(self):
        item = self.currentItem()
        if isinstance(item, ProjectTree):
            self.emit(SIGNAL("addProjectToConsole(QString)"), item.path)
            item.addedToConsole = True

    def _remove_project_from_console(self):
        item = self.currentItem()
        if isinstance(item, ProjectTree):
            self.emit(SIGNAL("removeProjectFromConsole(QString)"), item.path)
            item.addedToConsole = False

    def _open_file(self, item, column):
        if item.childCount() == 0 and not item.isFolder:
            fileName = os.path.join(item.path, unicode(item.text(column)))
            main_container.MainContainer().open_file(fileName)

    def _get_project_root(self):
        item = self.currentItem()
        while item is not None and item.parent() is not None:
            item = item.parent()
        return item

    def set_default_project(self, item):
        item.setForeground(0, QBrush(QColor(0, 204, 82)))
        if self._actualProject:
            self._actualProject.setForeground(0, QBrush(Qt.darkGray))
        self._actualProject = item

    def open_project_properties(self):
        item = self._get_project_root()
        proj = project_properties_widget.ProjectProperties(item, self)
        proj.show()

    def _refresh_project_by_path(self, project_folder):
        project_folder = unicode(project_folder)
        project = [path for path in self._projects if \
            file_manager.belongs_to_folder(path, project_folder)]
        if project:
            item = self._projects[unicode(project[0])]
            self._refresh_project(item)

    def _refresh_project(self, item=None):
        if item is None:
            item = self.currentItem()
        item.takeChildren()
        parentItem = self._get_project_root()
        if parentItem is None:
            return
        if item.parent() is None:
            path = item.path
        else:
            path = file_manager.create_path(item.path, unicode(item.text(0)))
        if parentItem.extensions != settings.SUPPORTED_EXTENSIONS:
            folderStructure = file_manager.open_project_with_extensions(
                path, parentItem.extensions)
        else:
            folderStructure = file_manager.open_project(path)
        if folderStructure[path][1] is not None:
            folderStructure[path][1].sort()
        else:
            return
        self._load_folder(folderStructure, path, item)
        item.setExpanded(True)

    def _close_project(self):
        item = self.currentItem()
        index = self.indexOfTopLevelItem(item)
        pathKey = item.path
        if self.__enableCloseNotification:
            self.emit(SIGNAL("closeProject(QString)"), pathKey)
        self._fileWatcher.removePath(pathKey)
        self.takeTopLevelItem(index)
        self._projects.pop(pathKey)
        item = self.currentItem()
        if item:
            self.set_default_project(item)

    def _create_init(self):
        item = self.currentItem()
        if item.parent() is None:
            pathFolder = item.path
        else:
            pathFolder = os.path.join(item.path, str(item.text(0)))
        try:
            file_manager.create_init_file_complete(pathFolder)
        except file_manager.NinjaFileExistsException, ex:
            QMessageBox.information(self, self.tr("Create INIT fail"),
                ex.message)
        self._refresh_project(item)
예제 #10
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
예제 #11
0
class TreeProjectsWidget(QTreeWidget):

###############################################################################
# TreeProjectsWidget SIGNALS
###############################################################################

    """
    runProject()
    closeProject(QString)
    closeFilesFromProjectClosed(QString)
    addProjectToConsole(QString)
    removeProjectFromConsole(QString)
    """

###############################################################################

    #Extra context menu 'all' indicate a menu for ALL LANGUAGES!
    EXTRA_MENUS = {'all': []}
    images = {
        'py': resources.IMAGES['tree-python'],
        'java': resources.IMAGES['tree-java'],
        'fn': resources.IMAGES['tree-code'],
        'c': resources.IMAGES['tree-code'],
        'cs': resources.IMAGES['tree-code'],
        'jpg': resources.IMAGES['tree-image'],
        'png': resources.IMAGES['tree-image'],
        'html': resources.IMAGES['tree-html'],
        'css': resources.IMAGES['tree-css'],
        'ui': resources.IMAGES['designer']}

    def __init__(self):
        QTreeWidget.__init__(self)

        self.header().setHidden(True)
        self.setSelectionMode(QTreeWidget.SingleSelection)
        self.setAnimated(True)

        self._actualProject = None
        #self._projects -> key: [Item, folderStructure]
        self._projects = {}
        self.__enableCloseNotification = True
        self._fileWatcher = QFileSystemWatcher()

        self.header().setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
        self.header().setResizeMode(0, QHeaderView.ResizeToContents)
        self.header().setStretchLastSection(False)

        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.connect(self, SIGNAL(
            "customContextMenuRequested(const QPoint &)"),
            self._menu_context_tree)
        self.connect(self, SIGNAL("itemClicked(QTreeWidgetItem *, int)"),
            self._open_file)
        self.connect(self._fileWatcher, SIGNAL("directoryChanged(QString)"),
            self._refresh_project_by_path)

    def add_extra_menu(self, menu, lang='all'):
        '''
        Add an extra menu for the given language
        @lang: string with the form 'py', 'php', 'json', etc
        '''
        #remove blanks and replace dots Example(.py => py)
        lang = lang.strip().replace('.', '')
        self.EXTRA_MENUS.setdefault(lang, [])
        self.EXTRA_MENUS[lang].append(menu)

    def _menu_context_tree(self, point):
        index = self.indexAt(point)
        if not index.isValid():
            return

        item = self.itemAt(point)
        handler = None
        menu = QMenu(self)
        if item.isFolder or item.parent() is None:
            action_add_file = menu.addAction(QIcon(resources.IMAGES['new']),
                self.tr("Add New File"))
            self.connect(action_add_file, SIGNAL("triggered()"),
                self._add_new_file)
            action_add_folder = menu.addAction(QIcon(
                resources.IMAGES['openProj']), self.tr("Add New Folder"))
            self.connect(action_add_folder, SIGNAL("triggered()"),
                self._add_new_folder)
            action_create_init = menu.addAction(
                self.tr("Create '__init__' Complete"))
            self.connect(action_create_init, SIGNAL("triggered()"),
                self._create_init)
            if item.isFolder and (item.parent() != None):
                action_remove_folder = menu.addAction(self.tr("Remove Folder"))
                self.connect(action_remove_folder, SIGNAL("triggered()"),
                    self._delete_folder)
        elif not item.isFolder:
            action_rename_file = menu.addAction(self.tr("Rename File"))
            action_move_file = menu.addAction(self.tr("Move File"))
            action_copy_file = menu.addAction(self.tr("Copy File"))
            action_remove_file = menu.addAction(
                self.style().standardIcon(QStyle.SP_DialogCloseButton),
                self.tr("Delete File"))
            self.connect(action_remove_file, SIGNAL("triggered()"),
                self._delete_file)
            self.connect(action_rename_file, SIGNAL("triggered()"),
                self._rename_file)
            self.connect(action_copy_file, SIGNAL("triggered()"),
                self._copy_file)
            self.connect(action_move_file, SIGNAL("triggered()"),
                self._move_file)
            #Allow to edit Qt UI files with the appropiate program
            if item.lang() == 'ui':
                action_edit_ui_file = menu.addAction(self.tr("Edit UI File"))
                self.connect(action_edit_ui_file, SIGNAL("triggered()"),
                    self._edit_ui_file)
            #menu per file language!
            for m in self.EXTRA_MENUS.get(item.lang(), ()):
                menu.addSeparator()
                menu.addMenu(m)
        if item.parent() is None:
            menu.addSeparator()
            actionRunProject = menu.addAction(QIcon(
                resources.IMAGES['play']), self.tr("Run Project"))
            self.connect(actionRunProject, SIGNAL("triggered()"),
                SIGNAL("runProject()"))
            actionMainProject = menu.addAction(self.tr("Set as Main Project"))
            self.connect(actionMainProject, SIGNAL("triggered()"),
                lambda: self.set_default_project(item))
            if item.addedToConsole:
                actionRemoveFromConsole = menu.addAction(
                    self.tr("Remove this Project from the Python Console"))
                self.connect(actionRemoveFromConsole, SIGNAL("triggered()"),
                    self._remove_project_from_console)
            else:
                actionAdd2Console = menu.addAction(
                    self.tr("Add this Project to the Python Console"))
                self.connect(actionAdd2Console, SIGNAL("triggered()"),
                    self._add_project_to_console)
            actionProperties = menu.addAction(QIcon(resources.IMAGES['pref']),
                self.tr("Project Properties"))
            self.connect(actionProperties, SIGNAL("triggered()"),
                self.open_project_properties)
            #get the extra context menu for this projectType
            handler = settings.get_project_type_handler(item.projectType)

            menu.addSeparator()
            action_refresh = menu.addAction(
                self.style().standardIcon(QStyle.SP_BrowserReload),
                self.tr("Refresh Project"))
            self.connect(action_refresh, SIGNAL("triggered()"),
                self._refresh_project)
            action_close = menu.addAction(
                self.style().standardIcon(QStyle.SP_DialogCloseButton),
                self.tr("Close Project"))
            self.connect(action_close, SIGNAL("triggered()"),
                self._close_project)

        #menu for all items!
        for m in self.EXTRA_MENUS.get('all', ()):
            menu.addSeparator()
            menu.addMenu(m)

        #menu for the Project Type(if present)
        if handler:
            for m in handler.get_context_menus():
                menu.addSeparator()
                menu.addMenu(m)
        #show the menu!
        menu.exec_(QCursor.pos())

    def _add_project_to_console(self):
        item = self.currentItem()
        if isinstance(item, ProjectTree):
            self.emit(SIGNAL("addProjectToConsole(QString)"), item.path)
            item.addedToConsole = True

    def _remove_project_from_console(self):
        item = self.currentItem()
        if isinstance(item, ProjectTree):
            self.emit(SIGNAL("removeProjectFromConsole(QString)"), item.path)
            item.addedToConsole = False

    def _open_file(self, item, column):
        if item.childCount() == 0 and not item.isFolder:
            fileName = os.path.join(item.path, unicode(item.text(column)))
            main_container.MainContainer().open_file(fileName)

    def _get_project_root(self, item=None):
        if item is None:
            item = self.currentItem()
        while item is not None and item.parent() is not None:
            item = item.parent()
        return item

    def set_default_project(self, item):
        item.setForeground(0, QBrush(QColor(0, 204, 82)))
        if self._actualProject:
            self._actualProject.setForeground(0, QBrush(Qt.darkGray))
        self._actualProject = item

    def open_project_properties(self):
        item = self._get_project_root()
        proj = project_properties_widget.ProjectProperties(item, self)
        proj.show()

    def _refresh_project_by_path(self, folder):
        item = self.get_item_for_path(unicode(folder))
        self._refresh_project(item)

    def _refresh_project(self, item=None):
        if item is None:
            item = self.currentItem()
        parentItem = self._get_project_root(item)
        if parentItem is None:
            return
        if item.parent() is None:
            path = item.path
        else:
            path = file_manager.create_path(item.path, unicode(item.text(0)))
        if parentItem.extensions != settings.SUPPORTED_EXTENSIONS:
            folderStructure = file_manager.open_project_with_extensions(
                path, parentItem.extensions)
        else:
            folderStructure = file_manager.open_project(path)
        if folderStructure.get(path, [None, None])[1] is not None:
            folderStructure[path][1].sort()
        else:
            return
        item.takeChildren()
        self._load_folder(folderStructure, path, item)
        item.setExpanded(True)

    def _close_project(self):
        item = self.currentItem()
        index = self.indexOfTopLevelItem(item)
        pathKey = item.path
        for directory in self._fileWatcher.directories():
            directory = unicode(directory)
            if file_manager.belongs_to_folder(pathKey, directory):
                self._fileWatcher.removePath(directory)
        self._fileWatcher.removePath(pathKey)
        self.takeTopLevelItem(index)
        self._projects.pop(pathKey)
        if self.__enableCloseNotification:
            self.emit(SIGNAL("closeProject(QString)"), pathKey)
        self.emit(SIGNAL("closeFilesFromProjectClosed(QString)"), pathKey)
        item = self.currentItem()
        if item:
            self.set_default_project(item)

    def _create_init(self):
        item = self.currentItem()
        if item.parent() is None:
            pathFolder = item.path
        else:
            pathFolder = os.path.join(item.path, str(item.text(0)))
        try:
            file_manager.create_init_file_complete(pathFolder)
        except file_manager.NinjaFileExistsException, ex:
            QMessageBox.information(self, self.tr("Create INIT fail"),
                ex.message)
        self._refresh_project(item)
예제 #12
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)
예제 #13
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)
예제 #14
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)
예제 #15
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)
예제 #16
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)