Beispiel #1
0
    def openProject(self, pathToProject):
        try:
            project = Project()
            project.open(pathToProject)

        except ProjectOpenError:
             logging.error("Can't open project " + pathToProject)
             raise Exception("Can't open project " + pathToProject)

        self._currentProject = project
Beispiel #2
0
class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.languages = sorted([
            "AVS", "Batch", "CoffeeScript", ("CSharp", "C#"), "Java", "CSS",
            "Diff", "JSON", "Makefile", "Matlab", "Pascal",
            "Python", "Spice", "TCL", "Verilog", "YAML", "Bash", "CMake",
            ("CPP", "C++"), "IDL", "JavaScript", "D", "Fortran", "Markdown",
            "Octave", "Perl", "PostScript", "Ruby", "SQL", "TeX", "VHDL"
        ],
                                key=lambda x: x[1]
                                if isinstance(x, tuple) else x)
        self.settings = Settings(self.languages)
        self.statusbar_lang = QLabel(self.settings.settings["lang"])
        self.project = None
        self.observer = None

        self.setupUi(self)
        self.initUI()

    def initUI(self):
        """
        Method sets UI
        :return: None
        """
        self.setWindowTitle("CodeDB")
        self.setCentralWidget(self.centralwidget)
        self.centralwidget.setLayout(self.main_layout)
        self.new_file_trigger()
        self.tabs.setTabsClosable(True)
        for language in self.languages:
            if isinstance(language, tuple):
                action = QAction(language[1], self)
                action.triggered.connect(
                    self.generate_lang_action_trigger(language))
            else:
                action = QAction(language, self)
                action.triggered.connect(
                    self.generate_lang_action_trigger(language))
            self.menuSwitch_language.addAction(action)

        self.project = Project(self.dirs_widget, self.files_widget)

        self.dirs_widget.setSizePolicy(QSizePolicy.Fixed,
                                       QSizePolicy.Expanding)
        self.files_widget.setSizePolicy(QSizePolicy.Fixed,
                                        QSizePolicy.Expanding)
        self.tabs.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))

        self.tables.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed))
        self.table_columns.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed))

        self.project.hide()
        self.db_layout_hide()

        self.statusbar.addPermanentWidget(self.statusbar_lang)
        self.settings_action.triggered.connect(self.settings_open)

        self.tabs.tabCloseRequested.connect(self.close_tab)
        self.tabs.currentChanged.connect(self.changed_tab)

        self.project_close.triggered.connect(self.project_close_trigger)
        self.project_open.triggered.connect(self.project_open_trigger)

        self.project_hide.triggered.connect(self.project.hide)
        self.project_show.triggered.connect(self.project.show)

        self.save_file.triggered.connect(self.save_file_trigger)
        self.save_as_file.triggered.connect(self.save_as_file_trigger)
        self.new_file.triggered.connect(self.new_file_trigger)
        self.open_file.triggered.connect(self.open_file_dialog_trigger)

        self.tables.setHeaderLabel("Tables")
        self.tables.currentItemChanged.connect(self.table_changed)

        self.table_columns.setColumnCount(5)
        self.table_columns.setHorizontalHeaderLabels(
            ["Column", "Type", "Not Null", "Default Value", "Primary Key"])

        self.files_widget.itemClicked.connect(self.open_file_project_trigger)
        self.dirs_widget.doubleClicked.connect(self.change_project_dir)

        self.new_db.triggered.connect(self.new_db_trigger)

        self.db_create.triggered.connect(self.create_table)
        self.db_remove.triggered.connect(self.remove_table)
        self.db_rename.triggered.connect(self.rename_table)
        self.db_add_row.triggered.connect(self.add_row)
        self.db_del_row.triggered.connect(self.remove_row)

        self.open_project(self.settings.settings["project_path"])

    def generate_lang_action_trigger(self, name):
        """
        Generating Method for QActions in Switch Menu for languages
        :param name: Name of language(str or tuple)
        :return: trigger Method for QAction
        """
        def trigger():
            nonlocal name, self
            widget = self.tabs.currentWidget()
            if isinstance(widget, TextEditorWidget):
                if isinstance(name, tuple):
                    widget.change_language(name)
                    self.statusbar_lang.setText(name[1])
                elif isinstance(name, str):
                    widget.change_language(name)
                    self.statusbar_lang.setText(name)

        return trigger

    def close_tab(self, index):
        """
        Method for handling closing tab event
        :param index: index of tab that will be closed
        :return: None
        """
        widget = self.tabs.widget(index)
        if isinstance(widget, TableEditorWidget):
            widget.close_con()
            self.table_columns.setRowCount(0)
        if not isinstance(self.tabs.widget(index - 1), TableEditorWidget):
            self.db_layout_hide()
        self.tabs.removeTab(index)

    def changed_tab(self, index):
        """
        Method for handling changing tab event
        :param index: index of new tab
        :return: None
        """
        if isinstance(self.tabs.widget(index), TextEditorWidget):
            self.statusbar_lang.setText(self.tabs.widget(index).lang)
            self.db_layout_hide()
        elif isinstance(self.tabs.widget(index), TableEditorWidget):
            self.show_db_others()
            self.render_table_structure(self.tabs.widget(index))

    def save_file_trigger(self):
        """
        Save Method for QAction of File Menu
        :return: None
        """
        widget = self.tabs.currentWidget()
        index = self.tabs.currentIndex()
        if isinstance(widget, TextEditorWidget):
            if widget.name is None:  # Save As if None
                name = self.save_as_file_trigger()
                if not name == "":
                    widget.name = name
                    self.tabs.setTabText(index, os.path.basename(widget.name))
            else:  # Save
                with open(widget.name, "w") as w:
                    w.write(widget.editor().text())
        elif isinstance(widget, TableEditorWidget):
            if widget.path is not None:
                widget.apply_changes()
            else:
                name = self.save_as_file_trigger()
                if not name == "":
                    widget.path = name
                    self.tabs.setTabText(index, os.path.basename(name))

    def save_as_file_trigger(self):
        """
        Open "Save as" dialog
        :return: filename from dialog
        """
        widget = self.tabs.currentWidget()
        name = QFileDialog.getSaveFileName(self, "Save as", '')[0]
        try:
            if isinstance(widget, TextEditorWidget):
                with open(name, "w") as w:
                    w.writelines(widget.editor().text().split("\n"))
            elif isinstance(widget, TableEditorWidget):
                with open(name, "w"):
                    pass
                con = sqlite3.connect(name)
                if widget.con is not None:
                    widget.con.commit()
                    for query in widget.con.iterdump():
                        try:
                            con.execute(query)
                        except sqlite3.OperationalError:
                            self.statusbar.showMessage("Some technical sweets",
                                                       20000)
                    con.commit()
                    widget.con = con
        except FileNotFoundError:
            pass
        return name

    def new_file_trigger(self):
        """
        Creating new tab
        :return: None
        """
        self.tabs.addTab(TextEditorWidget(self.settings), "New File")

    def new_db_trigger(self):
        """
        Createing new datebase with table "new_table"
        :return: None
        """
        editor = TableEditorWidget(self.render_active_table)
        editor.con = sqlite3.connect(":memory:")
        with contextlib.closing(editor.con.cursor()) as cur:
            cur.execute("""
                CREATE TABLE new_table (
                    `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
                    `name` varchar(255) NOT NULL
                );
            """)
            self.render_tables_names(editor)
            if isinstance(self.tabs.currentWidget(), QTableWidgetItem):
                self.show_db_others()
        self.tabs.addTab(editor, "New db")

    def file_open(self, name):
        """
        Open file with param name
        :param name: name of the file
        :return: None
        """
        try:
            if name.endswith(".db"):
                editor = TableEditorWidget(self.render_active_table)
                editor.con = sqlite3.connect(name)
                editor.path = name
                self.render_tables_names(editor)
                if isinstance(self.tabs.currentWidget(), QTableWidgetItem):
                    self.show_db_others()
                self.tabs.addTab(editor, os.path.basename(name))
            else:
                with open(name, "r") as r:
                    editor = TextEditorWidget(self.settings)
                    editor.editor().setText(r.read())
                    self.tabs.addTab(editor, os.path.basename(name))
                    editor.name = name
        except FileNotFoundError:
            self.statusbar.showMessage("File not found", 10000)
        except UnicodeDecodeError:
            try:
                with open(name, "rb") as r:
                    editor = TextEditorWidget(self.settings)
                    editor.editor().setText(r.read().decode("utf-8"))

            except UnicodeDecodeError:
                self.statusbar.showMessage("Can't read binary file", 10000)
            else:
                self.tabs.addTab(editor, os.path.basename(name))
        except PermissionError:
            self.statusbar.showMessage(
                f"Can't open {os.path.basename(name)}: Permission denied",
                10000)

    def open_file_dialog_trigger(self):
        """
        Open file Method for QAction in File Menu
        :return: None
        """
        self.file_open(QFileDialog.getOpenFileName(self, "Open", '')[0])

    def open_file_project_trigger(self, item):
        """
        Open file Method for file tree in project
        :return: None
        """
        if item is not None and self.settings.get_setting(
                "project_path") is not None:
            self.file_open(
                os.path.join(self.settings.get_setting("project_path"),
                             item.text(0)))

    def change_project_dir(self, index):
        """
        Method for changing project directory from directories' tree widget
        :param index:
        :return: None
        """
        if index is not None and self.settings.get_setting(
                "project_path") is not None:
            self.open_project(
                os.path.join(self.settings.get_setting("project_path"),
                             self.dirs_widget.itemFromIndex(index).text(0)))

    def settings_open(self):
        """
        Method opens settings' dialog
        :return: None
        """
        self.settings.exec_()

    def project_open_trigger(self):
        """
        Open project from directory dialog
        :return: None
        """
        self.open_project(
            QFileDialog.getExistingDirectory(self, "Open project", ''))

    def open_project(self, path):
        """
        Method for opening project
        :param path: full path to project
        :return: None
        """
        if path != "":
            self.settings.set_setting("project_path", path)
            if isinstance(self.observer, Observer):
                self.observer.stop()
            if self.settings.get_setting("project_path") is not None:
                self.project.open(self.settings.get_setting("project_path"))
                self.observer = Observer()
                self.observer.schedule(
                    Handler(self.project),
                    path=self.settings.get_setting("project_path"),
                    recursive=True)
                self.observer.start()

    def project_close_trigger(self):
        """
        Method for closing project
        :return: None
        """
        self.project.hide()
        self.settings.set_setting("project_path", None)
        self.project.clear()
        if self.observer is not None:
            self.observer.stop()

    def db_layout_hide(self):
        """
        Method hides elements from self.db_layout
        :return: None
        """
        self.table_columns.hide()
        self.tables.hide()

    def db_layout_show(self):
        """
        Method shows elements from self.db_layout
        :return: None
        """
        self.table_columns.show()
        self.tables.show()

    def table_changed(self, item: QTreeWidgetItem):
        """
        Method shows data from selected table
        :param item: selected item from QTreeWidget
        :return: None
        """
        widget = self.tabs.currentWidget()
        if isinstance(widget, TableEditorWidget):
            with contextlib.closing(widget.con.cursor()) as cur:
                if self.tables.indexFromItem(item).data() is not None:
                    widget._active_table = self.tables.indexFromItem(
                        item).data()
                    widget._columns[widget.active_table()] = list()
                    if widget.active_table() not in widget.structure:
                        widget.structure[widget.active_table()] =\
                            list(map(lambda x: (x[1], x[2], bool(x[3]), x[4] if x[4] is not None else "NULL",
                                                bool(x[5])), cur.execute(f"PRAGMA table_info({widget.active_table()})")
                                     .fetchall()))
                    self.render_table_structure(widget)
                    try:
                        res = cur.execute(
                            f"SELECT * FROM {widget._active_table}").fetchall(
                            )
                        with widget as table:
                            columns = list(map(lambda x: x[0],
                                               cur.description))
                            table.setColumnCount(len(columns))
                            table.setHorizontalHeaderLabels(columns)
                            widget._columns[
                                widget.active_table()] = columns.copy()
                            table.setRowCount(len(res))
                            for i, row in enumerate(res):
                                for j, cell in enumerate(row):
                                    table.setItem(i, j,
                                                  QTableWidgetItem(str(cell)))
                    except sqlite3.Error:
                        pass

    def show_table_list(self):
        """
        Method sets table_list to tables from current tab
        :return: None
        """
        widget = self.tabs.currentWidget()
        if isinstance(widget, TableEditorWidget):
            self.tables.clear()
            if widget.table_names is not None:
                self.tables.addTopLevelItems(
                    map(lambda x: QTreeWidgetItem([x]), widget.table_names))

    def show_db_others(self):
        """
        Method shows special db preferences
        :return: None
        """
        self.db_layout_show()
        self.show_table_list()
        self.statusbar_lang.setText("DateBase")

    def render_tables_names(self, editor: TableEditorWidget):
        """
        Method updates list of table from date base
        :param editor: TableEditorWidget where from will be taken tables' names
        :return: None
        """
        if editor.con is not None:
            with contextlib.closing(editor.con.cursor()) as cur:
                editor.table_names = list(
                    map(
                        lambda x: x[0],
                        cur.execute(
                            "SELECT name FROM 'sqlite_master' WHERE type = 'table'"
                        ).fetchall()))
                if "sqlite_sequence" in editor.table_names:
                    editor.table_names.remove("sqlite_sequence")
                if self.tabs.currentWidget(
                ) == editor or self.tabs.currentWidget() is None:
                    self.tables.clear()
                    self.tables.addTopLevelItems(
                        map(lambda x: QTreeWidgetItem([str(x)]),
                            editor.table_names))

    def create_table(self):
        """
        Method creates table from dialog's query
        :return: None
        """
        widget = self.tabs.currentWidget()
        if isinstance(widget, TableEditorWidget) and isinstance(
                widget.con, sqlite3.Connection):
            with contextlib.closing(widget.con.cursor()) as cur:
                text, ok = QInputDialog.getMultiLineText(
                    self, "Creating table", "Enter query", """
                CREATE TABLE new_table (
                    `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
                    `name` varchar(255) NOT NULL
                );
                """.strip().replace("                ", ""))
                if ok and text:
                    try:
                        cur.execute(text)
                    except sqlite3.OperationalError as e:
                        self.statusbar.showMessage(str(e), 15000)
                    self.render_tables_names(widget)

    def remove_table(self):
        """
        Method removes selected table
        :return: None
        """
        widget = self.tabs.currentWidget()
        if isinstance(widget, TableEditorWidget) and isinstance(widget.con, sqlite3.Connection)\
                and len(self.tables.selectedIndexes()) > 0:
            with contextlib.closing(widget.con.cursor()) as cur:
                try:
                    cur.execute(
                        f"DROP TABLE {self.tables.itemFromIndex(self.tables.selectedIndexes()[0]).text(0)}"
                    )
                except sqlite3.OperationalError as e:
                    self.statusbar.showMessage(str(e))
                self.render_tables_names(widget)

    def rename_table(self):
        """
        Method renames selected table from dialog
        :return: None
        """
        widget = self.tabs.currentWidget()
        if isinstance(widget, TableEditorWidget) and isinstance(widget.con, sqlite3.Connection) \
                and len(self.tables.selectedIndexes()) > 0:
            name = self.tables.itemFromIndex(
                self.tables.selectedIndexes()[0]).text(0)
            text, ok = QInputDialog.getText(self, "Rename table",
                                            "Table name:", QLineEdit.Normal,
                                            name)
            if ok and text:
                with contextlib.closing(widget.con.cursor()) as cur:
                    try:
                        cur.execute(f"ALTER TABLE {name} RENAME TO {text}")
                    except sqlite3.OperationalError as e:
                        self.statusbar.showMessage(str(e))
                    self.render_tables_names(widget)

    def add_row(self):
        """
        Method creates row by calling add_row() from current TableEditorWidget
        :return: None
        """
        widget = self.tabs.currentWidget()
        if isinstance(widget,
                      TableEditorWidget) and widget.active_table() is not None:
            widget.add_row()

    def remove_row(self):
        """
        Method removes selected rows by calling remove_row() from TableEditorWidget
        :return: None
        """
        widget = self.tabs.currentWidget()
        if isinstance(widget, TableEditorWidget):
            widget.remove_row()

    @staticmethod
    def render_active_table(widget):
        """
        Special method for updating table in TableEditorWidget
        :param widget: TableEditorWidget from that will be taken table name for query
        :return: None
        """
        if isinstance(widget, TableEditorWidget) and isinstance(
                widget.con, sqlite3.Connection):
            with contextlib.closing(widget.con.cursor()) as cur:
                try:
                    res = cur.execute(
                        f"SELECT * FROM {widget._active_table}").fetchall()
                    with widget as table:
                        table.setRowCount(len(res))
                        for i, row in enumerate(res):
                            for j, cell in enumerate(row):
                                table.setItem(i, j,
                                              QTableWidgetItem(str(cell)))
                except sqlite3.Error:
                    pass

    def render_table_structure(self, widget):
        """
        Method updates data in table_columns
        :param widget: TableEditorWidget from that will be taken structure
        :return: None
        """
        self.table_columns.setRowCount(0)
        if isinstance(widget, TableEditorWidget) and widget.active_table(
        ) in widget.structure:
            self.table_columns.setRowCount(
                len(widget.structure[widget.active_table()]))
            for i, row in enumerate(widget.structure[widget.active_table()]):
                for j, cell in enumerate(row):
                    self.table_columns.setItem(i, j,
                                               QTableWidgetItem(str(cell)))
            self.table_columns.resizeColumnsToContents()

    def show_error(self, text):
        self.statusbar.showMessage(str(text), 60000)