Ejemplo n.º 1
0
 def __init__(self, settings_ini: (str, SettingsINI), parent=None):
     """
     :param settings_ini: (str, SettingsINI)
         can be a settings_ini file path or configured SettingsINI object.
     """
     self.df_manager = DataFrameModelManager()
     QtGui.QMainWindow.__init__(self, parent=parent)
     self.setupUi(self)
     self.icons = Icons()
     self.dialog_settings = SettingsDialog(settings=settings_ini)
     self.dialog_merge_purge = MergePurgeDialog(self.df_manager)
     self.dialog_export = DataFrameModelExportDialog(self.df_manager,
                                                     parent=self)
     self.dialog_import = DataFrameModelImportDialog(self.df_manager,
                                                     parent=self)
     self.dialog_new_folder = DirectoryPathCreateDialog(self.treeView,
                                                        parent=self)
     self.dialog_cloud = None
     self.key_delete = QtGui.QShortcut(self)
     self.key_enter = QtGui.QShortcut(self)
     self.key_zip = QtGui.QShortcut(self)
     self.key_rename = QtGui.QShortcut(self)
     self.connect_window_title()
     self.connect_actions()
     self.connect_treeview()
     self.connect_icons()
     self.connect_settings_dialog()
     self.connect_import_dialog()
     self.connect_export_dialog()
     self.connect_cloud_dialog()
     self.current_model = None
Ejemplo n.º 2
0
Archivo: project.py Proyecto: g438/zeex
 def __init__(self,
              directory,
              settings_ini,
              default_dirs=True,
              tree_view=None,
              main_control=None,
              **kwargs):
     self.parent = kwargs.get('parent', None)
     self.main_control = main_control
     if default_dirs is True:
         settings_ini.set_safe('GENERAL', 'ROOT_DIRECTORY', directory)
         settings_ini.set_safe('GENERAL', 'LOG_DIRECTORY',
                               os.path.join(directory, 'logs'))
     self._directory = directory
     self._df_manager = DataFrameModelManager()
     self._file_tree_model = FileTreeModel(root_dir=directory,
                                           parent=self.parent)
     self._dialog_export_df_model = DataFrameModelExportDialog(
         self.df_manager)
     self._dialog_merge_purge = MergePurgeDialog(self.df_manager,
                                                 parent=self.parent)
     self._dialog_add_directory = DirectoryPathCreateDialog(
         base_dirname=directory, parent=self.parent)
     self._dialog_import_df_model = DataFrameModelImportDialog(
         self.df_manager, parent=self.parent)
     self._dialog_import_df_model.signalImported.connect(
         self._handle_imported_df_model)
     self._dialog_settings = ProjectSettingsDialog(settings_ini)
     self._tree_view = None
     self.tree_view = tree_view
Ejemplo n.º 3
0
    def dialog(self, qtbot, example_file_path) -> DataFrameModelImportDialog:

        dfm = DataFrameModelManager()
        dialog = DataFrameModelImportDialog(dfm, file_path=example_file_path)
        dialog.show()
        qtbot.add_widget(dialog)
        return dialog
Ejemplo n.º 4
0
Archivo: main.py Proyecto: g438/zeex
 def configure(self):
     """
     called once on __init__
     - Sets AlchemyConnectionManager and/or DataFrameModelManager if they
       were not set in the __init__(**kwargs)
     - Connects default actions
     - sets treeView model.
     :return: (None)
     """
     self.setupUi(self)
     if self.con_manager is None:
         self.con_manager = AlchemyConnectionManager()
     if self.df_manager is None:
         self.df_manager = DataFrameModelManager()
     self._key_enter.setKey('return')
     self._key_ctrl_t.setKey('ctrl+T')
     self._key_enter.activated.connect(self.open_query_alchemyview)
     self._key_ctrl_t.activated.connect(self.open_table_description_dialog)
     self.treeView.setModel(self.con_manager.get_standard_item_model())
     self.actionRemove.triggered.connect(self.delete)
     self.actionRefreshSchemas.triggered.connect(self.refresh_schemas)
     self.actionSaveText.triggered.connect(self.save_last_sql_text)
     self.actionSaveTextAs.triggered.connect(self.save_sql_text)
     self.actionOpenFile.triggered.connect(self.open_sql_text)
     self.actionOpenQueryData.triggered.connect(self.open_query_fileview)
     self.actionConnectToDatabase.triggered.connect(self.connect_database)
     self.actionDisconnectFromDatabase.triggered.connect(
         self.disconnect_database)
     self.actionExportFile.triggered.connect(self.export_table)
     self.actionImportFile.triggered.connect(self.open_import_dialog)
     self.actionAddDatabase.triggered.connect(
         self.open_add_connection_dialog)
     self.actionExecuteQuery.triggered.connect(self.execute_query)
     self.actionExecuteSelectedQuery.triggered.connect(
         self.execute_query_selected)
     self.treeView.expanded.connect(self.sync_current_database)
     self.connect_default_databases()
