Exemplo n.º 1
0
class Downloader(QObject):

    finished = pyqtSignal()

    def __init__(self, queue):
        """
        Class that spawns the separate download threads.  This is a separate class so it can be moved to its own thread
        and run simultaneously with post extraction.

        :param queue: The download queue in which extracted content is placed
        """
        super().__init__()
        self.queue = queue
        self.run = True

        self.settings = QSettings("SomeGuySoftware", "RedditDownloader")

        self.download_pool = QThreadPool()
        self.download_pool.setMaxThreadCount(
            self.settings.value('thread_limit', 4, type=int))

    def download(self):
        """Spawns the download pool threads"""
        while self.run:
            post = self.queue.get()
            if post is not None:
                self.download_pool.start(post)
            else:
                self.run = False
        self.download_pool.waitForDone()
        self.finished.emit()

    def stop(self):
        self.run = False
        self.download_pool.clear()
Exemplo n.º 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
Exemplo n.º 3
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,

      # See parent_.
      parent=None):

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

    # See `_start`_.
    def _start(self, future):
        # Asynchronously invoke f_.
        apw = _AsyncPoolWorker(future)
        self.threadPool.start(apw)

    # See `_terminate`_.
    def _terminate(self):
        self.threadPool.waitForDone()
        del self.threadPool
Exemplo n.º 4
0
class runDownload(QThread):
    finishDownload_signal = pyqtSignal()
    DownloadNums_signal = pyqtSignal(int)

    def __init__(self, gui):
        super(QThread, self).__init__()
        self.dl = gui.DL
        self.num = len(self.dl)
        self.save_path = gui.ui.SaveTextEdit.toPlainText()
        self.md_dir = gui.ui.checkBox.isChecked()
        self.FinishedNums = 0
        self.ThreadNums = int(gui.ui.ThreadsSpinBox.text())
        self.ThreadsPool = QThreadPool()
        self.ThreadsPool.setMaxThreadCount(int(gui.ui.ThreadsSpinBox.text()))

    def run(self) -> None:
        runList = []
        for temp in self.dl:
            while True:
                if len(runList) < self.ThreadNums:
                    self.inputThreadList(runList, temp)
                    break
                else:
                    for i in runList:
                        if i.isFinished():
                            runList.remove(i)
        while len(runList) > 0:
            for i in runList:
                if i.isFinished():
                    runList.remove(i)

        self.finishDownload_signal.emit()
        self.RemoveTempFile()
        print('start ' + '\'' + self.save_path + '\'')
        os.startfile(self.save_path)

    def inputThreadList(self, runList, dl):
        down = Downloading(dl, self.md_dir, self.save_path)
        runList.append(down)
        down.FinishSignal.connect(self.ed_signal)
        down.start()

    def RemoveTempFile(self):
        if self.md_dir:
            a = os.listdir(self.save_path)
            for j in a:
                b = os.listdir(self.save_path + '/' + j)
                path_temp = self.save_path + '/' + j
                for i in b:
                    if i[-3:] == 'tmp':
                        os.remove(path_temp + r'/' + i)
        else:
            a = os.listdir(self.save_path)
            for i in a:
                if i[-3:] == 'tmp':
                    os.remove(self.save_path + '/' + i)

    def ed_signal(self):
        self.FinishedNums += 1
        self.DownloadNums_signal.emit(int((self.FinishedNums / self.num) * 100))
Exemplo n.º 5
0
class TaskManagerSingleton(object):
    THREAD_POOL_SIZE = 10

    def __init__(self):
        self.tasks = Queue()
        self.thread_pool = QThreadPool()
        self.thread_pool.setMaxThreadCount(self.THREAD_POOL_SIZE)

    def add_task(self, task: Task):
        self.tasks.put(task)
        self.thread_pool.start(TaskRunner(self))

    def get_task(self) -> Task:
        return self.tasks.get()
Exemplo n.º 6
0
class TaskConsumer(metaclass=SingletonMeta):
    def __init__(self) -> None:
        self.lock = threading.Lock()
        self.q = list()
        self.thread_pool = QThreadPool()
        self.thread_pool.setMaxThreadCount(1)

    def set_thread_pool(self, tp):
        self.thread_pool = tp

    def insert_task(self, task: Task) -> None:
        """
        Insert task if whether its not in queue or its status is either done or failed.
        :param task:
        :return:
        """
        with self.lock:
            self.__remove_finished_task()
            if not any(task.task_type == each.task_type for each in self.q):
                self.q.append(task)

    def get_task(self) -> Task:
        with self.lock:
            self.__remove_finished_task()
            if self.q:
                for each in self.q:
                    if each.task_type == TaskTypes.SERIAL_OPEN:
                        return each
                return self.q[0]
            else:
                return None

    def consume_task(self) -> None:
        task = self.get_task()
        if task and self.thread_pool.activeThreadCount() == 0:
            self.thread_pool.start(task)

    def clear_task_queue(self) -> None:
        self.q.clear()
        self.thread_pool.clear()

    def __remove_finished_task(self) -> None:
        clr_lst = [
            index for index, task in enumerate(self.q)
            if task.status > TaskStatus.RUNNING
        ]
        for index in clr_lst:
            del self.q[index]
Exemplo n.º 7
0
class Downloader(QObject):

    finished = pyqtSignal()
    download_count_signal = pyqtSignal(int)
    send_downloaded = pyqtSignal(dict)

    def __init__(self, queue, thread_limit):
        """
        Class that spawns the separate download threads.  This is a separate class so it can be moved to its own thread
        and run simultaneously with post extraction.

        :param queue: The download queue in which extracted content is placed
        """
        super().__init__()
        self.logger = logging.getLogger('DownloaderForReddit.%s' % __name__)
        self.queue = queue
        self.download_count = 0
        self.run = True

        self.download_pool = QThreadPool()
        self.download_pool.setMaxThreadCount(thread_limit)

    def download(self):
        """Spawns the download pool threads"""
        self.logger.info('Downloader started')
        while self.run:
            post = self.queue.get()
            if post is not None:
                post.download_complete_signal.complete.connect(
                    self.send_download_dict)
                self.download_pool.start(post)
                self.download_count += 1
            else:
                self.run = False
        self.download_pool.waitForDone()
        self.logger.info('Downloader finished',
                         extra={'download_count': self.download_count})
        self.download_count_signal.emit(self.download_count)
        self.finished.emit()

    def send_download_dict(self, download_dict):
        self.send_downloaded.emit(download_dict)

    def stop(self):
        self.run = False
        self.download_pool.clear()
Exemplo n.º 8
0
Arquivo: qth.py Projeto: aviikc/gui
class Tasks(QObject):
    def __init__(self):
        super(Tasks, self).__init__()

        self.pool = QThreadPool()
        self.pool.setMaxThreadCount(1)

    def process_result(self, task):
        print('Receiving', task)

    def start(self):
        for task in range(10):
            worker = Worker(task)
            worker.signals.result.connect(self.process_result)

            self.pool.start(worker)

        self.pool.waitForDone()
Exemplo n.º 9
0
class HnQTThreadPool():
    thread_list = []

    def __init__(self):
        super().__init__()
        self.threadpool = QThreadPool()
        self.threadpool.globalInstance()
        poolnum = tool.loadJsons(tool.setting_address)[0]['pool_num']
        self.threadpool.setMaxThreadCount(int(poolnum))

    def addThread(self, _thread):
        self.thread_list.append(_thread)

    def Start(self):
        for i in self.thread_list:
            self.threadpool.start(i)
        self.threadpool.waitForDone()
        self.thread_list.clear()
Exemplo n.º 10
0
class Fuzzer(QObject):
    communication = None

    def __init__(self, communication, max_thread_number, inputs, wordlist):
        super(Fuzzer, self).__init__()
        self.communication = communication
        self.max_thread_number = max_thread_number
        self.pool = QThreadPool()
        self.pool.globalInstance()
        self.inputs = inputs
        self.wordlist = wordlist
        self.socket_data = [inputs[0], inputs[1], inputs[2]]

    def start(self):
        num_of_chunks = len(self.wordlist) // int(self.max_thread_number)
        new_fuzz_list = list(self.listChunks(self.wordlist, num_of_chunks))

        self.pool.setMaxThreadCount(self.max_thread_number)

        for sub_list in new_fuzz_list:
            QCoreApplication.processEvents()
            for word in sub_list:
                QCoreApplication.processEvents()
                if self.pool.activeThreadCount() < self.pool.maxThreadCount():
                    fuzz_thread = Fuzz(self.communication,
                                       self.socket_data, self.inputs[3],
                                       str(word), self.inputs[5])
                    fuzz_thread.setAutoDelete(True)
                    self.pool.start(fuzz_thread)
                else:
                    self.pool.waitForDone()

    def __del__(self):
        pass

    def listChunks(self, myList, numOfChunks):
        for i in range(0, len(myList), numOfChunks):
            yield myList[i:i + numOfChunks]
Exemplo n.º 11
0
class Server(QTcpServer):
    """ create tcp server and listen for new client connection
        add new connection to ThreadPool for communicate with that
    """

    def __init__(self, parent=None):
        """ initialize Queue, Manager and ThreadPool for work with that """

        super().__init__(parent)
        self.pool = QThreadPool(self)
        self.pool.setMaxThreadCount(5000)
        self.queues = {'HQueue': PGQ('HQueue'), 'MQueue': PGQ('MQueue'), 'LQueue': PGQ('LQueue')}

    def start(self):
        """ start listening on specific tcp ip/port and connect to TcpServer error signal """
        self.setMaxPendingConnections(5000)
        if self.listen(QHostAddress.AnyIPv4, 8001):
            print("Server: started")
        else:
            raise Exception(self.errorString())

        self.acceptError.connect(self.error)

    def incomingConnection(self, socketDescriptor: int):
        """ this method called when new connection will be  available
            initialize Handler and add it to ThreadPool
        """

        handle = Handler(socketDescriptor, self.queues)
        handle.setAutoDelete(True)
        self.pool.start(handle)

    @staticmethod
    def error(e):
        """ called with TcpServer error signal and handle received error """

        print(e.errorString())
Exemplo n.º 12
0
class PackageMonitor(QThread):
    signal = pyqtSignal()

    def __init__(self):
        super().__init__()
        self.pool = QThreadPool()
        self.pool.globalInstance()
        self.pool.setMaxThreadCount(3)

    def run(self):
        flag = True
        while flag:
            count = self.pool.activeThreadCount()
            if count <= 0:  # 打包完成,发完成信号,重置flag,终止线程
                self.signal.emit()
                flag = False
            else:  # 打包进行中,线程休眠
                QThread.sleep(3)

    def add_runnable(self, runnable):
        self.pool.start(runnable)

    def clear(self):
        self.pool.clear()
Exemplo n.º 13
0
class Tasks(QObject):
    def __init__(self):
        super(Tasks, self).__init__()

        self.pool = QThreadPool()
        self.pool.setMaxThreadCount(1)
        self.tasks = []
        self.data = []
        self.signals = TaskSignals()

    def add_task(self, task):
        self.tasks.append(task)

    def remove_task(self, task):
        self.tasks.remove(task)

    def clear(self):
        self.tasks.clear()

    def get_data(self):
        self.pool.waitForDone()
        return self.data

    def clear_data(self):
        self.data = []

    def start(self):
        for task in self.tasks:
            self.pool.start(task)
        self.pool.waitForDone()

        for task in self.tasks:
            self.data.append(task.exp)
            self.remove_task(task)

        self.signals.finished.emit(self.data)
Exemplo n.º 14
0
class MainWidget(QWidget, Ui_MainWidget):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)

        self.setupUi(self)

        self.threadpool = QThreadPool()
        self.threadpool.setMaxThreadCount(1)

        images = Images([])
        self.images_vm: ImagesViewModel = ImagesViewModel([], images)

        self.images_vm.subscribe(self.on_images_updated)

        self.calculation_service: Optional[CalculationService] = None

    # noinspection PyUnusedLocal
    def on_start_required(self, play_enabled: bool) -> None:
        if not self.images_vm.count:
            self.reset_play_button()
            return

        service = self.calculation_service
        if service is not None and not service.completed():
            if not service.stopped():
                self.on_pause_required()
                return

        service = CalculationService(self.images_vm)
        service.signals.started.connect(self.on_calculation_started)
        service.signals.completed.connect(self.on_calculation_completed)
        service.signals.progress.connect(self.on_calculation_progress)
        service.signals.failed.connect(self.on_calculation_failed)
        service.signals.cancelled.connect(self.on_calculation_cancelled)
        self.calculation_service: CalculationService = service

        self.threadpool.start(self.calculation_service)

    def on_calculation_started(self) -> None:
        self.lock_interface()

    def on_calculation_completed(self) -> None:
        self.unlock_interface()
        self.reset_play_button()

        show_success_dialog(self, "Расчёт успешно завершён!")

    def on_calculation_progress(self, current: int, count: int) -> None:
        self.progressBar.setMaximum(count)
        self.progressBar.setValue(current)

    def on_calculation_failed(self, message: str) -> None:
        show_error_dialog(self, f"Не удалось закончить расчёт: {message}")
        self.unlock_interface()
        self.reset_play_button()

    def on_calculation_cancelled(self) -> None:
        self.unlock_interface()
        self.reset_play_button()

    def on_pause_required(self) -> None:
        service = self.calculation_service
        if service is None:
            return

        if service.paused():
            self.calculation_service.resume()
        else:
            self.calculation_service.pause()

    def on_stop_required(self) -> None:
        if self.calculation_service is None:
            return

        self.calculation_service.stop()
        self.reset_play_button()

    def reset_play_button(self) -> None:
        self.startButton.blockSignals(True)
        self.startButton.setChecked(False)
        self.startButton.blockSignals(False)

    def on_save_required(self) -> None:
        filepath = save_file_dialog(self, "Сохранить таблицу", "*.csv")
        if not filepath.name:
            return

        self._save_table(filepath)

    def _save_table(self, filepath: Path) -> None:
        header, rows = self.tableWidget.dump()
        try:
            with open(filepath.as_posix(),
                      mode="w",
                      encoding="utf-8",
                      newline="") as file:
                writer = csv.DictWriter(file, fieldnames=header, delimiter=";")
                writer.writeheader()
                writer.writerows(rows)
            show_success_dialog(
                self, text=f"Таблица {filepath.name} успешно сохранена!")
        except Exception as ex:
            show_error_dialog(self, text=f"Ошибка сохранения таблицы: {ex}")

    def dragEnterEvent(self, event: QDragEnterEvent) -> None:
        mime_data: QMimeData = event.mimeData()
        if mime_data.hasUrls():
            event.accept()

    def dropEvent(self, event: QDropEvent) -> None:
        mime_data: QMimeData = event.mimeData()
        urls: List[QUrl] = mime_data.urls()

        if not len(urls):
            event.ignore()
            return

        paths = []
        for each in urls:
            filepath: Path = Path(each.toLocalFile())

            if not image_filepath_valid(filepath):
                continue
            paths.append(filepath)

        self._load_paths(paths)

    def _load_paths(self, paths: List[Path]) -> None:
        for each in paths:
            image = Image(each)
            self.images_vm.add(image)

    def on_images_updated(self, vm: ImagesViewModel, event: Event) -> None:
        if event is Event.ImageAdded:
            self.tableWidget.add_image(vm.last_image_vm)
            self.countLabel.setText(f"{vm.count} шт.")
        elif event is Event.ImageRemoved:
            self.countLabel.setText(f"{vm.count} шт.")

    def set_interface_enabled(self, enabled: bool = True) -> None:
        self.saveButton.setEnabled(enabled)
        self.tableWidget.setEnabled(enabled)

    def lock_interface(self) -> None:
        self.set_interface_enabled(False)

    def unlock_interface(self) -> None:
        self.set_interface_enabled(True)

    def on_images_removing_started(self) -> None:
        ...

    def on_images_removed(self, images_vm: List[ImageViewModel]) -> None:
        for image_vm in images_vm:
            self.images_vm.remove_vm(image_vm)

    def on_open_images_required(self) -> None:
        filepaths = open_files_dialog(
            self,
            caption="Загрузка изображений",
            filters="Изображение (*.png *.jpg, *.jpeg)",
        )
        self._load_paths(filepaths)
