pygadmin/pygadmin/widgets/command_history.py
2020-11-18 11:13:49 +01:00

339 lines
16 KiB
Python

import logging
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtWidgets import QDialog, QLabel, QListWidget, QGridLayout, QPushButton, QLineEdit, QMessageBox
from pygadmin.command_history_store import global_command_history_store
from pygadmin.configurator import global_app_configurator
from pygadmin.widgets.widget_icon_adder import IconAdder
class CommandHistoryDialog(QDialog):
"""
Create a dialog for showing the previous executed commands in the editor, which are saved in a history.
"""
# Define a signal for getting the current selected command after a double click.
get_double_click_command = pyqtSignal(str)
def __init__(self):
"""
Initialize the dialog based on the previous commands: If the command list is empty, this dialog does not need to
show the command selection UI.
"""
super().__init__()
self.setModal(True)
# Add the pygadmin icon as window icon.
icon_adder = IconAdder()
icon_adder.add_icon_to_widget(self)
# Get the current command history list.
command_history_list = global_command_history_store.get_command_history_from_yaml_file()
# Check the command history list for emptiness. If the list is empty, shown a UI with an information about the
# empty list.
if not command_history_list:
self.show_empty_ui()
# In this case, the list is not empty and the previous commands can be shown properly.
else:
# Get the reversed list, so the newest command is at the top of the list.
command_history_list.reverse()
self.command_history_list = command_history_list
self.init_ui()
self.init_grid()
def show_empty_ui(self):
"""
Show an empty user interface with only one label for the information about an empty history.
"""
# Create a label for informing about the empty history.
self.empty_label = QLabel("There are currently no commands in your history.")
# Place the label in a grid layout.
grid_layout = QGridLayout(self)
grid_layout.addWidget(self.empty_label, 0, 0)
grid_layout.setSpacing(10)
self.setLayout(grid_layout)
# Set a maximum size and show the widget.
self.setMaximumSize(200, 60)
self.setWindowTitle("Command History Information")
self.show()
def init_ui(self):
"""
Initialize the main user interface.
"""
# Create a list widget for showing the main information about a previous SQL command.
self.history_list_widget = QListWidget()
# Define labels for showing the information about a previous command.
self.command_label = QLabel("SQL Command")
# Set the command text as selectable.
self.command_label.setTextInteractionFlags(Qt.TextSelectableByMouse)
# Define a label for the date and the time.
self.date_time_label = QLabel("Date and Time")
# Define a label for the connection identifier.
self.connection_identifier_label = QLabel("Connection Identifier")
# Add a line edit for changing the command limit.
self.command_limit_line_edit = QLineEdit()
# Load the current limit in the line edit.
self.load_current_command_limit_in_line_edit()
# Create a button for saving a command limit.
self.save_command_limit_button = QPushButton("Save Command Limit")
# Connect the button with the function for checking and saving the command limit with user interaction.
self.save_command_limit_button.clicked.connect(self.check_and_save_command_limit)
# Create a button for deleting the current selected command.
self.delete_selected_command_button = QPushButton("Delete Selected Command")
# Set the button to disabled, because at the beginning, there is no selected item.
self.delete_selected_command_button.setEnabled(False)
# Connect the button with the function for deleting the selected command.
self.delete_selected_command_button.clicked.connect(self.delete_selected_command)
# Create a button for deleting the full history.
self.delete_full_history_button = QPushButton("Delete Full History")
# Connect the button with the function for deleting the history after asking the user, if they really want to
# delete the full history.
self.delete_full_history_button.clicked.connect(self.delete_full_command_history_after_user_question)
# Initialize the command history.
self.init_command_history()
# Connect the signal for changing the selection in the list widget with the method for showing the information
# about a previous command in the pre-defined QLabels.
self.history_list_widget.itemSelectionChanged.connect(self.show_command_information_in_labels)
self.history_list_widget.doubleClicked.connect(self.use_current_command_in_editor)
self.setMaximumSize(1280, 720)
self.setWindowTitle("Command History Information")
self.show()
def init_grid(self):
"""
Initialize a grid layout as layout of the dialog.
"""
grid_layout = QGridLayout(self)
# Set the list widget as one big widget on the left of the application.
grid_layout.addWidget(self.history_list_widget, 0, 0, 5, 1)
# Place the information labels on the right.
grid_layout.addWidget(self.command_label, 0, 1)
grid_layout.addWidget(self.date_time_label, 1, 1)
grid_layout.addWidget(self.connection_identifier_label, 2, 1)
# Add the line edit and the buttons for saving and deleting under the list widget and the components with
# information about the command.
grid_layout.addWidget(self.command_limit_line_edit, 6, 0)
grid_layout.addWidget(self.save_command_limit_button, 6, 1)
grid_layout.addWidget(self.delete_selected_command_button, 7, 1)
grid_layout.addWidget(self.delete_full_history_button, 8, 1)
grid_layout.setSpacing(10)
self.setLayout(grid_layout)
def init_command_history(self):
"""
Initialize the command history in the list widget for the history. Add every previous command to the
"""
# Define a counter for inserting items to the history list widget.
command_history_number_count = 0
# Add every previous command to the list widget.
for command_dictionary in self.command_history_list:
# Create a unique command identifier, based on the command itself and the used time.
command_identifier = "{}\n{}".format(command_dictionary["Command"], command_dictionary["Time"])
# Insert the identifier with the counter as a place in the list widget.
self.history_list_widget.insertItem(command_history_number_count, command_identifier)
# Increment the number count for the next item.
command_history_number_count += 1
def get_command_dictionary_of_current_selected_identifier(self):
"""
Get the command dictionary for the current selected command identifier.
"""
# Check for selected items.
if self.history_list_widget.selectedItems():
# Get the text of the selected item.
selected_item_text = self.history_list_widget.selectedItems()[0].text()
# Use the command history list for finding the match.
for command_dictionary in self.command_history_list:
# Define a command identifier for finding the correct dictionary for showing the information.
command_identifier = "{}\n{}".format(command_dictionary["Command"], command_dictionary["Time"])
# Check for a match.
if command_identifier == selected_item_text:
# Return the command dictionary with a match.
return command_dictionary
def show_command_information_in_labels(self):
"""
Show the information about a selected command in the QLabels.
"""
command_dictionary = self.get_command_dictionary_of_current_selected_identifier()
if command_dictionary is not None:
# Change the text of the labels and the data of the table to the items of the dictionary.
self.command_label.setText(command_dictionary["Command"])
self.date_time_label.setText(command_dictionary["Time"])
self.connection_identifier_label.setText(command_dictionary["Identifier"])
# Enable the button for deleting the selected command.
self.delete_selected_command_button.setEnabled(True)
def load_current_command_limit_in_line_edit(self):
"""
Load the current command limit out of the app configurator into the line edit.
"""
# Get the current command limit out of the configurator.
command_limit = global_app_configurator.get_single_configuration("command_limit")
# Set the current command limit as text in the command limit line edit. The cast to string is used for the
# command limit "None".
self.command_limit_line_edit.setText(str(command_limit))
def save_current_command_limit(self):
"""
Save the current command limit of the line edit in the global app configurator.
"""
# Get the current text of the command line edit.
command_limit_line_edit_text = self.command_limit_line_edit.text()
# If the text is None, then the new command limit is None.
if command_limit_line_edit_text == "None":
new_command_limit = None
# If the text is not None, it is a valid string, which can be casted to an integer value.
else:
new_command_limit = int(command_limit_line_edit_text)
# Save the new command limit.
global_app_configurator.set_single_configuration("command_limit", new_command_limit)
# Save the configuration data in a persistent yaml file.
global_app_configurator.save_configuration_data()
def check_valid_command_limit(self):
"""
Check for a valid command limit in the line edit.
"""
# Get the current user input for the command limit out of the command limit line edit.
command_limit = self.command_limit_line_edit.text()
# If the input for the command limit is None as text, return True, because None is a valid text for showing a
# non-existing limit.
if command_limit == "None":
return True
# Try to cast the command limit to an integer. This works in case of a valid string with a possible cast.
try:
# Get the command limit as integer.
command_limit = int(command_limit)
# The command limit is valid, if it is larger than 0, so True is returned in this case. As a consequence,
# False is returned in the invalid cases.
return command_limit > 0
# Return False for a value error, because in this case, the command limit is invalid.
except ValueError:
return False
def check_and_save_command_limit(self):
"""
Check for a valid command limit in the command limit line edit. If the limit is invalid, show a message to the
user about the invalidity. If the limit is valid, save the limit, inform the command history store and the user
about the success.
"""
if self.check_valid_command_limit() is False:
QMessageBox.critical(self, "Command Limit Error", "The command limit you entered is invalid. Please enter"
"an integer number larger than 0 or None, if you do "
"not wish to use a limit.")
return
# Save the current command limit in the global app configurator.
self.save_current_command_limit()
# Inform the global command history store about a new limit and adjust the saved history to the new command
# limit.
global_command_history_store.adjust_saved_history_to_new_command_limit()
# Save the current list in the yaml file.
global_command_history_store.commit_current_list_to_yaml()
QMessageBox.information(self, "Command Limit Saved", "The new command limit was saved successfully. Potential "
"changes in the command history dialog may apply after "
"a reopening of the dialog.")
def delete_selected_command(self):
"""
Get the command dictionary of the current selected command identifier and delete it in the list widget and in
the global command history.
"""
# Get the command dictionary of the current selected identifier.
command_dictionary = self.get_command_dictionary_of_current_selected_identifier()
# Delete the selected item in the list widget with the method takeItem. The current row is the current selected
# item. An additional or manual deletion process is not necessary, because this is the world of Python with a
# functional garbage collection.
self.history_list_widget.takeItem(self.history_list_widget.currentRow())
# Delete the command in the history.
global_command_history_store.delete_command_from_history(command_dictionary)
# Clear the selection in the list widget.
self.history_list_widget.selectionModel().clear()
# Deactivate the button for deleting the selected command, because there is no selected command.
self.delete_selected_command_button.setEnabled(False)
def delete_full_command_history(self):
"""
Delete the full command history and close the dialog after the deletion.
"""
global_command_history_store.delete_all_commands_from_history()
self.close()
def delete_full_command_history_after_user_question(self):
"""
Delete the full command history after a confirmation by the user.
"""
delete_full_command_history = QMessageBox.question(self, "Delete Full Command History", "Do you really want to"
"delete the full "
"command history?",
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
if delete_full_command_history == QMessageBox.Yes:
self.delete_full_command_history()
def use_current_command_in_editor(self):
"""
Get the current selected command after a double click. Emit a signal, so the command can be used be the editor
in the main window.
"""
# Get the dictionary of the selected command.
selected_command_dictionary = self.get_command_dictionary_of_current_selected_identifier()
# Try to get the selected command in the dictionary with its key.
try:
selected_command = selected_command_dictionary["Command"]
# Emit the signal with the selected command.
self.get_double_click_command.emit(selected_command)
# Close the dialog, because browsing the history is no longer necessary.
self.close()
except KeyError:
logging.critical("The selected dictionary for commands in the command history does not contain the key"
"command: {}".format(selected_command_dictionary))