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
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()
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']
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
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)
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)
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()