Exemplo n.º 15
0
class Gaitmenu(QtWidgets.QMainWindow):
    def __init__(self):
        super(self.__class__, self).__init__()
        # load user interface made with designer
        uifile = resource_filename('gaitutils', 'gui/gaitmenu.ui')
        uic.loadUi(uifile, self)

        # disable editing of log widget text
        self.txtOutput.setReadOnly(True)

        if (not cfg.general.allow_multiple_menu_instances
                and ulstools.env.already_running('gaitmenu')):
            qt_message_dialog('Another instance of the menu seems to be '
                              'running. Please use that instance or '
                              'stop it before starting a new one.')
            sys.exit()

        if cfg.general.git_autoupdate:
            if envutils._git_autoupdate():
                qt_message_dialog(
                    'The package was automatically updated. Restarting...')
                os.execv(sys.executable, ['python'] + sys.argv)

        self._web_report_dialog = WebReportDialog(self)

        # connect ui widgets
        self.btnAddSession.clicked.connect(self._add_session_dialog)
        self.btnAddTrials.clicked.connect(self._add_trials_dialog)
        self.btnAddNexusTrial.clicked.connect(self._add_nexus_trial)
        self.btnSelectAll.clicked.connect(self._select_all_trials)
        self.btnClearSelected.clicked.connect(self._remove_selected_trials)
        self.btnPlotTrials.clicked.connect(self._plot_selected_trials)
        self.btnAveragePlot.clicked.connect(self._average_trials)
        self.actionCreate_PDF_report.triggered.connect(
            lambda ev: self._create_pdf_report())
        self.actionWeb_reports.triggered.connect(self._web_report_dialog.show)
        self.actionQuit.triggered.connect(self.close)
        self.actionOpts.triggered.connect(self._options_dialog)
        self.actionTardieu_analysis.triggered.connect(self._tardieu)
        self.actionAutoprocess_session.triggered.connect(
            self._autoproc_session)
        self.actionAutoprocess_single_trial.triggered.connect(
            self._autoproc_trial)
        self.actionPDF_report_from_Nexus_session.triggered.connect(
            self._create_pdf_report_nexus)
        self.actionWeb_report_from_Nexus_session.triggered.connect(
            self._create_web_report_nexus)
        self.actionRun_postprocessing_pipelines.triggered.connect(
            self._postprocess_session)
        self.actionTrial_median_velocities.triggered.connect(
            self._plot_trial_median_velocities)
        self.actionTrial_timedep_velocities.triggered.connect(
            self._plot_trial_timedep_velocities)
        self.actionConvert_session_videos_to_web_format.triggered.connect(
            self._convert_session_videos)
        self.actionTime_distance_average.triggered.connect(
            self._plot_timedist_average)
        # XXX: these get run in the main thread
        self.actionCopy_session_videos_to_desktop.triggered.connect(
            copy_session_videos)
        self.actionAutomark_events.triggered.connect(self._automark_trial)
        # trials table settings
        # force "item selected" style, otherwise it will depend on focus; set font size
        table_sheet = "QTableView{ selection-background-color: rgba(0, 0, 255, 50%); font-size: 8pt; }"
        self.tableTrials.setStyleSheet(table_sheet)
        self.tableTrials.cellDoubleClicked.connect(self._cell_doubleclicked)

        # set up radio buttons
        self.rb_map_backend = {
            'plotly': self.rbPlotly,
            'matplotlib': self.rbMatplotlib
        }
        rb_backend_active = self.rb_map_backend[cfg.plot.backend]
        rb_backend_active.setChecked(True)

        def _colorstylevar_changed(self):
            logger.debug('callback')

        # set up color/style comboboxes
        self.cbColorStyleVar.currentIndexChanged.connect(
            self._colorstylevar_changed)
        # this callback only needs to fire when user activates the combobox
        self.cbColorStyleBy.activated.connect(self._colorstyleby_changed)
        # dynamically create combobox entries according to cfg
        for vartype, col_choice in cfg.plot.color_by.items():
            self.cbColorStyleVar.addItem('Set %s trace color by:' % vartype,
                                         userData=('color', vartype))
        for vartype, sty_choice in cfg.plot.style_by.items():
            self.cbColorStyleVar.addItem('Set %s trace style by:' % vartype,
                                         userData=('style', vartype))

        # add plot layouts to layout combobox
        cb_items = sorted(
            configdot.get_description(lo) or loname
            for loname, lo in cfg['layouts'])
        self.cbLayout.addItems(cb_items)
        # set default option to PiG lower body (if it's on the list)
        try:
            default_index = cb_items.index('PiG lower body kinematics')
        except ValueError:
            default_index = 0
        self.cbLayout.setCurrentIndex(default_index)
        # map descriptions to layout names
        self.layouts_map = {(configdot.get_description(lo) or loname): loname
                            for loname, lo in cfg['layouts']}

        XStream.stdout().messageWritten.connect(self._log_message)
        XStream.stderr().messageWritten.connect(self._log_message)
        self.threadpool = QThreadPool()
        # we need a thread for each web server plus one worker thread
        self.threadpool.setMaxThreadCount(cfg.web_report.max_reports + 1)
        # keep refs to tardieu+mpl windows so they don't get garbage collected
        self._tardieuwin = None
        self._mpl_windows = list()
        # progress bar
        self.prog = None

    def _colorstylevar_changed(self, _idx):
        """Callback for color/style variable selection combobox"""
        # get current style/color choice and show it in the other combobox
        mode, vartype = self.cbColorStyleVar.currentData()
        cfg_item = {
            'color': cfg.plot.color_by,
            'style': cfg.plot.style_by
        }[mode]
        choice = cfg_item[vartype]
        if choice is None:
            choice = 'none'
        idx = self.cbColorStyleBy.findText(choice)
        self.cbColorStyleBy.setCurrentIndex(idx)
        # don't allow styling for EMG variables
        enabled = not (vartype == 'emg' and mode == 'style')
        self.cbColorStyleBy.setEnabled(enabled)

    def _colorstyleby_changed(self, _idx):
        """Callback for color/style selection combobox"""
        mode, vartype = self.cbColorStyleVar.currentData()
        # set the appropriate cfg item according to choice
        cfg_item = {
            'color': cfg.plot.color_by,
            'style': cfg.plot.style_by
        }[mode]
        colorstyleby_choice = self.cbColorStyleBy.currentText()
        # Py2: convert unicode choices to str
        # (unicode prefix looks weird in Options interface)
        if sys.version_info.major == 2:
            colorstyleby_choice = str(colorstyleby_choice)
        if colorstyleby_choice == 'none':
            colorstyleby_choice = None
        cfg_item[vartype] = colorstyleby_choice

    def _get_plotting_backend_ui(self):
        """Get backend selection from UI"""
        for backend, rb in self.rb_map_backend.items():
            if rb.isChecked():
                return backend

    def _get_trial_sel(self):
        """Get trials selection from UI"""
        for trial_sel, rb in self.rb_map_trials.items():
            if rb.isChecked():
                return trial_sel

    def _automark_trial(self):
        session = _get_nexus_sessionpath()
        if session is None:
            return
        self._run_in_thread(automark_trial, finished_func=self._enable_main_ui)

    def _autoproc_session(self):
        """Wrapper to run autoprocess for Nexus session"""
        session = _get_nexus_sessionpath()
        if session is None:
            return
        c3ds = sessionutils.get_c3ds(session, trial_type='DYNAMIC')
        if c3ds:
            reply = qt_yesno_dialog('Some of the dynamic trials have been '
                                    'processed already. Are you sure you want '
                                    'to run autoprocessing?')
            if reply == QtWidgets.QMessageBox.NoRole:
                return

        self.prog = ProgressBar('Running autoprocessing...')
        signals = ProgressSignals()
        signals.progress.connect(lambda text, p: self.prog.update(text, p))
        self.prog._canceled.connect(signals.cancel)

        self._run_in_thread(autoproc_session,
                            finished_func=self._enable_main_ui,
                            signals=signals)

    def _autoproc_trial(self):
        """Wrapper to run autoprocess for Nexus trial"""

        self.prog = ProgressBar('Running autoprocessing...')
        signals = ProgressSignals()
        signals.progress.connect(lambda text, p: self.prog.update(text, p))
        self.prog._canceled.connect(signals.cancel)

        self._run_in_thread(autoproc_trial,
                            finished_func=self._enable_main_ui,
                            signals=signals)

    def _plot_timedist_average(self):
        """Plot time-distance average"""
        session = _get_nexus_sessionpath()
        if session is None:
            return
        backend = self._get_plotting_backend_ui()
        self._run_in_thread(
            do_session_average_plot,
            finished_func=self._enable_main_ui,
            result_func=self._show_plots,
            session=session,
            backend=backend,
        )

    def _plot_trial_median_velocities(self):
        """Trial velocity plot from current Nexus session"""
        session = _get_nexus_sessionpath()
        if session is None:
            return
        backend = self._get_plotting_backend_ui()
        self._run_in_thread(
            plot_trial_velocities,
            finished_func=self._enable_main_ui,
            result_func=self._show_plots,
            session=session,
            backend=backend,
        )

    def _plot_trial_timedep_velocities(self):
        """Trial velocity plot from current Nexus session"""
        session = _get_nexus_sessionpath()
        if session is None:
            return
        backend = self._get_plotting_backend_ui()
        self._run_in_thread(
            plot_trial_timedep_velocities,
            finished_func=self._enable_main_ui,
            result_func=self._show_plots,
            session=session,
            backend=backend,
        )

    def _add_trial_to_table(self, tr):
        """Adds a trial to the trials table"""
        nrows = self.tableTrials.rowCount()
        self.tableTrials.insertRow(nrows)
        texts = (
            tr.trialname,
            tr.eclipse_data['DESCRIPTION'],
            tr.eclipse_data['NOTES'],
            tr.sessionpath,
        )
        for k, txt in enumerate(texts):
            item_ = QtWidgets.QTableWidgetItem(txt)
            if k == 0:
                # actual Trial instances are stored as userdata of
                # QTableWidgetItems on each rows 1st column; thus
                # we don't have to keep separate references to them
                item_.setData(QtCore.Qt.UserRole, tr)
            self.tableTrials.setItem(nrows, k, item_)

    def _add_c3dfiles(self, c3dfiles):
        """Add given c3d files to trials list"""
        c3dfiles = (op.normpath(fn) for fn in c3dfiles)
        self._disable_main_ui()  # in case it takes a while
        for c3dfile in c3dfiles:
            try:
                tr = trial.Trial(c3dfile)
            except GaitDataError as e:
                title = 'Could not load trial %s. Details:' % op.split(
                    c3dfile)[-1]
                _report_exception(e, title=title)
            else:
                self._add_trial_to_table(tr)
                self.tableTrials.resizeColumnsToContents()
            finally:
                self._enable_main_ui()

    def _add_nexus_trial(self):
        """Add directly from Nexus"""
        try:
            tr = trial.nexus_trial(from_c3d=cfg.trial.load_from_c3d)
        except GaitDataError as e:
            _report_exception(e)
        else:
            self._add_trial_to_table(tr)
            self.tableTrials.resizeColumnsToContents()

    def _add_session_dialog(self):
        """Show the add session dialog and add trials to list"""
        dlg = AddSessionDialog(self)
        if dlg.exec_():
            self._add_c3dfiles(dlg.c3ds)

    def _add_trials_dialog(self):
        """Add individual trials to list"""
        fout = QtWidgets.QFileDialog.getOpenFileNames(self, 'Load C3D files',
                                                      None,
                                                      'C3D files (*.c3d)')
        if fout[0]:
            self._add_c3dfiles(fout[0])

    def _select_all_trials(self):
        """Select all trials"""
        self.tableTrials.selectAll()

    @property
    def _selected_rows(self):
        """Return indices of selected rows"""
        return list(
            set(idx.row() for idx in self.tableTrials.selectedIndexes()))

    def _cell_doubleclicked(self, row, col):
        """Plot trial on double click"""
        tr = self.tableTrials.item(row, 0).data(QtCore.Qt.UserRole)
        self._plot_trials([tr])

    @property
    def _selected_trials(self):
        """Return list of trials that are currently selected"""
        sel_items = (self.tableTrials.item(row, 0)
                     for row in self._selected_rows)
        return list(item.data(QtCore.Qt.UserRole) for item in sel_items)

    def _remove_selected_trials(self):
        """Remove selected trials from list"""
        while self._selected_rows:
            # this relies on _selected_rows dynamically changing after removals
            row = self._selected_rows[0]
            self.tableTrials.removeRow(row)

    def _plot_selected_trials(self):
        if not self._selected_rows:
            return
        if any(tr.is_static for tr in self._selected_trials):
            qt_message_dialog(
                'One or more trials are static, plotting all trials as unnormalized'
            )
            self._plot_trials(self._selected_trials, normalized=False)
        else:
            self._plot_trials(self._selected_trials)

    def _plot_trials(self, trials, normalized=True):
        """Plot specified trials, or selected trials from menu"""
        have_avgtrials = any(isinstance(tr, stats.AvgTrial) for tr in trials)
        if not normalized or self.xbPlotUnnorm.checkState():
            if have_avgtrials:
                qt_message_dialog('Cannot plot average trials as unnormalized')
                return
            cycles = 'unnormalized'
        else:
            cycles = None
        model_normaldata = read_default_normaldata()
        layout_desc = self.cbLayout.currentText()
        layout_name = self.layouts_map[layout_desc]
        backend = self._get_plotting_backend_ui()

        if self.xbEMGRMS.checkState():
            emg_mode = 'rms'
        elif have_avgtrials and 'EMG' in layout_name.upper():
            qt_message_dialog('Averaged EMG can only be plotted in RMS mode')
            return
        else:
            emg_mode = None

        backend = self._get_plotting_backend_ui()
        # FIXME: hardcoded legend type
        self._run_in_thread(
            plot_trials,
            finished_func=self._enable_main_ui,
            result_func=self._show_plots,
            trials=trials,
            layout_name=layout_name,
            backend=backend,
            cycles=cycles,
            emg_mode=emg_mode,
            legend_type='short_name_with_tag_and_cycle',
            model_normaldata=model_normaldata,
            auto_adjust_emg_layout=False,
        )

    def _average_trials(self):
        """Average trials from list, add resulting averaged trial to list"""
        if len(self._selected_rows) < 2:
            qt_message_dialog('Need at least 2 trials for averaging')
            return
        if any(isinstance(tr, stats.AvgTrial) for tr in self._selected_trials):
            qt_message_dialog('Cannot include averaged trials in average')
            return
        reject_outliers = cfg.trial.outlier_rejection_threshold
        self._run_in_thread(
            stats.AvgTrial.from_trials,
            finished_func=self._enable_main_ui,
            result_func=self._add_trial_to_table,
            trials=self._selected_trials,
            reject_outliers=reject_outliers,
        )

    def _create_web_report_nexus(self):
        """Create web report based on current Nexus session"""
        session = _get_nexus_sessionpath()
        if session:
            self._web_report_dialog._create_web_report(sessions=[session])

    def _show_plots(self, fig, backend=None):
        """Shows fig"""
        if backend is None:
            backend = self._get_plotting_backend_ui()
        if backend == 'matplotlib':
            _mpl_win = qt_matplotlib_window(fig)
            self._mpl_windows.append(_mpl_win)
        elif backend == 'plotly':
            _show_plotly_fig(fig)

    def _convert_vidfiles(self, vidfiles, signals):
        """Convert given list of video files"""
        # get the converter processes
        procs = convert_videos(vidfiles=vidfiles)
        if not procs:
            logger.warning('video converter processes could not be started')
            return
        completed = False
        # wait in sleep loop until all converter processes have finished
        while not completed:
            if signals.canceled:
                logger.debug('canceled, killing video converter processes')
                for p in procs:
                    p.kill()
                break
            n_complete = len([p for p in procs if p.poll() is not None])
            prog_txt = 'Converting videos: %d of %d files done' % (
                n_complete,
                len(procs),
            )
            prog_p = 100 * n_complete / float(len(procs))
            signals.progress.emit(prog_txt, prog_p)
            time.sleep(0.1)
            completed = n_complete == len(procs)

    def _convert_session_videos(self):
        """Convert Nexus session videos to web format"""
        session = _get_nexus_sessionpath()
        if session is None:
            return
        try:
            vidfiles = _collect_session_videos(session, tags=cfg.eclipse.tags)
        except GaitDataError as e:
            qt_message_dialog(_exception_msg(e))
            return
        if not vidfiles:
            qt_message_dialog('Cannot find any video files for session %s' %
                              session)
            return
        if convert_videos(vidfiles, check_only=True):
            reply = qt_yesno_dialog(
                'It looks like the session videos have already been converted. Redo?'
            )
            if reply == QtWidgets.QMessageBox.NoRole:
                return
        self._disable_main_ui()
        self.prog = ProgressBar('Converting session videos...')
        signals = ProgressSignals()
        signals.progress.connect(lambda text, p: self.prog.update(text, p))
        self.prog._canceled.connect(signals.cancel)
        self._convert_vidfiles(vidfiles, signals)
        self._enable_main_ui()

    def _postprocess_session(self):
        """Run additional postprocessing pipelines for tagged trials"""
        def _run_postprocessing():
            """Helper function that will be run in a separate thread"""
            nexus.close_trial()
            for k, tr in enumerate(trials, 1):
                trbase = op.splitext(tr)[0]
                vicon.OpenTrial(trbase, cfg.autoproc.nexus_timeout)
                nexus.run_pipelines_multiprocessing(
                    cfg.autoproc.postproc_pipelines)
                prog_txt = 'Running postprocessing pipelines: %s for %d trials' % (
                    cfg.autoproc.postproc_pipelines,
                    len(trials),
                )
                prog_p = 100 * k / float(len(trials))
                signals.progress.emit(prog_txt, prog_p)
                if signals.canceled:
                    logger.debug('postprocessing pipelines were canceled')
                    return

        session = _get_nexus_sessionpath()
        if session is None:
            return
        # XXX: run for tagged + static - maybe this should be configurable
        trials = sessionutils.get_c3ds(session,
                                       tags=cfg.eclipse.tags,
                                       trial_type='dynamic')
        trials += sessionutils.get_c3ds(session, trial_type='static')
        if trials and cfg.autoproc.postproc_pipelines:
            logger.debug('running postprocessing for %s' % trials)
            vicon = nexus.viconnexus()
            self.prog = ProgressBar('Running postprocessing pipelines...')
            self.prog.update(
                'Running postprocessing pipelines: %s for %d '
                'trials' % (cfg.autoproc.postproc_pipelines, len(trials)),
                0,
            )
            signals = ProgressSignals()
            signals.progress.connect(lambda text, p: self.prog.update(text, p))
            self.prog._canceled.connect(signals.cancel)
            self._run_in_thread(_run_postprocessing,
                                block_ui=True,
                                finished_func=self._enable_main_ui)
        elif not trials:
            qt_message_dialog('No trials in session to run postprocessing for')
        elif not cfg.autoproc.postproc_pipelines:
            qt_message_dialog('No postprocessing pipelines defined')

    def closeEvent(self, event):
        """ Confirm and close application. """

        if self._web_report_dialog.active_reports:
            reply = qt_yesno_dialog('There are active web reports which '
                                    'will be closed. Are you sure you '
                                    'want to quit?')
            if reply == QtWidgets.QMessageBox.YesRole:
                self._web_report_dialog.shutdown()
                self._close_mpl_windows()
                event.accept()
            else:
                event.ignore()
        else:
            self._close_mpl_windows()
            event.accept()

    def _close_mpl_windows(self):
        for win in self._mpl_windows:
            win.close()

    def _options_dialog(self):
        """Show the options dialog"""
        dlg = OptionsDialog(self)
        dlg.exec_()

    def _create_pdf_report_nexus(self):
        session = _get_nexus_sessionpath()
        if session is None:
            return
        self._create_pdf_report([session])

    def _create_pdf_report(self, sessions=None):
        """Create comparison or single session pdf report"""

        if sessions is None:
            dlg = ChooseSessionsDialog()
            if not dlg.exec_():
                return
            sessions = dlg.sessions

        comparison = len(sessions) > 1
        if comparison:
            qt_message_dialog('PDF comparison report not supported right now')
            return

        session = sessions[0]
        info = sessionutils.load_info(session)
        if info is None:
            info = sessionutils.default_info()
        prompt_ = 'Please give additional subject information:'
        dlg_info = PdfReportDialog(info, prompt=prompt_)
        if not dlg_info.exec_():
            return
        new_info = dict(
            hetu=dlg_info.hetu,
            fullname=dlg_info.fullname,
            session_description=dlg_info.session_description,
        )
        info.update(new_info)
        sessionutils.save_info(session, info)

        # create the report
        if comparison:
            self._run_in_thread(
                pdf.create_comparison_report,
                finished_func=self._enable_main_ui,
                sessions=sessions,
            )
        else:
            self._run_in_thread(
                pdf.create_report,
                finished_func=self._enable_main_ui,
                sessionpath=sessions[0],
                info=info,
                pages=dlg_info.pages,
            )

    def _log_message(self, msg):
        """Logs a message to the log widget"""
        c = self.txtOutput.textCursor()
        c.movePosition(QtGui.QTextCursor.End)
        self.txtOutput.setTextCursor(c)
        self.txtOutput.insertPlainText(msg)
        self.txtOutput.ensureCursorVisible()

    def _disable_main_ui(self):
        """ Disable all operation buttons """
        QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
        self.setEnabled(False)  # disables whole main window
        # update display immediately in case thread gets blocked
        QtWidgets.QApplication.processEvents()

    def _enable_main_ui(self):
        """Enable all operation buttons, close progress bar if any and restore cursor."""
        if self.prog is not None:
            self.prog.reset()
        QtWidgets.QApplication.restoreOverrideCursor()
        self.setEnabled(True)

    def _tardieu(self):
        """Open the Tardieu window if it is not currently open"""
        if self._tardieuwin is None or not self._tardieuwin.isVisible():
            self._tardieuwin = _tardieu.TardieuWindow()
            self._tardieuwin.show()

    def _run_in_thread(self,
                       fun,
                       block_ui=True,
                       finished_func=None,
                       result_func=None,
                       **kwargs):
        """Run function fun with args kwargs in a worker thread.
        If block_ui==True, disable main ui until worker thread is finished.
        finished_func will be called when thread is finished. result_func
        will be called with the function return value as its single argument,
        unless an exception is raised during thread execution."""
        fun_ = partial(fun, **kwargs)
        if block_ui:
            self._disable_main_ui()
        self.runner = Runner(fun_)
        if finished_func:
            self.runner.signals.finished.connect(finished_func)
        if result_func:
            self.runner.signals.result.connect(lambda r: result_func(r))
        self.runner.signals.error.connect(lambda e: _report_exception(e))
        self.threadpool.start(self.runner)
Exemplo n.º 16
0
class Ui(QtWidgets.QMainWindow, ihesync_app.Ui_MainWindow):
    def __init__(self, context, parent=None):
        super(Ui, self).__init__(parent)
        self.logger = logging.getLogger()
        self.network_available = False
        self.network_watchdog = None

        self.setupUi(self)
        self.context = context
        self.changed = False
        self.label_ihewebsite.setText('Visit IHE Website : <a href="http://www.ihe.net">www.ihe.net</a>')
        self.label_ihewebsite.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction)
        self.label_ihewebsite.setOpenExternalLinks(True)

        self.modifed_label = QLabel("Status: No change")
        self.network_label = QLabel("No network!")

        self.statusBar().setStyleSheet('border: 0; background-color: #FFF8DC;')
        self.statusBar().setStyleSheet("QStatusBar::item {border: none;}")
        self.statusBar().addPermanentWidget(VLine())
        self.statusBar().addPermanentWidget(self.network_label)
        self.statusBar().addPermanentWidget(VLine())
        self.statusBar().addPermanentWidget(self.modifed_label)

        self.threadpool = QThreadPool()
        self.threadpool.setMaxThreadCount(4)

        self.doc_model = documents_model.DocumentsModel([])
        self.tableView.setModel(self.doc_model)
        self.tableView.horizontalHeader().setSectionResizeMode(
            QtWidgets.QHeaderView.ResizeToContents
        )
        icon_delegate = OpenFolderDelegate(self.tableView)
        self.tableView.setItemDelegateForColumn(4, icon_delegate)
        self.tableView.clicked.connect(self.open_documents_folder)
        self.logger.info("Starts with %d threads" % self.threadpool.maxThreadCount())

        self.tabWidget.currentChanged.connect(self.current_tab_changed)

        if platform.system() in STYLES:
            self.setStyleSheet(STYLES[platform.system()])

    def main(self):
        conf_loaded = self.context.load_configuration()

        # default size
        (w, h) = (800, 700)
        if platform.system() in MAIN_WINDOW_DEFAULT_SIZE:
            (w, h) = MAIN_WINDOW_DEFAULT_SIZE[platform.system()]
        # overwritten size = user prefs
        if self.context.sync.geometry != (0, 0):
            (w, h) = self.context.sync.geometry
        self.centralwidget.parentWidget().resize(w, h)

        self.show()
        self.start_network_watchdog()

        if not conf_loaded:
            self.change_status("Configuration loading error : check network")

        self.refresh_configuration()
        self.refresh_information_counts()
        self.refresh_domain_list()
        self.refresh_public_comment()

        if self.context.local_file_count_ondisk != self.context.local_file_count:
            self.msgbox_out_of_sync()

    # -- Messages boxes ---
    # ---------------------
    def msgbox_out_of_sync(self):
        msg = QMessageBox.about(
            self,
            "Information...",
            (
                f"Out of sync ! Local files on disk {self.context.local_file_count_ondisk}"
                f" vs in configuration {self.context.local_file_count}\n"
                f" Sync needed"
            ),
        )
        msg.setIcon(QMessageBox.Warning)

    def msgbox_network_unavailable(self):
        QMessageBox.critical(self,
                             "Network unavailable", (
                                 f"No network"
                                 f" Check\n"), )

    # -- Messages boxes ---

    # -- Check Network
    def update_network_status(self, data):
        (ip, self.network_available) = data
        self.logger.debug(f"network status updated availaible = {self.network_available}")
        if self.network_available:
            self.network_label.setText("Connected")
            self.network_label.setStyleSheet('border: 0; color:  green;')
        else:
            self.network_label.setText("no Network!")
            self.network_label.setStyleSheet('border: 0; color:  red;')

    def start_network_watchdog(self):
        self.network_watchdog = sync_worker.NetworkWorker(ihesync.sync.IHE_URL, self.context.sync.proxy,
                                                          self.context.sync.ping_delay)
        self.network_watchdog.signals.progress.connect(self.update_network_status)
        self.threadpool.start(self.network_watchdog)

    def stop_network_watchdog(self):
        self.logger.debug("Stop network watchdog...")
        self.network_watchdog.abort()

    # -- Refresh counts

    def refresh_public_comment(self):
        state = (
            QtCore.Qt.Checked
            if self.context.sync.public_comment
            else QtCore.Qt.Unchecked
        )
        self.checkComments.setCheckState(state)

    def refresh_last_checked(self):
        """
            Refresh date of the last check
        :return: -
        """
        if self.context.sync.last_check:
            self.labelLastCheckDate.setText(
                self.context.sync.last_check.strftime("%Y-%m-%d %H:%M")
            )

    def refresh_domain_list(self) -> None:
        """
            Refresh the domain table
                - sorted by domain name
                - get the count of local files
        :return:
        """
        self.logger.info("refresh_domain_list")
        self.doc_model.log()

        self.labelDomainCountValue.setText(str(len(self.context.domains)))

        self.doc_model.set_documents(None)

        data = []
        for domain in sorted(self.context.domains, key=lambda v: v["name"]):
            local_count = self.context.sync.count_local_files(domain["name"])
            self.logger.debug(f"> domain >> {domain} - local cout = {local_count}")

            data.append(
                {
                    "checked": domain["name"] in self.context.selected_domains or domain["checked"],
                    "domain": domain["name"],
                    "title": DOMAIN_DICT[domain["name"]]
                    if domain["name"] in DOMAIN_DICT
                    else "",
                    "down": local_count,
                    "total": domain["files"],
                    "link": local_count > 0,
                    "local": local_count,
                    "error": 0
                }
            )

        self.doc_model.set_documents(data)
        self.tableView.model().layoutChanged.emit()

    def refresh_documents_directory_geometry(self):
        # get configuration directory geometry for height
        (conf_x, conf_y, conf_w, conf_h) = self.textConfDir.geometry().getRect()
        (x, y, w, h) = self.textDocDir.geometry().getRect()
        if len(self.context.doc_directory) > 60:
            h = conf_h * 2
        else:
            h = conf_h
        self.textDocDir.setGeometry(x, y, w, h)

    def refresh_configuration(self) -> None:
        """
            Refresh the configuration tab
        :return: -
        """
        self.textConfDir.setText(str(self.context.conf_directory))
        self.textDocDir.setText(str(self.context.doc_directory))
        self.refresh_documents_directory_geometry()
        self.newDocsGroupBox.setVisible(False)

        self.textLoggingFilename.setText(str(self.context.sync.log_filename))
        rad = dict(INFO=self.infoRadioButton, ERROR=self.errorRadioButton, DEBUG=self.debugRadioButton)
        if self.context.sync.log_level in rad:
            rad[self.context.sync.log_level].setChecked(True)
        self.textPingDelay.setText(str(self.context.sync.ping_delay))

        if self.context.sync.proxy:
            self.textProxyAddress.setText(self.context.sync.proxy['address'])
            self.textProxyPort.setText(self.context.sync.proxy['port'])
            if self.context.sync.proxy["active"]:
                self.specificProxyRadioButton.setChecked(self.context.sync.proxy["active"])
                self.noProxyRadioButton.setChecked(not self.context.sync.proxy["active"])
            self.update_proxy_state()
        else:
            self.noProxyRadioButton.setChecked(True)

    def refresh_information_counts(self) -> None:
        """
            Refresh counters and date last checked
        :return:
        """
        self.logger.debug("refresh_information_counts")
        self.newDocsGroupBox.setVisible(False)
        self.refresh_last_checked()
        self.context.scan_local_dirs()
        self.context.refresh_counts_current()
        self.labelDocumentCountValue.setText(str(self.context.file_count))
        self.labelLocalFilesCountValue.setText(str("{}/{}"
                                                   .format(self.context.local_file_count_ondisk,
                                                           self.context.local_file_count)))
        diff = self.context.check_updates_available()
        if diff > 0:
            self.newDocLabel.setText(f"{diff} document changes")
            self.newDocsGroupBox.setVisible(True)

    def change_status(self, msg=None, changed=None, duration=3000):
        if changed:
            self.changed = changed

        self.modifed_label.setText("Status: Changed !" if self.changed else "Status: No change")

        if msg:
            self.statusbar.showMessage(msg, duration)

    # -- UI callback

    @pyqtSlot()
    def on_checkComments_clicked(self):
        self.context.sync.public_comment = self.checkComments.checkState() == QtCore.Qt.Checked
        self.change_status(changed=True)

    @pyqtSlot()
    def on_aboutPushButton_clicked(self):
        dlg = dialogs.AboutDialog(self)
        dlg.main()

    @pyqtSlot()
    def on_infoRadioButton_clicked(self):
        self.context.sync.update_logger_config(level="INFO")
        if self.context.sync.log_level != "INFO":
            self.change_status(changed=True)

    @pyqtSlot()
    def on_errorRadioButton_clicked(self):
        self.context.sync.update_logger_config(level="ERROR")
        if self.context.sync.log_level != "ERROR":
            self.change_status(changed=True)

    @pyqtSlot()
    def on_debugRadioButton_clicked(self):
        self.context.sync.update_logger_config(level="DEBUG")
        if self.context.sync.log_level != "DEBUG":
            self.change_status(changed=True)

    @pyqtSlot()
    def on_noProxyRadioButton_clicked(self):
        self.change_status(changed=True)
        self.context.sync.proxy["active"] = False
        self.textProxyAddress.setDisabled(True)
        self.textProxyPort.setDisabled(True)

    @pyqtSlot()
    def on_specificProxyRadioButton_clicked(self):
        self.change_status(changed=True)
        self.context.sync.proxy["active"] = True
        self.textProxyAddress.setDisabled(False)
        self.textProxyPort.setDisabled(False)

    @pyqtSlot()
    def on_changeProxyPushButton_clicked(self):
        self.change_status(changed=True)
        self.context.sync.proxy['address'] = str(self.textProxyAddress.toPlainText())
        self.context.sync.proxy['port'] = str(self.textProxyPort.toPlainText())
        self.update_proxy_state()

    @pyqtSlot()
    def on_changeLogPushButton_clicked(self):
        self.context.sync.update_logger_config(filename=self.textLoggingFilename.toPlainText())
        self.change_status(changed=True)

    @pyqtSlot()
    def on_deleteLogPushButton_clicked(self):
        try:
            os.remove(self.textLoggingFilename.toPlainText())
        except OSError as e:
            self.logger.error(f"Can't remove {self.textLoggingFilename.toPlainText()} : {str(e)}")

    @pyqtSlot()
    def on_openLogPushButton_clicked(self):
        if os.path.exists(self.textLoggingFilename.toPlainText()):
            webbrowser.open_new(f"file://{self.textLoggingFilename.toPlainText()}")
        else:
            self.logger.error(f"Can't open file {self.textLoggingFilename.toPlainText()} which does not exist!")

    @pyqtSlot()
    def on_textConfDir_textChanged(self):
        self.context.conf_directory = self.textConfDir.toPlainText()
        self.change_status(changed=True)

    @pyqtSlot()
    def on_textDocDir_textChanged(self):
        self.context.doc_directory = self.textDocDir.toPlainText()
        self.change_status(changed=True)

    @pyqtSlot()
    def on_confSelectButton_clicked(self):
        previous = self.textConfDir.toPlainText()
        confdir = QFileDialog.getExistingDirectory(self, "Select Directory", previous, QFileDialog.ShowDirsOnly)
        if len(confdir):
            self.context.conf_directory = str(confdir)
            self.textConfDir.setText(self.context.conf_directory)
            self.change_status(changed=True)

    @pyqtSlot()
    def on_docSelectButton_clicked(self):
        previous = self.textDocDir.toPlainText()
        docdir = QFileDialog.getExistingDirectory(self, "Select Directory", previous, QFileDialog.ShowDirsOnly)
        if len(docdir):
            self.context.doc_directory = str(docdir)
            self.textDocDir.setText(self.context.doc_directory)
            self.change_status(changed=True)
            self.refresh_documents_directory_geometry()

    @pyqtSlot()
    def on_syncButton_clicked(self):
        if self.network_available:
            self.prepare_synchronization()
        else:
            self.msgbox_network_unavailable()

    @pyqtSlot()
    def on_changeConnectionPushButton_clicked(self):
        # get values before change
        new_delay = self.context.sync.ping_delay
        try:
            new_delay = int(self.textPingDelay.toPlainText())
        except ValueError as d_err:
            self.change_status("Delay value must be numeric !!!")
            self.logger.error(f"Configuration error while setting non numeric value for delay {d_err}")
            self.textPingDelay.setText(str(new_delay))

        if new_delay != self.context.sync.ping_delay:
            self.context.sync.ping_delay = new_delay
            self.stop_network_watchdog()
            self.start_network_watchdog()
            self.change_status("Ping informations changed.")

    @pyqtSlot()
    def on_synchronize_confirmed(self):
        self.logger.debug("on_synchronize_confirmed")
        self.do_synchronization()

    @pyqtSlot()
    def on_synchronize_rejected(self):
        self.context.revert_sync()

    def current_tab_changed(self, tab_number):
        if tab_number == 0:
            self.refresh_information_counts()
            self.refresh_domain_list()

    def closeEvent(self, event):
        # save new data
        self.logger.debug(f"Close - change ? {self.changed}")
        if self.changed or self.context.no_config_file:
            self.context.sync.save_infos()
            self.context.sync.save_configuration()
        else:
            self.logger.info("No changes")
        event.accept()
        self.stop_network_watchdog()

    def resizeEvent(self, event):
        h = self.centralwidget.parentWidget().size().height()
        w = self.centralwidget.parentWidget().size().width()
        self.context.sync.geometry = (w, h)

    # -- > Actions --
    # --------------

    def prepare_synchronization(self):
        """
            get the selected domains
            prepare elements to sync
            launch the UI dialog showing the compute of sync
        :return:
        """
        # get selected domains
        self.logger.debug("prepare_synchronization")
        domains = self.doc_model.checked()
        self.logger.info(domains)
        self.context.prepare_sync(domains)

        pd = dialogs.ProgressSyncDialog(
            dialogs.ProgressSyncDialog.REMOTE_INFO_TEXT, parent=self
        )
        worker = sync_worker.PrepareWorker(self.context)
        worker.signals.finished.connect(pd.accept)
        worker.signals.finished.connect(self.synchronize_dialog)
        worker.signals.aborted.connect(pd.reject)
        pd.main(worker)
        self.threadpool.start(worker)

    def synchronize_dialog(self):
        """
            Launch UI for synchro
        :return:
        """
        if self.network_available:
            sd = dialogs.SyncDialog(parent=self)
            sd.confirm_signal.connect(self.on_synchronize_confirmed)
            sd.reject_signal.connect(self.on_synchronize_rejected)

            sd.old_domains = self.context.infos["old_domain"]
            sd.new_domains = self.context.infos["new_domain"]
            sd.old_docs = self.context.infos["to_del"]
            sd.new_docs = self.context.infos["to_download"]
            if len(sd.old_domains) > 0 or len(sd.new_docs) > 0:
                self.change_status(changed=True)
            sd.main()
        else:
            self.msgbox_network_unavailable()

    def do_synchronization(self):
        self.doc_model.log()

        self.context.sync.display_available_docs()
        sd = dialogs.ProgressSyncDialog(
            dialogs.ProgressSyncDialog.SYNC_INFO_TEXT, parent=self
        )
        worker = sync_worker.SyncWorker(self.context)

        worker.signals.finished.connect(sd.accept)
        worker.signals.finished.connect(self.sync_finished)
        worker.signals.progress.connect(self.doc_model.update_documents)
        worker.signals.aborted.connect(sd.reject)
        sd.main(worker)
        self.threadpool.start(worker)

        self.context.confirm_sync()

    def sync_finished(self):
        """
            Syncho done.
            - display information in status bar
            - refresh informations counts
            - refresh doc table information
        """
        downloaded, error = self.doc_model.summary()
        self.change_status(f"{downloaded} download(s), {error} error(s)")

        self.refresh_information_counts()
        self.tableView.model().layoutChanged.emit()

    # -- < Actions

    def open_documents_folder(self, index: QtCore.QModelIndex) -> None:
        docinfo = index.model().docs[index.row()]
        if docinfo['link'] and index.column() == 4:
            dom = self.context.local_path_domain(docinfo['domain'])
            webbrowser.open_new(f"file://{dom}")

    def update_proxy_state(self):
        self.stop_network_watchdog()
        self.network_watchdog.set_proxy(self.context.sync.proxy)
        self.start_network_watchdog()
