Пример #1
0
class Tasks(QObject):
    def __init__(self, ar, process_result):
        super(Tasks, self).__init__()
        self.ar = ar
        self.process_result = process_result
        self.pool = QThreadPool()
        self.pool.setMaxThreadCount(10)

    # def process_result(self, rev):
    #     # print 'Receiving', rev
    #     self.ref.setText(rev)

    def start(self):
        self.factory = GenerWork(self.ar)

        self.factory.generateWorkers()
        workers = self.factory.get_workers()

        # print workers
        for worker in workers:
            worker.signals.result.connect(self.process_result)

            self.pool.start(worker)

        self.pool.waitForDone()
        return data

    def get_shell_data(self):
        return self.factory.get_shell_data()
Пример #2
0
class AsyncPoolController(AsyncAbstractController):
    def __init__(self,
      # A number *n* to create a pool of *n* simple threads, where each thread
      # lacks an event loop, so that it can emit but not receive signals.
      # This means that ``g`` may **not** be run in a thread of this pool,
      # without manually adding a event loop. If *n* < 1, the global thread pool
      # is used.
      maxThreadCount,

      # |parent|
      parent=None):

        super(AsyncPoolController, self).__init__(parent)
        if maxThreadCount < 1:
            self.threadPool = QThreadPool.globalInstance()
        else:
            self.threadPool = QThreadPool()
            self.threadPool.setMaxThreadCount(maxThreadCount)

    # |_start|
    def _start(self, future):
        # Asynchronously invoke ``f``.
        apw = _AsyncPoolWorker(future)
        self.threadPool.start(apw)

    # |terminate|
    def _terminate(self):
        self.threadPool.waitForDone()
        del self.threadPool
Пример #3
0
class PROCESS(QObject):
    queue_finished = pyqtSignal(int)
    prog_finished = pyqtSignal(tuple)
    stoped = pyqtSignal(int, int)  # 1: start, 0: finished, 2: error | queue
    state = pyqtSignal(int, str)

    def __init__(self, parent=None):
        QObject.__init__(self, parent)
        self.process = QProcess()
        self.process.setProcessChannelMode(QProcess.MergedChannels)
        self.process.finished.connect(self.emit_finished)
        self.process.stateChanged.connect(self.emit_state)
        self.process_type = 0  # 0 process, 1 runnable
        self.threadpool = QThreadPool()
        self.worker = Worker()
        self.worker.signals.state.connect(self.emit_state)
        self.queue = None

    def emit_state(self, state):
        self.state.emit(state, self.prog_name)

    def emit_finished(self, exitcode, exitstatus):
        self.prog_finished.emit((self.prog_name, exitcode, exitstatus))

    def set_queue(self, queue):
        self.queue = queue

    def start_process(self):
        try:
            obj = self.queue.get(False)
            self.prog_name = obj.keys()[0]
            if callable(obj.values()[0][0]):
                self.process_type = 1
                funct = obj.values()[0][0]
                args = obj.values()[0][1]
                self.worker.insert_function(funct, args, self.prog_name)
                self.threadpool.start(self.worker)
            else:
                self.process_type = 0
                self.process.start(obj.values()[0][0], obj.values()[0][1])
        except Queue.Empty:
            self.queue_finished.emit(self.queue.name)

    def force_finished(self):
        # for process (programs)
        self.stoped.emit(1, self.queue.name)
        if self.process_type == 0:
            self.process.terminate()
            if not self.process.waitForFinished(1000):
                self.process.kill()
        else:
            if self.threadpool.activeThreadCount():
                self.threadpool.clear()
                self.threadpool.waitForDone()
        with self.queue.mutex:
            self.queue.queue.clear()
        self.stoped.emit(0, self.queue.name)
Пример #4
0
    def start(self):
        self.idmap = {}
        self.entries = []

        pool = QThreadPool()
        pool.setMaxThreadCount(1)

        for label in LABELS:
            feed = Feed(label, self)
            pool.start(feed)
            imap = Imap(label, self)
            pool.start(imap)

        pool.waitForDone()
        self.done.emit()
Пример #5
0
    def start(self):
        self.idmap = {}
        self.entries = []

        pool = QThreadPool()
        pool.setMaxThreadCount(1)

        for label in LABELS:
            feed = Feed(label, self)
            pool.start(feed)
            imap = Imap(label, self)
            pool.start(imap)

        pool.waitForDone()
        self.done.emit()
Пример #6
0
class QFileWorker(object):

    def __init__(self, deletions, cls):
        """
        This class handles deletion of temporary file objects once processing
        has completed.

        It takes a reference to the class because we re-enable it's button
        because we disabled it during processing
        """

        self.deletions = deletions
        self.threadpool = QThreadPool()
        self.cls = cls
        self._start()

    def _start(self):
        deleter = QFileCleaner(self.deletions, self.cls)
        self.threadpool.start(deleter)
        self.threadpool.waitForDone()