Ejemplo n.º 5
0
 def main_window(self, qtbot, sqlite_db_path):
     """
     Creates & returns a DatabasesMainWindow object loaded with one test sqlite connection
     named "con1" - any existing tables are dropped.
     :param qtbot:
     :param sqlite_db_path:
     :return:
     """
     c = AlchemyConnection(name="con1")
     c.configure("sqlite:///" + sqlite_db_path, reset=True)
     c.meta.drop_all()
     cm = AlchemyConnectionManager()
     cm.add_connection(connection=c)
     window = DatabasesMainWindow(df_manager=DataFrameModelManager(), connection_manager=cm)
     qtbot.addWidget(window)
     return window
Ejemplo n.º 6
0
class ProjectMainWindow(QtGui.QMainWindow, Ui_ProjectWindow):
    """
    The ProjectMainWindow displays a project that the user wants to work on.
    The project lives in a directory within the ZeexApp.ROOT_DIRECTORY.
    Each project gets a DataFrameModelManager object which controls the
    DataFrameModel of each file opened.
    The main window shows all files in this directory and provides
    very convenient features to work with files containing rows/columns.

    Project's settings are stored in a .ini file
    in the root project directory.
    """
    signalModelChanged = QtCore.Signal(str)
    signalModelOpened = QtCore.Signal(str)
    signalModelDestroyed = QtCore.Signal(str)

    def __init__(self, settings_ini: (str, SettingsINI), parent=None):
        """
        :param settings_ini: (str, SettingsINI)
            can be a settings_ini file path or configured SettingsINI object.
        """
        self.df_manager = DataFrameModelManager()
        QtGui.QMainWindow.__init__(self, parent=parent)
        self.setupUi(self)
        self.icons = Icons()
        self.dialog_settings = SettingsDialog(settings=settings_ini)
        self.dialog_merge_purge = MergePurgeDialog(self.df_manager)
        self.dialog_export = DataFrameModelExportDialog(self.df_manager,
                                                        parent=self)
        self.dialog_import = DataFrameModelImportDialog(self.df_manager,
                                                        parent=self)
        self.dialog_new_folder = DirectoryPathCreateDialog(self.treeView,
                                                           parent=self)
        self.dialog_cloud = None
        self.key_delete = QtGui.QShortcut(self)
        self.key_enter = QtGui.QShortcut(self)
        self.key_zip = QtGui.QShortcut(self)
        self.key_rename = QtGui.QShortcut(self)
        self.connect_window_title()
        self.connect_actions()
        self.connect_treeview()
        self.connect_icons()
        self.connect_settings_dialog()
        self.connect_import_dialog()
        self.connect_export_dialog()
        self.connect_cloud_dialog()
        self.current_model = None

    @property
    def project_directory(self):
        """
        The project's root directory stores everything for the project.

        :return: (str)
            ProjectMainWindow.treeView.QFileSystemModel.rootPath
        """
        return self.treeView.model().rootPath()

    @property
    def log_directory(self):
        """
        The project's log directory stores output logs.
        :return: (str)
            ProjectMainWindow.project_directory/log
        """
        return os.path.join(self.project_directory, 'log')

    @QtCore.Slot(SettingsINI, str)
    def sync_settings(self, config: SettingsINI = None, file_path=None):
        """
        Anytime settings are saved this method gets triggered.
        Sets defaults for various views.
        :param config:
        :param file_path:
        :return:
        """
        self.connect_export_dialog()
        self.connect_import_dialog()

    def connect_window_title(self):
        """
        Sets the ProjectMainWindow.windowTitle to "Project - dirname - dirpath"
        :return: None
        """
        root_dir = self.dialog_settings.rootDirectoryLineEdit.text()
        base_name = os.path.basename(root_dir)
        self.dialog_settings.setWindowTitle("{} - Settings".format(base_name))
        self.setWindowTitle("Project: {} - {}".format(
            base_name, root_dir.replace(base_name, "")))

    def connect_actions(self):
        """
        Connects all project actions.
        :return: None
        """

        self.actionPreferences.triggered.connect(self.dialog_settings.show)
        self.actionAddFolder.triggered.connect(self.dialog_new_folder.show)
        self.actionNew.triggered.connect(self.dialog_import.show)
        self.actionOpen.triggered.connect(self.open_tableview_window)
        self.actionSave.triggered.connect(self.dialog_export.show)
        self.actionRemove.triggered.connect(self.remove_tree_selected_path)
        self.actionRename.triggered.connect(self.open_rename_path_dialog)
        self.actionMergePurge.triggered.connect(self.open_merge_purge_dialog)
        self.actionUnzip.triggered.connect(self.handle_compression)
        self.actionZip.triggered.connect(self.handle_compression)
        self.key_delete.setKey('del')
        self.key_enter.setKey('return')
        self.key_zip.setKey(QtGui.QKeySequence(self.tr('Ctrl+Z')))
        self.key_rename.setKey(QtGui.QKeySequence(self.tr('Ctrl+R')))
        self.key_delete.activated.connect(self.remove_tree_selected_path)
        self.key_enter.activated.connect(self.open_tableview_window)
        self.key_zip.activated.connect(self.handle_compression)
        self.key_rename.activated.connect(self.open_rename_path_dialog)

    def connect_icons(self):
        """
        Sets all the menu/window icons.
        :return: None
        """
        self.setWindowIcon(self.icons['folder'])
        self.actionNew.setIcon(self.icons['add'])
        self.actionAddFolder.setIcon(self.icons['folder'])
        self.actionOpen.setIcon(self.icons['spreadsheet'])
        self.actionPreferences.setIcon(self.icons['settings'])
        self.actionRemove.setIcon(self.icons['delete'])
        self.actionSave.setIcon(self.icons['save'])
        self.dialog_settings.setWindowIcon(self.icons['settings'])
        self.actionMergePurge.setIcon(self.icons['merge'])
        self.actionRename.setIcon(self.icons['rename'])
        self.dialog_merge_purge.setWindowIcon(self.icons['merge'])
        self.actionZip.setIcon(self.icons['archive'])
        self.dialog_new_folder.setWindowIcon(self.icons['folder'])
        self.actionUnzip.setIcon(self.icons['unzip'])

    def connect_treeview(self):
        """
        Uses the ProjectMainWindow.dialog_settings.rootDirectoryLineEdit
        to get the root directory name of the project. It then
        connects the filetree using a QFileSystemModel.
        :return: None
        """
        rootdir = self.dialog_settings.rootDirectoryLineEdit.text()
        model = FileTreeModel(root_dir=rootdir)
        self.treeView.setModel(model)
        self.treeView.setRootIndex(model.index(rootdir))
        self.treeView.setColumnWidth(0, 400)
        self.treeView.setSelectionMode(self.treeView.ExtendedSelection)

    def connect_settings_dialog(self):
        """
        Re-purposes the ProjectMainWindow.dialog_settings
        dialog to fit the scope of a project rather than
        the application as a whole.
        :return: None
        """
        #Adjust the box to remove irrelevant items.
        self.dialog_settings.cloudProviderComboBox.hide()
        self.dialog_settings.cloudProviderLabel.hide()
        self.dialog_settings.btnLogDirectory.hide()
        self.dialog_settings.btnRootDirectory.hide()
        self.dialog_settings.themeComboBox.hide()
        self.dialog_settings.themeLabel.hide()

        # Override the log/root directory options
        self.dialog_settings.logDirectoryLineEdit.setText(self.log_directory)
        self.dialog_settings.logDirectoryLineEdit.setReadOnly(True)
        self.dialog_settings.rootDirectoryLineEdit.setText(
            self.project_directory)
        self.dialog_settings.rootDirectoryLineEdit.setReadOnly(True)
        self.dialog_settings.btnSetDefault.setVisible(False)

        self.dialog_settings.signalSettingsSaved.connect(self.sync_settings)
        self.dialog_new_folder.base_dirname = self.dialog_settings.rootDirectoryLineEdit.text(
        )

    def connect_cloud_dialog(self):
        try:
            self.dialog_cloud = DropBoxViewDialog(self.treeView, self)
            self.actionViewCloud.triggered.connect(self.dialog_cloud.show)
            self.actionViewCloud.setIcon(self.icons['cloud'])
        except Exception as e:
            logging.error("Error connecting to cloud: {}".format(e))
            self.actionViewCloud.setVisible(False)

    def connect_export_dialog(self):
        """
        Sets defaults of the DataFrameModelExport Dialog.
        :return: None
        """
        self.dialog_export.signalExported.connect(self._flush_export)
        self.dialog_export.setWindowIcon(self.icons['export_generic'])
        sep = self.dialog_settings.separatorComboBox.currentText()
        enc = self.dialog_settings.encodingComboBox.currentText()
        self.dialog_export.set_encoding(enc)
        self.dialog_export.set_separator(sep)

    def connect_import_dialog(self):
        """
        Sets defaults of the DataFrameModelImport Dialog.
        :return: None
        """
        self.dialog_import.signalImported.connect(self.import_file)
        self.dialog_import.setWindowIcon(self.icons['add'])
        sep = self.dialog_settings.separatorComboBox.currentText()
        enc = self.dialog_settings.encodingComboBox.currentText()
        self.dialog_import.set_encoding(enc)
        self.dialog_import.set_separator(sep)

    def open_tableview_window(self, model: DataFrameModel = None):
        """
        Opens a FileTableWindow for the filename selected in the
        ProjectMainWindow.treeView.

        :param model: The qtpandas.models.DataFrameModel to edit.
        :return: None
        """
        if model is None:
            # Maybe it's selected on the tree?
            model = self.get_tree_selected_model()
            if model is None:
                # No, not sure why this was called..
                #box = get_ok_msg_box(self, "No model available to open.")
                #box.show()
                pass

        self.df_manager.get_fileview_window(model.filePath).show()
        self.add_recent_file_menu_entry(model.filePath, model)

    def open_merge_purge_dialog(self, model: DataFrameModel = None):
        if model is None:
            model = self.get_tree_selected_model()
        current_model = self.dialog_merge_purge.source_model
        if current_model is None or current_model.filePath is not model.filePath:
            self.dialog_merge_purge.set_source_model(model=model,
                                                     configure=True)
        self.dialog_merge_purge.show()

    def get_tree_selected_model(self,
                                raise_on_error=True) -> (DataFrameModel, None):
        """
        Returns a DataFrameModel based on the filepath selected
        in the ProjectMainWindow.treeView.
        :return: qtpandas.DataFrameModel
        """
        # Check if file is selected in tree view
        selected = self.treeView.selectedIndexes()
        if selected:
            idx = selected[0]
            file_path = self.treeView.model().filePath(idx)
            return self.df_manager.read_file(file_path)
        return None

    def get_tree_selected_path(self):
        selected = self.treeView.selectedIndexes()
        if selected:
            idx = selected[0]
            return self.treeView.model().filePath(idx)
        return None

    def remove_tree_selected_path(self):
        #TODO: need to emit a signal here.
        idxes = self.treeView.selectedIndexes()
        if idxes:
            file_model = self.treeView.model()
            for idx in idxes:
                if not file_model.isDir(idx):
                    file_model.remove(idx)
                else:
                    file_model.rmdir(idx)

    def open_tableview_current(self, model: DataFrameModel = None):
        """
        Opens a tableview window for the current_model or the model kwarg.

        :param model: DataFrameModel

        :return: None
        """
        if model is None:
            model = self.current_model
        else:
            self.set_current_df_model(model)

        assert isinstance(model, DataFrameModel), "No current DataFrame model."
        return self.open_tableview_window(model)

    def set_current_df_model(self, model: DataFrameModel):
        """
        Sets the current dataframe model. for the project.
        :param model: DataFrameModel
        :return: None
        """
        self.df_manager.set_model(model, model._filePath)
        self.current_model = self.df_manager.get_model(model._filePath)

    @QtCore.Slot('DataFrameModel', str)
    def import_file(self, filepath, open=True):
        if isinstance(filepath, DataFrameModel):
            model = filepath
            filepath = model.filePath
            self.df_manager.set_model(model, filepath)

        model = self.df_manager.get_model(filepath)
        name = os.path.basename(model.filePath)
        dirname = self.dialog_settings.rootDirectoryLineEdit.text()
        assert os.path.isdir(
            dirname), "Root Directory is not a directory: {}".format(dirname)

        if os.path.dirname(filepath) != dirname:
            newpath = os.path.join(dirname, name)
            self.df_manager.save_file(filepath,
                                      save_as=newpath,
                                      keep_orig=False)
            filepath = newpath

        if open is True:
            model = self.df_manager.get_model(filepath)
            self.open_tableview_window(model)

    def add_recent_file_menu_entry(self, name, model):
        action = QtGui.QAction(name, self.menuRecent_Files)
        action.triggered.connect(partial(self.open_tableview_window, model))
        actions = self.menuRecent_Files.actions()
        if actions:
            self.menuRecent_Files.insertAction(actions[0], action)
        else:
            self.menuRecent_Files.addAction(action)

    def maybe_save_copy(self, df, filepath, max_size=20000, **kwargs):
        """
        Saves a copy of the file to the project folder if
            its smaller than max_size
            its larger than 0 rows

        :param df: DataFrame: to save
        :param filepath: str: the filepath of the DataFrame
        :param max_size: int: max size of DataFrame
        :param kwargs: DataFrame.to_csv(**kwargs)
        :return: None
        """
        if df.index.size <= max_size and not df.empty:
            kwargs['index'] = kwargs.get('index', False)
            df.to_csv(filepath, **kwargs)

    def _flush_export(self, orig_path, new_path):
        if orig_path != new_path:
            self.add_recent_file_menu_entry(
                new_path, self.df_manager.get_model(new_path))

    def open_rename_path_dialog(self):
        current_path = self.get_tree_selected_path()
        dialog = FilePathRenameDialog(current_path, parent=self)
        dialog.show()

    def handle_compression(self, fpath=None, **kwargs):
        if fpath is None:
            fpath = self.get_tree_selected_path()
        assert fpath is not None, "No selected path!"
        if not fpath.lower().endswith('.zip'):
            return ostools.zipfile_compress(fpath, **kwargs)
        else:
            return ostools.zipfile_unzip(
                file_path=fpath,
                dir=self.dialog_settings.rootDirectoryLineEdit.text())