Exemplo n.º 17
0
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)
        Ui_MainWindow.__init__(self)
        self.setupUi(self)
        self.model = VentModel()  # settings file
        self.bconfirm.released.connect(self.confirm)
        self.bstart.released.connect(self.start)
        self.model.load()

        insptime, exptime, pressure = self.model.settings
        self.x1, self.x2, self.x3 = plot_points * [0], plot_points * [
            0
        ], plot_points * [0]
        self.y1, self.y2, self.y3 = plot_points * [0.0], plot_points * [
            0.0
        ], plot_points * [0.0]
        self.holding_registers = 3 * [0]
        self.coil = False

        self.tnewinsp.setValue(insptime)
        self.tnewexp.setValue(exptime)
        self.newpress.setValue(pressure)

        self.tcurrinsp.setValue(insptime)
        self.tcurrexp.setValue(exptime)
        self.currpress.setValue(pressure)

        self.graph1 = pg.PlotWidget()
        self.graph2 = pg.PlotWidget()
        self.graph3 = pg.PlotWidget()
        self.vlgraphs.addWidget(self.graph1)
        self.vlgraphs.addWidget(self.graph2)
        self.vlgraphs.addWidget(self.graph3)

        pen = pg.mkPen(color=(255, 0, 0))
        self.data_line1 = self.graph1.plot([], [], pen=pen)
        self.data_line2 = self.graph2.plot([], [], pen=pen)
        self.data_line3 = self.graph3.plot([], [], pen=pen)
        self.x1, self.y1 = self.graphInit(self.data_line1, plot_points)
        self.x1, self.y2 = self.graphInit(self.data_line2, plot_points)
        self.x1, self.y3 = self.graphInit(self.data_line3, plot_points)

        self.setStyle(self.graph1, 'Expiration vs time', 'Expiration',
                      'time (ms)')
        self.setStyle(self.graph2, 'Inspiration vs time', 'Inspiration',
                      'time (ms)')
        self.setStyle(self.graph3, 'Pressure vs time', 'Pressure', 'time (ms)')

        self.timer = QtCore.QTimer()
        self.timer.setInterval(time_interval)
        self.timer.timeout.connect(self.update_plot_data)

        self.modbuspool = QThreadPool()
        self.modbuswritepool = QThreadPool()

        self.modbustimer = QtCore.QTimer()
        self.modbustimer.setInterval(time_interval // 2)
        self.modbustimer.timeout.connect(self.modbus_timer)
        self.modbustimer.start()

    def start(self):
        if self.bstart.text() == 'Start':
            self.timer.start()

            self.coil = True

            self.bstart.setText('Stop')

            self.holding_registers = 3 * [0]
            self.x1, self.y1 = self.graphInit(self.data_line1, plot_points)
            self.x2, self.y2 = self.graphInit(self.data_line2, plot_points)
            self.x3, self.y3 = self.graphInit(self.data_line3, plot_points)

        elif self.bstart.text() == 'Stop':
            self.timer.stop()

            self.coil = False

            self.bstart.setText('Start')

        self.modbuswritepool.setMaxThreadCount(1)
        self.modbuswritetcp = ThreadHandler(self.modbus_write)
        self.modbuswritepool.start(self.modbuswritetcp)

    def confirm(self):
        insptime = self.tnewinsp.value()
        exptime = self.tnewexp.value()
        pressure = self.newpress.value()
        self.tcurrinsp.setValue(insptime)
        self.tcurrexp.setValue(exptime)
        self.currpress.setValue(pressure)
        self.model.settings = [insptime, exptime, pressure]
        self.model.save()

    def graphInit(self, dataline, pts):
        x = list(range(pts))  # 100 time points
        y = [0 for _ in range(pts)]  # 100 data points
        dataline.setData(x, y)  # Update the data.
        return x, y

    def update_plot_data(self):
        self.x1, self.y1 = self.update_points(self.data_line1, self.x1,
                                              self.y1)
        self.x2, self.y2 = self.update_points(self.data_line2, self.x2,
                                              self.y2)
        self.x3, self.y3 = self.update_points(self.data_line3, self.x3,
                                              self.y3)
        try:
            self.y1[-1] = self.holding_registers[0] / 1000.0
            self.y2[-1] = self.holding_registers[1] / 1000.0
            self.y3[-1] = self.holding_registers[2] / 1000.0
        except TypeError:
            self.holding_registers = 3 * [0]
            self.y1[-1] = self.holding_registers[0] / 1000.0
            self.y2[-1] = self.holding_registers[1] / 1000.0
            self.y3[-1] = self.holding_registers[2] / 1000.0
            print("typeerror")

    def update_points(self, dataline, x, y):
        x = x[1:]
        x.append(x[-1] + 1)
        y = y[1:]
        y.append(y[-1])
        dataline.setData(x, y)
        return x, y

    def setStyle(self, graph, title, labelx, labely):
        graph.setBackground('#000000')
        graph.setTitle(title, color='#0000FF')
        graph.setLabel('left', labelx, color='#00FF00')
        graph.setLabel('bottom', labely, color='#00FF00')

    def modbus_timer(self):
        self.modbuspool.setMaxThreadCount(1)
        self.modbustcp = ThreadHandler(self.modbus_request)
        self.modbuspool.start(self.modbustcp)

    def modbus_write(self, progress_callback):
        c = ModbusClient(host="localhost", auto_open=False)
        connection_ok = c.open()
        if connection_ok:
            try:
                c.write_single_coil(0, self.coil)
                c.close()
            except:
                print("modbus exception")
                pass
            else:
                print("other")
                pass
            finally:
                #print("passed")
                pass
        else:
            print("could not open")
            pass

    def modbus_request(self, progress_callback):
        c = ModbusClient(host="localhost", auto_open=False)
        connection_ok = c.open()
        if connection_ok:
            try:
                self.holding_registers = c.read_holding_registers(0, 3)
                print(self.holding_registers)
                regs = [
                    int(10 * self.tcurrinsp.value()),
                    int(10 * self.tcurrexp.value()),
                    int(10 * self.currpress.value())
                ]
                c.write_multiple_registers(3, regs)
                print(regs)
                c.close()
            except:
                print("modbus exception")
                pass
            else:
                print("other")
                pass
            finally:
                #print("passed")
                pass
        else:
            print("could not open")
            pass
Exemplo n.º 18
0
class UiMainWindow(object):
    __result = None

    def __init__(self, main_window_local):
        grover_circuit_constructor = GroverCircuit()
        shor_circuit_constructor = ShorCircuit()
        tokens = list(self.get_config().values())
        self.__qiskit = tokens[0]
        self.__dwave = tokens[1]
        self.__inspire = tokens[2]

        self.registration_dialog = RegistrationDialogWindow(init=tokens)

        self.ibm_backends = None
        self.dwave_backends = None
        self.inspire_backends = None

        self.window = main_window_local
        self.central_widget = QtWidgets.QWidget(main_window_local)
        self.central_widget.setObjectName("central_widget")
        self.repo = DataRepository()
        saved_results = self.repo.get_all()

        self.thread_pool = QThreadPool()
        self.thread_pool.setMaxThreadCount(8)

        self.inner_thread_pool = QThreadPool()
        self.inner_thread_pool.setMaxThreadCount(8)

        self.drawer = PlotDrawer()
        self.writer = FileWriter()
        self.tab_infos = []
        self.default_tests = []

        # quantumRegister = q.QuantumRegister(2)
        # classicalRegister = q.ClassicalRegister(2)
        # circuit = q.QuantumCircuit(quantumRegister, classicalRegister)
        # circuit.h(quantumRegister[0])
        # circuit.cx(quantumRegister[0], quantumRegister[1])
        # circuit.measure(quantumRegister, classicalRegister)
        # self.default_tests.append(QuantumTest("BellState", circuit, 1024))

        shor_answer = 4
        grover_2_answer = 3
        grover_3_answer = 6
        grover_4_answer = 2
        grover_5_answer = 21

        circuit3 = q.QuantumCircuit(5, 5)
        shor_circuit_constructor.add_circuit(circuit3)
        self.default_tests.append(
            QuantumTest('Shor_3_5', circuit3, 1024, shor_answer))

        circuit4 = q.QuantumCircuit(2, 2)
        grover_circuit_constructor.set_number(grover_2_answer)
        grover_circuit_constructor.add_circuit(circuit4)
        self.default_tests.append(
            QuantumTest('Grover_2_bits', circuit4, 1024, grover_2_answer))

        circuit_new = q.QuantumCircuit(3, 3)
        grover_circuit_constructor.set_number(grover_3_answer)
        grover_circuit_constructor.add_circuit(circuit_new)
        self.default_tests.append(
            QuantumTest('Grover_3_bits', circuit_new, 1024, grover_3_answer))

        circuit5 = q.QuantumCircuit(4, 4)
        grover_circuit_constructor.set_number(grover_4_answer)
        grover_circuit_constructor.add_circuit(circuit5)
        self.default_tests.append(
            QuantumTest('Grover_4_bits', circuit5, 1024, grover_4_answer))

        qubits = q.QuantumRegister(5)
        bits = q.ClassicalRegister(5)
        # controls = list()
        # controls.append(qubits[0])
        # controls.append(qubits[1])
        # controls.append(qubits[2])
        circuit6 = q.QuantumCircuit(qubits, bits)
        grover_circuit_constructor.set_number(grover_5_answer)
        grover_circuit_constructor.add_circuit(circuit6)

        self.default_tests.append(
            QuantumTest('Grover_5_bits', circuit6, 1024, grover_5_answer))

        self.test_info_tab = TestInfoTab(self.default_tests, self.drawer)
        self.results_info_tab = TestResultTab()

        self.setup_ui(main_window_local)

        self.setup_tabs()

        self.dialog = TestCreationDialogWindow(self.tab_infos,
                                               self.default_tests)
        self.dialog.setWindowTitle('Create test')

        for res in saved_results:
            self.add_result_from_db(res)

    def get_config(self):
        keys = ['qiskit', 'dwave', 'quantum-inspire']
        tokens = dict.fromkeys(keys)
        try:
            with open('main\\configuration\\config.ini') as config_file:
                lines = config_file.readlines()
                for line in lines:
                    api_name = line.strip().split('=')[0]
                    if api_name in keys:
                        tokens[api_name] = line.strip().split('=')[1]
        except OSError:
            print('Config file not found')
        except:
            print(sys.exc_info()[0])
            print('Corrupted file')
        return tokens

    def open_qasm_circuit(self):
        files_filter = 'QASM files (*.qasm);;All files (*.*)'
        name = QtWidgets.QFileDialog.getOpenFileName(self.window,
                                                     'Open File',
                                                     filter=files_filter)
        extension = os.path.splitext(name[0])[1]
        if extension == '.qasm':
            try:
                new_circuit = q.QuantumCircuit().from_qasm_file(name[0])
                new_test = QuantumTest(
                    os.path.basename(os.path.splitext(name[0])[0]),
                    new_circuit, 1024)
                self.default_tests.append(new_test)
                self.test_info_tab.add_new_test(new_test)
                self.dialog.add_new_test(new_test)
            except QasmError:
                msg = QtWidgets.QMessageBox()
                msg.setIcon(QtWidgets.QMessageBox.Critical)
                msg.setText('File %s is corrupted!' %
                            os.path.splitext(name[0])[0])
                msg.setWindowTitle('Error!')
                msg.exec_()

    def delete_results(self):
        indexes = self.results_info_tab.table_widget.selectionModel(
        ).selectedRows()
        for i in range(0, len(indexes)):
            self.results_info_tab.table_widget.removeRow(indexes[i].row())
        self.results_info_tab.table_widget.resizeColumnsToContents()

    def open_results(self):  # TODO
        files_filter = 'CSV files (*.csv);;All files (*.*)'
        name = QtWidgets.QFileDialog.getOpenFileName(self.window,
                                                     'Open File',
                                                     filter=files_filter)
        extension = os.path.splitext(name[0])[1]
        if extension == '.csv':
            try:
                with open(name[0], newline='') as csvfile:
                    csv_reader = csv.reader(csvfile, delimiter=';')
                    headers = next(csv_reader, None)
                    if headers != [
                            'Test_name', 'Result', 'Backend_name',
                            'Job_status', 'Date', 'Shots_taken', 'Time_taken',
                            'Accuracy', 'Counts'
                    ]:
                        raise RuntimeError('Incorrect file format!')
                    for row in csv_reader:
                        if len(row) == 0:
                            continue
                        self.results_info_tab.table_widget.insertRow(
                            self.results_info_tab.table_widget.rowCount())
                        dict_data = dict.fromkeys(headers)
                        test = next(
                            (x
                             for x in self.default_tests if x.name == row[0]),
                            QuantumTest(row[0], None, row[5], row[1]))
                        if test not in self.default_tests:
                            self.default_tests.append(test)

                        dict_data[headers[0]] = row[0]
                        self.results_info_tab.table_widget.setItem(
                            self.results_info_tab.table_widget.rowCount() - 1,
                            0, MyTableWidgetItem(str(row[0])))
                        for j in range(2, 8):
                            dict_data[headers[j]] = row[j]
                            self.results_info_tab.table_widget.setItem(
                                self.results_info_tab.table_widget.rowCount() -
                                1, j - 1, MyTableWidgetItem(str(row[j])))
                        dict_data[headers[8]] = row[8]
                        test.results.append(dict_data)
                    self.results_info_tab.table_widget.resizeColumnsToContents(
                    )

            except:
                msg = QtWidgets.QMessageBox()
                msg.setIcon(QtWidgets.QMessageBox.Critical)
                msg.setText("Oops! %s occurred." % sys.exc_info()[0])
                msg.setWindowTitle('Error!')
                msg.exec_()

    def save_results(self):
        files_filter = 'CSV files (*.csv);;All files (*.*)'
        name = QtWidgets.QFileDialog.getSaveFileName(self.window,
                                                     'Save File',
                                                     filter=files_filter)
        if all(name):
            func = self.writer.write_result(
                os.path.basename(os.path.splitext(name[0])[0]),
                self.default_tests)
            func()

    def save_qasm_circuit(self):
        files_filter = 'QASM files (*.qasm);;All files (*.*)'
        name = QtWidgets.QFileDialog.getSaveFileName(self.window,
                                                     'Save File',
                                                     filter=files_filter)
        if all(name):
            file = open(name[0], 'w')
            text = self.test_info_tab.get_selected_test().circuit.qasm()
            file.write(text)
            file.close()

    def background_calc(self, cur_circuit, cur_computer):
        job = q.execute(cur_circuit, backend=cur_computer)
        job_monitor(job)
        self.__result = job.result()
        return job.result()

    def test_and_print(self, cur_test, computers):
        print('Starting test and print')
        worker = Worker(self.test_selected_computers, cur_test, computers)
        print('Pool start')
        self.thread_pool.start(worker)

    def print_s(self):
        print('s')

    def add_result_from_db(self, result: Result):
        self.results_info_tab.table_widget.insertRow(
            self.results_info_tab.table_widget.rowCount())
        res_list = list()
        res_list.append(result.test_name)
        res_list.append(result.backend_name)
        res_list.append(result.job_status)
        res_list.append(result.date)
        res_list.append(result.shots)
        res_list.append(result.time)
        res_list.append(result.accuracy)
        for j in range(0, 7):
            self.results_info_tab.table_widget.setItem(
                self.results_info_tab.table_widget.rowCount() - 1, j,
                MyTableWidgetItem(str(res_list[j])))
        self.results_info_tab.table_widget.resizeColumnsToContents()

    def add_new_result(self, result):
        self.results_info_tab.table_widget.insertRow(
            self.results_info_tab.table_widget.rowCount())
        res_list = list(result.values())
        result_value = result['Result']
        res_list.remove(result_value)
        for j in range(0, 7):
            self.results_info_tab.table_widget.setItem(
                self.results_info_tab.table_widget.rowCount() - 1, j,
                MyTableWidgetItem(str(res_list[j])))
        self.results_info_tab.table_widget.resizeColumnsToContents()

    def test_selected_computers(self, cur_test, computers):
        fields = [
            'Test_name', 'Backend_name', 'Job_status', 'Date', 'Shots_taken',
            'Time_taken', 'Counts'
        ]
        print('start test_selected')
        print(str(self))
        print(str(cur_test))
        print(str(computers))
        tabs = []
        test = next(x for x in self.default_tests if x.name == cur_test)
        backend = None
        for computer in computers:
            try:
                backend = next(x for x in self.ibm_backends
                               if str(x) == computer)
            except StopIteration:
                for back in self.inspire_backends:
                    if computer == back['name']:
                        backend = back
            if backend is None:
                raise RuntimeError('Unknown backend')
            tab = next(x for x in self.tab_infos if x.name == computer)
            tabs.append(tab)
            if computer in [
                    'QX single-node simulator',
                    'QX single-node simulator SurfSara', 'Spin-2', 'Starmon-5'
            ]:
                worker = Worker2(test.run_inspire, backend, self.qi)
                print('Start inner pool')
                self.inner_thread_pool.start(worker)
            else:
                worker = Worker2(test.run_ibm, backend)
                print('Start inner pool')
                self.inner_thread_pool.start(worker)
        self.inner_thread_pool.waitForDone()

        for i in range(0, len(tabs)):
            self.drawer.plot_results2(tabs[i], test.not_drawn_res[i]['Counts'])
        print('Finished after testing work')
        for result in test.not_drawn_res:
            self.add_new_result(result)
            self.repo.insert_result(Result.create_result_from_dict(result))
        test.not_drawn_res.clear()

    def enter_keys(self):
        self.registration_dialog.show_dialog()
        if self.registration_dialog.result() == QtWidgets.QDialog.Accepted:
            qiskit_token, dwave_token, inspire_token = self.registration_dialog.provide_api_tokens(
            )
            is_update_necessary = False
            if self.__qiskit != qiskit_token:
                self.__qiskit = qiskit_token
                is_update_necessary = True
            if self.__dwave != dwave_token:
                self.__dwave = dwave_token
                is_update_necessary = True
            if self.__inspire != inspire_token:
                self.__inspire = inspire_token
                is_update_necessary = True

            if is_update_necessary:
                self.setup_tabs()
                self.dialog = TestCreationDialogWindow(self.tab_infos,
                                                       self.default_tests)

    def choose_test(self):
        self.dialog.show_dialog()
        if self.dialog.result() == QtWidgets.QDialog.Accepted:
            test = self.dialog.get_selected_test()
            computers = self.dialog.get_selected_computers()
            if computers:
                self.test_and_print(test, computers)

        elif self.dialog.result() == QtWidgets.QDialog.Rejected:
            self.status_label.setText('Rejected')

    def show_status(self, message):
        def show():
            self.status_label.setText(message)

        return show

    def setup_ui(self, main_window_local):
        main_window_local.setObjectName("main_window")
        main_window_local.resize(1024, 768)

        self.gridLayout_2 = QtWidgets.QGridLayout(self.central_widget)
        self.gridLayout_2.setObjectName("gridLayout_2")

        main_window_local.setCentralWidget(self.central_widget)
        self.menubar = QtWidgets.QMenuBar(main_window_local)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 766, 21))
        self.menubar.setObjectName("menubar")

        self.statusbar = QtWidgets.QStatusBar(main_window_local)
        self.statusbar.setObjectName("statusbar")
        main_window_local.setStatusBar(self.statusbar)

        self.menu_file = QtWidgets.QMenu(self.menubar)
        self.menu_file.setObjectName("menu_file")
        self.menu_service = QtWidgets.QMenu(self.menubar)
        self.menu_service.setObjectName("menu_service")
        self.menu_help = QtWidgets.QMenu(self.menubar)
        self.menu_help.setObjectName("menu_help")

        self.menu_edit = QtWidgets.QMenu(self.menubar)
        self.menu_edit.setObjectName("menu_edit")
        self.menu_edit.setStatusTip('Editing')

        main_window_local.setMenuBar(self.menubar)

        self.status_label = QtWidgets.QLabel(self.statusbar)
        self.status_label.setGeometry(5, -5, 500, 20)
        self.status_label.setObjectName('status_label')
        self.status_label.setText('')

        self.action_about = QtWidgets.QAction(main_window_local)
        self.action_about.setObjectName("action_about")
        self.action_about.setStatusTip('Подробнее о программе')

        self.action_registration = QtWidgets.QAction(main_window_local)
        self.action_registration.setObjectName('action_registration')
        self.action_registration.setStatusTip('Ввести ключи регистрации')
        self.action_registration.triggered.connect(self.enter_keys)

        self.action_new = QtWidgets.QAction(main_window_local)
        self.action_new.setObjectName("action_new")
        self.action_new.triggered.connect(self.choose_test)
        self.action_new.setStatusTip('Создать новый файл')

        self.test_info_tab.button_import.clicked.connect(
            self.open_qasm_circuit)
        self.test_info_tab.button_export.clicked.connect(
            self.save_qasm_circuit)

        self.results_info_tab.button_load.clicked.connect(self.open_results)
        self.results_info_tab.button_delete.clicked.connect(
            self.delete_results)

        self.action_open = QtWidgets.QAction(main_window_local)
        self.action_open.setObjectName("action_open")
        self.action_open.setStatusTip('Открыть файл результатов')

        self.action_save = QtWidgets.QAction(main_window_local)
        self.action_save.setObjectName("action_save")
        self.action_save.setStatusTip('Сохранить результаты')

        self.action_save_as = QtWidgets.QAction(main_window_local)
        self.action_save_as.setObjectName("action_save_as")
        self.action_save_as.setStatusTip('Сохранить результаты в новый файл')
        self.action_save_as.triggered.connect(self.save_results)

        self.action_close = QtWidgets.QAction(main_window_local)
        self.action_close.setObjectName("action_close")
        self.action_close.setStatusTip('Выйти из программы')

        self.action_update = QtWidgets.QAction(main_window_local)
        self.action_update.setObjectName("action_update")
        self.action_update.triggered.connect(self.clear_plots)
        self.action_update.setStatusTip('Очистить графики')

        self.action_test = QtWidgets.QAction(main_window_local)
        self.action_test.setObjectName("action_test")
        self.action_test.setStatusTip('Протестировать компьютеры')

        self.menu_file.addAction(self.action_new)
        self.menu_file.addAction(self.action_open)
        self.menu_file.addSeparator()
        self.menu_file.addAction(self.action_save)
        self.menu_file.addAction(self.action_save_as)
        self.menu_file.addSeparator()
        self.menu_file.addAction(self.action_close)
        self.menu_service.addAction(self.action_update)
        self.menu_service.addAction(self.action_test)
        self.menu_help.addAction(self.action_about)
        self.menu_help.addSeparator()
        self.menu_help.addAction(self.action_registration)
        self.menubar.addAction(self.menu_file.menuAction())
        self.menubar.addAction(self.menu_edit.menuAction())
        self.menubar.addAction(self.menu_service.menuAction())
        self.menubar.addAction(self.menu_help.menuAction())
        self.retranslate_ui(main_window_local)

        QtCore.QMetaObject.connectSlotsByName(main_window_local)

    def plot_result(self, cur_circuit, cur_result):
        def plot():
            for tab_info in self.tab_infos:
                axes = tab_info.figure.add_subplot(111)
                axes.clear()
                plot_histogram(self.__result.get_counts(cur_circuit), ax=axes)
                tab_info.canvas.draw()

        return plot

    def clear_plots(self):
        for tab_info in self.tab_infos:
            axes = tab_info.figure.add_subplot(111)
            axes.clear()
            tab_info.canvas.draw()

    def plot_circuit(self, cur_circuit):
        for tab_info in self.tab_infos:
            axes = tab_info.figure.add_subplot(111)
            axes.clear()
            cur_circuit.draw(output='mpl', ax=axes)
            tab_info.canvas.draw()

    def retranslate_ui(self, main_window_local):
        _translate = QtCore.QCoreApplication.translate
        main_window_local.setWindowTitle(
            _translate("main_window", "QuantumTester 0.0.1 x64"))
        self.menu_file.setTitle(_translate("main_window", "Файл"))
        self.menu_service.setTitle(_translate("main_window", "Сервис"))
        self.menu_help.setTitle(_translate("main_window", "Помощь"))
        self.menu_edit.setTitle(_translate("main_window", "Правка"))
        self.action_about.setText(_translate("main_window", "О программе..."))
        self.action_registration.setText(
            _translate("main_window", "Регистрация"))
        self.action_new.setText(_translate("main_window", "Новый"))
        self.action_new.setShortcut(_translate("main_window", "Ctrl+N"))
        self.action_open.setText(_translate("main_window", "Открыть"))
        self.action_open.setShortcut(_translate("main_window", "Ctrl+O"))
        self.action_save.setText(_translate("main_window", "Сохранить"))
        self.action_save.setShortcut(_translate("main_window", "Ctrl+S"))
        self.action_save_as.setText(
            _translate("main_window", "Сохранить как..."))
        self.action_close.setText(_translate("main_window", "Закрыть"))
        self.action_update.setText(_translate("main_window", "Обновить"))
        self.action_test.setText(_translate("main_window", "Протестировать"))

    def setup_tabs(self):
        _translate = QtCore.QCoreApplication.translate
        self.ibm_backends = None
        self.dwave_backends = None
        self.inspire_backends = None
        self.rigetti_backends = None
        self.tab_infos = []

        # backend1 = {'name': '9q-square-noisy-qvm', 'number_of_qubits': 9,
        #             'max_number_of_shots': 65536, 'max_experiments': 'unlimited',
        #             'description': 'v. 1.17', 'accuracy': 'not accurate',
        #             'remaining_jobs': 'unlimited'}
        # backend2 = {'name': '26q-qvm', 'number_of_qubits': 26,
        #             'max_number_of_shots': 65536, 'max_experiments': 'unlimited',
        #             'description': 'v. 1.17', 'accuracy': '0',
        #             'remaining_jobs': 'unlimited'}
        # self.rigetti_backends = [backend1, backend2]

        if self.__qiskit is not None:
            IBMQ.enable_account(self.__qiskit)
            ibm_provider = IBMQ.get_provider('ibm-q')
            self.ibm_backends = ibm_provider.backends()

        if self.__inspire is not None:
            enable_account(self.__inspire)
            QI.set_authentication()
            server_url = r'https://api.quantum-inspire.com'
            auth = get_token_authentication()
            self.qi = QuantumInspireAPI(server_url, auth)
            # backends = []
            # for back in self.qi.get_backend_types():
            #     current_backend = {'name': back.get('name'), 'number_of_qubits': back.get('number_of_qubits'),
            #                        'max_number_of_shots': back.get('max_number_of_shots'),
            #                        'max_experiments': back.get('max_number_of_simultaneous_jobs'),
            #                        'description': back.get('description'), 'accuracy': 'not measured',
            #                        'remaining_jobs': back.get('max_number_of_simultaneous_jobs')}
            #     backends.append(current_backend)
            self.inspire_backends = self.qi.get_backend_types()

        if self.ibm_backends is not None:
            for backend in self.ibm_backends:
                self.tab_infos.append(ComputerInfoTab(backend))

        # if self.inspire_backends is not None:
        #     for backend in self.inspire_backends:
        #         # server = r'https://api.quantum-inspire.com'
        #         # email = '*****@*****.**'
        #         # password = '******'
        #         # auth = HTTPBasicAuth(email, password)
        #         #
        #         # result = requests.get(f'{server}/projects', auth=auth)
        #         #
        #         # print(f'result status: {result.status_code}')
        #         # print(result.json())
        #         # server = r'https://api.quantum-inspire.com'
        #         # auth = coreapi.auth.TokenAuthentication(self.__inspire)
        #         # client = coreapi.Client(auth=auth)
        #         # content = client.get(f'{server}/backendtypes/1/calibration')
        #         # print(content)
        #         # action = ['projects', 'list']
        #         # result = client.action(schema, action)
        #         # print(result)
        #         # server = r'https://api.quantum-inspire.com'
        #         # email = '*****@*****.**'
        #         # password = '******'
        #         # auth = HTTPBasicAuth(email, password)
        #         # contents = urllib.request.urlopen(r'https://api.quantum-inspire.com/backendtypes/1/calibration/',).read()
        #         self.tab_infos.append(ComputerInfoTab(backend._QuantumInspireBackend__backend))

        if self.inspire_backends is not None:
            for backend in self.inspire_backends:
                self.tab_infos.append(ComputerInfoTab(backend))

        if self.rigetti_backends is not None:
            for backend in self.rigetti_backends:
                self.tab_infos.append(ComputerInfoTab(backend))

        self.tab_widget = QtWidgets.QTabWidget(self.central_widget)
        self.tab_widget.setObjectName("tabWidget")

        self.tab_widget.addTab(self.test_info_tab.tab, "")
        self.tab_widget.setTabText(
            self.tab_widget.indexOf(self.test_info_tab.tab),
            _translate('main_window', self.test_info_tab.name))

        self.tab_widget.addTab(self.results_info_tab.tab, "")
        self.tab_widget.setTabText(
            self.tab_widget.indexOf(self.results_info_tab.tab),
            _translate('main_window', self.results_info_tab.name))

        for tab_info in self.tab_infos:
            self.tab_widget.addTab(tab_info.tab, "")
            self.tab_widget.setTabText(
                self.tab_widget.indexOf(tab_info.tab),
                _translate("main_window", tab_info.tab.objectName()))

        self.gridLayout_2.addWidget(self.tab_widget, 0, 0, 1, 1)
        self.tab_widget.setCurrentIndex(0)
