Open previous files/tabs after restart
Remember the open files in the editor tabs and reload them after restarting the application. This feature is configurable and described in issue #38
This commit is contained in:
parent
4369650972
commit
0bc39ada3d
@ -102,6 +102,13 @@ class AppConfigurator:
|
||||
# Save the data in the configuration dictionary.
|
||||
self.save_configuration_data()
|
||||
|
||||
try:
|
||||
self.configuration_dictionary["open_previous_files"]
|
||||
|
||||
except KeyError:
|
||||
self.configuration_dictionary["open_previous_files"] = True
|
||||
self.save_configuration_data()
|
||||
|
||||
except Exception as file_error:
|
||||
logging.error("The file {} cannot be opened and app configuration parameter cannot be loaded with the "
|
||||
"following error: {}".format(self.yaml_app_configuration_file, file_error), exc_info=True)
|
||||
|
@ -102,7 +102,7 @@ class ConnectionStore:
|
||||
|
||||
except Exception as file_error:
|
||||
logging.error("The file {} cannot be opened and database connection parameter cannot be saved with the "
|
||||
"following error: {}".format(self.yaml_connection_parameters_file, file_error))
|
||||
"following error: {}".format(self.yaml_connection_parameters_file, file_error), exc_info=True)
|
||||
|
||||
def check_parameter_for_duplicate(self, connection_parameter_dictionary_for_check):
|
||||
"""
|
||||
|
83
pygadmin/file_manager.py
Normal file
83
pygadmin/file_manager.py
Normal file
@ -0,0 +1,83 @@
|
||||
import copy
|
||||
import logging
|
||||
import os
|
||||
import yaml
|
||||
|
||||
from pygadmin.configurator import global_app_configurator
|
||||
|
||||
|
||||
class FileManager:
|
||||
def __init__(self):
|
||||
self.open_files = []
|
||||
# Define a path for the configuration files.
|
||||
configuration_path = os.path.join(os.path.expanduser("~"), '.pygadmin')
|
||||
|
||||
# If the path for the configuration files does not exist, create it.
|
||||
if not os.path.exists(configuration_path):
|
||||
os.mkdir(configuration_path)
|
||||
|
||||
# Define a yaml file, which stores the current open files in the editor widgets, so it is independent from the
|
||||
# user's operating system.
|
||||
self.open_files_file = os.path.join(configuration_path, "open_files.yaml")
|
||||
|
||||
# Check for the existence of the file.
|
||||
if not os.path.exists(self.open_files_file):
|
||||
# Create the file as an empty file for writing in it.
|
||||
open(self.open_files_file, "a")
|
||||
|
||||
if global_app_configurator.get_single_configuration("open_previous_files") is False:
|
||||
self.delete_all_files()
|
||||
self.commit_current_files_to_yaml()
|
||||
|
||||
def add_new_file(self, file_name):
|
||||
self.open_files.append(file_name)
|
||||
|
||||
return True
|
||||
|
||||
def delete_file(self, file_name):
|
||||
if file_name not in self.open_files:
|
||||
return False
|
||||
|
||||
self.open_files.remove(file_name)
|
||||
|
||||
return True
|
||||
|
||||
def delete_all_files(self):
|
||||
self.open_files = []
|
||||
|
||||
def commit_current_files_to_yaml(self):
|
||||
try:
|
||||
with open(self.open_files_file, "w") as file_data:
|
||||
yaml.safe_dump(self.open_files, file_data)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as file_error:
|
||||
logging.error("The file {} cannot be opened and the open files in the editor cannot be saved with the "
|
||||
"following error: {}".format(self.open_files_file, file_error), exc_info=True)
|
||||
|
||||
return False
|
||||
|
||||
def load_open_file_list(self):
|
||||
# Use a try statement in case of a broken .yaml file.
|
||||
try:
|
||||
# Use the read mode for getting the content of the file.
|
||||
with open(self.open_files_file, "r") as file_data:
|
||||
# Use the function for a safe load, because the file can be edited manually.
|
||||
self.open_files = yaml.safe_load(file_data)
|
||||
|
||||
# If the list with the open files out of the .yaml file is empty, it is None after a load. But for
|
||||
# preventing further errors, because a list is expected, this if branch is used.
|
||||
if self.open_files is None:
|
||||
# Define the list of open files as an empty list.
|
||||
self.open_files = []
|
||||
|
||||
# Return a copy of the list, so there is no manipulation from the outside.
|
||||
return copy.copy(self.open_files)
|
||||
|
||||
except Exception as file_error:
|
||||
logging.error("The file {} cannot be opened previous open files cannot be loaded with the following "
|
||||
"error: {}".format(self.open_files_file, file_error), exc_info=True)
|
||||
|
||||
|
||||
global_file_manager = FileManager()
|
@ -2,6 +2,7 @@ import logging
|
||||
import re
|
||||
import datetime
|
||||
|
||||
from PyQt5 import QtGui
|
||||
from PyQt5.Qsci import QsciScintilla
|
||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QEvent
|
||||
from PyQt5.QtGui import QKeySequence
|
||||
@ -16,6 +17,7 @@ from pygadmin.database_query_executor import DatabaseQueryExecutor
|
||||
from pygadmin.widgets.search_replace_widget import SearchReplaceWidget
|
||||
from pygadmin.widgets.search_replace_parent import SearchReplaceParent
|
||||
from pygadmin.command_history_store import global_command_history_store
|
||||
from pygadmin.file_manager import global_file_manager
|
||||
|
||||
|
||||
class MetaEditor(type(QWidget), type(SearchReplaceParent)):
|
||||
@ -388,13 +390,17 @@ class EditorWidget(QWidget, SearchReplaceParent, metaclass=MetaEditor):
|
||||
self.stop_query_button.setEnabled(activation)
|
||||
self.stop_query_shortcut.setEnabled(activation)
|
||||
|
||||
def save_current_statement_in_file(self):
|
||||
def save_current_statement_in_file(self, previous_file_name=None):
|
||||
"""
|
||||
Save the current text/statement of the lexer as query editor in for further usage. The class-wide variable for
|
||||
the corresponding file is used as directory with file. If this variable contains its initialized value None,
|
||||
use the function for opening a file dialog.
|
||||
"""
|
||||
|
||||
if previous_file_name is None and global_app_configurator.get_single_configuration("open_previous_files") is\
|
||||
True:
|
||||
previous_file_name = self.corresponding_saved_file
|
||||
|
||||
# Check if the class-wide variable for the corresponding file is None.
|
||||
if self.corresponding_saved_file is None:
|
||||
# Open a file dialog and if the result is False, the process has been aborted.
|
||||
@ -402,14 +408,33 @@ class EditorWidget(QWidget, SearchReplaceParent, metaclass=MetaEditor):
|
||||
# End the function with a return.
|
||||
return
|
||||
|
||||
# Open the file in the write mode, so every content is also overwritten.
|
||||
with open(self.corresponding_saved_file, "w") as file_to_save:
|
||||
# Define the current text of the query input editor as current text.
|
||||
current_text = self.query_input_editor.text()
|
||||
# Write the current text of the lexer as SQL editor in the file.
|
||||
file_to_save.write(current_text)
|
||||
# Save the current text in the class-wide current editor text.
|
||||
self.current_editor_text = current_text
|
||||
try:
|
||||
# Open the file in the write mode, so every content is also overwritten.
|
||||
with open(self.corresponding_saved_file, "w") as file_to_save:
|
||||
# Define the current text of the query input editor as current text.
|
||||
current_text = self.query_input_editor.text()
|
||||
# Write the current text of the lexer as SQL editor in the file.
|
||||
file_to_save.write(current_text)
|
||||
|
||||
except Exception as file_error:
|
||||
error_text = "The file {} cannot be written with the error: {}".format(self.corresponding_saved_file,
|
||||
file_error)
|
||||
|
||||
QMessageBox.critical(self, "File Reading Error", error_text)
|
||||
logging.critical(error_text, exc_info=True)
|
||||
|
||||
self.corresponding_saved_file = previous_file_name
|
||||
|
||||
return
|
||||
|
||||
if self.corresponding_saved_file != previous_file_name \
|
||||
and global_app_configurator.get_single_configuration("open_previous_files") is True:
|
||||
global_file_manager.delete_file(previous_file_name)
|
||||
global_file_manager.add_new_file(self.corresponding_saved_file)
|
||||
global_file_manager.commit_current_files_to_yaml()
|
||||
|
||||
# Save the current text in the class-wide current editor text.
|
||||
self.current_editor_text = current_text
|
||||
|
||||
# Update the current window title.
|
||||
self.update_window_title_and_description()
|
||||
@ -455,20 +480,46 @@ class EditorWidget(QWidget, SearchReplaceParent, metaclass=MetaEditor):
|
||||
# Get the file name out of the tuple.
|
||||
file_name = file_name_and_type[0]
|
||||
|
||||
if file_name is False:
|
||||
logging.info("The current file opening process was aborted by the user, so the content of this file is not "
|
||||
"loaded.")
|
||||
|
||||
return False
|
||||
|
||||
return self.load_statement_with_file_name(file_name)
|
||||
|
||||
def load_statement_with_file_name(self, file_name):
|
||||
# Check for the success in form of an existing file and not an empty string.
|
||||
if file_name != "":
|
||||
try:
|
||||
# Open the file in reading mode.
|
||||
with open(file_name, "r") as file_to_load:
|
||||
# Read the whole given file and save its text.
|
||||
file_text = file_to_load.read()
|
||||
|
||||
except Exception as file_error:
|
||||
error_text = "The file {} cannot be loaded with the error: {}".format(file_name, file_error)
|
||||
QMessageBox.critical(self, "File Reading Error", error_text)
|
||||
logging.critical(error_text, exc_info=True)
|
||||
|
||||
return False
|
||||
|
||||
if global_app_configurator.get_single_configuration("open_previous_files") is True and \
|
||||
self.corresponding_saved_file is not None:
|
||||
global_file_manager.delete_file(self.corresponding_saved_file)
|
||||
|
||||
# Save the name of the file in the class variable for the corresponding file.
|
||||
self.corresponding_saved_file = file_name
|
||||
|
||||
# Open the file in reading mode.
|
||||
with open(self.corresponding_saved_file, "r") as file_to_load:
|
||||
# Read the whole given file and save its text.
|
||||
file_text = file_to_load.read()
|
||||
# Show the content of the file as text in the lexer as SQL query editor.
|
||||
self.query_input_editor.setText(file_text)
|
||||
# Save the text of the file in the class-wide variable for the current text to check for changes and
|
||||
# get the current state of saved/unsaved.
|
||||
self.current_editor_text = file_text
|
||||
if global_app_configurator.get_single_configuration("open_previous_files") is True:
|
||||
global_file_manager.add_new_file(self.corresponding_saved_file)
|
||||
global_file_manager.commit_current_files_to_yaml()
|
||||
|
||||
# Show the content of the file as text in the lexer as SQL query editor.
|
||||
self.query_input_editor.setText(file_text)
|
||||
# Save the text of the file in the class-wide variable for the current text to check for changes and get the
|
||||
# current state of saved/unsaved.
|
||||
self.current_editor_text = file_text
|
||||
|
||||
# Update the window title
|
||||
self.update_window_title_and_description()
|
||||
@ -476,13 +527,6 @@ class EditorWidget(QWidget, SearchReplaceParent, metaclass=MetaEditor):
|
||||
# Report the success with a return value.
|
||||
return True
|
||||
|
||||
else:
|
||||
logging.info("The current file opening process was aborted by the user, so the content of this file is not "
|
||||
"loaded.")
|
||||
|
||||
# Report the failure with a return value.
|
||||
return False
|
||||
|
||||
def load_file_with_potential_overwrite_in_editor(self):
|
||||
"""
|
||||
Load an existing file in the editor widget. This function is a wrapper for load_statement_out_of_file with more
|
||||
@ -989,3 +1033,11 @@ class EditorWidget(QWidget, SearchReplaceParent, metaclass=MetaEditor):
|
||||
# Write a newline at the end of a data row.
|
||||
file_to_save.write("\n")
|
||||
|
||||
def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
|
||||
if self.corresponding_saved_file is not None \
|
||||
and global_app_configurator.get_single_configuration("open_previous_files"):
|
||||
global_file_manager.delete_file(self.corresponding_saved_file)
|
||||
global_file_manager.commit_current_files_to_yaml()
|
||||
|
||||
self.close()
|
||||
|
||||
|
@ -360,6 +360,14 @@ class MainWindow(QMainWindow):
|
||||
|
||||
# Check, if the current editor widget exists.
|
||||
if current_editor_widget is not None:
|
||||
if global_app_configurator.get_single_configuration("open_previous_files"):
|
||||
# Get the current corresponding file name for the usage as previous file name, so an overwrite in the
|
||||
# editor for the global file manager can be realized.
|
||||
current_corresponding_file = current_editor_widget.corresponding_saved_file
|
||||
|
||||
else:
|
||||
current_corresponding_file = None
|
||||
|
||||
# Check the parameter for save_as. If the parameter is True, the if clause gets to the point for a new
|
||||
# file dialog. If the result of this file dialog is False, end the function with a return. In this case,
|
||||
# the process has been aborted.
|
||||
@ -368,7 +376,7 @@ class MainWindow(QMainWindow):
|
||||
return
|
||||
|
||||
# Save the current statement and text in the query input editor with the function of the editor widget.
|
||||
current_editor_widget.save_current_statement_in_file()
|
||||
current_editor_widget.save_current_statement_in_file(current_corresponding_file)
|
||||
|
||||
# Define an else branch for error handling with a non existing current editor widget.
|
||||
else:
|
||||
|
@ -5,8 +5,10 @@ from PyQt5.QtGui import QIcon, QPixmap
|
||||
from PyQt5.QtWidgets import QMdiArea
|
||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal
|
||||
|
||||
from pygadmin.configurator import global_app_configurator
|
||||
from pygadmin.widgets.editor import EditorWidget
|
||||
from pygadmin.connectionfactory import global_connection_factory
|
||||
from pygadmin.file_manager import global_file_manager
|
||||
|
||||
|
||||
class MdiArea(QMdiArea):
|
||||
@ -45,6 +47,28 @@ class MdiArea(QMdiArea):
|
||||
# Use the empty icon as window icon, so the pygadmin logo is not in the window title bar of every editor widget.
|
||||
self.setWindowIcon(icon)
|
||||
|
||||
self.init_open_files()
|
||||
|
||||
def init_open_files(self):
|
||||
"""
|
||||
Get the list of previous opened files of the editor out of the file manager and load them in new editor widgets.
|
||||
"""
|
||||
|
||||
if global_app_configurator.get_single_configuration("open_previous_files") is False:
|
||||
return
|
||||
|
||||
# Get the files for opening.
|
||||
files_to_open = global_file_manager.load_open_file_list()
|
||||
# Delete all current files in the global file manager. They will be added after their load in the editor widget.
|
||||
global_file_manager.delete_all_files()
|
||||
|
||||
# Add a widget for every file.
|
||||
for file in files_to_open:
|
||||
# Get a new editor.
|
||||
new_editor = self.generate_editor_tab()
|
||||
# Load the file in the editor.
|
||||
new_editor.load_statement_with_file_name(file)
|
||||
|
||||
def generate_editor_tab(self):
|
||||
"""
|
||||
Generate a new editor widget as sub window of the MdiArea and connect a signal for a change table/view for an
|
||||
|
Loading…
x
Reference in New Issue
Block a user