Ejemplo n.º 7
0
 def manager(self) -> DataFrameModelManager:
     return DataFrameModelManager()
Ejemplo n.º 8
0
Archivo: main.py Proyecto: g438/zeex
class DatabasesMainWindow(QtGui.QMainWindow, Ui_DatabasesMainWindow):
    """
    The general query/database maintenance MainWindow that is home
    to the following main objects:
        - A ToolBar with actions for simple database actions.
        - A TreeView of all databases registered.
        - A TextEdit for writing SQL queries
        - A TableView for viewing results.
    """
    def __init__(self,
                 *args,
                 df_manager: DataFrameModelManager = None,
                 connection_manager: AlchemyConnectionManager = None,
                 **kwargs):
        QtGui.QMainWindow.__init__(self, *args, **kwargs)
        self.bookmarks = BookmarkManager('sql_bookmark_manager')
        self._last_df_model = None
        self._last_text_dir = ''
        self._last_text_path = ''
        self.con_manager = connection_manager
        self.df_manager = df_manager
        self._dialog_add_con = None
        self._dialog_import = None
        self._key_enter = QtGui.QShortcut(self)
        self._key_ctrl_t = QtGui.QShortcut(self)
        self.configure()

    @property
    def tree_model(self) -> QtGui.QStandardItemModel:
        """
        Returns the QStandardItemModel containing
        the databases, tables, and columns.
        :return: (QtGui.QStandardItemModel)
        """
        return self.treeView.model()

    @property
    def dialog_add_con(self) -> AlchemyConnectionDialog:
        if self._dialog_add_con is None:
            self._dialog_add_con = AlchemyConnectionDialog(self.con_manager,
                                                           parent=self)
            self._dialog_add_con.signalConnectionAdded.connect(
                self.refresh_schemas)
        return self._dialog_add_con

    @property
    def dialog_import(self) -> AlchemyTableImportDialog:
        if self._dialog_import is None:
            self._dialog_import = AlchemyTableImportDialog(
                self.connection,
                self.con_manager,
                df_manager=self.df_manager,
                parent=self)
        return self._dialog_import

    @property
    def connection(self) -> AlchemyConnection:
        return self.con_manager.connection(
            self.comboBoxCurrentDatabase.currentText())

    def connect_default_databases(self):
        """
        Connects to system databases by default.
        TODO: maybe repurpose this into adding a dictionary of
        database configurations?
        :return: (None)
        """
        new = False
        for name, ci in DEFAULT_CONNECTIONS.items():
            try:
                self.con_manager.connection(name)
            except KeyError:
                try:
                    self.con_manager.add_connection(name=name, **ci)
                    new = True
                except Exception as e:
                    pass

        others = self.con_manager.add_connections_from_settings()
        if new or others:
            self.treeView.setModel(self.con_manager.get_standard_item_model())

    def configure(self):
        """
        called once on __init__
        - Sets AlchemyConnectionManager and/or DataFrameModelManager if they
          were not set in the __init__(**kwargs)
        - Connects default actions
        - sets treeView model.
        :return: (None)
        """
        self.setupUi(self)
        if self.con_manager is None:
            self.con_manager = AlchemyConnectionManager()
        if self.df_manager is None:
            self.df_manager = DataFrameModelManager()
        self._key_enter.setKey('return')
        self._key_ctrl_t.setKey('ctrl+T')
        self._key_enter.activated.connect(self.open_query_alchemyview)
        self._key_ctrl_t.activated.connect(self.open_table_description_dialog)
        self.treeView.setModel(self.con_manager.get_standard_item_model())
        self.actionRemove.triggered.connect(self.delete)
        self.actionRefreshSchemas.triggered.connect(self.refresh_schemas)
        self.actionSaveText.triggered.connect(self.save_last_sql_text)
        self.actionSaveTextAs.triggered.connect(self.save_sql_text)
        self.actionOpenFile.triggered.connect(self.open_sql_text)
        self.actionOpenQueryData.triggered.connect(self.open_query_fileview)
        self.actionConnectToDatabase.triggered.connect(self.connect_database)
        self.actionDisconnectFromDatabase.triggered.connect(
            self.disconnect_database)
        self.actionExportFile.triggered.connect(self.export_table)
        self.actionImportFile.triggered.connect(self.open_import_dialog)
        self.actionAddDatabase.triggered.connect(
            self.open_add_connection_dialog)
        self.actionExecuteQuery.triggered.connect(self.execute_query)
        self.actionExecuteSelectedQuery.triggered.connect(
            self.execute_query_selected)
        self.treeView.expanded.connect(self.sync_current_database)
        self.connect_default_databases()

    def refresh_schemas(self):
        """
        Refreshes the database schemas for each connection.
        Then resets the treeView with the new info.
        :return: (None)
        """
        for c in self.con_manager.connections.keys():
            con = self.con_manager.connection(c)
            con.refresh_schemas()
        self.treeView.setModel(self.con_manager.get_standard_item_model())

    def delete(self, idx=None):
        """
        Deletes a database, table, or column on a database.
        If a database can't support dropping a column (like SQLite), an error
        is raised. Otherwise, the item is removed from the database if it's a table/column,
        the item is always removed from the treeView list if removed.
        :param idx:
        :return:
        """
        if idx is None:
            idx = self.treeView.selectedIndexes()
            if not idx:
                raise Exception("Nothing selected to delete!")
            idx = idx[0]
        item = self.tree_model.itemFromIndex(idx)
        if item:
            if not item.parent():
                # It's a database - just remove it off the list
                self.con_manager.remove_connection(item.text())
                self.tree_model.takeRow(item.row())
            elif not item.parent().parent():
                # It's a table
                db_item = item.parent()
                table_name = item.text()
                db_name = db_item.text()
                con = self.con_manager.connection(db_name)
                table = con.meta.tables[table_name]
                table.drop(checkfirst=True)
                db_item.removeRow(item.row())
            elif not item.hasChildren():
                # It's a column
                table_item = self.tree_model.itemFromIndex(
                    item.parent().index())
                db_name = self.tree_model.itemFromIndex(
                    table_item.parent().index()).text()
                con = self.con_manager.connection(db_name)
                table_name = table_item.text()
                try:
                    sql = "ALTER TABLE '{}' DROP COLUMN '{}'".format(
                        table_name, item.text())
                    con.engine.execute(sql)
                    table_item.removeRow(item.row())
                except Exception:
                    raise NotImplementedError(
                        "Unable to drop columns for SQL version: {}".format(
                            con.engine.name))

    def save_sql_text(self, file_path=''):
        """
        Saves the current text in the textEdit to file_path.
        :param file_path: (str, default None)
            None will open a QFileDialog to ask for a save name.
        :return: (str)
            Of the saved file_path.
        """
        if file_path == '':
            file_path = self.comboBoxCurrentFile.currentText()
            if file_path == '':
                file_path = QtGui.QFileDialog.getSaveFileName(
                    dir=self._last_text_dir)[0]

        self._last_text_dir = os.path.dirname(file_path)
        new_text = self.textEdit.document().toPlainText()
        with open(file_path, "w") as fp:
            fp.write(new_text)
        if file_path in self.bookmarks.names:
            self.bookmarks.bookmark(file_path).set_text(new_text,
                                                        save_changes=False)

        self.set_current_file(file_path)

        return file_path

    def save_last_sql_text(self):
        """
        Convenience function to save the last opened text file back
        to disk.
        :return: (str)
            The saved file path
        """
        return self.save_sql_text(file_path=self._last_text_path)

    def open_sql_text(self, file_path=''):
        """
        Reads all text from file_path into the text edit widget
        :param file_path: (str, default '')
            The file_path to open.
            Defaults to a getOpenFileName
        :return: None
        """
        if file_path is '':
            file_path = QtGui.QFileDialog.getOpenFileName(
                dir=self._last_text_dir)[0]
        self._last_text_dir = os.path.dirname(file_path)
        self._last_text_path = file_path
        self.bookmarks.add_bookmark(file_path, file_path=file_path)
        text = self.bookmarks.bookmark(file_path).get_text()
        self.textEdit.setPlainText(text)
        self.set_current_file(file_path)

    def open_add_connection_dialog(self):
        self.dialog_add_con.show()

    def open_import_dialog(self):
        self.dialog_import.show()

    def open_table_description_dialog(self, idx=None):
        if idx is None:
            idx = self.treeView.selectedIndexes()[0]
        table_name = self.tree_model.itemFromIndex(idx).text()
        con_name = self.tree_model.itemFromIndex(idx.parent()).text()
        con = self.con_manager.connection(con_name)
        self._table_desc_dialog = AlchemyTableDescriptionDialog(con)
        self._table_desc_dialog.set_table(table_name)
        self._table_desc_dialog.show()

    def set_current_file(self, file_path):
        """
        Sets the current file combo box based on the given
        :param file_path. new items are inserted at the top.
        :param file_path: (str)
        :return: None
        """
        idx = self.comboBoxCurrentFile.findText(file_path)
        if idx < 0:
            self.comboBoxCurrentFile.insertItem(0, file_path)
            idx = self.comboBoxCurrentFile.findText(file_path)
        self.comboBoxCurrentFile.setCurrentIndex(idx)

    def set_current_database(self, db_name):
        """
        Sets the current database combo box based on the
        :param db_name. new items are inserted at the top.
        :param db_name: (str)
        :return: None
        """
        idx = self.comboBoxCurrentDatabase.findText(db_name)
        if idx < 0:
            self.comboBoxCurrentDatabase.insertItem(0, db_name)
            idx = self.comboBoxCurrentDatabase.currentIndex()
        self.comboBoxCurrentDatabase.setCurrentIndex(idx)

    def sync_current_database(self, idx):
        """
        Keeps the database combo box item up-to-date based on the
        user's current treeview selection.
        :param idx: (QModelIndex)
            Accepted from TreeView (or anyone, really)
        :return: None
        """
        item = self.tree_model.itemFromIndex(idx)
        if not item.parent():
            # It's a database
            self.set_current_database(item.text())
        elif not item.parent().parent():
            # It's a table
            self.set_current_database(item.parent().text())
        elif not item.hasChildren():
            # It's a column
            self.set_current_database(item.parent().parent().text())

    def connect_database(self, db_name=None):
        """
        Sets the currently active database.
        This one is a 'tough' design call because the con_manager
        automatically generates connections for databases
        in order to read out their MetaData and such...
        :param db_name: (str)
            The name of the database to activate
        :return: None
        """
        if db_name is None:
            db_name = self.tree_model.itemFromIndex(
                self.treeView.selectedIndexes()[0]).text()
        self.set_current_database(db_name)

    def disconnect_database(self):
        """
        Sets the comboBoxCurrentDatabase to blank.
        :param db_name: (str)
            The name of the database to deactivate.
        :return: None
        """
        self.set_current_database('')

    def export_table(self, db_name, table_name, **kwargs):
        """
        Opens export dialog for current database displaying
        a DatabaseTableExportDialog. The user can export a table
        to a file from there.
        :param db_name:
        :param table_name:
        :param kwargs:
        :return: (None)
        """
        pass

    def add_database(self, *args, **kwargs):
        """
        Registers a new database connection.
        and sync's the databases treeview.

        This is also a slot that gets activated
        when the DatabaseImportDialog is accepted.

        See zeex.core.ctrls.sql.AlchemyConnectionManager.add_connection(*args, **kwargs)
        for more details.

        :param args: (AlchemyConnectionManager.add_connection(*args, **kwargs))
        :param kwargs: (AlchemyConnectionManager.add_connection(*args, **kwargs))
        :return: (None)
        """
        name = kwargs.get('name', None)
        con = kwargs.get('connection', None)
        check_name = (name if name is not None else con.name)
        kwargs['allow_duplicate'] = False
        self.con_manager.add_connection(*args, **kwargs)
        self.refresh_schemas()

    def execute_query(self, db_name=None, selected_text_only=False):
        """
        Executes the given SQL query against the given database.
        Displays a message box with the error (if any), otherwise:
        Stores the DataFrameModel to the TableView.

        :param db_name: (str)
            The name of the database to execute the query on.
        :param selected_text_only: (bool, default False)
            True executes only the selected text in the textEdit
            False executes the entire text no matter what.
        :return: None
        """
        if db_name is None:
            db_name = self.comboBoxCurrentDatabase.currentText()

        try:
            con = self.con_manager.connection(db_name)
            statement = self.textEdit.textCursor().selectedText().lstrip(
            ).rstrip()
            if selected_text_only is False or statement is '':
                statement = self.textEdit.document().toPlainText().lstrip(
                ).rstrip()
            self._last_df_model = dfm = con.execute_sql(statement)
        except Exception as e:
            if isinstance(e, KeyError):
                e = "Invalid database selection: {}".format(e)
            box = get_ok_msg_box(self, str(e), title="ERROR EXECUTING QUERY")
            box.show()
            raise
        self.tableView.setModel(dfm)

    def execute_query_selected(self, db_name=None):
        """
        Executes the selected text against the given database.
        DatabasesMainWindow.execute_query(db_name, selected_text_only=True)
        :param db_name: (str)
            The database to execute the query on.
        :return: None
        """
        return self.execute_query(db_name=db_name, selected_text_only=True)

    def open_query_fileview(self, save_path=None):
        dfm = self.tableView.model()
        assert dfm is not None, "No dataframe model available!"
        if save_path is None:
            if self._last_text_path is not '':
                save_path = os.path.splitext(self._last_text_path)[0] + ".csv"
            else:
                save_path = QtGui.QFileDialog.getSaveFileName(
                    dir=self._last_text_dir)[0]
        base, ext = os.path.splitext(save_path)
        if ext.lower() not in ['.txt', '.xlsx', '.csv']:
            save_path = base + ".csv"
        self.df_manager.set_model(dfm, save_path)
        self.df_manager.get_fileview_window(save_path).show()

    def open_query_alchemyview(self):
        con = self.connection
        table_name = self.tree_model.itemFromIndex(
            self.treeView.selectedIndexes()[0]).text()
        window = con.get_alchemy_query_editor_window(table_name, parent=self)
        window.show()
Ejemplo n.º 9
0
 def df_manager(self) -> DataFrameModelManager:
     if self._df_manager is None:
         self._df_manager = DataFrameModelManager()
     return self._df_manager