Exemplo n.º 19
0
class TiledTexture(glh.Texture, metaclass=TiledTextureMeta):
    ''' Tiled texture implementation for the BlueSky GL gui. '''
    class SlotHolder(QObject):
        ''' Wrapper class for Qt slot, which can only be owned by a
            QObject-derived parent. We need slots to allow signal receiver
            to be executed in the receiving (main) thread. '''
        def __init__(self, callback):
            super().__init__()
            self.cb = callback

        @pyqtSlot(Tile)
        def slot(self, *args, **kwargs):
            self.cb(*args, **kwargs)

    def __init__(self, glsurface, tilesource='opentopomap'):
        super().__init__(target=glh.Texture.Target2DArray)
        self.threadpool = QThreadPool()
        tileinfo = bs.settings.tile_sources.get(tilesource)
        if not tileinfo:
            raise KeyError(f'Tile source {tilesource} not found!')
        max_dl = tileinfo.get('max_download_workers',
                              bs.settings.max_download_workers)
        self.maxzoom = tileinfo.get('max_tile_zoom', bs.settings.max_tile_zoom)
        self.threadpool.setMaxThreadCount(
            min(bs.settings.max_download_workers, max_dl))
        self.tileslot = TiledTexture.SlotHolder(self.load_tile)
        self.tilesource = tilesource
        self.tilesize = (256, 256)
        self.curtileext = (0, 0, 0, 0)
        self.curtilezoom = 1
        self.curtiles = OrderedDict()
        self.fullscreen = False
        self.offsetscale = np.array([0, 0, 1], dtype=np.float32)
        self.bbox = list()
        self.glsurface = glsurface
        self.indextexture = glh.Texture(target=glh.Texture.Target2D)
        self.indexsampler_loc = 0
        self.arraysampler_loc = 0
        bs.net.actnodedata_changed.connect(self.actdata_changed)
        Signal('panzoom').connect(self.on_panzoom_changed)

    def add_bounding_box(self, lat0, lon0, lat1, lon1):
        ''' Add the bounding box of a textured shape.

            These bounding boxes are used to determine if tiles need to be
            downloaded. '''
        if abs(lat1 - lat0) >= 178 and abs(lon1 - lon0) >= 358:
            self.fullscreen = True
        else:
            self.bbox.append((lat0, lon0, lat1, lon1))

    def create(self):
        ''' Create this texture in GPU memory. '''
        if self.isCreated():
            return
        super().create()
        # Fetch a temporary tile image to get dimensions
        tmptile = Tile(self.tilesource, 1, 1, 1, 0, 0)
        img = tmptile.image
        self.setFormat(glh.Texture.RGBA8_UNorm)
        self.tilesize = (img.width(), img.height())
        self.setSize(img.width(), img.height())
        self.setLayers(bs.settings.tile_array_size)
        super().bind()
        self.allocateStorage()
        self.setWrapMode(glh.Texture.DirectionS, glh.Texture.ClampToBorder)
        self.setWrapMode(glh.Texture.DirectionT, glh.Texture.ClampToBorder)
        self.setMinMagFilters(glh.Texture.Linear, glh.Texture.Linear)

        # Initialize index texture
        # RG = texcoord offset, B = zoom factor, A = array index
        itexw = int(np.sqrt(bs.settings.tile_array_size) * 4 / 3 + 10)
        itexh = int(np.sqrt(bs.settings.tile_array_size) * 3 / 4 + 10)
        self.indextexture.create()
        self.indextexture.setFormat(glh.Texture.RGBA32I)
        self.indextexture.setSize(itexw, itexh)
        self.indextexture.bind(1)
        # self.indextexture.allocateStorage(glh.Texture.RGBA_Integer, glh.Texture.Int32)

        idxdata = np.array(itexw * itexh * [(0, 0, 0, -1)], dtype=np.int32)
        glh.gl.glTexImage2D_alt(glh.Texture.Target2D, 0, glh.Texture.RGBA32I,
                                itexw, itexh, 0, glh.Texture.RGBA_Integer,
                                glh.Texture.Int32, idxdata.tobytes())

        self.indextexture.setWrapMode(glh.Texture.DirectionS,
                                      glh.Texture.ClampToBorder)
        self.indextexture.setWrapMode(glh.Texture.DirectionT,
                                      glh.Texture.ClampToBorder)
        self.indextexture.setMinMagFilters(glh.Texture.Nearest,
                                           glh.Texture.Nearest)

        shader = glh.ShaderSet.get_shader('tiled')
        self.indexsampler_loc = shader.uniformLocation('tile_index')
        self.arraysampler_loc = shader.uniformLocation('tile_texture')

    def bind(self, unit=0):
        ''' Bind this texture for drawing. '''
        # Set sampler locations
        glh.ShaderProgram.bound_shader.setUniformValue(self.indexsampler_loc,
                                                       2)
        glh.ShaderProgram.bound_shader.setUniformValue(self.arraysampler_loc,
                                                       4)
        # Bind index texture to texture unit 0
        self.indextexture.bind(2)
        # Bind tile array texture to texture unit 1
        super().bind(4)

    def actdata_changed(self, nodeid, nodedata, changed_elems):
        ''' Update tile buffers when a different node is selected, or when
            the data of the current node is updated. '''
        # Update pan/zoom
        if 'PANZOOM' in changed_elems:
            self.on_panzoom_changed(True)

    def on_panzoom_changed(self, finished=False):
        ''' Update textures whenever pan/zoom changes. '''
        # Check if textures need to be updated
        viewport = self.glsurface.viewportlatlon()
        surfwidth_px = self.glsurface.width()
        # First determine floating-point, hypothetical values
        # to calculate the required tile zoom level
        # floating-point number of tiles that fit in the width of the view
        ntiles_hor = surfwidth_px / self.tilesize[0]
        # Estimated width in longitude of one tile
        est_tilewidth = abs(viewport[3] - viewport[1]) / ntiles_hor

        self.curtilezoom = tilezoom(est_tilewidth, self.maxzoom)
        # With the tile zoom level get the required number of tiles
        # Top-left and bottom-right tiles:
        x0, y0 = latlon2tilenum(*viewport[:2], self.curtilezoom)
        x1, y1 = latlon2tilenum(*viewport[2:], self.curtilezoom)
        nx = abs(x1 - x0) + 1
        ny = abs(y1 - y0) + 1
        self.curtileext = (x0, y0, x1, y1)

        # Calculate the offset of the top-left tile w.r.t. the screen top-left corner
        tile0_topleft = np.array(tilenum2latlon(x0, y0, self.curtilezoom))
        tile0_bottomright = np.array(
            tilenum2latlon(x0 + 1, y0 + 1, self.curtilezoom))
        tilesize_latlon0 = np.abs(tile0_bottomright - tile0_topleft)
        offset_latlon0 = viewport[:2] - tile0_topleft
        tex_y0, tex_x0 = np.abs(offset_latlon0 / tilesize_latlon0)

        # Calculate the offset of the bottom-right tile w.r.t. the screen bottom right corner
        tile1_topleft = np.array(tilenum2latlon(x1, y1, self.curtilezoom))
        tile1_bottomright = np.array(
            tilenum2latlon(x1 + 1, y1 + 1, self.curtilezoom))
        tilesize_latlon1 = np.abs(tile1_bottomright - tile1_topleft)
        offset_latlon1 = viewport[2:] - tile1_topleft
        tex_y1, tex_x1 = np.abs(
            offset_latlon1 / tilesize_latlon1) + [ny - 1, nx - 1]
        # Store global offset and scale for shader uniform
        self.offsetscale = np.array(
            [tex_x0, tex_y0, tex_x1 - tex_x0, tex_y1 - tex_y0],
            dtype=np.float32)
        # Determine required tiles
        index_tex = []

        curtiles = OrderedDict()
        curtiles_difzoom = OrderedDict()
        for j, y in enumerate(range(y0, y1 + 1)):
            for i, x in enumerate(range(x0, x1 + 1)):
                # Find tile index if already loaded
                idx = self.curtiles.pop((x, y, self.curtilezoom), None)
                if idx is not None:
                    # correct zoom, so dx,dy=0, zoomfac=1
                    index_tex.extend((0, 0, 1, idx))
                    curtiles[(x, y, self.curtilezoom)] = idx
                else:
                    if finished:
                        # Tile not loaded yet, fetch in the background
                        task = TileLoader(self.tilesource, self.curtilezoom, x,
                                          y, i, j)
                        task.signals.finished.connect(self.tileslot.slot,
                                                      Qt.QueuedConnection)
                        self.threadpool.start(task)

                    # In the mean time, check if more zoomed-out tiles are loaded that can be used
                    for z in range(self.curtilezoom - 1,
                                   max(2, self.curtilezoom - 5), -1):
                        zx, zy, dx, dy = zoomout_tilenum(
                            x, y, z - self.curtilezoom)
                        idx = self.curtiles.pop((zx, zy, z), None)
                        if idx is not None:
                            curtiles_difzoom[(zx, zy, z)] = idx
                            zoomfac = int(2**(self.curtilezoom - z))
                            dxi = int(round(dx * zoomfac))
                            dyi = int(round(dy * zoomfac))
                            # offset zoom, so possible offset dxi, dyi
                            index_tex.extend((dxi, dyi, zoomfac, idx))
                            break
                    else:
                        # No useful tile found
                        index_tex.extend((0, 0, 0, -1))
        # Update curtiles ordered dict
        curtiles.update(curtiles_difzoom)
        curtiles.update(self.curtiles)
        self.curtiles = curtiles

        data = np.array(index_tex, dtype=np.int32)
        self.glsurface.makeCurrent()
        self.indextexture.bind(2)
        glh.gl.glTexSubImage2D_alt(glh.Texture.Target2D, 0, 0, 0, nx, ny,
                                   glh.Texture.RGBA_Integer, glh.Texture.Int32,
                                   data.tobytes())

    def load_tile(self, tile):
        ''' Send loaded image data to GPU texture array.

            This function is called on callback from the
            asynchronous image load function.
        '''
        # First check whether tile is still relevant
        # If there's a lot of panning/zooming, sometimes tiles are loaded that
        # become obsolete before they are even downloaded.
        if not ((self.curtilezoom == tile.zoom) and
                (self.curtileext[0] <= tile.tilex <= self.curtileext[2]) and
                (self.curtileext[1] <= tile.tiley <= self.curtileext[3])):
            return

        # Make sure our GL context is current
        self.glsurface.makeCurrent()

        # Check if tile is already loaded. This can happen here with multiple
        # pans/zooms shortly after each other
        layer = self.curtiles.get((tile.tilex, tile.tiley, tile.zoom), None)
        if layer is None:
            # This tile is not loaded yet. Select layer to upload it to
            layer = len(self.curtiles)
            if layer >= bs.settings.tile_array_size:
                # we're exceeding the size of the GL texture array.
                # Replace the least-recent tile
                _, layer = self.curtiles.popitem()

            self.curtiles[(tile.tilex, tile.tiley, tile.zoom)] = layer
            # Upload tile to texture array
            super().bind(4)
            self.setLayerData(layer, tile.image)
            self.release()

        # Update the ordering of the tile dict: the new tile should be on top
        self.curtiles.move_to_end((tile.tilex, tile.tiley, tile.zoom),
                                  last=False)

        # Update index texture
        idxdata = np.array([0, 0, 1, layer], dtype=np.int32)
        self.indextexture.bind(2)
        glh.gl.glTexSubImage2D_alt(glh.Texture.Target2D, 0, tile.idxx,
                                   tile.idxy, 1, 1, glh.Texture.RGBA_Integer,
                                   glh.Texture.Int32, idxdata.tobytes())

        self.indextexture.release()
