Esempio n. 1
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
Esempio n. 2
0
class MainWindow(QMainWindow):
    """
    You can use @pyqtSlot(int) syntax (with parameters), or you can pass this,
    but it make code more readable.
    Вы можете использовать синтаксис объявления слотов @pyqtSlot(int)
    с указанием типа передаваемых значений, или опустить его вовсе,
    однако это делает код нагляднее
    и позволяет быстро понять, что слот, а что - функция.
    """

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self._counter = 0
        self.init_ui()

        self._threadpool = QThreadPool()
        #self._threadpool = QtCore.QThreadPool.globalInstance()
        #self._threadpool.setMaxThreadCount(2)
        print("Multithreading with maximum {} threads" .format(self._threadpool.maxThreadCount()))

        self._timer = QTimer()
        self._timer.setInterval(1000)
        self._timer.timeout.connect(self.recurring_timer)
        self._timer.start()

    def init_ui(self):
        layout = QVBoxLayout()

        self._label = QLabel("Start")
        b = QPushButton("Start QRunnable")
        b.pressed.connect(self.start_new_runnable)

        layout.addWidget(self._label)
        layout.addWidget(b)

        w = QWidget()
        w.setLayout(layout)

        self.setCentralWidget(w)

    @pyqtSlot(int)
    def thread_progress_fn(self, n):
        print("{}% done".format(n))

    @pyqtSlot(object)
    def thread_print_output(self, s):
        print('Result: {}'.format(s))

    @pyqtSlot()
    def thread_complete(self):
        print("QRunnable worker COMPLETE!")

    @pyqtSlot(tuple)
    def thread_error(self, err):
        QMessageBox.warning(self, "Warning!", err[1], QMessageBox.Ok)
        print('Error {}\n{}'.format(err[1], err[2]))

    @pyqtSlot()
    def start_new_runnable(self):
        # Pass the function to execute
        worker = Worker(1, debug=True) # Any other args, kwargs are passed to the run function
        worker.signals.result.connect(self.thread_print_output)
        worker.signals.finished.connect(self.thread_complete)
        worker.signals.progress.connect(self.thread_progress_fn)
        worker.signals.error.connect(self.thread_error)
        worker.setAutoDelete(True)
        # Execute (tryStart() better than start() )
        if self._threadpool.tryStart(worker) is False:
            print("Can't create worker!")
            QMessageBox.warning(self, "Warning!", "Can't create worker!", QMessageBox.Ok)

    @pyqtSlot()
    def recurring_timer(self):
        self._counter += 1
        self._label.setText("Counter: {}".format(self._counter))
        print('Active thread count: {}'.format(self._threadpool.activeThreadCount()))

    def closeEvent(self, event):
        """Main window closed, override PyQt5 widget function"""
        print('Try to exit, active thread count: {}'.format(self._threadpool.activeThreadCount()))
        reply = QMessageBox.question(self, 'Message',
                                     "Are you sure to quit?", QMessageBox.Yes |
                                     QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            self._threadpool.waitForDone()
            self._timer.stop()
            event.accept()
        else:
            event.ignore()
Esempio n. 3
0
class DataHandler():
    '''
    This class takes care of starting a new QThread which
    is entirey dedicated to read data from the ESP32.
    You will need to connect a DataFiller using connect_data_filler(),
    and this class will fill the data direclty.
    '''
    def __init__(self, config, esp32):
        '''
        Initializes this class by creating a new QThreadPool
        '''
        self._running = False
        self._threadpool = QThreadPool()
        print('Number of available threads:',
              self._threadpool.maxThreadCount())

        self._config = config
        self._esp32 = esp32

        self._n_attempts = 0

    def connect_data_filler(self, data_filler):
        '''
        Connects a DataFiller to this class
        '''
        self._data_f = data_filler

    def esp32_data_callback(self, parameter, data):
        '''
        This method is called everytime there is a new read from
        the ESP32, and it receives the parameters read, and
        the data associated to it
        '''

        if parameter == 'Done.' and data is None and not self._running:
            # it is the signal that the thread is closing gracefully,
            # ignore it! TODO: feel free to implement a better way to do
            # this.
            return

        # print('Got data:', parameter, data)
        status = self._data_f.add_data_point(parameter, data)

        # if not status:
        #     print("\033[91mERROR: Will ingore parameter {parameter}.\033[0m")

    def stop_io(self):
        '''
        Ask the thread to gracefully stop iterating
        '''

        self._running = False
        self._threadpool.waitForDone()

    def esp32_io(self, data_callback):
        '''
        This is the main function that runs in the thread.
        '''

        if self._running:
            return

        self._running = True

        while self._running:
            start = datetime.datetime.now()

            # Get all params from ESP
            current_values = self._esp32.get_all()

            # Converting from str to float
            for p, v in current_values.items():
                current_values[p] = float(v)

            # finally, emit for all the values we have:
            for p, v in current_values.items():
                data_callback.emit(p, v)

            delta_secs = (datetime.datetime.now() - start).total_seconds()
            sleep_secs = max(0, self._config['sampling_interval'] - delta_secs)

            # Sleep for some time...
            time.sleep(sleep_secs)

        return "Done."

    def thread_complete(self):
        '''
        Called when a thread ends.
        '''

        if self._running:
            print(
                "\033[91mERROR: The I/O thread finished! Going to start a new one...\033[0m"
            )
            self._n_attempts += 1
            self._running = False

            if self._n_attempts > 10:
                self._n_attempts = 0
                msg = MessageBox()

                # TODO: find a good exit point
                callbacks = {
                    msg.Retry: self.start_io_thread,
                    msg.Abort: lambda: sys.exit(-1)
                }

                fn = msg.critical("COMMUNICATION ERROR",
                                  "CANNOT COMMUNICATE WITH THE HARDWARE",
                                  "Check cable connections then click retry.",
                                  "COMMUNICATION ERROR", callbacks)
                fn()

            time.sleep(0.05)
            self.start_io_thread()

    def start_io_thread(self):
        '''
        Starts the thread.
        '''
        worker = Worker(self.esp32_io)
        worker.signals.result.connect(self.esp32_data_callback)
        worker.signals.finished.connect(self.thread_complete)

        self._threadpool.start(worker)

    def set_data(self, param, value):

        result = self._esp32.set(param, value)

        return result == self._config['return_success_code']
Esempio n. 4
0
class MainControl():
    def __init__(self, UI):
        self.preprocessor = Preprocessor()
        self.k = 0
        self.storage = Storage()
        self.threadpool = QThreadPool()

    def classifyReview(self, review, tree):
        clf = self.storage.load(f"./data/models/{tree}")
        return clf.predict(clf.vectors, [review])

    def import_excel(self, UI):
        return self.openFileDialog(UI)

    def openFileDialog(self, UI):
        try:
            options = QFileDialog.Options()
            options |= QFileDialog.DontUseNativeDialog
            fileName, _ = QFileDialog.getOpenFileName(
                UI,
                "Select Excel File",
                "",
                "Excel Files(*.xls *.xlsx)",
                options=options)
            if (fileName):
                importer = DataImporter(fileName)
                return importer.get_data()
        except:
            UI.msg = QMessageBox()
            UI.msg.setIcon(QMessageBox.Warning)
            UI.msg.setWindowTitle("Warning")
            UI.msg.setText("File tidak memiliki kolom Review dan Label")
            UI.msg.setStandardButtons(QMessageBox.Ok)
            UI.msg.show()
            UI.statusBar().showMessage("Import failed")
        return None

    def preprocess_data(self, UI, data):
        totalTime = 0
        resultReview = []
        for i, (review, label) in enumerate(zip(data["Review"],
                                                data["Label"])):
            if i > 0:
                UI.tableWidget.item(i - 1,
                                    0).setBackground(QColor(255, 255, 255))
            UI.tableWidget.item(i, 0).setBackground(QColor(255, 128, 128))
            startTime = time.time()
            preprocessedReview = " ".join(self.preprocessor.preprocess(review))
            endTime = time.time()
            resultReview.append(preprocessedReview)
            UI.logOutput.append(
                f"Review {i + 1} preprocessed in {round(endTime - startTime, 2)}s"
            )
            totalTime += (endTime - startTime)
            UI.tableWidget.scrollToItem(UI.tableWidget.item(i - 1, 0),
                                        QAbstractItemView.PositionAtCenter)
            QApplication.processEvents()
        dlen = len(data)
        UI.tableWidget.item(dlen - 1, 0).setBackground(QColor(255, 255, 255))
        UI.tableWidget.scrollToItem(UI.tableWidget.item(dlen - 1, 0),
                                    QAbstractItemView.PositionAtCenter)
        UI.logOutput.append(
            f"{dlen} review(s) preprocessed in {round(totalTime, 2)}s")
        return resultReview

    def save_data(self, data):
        self.storage.save(data, "data/preprocessed/preprocessed.pckl")

    def fold_data(self, k, UI=None):
        self.k = k
        self.threadpool.setMaxThreadCount(self.k)
        self.data = self.storage.load("data/preprocessed/preprocessed.pckl")
        kf = KFold(n_splits=self.k, shuffle=True, random_state=2)
        for i, (train, test) in enumerate(kf.split(self.data)):
            self.storage.save(self.data.iloc[train],
                              f"data/folds/train{i + 1}.pckl")
            self.storage.save(self.data.iloc[test],
                              f"data/folds/test{i + 1}.pckl")
        if UI is not None:
            UI.logOutput.append(f"Data folded by {k}")

    def mltrain_fn(self,
                   params={
                       'i': None,
                       'remove_zero_tfidf': False,
                       'UI': None
                   }):
        train = self.storage.load(f"data/folds/train{params['i'] + 1}.pckl")
        tfidf = TFIDF(train["Review"])
        if params['remove_zero_tfidf']:
            tfidf.weights = tfidf.remove_zero_tfidf(tfidf.weights, 0.4)
        clf = C45(tfidf, train)
        clf.train()
        return params["i"], clf, tfidf, params['UI'] or None

    def mltrain_result(self, res):
        self.attrs[res[0]] = res[2].count_vect.get_feature_names()
        self.clfs[res[0]] = res[1]
        self.tfidfs[res[0]] = res[2]
        self.storage.save(res[1], f"data/models/tree{res[0] + 1}.pckl")
        if res[3] is not None:
            res[3].logOutput.append(f"Tree {res[0] + 1} trained")

    def train_model(self, UI=None):
        if self.k <= 0 and UI is not None:
            UI.msg = QMessageBox()
            UI.msg.setIcon(QMessageBox.Warning)
            UI.msg.setWindowTitle("Warning")
            UI.msg.setText(
                "Anda harus membagi data menggunakan k-fold terlebih dahulu")
            UI.msg.setStandardButtons(QMessageBox.Ok)
            UI.msg.show()
            UI.statusBar().showMessage("Train and test failed")
            return False

        self.attrs, self.clfs, self.tfidfs = ([0 for i in range(self.k)
                                               ], [0 for i in range(self.k)],
                                              [0 for i in range(self.k)])
        el = QEventLoop()
        for i in range(self.k):
            if UI is not None:
                UI.logOutput.append(f"Train tree {i + 1}")
            params = {'i': i, 'remove_zero_tfidf': True, 'UI': UI}
            worker = Worker(self.mltrain_fn, params)
            worker.signals.result.connect(self.mltrain_result)
            self.threadpool.start(worker)

        self.threadpool.waitForDone()
        el.processEvents()
        el.exit()
        if UI is not None:
            UI.logOutput.append("Training completed")
        return self.attrs

    def test_model(self):
        scores = [0 for i in range(self.k)]
        for i in range(self.k):
            test = self.storage.load(f"data/folds/test{i + 1}.pckl")
            score = self.clfs[i].score(self.tfidfs[i], test)
            self.clfs[i].set_score(score)
            # self.clfs[i].scores = self.clfs[i].score(self.tfidfs[i], test)
            self.storage.save(self.clfs[i], f"data/models/tree{i + 1}.pckl")
            # scores[i] = self.clfs[i].scores
            scores[i] = score
        return scores

    def optimize_model(self, popSize, numIteration, c1, c2, target):
        results = []
        for i in range(self.k):
            train, test = self.storage.load(
                f"data/folds/train{i + 1}.pckl"), self.storage.load(
                    f"data/folds/test{i + 1}.pckl")
            clf = self.storage.load(f"data/models/tree{i + 1}.pckl")
            particleSize = len(clf.termsInfo)
            pso = PSO(particleSize, popSize, numIteration, c1, c2,
                      clf.get_score() + target)
            bestParticle = pso.exec(train, test)
            results.append(bestParticle)
            self.storage.save(bestParticle,
                              f"data/particles/particle{i + 1}.pckl")
        return results

    def get_data(self, kth, dstype):
        t = "train" if dstype == "Training Data" else "test"
        return self.storage.load(f"data/folds/{t}{kth}.pckl")

    def load_data(self, path):
        if os.path.exists(path):
            return self.storage.load(path)
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Warning)
        msg.setWindowTitle("Error")
        msg.setText("Data yang dimuat tidak ada")
        msg.setStandardButtons(QMessageBox.Ok)
        msg.exec_()
        return None