Пример #7
0
class Validation:
    def __init__(self, iface, db, validation_dia, plugin_dir, params):
        self.iface = iface
        self.db = db
        self.params = params
        self.validation_dia = validation_dia
        self.app_root = plugin_dir
        self.open_image = QPixmap(os.path.join(self.app_root,
                                               "image",
                                               "folder_open_icon.png"))
        self.validation_dia.ui.openPushButton.setIcon(QIcon(self.open_image))
        self.validation_dia.ui.openPushButton.setToolTip("Select File")
        self.export_globals = None
        self.validation_dk = None
        self.report_to_dialog = None
        self.re = QRegExp("CheckBox")
        self.check_boxes_names = []
        self.long_task = QThreadPool(None).globalInstance()
        self.summary_tables = {}
        self.model_navigation()
        self.form_load()
        self.file_dialog = QFileDialog()
        self.summary_functions = {}
        self.report_file_path = None
        self.home_dir = os.path.expanduser('~')
        self.org_name = database.get_from_gaz_metadata(db, "owner")
        self.report = ExportValidationReport("roadNet Validation Report",
                                             self.org_name,
                                             self.db, self.iface, None)
        self.list_check_boxes = []
        self.progress_win = QProgressDialog("", None, 0, 100, self.validation_dia)
        self.progress_win.setFixedSize(380, 100)
        self.progress_win.setModal(True)
        self.progress_win.setWindowTitle("Export Validation Report")

        self.summary_runnables = {'dupStreetCheckBox': [lambda: DupStreetDesc(), 0],
                                  'notStreetEsuCheckBox': [lambda: StreetsNoEsuDesc(), 1],
                                  'notType3CheckBox': [lambda: Type3Desc(False), 2],
                                  'incFootPathCheckBox': [lambda: Type3Desc(True), 2],
                                  'dupEsuRefCheckBox': [lambda: DupEsuRef(True), 3],
                                  'notEsuStreetCheckBox': [lambda: NoLinkEsuStreets(), 4],
                                  'invCrossRefCheckBox': [lambda: InvalidCrossReferences()],
                                  'startEndCheckBox': [lambda: CheckStartEnd(), 8],
                                  'tinyEsuCheckBox': [lambda: CheckTinyEsus("esu", 1)],
                                  'notMaintReinsCheckBox': [lambda: CheckMaintReins()],
                                  'asdStartEndCheckBox': [lambda: CheckAsdCoords()],
                                  'notMaintPolysCheckBox': [lambda: MaintNoPoly(), 16],
                                  'notPolysMaintCheckBox': [lambda: PolyNoMaint()],
                                  'tinyPolysCheckBox': [lambda: CheckTinyEsus("rd_poly", 1)]}

    def init_functions(self, ref_class, tolerance):
        """
        initialise a dictionary which keys are names of check boxes
        and values are all functions that create the respective table on the screen report
        functions are lambda because they will be called upon report creation and
        int references to a dictionary in the validation_summary
        class to the associated table widget to populate
        :param ref_class: class, the class holding the relative function
        :return: void
        """

        self.summary_functions = {'dupStreetCheckBox': [lambda: ref_class.dup_street_desc(), 0],
                                  'notStreetEsuCheckBox': [lambda: ref_class.street_not_esu_desc(), 1],
                                  'notType3CheckBox': [lambda: ref_class.no_type3_desc(include_footpath=False), 2],
                                  'incFootPathCheckBox': [lambda: ref_class.no_type3_desc(include_footpath=True), 2],
                                  'dupEsuRefCheckBox': [lambda: ref_class.dup_esu_ref(), 3],
                                  'notEsuStreetCheckBox': [lambda: ref_class.no_link_esu_streets(), 4],
                                  'invCrossRefCheckBox': [lambda: ref_class.invalid_cross_references()],
                                  'startEndCheckBox': [lambda: ref_class.check_start_end(tolerance), 8],
                                  'tinyEsuCheckBox': [lambda: ref_class.check_tiny_esus("esu", 1)],
                                  'notMaintReinsCheckBox': [lambda: ref_class.check_maint_reinst()],
                                  'asdStartEndCheckBox': [lambda: ref_class.check_asd_coords()],
                                  'notMaintPolysCheckBox': [lambda: ref_class.maint_no_poly(), 16],
                                  'notPolysMaintCheckBox': [lambda: ref_class.poly_no_maint()],
                                  'tinyPolysCheckBox': [lambda: ref_class.check_tiny_esus("rd_poly", 1)]}

    def model_navigation(self):
        """
        events handler for buttons in the form
        :return: object
        """
        buttons = self.validation_dia.ui.okCancelButtons.buttons()
        buttons[0].clicked.connect(self.get_data)
        buttons[1].clicked.connect(self.close_browser)
        self.validation_dia.ui.openPushButton.clicked.connect(self.select_file)
        self.validation_dia.ui.screenRadioButton.toggled.connect(lambda: self.selection_handler(0))
        self.validation_dia.ui.fileRadioButton.toggled.connect(lambda: self.selection_handler(1))
        self.validation_dia.ui.notType3CheckBox.toggled.connect(self.check_no_type_click)
        self.validation_dia.ui.startEndCheckBox.toggled.connect(self.check_coords_click)
        self.validation_dia.ui.selectAllButton.clicked.connect(self.select_all)
        self.validation_dia.ui.clearAllButton.clicked.connect(self.clear_all)

    def form_load(self):
        # set the initial status of the form
        self.validation_dia.ui.screenRadioButton.setChecked(True)
        self.validation_dia.ui.filePathLineEdit.setEnabled(False)
        self.check_coords_click()
        self.check_no_type_click()
        self.validation_dia.ui.dupStreetCheckBox.setChecked(True)
        self.validation_dia.ui.notStreetEsuCheckBox.setChecked(True)
        self.validation_dia.ui.notType3CheckBox.setChecked(True)
        self.validation_dia.ui.dupEsuRefCheckBox.setChecked(True)
        self.validation_dia.ui.notEsuStreetCheckBox.setChecked(True)
        self.validation_dia.ui.invCrossRefCheckBox.setChecked(True)
        self.validation_dia.ui.startEndCheckBox.setChecked(False)
        self.validation_dia.ui.tinyEsuCheckBox.setChecked(True)
        if self.params['RNsrwr'].lower() == 'true':
            self.validation_dia.ui.notMaintReinsCheckBox.setChecked(True)
            self.validation_dia.ui.asdStartEndCheckBox.setChecked(True)
        if self.params['RNsrwr'].lower() == 'false':
            self.validation_dia.ui.notMaintReinsCheckBox.setChecked(False)
            self.validation_dia.ui.asdStartEndCheckBox.setChecked(False)
            self.validation_dia.ui.notMaintReinsCheckBox.setEnabled(False)
            self.validation_dia.ui.asdStartEndCheckBox.setEnabled(False)
        if self.params['RNPolyEdit'].lower() == 'true':
            self.validation_dia.ui.notMaintPolysCheckBox.setChecked(True)
            self.validation_dia.ui.notPolysMaintCheckBox.setChecked(True)
            self.validation_dia.ui.tinyPolysCheckBox.setChecked(True)
        if self.params['RNPolyEdit'].lower() == 'false':
            self.validation_dia.ui.notMaintPolysCheckBox.setChecked(False)
            self.validation_dia.ui.notPolysMaintCheckBox.setChecked(False)
            self.validation_dia.ui.tinyPolysCheckBox.setChecked(False)
            self.validation_dia.ui.notMaintPolysCheckBox.setEnabled(False)
            self.validation_dia.ui.notPolysMaintCheckBox.setEnabled(False)
            self.validation_dia.ui.tinyPolysCheckBox.setEnabled(False)

    def close_browser(self):
        # close the dialog
        self.validation_dia.close()

    def get_data(self):
        """
        produce the report according to the options specified by the user
        :return: validation report either on screen or as text file
        """
        if self.validation_dia.ui.fileRadioButton.isChecked():
            # alert the user if no path is specified
            if self.validation_dia.ui.filePathLineEdit.text() == "":
                no_path_msg_box = QMessageBox(QMessageBox.Warning, " ",
                                              "You must specify a path and filename for the report",
                                              QMessageBox.Ok, None)
                no_path_msg_box.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint)
                no_path_msg_box.exec_()
                return
            # format the path and runs the export
            val_file_path = self.validation_dia.ui.filePathLineEdit.text()
            # checks if the export directory exists and it is valid
            if not os.path.isdir(os.path.dirname(val_file_path)):
                path_invalid_msg_box = QMessageBox(QMessageBox.Warning, " ", "A valid directory must be selected",
                                                   QMessageBox.Ok, None)
                path_invalid_msg_box.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint)
                path_invalid_msg_box.exec_()
            else:
                self.report_to_file(val_file_path)
            # text file report = false, create a screen report, instantiate the report creator class
        if not self.validation_dia.ui.fileRadioButton.isChecked():
            self.report_to_screen()

    def select_file(self):
        """
        open the dialog window to select the file and print the path on the main line edit
        :return: void
        """
        self.file_dialog.setDirectory(self.home_dir)
        self.file_dialog.setFileMode(QFileDialog.ExistingFiles)
        filters = "Text files (*.txt)"
        self.file_dialog.setNameFilter(filters)
        save_file_name = self.file_dialog.getSaveFileName(self.file_dialog, "Export Validation Report",
                                                          self.home_dir,
                                                          filter="Text files (*.txt)")
        if save_file_name != "":
            self.validation_dia.ui.filePathLineEdit.setText(("{}.txt".format(save_file_name)))
        if save_file_name.endswith(".txt"):
            self.validation_dia.ui.filePathLineEdit.setText(save_file_name)

    def selection_handler(self, button_id):
        """
        change the status of the line edit according to the radio button selection
        :return:
        """
        # if screen report is selected disable line edit for the file path
        if button_id == 0:
            self.validation_dia.ui.filePathLineEdit.setEnabled(False)
            self.validation_dia.ui.openPushButton.setEnabled(False)
            self.check_boxes_names = []

        else:
            self.validation_dia.ui.filePathLineEdit.setEnabled(True)
            self.validation_dia.ui.openPushButton.setEnabled(True)
            self.check_boxes_names = []

    def check_coords_click(self):
        # show/hide tolerance metres spin box
        if self.validation_dia.ui.startEndCheckBox.isChecked():
            self.validation_dia.ui.metresSpinBox.setVisible(True)
            self.validation_dia.ui.toleranceLabel.setVisible(True)
        else:
            self.validation_dia.ui.metresSpinBox.setVisible(False)
            self.validation_dia.ui.toleranceLabel.setVisible(False)

    def check_no_type_click(self):
        # enable/disable footpath check box
        if self.validation_dia.ui.notType3CheckBox.isChecked():
            self.validation_dia.ui.incFootPathCheckBox.setEnabled(True)
        else:
            self.validation_dia.ui.notType3CheckBox.setChecked(False)
            self.validation_dia.ui.incFootPathCheckBox.setEnabled(False)

    def select_all(self):
        # reset the form to default status, select all
        self.form_load()

    def clear_all(self):
        # uncheck all checkboxes
        self.validation_dia.ui.dupStreetCheckBox.setChecked(False)
        self.validation_dia.ui.notStreetEsuCheckBox.setChecked(False)
        self.validation_dia.ui.notType3CheckBox.setChecked(False)
        self.validation_dia.ui.incFootPathCheckBox.setChecked(False)
        self.validation_dia.ui.incFootPathCheckBox.setEnabled(False)
        self.validation_dia.ui.dupEsuRefCheckBox.setChecked(False)
        self.validation_dia.ui.notEsuStreetCheckBox.setChecked(False)
        self.validation_dia.ui.invCrossRefCheckBox.setChecked(False)
        self.validation_dia.ui.startEndCheckBox.setChecked(False)
        self.validation_dia.ui.tinyEsuCheckBox.setChecked(False)
        self.validation_dia.ui.notMaintReinsCheckBox.setChecked(False)
        self.validation_dia.ui.asdStartEndCheckBox.setChecked(False)
        self.validation_dia.ui.notMaintPolysCheckBox.setChecked(False)
        self.validation_dia.ui.notPolysMaintCheckBox.setChecked(False)
        self.validation_dia.ui.tinyPolysCheckBox.setChecked(False)

    def report_to_file(self, val_file_path):
        """
        creates a text file report
        :return: void
        """
        # assign to the report class the file path property
        self.report_file_path = val_file_path
        # start writing
        # get all checked check-boxes
        if len(self.list_check_boxes) > 0:
            self.list_check_boxes = []
        self.list_check_boxes = self.validation_dia.findChildren(QCheckBox, self.re)
        if len(self.check_boxes_names) > 0:
            self.check_boxes_names = []
        for check_box in self.list_check_boxes:
            if check_box.isChecked():
                self.check_boxes_names.append(str(check_box.objectName()))
        if len(self.check_boxes_names) < 1:
            no_val_check_msg_box = QMessageBox(QMessageBox.Warning, " ",
                                               "At least one validation option must be selected", QMessageBox.Ok, None)
            no_val_check_msg_box.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint)
            no_val_check_msg_box.exec_()
            return
        tolerance = self.validation_dia.ui.metresSpinBox.value()
        if self.validation_dia.ui.startEndCheckBox.isChecked() and (tolerance is None or tolerance == 0):
            no_tol_msg_box_file = QMessageBox(QMessageBox.Warning, " ", "You must specify a tolerance",
                                              QMessageBox.Ok, None)
            no_tol_msg_box_file.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint)
            no_tol_msg_box_file.exec_()
            return
        self.progress_win.setWindowTitle("Export Validation Report")
        self.progress_win.show()
        if self.validation_dia.ui.notType3CheckBox.isChecked():
            if 'incFootPathCheckBox' in self.check_boxes_names:
                # locate the include footpath option and run just the appropriate function
                self.check_boxes_names.remove('notType3CheckBox')
        self.export_globals = InitGlobals(self.db, self.params, tolerance)
        self.long_task.setMaxThreadCount(1)
        start_report = StartReport(val_file_path, self.org_name)
        start_report.signals.result.connect(self.log_progress)
        end_report = EndReport()
        end_report.signals.result.connect(self.log_progress)
        end_report.signals.report_finished.connect(self.show_finished)
        self.long_task.start(start_report)
        for check_box_name in self.check_boxes_names:
            run_class = self.summary_runnables[check_box_name]
            runnable = run_class[0]()
            runnable.signals.result.connect(self.log_progress)
            self.long_task.start(runnable)
        self.long_task.start(end_report)

        self.list_check_boxes = []
        self.check_boxes_names = []

    @pyqtSlot()
    def show_finished(self):
        self.long_task.waitForDone()
        show_finished_msg_box = QMessageBox(QMessageBox.Information, " ",
                                            "Report successfully exported at \n {0}"
                                            .format(str(self.report_file_path))
                                            .replace("\\\\", "\\"), QMessageBox.Ok, None)
        show_finished_msg_box.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint)
        show_finished_msg_box.exec_()

    @pyqtSlot(str, int)
    def log_progress(self, task, value):
        self.progress_win.setLabelText(task)
        self.progress_win.setValue(value)

    def report_to_screen(self):
        tolerance = self.validation_dia.ui.metresSpinBox.value()
        if self.validation_dia.ui.startEndCheckBox.isChecked() and (tolerance is None or tolerance == 0):
            no_tol_msg_box_screen = QMessageBox(QMessageBox.Warning, " ", "You must specify a tolerance",
                                                QMessageBox.Ok, None)
            no_tol_msg_box_screen.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint)
            no_tol_msg_box_screen.exec_()
            return
        report_to_screen = self.report
        report_to_screen.validation_dia = self.validation_dia
        # in case the user selects a screen report immediately after the generation of
        # a file report create a new instance of the report class and None the file path
        # instantiate the report window, the parent is the main validation report dialog
        if self.validation_dk is None:
            self.validation_dk = ValidationSummaryDock(self.validation_dia)
            self.validation_dk.setWindowTitle("Validation Report Summary")
            self.validation_dk.setWindowFlags(Qt.WindowMaximizeButtonHint | Qt.WindowMinimizeButtonHint)
            rn_icon = QIcon()
            rn_icon.addPixmap(QPixmap(os.path.join(self.app_root,
                                                         "image",
                                                         "rn_logo_v2.png")))
            self.validation_dk.setWindowIcon(rn_icon)
        # instantiate the class handler (functions to create and format the screen report)
        # include the window in the instantiation
        report_to_dialog = ValidationSummary(self.validation_dk, self.iface, self.db, tolerance)
        # creates a list of the checked checkboxes to create the tables
        self.list_check_boxes = self.validation_dia.findChildren(QCheckBox, self.re)
        for check_box in self.list_check_boxes:
            if check_box.isChecked():
                self.check_boxes_names.append(str(check_box.objectName()))
        # check if at least one checkbox is checked before running the report
        if len(self.check_boxes_names) < 1:
            no_val_check_msg_box = QMessageBox(QMessageBox.Warning, " ",
                                               "At least one validation option must be selected", QMessageBox.Ok, None)
            no_val_check_msg_box.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint)
            no_val_check_msg_box.exec_()
            return
        # initialises the functions
        self.init_functions(report_to_screen, tolerance)
        # runs the functions, each function passes a list and a table reference
        # to the handler class methods that create, populate and finally show the window
        if self.validation_dia.ui.notType3CheckBox.isChecked():
            if 'incFootPathCheckBox' in self.check_boxes_names:
                # locate the include footpath option and run just the appropriate function
                self.check_boxes_names.remove('notType3CheckBox')
                report_to_dialog.include_footpath = True
        self.report.start_report()
        for check_box_name in self.check_boxes_names:
            function = self.summary_functions[check_box_name][0]
            # handles multiple results for a unique check box (cross references)
            if check_box_name == 'invCrossRefCheckBox':
                function_list = function()
                report_to_dialog.set_table(function_list[0], 5)
                report_to_dialog.set_table(function_list[1], 6)
                report_to_dialog.set_table(function_list[2], 7)
            # handles multiple results for a unique check box (tiny/empty ESUs Polys)
            elif check_box_name == 'tinyEsuCheckBox':
                function_list = function()
                report_to_dialog.set_table(function_list[0], 9)
                report_to_dialog.set_table(function_list[1], 10)
            elif check_box_name == 'tinyPolysCheckBox':
                function_list = function()
                report_to_dialog.set_table(function_list[0], 19)
                report_to_dialog.set_table(function_list[1], 20)
            # handles multiple results for a unique check box (maint/reins asd streets)
            elif check_box_name == 'notMaintReinsCheckBox':
                function_list = function()
                report_to_dialog.set_table(function_list[0], 11)
                report_to_dialog.set_table(function_list[1], 12)
            # handles multiple results for a unique check box (maint/reins asd coords)
            elif check_box_name == 'asdStartEndCheckBox':
                function_list = function()
                report_to_dialog.set_table(function_list[0], 13)
                report_to_dialog.set_table(function_list[1], 14)
                report_to_dialog.set_table(function_list[2], 15)
            # handles multiple results for a unique check box (polygons with no link, multi maintenance assignation)
            elif check_box_name == 'notPolysMaintCheckBox':
                function_list = function()
                report_to_dialog.set_table(function_list[0], 17)
                report_to_dialog.set_table(function_list[1], 18)
            # all other normal cases
            else:
                table_id = self.summary_functions[check_box_name][1]
                report_to_dialog.set_table(function(), table_id)
        self.report.end_report(self.validation_dia)
        self.report = ExportValidationReport("roadNet Validation Report",
                                             self.org_name,
                                             self.db, self.iface, None)
        report_to_dialog.show_validation_widget()
