def wordlist_dir_dialog(self): """ Pop up the "open a file" dialog and ask for which corpus text file to use """ self.corpus_filename = self._get_filename_from_dialog(ftype='wordlist') process_all_gui_events() if type(self.corpus_filename) != str: return # note that self.corpus_filename is an absolute full path self.corpus_name = os.path.basename(self.corpus_filename) self.corpus_stem_name = Path(self.corpus_name).stem self.lexicon = read_wordlist(self.corpus_filename) self.initialize_lexicon_tree() self.load_main_window(major_display=QWidget(), parameter_window=QWidget()) process_all_gui_events() self.status.clearMessage() self.status.showMessage( 'Wordlist selected: {}'.format(self.corpus_filename))
def _get_filename_from_dialog(self, ftype='input'): self.determine_last_file() if self.last_file_path and self.last_file_type == ftype: open_dir = self.last_file_path else: open_dir = os.getcwd() # noinspection PyTypeChecker,PyCallByClass fname = QFileDialog.getOpenFileName(self, 'Select the {} file'.format(ftype), open_dir) process_all_gui_events() # HACK: fname is supposed to be a string (at least according to the # PyQt5 documentation), but for some reason fname is a tuple. # So we need this hack to make sure that fname is a string of a filename # -- Jackson Lee, 2015/06/22 # update: it's turned out that this behavior is due to compatibility # between PyQt and PySide. The "tuple" behavior is in line with the # newer API2 for PyQt. (PyQt on python 3 uses API2 by default.) # more here: http://srinikom.github.io/pyside-bz-archive/343.html # so perhaps we keep our current hack for now? # -- Jackson Lee, 2015/08/24 if fname and any(fname) and (type(fname) is tuple): return fname[0] else: # if this hack isn't needed somehow... return fname
def update_progress(self, progress_text, target_percentage): """ Update the progress dialog. This function is triggered by the "progress_signal" emitted from the linguistica component worker thread. """ self.progressDialog.setLabelText(progress_text) self.progressDialog.setValue(target_percentage) process_all_gui_events()
def parameters_dialog(self): if self.lexicon is None: warning = QMessageBox() warning.setIcon(QMessageBox.Warning) warning.setText('Parameters can only be accessed when an input ' 'file is specified.') warning.setWindowTitle('No input file selected') warning.setStandardButtons(QMessageBox.Ok) warning.exec_() return process_all_gui_events() parameters = self.lexicon.parameters() dialog = QDialog() layout = QVBoxLayout() layout.addWidget( QLabel('Filename: {}'.format(Path(self.corpus_filename).name))) file_type = 'Wordlist' if self.lexicon.file_is_wordlist else 'Corpus' layout.addWidget(QLabel('Type: {}'.format(file_type))) grid = QGridLayout() self.parameter_spinboxes = [QSpinBox() for _ in range(len(parameters))] for i, parameter_name in enumerate(sorted(parameters.keys())): self.parameter_spinboxes[i].setObjectName(parameter_name) self.parameter_spinboxes[i].setRange( *PARAMETERS_RANGES[parameter_name]) self.parameter_spinboxes[i].setValue(parameters[parameter_name]) self.parameter_spinboxes[i].setSingleStep(1) # noinspection PyUnresolvedReferences self.parameter_spinboxes[i].valueChanged.connect( self.update_parameter) grid.addWidget(QLabel(parameter_name), i, 0) grid.addWidget(self.parameter_spinboxes[i], i, 1) grid.addWidget(QLabel(PARAMETERS_HINTS[parameter_name]), i, 2) layout.addLayout(grid) reset_button = QPushButton() reset_button.setText('&Reset') # noinspection PyUnresolvedReferences reset_button.clicked.connect(self.reset_parameters) spacer = QWidget() # just for padding in tool_bar spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) tool_bar = QHBoxLayout() tool_bar.addWidget(spacer) # so that the buttons are right-aligned tool_bar.addWidget(reset_button) layout.addLayout(tool_bar) dialog.setLayout(layout) dialog.setWindowTitle('Parameters') dialog.exec_()
def populate_lexicon_tree(self): self.lexicon_tree.clear() process_all_gui_events() # wordlist ancestor = QTreeWidgetItem(self.lexicon_tree, [WORDLIST]) self.lexicon_tree.expandItem(ancestor) # word ngrams ancestor = QTreeWidgetItem(self.lexicon_tree, [WORD_NGRAMS]) self.lexicon_tree.expandItem(ancestor) for item_str in [BIGRAMS, TRIGRAMS]: item = QTreeWidgetItem(ancestor, [item_str]) self.lexicon_tree.expandItem(item) # signatures ancestor = QTreeWidgetItem(self.lexicon_tree, [SIGNATURES]) self.lexicon_tree.expandItem(ancestor) for item in [SIGS_TO_STEMS, WORDS_TO_SIGS]: self.lexicon_tree.expandItem(QTreeWidgetItem(ancestor, [item])) # tries ancestor = QTreeWidgetItem(self.lexicon_tree, [TRIES]) self.lexicon_tree.expandItem(ancestor) for item in [WORDS_AS_TRIES, SUCCESSORS, PREDECESSORS]: self.lexicon_tree.expandItem(QTreeWidgetItem(ancestor, [item])) # phonology ancestor = QTreeWidgetItem(self.lexicon_tree, [PHONOLOGY]) self.lexicon_tree.expandItem(ancestor) for item in [PHONES, BIPHONES, TRIPHONES]: self.lexicon_tree.expandItem(QTreeWidgetItem(ancestor, [item])) # manifolds ancestor = QTreeWidgetItem(self.lexicon_tree, [MANIFOLDS]) self.lexicon_tree.expandItem(ancestor) for item in [WORD_NEIGHBORS, VISUALIZED_GRAPH]: self.lexicon_tree.expandItem(QTreeWidgetItem(ancestor, [item])) self.status.clearMessage() self.status.showMessage('Navigation tree populated') print('Lexicon navigation tree populated', flush=True)
def run_file(self): if self.lexicon is None: warning = QMessageBox() warning.setIcon(QMessageBox.Warning) warning.setText('No input file is selected.') warning.setWindowTitle('Error') warning.setStandardButtons(QMessageBox.Ok) warning.exec_() return self.status.clearMessage() self.status.showMessage('Running the file {} now...' .format(self.corpus_name)) print('\nInput file in use:\n{}\n'.format(self.corpus_filename), flush=True) # set up the Linguistica components worker # The worker is a QThread. We spawn this thread, and the linguistica # components run on this new thread but not the main thread for the GUI. # This makes the GUI still responsive # while the long and heavy running process of # the Linguistica components is ongoing. self.lxa_worker = LinguisticaWorker(self.lexicon) self.lxa_worker.progress_signal.connect(self.update_progress) # set up progress dialog process_all_gui_events() self.progressDialog = QProgressDialog() self.progressDialog.setRange(0, 100) # it's like from 0% to 100% self.progressDialog.setLabelText('Initializing...') self.progressDialog.setValue(0) # initialize as 0 (= 0%) self.progressDialog.setWindowTitle( 'Processing {}'.format(self.corpus_name)) self.progressDialog.setCancelButton(None) self.progressDialog.resize(400, 100) process_all_gui_events() self.progressDialog.show() # We disable the "cancel" button # Setting up a "cancel" mechanism may not be a good idea, # since it would probably involve killing the linguistica component # worker at *any* point of its processing. # This may have undesirable effects (e.g., freezing the GUI) -- BAD! # make sure all GUI stuff up to this point has been processed before # doing the real work of running the Lxa components process_all_gui_events() # Now the real work begins here! self.lxa_worker.start() process_all_gui_events() self.lexicon = self.lxa_worker.get_lexicon() print('\nAll Linguistica components run for the file', flush=True) self.status.clearMessage() self.status.showMessage('{} processed'.format(self.corpus_name)) self.load_main_window(major_display=QWidget(), parameter_window=QWidget()) self.populate_lexicon_tree() self.update_last_file() process_all_gui_events() # display corpus name (in the tree header label) file_type = 'wordlist' if self.lexicon.file_is_wordlist else 'corpus' header_label = 'File: {}\nFile type: {}\n\n# word types: {:,}\n'.format( self.corpus_name, file_type, self.lexicon.number_of_word_types()) if file_type == 'corpus': header_label += '# word tokens: {:,}\n'.format( self.lexicon.number_of_word_tokens()) self.lexicon_tree.setHeaderLabel(header_label)