Example #1
0
    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()
Example #2
0
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.
                """,
        )