Exemplo n.º 20
0
class Ui_MainWindow(object):
    """
    Some static colors that will be used often in the UI
    """
    GREEN = QtGui.QColor(196, 237, 194)
    YELLOW = QtGui.QColor(247, 247, 181)
    RED = QtGui.QColor(237, 194, 194)

    def __init__(self, MainWindow):
        """
        UI Setup (Nasty, auto-generated code with some modifications)
        :param MainWindow: the main PyQt window
        """
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(550, 540)
        MainWindow.setMinimumSize(QtCore.QSize(550, 525))
        MainWindow.setMaximumSize(QtCore.QSize(550, 525))
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.lblLogo = QtWidgets.QLabel(self.centralwidget)
        self.lblLogo.setGeometry(QtCore.QRect(10, 10, 111, 51))
        self.lblLogo.setText("")
        self.lblLogo.setPixmap(QtGui.QPixmap("data_files/DOH.gif"))
        self.lblLogo.setScaledContents(True)
        self.lblLogo.setObjectName("lblLogo")
        self.lblName = QtWidgets.QLabel(self.centralwidget)
        self.lblName.setGeometry(QtCore.QRect(150, 0, 391, 71))
        self.lblName.setStyleSheet("color: rgb(36, 21, 255)")
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(22)
        font.setBold(True)
        font.setWeight(75)
        self.lblName.setFont(font)
        self.lblName.setObjectName("lblName")
        self.hline1 = QtWidgets.QFrame(self.centralwidget)
        self.hline1.setGeometry(QtCore.QRect(0, 60, 551, 16))
        self.hline1.setFrameShape(QtWidgets.QFrame.HLine)
        self.hline1.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.hline1.setObjectName("hline1")
        self.lblPath = QtWidgets.QLabel(self.centralwidget)
        self.lblPath.setGeometry(QtCore.QRect(10, 70, 411, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(11)
        font.setBold(True)
        font.setWeight(75)
        self.lblPath.setFont(font)
        self.lblPath.setObjectName("lblPath")
        self.btnSelectFolder = QtWidgets.QPushButton(self.centralwidget)
        self.btnSelectFolder.setGeometry(QtCore.QRect(430, 70, 111, 31))
        self.btnSelectFolder.setObjectName("btnSelectFolder")
        self.btnStart = QtWidgets.QPushButton(self.centralwidget)
        self.btnStart.setGeometry(QtCore.QRect(10, 440, 531, 31))
        self.btnStart.setObjectName("btnStart")
        self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
        self.progressBar.setGeometry(QtCore.QRect(10, 480, 531, 20))
        self.progressBar.setProperty("value", 0)
        self.progressBar.setObjectName("progressBar")
        self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
        self.tableWidget.setGeometry(QtCore.QRect(10, 150, 531, 281))
        self.tableWidget.setColumnCount(3)
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setRowCount(0)
        self.tableWidget.setEditTriggers(QtWidgets.QTableWidget.NoEditTriggers)
        item = QtWidgets.QTableWidgetItem()
        font = QtGui.QFont()
        font.setFamily("Arial")
        item.setFont(font)
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()
        font = QtGui.QFont()
        font.setFamily("Arial")
        item.setFont(font)
        self.tableWidget.setHorizontalHeaderItem(1, item)
        item = QtWidgets.QTableWidgetItem()
        font = QtGui.QFont()
        font.setFamily("Arial")
        item.setFont(font)
        item.setTextAlignment(QtCore.Qt.AlignLeft)
        self.tableWidget.setHorizontalHeaderItem(2, item)
        self.lblUsername = QtWidgets.QLabel(self.centralwidget)
        self.lblUsername.setGeometry(QtCore.QRect(10, 110, 81, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(11)
        font.setBold(True)
        font.setWeight(75)
        self.lblUsername.setFont(font)
        self.lblUsername.setObjectName("lblUsername")
        self.txtUsername = QtWidgets.QLineEdit(self.centralwidget)
        self.txtUsername.setGeometry(QtCore.QRect(90, 110, 181, 31))
        self.txtUsername.setObjectName("txtUsername")
        self.lblPassword = QtWidgets.QLabel(self.centralwidget)
        self.lblPassword.setGeometry(QtCore.QRect(280, 110, 81, 31))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(11)
        font.setBold(True)
        font.setWeight(75)
        self.lblPassword.setFont(font)
        self.lblPassword.setObjectName("lblPassword")
        self.txtPassword = QtWidgets.QLineEdit(self.centralwidget)
        self.txtPassword.setGeometry(QtCore.QRect(360, 110, 181, 31))
        self.txtPassword.setObjectName("txtPassword")
        self.txtPassword.setEchoMode(QtWidgets.QLineEdit.Password)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 550, 25))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        """
        Set custom names and events for UI
        """
        self.retranslate_ui(MainWindow)
        self.setup_events()
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        """
        Thread pool for processing
        """
        self.threadpool = QThreadPool()
        self.threadpool.setMaxThreadCount(max_threads)
        """
        Variables for tracking progress
        """
        # The total number of upload attempts, successful or not
        self.completed_tasks = 0
        self.completed_tasks_lock = QtCore.QMutex()

    @staticmethod
    def show_error_message(msg):
        """
        Helper method to display a messagebox with an error
        :param msg: the error message
        :return: None
        """
        msg_box = QMessageBox()
        msg_box.setIcon(QMessageBox.Critical)
        msg_box.setText(msg)
        msg_box.setWindowTitle("There's a problem...")
        msg_box.setStandardButtons(QMessageBox.Ok)
        msg_box.exec_()

    def start(self):
        """
        Called when the user clicks the Begin Upload button.
        This function checks if there are valid MODS XML files loaded,
        whether there is a username and password and if there is, whether
        the username and password are valid. Then creates a threadpool and
        submits all the files to be processed
        :return: None
        """
        # Make sure table has files, username and password are entered
        if self.tableWidget.rowCount() is 0:
            self.show_error_message("No files have been loaded! Select" +
                                    " a folder with valid MODS XML files.")
        elif len(self.txtUsername.text().strip()) is 0:
            self.show_error_message("You must enter a username!")
        elif len(self.txtPassword.text().strip()) is 0:
            self.show_error_message("You must enter a password!")
        elif not sign_in(self.txtUsername.text(), self.txtPassword.text()):
            self.show_error_message(
                "Ensure your username and password are correct!")
        else:
            for i in range(self.tableWidget.rowCount()):
                items = []
                for j in range(self.tableWidget.columnCount()):
                    item = self.tableWidget.item(i, j)
                    items.append(str(item.text()))

                worker = Worker(upload, items, i)
                worker.signals.started.connect(self.started)
                worker.signals.result.connect(self.worker_response_handler)

                self.threadpool.start(worker)

    def started(self, row_index):
        """
        The function that handles a started emit from a Worker.
        When a upload task starts, it calls this method to update the UI
        and set the row to yellow to inform the user the row is being
        processed (i.e. the uploading process)
        :param row_index: the index of the row being processed
        :return: None
        """
        self.set_row_color(row_index, Ui_MainWindow.YELLOW)

    def worker_response_handler(self, completed):
        """
        The handler to handle a response from a Worker when
        an upload task is completed (not necessarily successful)
        :param completed: the tuple holding the result from the Worker
        :return: None
        """
        # Pattern match the tuple
        success, row_index = completed
        if success:
            self.set_row_color(row_index, Ui_MainWindow.GREEN)
        else:
            self.set_row_color(row_index, Ui_MainWindow.RED)

        # Safely increment completed tasks
        # and set progress bar value
        self.completed_tasks_lock.lock()
        self.completed_tasks = self.completed_tasks + 1
        self.progressBar.setValue(
            (self.completed_tasks / self.tableWidget.rowCount()) * 100)
        self.completed_tasks_lock.unlock()

    def load_xml_from_folder(self):
        """
        Loads all XML files from a folder recursively using globe.
        :return: None
        """
        # Reset the UI and its variables
        self.reset()
        # Create a set of all the files to upload
        file_list = set()
        for filename in glob.iglob(self.lblPath.text().replace("/", os.sep) +
                                   os.sep + '**' + os.sep + '*.xml',
                                   recursive=True):
            file_list.add(filename)
        # Set the new file count
        self.file_count = len(file_list)
        # Reset the table widget
        self.tableWidget.clearContents()
        # Add the rows
        self.tableWidget.setRowCount(len(file_list))
        # Populate the rows
        row_index = 0
        for filename in file_list:
            try:
                # Parse the path to get the repository and number
                fnms = filename.split(os.sep)
                objIDpts = fnms[-1].split("_")
                repository = objIDpts[0]
                number = objIDpts[1].split(".")[0]
                self.tableWidget.setItem(
                    row_index, 0, QtWidgets.QTableWidgetItem(repository))
                self.tableWidget.setItem(row_index, 1,
                                         QtWidgets.QTableWidgetItem(number))
                self.tableWidget.setItem(row_index, 2,
                                         QtWidgets.QTableWidgetItem(filename))
                row_index = row_index + 1
            except Exception as e:
                print(e)

        # No valid DOH files found
        if row_index is 0:
            self.tableWidget.setRowCount(0)

        # Set Path column size
        header = self.tableWidget.horizontalHeader()
        header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)

    def set_folder(self):
        """
        Sets the folder selected by the user in the label and
        calls the function to load XML files from it
        :return: None
        """
        dir_path = str(
            QFileDialog.getExistingDirectory(None, 'Select Folder',
                                             self.lblPath.text(),
                                             QFileDialog.ShowDirsOnly))
        if dir_path is not None and len(dir_path) > 0:
            self.lblPath.setText(dir_path.replace("/", os.sep))
            self.load_xml_from_folder()

    def reset(self):
        """
        Resets the UI variables and elements
        for another upload session
        :return: None
        """
        self.completed_tasks = 0
        self.progressBar.setValue(0)

    def setup_events(self):
        """
        Connects the UI buttons to relevant functions,
        called only once at the start of the program
        :return: None
        """
        self.btnSelectFolder.clicked.connect(self.set_folder)
        self.btnStart.clicked.connect(self.start)

    def set_row_color(self, row_index, color):
        """
        Sets the color of a row given the index of it
        :param row_index: the index of the row to color
        :param color: the color as a QtGui.QColor e.g. QtGui.QColor(QtCore.Qt.green)
        Can also use the static color variables set in the UI for yellow, red & green
        :return: None
        """
        for i in range(self.tableWidget.columnCount()):
            self.tableWidget.item(row_index, i).setBackground(color)

    def retranslate_ui(self, MainWindow):
        """
        Renames all the elements that need custom text from
        their default text
        :param MainWindow: the ui window
        :return: None
        """
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(
            _translate("MainWindow", "DOH - MODS XML Uploader"))
        self.lblName.setText(
            _translate("MainWindow", "DOH - MODS XML Uploader"))
        self.lblPath.setText(
            _translate("MainWindow",
                       os.path.dirname(os.path.realpath(__file__))))
        self.btnSelectFolder.setText(
            _translate("MainWindow", "Select Folder..."))
        self.btnStart.setText(_translate("MainWindow", "Start Upload"))
        item = self.tableWidget.horizontalHeaderItem(0)
        item.setText(_translate("MainWindow", "Repository"))
        item = self.tableWidget.horizontalHeaderItem(1)
        item.setText(_translate("MainWindow", "Number"))
        item = self.tableWidget.horizontalHeaderItem(2)
        item.setText(_translate("MainWindow", "Path"))
        self.lblUsername.setText(_translate("MainWindow", "Username"))
        self.lblPassword.setText(_translate("MainWindow", "Password"))
Exemplo n.º 21
0
class main_program(QObject):
    def __init__(self):
        super(main_program, self).__init__()
        self.video_source = None
        self.ui = Ui_MainWindow()
        self.threadpool = QThreadPool()
        self.threadpool.setMaxThreadCount(8)
        self.database = Database()
        self.combo_list = [
            "Görüntü kaynağını seçiniz...", "Webcam", "IP Kamera",
            "IP Kamera 2"
        ]
        self.is_signedin = False

    def button_connections(self):
        # Activate in main.
        self.ui.start_stop_button.clicked.connect(self.Start)
        # self.ui.stop_button.clicked.connect(self.Stop)

        self.ui.to_taskbar_button.clicked.connect(MainWindow.showMinimized)
        self.ui.quit_button.clicked.connect(sys.exit)

        self.ui.btn_page_1.clicked.connect(
            lambda: self.ui.stackedWidget.setCurrentWidget(self.ui.page_1))

        self.ui.btn_page_2.clicked.connect(
            lambda: self.ui.stackedWidget.setCurrentWidget(self.ui.page_2))

        self.ui.media_source_combobox.addItems(self.combo_list)
        self.ui.media_source_combobox.activated.connect(self.ComboSelected)
        self.ui.sign_out_button.clicked.connect(self.sign_out_clicked)
        self.ui.sign_in_button.clicked.connect(self.login_func)
        self.ui.reload_table_button.clicked.connect(self.load_report_table)

    @pyqtSlot(QImage)
    def setImage(self, image):
        self.ui.image_frame.setPixmap(QPixmap.fromImage(image))
        if self.detection.stopped:
            self.ui.image_frame.setPixmap(
                QtGui.QPixmap(":/placeholders/Resources/placeholder2.png"))

    @pyqtSlot(str)
    def setInfo(self, statement):
        self.ui.out_info_box.setText(statement)

    @pyqtSlot(str)
    def setTitleBox(self, statement):
        self.ui.info_title_box.setText(statement)

    def Start(self):
        if self.is_signedin:
            self.ui.out_info_box.setText("")
            if self.check_video_source():
                self.ui.image_frame.setPixmap(
                    QtGui.QPixmap("Resources/please_wait.png"))
                self.detection = Detection()
                self.detection.video_source = self.video_source

                # Connect signals and slots
                self.detection.signals.changeButton.connect(
                    self.change_start_context)
                self.detection.signals.changePixmap.connect(self.setImage)
                self.detection.signals.changeTextBox.connect(self.setInfo)
                self.detection.signals.changeTitleBox.connect(self.setTitleBox)

                self.threadpool.start(self.detection)
                self.change_start_context("stop_button")
            else:
                self.ui.out_info_box.setText("Video kaynağınızı kontrol edin.")
        else:
            self.ui.out_info_box.setText("Lütfen önce giriş yapınız.")

    @pyqtSlot(str)
    def change_start_context(self, mode):
        if mode == "start_button":
            self.ui.start_stop_button.clicked.disconnect()
            self.ui.start_stop_button.clicked.connect(self.Start)
            self.ui.start_stop_button.setStyleSheet(
                "QPushButton {\n"
                "color: rgb(255, 255, 255);\n"
                "border: 0px solid;\n"
                "background-color:  #667BC4;\n"
                "padding:0.5em;\n"
                "font-weight:bold;\n"
                "font-size:12px;\n"
                "}\n"
                "QPushButton:hover {\n"
                "    background-color: #7289DA;\n"
                "}")
            self.ui.start_stop_button.setText("Başlat")

        elif mode == "stop_button":
            self.ui.start_stop_button.clicked.disconnect()
            self.ui.start_stop_button.clicked.connect(self.Stop)
            self.ui.start_stop_button.setStyleSheet(
                "QPushButton {\n"
                "color: rgb(255, 255, 255);\n"
                "border: 0px solid;\n"
                "background-color:  #ff2115;\n"
                "padding:0.5em;\n"
                "font-weight:bold;\n"
                "font-size:12px;\n"
                "}\n"
                "QPushButton:hover {\n"
                "background-color: #ff392e;\n"
                "}")
            self.ui.start_stop_button.setText("Durdur")

    def check_video_source(self):
        if self.video_source is None:
            return False
        else:
            _, frame = cv2.VideoCapture(self.video_source).read()
            if frame is not None:
                return True
            else:
                return False

    # TODO: When loop ends, button context should change.
    def Stop(self):
        try:
            self.detection.stopped = True
            self.threadpool.clear()
            self.threadpool.releaseThread()
            # print("Active thread count last (stop): {}".format(self.threadpool.activeThreadCount()))
            # self.change_start_context("start_button")

        except AttributeError:
            print("Detection is not initialize yet.")

    def ComboSelected(self):
        media_source_str = self.ui.media_source_combobox.currentText()
        current_index = self.ui.media_source_combobox.currentIndex()
        if current_index == 0:
            self.video_source = None
        if current_index == 1:
            self.video_source = 0
        if current_index == 2:
            self.video_source = 'videos/toprakli_fit_buyuk.mp4'
        if current_index == 3:
            self.video_source = "videos/inek_field.mp4"

    ####################################################
    #                    SIGN IN - SIGN OUT            #
    ####################################################

    def starting_page(self):
        """
        This function answer this question: 'Did user sign in before or not?'
        """
        is_matching, file_path = self.database.get_data_n_check()
        if is_matching:
            self.change_profile_button_context(1)
        else:
            try:
                os.remove(file_path)
                self.ui.info_box.setText(
                    "Güvenlikle ilgili bir sorun meydana geldi.\nYeniden giriş yapın."
                )
            except FileNotFoundError:
                print("Usr.md file doesn't exist.")
            self.change_profile_button_context(0)

    def change_profile_button_context(self, status):
        # 0 is for NOT signed in, 1 is for signed in.
        icon = QtGui.QIcon()
        if status == 0:
            self.ui.btn_page_4.disconnect()
            self.ui.btn_page_4.clicked.connect(
                lambda: self.ui.stackedWidget.setCurrentWidget(self.ui.
                                                               sign_in_page))
            icon.addPixmap(
                QtGui.QPixmap(
                    ":/icons/Resources/icons/32px/user_alternative.png"),
                QtGui.QIcon.Normal, QtGui.QIcon.Off)
            self.ui.btn_page_4.setIcon(icon)
            self.is_signedin = False

        elif status == 1:
            self.ui.btn_page_4.disconnect()
            self.ui.btn_page_4.clicked.connect(
                lambda: self.ui.stackedWidget.setCurrentWidget(self.ui.
                                                               profile_page))
            icon.addPixmap(
                QtGui.QPixmap(":/icons/Resources/icons/32px/user_active.png"),
                QtGui.QIcon.Normal, QtGui.QIcon.Off)
            self.ui.btn_page_4.setIcon(icon)
            self.is_signedin = True
            # cu stands for current user.
            cu_email, cu_name, cu_surname = self.database.get_current_user_info(
            )
            self.ui.current_name_surname.setText("{} {}".format(
                cu_name, cu_surname))
            self.ui.current_email.setText("{}".format(cu_email))

    def login_func(self):
        self.ui.profile_info_box.setAlignment(QtCore.Qt.AlignCenter)
        if self.ui.email.text() == "" or self.ui.password.text() == "":
            self.ui.info_box.setStyleSheet("color:#ff5048;font-weight: 700;")
            self.ui.info_box.setText("Boş alanları doldurunuz.")
        else:
            encrypted_pass = self.database.encrpt_password(
                self.ui.password.text())
            email = self.ui.email.text()

            try:
                self.database.cursor.execute(
                    "select name, email, password, userID from User WHERE User.email = %s and User.password=%s",
                    (email, encrypted_pass))
                x = self.database.cursor.next()
                CURRENT_USER_NAME = x[0]
                CURRENT_USER_EMAIL = x[1]
                CURRENT_USER_ID = x[3]

                # Save user information to local.
                self.save_current_user_local(CURRENT_USER_EMAIL,
                                             CURRENT_USER_ID)
                # Change the context and icon of profile button
                self.change_profile_button_context(1)
                # Redirecting to page 2.
                self.ui.stackedWidget.setCurrentWidget(self.ui.profile_page)
                # Complete message.
                self.ui.profile_info_box.setText(f"Giriş başarılı.")

            except StopIteration:
                self.ui.info_box.setStyleSheet(
                    "color:#ff5048; font-weight: 700;")
                self.ui.info_box.setText("E-postanız veya parolanız yanlış.")

    def save_current_user_local(self, email, uid):
        file_exist_message = "Old user. [File exist]"
        # Get OS of user.
        platform_name = platform.system()

        if platform_name == "Windows":
            save_dir = os.getenv('APPDATA')
            save_dir += '\\Provactus\\'
            file_path = save_dir + '\\usr.md'

        elif platform_name == "Linux":
            save_dir = '/var/Provactus'
            file_path = '/var/Provactus/usr.md'

        # Trying to create file.
        try:
            os.mkdir(save_dir)
        except FileExistsError:
            print(file_exist_message)
        # Local registry for following usages.
        with open(file_path, 'w+') as file:
            file.write(f"{uid}\n{email}\n")

    def sign_out_clicked(self):
        # Get OS of user.
        platform_name = platform.system()
        if platform_name == "Windows":
            save_dir = os.getenv('APPDATA')
            save_dir += '\\Provactus\\'
            file_path = save_dir + '\\Provactus\\usr.md'

        elif platform_name == "Linux":
            file_path = "/var/Provactus/usr.md"

        os.remove(file_path)
        self.change_profile_button_context(0)
        # Redirecting to page 2.
        self.ui.stackedWidget.setCurrentWidget(self.ui.sign_in_page)
        self.ui.info_box.setText("")

    ####################################################
    #                   Report                         #
    ####################################################
    def load_report_table(self):

        if self.is_signedin:
            t = datetime.now()
            current_time = t.strftime("%d/%m/%y %H:%M:%S.%f")[:-4]
            self.report_page_controls(True)
            self.ui.report_info_box.setText(
                "Son güncellenme tarihi: {}".format(current_time))
            # To update the information, we have to re-initialize the db.
            self.database = Database()
            (report_id, date, elapsed, total_left, total_right, user_id,
             enter_position) = self.database.get_reports(
                 self.database.get_id_local())
            var_collection = [date, elapsed, total_left, total_right]
            var_collection_rev = [date, elapsed, total_right, total_right]

            # reverse vars
            for var in var_collection:
                var.reverse()
            # listing
            if len(report_id) != 0:
                self.ui.report_table.setRowCount(len(report_id))

                for col_n in range(4):
                    for row_n in range(len(report_id)):
                        if enter_position[row_n] == "right":
                            self.ui.report_table.setItem(
                                row_n, col_n,
                                QtWidgets.QTableWidgetItem(
                                    str(var_collection[col_n][row_n])))
                        elif enter_position[row_n] == "left":
                            self.ui.report_table.setItem(
                                row_n, col_n,
                                QtWidgets.QTableWidgetItem(
                                    str(var_collection_rev[col_n][row_n])))

            else:
                self.report_page_controls(False)
                self.ui.report_info_box.setText("Henüz rapor oluşturmadınız.")
        else:
            self.report_page_controls(False)
            self.ui.report_info_box.setText("Önce giriş yapınız.")

    def report_page_controls(self, is_okay):
        self.ui.report_info_box.setAlignment(QtCore.Qt.AlignCenter)
        if is_okay is True:

            self.ui.report_info_box.setStyleSheet("color: #FFFFFF;\n"
                                                  "font-weight: 700;\n"
                                                  "font-size: 14px;\n"
                                                  "margin:2em;\n"
                                                  "")
            self.ui.report_table.show()
            self.ui.report_info_box.setText("")
        else:
            self.ui.report_info_box.setStyleSheet("color: #ff5048;\n"
                                                  "font-weight: 700;\n"
                                                  "font-size: 14px;\n"
                                                  "margin:2em;\n"
                                                  "")
            self.ui.report_table.close()
            self.ui.report_info_box.show()
Exemplo n.º 22
0
class BaseProcess(QObject):

    def __init__(self, queue_name, default_limit):
        super().__init__()
        self.running_tasks = {}
        self.done_tasks = []
        self.default_limit = default_limit
        self.queue = PGQ(queue_name)
        self.queue_name = queue_name
        self.pool = QThreadPool(self)
        self.pool.setMaxThreadCount(5000)
        signal.signal(signal.SIGUSR1, self.__signal_handler)
        self.items = []
        try:
            __items = TaskModel.select().where((TaskModel.status == constants.STATUS_RUNNING) & (TaskModel.queue_name == self.queue_name))
            for __item in __items:
                self.items.append(__item)
        except OperationalError:
            raise NetworkError('Network is unreachable')

    def run(self):
        while 1:
            # sleep for decrease cpu usage
            sleep(0.01)
            QCoreApplication.processEvents()
            from_db = False
            if self.running_tasks.__len__() >= self.limit():
                continue
            try:
                if self.items:
                    from_db = True
                    item = self.items.pop()
                else:
                    item = self.queue.get()  # type: RequestObject
                if item:
                    if not from_db:
                        task_model = TaskModel()
                        task_model.route = item.route
                        task_model.process_id = item.process_id
                        task_model.status = constants.STATUS_RUNNING
                        task_model.data = item.data
                        task_model.call_back = item.call_back
                        task_model.token = item.token
                        task_model.module_version = item.module_version
                        task_model.queue_name = self.queue_name
                        task_model.save()
                    else:
                        task_model = item

                    task = Task(task_model)
                    if task.instance_module:
                        task.instance_module.task_finished.connect(self.__task_finished)
                        task.setAutoDelete(True)
                        self.running_tasks.update({item.process_id: task})
                        # check cancel or pause request before start
                        self.apply_action_by_pid(task, item.process_id)
                        self.pool.start(task)
                    else:
                        # TODO: set error alarm
                        ApiLogging.error('problem running task')
            except Exception as e:
                ApiLogging.error('process exception' + str(e))
                # TODO: set error alarm
                continue

    def limit(self):
        try:
            limit = Setting.get(self.queue_name)
            if limit:
                return int(limit)
        except Exception:
            # use default limit if exception or not present
            pass
        return self.default_limit

    @pyqtSlot(str, name='task_finished')
    def __task_finished(self, task_id):
        ApiLogging.info('task finished')
        del self.running_tasks[task_id]

        self.done_tasks.append(task_id)
        self.send_signal()

    @staticmethod
    def send_signal():
        pids = []
        for process_name in constants.APP_PROCESSES:
            if process_name.get('name') == 'process_sync.py':
                pids = find_pid(process_name.get('token'))
        if len(pids) > 1:
            ApiLogging.warning('Too many sync process running')
        elif len(pids) == 1:
            p = psutil.Process(pids[0])
            p.send_signal(signal.SIGUSR1)

    def __signal_handler(self, signal, frame):
        ApiLogging.info('base process signal received')
        # TODO: update limit
        self.check_pending_actions()

    def apply_action_by_pid(self, task, pid):
        actions = ActionModel.select().where(ActionModel.process_id == pid)
        for action in actions:
            if action.action == constants.STATUS_PAUSE:
                task.instance_module.pause_request = action.timeout
            elif action.action == constants.STATUS_CANCEL:
                task.instance_module.cancel_request = True
            elif action.action == constants.STATUS_RESUME:
                task.instance_module.resume_request = True

            action.status = constants.STATUS_SUCCESS
            action.save()

    def check_pending_actions(self):
        actions = ActionModel.select().where(ActionModel.status == constants.STATUS_PENDING)
        for action in actions:
            if action.process_id in self.running_tasks:
                task = self.running_tasks.get(action.process_id)
                if task:
                    if action.action == constants.STATUS_PAUSE:
                        task.instance_module.pause_request = action.timeout
                    elif action.action == constants.STATUS_CANCEL:
                        task.instance_module.cancel_request = True
                    elif action.action == constants.STATUS_RESUME:
                        task.instance_module.resume_request = True

                action.status = constants.STATUS_SUCCESS
                action.save()
            elif action.process_id in self.done_tasks:
                action.status = constants.STATUS_SUCCESS
                action.save()
Exemplo n.º 23
0
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QDate, QRunnable, QObject, pyqtSignal, pyqtSlot, QThreadPool, QMutex
from PyQt5.QtWidgets import QMessageBox, QAbstractItemView
from robobrowser import RoboBrowser
import getDOHCollns
import os
import re
from datetime import datetime
"""
Thread pool for all the threads
"""
threadpool = QThreadPool()
threadpool.setMaxThreadCount(100)
"""
The session that will be used by
all browser instances to access the
collections. Set in sign_in()
"""
global_session = None
"""
Regex to get individual collection objects that end
in numbers
"""
num_str = r'/islandora/object/\D*?%3A\d*?$'  # [a-z]+%3A[0-9]+
"""
Track how many MODS XML it is possible to download.
This value changes based on how many collections the
user selects.
"""
downloadable = 0
"""
Exemplo n.º 24
0
class videoDownloaderGui(QMainWindow):
    def __init__(self, parent=None):
        '''
        sets up videoDownloader gui
        '''

        super().__init__(parent)
        self.setGeometry(100, 100, 2000, 1250)
        self.setWindowTitle('DownTube')
        self.setWindowIcon(QIcon(resource_path('./gui/py.png')))
        central = QWidget()
        self.setCentralWidget(central)
        self.statusBar = QStatusBar()
        self.setStatusBar(self.statusBar)

        self.settings = {}
        self.settingsFile = 'settings.pickle'

        if os.path.isfile(self.settingsFile):
            self.loadSettings()
        else:
            self.settings['path'] = addToPath(findDesktop(),
                                              '/downloaded videos')
            self.settings['defultRes'] = '720p'
            self.dumpSettings()

        self.index = 0
        self.videos = []
        self.resolution = self.settings['defultRes']
        self.downloadPath = self.settings['path']
        self.downloadCounter = 0
        self.progressbars = []
        self.downloading = {}
        self.downloadTitles = []

        self.status = []
        self.botbots = []
        self.threadpool = QThreadPool()
        self.threadpool.setMaxThreadCount(10)
        self.startbot = self.threadpool.start
        self.grid = QGridLayout()
        self.grid.setAlignment(Qt.AlignCenter)
        self.api = None

        central.setLayout(self.grid)

        self.makeAndSendBot(self.connectToApi)

        mainMenu = self.menuBar()
        settingsMenu = mainMenu.addMenu('&Settings')
        fileLocation = QAction("&Download locaition", self)
        fileLocation.setShortcut("Ctrl+SHIFT+S")
        fileLocation.setStatusTip(
            'set the file location to download videos to')
        fileLocation.triggered.connect(self.getfolder)

        settingsMenu.addAction(fileLocation)

        resoulutionMenu = mainMenu.addMenu('&Resoulution')
        for res in ['360p', '720p']:
            resoulution = QAction(f"&{res}", self)
            resoulution.setStatusTip(f'set the download size to {res}')
            resoulution.triggered.connect(partial(self.setRes, res))
            resoulutionMenu.addAction(resoulution)

        self.description = QTextBrowser()
        self.description.setText(
            'description:\ntype somthing in to look for videos\nenjoy 🤞')
        self.description.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding,
                        QSizePolicy.MinimumExpanding))
        self.description.setStatusTip('the description of the current video')

        self.searchbar = QLineEdit()
        self.searchbar.setPlaceholderText('search for video or enter url')
        self.searchbar.setStyleSheet(
            "border: 2px solid black;border-radius: 10px;font-size: 36px;")
        self.searchbar.setSizePolicy(
            QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred))
        self.searchbar.setStatusTip('search for a video')
        self.searchbar.returnPressed.connect(self.searchVids)

        self.downloadbtn = QPushButton()
        self.downloadbtn.setText('Download')
        self.downloadbtn.setStyleSheet(
            'QPushButton {background-color: darkgray; border: 1px solid black;border-radius: 10px;font-size: 30px;color: white;padding: 5px 15px;}'
        )
        self.downloadbtn.setEnabled(False)
        self.downloadbtn.setSizePolicy(
            QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred))
        self.downloadbtn.setCursor(QCursor(Qt.PointingHandCursor))
        self.downloadbtn.setStatusTip('download the current video')
        self.downloadbtn.clicked.connect(
            lambda: self.makeAndSendBot(self.downloadVideo,
                                        startedFunc=self.onDownloadStart,
                                        progressFunc=self.showBars,
                                        finishedFunc=self.downloadBtnSetup,
                                        errorFunc=self.downloadFailed))

        self.backbtn = QPushButton()
        self.backbtn.setText('<<<')
        self.backbtn.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred))
        self.backbtn.setCursor(QCursor(Qt.PointingHandCursor))
        self.backbtn.clicked.connect(lambda: self.changeIndex(-1))
        self.backbtn.setStatusTip('go to last video')
        self.backbtn.setEnabled(False)

        self.nextbtn = QPushButton()
        self.nextbtn.setText('>>>')
        self.nextbtn.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred))
        self.nextbtn.setCursor(QCursor(Qt.PointingHandCursor))
        self.nextbtn.clicked.connect(lambda: self.changeIndex(1))
        self.nextbtn.setStatusTip('go to next video')
        self.nextbtn.setEnabled(False)

        self.picture = QLabel()
        img = QPixmap(resource_path('./gui/youtube-logo.jpg'))
        img = img.scaled(1280, 720)
        self.picture.setPixmap(img)
        self.picture.setStatusTip('the thumbnail of the video selected')

        self.titleDisplay = QLabel()
        self.titleDisplay.setText('search for a video')
        self.titleDisplay.setStatusTip('the title of the selected video')

        self.videoType = QComboBox()
        self.videoType.addItems(['video and audio', 'audio only'])
        self.videoType.setCursor(QCursor(Qt.PointingHandCursor))
        self.videoType.setStatusTip('pick your download format')

        self.progressGrid = QGridLayout()
        progressSpacer = QSpacerItem(20, 200, QSizePolicy.Minimum,
                                     QSizePolicy.MinimumExpanding)

        for i in range(20):
            if i % 2 == 0:
                title = QLabel()
                title.setWordWrap(True)
                self.progressGrid.addWidget(title, i, 0, 1, 2)
                self.downloadTitles.append(title)
                title.setHidden(True)
            else:
                prog = QProgressBar()
                prog.setValue(100)
                self.progressGrid.addWidget(prog, i, 0, 1, 2)
                self.progressbars.append(prog)
                prog.setHidden(True)

        self.progressGrid.addItem(progressSpacer, 20, 0)

        searchbarSpacer = QSpacerItem(20, 60, QSizePolicy.Minimum,
                                      QSizePolicy.Preferred)
        descriptionSpacer = QSpacerItem(325, 20, QSizePolicy.Preferred,
                                        QSizePolicy.Minimum)
        downloadSpacer = QSpacerItem(20, 25, QSizePolicy.Maximum,
                                     QSizePolicy.Preferred)
        progressGridSpacer = QSpacerItem(200, 20, QSizePolicy.Preferred,
                                         QSizePolicy.Minimum)

        self.grid.addWidget(self.videoType, 3, 9, 1, 2)
        self.grid.addItem(progressGridSpacer, 1, 0, rowSpan=1, columnSpan=2)
        self.grid.addWidget(self.titleDisplay, 3, 5, 1, 3)
        self.grid.addWidget(self.picture, 4, 5, 3, 6)
        self.grid.addWidget(self.backbtn, 7, 5, 1, 3)
        self.grid.addWidget(self.nextbtn, 7, 8, 1, 3)
        self.grid.addItem(downloadSpacer, 8, 6, rowSpan=1, columnSpan=1)
        self.grid.addWidget(self.downloadbtn, 9, 5, 1, 6)
        self.grid.addItem(searchbarSpacer, 10, 4, rowSpan=1, columnSpan=1)
        self.grid.addWidget(self.searchbar, 11, 4, 1, 10)
        self.grid.addItem(descriptionSpacer, 8, 14, rowSpan=1, columnSpan=4)
        self.grid.addWidget(self.description, 3, 14, 4, 3)
        self.grid.addLayout(self.progressGrid, 4, 0, 3, 4)

    def connectToApi(self):
        self.api = ytsearch.youtubeConnect(key.key)

    def setRes(self, resoulution):
        self.resolution = resoulution

    def searchVids(self):
        '''
        search through the youtube api and acts acordingly
        '''
        while not self.api:
            time.sleep(.05)
        matchedText = re.search('youtube\.com/watch\?v=(.+)',
                                self.searchbar.text())
        if matchedText:
            url = matchedText.group(1)
            self.videos = [
                self.api.video_by_url(url,
                                      thumbnailQuality='maxres',
                                      defultQuality='high')
            ]
        else:
            self.videos = self.api.video_search(self.searchbar.text(),
                                                limit=10,
                                                thumbnailQuality='maxres',
                                                defultQuality='high')
        videoCount = len(self.videos)
        if videoCount >= 1:
            self.index = 0
            self.status = ['download' for x in self.videos]
            self.downloadThumbnails()
            self.setVideo()
            if videoCount == 1:
                self.nextbtn.setEnabled(False)
                self.backbtn.setEnabled(False)
            else:
                self.nextbtn.setEnabled(True)
                self.backbtn.setEnabled(False)
            self.searchbar.setPlaceholderText('search for video or enter url')

        else:
            self.emptySearch()

    def emptySearch(self):
        '''
        sets up a empty search gui
        '''
        self.searchbar.setText('')
        self.searchbar.setPlaceholderText(
            'no videos availaible that match the search')
        self.nextbtn.setEnabled(False)
        self.backbtn.setEnabled(False)
        img = QPixmap(resource_path('./gui/empty.jpg'))
        img = img.scaled(1280, 720)
        self.picture.setPixmap(img)
        self.downloadBtnSetup(state='empty')

    def downloadThumbnails(self):
        '''
        downloads the thumbnails of all the videos so you could display them later
        '''
        count = 0
        for vid in self.videos:
            urlretrieve(vid.thumbnailLink,
                        f'gui/thumbnails/thumbnail{count}.jpg')
            count += 1

    def setVideo(self):
        '''
        set up the gui based on the current video
        '''
        img = QPixmap(
            resource_path(f'gui/thumbnails/thumbnail{self.index}.jpg'))
        img = img.scaled(1280, 720, transformMode=Qt.FastTransformation)
        self.picture.setPixmap(img)
        self.description.setText(self.videos[self.index].description)
        self.setTitleText(self.videos[self.index].title)
        self.downloadBtnSetup()

    def setTitleText(self, text):
        '''
        displays title text above thumbnail
        '''
        if len(text) >= 40:
            self.titleDisplay.setText(text[:40] + '...')
        else:
            self.titleDisplay.setText(text)

    def changeIndex(self, num):
        '''
        changes the index if its not out of range
        '''
        self.index += num
        if self.index + 1 == len(self.videos):
            self.nextbtn.setEnabled(False)
            self.backbtn.setEnabled(True)
        elif self.index == 0:
            self.backbtn.setEnabled(False)
            self.nextbtn.setEnabled(True)
        else:
            self.backbtn.setEnabled(True)
            self.nextbtn.setEnabled(True)

        self.setVideo()

    def downloadBtnSetup(self, state=None):
        '''
        sets up the download bar based on the currently selected video
        '''
        if not state:
            state = self.status[self.index]

        if state == 'downloading':
            self.downloadbtn.setText('Downloading..')
            bgColor = 'darkgray'
            bgColorHover = 'gray'
            bgColorPressed = 'light gray'
            self.downloadbtn.setEnabled(False)
        elif state == 'downloaded':
            self.downloadbtn.setText('Download again')
            bgColor = 'gray'
            bgColorHover = 'lightgray'
            bgColorPressed = 'lightgray'
            self.downloadbtn.setEnabled(True)
        elif state == 'download':
            self.downloadbtn.setText('Download')
            bgColor = 'rgb(0, 85, 255)'
            bgColorHover = 'rgb(89, 111, 255)'
            bgColorPressed = 'lightblue'
            self.downloadbtn.setEnabled(True)
        elif state == 'empty':
            self.downloadbtn.setText('Download')
            bgColor = 'darkgray'
            bgColorHover = 'gray'
            bgColorPressed = 'light gray'
            self.downloadbtn.setEnabled(False)
        elif state == 'error':
            self.downloadbtn.setText('Download Failed')
            bgColor = 'red'
            bgColorHover = 'orange'
            bgColorPressed = 'darkred'
            self.downloadbtn.setEnabled(True)

        self.downloadbtn.setStyleSheet(
            'QPushButton {background-color: %s; border: 1px solid black;border-radius: 10px;font-size: 30px;color: white;padding: 5px 15px;}'
            'QPushButton:pressed { background-color: %s }'
            'QPushButton:hover { background-color: %s }' %
            (bgColor, bgColorPressed, bgColorHover))

    def onDownloadStart(self):
        self.status[self.index] = 'downloading'
        self.downloadBtnSetup()

    def downloadVideo(self):
        '''
        downloads video that is currently selected
        '''
        index = self.index
        video = self.videos[index]
        print('downloading ' + video.url)
        vid = None
        for i in range(4):
            try:
                yt = YouTube(video.url,
                             on_progress_callback=self.progress_func)
                vid = yt.streams.filter(res=self.resolution,
                                        progressive=True).first()
                vid.download(self.settings['path'])
                self.status[index] = 'downloaded'
                print('download complete')
                return
            except:
                print(f'tried {i} time(s)')
                if vid in self.downloading:
                    index = self.downloading.pop(vid)
                    self.progressbars[index].setHidden(True)
                    self.downloadTitles[index].setHidden(True)

        yt = YouTube(video.url, on_progress_callback=self.progress_func)
        vid = yt.streams.filter(res=self.resolution, progressive=True).first()
        vid.download('./videos')
        self.status[index] = 'downloaded'
        print('download complete')
        return

    def downloadFailed(self, error):
        print(error)
        self.status[self.index] = 'error'
        self.downloadBtnSetup()

    def progress_func(self, stream, chunk, bytes_remaining):
        size = stream.filesize
        progress = (float(abs(bytes_remaining - size) / size)) * float(100)
        self.bot.signals.progress.emit((stream, int(progress)))

    def showBars(self, info):
        '''
        display all progressBars in a list
        '''
        stream, progress = info
        if stream not in self.downloading:
            self.downloadCounter += 1
            self.downloading[stream] = self.downloadCounter % 10
            index = self.downloading[stream]
            self.downloadTitles[index].setText(stream.title)
            self.downloadTitles[index].setHidden(False)
        else:
            index = self.downloading[stream]
        self.progressbars[index].setValue(progress)
        self.progressbars[index].setHidden(False)
        if progress == 100:
            self.downloadTitles[index].setHidden(True)
            self.progressbars[index].setHidden(True)
            self.downloading.pop(stream)
        return

    def getfolder(self):
        dirName = QFileDialog.getExistingDirectory(caption='open a folder',
                                                   directory='c:\\')
        if dirName != '':
            self.settings['path'] = os.path.normpath(dirName)
            self.dumpSettings()

    def loadSettings(self):
        with open(self.settingsFile, 'rb') as f:
            self.settings = pickle.load(f)

    def dumpSettings(self):
        with open(self.settingsFile, 'wb') as f:
            pickle.dump(self.settings, f)

    def makeAndSendBot(self, func, *args, **kwargs):
        '''
        makes and sends out a thread
        '''
        self.bot = makebot(func, *args, **kwargs)
        self.startbot(self.bot)
Exemplo n.º 25
0
        super().__init__()

    def run(self):
        index = 0
        if not os.path.exists(
                f"download/{self.qq}/{self.item[:self.item.find('.json')]}"):
            os.makedirs(
                f"download/{self.qq}/{self.item[:self.item.find('.json')]}")
        for item in self.l:
            print("正在处理", index, self.qq)
            url = item.get("photo") or item.get('video')
            html = requests.get(url)
            if html.status_code != 200:
                print("下载失败!")
                return None
            filename = f"download/{self.qq}/{self.item[:self.item.find('.json')]}/{index}.{'mp4' if item.get('video') else 'jpeg'}"
            with open(filename, 'wb') as f:
                f.write(html.content)
                index += 1


threadpool = QThreadPool()
threadpool.setMaxThreadCount(8)
qqs = [""]
for qq in qqs:
    for item in os.listdir(f"data/{qq}"):
        with open(f"data/{qq}/{item}", encoding='utf-8') as f:
            urls = json.load(f)
        threadpool.start(Download(urls, qq, item))
threadpool.waitForDone()
Exemplo n.º 26
0
class Window(QMainWindow, ClipableObserver):
    def __init__(self, brres_files=[]):
        super().__init__()
        self.open_files = []
        self.brres = None
        self.image_updater = {}  # maps brres to list of subscribers
        self.cwd = os.getcwd()
        self.__init_threads()
        self.locked_files = set(
        )  # lock files that are pending conversion etc...
        # AutoFix.get().set_pipe(self)
        self.__init_UI()
        self.shell_is_shown = False
        self.shell = None
        for file in brres_files:
            self.open(file.name)
        AutoFix.get().info('Initialized main window', 5)
        self.show()

    def __init_threads(self):
        AutoFix.get().info('Starting threads...', 5)
        self.threadpool = QThreadPool()  # for multi-threading
        self.threadpool.setMaxThreadCount(5)
        self.converter = converter = ConvertManager.get()
        converter.signals.on_conversion_finish.connect(
            self.on_conversion_finish)
        self.image_manager = image_manager = ImageManager.get()
        if image_manager.enabled:
            image_manager.signals.on_image_update.connect(self.on_image_update)
            self.threadpool.start(image_manager)
        else:
            AutoFix.get().warn(
                'Image Manager disabled, do you have Wiimms SZS Tools installed?'
            )
        self.threadpool.start(converter)
        log_pipe = LoggerPipe()
        log_pipe.info_sig.connect(self.info)
        log_pipe.warn_sig.connect(self.warn)
        log_pipe.error_sig.connect(self.error)

    def __init_menus(self):
        # Files
        # Exit
        exit_act = QAction('&Exit', self)
        exit_act.setShortcut('Ctrl+q')
        exit_act.setStatusTip('Exit Application')
        exit_act.triggered.connect(self.close)
        # Open
        open_act = QAction('&Open', self)
        open_act.setShortcut('Ctrl+o')
        open_act.setStatusTip('Open a Brres file')
        open_act.triggered.connect(self.open_dialog)
        # Save
        save_act = QAction('&Save', self)
        save_act.setShortcut('Ctrl+s')
        save_act.setStatusTip('Save file')
        save_act.triggered.connect(self.save)
        # Save as
        save_as = QAction('Save &As', self)
        save_as.setStatusTip('Save file as')
        save_as.triggered.connect(self.save_as_dialog)
        # import
        import_act = QAction('&Import', self)
        import_act.setShortcut('Ctrl+i')
        import_act.setStatusTip('Import file')
        import_act.triggered.connect(self.import_file_dialog)
        # export
        export_act = QAction('&Export', self)
        export_act.setShortcut('Ctrl+e')
        export_act.setStatusTip('Export file')
        export_act.triggered.connect(self.export_file_dialog)
        # File Menu
        menu = self.menuBar()
        fileMenu = menu.addMenu('&File')
        fileMenu.addAction(open_act)
        fileMenu.addAction(save_act)
        fileMenu.addAction(save_as)
        fileMenu.addSeparator()
        fileMenu.addAction(import_act)
        fileMenu.addAction(export_act)
        fileMenu.addSeparator()
        fileMenu.addAction(exit_act)

        # Tools
        shell_Act = QAction('&Interactive Shell', self)
        shell_Act.setShortcut('Ctrl+Shift+I')
        shell_Act.setStatusTip('Run interactive commands')
        shell_Act.triggered.connect(self.open_interactive_shell)
        kcl_calc_Act = QAction('&KCL Calculator', self)
        kcl_calc_Act.setShortcut('Ctrl+k')
        kcl_calc_Act.setStatusTip('KCL Flag Calculator')
        kcl_calc_Act.triggered.connect(self.open_kcl_calculator)
        toolMenu = menu.addMenu('&Tools')
        toolMenu.addAction(shell_Act)
        toolMenu.addAction(kcl_calc_Act)

        # Help
        report_Act = QAction('&Report Issue', self)
        report_Act.setStatusTip('Report an issue')
        report_Act.triggered.connect(self.report_issue)
        website_Act = QAction('&Website', self)
        website_Act.setStatusTip('Visit website')
        website_Act.triggered.connect(self.open_website)
        about_Act = QAction('&About', self)
        about_Act.setStatusTip('Information about ABMatt')
        about_Act.triggered.connect(self.about_abmatt)
        help_menu = menu.addMenu('&Help')
        help_menu.addAction(report_Act)
        help_menu.addAction(website_Act)
        help_menu.addSeparator()
        help_menu.addAction(about_Act)

    def open_website(self):
        webbrowser.open('https://github.com/Robert-N7/abmatt')

    def report_issue(self):
        webbrowser.open('https://github.com/Robert-N7/abmatt/issues')

    def about_abmatt(self):
        self.box = QMessageBox()
        bit_size = '64-Bit' if sys.maxsize > 2**32 else '32-Bit'
        self.box.setText(
            f'ABMatt Version {load_config.VERSION} {platform.platform()} {bit_size}'
        )
        self.box.setWindowTitle('ABMatt')
        self.box.show()

    def open_kcl_calculator(self):
        self.calculator = KCLCalculator()

    def open_interactive_shell(self):
        if not self.shell_is_shown:
            if self.shell is None:
                self.shell = InteractiveCmd()
                self.left.addWidget(self.shell)
            else:
                self.shell.show()
            self.shell_is_shown = True
        else:
            self.shell_is_shown = False
            self.shell.hide()

    def locate_material(self, brres_path):
        return self.material_browser.locate_material(brres_path)

    def __init_child_UI(self, top_layout):
        # left
        vert_widget = QWidget(self)
        # policy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding)
        # policy.setHorizontalStretch(2)
        # vert_widget.setSizePolicy(policy)
        top_layout.addWidget(vert_widget)
        self.left = vert_layout = QVBoxLayout()
        vert_widget.setLayout(vert_layout)
        widget = QWidget(self)
        vert_layout.addWidget(widget)
        center_layout = QHBoxLayout()
        widget.setLayout(center_layout)
        self.logger = QPlainTextEdit(self)
        self.logger.setReadOnly(True)
        self.logger.setFixedHeight(100)
        vert_layout.addWidget(self.logger)

        self.treeview = BrresTreeView(self)
        self.treeview.setMinimumWidth(300)
        center_layout.addWidget(self.treeview)
        self.poly_editor = PolyEditor(self)
        center_layout.addWidget(self.poly_editor)
        # center_widget.setGeometry(0, 0, 300, 300)

        # right
        # top_layout.addSpacing(30)
        self.material_browser = MaterialTabs(self)
        policy = QSizePolicy(QSizePolicy.Preferred,
                             QSizePolicy.MinimumExpanding)
        policy.setHorizontalStretch(1)
        self.material_browser.setSizePolicy(policy)
        top_layout.addWidget(self.material_browser)
        # self.material_browser.setFixedWidth(300)

    def __init_UI(self):
        self.setWindowTitle('ANoobs Brres Material Tool')
        self.resize(1000, 700)
        self.__init_menus()
        main_layout = QHBoxLayout()
        self.__init_child_UI(main_layout)
        widget = QWidget()
        widget.setLayout(main_layout)
        self.setCentralWidget(widget)
        self.statusBar().showMessage('Ready')

    def on_image_update(self, brres_dir):
        # simply gives it back to the image manager to update observers
        self.image_manager.notify_image_observers(*brres_dir)

    def on_update_polygon(self, poly):
        enable_edits = poly.parent.parent not in self.locked_files
        self.poly_editor.on_update_polygon(poly, enable_edits=enable_edits)

    def emit(self, message, head=None, tail=None):
        message = message.replace('\n', '<br/>').replace(' ', '&nbsp;')
        if head:
            message = head + message
        if tail:
            message += tail
        self.logger.appendHtml(message)

    def get_brres_by_fname(self, fname):
        path = os.path.abspath(fname)
        for x in self.open_files:
            if os.path.abspath(x.name) == path:
                return x

    def set_brres(self, brres):
        if brres != self.brres:
            self.brres = brres

    def open_dialog(self):
        fname, filter = QFileDialog.getOpenFileName(self, 'Open file',
                                                    self.cwd,
                                                    "Brres files (*.brres)")
        if fname:
            self.open(fname)

    def on_node_update(self, node):
        self.treeview.on_brres_update(node)

    def on_rename_update(self, node, old_name):
        if type(node) == Brres:
            self.treeview.on_brres_rename(old_name, node.name)
        self.material_browser.on_name_update()

    def on_child_update(self, child):
        self.treeview.on_brres_update(child.parent)

    def open(self, fname, force_update=False):
        self.cwd = os.path.dirname(fname)
        opened = self.get_brres_by_fname(fname)
        if opened:
            if not force_update:
                return self.set_brres(opened)
        else:
            opened = Brres.get_brres(fname)
            self.open_files.append(opened)
            opened.register_observer(self)
        # either it's newly opened or forcing update
        self.set_brres(opened)
        self.treeview.on_brres_update(opened)
        self.material_browser.add_brres_materials_to_scene(opened)

    def save(self):
        if len(self.open_files):
            last = None
            for x in self.open_files:
                # A precaution against overwriting old models
                # overwrite = True if not x.has_new_model else Brres.OVERWRITE
                if x.save(overwrite=True):
                    last = x
            if last is not None:
                self.update_status('Wrote file {}'.format(last.name))

    def save_as_dialog(self):
        if self.brres:
            fname, filter = QFileDialog.getSaveFileName(
                self, 'Save as', self.cwd, 'Brres files (*.brres)')
            if fname:
                self.cwd = os.path.dirname(fname)
                self.brres.save(fname, overwrite=True)
                self.update_status('Wrote file {}'.format(fname))
        else:
            AutoFix.get().error('No Brres file selected.')

    def import_texture(self, filename):
        raise NotImplementedError()

    def on_material_select(self, material):
        self.material_browser.on_material_select(material)

    def import_file(self, fname, brres_name=None, brres=None, mdl0=None):
        if mdl0 is not None:
            brres = mdl0.parent
        if not brres:
            if brres_name is not None:
                brres = self.get_brres_by_fname(brres_name)
            elif self.brres and os.path.splitext(os.path.basename(self.brres.name))[0] == \
                    os.path.splitext(os.path.basename(fname))[0]:
                brres = self.brres
        self.cwd, name = os.path.split(fname)
        base_name, ext = os.path.splitext(name)
        lower = ext.lower()
        if lower == '.dae':
            converter = DaeConverter2(brres, fname, mdl0=mdl0)
        elif lower == '.obj':
            converter = ObjConverter(brres, fname, mdl0=mdl0)
        # elif lower in ('.png', '.jpg', '.bmp', '.tga'):
        #     return self.import_texture(fname)
        else:
            self.statusBar().showMessage('Unknown extension {}'.format(ext))
            return
        # converter.load_model()
        # self.on_conversion_finish(converter)
        self.update_status('Added {} to queue...'.format(fname))
        self.lock_file(converter.brres)
        self.converter.enqueue(converter)

    def lock_file(self, brres):
        self.locked_files.add(brres)
        self.poly_editor.on_brres_lock(brres)

    def unlock_file(self, brres):
        try:
            self.locked_files.remove(brres)
        except KeyError:
            pass
        self.poly_editor.on_brres_unlock(brres)

    def on_conversion_finish(self, converter):
        self.open(converter.brres.name, force_update=True)
        self.unlock_file(converter.brres)
        self.update_status('Finished Converting {}'.format(
            converter.brres.name))

    def import_file_dialog(self, brres=None, mdl0=None):
        fname, filter = QFileDialog.getOpenFileName(self, 'Import model',
                                                    self.cwd, '(*.dae *.obj)')
        if fname:
            self.import_file(fname, brres=brres, mdl0=mdl0)

    def export_file_dialog(self, brres=None, mdl0=None):
        if mdl0 is not None:
            brres = mdl0.parent
        multiple_models = False
        if not brres:
            brres = self.brres
            if not brres:
                AutoFix.get().error('Nothing to export!')
                return
        elif mdl0 is None:
            if len(brres.models) > 1:
                multiple_models = True
        fname, fil = QFileDialog.getSaveFileName(self, 'Export model',
                                                 self.cwd,
                                                 'Model files (*.dae *.obj)')
        if fname:
            self.cwd, name = os.path.split(fname)
            base_name, ext = os.path.splitext(name)
            lower = ext.lower()
            if lower == '.obj':
                klass = ObjConverter
            elif lower == '.dae':
                klass = DaeConverter2
            else:
                self.statusBar().showMessage(
                    'Unknown extension {}'.format(ext))
                return
            if multiple_models:
                for x in brres.models:
                    export_name = os.path.join(self.cwd,
                                               base_name + '-' + x.name + ext)
                    converter = klass(brres, export_name, encode=False, mdl0=x)
                    self.update_status('Added {} to queue...'.format(x.name))
                    self.converter.enqueue(converter)
            else:
                if mdl0 is None:
                    mdl0 = brres.models[0]
                converter = klass(brres, fname, encode=False, mdl0=mdl0)
                self.update_status('Added {} to queue...'.format(mdl0.name))
                self.converter.enqueue(converter)

    def close_file(self, brres=None):
        if brres is None:
            brres = self.brres
        if brres.is_modified:
            m = QMessageBox(QMessageBox.Warning,
                            'Save Before Closing',
                            f'Save {brres.name} before closing?',
                            buttons=QMessageBox.Yes | QMessageBox.No,
                            parent=self)
            if m.exec_() == QMessageBox.Yes:
                brres.save(overwrite=True)
        if brres in self.open_files:
            self.open_files.remove(brres)
            brres.unregister(self)
        brres.close(try_save=False)
        self.brres = None
        self.poly_editor.on_brres_lock(brres)
        self.treeview.on_file_close(brres)

    def shouldExitAnyway(self, result):
        return result == QMessageBox.Ok

    def closeEvent(self, event):
        self.material_browser.on_close()
        files_to_save = []
        for x in self.open_files:
            if x.is_modified:
                files_to_save.append(x)
        if Brres.OVERWRITE:
            for x in files_to_save:
                x.close()
        elif files_to_save:
            self.files_to_save = files_to_save
            fnames = ', '.join([x.name for x in files_to_save])

            m = QMessageBox(QMessageBox.Warning,
                            'Confirm Exit?',
                            f'Exit without saving {fnames}?',
                            buttons=QMessageBox.Yes | QMessageBox.No,
                            parent=self)

            if m.exec_() == QMessageBox.No:
                event.ignore()
                return
            for x in files_to_save:
                x.close()
        self.threadpool.clear()
        self.image_manager.stop()
        self.converter.stop()
        event.accept()

    def info(self, message):
        self.emit(message, '<p style="color:Blue;">', '</p>')

    def warn(self, message):
        self.emit(message, '<p style="color:Red;">', '</p>')

    def error(self, message):
        self.emit(message, '<p style="color:Red;">', '</p>')

    def update_status(self, message):
        self.statusBar().showMessage(message)
Exemplo n.º 27
0
class GuiBehavior:
    def __init__(self, gui):
        self.filter_thread = QThreadPool()
        self.download_thread = QThreadPool()
        self.download_thread.setMaxThreadCount(
            1)  # Limits concurrent downloads to 1.
        self.download_workers = []
        self.gui = gui
        self.handle_init()

    def handle_init(self):
        '''
        Load cached downloads.
        Create file in case it does not exist.
        '''
        try:
            with open(absp('app/cache'), 'rb') as f:
                self.cached_downloads = pickle.load(f)
                for download in self.cached_downloads:
                    self.gui.links = download[0]
                    self.add_links(True, download)
        except EOFError:
            self.cached_downloads = []
            logging.debug('No cached downloads.')
        except FileNotFoundError:
            self.cached_downloads = []
            create_file('app/cache')
        '''
        Load settings.
        Create file in case it doesn't exist.
        '''
        try:
            with open(absp('app/settings'), 'rb') as f:
                self.settings = pickle.load(f)
        except EOFError:
            self.settings = None
            logging.debug('No settings found.')
        except FileNotFoundError:
            self.settings = None
            create_file('app/settings')

    def resume_download(self):
        '''
        Resume selected downloads.
        '''
        selected_rows = check_selection(self.gui.table)

        if selected_rows:
            for i in selected_rows:
                if i < len(self.download_workers):
                    self.download_workers[i].resume()

    def stop_download(self):
        '''
        Stop selected downloads.
        '''
        selected_rows = check_selection(self.gui.table)

        if selected_rows:
            for i in reversed(selected_rows):
                if i < len(self.download_workers):
                    self.download_workers[i].stop(i)
                    self.download_workers.remove(self.download_workers[i])

    def pause_download(self):
        '''
        Pause selected downloads.
        '''
        selected_rows = check_selection(self.gui.table)

        if selected_rows:
            for i in selected_rows:
                if i < len(self.download_workers):
                    self.download_workers[i].signals.update_signal.emit(
                        self.download_workers[i].data,
                        [None, None, 'Paused', '0 B/s'])
                    self.download_workers[i].pause()

    def add_links(self, state, cached_download=''):
        '''
        Calls FilterWorker()
        '''
        worker = FilterWorker(
            self, cached_download,
            (self.gui.password.text() if not cached_download else ''))

        worker.signals.download_signal.connect(self.download_receive_signal)
        worker.signals.alert_signal.connect(alert)

        self.filter_thread.start(worker)

    def download_receive_signal(self,
                                row,
                                link,
                                append_row=True,
                                dl_name='',
                                progress=0):
        '''
        Append download to row and start download.
        '''
        if append_row:
            self.gui.table_model.appendRow(row)
            index = self.gui.table_model.index(
                self.gui.table_model.rowCount() - 1, 4)
            progress_bar = QProgressBar()
            progress_bar.setValue(progress)
            self.gui.table.setIndexWidget(index, progress_bar)
            row[4] = progress_bar

        worker = DownloadWorker(link, self.gui.table_model, row, self.settings,
                                dl_name)
        worker.signals.update_signal.connect(self.update_receive_signal)
        worker.signals.unpause_signal.connect(self.download_receive_signal)

        self.download_thread.start(worker)
        self.download_workers.append(worker)

    def update_receive_signal(self, data, items):
        '''
        Update download data.
        items = [File Name, Size, Down Speed, Progress, Pass]
        '''
        if data:
            if not PyQt5.sip.isdeleted(data[2]):
                for i in range(len(items)):
                    if items[i] and isinstance(items[i], str):
                        data[i].setText(items[i])
                    if items[i] and not isinstance(items[i], str):
                        data[i].setValue(items[i])

    def set_dl_directory(self):
        file_dialog = QFileDialog(self.gui.settings)
        file_dialog.setFileMode(QFileDialog.Directory)
        file_dialog.exec_()
        self.gui.dl_directory_input.setText(file_dialog.selectedFiles()[0])

    def change_theme(self, theme=None):
        '''
        Change app palette (theme).
        0 = Light
        1 = Dark
        '''
        if theme:
            self.gui.theme_select.setCurrentIndex(theme)

        if self.gui.theme_select.currentIndex() == 0:
            self.gui.app.setPalette(self.gui.main.style().standardPalette())
        elif self.gui.theme_select.currentIndex() == 1:
            self.gui.app.setPalette(dark_theme)

    def save_settings(self):
        with open(absp('app/settings'), 'wb') as f:
            settings = []
            settings.append(
                self.gui.dl_directory_input.text())  # Download Directory - 0
            settings.append(
                self.gui.theme_select.currentIndex())  # Theme              - 1
            settings.append(
                self.gui.timeout_input.value())  # Timeout            - 2
            settings.append(
                self.gui.proxy_settings_input.text())  # Proxy Settings     - 3
            pickle.dump(settings, f)
            self.settings = settings
        self.gui.settings.hide()

    def select_settings(self):
        '''
        Select settings page.
        '''
        selection = self.gui.settings_list.selectedIndexes()[0].row()
        self.gui.stacked_settings.setCurrentIndex(selection)

    def handle_exit(self):
        '''
        Save cached downloads data.
        '''
        active_downloads = []
        for w in self.download_workers:
            download = w.return_data()
            if download: active_downloads.append(download)
        active_downloads.extend(self.cached_downloads)

        with open(absp('app/cache'), 'wb') as f:
            if active_downloads:
                pickle.dump(active_downloads, f)

        os._exit(1)
Exemplo n.º 28
0
class DataProcess(DataGUI):
    # Main GUI for data module
    sdk_level_signal = QtCore.pyqtSignal(list)
    authority_signal = QtCore.pyqtSignal(list)
    type_signal = QtCore.pyqtSignal(list)
    add_apk_signal = pyqtSignal(int, int, int)
    add_progress_signal = pyqtSignal(float)
    market_signal = pyqtSignal(list)
    app_signal = pyqtSignal(list)
    update_signal = pyqtSignal(list)
    update_information_signal = pyqtSignal(list)
    delete_apk_signal = pyqtSignal()
    delete_progress_signal = pyqtSignal(float)
    error_signal = pyqtSignal(str)

    market_model = None
    app_model = None
    update_model = None

    search_app_thread = None
    search_platform_thread = None
    search_update_thread = None
    search_update_info_thread = None

    inbox_update_id_list = []

    def __init__(self):
        super(DataProcess, self).__init__()
        self.thread_pool = QThreadPool()
        self.thread_pool.globalInstance()
        self.thread_pool.setMaxThreadCount(8)

        self.bind_error()
        self.load_data()
        self.bind_add_apk()
        self.bind_search()
        self.bind_delete()
        self.check_value()

    """
    加载ComboBox数据
    """

    def load_data(self):
        # sdk level combobox
        sdk_thread = SDKLevelThread()
        sdk_thread.transfer(self)
        self.sdk_level_signal.connect(self.update_sdk)
        self.thread_pool.start(sdk_thread)

        # authority combobox
        authority_thread = AuthorityThread()
        authority_thread.transfer(self)
        self.authority_signal.connect(self.update_authority)
        self.thread_pool.start(authority_thread)

        # type combobox
        type_thread = TypeThread()
        type_thread.transfer(self)
        self.type_signal.connect(self.update_type)
        self.thread_pool.start(type_thread)

    def update_sdk(self, sdk_list):
        sdk_list = ["UNKNOWN"] + sdk_list
        self.sdk_list = sdk_list
        self.sdk_combobox.addItems(sdk_list)

    def update_authority(self, authority_list):
        if not authority_list:
            return
        authority_index_list, authority_name_list = zip(*authority_list)
        authority_name_list = list(authority_name_list)
        self.authority_id_list = list(authority_index_list)
        self.authority_combobox.addItems(authority_name_list)

    def update_type(self, type_list):
        if not type_list:
            return
        type_index_list, type_name_list = zip(*type_list)
        self.type_id_list = list(type_index_list)
        self.type_combobox.addItems(type_name_list)

    """
    查找
    """

    def bind_search(self):
        self.search_button.clicked.connect(self.search_click)

        self.market_signal.connect(self.update_market)
        self.first_file_tree.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.first_file_tree.clicked.connect(self.first_tree_click)

        self.app_signal.connect(self.update_app)
        self.second_file_tree.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.second_file_tree.clicked.connect(self.second_tree_click)

        self.update_signal.connect(self.update_update)
        self.third_file_tree.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.third_file_tree.clicked.connect(self.third_tree_click)

        self.update_information_signal.connect(self.update_information)

        self.bind_drag_search()

    def search_click(self):
        # get the combobox value
        sdk_name_list = self.sdk_combobox.get_select_text()
        authority_select_list = self.authority_combobox.get_select_index()
        app_type_select_list = self.type_combobox.get_select_index()

        # 选中全部sdk 或 未选中任何sdk时,无需筛选
        if len(sdk_name_list) == len(self.sdk_list) or len(sdk_name_list) == 0:
            sdk_name_list = None
        self.selected_sdk_name_list = sdk_name_list

        # 未选中任何authority时,无需筛选
        if len(authority_select_list) == 0:
            authority_id_list = None
        else:
            authority_id_list = [
                self.authority_id_list[_index_]
                for _index_ in authority_select_list
            ]
        self.selected_authority_id_list = authority_id_list

        # 未选中任何type 或 选中全部type时, 无需筛选
        if len(app_type_select_list) == 0 or len(app_type_select_list) == len(
                self.type_id_list):
            type_id_list = None
        else:
            type_id_list = [
                self.type_id_list[_index_] for _index_ in app_type_select_list
            ]
        self.selected_type_id_list = type_id_list

        search_platform_thread = SearchPlatformThread()
        search_platform_thread.transfer(self, sdk_name_list, authority_id_list,
                                        type_id_list)
        if self.search_platform_thread:
            # 取消上一个请求
            try:
                self.thread_pool.cancel(self.search_platform_thread)
            except RuntimeError:
                pass
        self.search_platform_thread = search_platform_thread
        self.thread_pool.start(search_platform_thread)

    def update_market(self, market_list):
        platform_model = QStandardItemModel()
        icon_path = os.path.join(__current_folder_path__,
                                 "./images/folder.png")
        for market in market_list:
            platform_model.appendRow(
                QStandardItem(QIcon(icon_path), market['market_name']))
        self.market_list = market_list
        if self.market_model:
            self.market_model.deleteLater()
        self.market_model = platform_model
        self.first_file_tree.setModel(platform_model)
        self.first_file_tree.scrollTo(platform_model.index(0, 0))

        # clear the second tree
        app_model = QStandardItemModel()
        self.app_list = []
        if self.app_model:
            self.app_model.deleteLater()
        self.app_model = app_model
        self.second_file_tree.setModel(app_model)

        # clear the third tree
        update_model = QStandardItemModel()
        self.update_list = []
        if self.update_model:
            self.update_model.deleteLater()
        self.update_model = update_model
        self.third_file_tree.setModel(update_model)

        # clear the information box
        self.clear_apk_info_layout()
        self.inbox_update_id_list = []

        # not find any data
        if len(market_list) == 0:
            QMessageBox().warning(self, "Not Found",
                                  "Not found any apk in database.",
                                  QMessageBox.Ok)

    def first_tree_click(self):
        current_row_index = self.first_file_tree.currentIndex().row()

        search_app_thread = SearchAppThread()
        search_app_thread.transfer(
            self, self.market_list[current_row_index]['market_id'],
            self.selected_sdk_name_list, self.selected_authority_id_list,
            self.selected_type_id_list)
        if self.search_app_thread:
            # 取消上一个请求
            try:
                self.thread_pool.cancel(self.search_app_thread)
            except RuntimeError:
                pass
        self.search_app_thread = search_app_thread
        self.thread_pool.start(search_app_thread)

    def update_app(self, app_list):
        app_model = QStandardItemModel()
        icon_path = os.path.join(__current_folder_path__,
                                 "./images/android.png")
        for app in app_list:
            app_model.appendRow(
                QStandardItem(QIcon(icon_path), app['app_title']))
        self.app_list = app_list
        if self.app_model:
            self.app_model.deleteLater()
        self.app_model = app_model
        self.second_file_tree.setModel(app_model)
        self.second_file_tree.scrollTo(app_model.index(0, 0))

    def second_tree_click(self):
        current_row_index = self.second_file_tree.currentIndex().row()

        search_update_thread = SearchUpdateThread()
        search_update_thread.transfer(
            self, self.app_list[current_row_index]['app_id'],
            self.selected_sdk_name_list, self.selected_authority_id_list,
            self.selected_type_id_list)
        if self.search_update_thread:
            # 取消上一个请求
            try:
                self.thread_pool.cancel(self.search_update_thread)
            except RuntimeError:
                pass
        self.search_update_thread = search_update_thread
        self.thread_pool.start(search_update_thread)

    def update_update(self, update_list):
        update_model = QStandardItemModel()
        icon_path = os.path.join(__current_folder_path__,
                                 "./images/version.png")
        for update in update_list:
            version = update['version'].split(
                '.apk')[0] if update['version'].endswith(
                    '.apk') else update['version']
            version = update['version'].split(
                '.xapk')[0] if update['version'].endswith('.xapk') else version
            update_model.appendRow(QStandardItem(QIcon(icon_path), version))
        self.update_list = update_list
        if self.update_model:
            self.update_model.deleteLater()
        self.update_model = update_model
        self.third_file_tree.setModel(update_model)
        self.third_file_tree.scrollTo(update_model.index(0, 0))

    def third_tree_click(self):
        current_third_tree_row_index = self.third_file_tree.currentIndex().row(
        )

        search_apk_info_by_update_id_thread = SearchApkInfoByUpdateIdThread()
        search_apk_info_by_update_id_thread.transfer(
            self, self.update_list[current_third_tree_row_index]['update_id'])
        if self.search_update_info_thread:
            # 取消上一个请求
            try:
                self.thread_pool.cancel(self.search_update_info_thread)
            except RuntimeError:
                pass
        self.search_update_info_thread = search_apk_info_by_update_id_thread
        self.thread_pool.start(search_apk_info_by_update_id_thread)

        # clear the information box
        self.clear_apk_info_layout()
        self.inbox_update_id_list = []

    def update_information(self, information_list):
        self.clear_apk_info_layout()
        inbox_update_id_list = []
        for information in information_list:  # add the new information widget
            information['market'] = information['market_name']
            update_folder = get_app_folder(information)
            image_file_list = glob.glob(os.path.join(update_folder, "*.jpg"))
            description_file = os.path.join(update_folder, "description.txt")
            if os.path.exists(description_file):
                with open(description_file, 'r') as _file_:
                    description = _file_.read().strip()
                    if description != "":
                        information['description'] = description
            information['image_file_list'] = image_file_list
            new_information_widget = InformationWidget()
            new_information_widget.load_data(information)
            self.apk_info_layout.addWidget(new_information_widget)
            inbox_update_id_list.append(information['update_id'])
        self.inbox_update_id_list = inbox_update_id_list

    """
    拖动查找
    """

    def bind_drag_search(self):
        self.apk_info_scroll_area.file_signal.connect(self.drag_search)

    def drag_search(self, file_url):
        drag_search_thread = DragSearchThread()
        drag_search_thread.transfer(self, file_url)
        if self.search_app_thread:
            #  取消上一个进程
            try:
                self.thread_pool.cancel(self.search_app_thread)
            except RuntimeError:
                pass
        self.search_app_thread = drag_search_thread
        self.thread_pool.start(drag_search_thread)

        # uncheck the third tree
        row_index = self.third_file_tree.currentIndex().row()
        if row_index != -1:
            self.third_file_tree.setCurrentIndex(
                self.update_model.index(-1, -1))

        # clear the information box
        self.clear_apk_info_layout()
        self.inbox_update_id_list = []

    """
    删除
    """

    def bind_delete(self):
        self.delete_apk_button.clicked.connect(self.delete_apk_button_click)
        self.delete_apk_signal.connect(self.delete_apk_success)
        self.delete_from_folder_button.clicked.connect(
            self.delete_from_folder_button_click)

    def delete_apk_button_click(self):
        if not self.inbox_update_id_list:
            return

        # clear the information box
        self.clear_apk_info_layout()
        inbox_update_id_list = self.inbox_update_id_list
        self.inbox_update_id_list = []

        # check the third file system tree
        in_third_tree = False
        reserved_update_list = []  # type: List[Dict]
        for update in self.update_list:
            if update['update_id'] in inbox_update_id_list:
                in_third_tree = True
            else:
                reserved_update_list.append(update)
        if in_third_tree:
            new_update_model = QStandardItemModel()
            icon_path = os.path.join(__current_folder_path__,
                                     "./images/version.png")
            for update in reserved_update_list:
                version = update['version'].split(
                    '.apk')[0] if update['version'].endswith(
                        '.apk') else update['version']
                version = update['version'].split(
                    '.xapk')[0] if update['version'].endswith(
                        '.xapk') else version
                new_update_model.appendRow(
                    QStandardItem(QIcon(icon_path), version))
            self.update_list = reserved_update_list
            if self.update_model:
                self.update_model.deleteLater()
            self.update_model = new_update_model
            self.third_file_tree.setModel(new_update_model)
            self.third_file_tree.scrollTo(new_update_model.index(0, 0))

        # start the delete thread
        delete_apk_thread = DeleteApkThread()
        delete_apk_thread.transfer(self, inbox_update_id_list)
        self.thread_pool.start(delete_apk_thread)

    def delete_apk_success(self):
        QMessageBox.information(self, "Delete successfully",
                                "Successfully delete APK(s).", QMessageBox.Yes)

        self.delete_from_folder_button.setVisible(True)
        self.delete_progress_bar.setValue(0)
        self.delete_progress_bar.setVisible(False)

    def delete_from_folder_button_click(self):
        dir_choose = QFileDialog.getExistingDirectory(
            self, "Choose APK Directory",
            os.path.join(__current_folder_path__, "../../"))
        if not dir_choose:
            return

        user_choose = QMessageBox.question(
            self, "Delete Confirm",
            "Do confirm to delete folder '{}'?.".format(dir_choose),
            QMessageBox.Yes | QMessageBox.Cancel, QMessageBox.Cancel)
        if user_choose == QMessageBox.Cancel:
            return

        # set the layout
        self.delete_from_folder_button.setVisible(False)
        self.delete_progress_bar.setValue(0)
        self.delete_progress_bar.setVisible(True)

        # start the delete thread
        delete_thread = MultiDeleteThread()
        delete_thread.transfer(self, dir_choose)
        self.thread_pool.start(delete_thread)

    """
    错误
    """

    def bind_error(self):
        self.error_signal.connect(self.catch_error)

    def catch_error(self, _err_: str):
        log_file = os.path.join(
            __current_folder_path__, "../../log/main_gui.{}.log".format(
                datetime.datetime.now().strftime("%Y-%m-%d-%H")))
        with open(log_file, 'a') as _file_:
            _file_.write(_err_)

    def check_value(self):
        enviro = True
        if python_interface is None:
            QMessageBox.warning(
                self, "Python Interface Error",
                "Please set the 'python_interface' in setting.py.",
                QMessageBox.Ok, QMessageBox.Ok)
            enviro = False
        return enviro

    """
    批量添加APK
    """

    def bind_add_apk(self):
        self.add_progress_signal.connect(self.add_apk_progress_bar.setValue)
        self.add_apk_signal.connect(self.add_apk_success)
        self.add_apk_button.clicked.connect(self.add_apk_button_click)

    def add_apk_button_click(self):
        dir_choose = QFileDialog.getExistingDirectory(
            self, "Choose APK Directory",
            os.path.join(__current_folder_path__, "../../"))
        if not dir_choose:
            return
        self.add_apk_button.setVisible(False)
        self.add_apk_progress_bar.setValue(0)
        self.add_apk_progress_bar.setVisible(True)
        add_apk_thread = AddAPKThread()
        add_apk_thread.transfer(self, dir_choose)
        self.thread_pool.start(add_apk_thread)

    def add_apk_success(self, success_number, repeated_number, error_number):
        QMessageBox.information(
            self, "Add APK",
            "Successfully add APKs.  {} success,  {} Repeated  and  {} error.".
            format(success_number, repeated_number,
                   error_number), QMessageBox.Ok, QMessageBox.Ok)

        self.add_apk_button.setVisible(True)
        self.add_apk_progress_bar.setVisible(False)
        self.add_apk_progress_bar.setValue(0)
Exemplo n.º 29
0
class TrainingWindow(Window):
    """Abstraction  for training view based on Window class"""
    def __init__(self, window_path: str) -> None:
        super().__init__(window_path)
        sys.stdout = EmittingStream(
            textWritten=self._add_info)  # textWritten works this way
        self.lbl_cancel.mouseReleaseEvent = self._cancel_training
        # thread_pool and ml_worker setup. A training view needs 2 threads to work properly
        self._thread_pool = QThreadPool()
        self._thread_pool.setMaxThreadCount(2)
        self._ml_worker = LongWorker()
        self._ml_worker.set_params(self._train_model)
        self._ml_worker.signals.program_finished.connect(self.next)
        self._ml_worker.signals.program_error.connect(self.handle_error)
        self._ml_worker.signals.result.connect(self._add_info)
        self._ml_worker.setAutoDelete(True)
        self._thread_pool.start(self._ml_worker, priority=1)

    def close_window(self) -> None:
        super(TrainingWindow, self).close_window()
        widget.close()

    def next(self) -> None:
        QThread.sleep(1)
        next_form = FinalResultWindow()
        widget.addWidget(next_form)
        widget.removeWidget(widget.currentWidget())
        widget.setCurrentIndex(widget.currentIndex())

    @abstractmethod
    def _train_model(self) -> None:
        pass

    def last_warning_pop_up(self) -> bool:
        pop_up: PopUp = WarningPopUp()
        title = "Cancelar entrenamiento"
        body = "¿Estas seguro que deseas cancelar el entrenamiento?"
        additional = "La aplicación se cerrará para evitar conflictos con las variables utilizadas hasta el momento."
        answer = pop_up.open_pop_up(title, body, additional)
        return answer

    def _cancel_training(self, event) -> None:
        """Show a Warning pop up and then if user wants to finished the app, close it"""
        event.accept()
        want_to_stop_training = self.last_warning_pop_up()
        if want_to_stop_training:
            self._thread_pool.cancel(self._ml_worker)
            self.close_window()

    def handle_error(self, error) -> None:
        """Print error message to the QTextEdit"""
        def write_error():
            for i in info:
                self._add_info(i)
                QThread.sleep(1)

        # deactivate lbl press behaviour due to an error
        lbl_cancel_style = cancel_buttons_style["Not_available"]
        self.lbl_cancel.mouseReleaseEvent = None
        self.lbl_cancel.setStyleSheet(lbl_cancel_style)
        info = ("Error\n", str(error),
                "\nCerrando aplicación para evitar conflictos de memoria" +
                " ", ".", ".", ".")
        # worker to write info to ted_info
        temp_worker = LongWorker(func=write_error)
        temp_worker.signals.program_finished.connect(self.close_window)
        self._thread_pool.start(temp_worker, priority=2)

    def _add_info(self, info: str) -> None:
        """Append text to the QTextEdit."""
        cursor = self.ted_info.textCursor()
        cursor.movePosition(QTextCursor.End)
        cursor.insertText(info)
        self.ted_info.setTextCursor(cursor)
        self.ted_info.ensureCursorVisible()
Exemplo n.º 30
0
class FileHandler:
    """
    A class to handle finding/loading/saving to files. So, IO operations.
    """

    # TODO: Implement logging, since returned values from threaded functions are discarded.
    # Need to know if errors hanppen!

    def __init__(self, settings='settings.json', profiles='profiles.json'):

        self.profile_path = profiles
        self.settings_path = settings
        self.work_dir = os.getcwd().replace('\\', '/')

        self.force_save = False

        self.threadpool = QThreadPool()
        self.threadpool.setMaxThreadCount(1)

    @staticmethod
    def find_file(relative_path, exist=True):
        """ Get absolute path to resource, works for dev and for PyInstaller """
        try:
            # PyInstaller creates a temp folder and stores path in _MEIPASS
            base_path = sys._MEIPASS
        except AttributeError:
            base_path = os.path.abspath(".")

        path = os.path.join(base_path, relative_path).replace('\\', '/')

        if exist:
            if FileHandler.is_file(path):
                # print(f'Returning existing path: {path}')
                return path
            else:
                # print(f'No found: {relative_path}')
                return None

        else:
            # print(f'Returning path: {path}')
            return path

    @threaded_cooldown
    def save_settings(self, settings):
        try:
            with open(self.settings_path, 'w') as f:
                json.dump(settings, f, indent=4, sort_keys=True)
                return True
        except (OSError, IOError) as e:
            # TODO: Logging!
            return False

    @threaded_cooldown
    def save_profiles(self, profiles):
        try:
            with open(self.profile_path, 'w') as f:
                json.dump(profiles, f, indent=4, sort_keys=True)
                return True
        except (OSError, IOError) as e:
            # TODO: Logging!
            return False

    def load_settings(self, reset=False) -> SettingsClass:
        """ Reads settings, or writes them if absent, or if instructed to using reset. """
        def get_file(path):
            """  """
            if FileHandler.is_file(path):
                with open(path, 'r') as f:
                    return json.load(f)
            else:
                return {}

        try:
            profiles = get_file(self.profile_path)
        except json.decoder.JSONDecodeError as e:
            raise ProfileLoadError(str(e))

        if reset:
            return SettingsClass(get_base_settings(), profiles, self)
        else:
            settings = get_file(self.settings_path)
            if settings:
                return SettingsClass(settings, profiles, self)
            else:
                return self.load_settings(reset=True)

    @staticmethod
    def is_file(path):
        return os.path.isfile(path) and os.access(path, os.X_OK)

    def find_exe(self, program):
        """Used to find executables."""
        local_path = os.path.join(self.work_dir, program)
        if FileHandler.is_file(local_path):
            # print(f'Returning existing isfile exe: {os.path.join(self.work_dir, program)}')
            return local_path
        for path in os.environ["PATH"].split(os.pathsep):
            path = path.strip('"')
            exe_file = os.path.join(path, program)
            if FileHandler.is_file(exe_file):
                # print(f'Returning existing exe: {os.path.abspath(exe_file)}')
                return os.path.abspath(exe_file)
        # TODO: Check if not covered by path above!

        # print(f'No found: {program}')
        return None

    @staticmethod
    def read_textfile(path):
        if FileHandler.is_file(path):
            try:
                with open(path, 'r') as f:
                    content = f.read()
                return content
            except (OSError, IOError) as e:
                return None
        else:
            return None

    @threaded
    def write_textfile(self, path, content):
        # TODO: Warn user on error. Need smart simple method to send message from threadpool.
        if FileHandler.is_file(path):
            try:
                with open(path, 'w') as f:
                    f.write(content)
                return True
            except (OSError, IOError) as e:
                print('Error! ', e)
                return False
        else:
            print('Error!')
            return False
class MainWindow(QWidget):
    
	def __init__(self, inList):
		super().__init__()
		self.inList = inList
		self.nameFrom = 'Folder'
		self.codec = 'utvideo'
		self.alpha = False
		self.frameRate = 24
		self.defaulStyle = ''
		
		self.okIcon = QIcon(self.style().standardIcon(QStyle.SP_CustomBase))
		self.okPix = QPixmap(self.okIcon.pixmap(QSize(13, 13)))
		self.goodIcon = QIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton))
		self.goodPix = QPixmap(self.goodIcon.pixmap(QSize(13, 13)))
		self.badIcon = QIcon(self.style().standardIcon(QStyle.SP_MessageBoxCritical))
		self.badPix = QPixmap(self.badIcon.pixmap(QSize(13, 13)))
		self.processingIcon = QIcon(self.style().standardIcon(QStyle.SP_ArrowRight))
		self.processingPix = QPixmap(self.processingIcon.pixmap(QSize(13, 13)))
		self.removeIcon = QIcon(self.style().standardIcon(QStyle.SP_DockWidgetCloseButton))
		self.removePix = QPixmap(self.removeIcon.pixmap(QSize(19, 19)))
		self.folderIcon = QIcon(self.style().standardIcon(QStyle.SP_FileDialogNewFolder))
		self.folderPix = QPixmap(self.folderIcon.pixmap(QSize(19, 19)))
		
		self.pbList = []
		self.chList = []
		self.lblList = []
		self.rmbList = []
		#self.newFolders = []
		self.initUI()

	def initUI(self):
		self.resize(720, 300)
		self.setWindowTitle('FFMpeg Python Compressor')
		self.verticalLayout = QVBoxLayout(self)
		self.verticalLayout.setContentsMargins(11, 11, 11, 11)
		self.verticalLayout.setSpacing(11)
		
		#COMBOBOX LABELS
		self.gridLayoutControlls = QGridLayout()
		self.codecLabel = QLabel('Codec', self)
		self.codecLabel.setMinimumHeight(13)
		self.alphaLabel = QLabel('Alpha' , self)
		self.alphaLabel.setMinimumHeight(13)
		self.frameRateLabel = QLabel('Frame Rate' , self)
		self.frameRateLabel.setMinimumHeight(13)
		self.gridLayoutControlls.addWidget(self.codecLabel, 0, 0, 1, 1)
		self.gridLayoutControlls.addWidget(self.alphaLabel, 0, 1, 1, 1)
		self.gridLayoutControlls.addWidget(self.frameRateLabel, 0, 2, 1, 1)
		
		#COMBOBOXES AND COMPRESS BUTTON
		self.codecComboBox = QComboBox(self)
		self.codecComboBox.setMinimumSize(80,23)
		self.codecComboBox.addItem("UT Video")
		self.codecComboBox.activated[str].connect(self.chooseCodec)
		
		self.alphaComboBox = QComboBox(self)
		self.alphaComboBox.setMinimumSize(80,23)
		self.alphaComboBox.addItem("No Alpha")
		self.alphaComboBox.addItem("with Alpha")
		self.alphaComboBox.activated[str].connect(self.chooseAlpha)
		
		self.frameRateComboBox = QComboBox(self)
		self.frameRateComboBox.setMinimumSize(80,23)
		self.frameRateComboBox.addItem("23.976")
		self.frameRateComboBox.addItem("24.00")
		self.frameRateComboBox.addItem("29.97")
		self.frameRateComboBox.addItem("30.00")
		self.frameRateComboBox.setCurrentIndex(1)
		self.frameRateComboBox.activated[str].connect(self.chooseFrameRate)
		
		self.compressButton = QPushButton('Compress', self)
		self.compressButton.setMinimumSize(80,23)
		self.compressButton.clicked[bool].connect(self.compressPress)
			
		self.gridLayoutControlls.addWidget(self.codecComboBox, 1, 0, 1, 1)
		self.gridLayoutControlls.addWidget(self.alphaComboBox, 1, 1, 1, 1)
		self.gridLayoutControlls.addWidget(self.frameRateComboBox, 1, 2, 1, 1)
		self.gridLayoutControlls.addWidget(self.compressButton, 1, 3, 1, 1)
			
		#RADIO BUTTON GROUP
		self.groupBox = QButtonGroup(self)
		self.radio1 = QRadioButton('Output file name from Folder name', self)
		self.radio1.setMinimumSize(80,25)
		self.radio2 = QRadioButton('Output file name from File name', self)
		self.radio2.setMinimumSize(80,25)
		self.radio1.setChecked(True)
		self.groupBox.addButton(self.radio1,1)
		self.groupBox.addButton(self.radio2,2)
		self.groupBox.buttonClicked[int].connect(self.radioBtnState)
		
		self.gridLayoutControlls.addWidget(self.radio1, 2, 0, 1, 2)
		self.gridLayoutControlls.addWidget(self.radio2, 2, 2, 1, 2)
		
		#LINE
		self.line = QFrame(self)
		self.line.setLineWidth(2)
		self.line.setMinimumHeight(3)
		self.line.setFrameShape(QFrame.HLine)
		self.line.setFrameShadow(QFrame.Sunken)
		
		self.gridLayoutControlls.addWidget(self.line, 3, 0, 1, 4)
		
		#PROGRESS BAR 
		self.gridLayoutProgress = QGridLayout()
		self.gridLayoutProgress.setVerticalSpacing(11)
		self.gridLayoutProgress.setHorizontalSpacing(6)
		self.gridLayoutProgress.setSizeConstraint(QLayout.SetNoConstraint)
		
		self.removeGroupBox = QButtonGroup(self)
		self.addProgressBarUI(self.inList)			
		self.removeGroupBox.buttonClicked[int].connect(self.removeButtonClicked)

		#ADD MORE AREA
		self.gridLayoutAddMore = QGridLayout()
		self.gridLayoutAddMore.setContentsMargins(0, 0, 0, 0)
		
		self.dragAndDropLabel_1 = QLabel("Drag and Drop folders here", self)
		self.dragAndDropLabel_1.setMinimumSize(QSize(120, 40))
		self.dragAndDropLabel_1.setAlignment(Qt.AlignCenter)
		
		self.dragAndDropLabel_2 = QLabel("", self)
		self.dragAndDropLabel_2.setFixedSize(QSize(20, 40))
		self.dragAndDropLabel_2.setAlignment(Qt.AlignCenter)
		self.dragAndDropLabel_2.setPixmap(self.folderPix)
		
		sI = QSpacerItem(40, 40,QSizePolicy.Expanding, QSizePolicy.Minimum)
		sI2 = QSpacerItem(40, 40,QSizePolicy.Expanding, QSizePolicy.Minimum)
		
		self.gridLayoutAddMore.addItem(sI, 1, 0, 1, 1)
		self.gridLayoutAddMore.addWidget(self.dragAndDropLabel_2, 1, 1, 1, 1)
		self.gridLayoutAddMore.addWidget(self.dragAndDropLabel_1, 1, 2, 1, 1)
		self.gridLayoutAddMore.addItem(sI2, 1, 3, 1, 1)
		
		#DEBUG AREA
		self.gridLayoutDebug = QGridLayout()
		self.line_2 = QFrame(self)
		self.line_2.setLineWidth(2)
		self.line_2.setFrameShape(QFrame.HLine)
		self.line_2.setFrameShadow(QFrame.Sunken)
		
		self.exploreOutputBtn = QPushButton('Explore Output',self)
		self.exploreOutputBtn.clicked[bool].connect(self.exploreOutput)

		self.hideShowLog = QPushButton('Show Log',self)
		self.hideShowLog.setCheckable(True)
		self.hideShowLog.clicked[bool].connect(self.showDebugLog)
		#self.hideShowLog.setMinimumSize(QSize(0, 20))
		
		self.logText = QPlainTextEdit('',self)
		self.logText.setReadOnly(True)
		self.logText.hide()
		
		self.spacerItem = QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Expanding)
		
		self.gridLayoutDebug.addWidget(self.line_2, 0, 0, 1, 1)
		self.gridLayoutDebug.addWidget(self.exploreOutputBtn, 1, 0, 1, 1)
		self.gridLayoutDebug.addWidget(self.hideShowLog, 2, 0, 1, 1)
		self.gridLayoutDebug.addWidget(self.logText, 3, 0, 1, 1)
		self.gridLayoutDebug.addItem(self.spacerItem, 4, 0, 1, 1)
		
		self.verticalLayout.addLayout(self.gridLayoutControlls)
		self.verticalLayout.addLayout(self.gridLayoutProgress)
		self.verticalLayout.addLayout(self.gridLayoutAddMore)
		self.verticalLayout.addLayout(self.gridLayoutDebug)
		
		# Enable dragging and dropping onto the GUI
		self.setAcceptDrops(True)
		
		#QtCore.QMetaObject.connectSlotsByName(self)
		self.show()
		self.setMinimumSize(self.size())
		
		self.threadpool = QThreadPool()
		self.threadpool.setMaxThreadCount(1)
		print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount())
    
	
	
	'''
	Progress bar populate function
	'''
	def addProgressBarUI(self, arr):
		pbCount = len(self.pbList)
		for i in range(len(arr)):
			tempCheckBox = QCheckBox(self)
			tempCheckBox.setChecked(True)
			tempCheckBox.setMinimumSize(14,21)
			
			tempRemoveButton = QPushButton(self)
			tempRemoveButton.setIcon(self.removeIcon)
			tempRemoveButton.setFlat(True)
			tempRemoveButton.setIconSize(QSize(19,19))
			tempRemoveButton.setFixedSize(QSize(19,21))
			
			
			tempPB = QProgressBar(self)
			tempPB.setMinimumSize(50,21)
			tempPB.setMinimum(0)
			tempPB.setMaximum(100)
			tempPB.setTextVisible(True)
			tempPB.setFormat(str(arr[i])+" %p%")
			tempPB.setAlignment(Qt.AlignCenter)
			tempPB.setValue(0)
			if i==0:
				self.defaulStyle = tempPB.styleSheet()
			
			tempStatusLabel = QLabel(self)			
			tempStatusLabel.setPixmap(self.okPix)
			tempStatusLabel.setMinimumSize(13,21)
			
			self.gridLayoutProgress.addWidget(tempCheckBox, pbCount+i, 0, 1, 1)
			self.gridLayoutProgress.addWidget(tempPB, pbCount+i, 1, 1, 1)
			self.gridLayoutProgress.addWidget(tempStatusLabel, pbCount+i, 2, 1, 1)
			self.gridLayoutProgress.addWidget(tempRemoveButton, pbCount+i, 3, 1, 1)
			self.removeGroupBox.addButton(tempRemoveButton, pbCount+i)
			
			self.pbList.append(tempPB)
			self.chList.append(tempCheckBox)
			self.lblList.append(tempStatusLabel)
			self.rmbList.append(tempRemoveButton)
	
		
	'''
	Drag+Drop Functions
	'''
	# The following three methods set up dragging and dropping for the app
	def dragEnterEvent(self, e):
		if e.mimeData().hasUrls:
			e.accept()
		else:
			e.ignore()

	def dragMoveEvent(self, e):
		if e.mimeData().hasUrls:
			e.accept()
		else:
			e.ignore()

	def dropEvent(self, e):
		"""
		Drop files directly onto the widget
		File locations are stored in fname
		:param e:
		:return:
		"""
		newFolders = []
		if e.mimeData().hasUrls:
			e.setDropAction(Qt.CopyAction)
			e.accept()
			# Workaround for OSx dragging and dropping
			for url in e.mimeData().urls():
				if op_sys == 'Darwin':
					#check for dir here as well
					fname = str(NSURL.URLWithString_(str(url.toString())).filePathURL().path())
				else:
					fname = str(url.toLocalFile())
					if os.path.isdir(fname) == True:
						newFolders.append(fname)
					#print(fname)

			#self.fname = fname
			#print(self.fname)
			#self.load_image()
			self.addNewFolders(newFolders)
			self.inList = self.inList + newFolders
		else:
			e.ignore()
	

	def addNewFolders(self, newFolders):
		self.addProgressBarUI(newFolders)
		self.setMinimumHeight(self.height()+len(newFolders)*32)
		#self.resize(self.width(),self.height()+200)
		self.update()
		self.adjustSize()
		self.setMinimumSize(self.size())
	'''
	Button Functions
	'''
	def chooseAlpha(self, text):
		switcher={
			"No Alpha":False,
			"with Alpha":True
		}
		self.alpha = switcher.get(text,"Invalid day of week")
		#print (self.alpha)

	def chooseCodec(self, text):
		switcher={
			"UT Video":"utvideo"
		}
		self.codec =  switcher.get(text,"Invalid day of week")
		#print (self.codec)
		
	def chooseFrameRate(self, text):
		self.frameRate =  float(text)
		#print (self.frameRate)
	
	
	def currentData(self, widget):
		return widget.currentText() 

	def radioBtnState(self, text):
		switcher={
			1:'Folder',
			2:'File'
		}
		self.nameFrom = switcher.get(text,"Invalid day of week")
		#print(self.nameFrom)
	
	def removeButtonClicked(self, i):
		#print('remove trigger on id '+str(i))
		
		#self.pbList.pop(i)
		'''
		self.pbList[i].hide()
		self.chList[i].setChecked(False)
		self.chList[i].hide()
		self.lblList[i].hide()
		self.rmbList[i].hide()
		'''
		print(self.gridLayoutProgress.rowCount())
		self.pbList[i].setParent(None)
		self.chList[i].setChecked(False)
		self.chList[i].setParent(None)
		self.lblList[i].setParent(None)
		self.rmbList[i].setParent(None)
		
		self.removeGroupBox.removeButton(self.rmbList[i])

		self.gridLayoutProgress.removeWidget(self.pbList[i])
		self.gridLayoutProgress.removeWidget(self.chList[i])
		self.gridLayoutProgress.removeWidget(self.lblList[i])
		self.gridLayoutProgress.removeWidget(self.rmbList[i])
		self.gridLayoutProgress.invalidate()
		self.gridLayoutProgress = QGridLayout()
		self.gridLayoutProgress.setVerticalSpacing(11)
		self.gridLayoutProgress.setHorizontalSpacing(6)
		self.gridLayoutProgress.setSizeConstraint(QLayout.SetDefaultConstraint)
		self.verticalLayout.insertLayout(1,self.gridLayoutProgress)
		
		print(self.gridLayoutProgress.rowCount())
		'''
		print(self.pbList)
		print(self.chList)
		print(self.lblList)
		print(self.rmbList)
		'''
		
		self.pbList.pop(i)
		self.chList.pop(i)
		self.lblList.pop(i)
		self.rmbList.pop(i)
		self.inList.pop(i)		
		

		#clear the gridLayout
		for j in reversed(range(len(self.pbList))):
			self.pbList[j].setParent(None)
			self.chList[j].setParent(None)
			self.lblList[j].setParent(None)
			self.rmbList[j].setParent(None)
		
		
		#reorder the gridLayout
		for j in range(len(self.pbList)):
			self.gridLayoutProgress.addWidget(self.chList[j], j, 0, 1, 1)
			self.gridLayoutProgress.addWidget(self.pbList[j], j, 1, 1, 1)
			self.gridLayoutProgress.addWidget(self.lblList[j], j, 2, 1, 1)
			self.gridLayoutProgress.addWidget(self.rmbList[j], j, 3, 1, 1)
			self.removeGroupBox.setId(self.rmbList[j],j)

		
		
		self.setMinimumHeight(self.height()-30)
		#self.setMinimumHeight(100)
		self.adjustSize()
		self.setMinimumSize(self.size())
		print(self.gridLayoutProgress.rowCount())
		#self.correctSize()
		'''
		for j in range(len(self.removeGroupBox.buttons())):
			button = self.removeGroupBox.buttons()[j]
			#print(button)
			#print('original id '+str(self.removeGroupBox.id(button)))
			#print('new id '+str(j))
			self.removeGroupBox.setId(button,j)
		'''
	
	def correctSize(self):
			self.logText.show()
			self.gridLayoutDebug.removeItem(self.spacerItem)
			self.adjustSize()
			self.setMinimumSize(self.size())
			self.repaint()
			self.gridLayoutDebug.addItem(self.spacerItem)
			self.logText.hide()
			self.setMinimumHeight(100)
			self.adjustSize()
			self.setMinimumSize(self.size())
	
	def showDebugLog(self, bol):
		if bol:
			self.logText.show()
			self.hideShowLog.setText('Hide Log')
			self.gridLayoutDebug.removeItem(self.spacerItem)
			self.adjustSize()
			#self.resize(self.width(), self.height()+80)
			self.setMinimumSize(self.size())
		else:
			self.gridLayoutDebug.addItem(self.spacerItem)
			self.logText.hide()
			self.hideShowLog.setText('Show Log')
			self.setMinimumHeight(100)
			self.adjustSize()
			self.setMinimumSize(self.size())
			#self.resize(self.width(), 100)
			#self.setGeometry(0,0,self.width(),100)
	
	'''
	Execution Functions
	'''
	def execute_this_fn(self, path, codec, alpha, frameRate, nameFrom, i, progress_callback, errorFFMPEG_callback):
		#print(path)
		pyCompression = pyFFMEGCompress(path, codec, alpha, frameRate, nameFrom)
		ffProcess = pyCompression.ffmpegCompress()
		self.lblList[i].setPixmap(self.processingPix)
		
		#with kwargs
		kwargs = {'progress_callback':progress_callback, 'errorFFMPEG_callback':errorFFMPEG_callback}
		pyCompression.printProcess(ffProcess, **kwargs)

		return (pyCompression.debugString,pyCompression.error,i)
 
	def printOutput(self, s):
		print("Printing output "+ str(s))
		
	def threadComplete(self, r):
		#print("THREAD COMPLETE! WITH ERROR " + str(r[2]) )
		if r[1] == False:
			self.lblList[r[2]].setPixmap(self.goodPix)
			self.pbList[r[2]].setStyleSheet("QProgressBar::chunk {background: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #44dd14,stop: 0.4999 #39c10f,stop: 0.5 #39c10f,stop: 1 #39c10f );border-radius: 3px; border: 1px solid #29880b;}QProgressBar{color:white}")
			self.pbList[r[2]].setValue(100)

	def errorPB(self, err):
		for i in range(len(self.pbList)):
			if self.pbList[i].format() == err:
				self.pbList[i].setValue(100)
				self.pbList[i].setStyleSheet("QProgressBar::chunk {background: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #FF0350,stop: 0.4999 #FF0020,stop: 0.5 #FF0019,stop: 1 #FF0000 );border-radius: 3px; border: 1px solid #a60233;}QProgressBar{color:white}")
				self.pbList[i].setFormat(self.pbList[i].format()+" - Error")
				self.chList[i].setChecked(False)
				self.lblList[i].setPixmap(self.badPix)
				
	def resetProgressBar(self, pb, text, lbl):
		pb.setValue(0)
		pb.setFormat(text + ' %p%')
		pb.setStyleSheet(self.defaulStyle)
		lbl.setPixmap(self.okPix)
	

	def compressPress(self):
		for i in range(len(self.pbList)):				
			if self.chList[i].isChecked():
				self.resetProgressBar(self.pbList[i],self.inList[i],self.lblList[i])
				
				worker = Worker(self.execute_this_fn, self.inList[i], self.codec, self.alpha, self.frameRate, self.nameFrom, i) # Any other args, kwargs are passed to the run function
				#worker.signals.result.connect(self.printOutput)
				worker.signals.result.connect(self.logText.appendPlainText)
				worker.signals.progress.connect(self.pbList[i].setValue)
				worker.signals.errorFFMPEG.connect(self.errorPB)
				worker.signals.error.connect(self.errorPB)
				worker.signals.finished.connect(self.threadComplete)
				#worker.signals.finished.connect(self.logText.appendPlainText)
				
				# Execute
				self.threadpool.start(worker)

	def exploreOutput(self):
		FILEBROWSER_PATH = os.path.join(os.getenv('WINDIR'), 'explorer.exe')
		explorePath = os.path.normpath('C:\\ExportedMOVs')
		subprocess.run([FILEBROWSER_PATH, explorePath])