Пример #8
0
class CatalogDialogTool(QObject):
    """
    Tool for managing the search and export functionality
    """

    def __init__(self, iface, dialog_ui, bbox_tool):
        """
        Constructor for the dialog tool
        :param iface: The QGIS Interface
        :param dialog_ui: The dialog GUI
        :param bbox_tool The bounding box tool
        :return: dialog tool
        """
        QObject.__init__(self, None)
        self.iface = iface
        self.dialog_ui = dialog_ui
        self.bbox_tool = bbox_tool

        self.progress_bar = None
        self.progress_message_bar = None
        self.progress_message_bar_widget = None
        self.search_thread_pool = QThreadPool()
        self.search_lock = Lock()
        self.export_thread_pool = QThreadPool()
        self.export_lock = Lock()
        self.query = None
        self.previous_credentials = None
        self.export_file = None
        self.footprint_layer = None

        self.filters = CatalogFilters(self.dialog_ui)

        self.dialog_ui.aoi_button.clicked.connect(self.aoi_button_clicked)
        self.dialog_ui.reset_button.clicked.connect(self.reset_button_clicked)
        self.dialog_ui.export_button.clicked.connect(self.export_button_clicked)
        self.bbox_tool.released.connect(self.search)
        self.model = None

    def init_progress_bar(self, progress_max):
        """
        Sets up the progress bar for search functionality
        :return: None
        """
        if not self.progress_message_bar:
            self.progress_message_bar = self.iface.messageBar().createMessage("Querying for data")
            self.progress_bar = QProgressBar()
            self.progress_bar.setMinimum(0)
            self.progress_bar.setMaximum(progress_max)
            self.progress_bar.setAlignment(Qt.AlignLeft | Qt.AlignCenter)
            self.progress_message_bar.layout().addWidget(self.progress_bar)
            self.progress_message_bar_widget = self.iface.messageBar().pushWidget(self.progress_message_bar, self.iface.messageBar().INFO)

    def init_layers(self):
        """
        Sets up the layers for rendering the items
        :return: None
        """
        if self.footprint_layer:
            QgsMapLayerRegistry.instance().removeMapLayer(self.footprint_layer.id())

        self.footprint_layer = QgsVectorLayer("Polygon?crs=EPSG:4326", "Catalog Footprints", "memory")
        self.footprint_layer.setCrs(QgsCoordinateReferenceSystem(4326), True)
        self.footprint_layer.dataProvider().addAttributes(CatalogAcquisitionFeature.get_fields())
        QgsMapLayerRegistry.instance().addMapLayer(self.footprint_layer)

    def clear_widgets(self):
        """
        Clears the progress bar
        :return: None
        """
        self.progress_bar = None
        self.progress_message_bar = None
        if self.progress_message_bar_widget:
            self.iface.messageBar().popWidget(self.progress_message_bar_widget)
        self.progress_message_bar_widget = None

    def is_searching(self):
        """
        Check to see if the system is still searching (checks if there's work in the search thread pool)
        :return: True if searching; False otherwise
        """
        return self.get_search_active_thread_count() > 0

    def is_exporting(self):
        """
        Check to see if the system is still exporting (checks if there's work in the export thread pool)
        :return: True if searching; False otherwise
        """
        return self.get_export_active_thread_count() > 0

    def get_search_active_thread_count(self):
        """
        Gets the number of active threads in the search thread pool
        :return:
        """
        with self.search_lock:
            return self.search_thread_pool.activeThreadCount()

    def get_export_active_thread_count(self):
        """
        Gets the number of active threads in the export thread pool
        :return:
        """
        with self.export_lock:
            return self.export_thread_pool.activeThreadCount()

    def aoi_button_clicked(self):
        """
        Validates and runs the search if validation successful
        :return: None
        """
        # can't run search during export
        if self.is_exporting():
            self.iface.messageBar().pushMessage("Error", "Cannot run search while export is running.", level=QgsMessageBar.CRITICAL)
        # can't run multiple search
        elif self.is_searching():
            self.iface.messageBar().pushMessage("Error", "Cannot run a new search while a search is running.", level=QgsMessageBar.CRITICAL)
        else:
            self.bbox_tool.reset()
            self.iface.mapCanvas().setMapTool(self.bbox_tool)

    def reset_button_clicked(self):
        """
        Resets filters.
        :return: None
        """
        self.reset()

    def export_button_clicked(self):
        """
        Validates and runs the export if validation successful
        :return: None
        """
        # can't run export during search
        if self.is_searching():
            self.iface.messageBar().pushMessage("Error", "Cannot run export while search is running.", level=QgsMessageBar.CRITICAL)
        # can't run multiple exports
        elif self.is_exporting():
            self.iface.messageBar().pushMessage("Error", "Cannot run a new export while a export is running.", level=QgsMessageBar.CRITICAL)
        else:
            self.export()

    def search(self, top, bottom, left, right):
        self.search_thread_pool.waitForDone(0)

        # validate credentials if they changed
        errors = []
        username, password, api_key, max_items_to_return = SettingsOps.get_settings()
        credentials = [username, password, api_key]
        if not self.previous_credentials or self.previous_credentials != credentials:
            SettingsOps.validate_stored_info(username, password, api_key, max_items_to_return, errors)
        self.previous_credentials = credentials

        # validate filters
        if not errors:
            self.filters.validate(errors)

        if errors:
            self.iface.messageBar().pushMessage("Error", "The following errors occurred: " + "<br />".join(errors), level=QgsMessageBar.CRITICAL)
        else:
            self.init_layers()

            self.dialog_ui.tab_widget.setCurrentIndex(RESULTS_TAB_INDEX)
            
            next_x_list = self.drange_list(float(left) + INCREMENTAL_INTERVAL, float(right), INCREMENTAL_INTERVAL)
            next_y_list = self.drange_list(float(bottom) + INCREMENTAL_INTERVAL, float(top), INCREMENTAL_INTERVAL)
            self.init_progress_bar(len(next_x_list) * len(next_y_list))

            self.model = CatalogTableModel(self.dialog_ui.table_view)
            self.dialog_ui.table_view.setModel(self.model)
            self.dialog_ui.table_view.selectionModel().selectionChanged.connect(self.selection_changed)

            if not self.query:
                self.query = GBDQuery(username=username, password=password, api_key=api_key)

            filters = self.filters.get_query_filters()
            time_begin = self.filters.get_datetime_begin()
            time_end = self.filters.get_datetime_end()

            current_x = float(left)
            current_y = float(bottom)
            for next_x in next_x_list:
                for next_y in next_y_list:
                    search_runnable = CatalogSearchRunnable(self.query, self.model, self, top=next_y, left=current_x, right=next_x, bottom=current_y, 
                                                            time_begin=time_begin, time_end=time_end, filters=filters)
                    search_runnable.task_object.task_complete.connect(self.on_search_complete)
                    self.search_thread_pool.start(search_runnable)
                    current_y = next_y
                current_y = bottom
                current_x = next_x

    def reset(self):
        self.filters.remove_all()

    def export(self):
        self.export_thread_pool.waitForDone(0)
        acquisitions = None
        if self.model is not None:
            acquisitions = self.model.data

        if not acquisitions:
            self.iface.messageBar().pushMessage("Error", "No data to export.", level=QgsMessageBar.CRITICAL)
        else:
            # open file ui
            select_file_ui = QFileDialog()
            starting_file = self.export_file or os.path.expanduser("~")
            export_file = select_file_ui.getSaveFileName(None, "Choose output file", starting_file, SELECT_FILTER)

            if export_file:
                self.export_file = export_file
                self.init_progress_bar(0)
                export_runnable = CatalogExportRunnable(acquisitions, self.export_file)
                export_runnable.task_object.task_complete.connect(self.on_export_complete)
                self.export_thread_pool.start(export_runnable)

    @pyqtSlot()
    def on_search_complete(self):
        thread_count = self.get_search_active_thread_count()
        if self.progress_message_bar:
            self.progress_bar.setValue(self.progress_bar.value() + 1)
        if thread_count == 0:
            self.clear_widgets()
            self.dialog_ui.table_view.resizeColumnsToContents()

    @pyqtSlot()
    def on_export_complete(self):
        thread_count = self.get_export_active_thread_count()
        if self.progress_message_bar:
            self.progress_bar.setValue(self.progress_bar.value() + 1)
        if thread_count == 0:
            self.clear_widgets()
            self.iface.messageBar().pushMessage("Info", 'File export has completed to "%s".' % self.export_file)

    def selection_changed(self, selected, deselected):
        self.footprint_layer.startEditing()

        # draw footprints for selected rows
        selected_rows = set()
        for index in selected.indexes():
            selected_rows.add(index.row())
        for row in selected_rows:
            acquisition = self.model.get(row)
            feature_id = self.model.generate_feature_id()
            self.model.set_feature_id(acquisition, feature_id)
            feature = CatalogAcquisitionFeature(feature_id, acquisition)
            self.footprint_layer.dataProvider().addFeatures([feature])

        # remove footprints for deselected rows
        deselected_rows = set()
        for index in deselected.indexes():
            deselected_rows.add(index.row())
        feature_ids_to_remove = []
        for row in deselected_rows:
            acquisition = self.model.get(row)
            feature_id = self.model.get_feature_id(acquisition)
            feature_ids_to_remove.append(feature_id)
            self.model.remove_feature_id(acquisition)
        if feature_ids_to_remove:
            self.footprint_layer.dataProvider().deleteFeatures(feature_ids_to_remove)

        self.footprint_layer.commitChanges()
        self.footprint_layer.updateExtents()
        self.footprint_layer.triggerRepaint()

    def drange_list(self, start, stop, step):
        drange_list = []
        r = start
        while r < stop:
            drange_list.append(r)
            r += step
        if not drange_list:
            drange_list.append(stop)
        return drange_list