Esempio n. 5
0
class DataHandler():
    '''
    This class takes care of starting a new QThread which
    is entirey dedicated to reading data from the ESP32.
    You will need to connect a DataFiller using connect_data_filler(),
    and this class will fill the data direclty.
    '''
    def __init__(self, config, esp32):
        '''
        Initializes this class by creating a new QThreadPool
        '''
        self._running = False
        self._threadpool = QThreadPool()
        print('Number of available threads:',
              self._threadpool.maxThreadCount())

        self._config = config
        self._esp32 = esp32

        self._n_attempts = 0

    def connect_data_filler(self, data_filler):
        '''
        Connects a DataFiller to this class
        '''
        self._data_f = data_filler

    def esp32_data_callback(self, parameter, data):
        '''
        This method is called everytime there is a new read from
        the ESP32, and it receives the parameters read, and
        the data associated to it
        '''

        if parameter == 'Done.' and data is None and not self._running:
            # it is the signal that the thread is closing gracefully,
            # ignore it! TODO: feel free to implement a better way to do
            # this.
            return

        # print('Got data:', parameter, data)
        status = self._data_f.add_data_point(parameter, data)

        if not status:
            print(f"\033[91mERROR: Will ingore parameter {parameter}.\033[0m")

    def stop_io(self):
        '''
        Ask the thread to gracefully stop iterating
        '''

        self._running = False
        self._threadpool.waitForDone()

    def esp32_io(self, data_callback):
        '''
        This is the main function that runs in the thread.
        '''

        if self._running:
            return

        self._running = True

        while self._running:
            # retrieve the values
            mve = float(self._esp32.get("mve"))
            vti = float(self._esp32.get("vti"))
            vte = float(self._esp32.get("vte"))

            # data_callback emits a signal, which is
            # received by esp32_data_callback, which
            # then sets the parameters in the DataFiller
            data_callback.emit('mve', mve)
            data_callback.emit('vti', vti)
            data_callback.emit('vte', vte)

            # Sleep for some time...
            time.sleep(self._config['sampling_interval'])

        return "Done."

    def thread_complete(self):
        '''
        Called when a thread ends.
        '''

        if self._running:
            print(
                f"\033[91mERROR: The I/O thread finished! Going to start a new one...\033[0m"
            )
            self._n_attempts += 1
            if self._n_attempts > 100:
                raise Exception(
                    'Failed to communicate with ESP after 100 attempts.')
            self._running = False
            self.start_io_thread()

    def start_io_thread(self):
        '''
        Starts the thread.
        '''
        worker = Worker(self.esp32_io)
        worker.signals.result.connect(self.esp32_data_callback)
        worker.signals.finished.connect(self.thread_complete)

        self._threadpool.start(worker)
