def __init__(self, *args): QtGui.QMainWindow.__init__(self, *args) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.texteditInterlinear.setReadOnly(True) self.initConnects() self.initSettings() self.project = PoioProject(os.getcwd()) self.ui.listFiles.setModel(self.project) self.initCorpusReader() self.currentFilter = AnnotationTreeFilter()
def __init__(self, *args): """ Initialize Main Window """ QtGui.QMainWindow.__init__(self, *args) # self.reset_data_structure_type(poioapi.data.GRAID) self.vertical_position_of_file = {} self.ui = Ui_MainWindow() self.ui.setupUi(self) self.init_connects() self.init_settings() self.project = PoioProject(os.getcwd()) self.ui.listFiles.setModel(self.project) self.init_corpus() self.add_search_tab()
def __init__(self, *args): """ The consctructor of the main application object, i.e. the main window. Calls a lot of other init methods. """ QtGui.QMainWindow.__init__(self, *args) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.init_vars() self.init_connects() self.init_settings() self.init = 0 self.project = PoioProject(os.getcwd()) self.ui.listFiles.setModel(self.project) self.ui.projectManager.setShown(False) self.ui.projectBtn.setStyleSheet(""" QPushButton#projectBtn { border-style: outset; border-width: 1px; border-color: black; padding: 1px; } QPushButton#projectBtn:checked { background-color: lightblue; border-style: inset; border-width: 2px; }""") self.ui.textedit.append_title( self.tr("Please create or open a file...")) self._dialog_find_and_replace = FindReplaceDialog(self) self._dialog_find_and_replace.setModal(False) self._dialog_find_and_replace.set_text_edit(self.ui.textedit) self._dialog_find = FindDialog(self) self._dialog_find.setModal(False) self._dialog_find.set_text_edit(self.ui.textedit)
class PoioAnalyzer(QtGui.QMainWindow): """The main window of the PoioAnalyzer application.""" def __init__(self, *args): """ Initialize Main Window """ QtGui.QMainWindow.__init__(self, *args) # self.reset_data_structure_type(poioapi.data.GRAID) self.vertical_position_of_file = {} self.ui = Ui_MainWindow() self.ui.setupUi(self) self.init_connects() self.init_settings() self.project = PoioProject(os.getcwd()) self.ui.listFiles.setModel(self.project) self.init_corpus() self.add_search_tab() def init_corpus(self): """ Initializes an empty corpus. """ # print sys.path self.corpus = poioapi.corpus.CorpusGraphs() def init_connects(self): """ Initializes all signal/slots connections of the application. """ # Menu buttons self.ui.actionPrint.triggered.connect(self.printdoc) self.ui.actionQuit.triggered.connect(self.close) self.ui.actionAboutPoioAnalyzer.triggered.connect(self.about_dialog) self.ui.actionZoom_In.triggered.connect(self.zoom_in) self.ui.actionZoom_Out.triggered.connect(self.zoom_out) self.ui.actionReset_Zoom.triggered.connect(self.reset_zoom) # Push Buttons self.ui.buttonAddFiles.pressed.connect(self.add_files) self.ui.buttonRemoveFiles.pressed.connect(self.remove_files) # Filter and Search self.ui.buttonSearch.pressed.connect(self.apply_filter) self.ui.buttonCloseThisSearch.pressed.connect(self.search_tab_closed) self.ui.buttonClearThisSearch.pressed.connect(self.search_tab_cleared) self.ui.tabWidget.currentChanged.connect(self.search_tab_changed) self.ui.listFiles.activated.connect(self.set_current_file_in_result_view) self.ui.actionExportSearchResult.triggered.connect(self.export_search_results) # Quick Search # QtCore.QObject.connect(self.ui.actionQuickSearch, QtCore.SIGNAL("triggered()"), self.ui.lineeditQuickSearch.setFocus) # QtCore.QObject.connect(self.ui.lineeditQuickSearch, QtCore.SIGNAL("textChanged(const QString &)"), self.findFromStart) # QtCore.QObject.connect(self.ui.lineeditQuickSearch, QtCore.SIGNAL("returnPressed()"), self.findNext) def init_settings(self): """ Initialize QSettings; Set default zoom to 100% """ QtCore.QCoreApplication.setOrganizationName("Interdisciplinary Centre for Social and Language Documentation") QtCore.QCoreApplication.setOrganizationDomain("cidles.eu") QtCore.QCoreApplication.setApplicationName("PoioAnalyzer") settings = QtCore.QSettings() settings.setValue("FontZoom", 100) # def reset_data_structure_type(self, data_structure_type): # self.data_structure_type = data_structure_type # self.structure_type_handler = poioapi.data.data_structure_handler_for_type( # data_structure_type # ) def remove_files(self): """ Remove files from the "Added files list" """ countRemoved = 0 for i in self.ui.listFiles.selectedIndexes(): self.project.removeFilePathAt(i.row() - countRemoved) countRemoved = countRemoved + 1 self.init_corpus() self.project.setAllFilesAsNew() self.update_corpus_reader() self.update_result_view() def add_files(self): """ Loads the file as filepath; Tells the user the time it took to load the file selected. """ # PySide version # filepaths, types = QtGui.QFileDialog.getOpenFileNames(self, self.tr("Add Files"), "", self.tr("Elan files (*.eaf);;Toolbox files (*.txt);;All files (*.*)")) # PyQt version filepaths = QtGui.QFileDialog.getOpenFileNames( self, self.tr("Add Files"), "", self.tr("Elan files (*.eaf);;Pickle files (*.pickle);;Typecraft files (*.xml)"), ) # filepaths = QtGui.QFileDialog.getOpenFileNames(self, self.tr("Add Files"), "", self.tr("Elan files (*.eaf);;Toolbox files (*.txt);;Kura files (*.xml);;All files (*.*)")) self.project.addFilePaths(filepaths) start = time.time() self.update_corpus_reader() end = time.time() print("Time elapsed = ", end - start, "seconds") start = time.time() self.update_result_view() end = time.time() print("Time elapsed = ", end - start, "seconds") self.update_search_tabs_with_tier_names() self.apply_filter() def update_corpus_reader(self): """ Updates the shown opened files view """ itemsCount = self.project.rowCount() progress = QtGui.QProgressDialog(self.tr("Loading Files..."), self.tr("Abort"), 0, itemsCount, self.parent()) progress.setWindowModality(QtCore.Qt.WindowModal) incompatible_files = [] for i in range(itemsCount): progress.setValue(i) poiofile = self.project.poioFileAt(i) if poiofile.is_new: # print poiofile.filepath try: self.corpus.add_item(poiofile.filepath, poiofile.type) except poioapi.data.UnknownFileFormatError: incompatible_files.append(poiofile.filepath) poiofile.is_new = False if progress.wasCanceled(): self.init_corpus() break progress.setValue(itemsCount) if len(incompatible_files) > 0: QtGui.QMessageBox.warning( self, "Cannot add files", "The following files could not be added to the project:" "<br/><br/><b>{0}</b><br/><br/>The file format was not " "recognized.<br/><b>The file will not be diplayed in the " "search result view</b>.".format(", ".join(incompatible_files)), ) def set_current_file_in_result_view(self, modelIndex): """ Sets what shows up in the result view ... Parameters ---------- modelIndex : """ e_id = "#file_{0}".format(modelIndex.row()) e = self.ui.webviewResult.page().mainFrame().findFirstElement(e_id) if not e.isNull(): e.evaluateJavaScript("this.scrollIntoView(true);") def update_result_view(self): """ Updates the view screen with the refreshed data and zoom settings """ settings = QtCore.QSettings() html = '<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head><body>\n' i = 0 for filepath, annotationgraph in self.corpus: html += '<h1 id="file_{1}">{0}</h1>\n'.format(os.path.basename(str(filepath)), i) html += annotationgraph.as_html_table(True, False) i += 1 html += "</body></html>" # self.ui.webviewResult.settings().setUserStyleSheetUrl( # QtCore.QUrl(":/css/css/resultview.css")) #.setStyleSheet("td { border: 1px solid black; }") # stylesheet_url = QtCore.QUrl.fromLocalFile("h:/ProjectsWin/git-github/Poio/ui/css/resultview.css") css = QtCore.QByteArray( "td { border: 1px solid black; }\ntable { margin-top: 10px; }\ntd.element_id { margin-left: 10px; margin-right: 10px; font-weight:bold; }\ntd.ann_type { margin-left: 10px; margin-right: 10px; font-variant:small-caps; }\nbody {}" ) if sys.version_info < (3, 0): zoom = str(settings.value("FontZoom").toInt()[0]) css = QtCore.QByteArray( "td { font-size: " + zoom + "%; border: 1px solid black; }\ntable { margin-top: 10px; }\ntd.element_id { margin-left: 10px; margin-right: 10px; font-weight:bold; }\ntd.ann_type { margin-left: 10px; margin-right: 10px; font-variant:small-caps; }\nbody { font-size: " + zoom + "%; }" ) else: zoom = str(settings.value("FontZoom")) self.ui.webviewResult.setTextSizeMultiplier(int(zoom) / 100) self.ui.webviewResult.settings().setUserStyleSheetUrl( QtCore.QUrl("data:text/css;charset=utf-8;base64," + str(css.toBase64())) ) self.ui.webviewResult.setHtml(html) def zoom_in(self): """ Increase the zoom setting by 10 % when the menu button is clicked until the 200% zoom limit is reached """ settings = QtCore.QSettings() if sys.version_info < (3, 0): currentzoom = settings.value("FontZoom").toInt()[0] else: currentzoom = settings.value("FontZoom") if currentzoom < 200: zoom = currentzoom + 10 settings.setValue("FontZoom", zoom) self.update_result_view() def zoom_out(self): """ Decreases the zoom setting by 10 % when the menu button is clicked until the 50% zoom limit is reached """ settings = QtCore.QSettings() if sys.version_info < (3, 0): currentzoom = settings.value("FontZoom").toInt()[0] else: currentzoom = settings.value("FontZoom") if currentzoom > 50: zoom = currentzoom - 10 settings.setValue("FontZoom", zoom) self.update_result_view() def reset_zoom(self): """ Resets the zoom setting to the default value of 100% """ settings = QtCore.QSettings() settings.setValue("FontZoom", 100) self.update_result_view() def printdoc(self): """ Prints the document in the view """ printer = QPrinter() setup = QPageSetupDialog(printer) setup.setWindowTitle("Print - Page Settings") if setup.exec_() == QDialog.Accepted: dialog = QPrintDialog(printer, self) dialog.setWindowTitle("Print Document") if dialog.exec_() == QDialog.Accepted: self.ui.webviewResult.print_(printer) def export_search_results(self): """ Exports the current annotationtree as HTML """ export_file = QtGui.QFileDialog.getSaveFileName( self, self.tr("Export Search Result"), "", self.tr("HTML file (*.html)") ) export_file = str(export_file) OUT = codecs.open(export_file, "w", "utf-8") html = '<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head><body>\n' for filepath, annotation_graph in self.corpus: html += "<h1>{0}</h1>\n".format(os.path.basename(filepath)) html += annotation_graph.as_html_table(True, False) html += "</body></html>" OUT.write(html) OUT.close() # def findFromStart(self, exp): # self.ui.texteditInterlinear.setTextCursor(QtGui.QTextCursor(self.ui.texteditInterlinear.document())) # if not self.ui.texteditInterlinear.find(exp) and exp != "": # self.statusBar().showMessage(self.tr("No match found."), 2000) # def findNext(self): # found = self.ui.texteditInterlinear.find(self.ui.lineeditQuickSearch.text()) # if not found: # self.statusBar().showMessage(self.tr("Restarting search from beginning of document."), 2000) # found = self.findFromStart(self.ui.lineeditQuickSearch.text()) # return found def apply_filter(self): """ Check for the search options and update the resul view """ for _, annotation_graph in self.corpus: annotation_graph.init_filters() for i in range(0, self.ui.tabWidget.currentIndex() + 1): search_terms = dict() for tier in self.corpus.tier_names: inputfield = self.ui.tabWidget.findChild(QtGui.QLineEdit, "lineedit_{0}_{1}".format(tier, i + 1)) if inputfield: search_terms[tier] = str(inputfield.text()) # currentFilter.set_filter_for_type(ann_type, str(inputfield.text())) checkbox = self.ui.tabWidget.findChild(QtGui.QCheckBox, "checkboxInvert_%i" % (i + 1)) is_inverted = checkbox.isChecked() checkbox = self.ui.tabWidget.findChild(QtGui.QCheckBox, "checkboxContained_%i" % (i + 1)) is_contained = checkbox.isChecked() radiobutton_or = self.ui.tabWidget.findChild(QtGui.QRadioButton, "radiobuttonOr_%i" % (i + 1)) boolean_op = poioapi.annotationtree.AnnotationTreeFilter.AND if radiobutton_or.isChecked(): boolean_op = poioapi.annotationtree.AnnotationTreeFilter.OR for _, annotation_graph in self.corpus: # annotation_graph.init_filters() filter = annotation_graph.create_filter_for_dict(search_terms) filter.inverted = is_inverted filter.contained_matches = is_contained filter.booleab_operation = boolean_op annotation_graph.append_filter(filter) self.update_result_view() def search_tab_changed(self, index): """ Check if the search tab changed ... Parameters ---------- index : int """ if index == self.ui.tabWidget.count() - 1: self.add_search_tab() else: self.apply_filter() def _add_search_box_to_widget(self, widget, nr_of_tab, tier_name): label = QtGui.QLabel(widget) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(label.sizePolicy().hasHeightForWidth()) label.setSizePolicy(sizePolicy) label.setSizeIncrement(QtCore.QSize(1, 0)) label.setText( QtGui.QApplication.translate( "TabWidgetSearch", "{0}:".format(tier_name), None, QtGui.QApplication.UnicodeUTF8 ) ) label.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) widget.ui.layoutLabels.addWidget(label) lineedit = QtGui.QLineEdit(self.ui.tabWidget) lineedit.setSizeIncrement(QtCore.QSize(2, 0)) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) lineedit.setSizePolicy(sizePolicy) lineedit.setObjectName("lineedit_{0}_{1}".format(tier_name, nr_of_tab)) widget.ui.layoutLineedits.addWidget(lineedit) lineedit.returnPressed.connect(self.apply_filter) def add_search_tab(self): """ Add a search tab """ nr_of_new_tab = self.ui.tabWidget.count() widget_search = QtGui.QWidget() widget_search.ui = Ui_TabWidgetSearch() widget_search.ui.setupUi(widget_search) widget_search.setObjectName("%s_%i" % (widget_search.objectName(), nr_of_new_tab)) for i, tier in enumerate(self.corpus.tier_names): self._add_search_box_to_widget(widget_search, nr_of_new_tab, tier) # update names of checkboxes and radiobuttons in search for child_widget in widget_search.findChildren(QtGui.QWidget): child_name = child_widget.objectName() if child_name.startswith("checkbox") or child_name.startswith("radiobutton"): child_widget.setObjectName("%s_%i" % (child_name, nr_of_new_tab)) self.ui.tabWidget.insertTab(nr_of_new_tab - 1, widget_search, "Search %i" % nr_of_new_tab) self.ui.tabWidget.setCurrentIndex(nr_of_new_tab - 1) def update_search_tabs_with_tier_names(self): for i in range(0, self.ui.tabWidget.count() - 1): widget = self.ui.tabWidget.widget(i) # remove search boxes for non-existent tiers existing_searches = [] for child_widget in widget.findChildren(QtGui.QWidget): if child_widget.objectName().startswith("lineedit_"): _, suffix = child_widget.objectName().split("_", 1) tier_name, _ = suffix.rsplit("_", 1) print(tier_name) if tier_name not in self.corpus.tier_names: child_widget.hide() child_widget.deleteLater() del child_widget existing_searches.append(suffix) # add new tiers for tier in self.corpus.tier_names: if tier not in existing_searches: self._add_search_box_to_widget(widget, i + 1, tier) def update_search_tab_widget_names(self): """ Updates the search tab """ for i in range(0, self.ui.tabWidget.count() - 1): widget = self.ui.tabWidget.widget(i) for childWidget in widget.findChildren(QtGui.QWidget): childWidget.setObjectName("%s_%i" % (childWidget.objectName()[:-2], i + 1)) self.ui.tabWidget.setTabText(i, "Search %i" % (i + 1)) def search_tab_closed(self): """ Closes the search tab """ # always leave at least one Search tab open if self.ui.tabWidget.indexOf(self.ui.tabNewSearch) < 2: return currentIndex = self.ui.tabWidget.currentIndex() if currentIndex < 1: return widgetSearch = self.ui.tabWidget.currentWidget() self.ui.tabWidget.setCurrentIndex(currentIndex - 1) self.ui.tabWidget.removeTab(currentIndex) widgetSearch.close() widgetSearch.deleteLater() del widgetSearch self.update_search_tab_widget_names() def search_tab_cleared(self): """ Clears the search tab """ widget = self.ui.tabWidget.currentWidget() for childWidget in widget.findChildren(QtGui.QWidget): if childWidget.objectName().startswith("lineedit_"): childWidget.setText("") self.apply_filter() def about_dialog(self): """ Popup a message box containing the "About Information" of Poio Analyser """ QtGui.QMessageBox.about( self, "About Poio Analyzer", """<b>Poio Analyzer v0.3.0</b> <br/><br/> Poio Analyzer is an analysis tool for linguists to analyze interlinear data. It is developed by Peter Bouda at the <b><a href=\"http://www.cidles.eu\"> Interdisciplinary Centre for Social and Language Documentation</a> </b> <br/><br/> Please send bug reports and comments to <b><a href=\"mailto:[email protected]\"> [email protected]</a></b>. <br/><br/> For more information visit the website of the project: <br/> <b><a href="http://media.cidles.eu/poio/"> http://media.cidles.eu/poio/ </a></b> <br/><br/> All rights reserved. See LICENSE file for details. """, )
class PoioGRAID(QtGui.QMainWindow): """The main window of the PoioGRAID application.""" def __init__(self, *args): """ The consctructor of the main application object, i.e. the main window. Calls a lot of other init methods. """ QtGui.QMainWindow.__init__(self, *args) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.init_vars() self.init_connects() self.init_settings() self.init = 0 self.project = PoioProject(os.getcwd()) self.ui.listFiles.setModel(self.project) self.ui.projectManager.setShown(False) self.ui.projectBtn.setStyleSheet(""" QPushButton#projectBtn { border-style: outset; border-width: 1px; border-color: black; padding: 1px; } QPushButton#projectBtn:checked { background-color: lightblue; border-style: inset; border-width: 2px; }""") self.ui.textedit.append_title( self.tr("Please create or open a file...")) self._dialog_find_and_replace = FindReplaceDialog(self) self._dialog_find_and_replace.setModal(False) self._dialog_find_and_replace.set_text_edit(self.ui.textedit) self._dialog_find = FindDialog(self) self._dialog_find.setModal(False) self._dialog_find.set_text_edit(self.ui.textedit) def init_vars(self): """ Initializes several attributes of the application, for example creates an empty annotation tree and a data structure type. """ self.reset_data_structure_type(poioapi.data.GRAID) self.filepath = None self.projectfilepath = None self.title = '' def init_settings(self): """ Load application settings from QSettings object. """ QtCore.QCoreApplication.setOrganizationName( "Interdisciplinary Centre for Social and Language Documentation"); QtCore.QCoreApplication.setOrganizationDomain("cidles.eu"); QtCore.QCoreApplication.setApplicationName("PoioGRAID"); #settings = QtCore.QSettings() def init_connects(self): """ Initializes all signal/slots connections of the application. """ # Files self.ui.actionOpenFile.triggered.connect(self.open_file) self.ui.actionSaveFile.triggered.connect(self.save_file) self.ui.actionSaveFileAs.triggered.connect(self.save_file_as) self.ui.actionOpen_Project.triggered.connect(self.open_project) self.ui.actionSave_Project.triggered.connect(self.save_project) self.ui.actionSave_Project_as.triggered.connect(self.save_project_as) self.ui.actionNewFile.triggered.connect(self.new_file) # Application stuff self.ui.actionQuit.triggered.connect(self.close) self.ui.actionAboutPoioGRAID.triggered.connect(self.about_dialog) # insert and delete tables and columns self.ui.actionInsertUtteranceAfter.triggered.connect( self.insert_utterance_after) self.ui.actionInsertUtteranceBefore.triggered.connect( self.insert_utterance_before) self.ui.actionDeleteUtterance.triggered.connect( self.delete_utterance) self.ui.actionInsertColumnBefore.triggered.connect( self.insert_column_before) self.ui.actionInsertColumnAfter.triggered.connect( self.insert_column_after) self.ui.actionDeleteColumn.triggered.connect(self.delete_column) # find and replace self.ui.actionFindAndReplace.triggered.connect( self.find_and_replace) self.ui.actionFind.triggered.connect(self.find) # Poio Project self.ui.listFiles.activated.connect(self.open_selected_file) self.connect(self.ui.projectBtn,SIGNAL("toggled()"),self.show_project) self.connect(self.ui.addfileBtn,SIGNAL("clicked()"),self.add_file) self.connect(self.ui.removefileBtn,SIGNAL("clicked()"),self.remove_file) self.connect(self.ui.saveprojectBtn,SIGNAL("clicked()"),self.save_project) self.connect(self.ui.openprojectBtn,SIGNAL("clicked()"),self.open_project) def about_dialog(self): """ Display the About dialog. """ about = QtGui.QMessageBox(self) about.setTextFormat(QtCore.Qt.RichText) about.setWindowTitle(self.tr("About PoioGRAID")) about.setText(self.tr("<b>PoioGRAID 0.2.1</b><br/>Poio GRAID Editor " "by the <a href=\"http://www.cidles.eu\">" "Interdisciplinary Centre for Social and " "Language Documentation</a>.<br/><br/>All " "rights reserved. See LICENSE file for details." "<br/><br/>For more information visit the " "website:<br/><a href=\"http://media.cidles.eu" "/poio/\">http://media.cidles.eu/poio/" "</a>")) about.exec_() def reset_data_structure_type(self, data_structure_type): self.annotation_tree = poioapi.annotationtree.AnnotationTree( data_structure_type) self.ui.textedit.structure_type_handler =\ self.annotation_tree.structure_type_handler def show_project(self): """ Show or hide the project manager. """ self.ui.projectManager.setShown(self.ui.projectBtn.isChecked()) def open_file(self): """ Prompt the user for a file, add it to the project and open it. """ filepaths = QtGui.QFileDialog.getOpenFileNames(self, self.tr("Open File"), "", self.tr("Pickle files (*.pickle);;All files (*.*)")) if len(filepaths) == 1: self.project.clear() self.project.addFilePaths(filepaths) self.open_file_at_path(filepaths[0]) def open_file_at_path(self, filepath): """ Load the data into the annotation tree and then update the text edit widget. ... Parameters ---------- filepath: str """ if filepath != '': self.annotation_tree.load_tree_from_pickle(filepath) self.ui.textedit.structure_type_handler =\ self.annotation_tree.structure_type_handler #file = open(filepath, "rb") #data = pickle.load(file) #if data[0] == 'poio_pickle_v2': # self.reset_data_structure_type(data[1]) # self.annotation_tree.tree = data[2] #else: # file.seek(0) # self.reset_data_structure_type(poioapi.data.GRAID) # self.annotation_tree.tree = pickle.load(file) #file.close() self.update_textedit() self.filepath = filepath def open_project(self): """ Prompt the user for a project, clear previous project and screen and open the project in the manager. """ path = QtGui.QFileDialog.getOpenFileNames( self, self.tr("Open Project"), "", self.tr("Poio project file (*.poioprj);;All files (*.*)")) if len(path) > 0: self.project.clear() self.annotation_tree.tree = [] self.update_textedit() self.projectfilepath = path[0] self.project.openproject(path[0]) def add_file(self): """ Add a file to the current project """ filepaths = QtGui.QFileDialog.getOpenFileNames(self, self.tr("Add Files"), "", self.tr("Pickle files (*.pickle);;All files (*.*)")) self.project.addFilePaths(filepaths) self.open_file_at_path(filepaths[0]) def remove_file(self): """ Remove the selected file from the current project """ countRemoved = 0 for i in self.ui.listFiles.selectedIndexes(): currentrow = i.row()-countRemoved project = self.project.poioFileAt(currentrow) self.project.removeFilePathAt(currentrow) if self.filepath == project.filepath: self.ui.textedit.clear() countRemoved += 1 def open_selected_file(self): """ Open the selected file in the manager to the TextEdit Screen """ selected = self.ui.listFiles.selectedIndexes() count = 0 for item in self.project.projectfiles: if item.filepath == 'tmp\\untitled.pickle': ret = self.project.saveuntitled() if ret == QMessageBox.Save: path = self.save_file_as() self.project.removeFilePathAt(count) self.project.addFilePath(path) elif ret == QMessageBox.Discard: self.project.removeFilePathAt(count) elif ret == QMessageBox.Cancel: return count +=1 if len(selected) == 1: project = self.project.poioFileAt(selected[0].row()) self.open_file_at_path(project.filepath) def update_textedit(self): """ Updates the text edit view with the data from the annotation tree. """ self.ui.textedit.blockSignals(True) self.ui.textedit.clear() self.ui.textedit.append_title(self.title) for element in self.annotation_tree.elements(): self.ui.textedit.append_element(element) self.ui.textedit.scrollToAnchor("title") self.ui.textedit.blockSignals(False) def delete_utterance(self): """ Delete one utterance from the text edit widget and from the annotation tree. """ deleted_id = self.ui.textedit.delete_current_element() if deleted_id: self.annotation_tree.remove_element(deleted_id) def insert_utterance_before(self): """ Insert an utteranance *before* the currently edited utterance in the text view. Then adds the utterance to the annotation tree. """ element = self.annotation_tree.empty_element() current_id = self.ui.textedit.insert_element(element) if current_id: self.annotation_tree.insert_element(element, current_id) def insert_utterance_after(self): """ Insert an utteranance *after* the currently edited utterance in the text view. Then adds the utterance to the annotation tree. """ element = self.annotation_tree.empty_element() current_id = self.ui.textedit.insert_element(element, True) if current_id: self.annotation_tree.insert_element(element, current_id, True) def delete_column(self): """ Deletes the column that is currently edited in the text view. Also remove all annotation and sub-elements belonging to this column. Then delete the elements from the annotation tree. """ self.ui.textedit.delete_column_at_cursor() def insert_column_before(self): """ Inserts an empty column at the current cursor position *before* the currently edited element. Then insert the element into the annotation tree. """ next_id = self.ui.textedit.insert_column_at_cursor( self.annotation_tree.next_annotation_id, False) self.annotation_tree.next_annotation_id = next_id def insert_column_after(self): """ Inserts an empty column at the current cursor position *after* the currently edited element. Then insert the element into the annotation tree. """ next_id = self.ui.textedit.insert_column_at_cursor( self.annotation_tree.next_annotation_id, True) self.annotation_tree.next_annotation_id = next_id def new_file(self): """ Create a new file from a given input text. The user has to enter the text in an input dialog. There are two types of input text: plain text or tb style text (with markup like ``\sl`` at the beginning of lines). """ dialog = QtGui.QDialog(self) ui = Ui_NewFileGraid() ui.setupUi(dialog) ret = dialog.exec_() if ret == QtGui.QDialog.Accepted: # get the data structure type that the user chose combo_data_structure_type = ui.comboDataStructureType.currentText() data_structure_type = poioapi.data.GRAID if combo_data_structure_type == "GRAID2 (Diana)": data_structure_type = poioapi.data.GRAIDDIANA self.reset_data_structure_type(data_structure_type) self.title = "" self.statusBar().showMessage(self.tr("Parsing text..."), 5) if ui.radioButtoTbStyleText.isChecked(): self._parse_tb_style_text( ui.textedit.document().toPlainText()) else: self._parse_plain_text( ui.textedit.document().toPlainText()) self.statusBar().showMessage(self.tr("Parsing done."), 5) self.update_textedit() self.project.addFilePath('tmp\\untitled.pickle') def save_file(self): """ Save the current data into a file. If no filename is specified yet then ask for the path and filename by opening a file dialog. """ if not self.filepath: self.save_file_as() else: tree = self.ui.textedit.annotation_tree_from_document() self.annotation_tree.tree = tree file = open(self.filepath, "wb") pickle.dump(['poio_pickle_v2', self.annotation_tree.data_structure_type, self.annotation_tree.tree], file) file.close() self.statusBar().showMessage(self.tr("File saved."), 5) def save_file_as(self): """ Open a file dialog and ask for path and filename for the file. Then call `PoioGRAID.save_file()`. """ filepath = QtGui.QFileDialog.getSaveFileName( self, self.tr("Save File As"), "", self.tr("Pickle file (*.pickle);;All files (*.*)")) if filepath != '': if not filepath.endswith(".pickle"): filepath += ".pickle" self.filepath = filepath self.save_file() return filepath else: return def save_project(self): """ Save the current project in the manager. Prompt for a path if the current project didn't exist yet """ if not self.projectfilepath: self.save_project_as() else: self.save_file() self.project.saveprojectas(self.projectfilepath) def save_project_as(self): """ Prompt for a path and save the current project to it """ self.save_file() savepath = "" savepath = QtGui.QFileDialog.getSaveFileName( self, self.tr("Save Project As"), "", self.tr("Poio project file (*.poioprj);;All files (*.*)")) if savepath !="": self.project.saveprojectas(savepath) self.projectfilepath = savepath def find_and_replace(self): """ Shows find and replace dialog """ self._dialog_find_and_replace.show() def find(self): """ Shows find dialog """ self._dialog_find.show() # Private functions ####################################################### def _parse_plain_text(self, text): """ Parses plain text data into an annotation tree. ... Parameters ---------- text : str """ lines = text.split("\n") progress = QtGui.QProgressDialog(self.tr("Parsing text..."), self.tr("Abort"), 0, len(lines), self.parent()) progress.setWindowModality(QtCore.Qt.WindowModal) for i, line in enumerate(lines): progress.setValue(i) line = line.strip() utterance = line clause_unit = re.sub("[.,;:]", "", line) words = clause_unit.split() il_elements = list() for w in words: if self.annotation_tree.data_structure_type == \ poioapi.data.GRAID: il_elements.append([ { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : w }, { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : '' }, { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : '' }]) elif self.annotation_tree.data_structure_type ==\ poioapi.data.GRAIDDIANA: il_elements.append([ { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : w }, # morphemes [ [ { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : '' }, { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : '' } ] ], # graid1, graid 3 { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : '' }, { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : '' }]) elements = [ [ { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : clause_unit }, il_elements, { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : '' }] ] utterance = [ { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : utterance }, elements, { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : '' }, { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : '' } ] if self.annotation_tree.data_structure_type ==\ poioapi.data.GRAIDDIANA: utterance.append( { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : '' }) self.annotation_tree.append_element(utterance) if (progress.wasCanceled()): initCorpusReader() break progress.setValue(len(lines)) def _parse_tb_style_text(self, text): """ Parses tb style data into an annotation tree. In tb style data lines start with markup like ``\sl``. ... Parameters ---------- text : str """ block = list() lines = text.split("\n") line = lines.pop(0) title = [] while not line.startswith("\\"): if line: title.append(line) line = lines.pop(0) self.title = " ".join(title) for line in lines: if line and line.startswith("\\id") and len(block): utterance = self._parse_element_from_tb_style(block) self.annotation_tree.append_element(utterance) block = list() elif line: if line.startswith("\\"): block.append(line.strip()) utterance = self._parse_element_from_tb_style(block) self.annotation_tree.append_element(utterance) #print self.annotation_tree.tree def _parse_element_from_tb_style(self, block): """ Helper function for `PoioGRAID._parse_tb_style_text()`. Parse one paragraph of tb style data. ... Parameters ---------- block : list """ element_tb = dict() utterance = "" translation = "" comment = "" for line in block: line = re.sub(" +", " ", line) line = line.strip() line_elements = line.split(None, 1) if len(line_elements) < 2: type = line text = "" else: type = line_elements[0] text = line_elements[1] if type.startswith("\\"): if type[1:] == "sl": #text = re.sub("\(\d+\) ?", "", text) utterance = text utterance = re.sub("\d?# ?", "", utterance) utterance = re.sub(" +", " ", utterance) utterance = utterance.strip() if type[1:] == "ft": translation = text elif type[1:] == "com": comment = text else: #text = re.sub("^x ", "", text) element_tb[type[1:]] = list() last_start = 0 for m in re.finditer("(?:\d?#|$)", text): element_tb[type[1:]].append( text[last_start:m.start(0)]) last_start = m.end(0) elements = [] for i, phrase in enumerate(element_tb['sl']): words = phrase.split() wfw = [] try: wfw = element_tb['wfw'][i].split() except IndexError: pass except KeyError: pass graid1 = [] try: graid1 = element_tb['gr_1'][i].split() except IndexError: pass except KeyError: pass graid2 = '' try: graid2 = element_tb['gr_2'][i] except IndexError: pass except KeyError: pass il_elements = [] for i in range(max(len(words), len(wfw), len(graid1))): e1 = '' e2 = '' e3 = '' if i < len(words): e1 = words[i] if i < len(wfw): e2 = wfw[i] if i < len(graid1): e3 = graid1[i] il_elements.append([ { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : e1 }, { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : e2 }, { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : e3 }]) elements.append([ { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : phrase }, il_elements, { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : graid2 }]) return [ { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : utterance }, elements, { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : translation }, { 'id' : self.annotation_tree.next_annotation_id, 'annotation' : comment } ]
class PoioAnalyzer(QtGui.QMainWindow): """The main window of the PoioAnalyzer application.""" def __init__(self, *args): QtGui.QMainWindow.__init__(self, *args) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.texteditInterlinear.setReadOnly(True) self.initConnects() self.initSettings() self.project = PoioProject(os.getcwd()) self.ui.listFiles.setModel(self.project) self.initCorpusReader() self.currentFilter = AnnotationTreeFilter() def initCorpusReader(self): self.corpusreader = GlossCorpusReader( utterancetierTypes=self.arrUtteranceTierTypes, wordtierTypes=self.arrWordTierTypes, translationtierTypes=self.arrTranslationTierTypes, morphemetierTypes=self.arrMorphemeTierTypes, glosstierTypes=self.arrGlossTierTypes, ) def updateCorpusReaderFilter(self): self.currentFilter.resetMatchObject() for [filepath, annotationtree] in self.corpusreader.annotationtrees: new_filter = copy.deepcopy(self.currentFilter) annotationtree.updateLastFilter(new_filter) def updateCorpusReader(self): itemsCount = self.project.rowCount() progress = QtGui.QProgressDialog(self.tr("Loading Files..."), self.tr("Abort"), 0, itemsCount, self.parent()) progress.setWindowModality(QtCore.Qt.WindowModal) for i in range(itemsCount): progress.setValue(i) poiofile = self.project.poioFileAt(i) if poiofile.isNew: self.corpusreader.addFile(poiofile.filepath, poiofile.type) poiofile.setIsNew(False) if progress.wasCanceled(): initCorpusReader() break progress.setValue(itemsCount) self.updateCorpusReaderFilter() def initConnects(self): QtCore.QObject.connect(self.ui.buttonAddFiles, QtCore.SIGNAL("pressed()"), self.addFiles) QtCore.QObject.connect(self.ui.buttonRemoveFiles, QtCore.SIGNAL("pressed()"), self.removeFiles) # Filter and Search QtCore.QObject.connect(self.ui.buttonSearch, QtCore.SIGNAL("pressed()"), self.applyFilter) QtCore.QObject.connect(self.ui.lineeditSearchUtterances, QtCore.SIGNAL("returnPressed()"), self.applyFilter) QtCore.QObject.connect(self.ui.lineeditSearchWords, QtCore.SIGNAL("returnPressed()"), self.applyFilter) QtCore.QObject.connect(self.ui.lineeditSearchMorphemes, QtCore.SIGNAL("returnPressed()"), self.applyFilter) QtCore.QObject.connect(self.ui.lineeditSearchGlosses, QtCore.SIGNAL("returnPressed()"), self.applyFilter) QtCore.QObject.connect(self.ui.lineeditSearchTranslations, QtCore.SIGNAL("returnPressed()"), self.applyFilter) # Quick Search QtCore.QObject.connect( self.ui.actionQuickSearch, QtCore.SIGNAL("triggered()"), self.ui.lineeditQuickSearch.setFocus ) QtCore.QObject.connect( self.ui.lineeditQuickSearch, QtCore.SIGNAL("textChanged(const QString &)"), self.findFromStart ) QtCore.QObject.connect(self.ui.lineeditQuickSearch, QtCore.SIGNAL("returnPressed()"), self.findNext) def initSettings(self): QtCore.QCoreApplication.setOrganizationName("Interdisciplinary Centre for Social and Language Documentation") QtCore.QCoreApplication.setOrganizationDomain("cidles.eu") QtCore.QCoreApplication.setApplicationName("PoioAnalyzer") settings = QtCore.QSettings() self.strMorphemeSeperator = unicode(settings.value("Ann/MorphSep", QtCore.QVariant("-")).toString()) self.strGlossSepereator = unicode(settings.value("Ann/GlossSep", QtCore.QVariant(":")).toString()) self.strEmptyCharacter = unicode(settings.value("Ann/EmptyChar", QtCore.QVariant("#")).toString()) self.arrUtteranceTierTypes = unicode( settings.value( "Ann/UttTierTypeRefs", QtCore.QVariant(u"utterance|utterances|Äußerung|Äußerungen") ).toString() ).split("|") self.arrWordTierTypes = unicode( settings.value("Ann/WordTierTypeRefs", QtCore.QVariant(u"words|word|Wort|Worte|Wörter")).toString() ).split("|") self.arrMorphemeTierTypes = unicode( settings.value("Ann/MorphTierTypeRefs", QtCore.QVariant(u"morpheme|morphemes|Morphem|Morpheme")).toString() ).split("|") self.arrGlossTierTypes = unicode( settings.value("Ann/GlossTierTypeRefs", QtCore.QVariant(u"glosses|gloss|Glossen|Gloss|Glosse")).toString() ).split("|") self.arrTranslationTierTypes = unicode( settings.value( "Ann/TransTierTypeRefs", QtCore.QVariant(u"translation|translations|Übersetzung|Übersetzungen") ).toString() ).split("|") def removeFiles(self): pass def addFiles(self): filepaths = QtGui.QFileDialog.getOpenFileNames( self, self.tr("Add Files"), "", self.tr("Elan files (*.eaf);;Toolbox files (*.txt);;All files (*.*)") ) self.project.addFilePaths(filepaths) start = time.time() self.updateCorpusReader() end = time.time() print "Time elapsed = ", end - start, "seconds" start = time.time() self.updateIlTextEdit() end = time.time() print "Time elapsed = ", end - start, "seconds" def updateIlTextEdit(self): self.ui.texteditInterlinear.clear() self.ui.texteditInterlinear.setReadOnly(True) itemsCount = self.project.rowCount() for [filepath, annotationtree] in self.corpusreader.annotationtrees: self.ui.texteditInterlinear.appendTitle(os.path.basename(filepath)) utterancesIds = annotationtree.getFilteredUtteranceIds() filter = annotationtree.lastFilter() for id in utterancesIds: utterance = annotationtree.getUtteranceById(id) if id in filter.matchobject["utterance"]: offset = 0 for g in filter.matchobject["utterance"][id]: utterance = ( utterance[: g[0] + offset] + '<span style="color:green;">' + utterance[g[0] + offset :] ) offset = offset + len('<span style="color:green;">') utterance = utterance[: g[1] + offset] + "</span>" + utterance[g[1] + offset :] offset = offset + len("</span>") translations = annotationtree.getTranslationsForUtterance(id) if len(translations) == 0: translationId = annotationtree.newTranslationForUtteranceId(id, "") translations = [[translationId, self.strEmptyCharacter]] else: new_translations = [] for t in translations: if t[1] == "": new_t = self.strEmptyCharacter new_translations.append = [t[0], new_t] if t[0] in filter.matchobject["translation"]: offset = 0 new_t = t[1] for g in filter.matchobject["translation"][t[0]]: new_t = new_t[: g[0] + offset] + '<span style="color:green;">' + new_t[g[0] + offset :] offset = offset + len('<span style="color:green;">') new_t = new_t[: g[1] + offset] + "</span>" + new_t[g[1] + offset :] offset = offset + len("</span>") new_translations.append([t[0], new_t]) else: new_translations.append([t[0], t[1]]) translations = new_translations wordIds = annotationtree.getWordIdsForUtterance(id) ilElements = [] for wid in wordIds: strWord = annotationtree.getWordById(wid) if strWord == "": strWord = self.strEmptyCharacter strMorphemes = annotationtree.getMorphemeStringForWord(wid) if strMorphemes == "": strMorphemes = strWord strGlosses = annotationtree.getGlossStringForWord(wid) if strGlosses == "": strGlosses = self.strEmptyCharacter markWord = False if wid in filter.matchobject["word"]: markWord = True ilElements.append([wid, strWord, strMorphemes, strGlosses, markWord]) self.ui.texteditInterlinear.appendUtterance(id, utterance, ilElements, translations) self.ui.texteditInterlinear.scrollToAnchor("#") def findFromStart(self, exp): self.ui.texteditInterlinear.setTextCursor(QtGui.QTextCursor(self.ui.texteditInterlinear.document())) if not self.ui.texteditInterlinear.find(exp) and exp != "": self.statusBar().showMessage(self.tr("No match found."), 2000) def findNext(self): found = self.ui.texteditInterlinear.find(self.ui.lineeditQuickSearch.text()) if not found: self.statusBar().showMessage(self.tr("Restarting search from beginning of document."), 2000) found = self.findFromStart(self.ui.lineeditQuickSearch.text()) return found def applyFilter(self): self.currentFilter.setUtteranceFilter(unicode(self.ui.lineeditSearchUtterances.text())) self.currentFilter.setTranslationFilter(unicode(self.ui.lineeditSearchTranslations.text())) self.currentFilter.setWordFilter(unicode(self.ui.lineeditSearchWords.text())) self.currentFilter.setMorphemeFilter(unicode(self.ui.lineeditSearchMorphemes.text())) self.currentFilter.setGlossFilter(unicode(self.ui.lineeditSearchGlosses.text())) self.currentFilter.setInvertedFilter(self.ui.checkboxInvert.isChecked()) self.currentFilter.setContainedMatches(self.ui.checkboxContained.isChecked()) if self.ui.radiobuttonAnd.isChecked(): self.currentFilter.setBooleanOperation(AnnotationTreeFilter.AND) elif self.ui.radiobuttonOr.isChecked(): self.currentFilter.setBooleanOperation(AnnotationTreeFilter.OR) self.updateCorpusReaderFilter() self.updateIlTextEdit()