Пример #9
0
class WorkHandler(object):
    """
    Class to handle threading of "work" (concretely this is just the evaluation of a function of the
     form func(*args, **kwargs).

     The process ID identifies (uniquely) each instance of a Worker; but the caller also defines
     an ID which is then used to identify the Worker through the API.
    """
    class WorkListener(with_metaclass(ABCMeta, object)):
        """
        This abstract base class defines methods which must be overriden and which
        handle responses to certain worker actions such as raised errors, or completion.
        """
        def __init__(self):
            pass

        @abstractmethod
        def on_processing_finished(self, result):
            pass

        @abstractmethod
        def on_processing_error(self, error):
            pass

    def __init__(self):
        self.thread_pool = None
        self._listener = {}
        self._worker = {}
        self.thread_pool = QThreadPool()

    def _add_listener(self, listener, process_id, id):
        if not isinstance(listener, WorkHandler.WorkListener):
            raise ValueError("The listener is not of type "
                             "WorkListener but rather {}".format(
                                 type(listener)))
        self._listener.update({process_id: {'id': id, 'listener': listener}})

    @pyqtSlot()
    def on_finished(self, process_id):
        if process_id not in self._worker:
            return
        result = self._worker.pop(process_id)['worker'].result
        self._listener.pop(process_id)['listener'].on_processing_finished(
            result)

    @pyqtSlot()
    def on_error(self, process_id, error):
        if process_id not in self._worker:
            return
        self._listener[process_id]['listener'].on_processing_error(error)

    def process(self, caller, func, id, *args, **kwargs):
        """
        Process a function call with arbitrary arguments on a new thread.

        :param caller: ??
        :param func: The function to be evaluated.
        :param id: An identifying integer for the task.
        :param args: args for func.
        :param kwargs: keyword args for func.
        """
        self.remove_already_processing(id)
        process_id = uuid.uuid4()
        # Add the caller
        self._add_listener(caller, process_id, id)

        finished_callback = functools.partial(self.on_finished, process_id)
        error_callback = functools.partial(self.on_error, process_id)

        worker = Worker(func, *args, **kwargs)
        worker.signals.finished.connect(finished_callback)
        worker.signals.error.connect(error_callback)

        self._worker.update({process_id: {'id': id, 'worker': worker}})

        self.thread_pool.start(self._worker[process_id]['worker'])

    def remove_already_processing(self, id):
        """
        Remove workers with ID
        :param id:
        """
        for key, process in list(self._listener.items()):
            if process['id'] == id:
                self._listener.pop(key)
                self._worker.pop(key)

    def wait_for_done(self):
        self.thread_pool.waitForDone()

    def __eq__(self, other):
        return self.__dict__ == other.__dict__

    def __ne__(self, other):
        return self.__dict__ != other.__dict__