Esempio n. 6
0
class CommandExecutionFactory(QThread):
    finish_event = pyqtSignal('PyQt_PyObject', int)
    result_event = pyqtSignal('PyQt_PyObject')

    def __init__(self, runnable_commands, logger=None, max_threads=None):
        super().__init__()
        self.pending_jobs = runnable_commands
        self.total_jobs = len(self.pending_jobs)
        self.completed = []
        self.thread_pool = QThreadPool()
        self.stop = False
        self.logger = logger
        if max_threads:
            self.max_threads = min(max_threads,
                                   self.thread_pool.maxThreadCount())
        else:
            self.max_threads = self.thread_pool.maxThreadCount()

        self.log(f"Multi-threading with maximum of {self.max_threads} threads")

    def add_task(self, task):
        self.pending_jobs.append(task)
        self.total_jobs += 1
        self.run()

    def get_max_threads(self):
        return self.max_threads

    def get_active_thread_count(self):
        return self.thread_pool.activeThreadCount()

    def stop_scan(self):
        self.stop = True

    def run(self):
        self.do_work()

    def thread_complete(self, result):
        self.completed.append(result)
        self.do_work()

    def is_running(self):
        return self.thread_pool.activeThreadCount() > 0

    def do_work(self):
        if len(self.pending_jobs) > 0:
            if self.stop:
                self.log(
                    "Stop has been received. Waiting for threads to finish before exiting..."
                )
                self.thread_pool.waitForDone()
                self.log(
                    f"Waiting for {self.total_jobs - len(self.pending_jobs) - len(self.completed)} "
                    f"threads to finish")
                if (self.total_jobs - len(self.pending_jobs) -
                        len(self.completed)) == 0:
                    self.finish_event.emit(self.completed, sum(self.completed))
                return
            active_threads = self.thread_pool.activeThreadCount()
            available_threads = self.max_threads - active_threads
            for i in range(0, min(available_threads, len(self.pending_jobs))):
                self.log("Dispatching thread on empty slot...")
                runnable = self.pending_jobs.pop()
                runnable.signals.__complete__.connect(self.thread_complete)
                runnable.setAutoDelete(True)
                # Execute
                self.thread_pool.start(runnable)
        else:
            self.log("Waiting for threads to finish")
            if len(self.completed) == self.total_jobs:
                total_time = sum(self.completed)
                self.log(f"Done. Total work completed in...{total_time}")
                self.finish_event.emit(self.completed, total_time)

    def log(self, message):
        if self.logger:
            self.logger.info(message)
