Esempio 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
Esempio n. 2
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
Esempio n. 3
0
File: project.py Progetto: 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
Esempio n. 4
0
 def test_separators(self, dialog: DataFrameModelImportDialog,
                     example_file_path, sep):
     out_path = os.path.splitext(example_file_path)[0] + "_sep_test.csv"
     if os.path.exists(out_path):
         os.remove(out_path)
     df = pandatools.superReadFile(example_file_path)
     df.to_csv(out_path, sep=SEPARATORS[sep], index=False)
     try:
         assert out_path not in dialog.df_manager.file_paths
         idx = dialog.comboBoxSeparator.findText(sep)
         assert idx >= 0
         dialog.comboBoxSeparator.setCurrentIndex(idx)
         dialog.set_file_path(out_path)
         dialog.checkBoxParseDates.setChecked(False)
         dialog.checkBoxTrimSpaces.setChecked(False)
         dialog.checkBoxScrubLinebreaks.setChecked(False)
         dialog.checkBoxHasHeaders.setChecked(True)
         dialog.execute()
         assert out_path in dialog.df_manager.file_paths
     finally:
         os.remove(out_path)
Esempio n. 5
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())
Esempio n. 6
0
 def test_general(self, dialog: DataFrameModelImportDialog,
                  example_file_path):
     assert example_file_path not in dialog.df_manager.file_paths
     dialog.execute()
     assert example_file_path in dialog.df_manager.file_paths