Пример #10
0
class CatalogDialogTool(QObject):
    """
    Tool for managing the search and export functionality
    """
    def __init__(self, iface, dialog_ui, bbox_tool):
        """
        Constructor for the dialog tool
        :param iface: The QGIS Interface
        :param dialog_ui: The dialog GUI
        :param bbox_tool The bounding box tool
        :return: dialog tool
        """
        QObject.__init__(self, None)
        self.iface = iface
        self.dialog_ui = dialog_ui
        self.bbox_tool = bbox_tool

        self.progress_bar = None
        self.progress_message_bar = None
        self.progress_message_bar_widget = None
        self.search_thread_pool = QThreadPool()
        self.search_lock = Lock()
        self.export_thread_pool = QThreadPool()
        self.export_lock = Lock()
        self.query = None
        self.previous_credentials = None
        self.export_file = None
        self.footprint_layer = None

        self.filters = CatalogFilters(self.dialog_ui)

        self.dialog_ui.aoi_button.clicked.connect(self.aoi_button_clicked)
        self.dialog_ui.reset_button.clicked.connect(self.reset_button_clicked)
        self.dialog_ui.export_button.clicked.connect(
            self.export_button_clicked)
        self.bbox_tool.released.connect(self.search)
        self.model = None

    def init_progress_bar(self, progress_max):
        """
        Sets up the progress bar for search functionality
        :return: None
        """
        if not self.progress_message_bar:
            self.progress_message_bar = self.iface.messageBar().createMessage(
                "Querying for data")
            self.progress_bar = QProgressBar()
            self.progress_bar.setMinimum(0)
            self.progress_bar.setMaximum(progress_max)
            self.progress_bar.setAlignment(Qt.AlignLeft | Qt.AlignCenter)
            self.progress_message_bar.layout().addWidget(self.progress_bar)
            self.progress_message_bar_widget = self.iface.messageBar(
            ).pushWidget(self.progress_message_bar,
                         self.iface.messageBar().INFO)

    def init_layers(self):
        """
        Sets up the layers for rendering the items
        :return: None
        """
        if self.footprint_layer:
            QgsMapLayerRegistry.instance().removeMapLayer(
                self.footprint_layer.id())

        self.footprint_layer = QgsVectorLayer("Polygon?crs=EPSG:4326",
                                              "Catalog Footprints", "memory")
        self.footprint_layer.setCrs(QgsCoordinateReferenceSystem(4326), True)
        self.footprint_layer.dataProvider().addAttributes(
            CatalogAcquisitionFeature.get_fields())
        QgsMapLayerRegistry.instance().addMapLayer(self.footprint_layer)

    def clear_widgets(self):
        """
        Clears the progress bar
        :return: None
        """
        self.progress_bar = None
        self.progress_message_bar = None
        if self.progress_message_bar_widget:
            self.iface.messageBar().popWidget(self.progress_message_bar_widget)
        self.progress_message_bar_widget = None

    def is_searching(self):
        """
        Check to see if the system is still searching (checks if there's work in the search thread pool)
        :return: True if searching; False otherwise
        """
        return self.get_search_active_thread_count() > 0

    def is_exporting(self):
        """
        Check to see if the system is still exporting (checks if there's work in the export thread pool)
        :return: True if searching; False otherwise
        """
        return self.get_export_active_thread_count() > 0

    def get_search_active_thread_count(self):
        """
        Gets the number of active threads in the search thread pool
        :return:
        """
        with self.search_lock:
            return self.search_thread_pool.activeThreadCount()

    def get_export_active_thread_count(self):
        """
        Gets the number of active threads in the export thread pool
        :return:
        """
        with self.export_lock:
            return self.export_thread_pool.activeThreadCount()

    def aoi_button_clicked(self):
        """
        Validates and runs the search if validation successful
        :return: None
        """
        # can't run search during export
        if self.is_exporting():
            self.iface.messageBar().pushMessage(
                "Error",
                "Cannot run search while export is running.",
                level=QgsMessageBar.CRITICAL)
        # can't run multiple search
        elif self.is_searching():
            self.iface.messageBar().pushMessage(
                "Error",
                "Cannot run a new search while a search is running.",
                level=QgsMessageBar.CRITICAL)
        else:
            self.bbox_tool.reset()
            self.iface.mapCanvas().setMapTool(self.bbox_tool)

    def reset_button_clicked(self):
        """
        Resets filters.
        :return: None
        """
        self.reset()

    def export_button_clicked(self):
        """
        Validates and runs the export if validation successful
        :return: None
        """
        # can't run export during search
        if self.is_searching():
            self.iface.messageBar().pushMessage(
                "Error",
                "Cannot run export while search is running.",
                level=QgsMessageBar.CRITICAL)
        # can't run multiple exports
        elif self.is_exporting():
            self.iface.messageBar().pushMessage(
                "Error",
                "Cannot run a new export while a export is running.",
                level=QgsMessageBar.CRITICAL)
        else:
            self.export()

    def search(self, top, bottom, left, right):
        self.search_thread_pool.waitForDone(0)

        # validate credentials if they changed
        errors = []
        username, password, api_key, max_items_to_return = SettingsOps.get_settings(
        )
        credentials = [username, password, api_key]
        if not self.previous_credentials or self.previous_credentials != credentials:
            SettingsOps.validate_stored_info(username, password, api_key,
                                             max_items_to_return, errors)
        self.previous_credentials = credentials

        # validate filters
        if not errors:
            self.filters.validate(errors)

        if errors:
            self.iface.messageBar().pushMessage(
                "Error",
                "The following errors occurred: " + "<br />".join(errors),
                level=QgsMessageBar.CRITICAL)
        else:
            self.init_layers()

            self.dialog_ui.tab_widget.setCurrentIndex(RESULTS_TAB_INDEX)

            next_x_list = self.drange_list(
                float(left) + INCREMENTAL_INTERVAL, float(right),
                INCREMENTAL_INTERVAL)
            next_y_list = self.drange_list(
                float(bottom) + INCREMENTAL_INTERVAL, float(top),
                INCREMENTAL_INTERVAL)
            self.init_progress_bar(len(next_x_list) * len(next_y_list))

            self.model = CatalogTableModel(self.dialog_ui.table_view)
            self.dialog_ui.table_view.setModel(self.model)
            self.dialog_ui.table_view.selectionModel(
            ).selectionChanged.connect(self.selection_changed)

            if not self.query:
                self.query = GBDQuery(username=username,
                                      password=password,
                                      api_key=api_key)

            filters = self.filters.get_query_filters()
            time_begin = self.filters.get_datetime_begin()
            time_end = self.filters.get_datetime_end()

            current_x = float(left)
            current_y = float(bottom)
            for next_x in next_x_list:
                for next_y in next_y_list:
                    search_runnable = CatalogSearchRunnable(
                        self.query,
                        self.model,
                        self,
                        top=next_y,
                        left=current_x,
                        right=next_x,
                        bottom=current_y,
                        time_begin=time_begin,
                        time_end=time_end,
                        filters=filters)
                    search_runnable.task_object.task_complete.connect(
                        self.on_search_complete)
                    self.search_thread_pool.start(search_runnable)
                    current_y = next_y
                current_y = bottom
                current_x = next_x

    def reset(self):
        self.filters.remove_all()

    def export(self):
        self.export_thread_pool.waitForDone(0)
        acquisitions = None
        if self.model is not None:
            acquisitions = self.model.data

        if not acquisitions:
            self.iface.messageBar().pushMessage("Error",
                                                "No data to export.",
                                                level=QgsMessageBar.CRITICAL)
        else:
            # open file ui
            select_file_ui = QFileDialog()
            starting_file = self.export_file or os.path.expanduser("~")
            export_file = select_file_ui.getSaveFileName(
                None, "Choose output file", starting_file, SELECT_FILTER)

            if export_file:
                self.export_file = export_file
                self.init_progress_bar(0)
                export_runnable = CatalogExportRunnable(
                    acquisitions, self.export_file)
                export_runnable.task_object.task_complete.connect(
                    self.on_export_complete)
                self.export_thread_pool.start(export_runnable)

    @pyqtSlot()
    def on_search_complete(self):
        thread_count = self.get_search_active_thread_count()
        if self.progress_message_bar:
            self.progress_bar.setValue(self.progress_bar.value() + 1)
        if thread_count == 0:
            self.clear_widgets()
            self.dialog_ui.table_view.resizeColumnsToContents()

    @pyqtSlot()
    def on_export_complete(self):
        thread_count = self.get_export_active_thread_count()
        if self.progress_message_bar:
            self.progress_bar.setValue(self.progress_bar.value() + 1)
        if thread_count == 0:
            self.clear_widgets()
            self.iface.messageBar().pushMessage(
                "Info",
                'File export has completed to "%s".' % self.export_file)

    def selection_changed(self, selected, deselected):
        self.footprint_layer.startEditing()

        # draw footprints for selected rows
        selected_rows = set()
        for index in selected.indexes():
            selected_rows.add(index.row())
        for row in selected_rows:
            acquisition = self.model.get(row)
            feature_id = self.model.generate_feature_id()
            self.model.set_feature_id(acquisition, feature_id)
            feature = CatalogAcquisitionFeature(feature_id, acquisition)
            self.footprint_layer.dataProvider().addFeatures([feature])

        # remove footprints for deselected rows
        deselected_rows = set()
        for index in deselected.indexes():
            deselected_rows.add(index.row())
        feature_ids_to_remove = []
        for row in deselected_rows:
            acquisition = self.model.get(row)
            feature_id = self.model.get_feature_id(acquisition)
            feature_ids_to_remove.append(feature_id)
            self.model.remove_feature_id(acquisition)
        if feature_ids_to_remove:
            self.footprint_layer.dataProvider().deleteFeatures(
                feature_ids_to_remove)

        self.footprint_layer.commitChanges()
        self.footprint_layer.updateExtents()
        self.footprint_layer.triggerRepaint()

    def drange_list(self, start, stop, step):
        drange_list = []
        r = start
        while r < stop:
            drange_list.append(r)
            r += step
        if not drange_list:
            drange_list.append(stop)
        return drange_list