Esempio n. 7
0
class CalculationThread(QThread):
    done = pyqtSignal(str, str, str)
    progress = pyqtSignal(int)

    def __init__(self, is_cord_dim_enabled, files_list, dimension, window_size,
                 step_size, cor_dim_radius, is_samp_en, is_ap_en,
                 en_use_threshold, en_threshold_value, en_dev_coef_value,
                 en_calculation_type, is_frac_dim_enabled, fd_max_k,
                 is_pertropy_enabled, is_pertropy_normalized, pertropy_stride):
        QThread.__init__(self)
        self.is_cord_dim_enabled = is_cord_dim_enabled
        self.files_list = files_list
        self.dimension = dimension
        self.window_size = window_size
        self.step_size = step_size
        self.cor_dim_radius = cor_dim_radius
        self.is_samp_en = is_samp_en
        self.is_ap_en = is_ap_en
        self.en_use_threshold = en_use_threshold
        self.en_threshold_value = en_threshold_value
        self.en_dev_coef_value = en_dev_coef_value
        self.en_calculation_type = en_calculation_type
        self.is_frac_dim_enabled = is_frac_dim_enabled
        self.fd_max_k = fd_max_k
        self.is_pertropy_enabled = is_pertropy_enabled
        self.is_pertropy_normalized = is_pertropy_normalized
        self.pertropy_stride = pertropy_stride

        self.res_dic = {}
        self.mutex = QMutex()
        self.threadpool = QThreadPool()
        print("Multithreading with maximum %d threads" %
              self.threadpool.maxThreadCount())
        self.print_my_configuration()

    def __del__(self):
        self.wait()

    def run(self):
        self.calc(self.is_cord_dim_enabled, self.files_list, self.dimension,
                  self.window_size, self.step_size, self.cor_dim_radius,
                  self.is_samp_en, self.is_ap_en, self.en_use_threshold,
                  self.en_threshold_value, self.en_dev_coef_value,
                  self.en_calculation_type, self.is_frac_dim_enabled,
                  self.fd_max_k, self.is_pertropy_enabled,
                  self.is_pertropy_normalized, self.pertropy_stride)

    def calc(self,
             is_cord_dim_enabled,
             files_list,
             dimension,
             window_size,
             step_size,
             cor_dim_radius=0,
             is_samp_en=False,
             is_ap_en=False,
             en_use_threshold=False,
             en_threshold_value=0,
             en_dev_coef_value=0,
             en_calculation_type=0,
             is_frac_dim_enabled=False,
             fd_max_k=0,
             is_pertropy_enabled=False,
             is_pertropy_normalized=False,
             pertropy_stride=None):
        # calculate what is the denominator for progress
        # num files * number of analysis algos
        num_algos = 0
        if is_cord_dim_enabled:
            num_algos += 1
        if is_frac_dim_enabled:
            num_algos += 1
        if is_samp_en:
            num_algos += 1
        if is_ap_en:
            num_algos += 1
        if is_pertropy_enabled:
            num_algos += 1
        self.full_job = len(files_list) * num_algos

        self.job_index = 0
        workers = []
        for file_name in files_list:
            if is_cord_dim_enabled:
                workers.append(
                    GeneralWorker(CorDim.prepare_calculate_window_cor_dim,
                                  file_name, dimension, cor_dim_radius,
                                  window_size, step_size))

            if is_frac_dim_enabled:
                workers.append(
                    GeneralWorker(FracDim.prepare_calculate_window_frac_dim,
                                  file_name, fd_max_k, window_size, step_size))

            if is_samp_en:
                workers.append(
                    GeneralWorker(SampleEntropy.prepare_calculate_windowed,
                                  dimension, file_name, en_use_threshold,
                                  en_threshold_value, window_size, step_size,
                                  en_calculation_type, en_dev_coef_value))

            if is_ap_en:
                workers.append(
                    GeneralWorker(
                        ApproximateEntropy.prepare_calculate_windowed,
                        dimension, file_name, en_use_threshold,
                        en_threshold_value, window_size, step_size,
                        en_calculation_type, en_dev_coef_value))

            if is_pertropy_enabled:
                workers.append(
                    GeneralWorker(
                        PermutationEntropy.prepare_calculate_windowed,
                        dimension, file_name, en_use_threshold,
                        en_threshold_value, window_size, step_size, None, None,
                        is_pertropy_normalized, pertropy_stride))

        for worker in workers:
            self.threadpool.start(worker)
            worker.signals.result.connect(self.receive_report)

    def on_threads_completed(self):
        self.threadpool.waitForDone()
        self.job_index = 0
        self.full_job = 0

        if not self.res_dic:
            self.done.emit('Error', 'Result is empty', None)
            return

        analysis_names = ReportManager.get_analysis_types(self.res_dic)
        ReportManager.prepare_write_report(analysis_types=analysis_names,
                                           res_dic=self.res_dic)

        all_analysis_names = ','.join(analysis_names)
        all_files_names = ','.join(list(self.res_dic.keys()))
        self.done.emit(all_analysis_names, all_files_names,
                       ReportManager.get_report_path())

    def update_progress(self, full, current):
        progress = math.ceil((current / full) * 100)
        if progress == 100:
            self.on_threads_completed()
        self.progress.emit(progress)

    def receive_report(self, report):
        self.mutex.lock()
        try:
            self.res_dic[report.get_file_name()].append(report)
        except KeyError:
            self.res_dic[report.get_file_name()] = [
                report,
            ]

        self.job_index += 1
        self.update_progress(self.full_job, self.job_index)
        self.mutex.unlock()

    def print_my_configuration(self):
        print('m: {}'.format(self.dimension))
        print('window size: {}'.format(self.window_size))
        print('step size: {}'.format(self.step_size))
        print('calculating cordim: {}'.format(self.is_cord_dim_enabled))
        print('cordim radius: {}'.format(self.cor_dim_radius))
        print('calculating apen: {}'.format(self.is_ap_en))
        print('calculating sampen: {}'.format(self.is_samp_en))
        print('using threshold: {}'.format(self.en_use_threshold))
        print('threshold: {}'.format(self.en_threshold_value))
        print('r type: {}'.format(self.en_calculation_type))
        print('r dev coef: {}'.format(self.en_dev_coef_value))
        print('calculating fracdim: {}'.format(self.is_frac_dim_enabled))
        print('fracdim max k: {}'.format(self.fd_max_k))
        print('calculating pertropy: {}'.format(self.is_pertropy_enabled))
class SourceDialogSlots():
    """ Handles user interaction with the GUI of the dialog. Each function
    is called when a specific action is performed by the user, to fulfill
    the request that corresponds to that action.

    Attributes:
        model: Object of the Download Class.
        view:  The view of the Source Dialog.
        appPath(str): The path where the application will store & fetch data.
        filepathList(list of str): A list that contains all the files path(or url) which are valid.
        downloadList(list of str): A list that contains all the urls which are valid.
        validSources(list of SourceHBox): A list that contains all the sources which are valid.
    """
    def __init__(self,model,view):
        super().__init__()
        self.model = model
        self.view = view
        self.appPath = get_appData_path()

        self.threadPool = QThreadPool()
        self.filepathList = []
        self.downloadList = []
        self.validSources = []

    def on_deleteButton_clicked(self,sourceHBox):
        """ Removes a source.
        Args:
            sourceHBox: The source to be deleted.

        Trigger: User clicks the "Delete" Button(Trash Icon) on the Source Dialog.
        """

        # Remove sourceHBox from the sourcesList
        self.view.sources.remove(sourceHBox)
        self.view.sourcesCounter = self.view.sourcesCounter-1

        # Remove Source Horizontal Box from View
        self.view.layout.removeWidget(sourceHBox)
        sourceHBox.deleteLater()
        sourceHBox = None

        # Fix GUI after the removal of the sourceHBox
        self.view.adjustSize()

    def on_applyButton_clicked(self):
        """ Checks validity of all of the sources. Creates all the
        list(filepathList,downloadList,validSources) and sets the status of each source.

        Trigger: User clicks the "Apply" Button on the Source Dialog.
        """

        #Empty Lists
        self.filepathList = []
        self.downloadList = []
        self.validSources = []

        # Create a Thread to complete the operations.
        applyThread = Thread(target=self.apply_thread)
        applyThread.daemon = True
        applyThread.start()

    def on_okButton_clicked(self):
        """ Closes the Source Dialog and performs all the necessary operations depending
        the user input. These operations are:
            1) Download the pgn files, if any
            2) Make the games.pgn from all the sources, if there are valid sources.
            3) If the rememberOption is checked save the valid sources to the JSON file.

        Trigger: User clicks the "OK" Button of the Source Dialog.
        """

        # Create a Thread to complete the operations.
        exitThread = Thread(target=self.on_exit_thread)
        exitThread.daemon = True
        exitThread.start()

        # Close the Dialog
        self.view.accept()
        self.view.close()

    def apply_thread(self):
        """ Function called by Thread to perform the operations of the on_applyButton_clicked."""

        # Loop to check the validity of all the sources.
        for source in self.view.sources:
            if (source.get_source_index() == 0): # Web Download Option
                checkDownloadWorker = CheckDownload(self,source)
                self.threadPool.start(checkDownloadWorker)
            else: # Local File Option
                filepath = source.get_value()
                if os.path.exists(filepath):
                    source.set_status("ok")
                    if filepath not in self.filepathList:
                        self.filepathList.append(filepath)
                        self.validSources.append(source)
                else:
                    source.set_status("error")

        # Wait until all the sources are checked.
        self.threadPool.waitForDone()

        # Add the valid urls to the filepathList
        for index in range(0,len(self.downloadList)):
            filepath = os.path.join(self.appPath,"games"+str(index)+".pgn")
            if filepath not in self.filepathList:
                self.filepathList.append(filepath)

    def on_exit_thread(self):
        """ Function called by Thread to perform the operations of the on_okButton_clicked."""

        # Download the pgn files
        downloadListWorker = DownloadList(self.downloadList,False)
        downloadListWorker.start()
        downloadListWorker.wait()

        #  Make the games.pgn
        makePgnWorker = MakePgn(self.filepathList,False)
        makePgnWorker.start()

        # If the rememberOption is checked save the valid sources to the JSON file.
        rememberOption = self.view.get_remember_option()
        if (rememberOption.isChecked()):
            self.save_sources()
        else:
            os.remove(os.path.join(self.appPath,'sources.json'))

    def save_sources(self):
        """ Saves the valid sources to the JSON file """
        data = []
        for source in self.validSources:
            data.append({"option":source.get_source_index(),"value":source.get_value()})

        file = open(os.path.join(self.appPath,'sources.json'), 'w')
        json.dump(data,file,indent=4)
        file.close()