Пример #11
0
class WorkHandler(object):
    """
    Class to handle threading of "work" (concretely this is just the evaluation of a function of the
     form func(*args, **kwargs).

     The process ID identifies (uniquely) each instance of a Worker; but the caller also defines
     an ID which is then used to identify the Worker through the API.
    """

    class WorkListener(with_metaclass(ABCMeta, object)):
        """
        This abstract base class defines methods which must be overriden and which
        handle responses to certain worker actions such as raised errors, or completion.
        """

        def __init__(self):
            pass

        @abstractmethod
        def on_processing_finished(self, result):
            pass

        @abstractmethod
        def on_processing_error(self, error):
            pass

    def __init__(self):
        self.thread_pool = None
        self._listener = {}
        self._worker = {}
        self.thread_pool = QThreadPool()

    def _add_listener(self, listener, process_id, id):
        if not isinstance(listener, WorkHandler.WorkListener):
            raise ValueError("The listener is not of type "
                             "WorkListener but rather {}".format(type(listener)))
        self._listener.update({process_id: {'id': id, 'listener': listener}})

    @pyqtSlot()
    def on_finished(self, process_id):
        if process_id not in self._worker:
            return
        result = self._worker.pop(process_id)['worker'].result
        self._listener.pop(process_id)['listener'].on_processing_finished(result)

    @pyqtSlot()
    def on_error(self, process_id, error):
        if process_id not in self._worker:
            return
        self._listener[process_id]['listener'].on_processing_error(error)

    def process(self, caller, func, id, *args, **kwargs):
        """
        Process a function call with arbitrary arguments on a new thread.

        :param caller: ??
        :param func: The function to be evaluated.
        :param id: An identifying integer for the task.
        :param args: args for func.
        :param kwargs: keyword args for func.
        """
        self.remove_already_processing(id)
        process_id = uuid.uuid4()
        # Add the caller
        self._add_listener(caller, process_id, id)

        finished_callback = functools.partial(self.on_finished, process_id)
        error_callback = functools.partial(self.on_error, process_id)

        worker = Worker(func, *args, **kwargs)
        worker.signals.finished.connect(finished_callback)
        worker.signals.error.connect(error_callback)

        self._worker.update({process_id: {'id': id, 'worker': worker}})

        self.thread_pool.start(self._worker[process_id]['worker'])

    def remove_already_processing(self, id):
        """
        Remove workers with ID
        :param id:
        """
        for key, process in list(self._listener.items()):
            if process['id'] == id:
                self._listener.pop(key)
                self._worker.pop(key)

    def wait_for_done(self):
        self.thread_pool.waitForDone()

    def __eq__(self, other):
        return self.__dict__ == other.__dict__

    def __ne__(self, other):
        return self.__dict__ != other.__dict__