"""
This module defines the `RecentOpen` class, which represents a PyQt6-based GUI application for managing recent projects and directories.
The `RecentOpen` class provides functionality for opening recent projects, displaying files in selected directories, and managing recent projects in the UI.
It includes methods for handling click events, processing directories, managing recent projects in the UI, and opening recent project images.
Classes:
- RecentOpen: Represents the main application window for managing recent projects and directories.
"""
import glob
import os
from PyQt6 import QtCore, QtWidgets
from PyQt6.QtWidgets import QFrame
[docs]
class RecentOpen(QtWidgets.QMainWindow):
"""
The `RecentOpen` class represents a PyQt6-based GUI application for managing recent projects and directories.
Attributes:
- controller: The controller object responsible for managing the application.
- _model: The model object containing the application's data and logic.
- _stylesheet: The stylesheet object for defining the visual appearance of the UI.
- directory_open_list: A list to store the paths of files in the selected directory.
Methods:
- __init__: Initializes the RecentOpen instance.
- click_open_directory: Handles the click event of the open directory button.
- process_open_directory: Processes the selected directory and displays the files in the UI.
- combo_box_selected: Handles the selection of options from the combo box.
- detect_available_on_recent: Detects if a given path is available in the recent project list.
- click_push_button_open_file_directory: Handles the click event of the push button to open a project.
- clear_item_layout_directory_open: Clears the directory item projects in the user interface.
- get_list_recent_project: Retrieves the list of recent projects and displays them in the UI.
- add_list_recent_project_to_ui: Adds recent projects to the user interface.
- click_push_button_open: Handles the click event of the push button to open a recent project.
- click_push_button_delete: Handles the click event of the push button to delete a recent project.
- clear_item_layout: Clears the items from the layout.
- open_image_from_directory: Opens an image from the selected directory.
- open_recent_image: Opens a recent project image.
- remove_recent_image: Removes a recent project image.
"""
def __init__(self, controller):
QtWidgets.QMainWindow.__init__(self)
self._controller = controller
self._model = controller.model
self._stylesheet = controller.model.get_stylesheet
self._ui_object = controller.ui_object
self.directory_open_list = []
self._ui_object.pushButton_open_directory.clicked.connect(self.click_open_directory)
self._ui_object.pushButton_open_directory.setStyleSheet(self._model.get_stylesheet.pushbutton_stylesheet())
self._ui_object.lineEdit_open_directory.setStyleSheet(self._model.get_stylesheet.line_edit_stylesheet())
[docs]
def click_open_directory(self):
"""
Handle the click event of the open directory button.
"""
dir_path = self._model.select_directory()
if dir_path is None or dir_path == "":
dir_path = self._model.open_directory_path
else:
self._model.open_directory_path = dir_path
self._ui_object.lineEdit_open_directory.setText(dir_path)
self.process_open_directory()
[docs]
def process_open_directory(self):
"""
Process the selected directory and display the files in the UI.
This method retrieves files with specific extensions from the selected directory,
creates UI elements for each file, and adds them to the layout.
Return:
None
"""
dir_path = self._ui_object.lineEdit_open_directory.text()
if dir_path is None or dir_path == "" or not os.path.exists(dir_path):
dir_path = self._model.open_directory_path
self._ui_object.lineEdit_open_directory.setText(dir_path)
extensions = ["png", "jpg", "jpeg", "avi", 'mp4', 'MOV']
self.directory_open_list = []
self.clear_item_layout_directory_open()
for ext in extensions:
files = glob.glob(f"{dir_path}/*.{ext}")
files = [file.replace("\\", "/") for file in files]
self.directory_open_list.extend(files)
if self.directory_open_list:
for index, path in enumerate(self.directory_open_list):
frame = QFrame()
frame.setFrameShape(QFrame.Shape.Box) # Set the shape of the frame
frame.setLineWidth(50) # Set the width of the frame line
frame.setStyleSheet("QToolTip {\n"
"font: 9pt \"Segoe UI\";\n"
"color: rgb(0, 0, 0);\n"
"background-image: none;\n"
"background-color: rgb(238, 238, 236);\n"
"background-repeat: no-repeat;\n"
"border: none;\n"
"border-left: 5px solid rgb(40, 44, 52);\n"
"text-align: left;\n"
"padding-left: 8px;\n"
"margin: 0px;\n"
"}\n")
vertical_layout_1 = QtWidgets.QVBoxLayout(frame)
vertical_layout_1.setObjectName("vertical_layout_1")
vertical_layout_1.setSpacing(1)
horizontal_layout = QtWidgets.QHBoxLayout()
horizontal_layout.setObjectName("horizontal_layout")
horizontal_layout.setSpacing(1)
vertical_layout_2 = QtWidgets.QVBoxLayout()
vertical_layout_2.setObjectName("vertical_layout_2")
vertical_layout_2.setSpacing(1)
push_button_open = QtWidgets.QPushButton(os.path.basename(str(path)))
push_button_open.setObjectName(str(index))
_, _, theme = self.detect_available_on_recent(str(path))
push_button_open.setStyleSheet(self._stylesheet.stylesheet_button_additional(theme))
push_button_open.clicked.connect(self.click_push_button_open_file_directory)
push_button_open.setMaximumWidth(210) # 120 or 220
push_button_open.setMinimumWidth(210) # 120 or 220
push_button_open.setMinimumHeight(30)
_translate = QtCore.QCoreApplication.translate
try:
push_button_open.setToolTip(_translate("MainWindow", "Path :" + path))
except FileNotFoundError:
print(f"File not found: {path}")
except PermissionError:
print(f"Permission denied: {path}")
vertical_layout_2.addWidget(push_button_open)
horizontal_layout.addLayout(vertical_layout_2)
combo_box = QtWidgets.QComboBox()
combo_box.setObjectName(str(index))
combo_box.setMaximumWidth(100)
combo_box.setMinimumWidth(100)
combo_box.setMinimumHeight(30)
combo_box.addItem("Save")
combo_box.addItem("Save As")
combo_box.addItem("Rename")
combo_box.activated.connect(lambda ind=index: self.combo_box_selected())
vertical_layout_1.addLayout(horizontal_layout)
self._ui_object.verticalLayout_directory_file.addWidget(frame)
[docs]
def combo_box_selected(self):
"""
Handle the selection of options from the combo box.
Retrieves the index of the selected combo box and performs further actions based on the selection.
Return:
None
"""
# sender = self.sender()
# combo_box = self._ui_object.scrollArea_8.findChild(QtWidgets.QComboBox, sender.objectName())
# index = combo_box.objectName()
[docs]
def detect_available_on_recent(self, path):
"""
Detects if the given path is available in the recent project list.
Checks if the provided path matches any of the paths stored in the recent project list.
Arg:
path (str): The path to be checked.
Return:
tuple: - status (bool): True if the path is found in the recent project list, False otherwise.
- index_recent_list (int or None): The index of the matching path in the recent project list, or None if not found.
- theme (str): The theme of the detected path ('install' if found, 'None' otherwise).
"""
status = False
index_recent_list = None
for i, recent_path in enumerate(self._model.recent_project_list):
config = self._model.model_config.load_json_file_project(recent_path)
if path == config["Media_path"]:
status = True
index_recent_list = i
break # Exit the loop once a match is found
if status:
return status, index_recent_list, "install"
return status, index_recent_list, "None" # Moved outside the loop
[docs]
def clear_item_layout_directory_open(self):
"""
Clear the directory item projects in the user interface.
Remove all widgets from the vertical layout containing directory item projects.
Return:
None
"""
if self._model.debug_mode:
self._model.activity_logger.info("RecentOpen: clear_item_layout_directory_open(), "
"OnClick clear directory item project in user interface")
while self._ui_object.verticalLayout_directory_file.count():
item = self._ui_object.verticalLayout_directory_file.takeAt(0)
widget = item.widget()
widget.deleteLater()
[docs]
def get_list_recent_project(self):
"""
Retrieve the list of recent projects and displays them in the UI.
"""
if self._model.debug_mode:
self._model.activity_logger.info("RecentOpen: get_list_recent_project(), "
"Get list of recent project and show it to user interface")
self._model.model_config.read_recent_config_path()
self.process_open_directory()
self.add_list_recent_project_to_ui()
[docs]
def add_list_recent_project_to_ui(self):
"""
Add recent projects to the user interface.
"""
if self._model.debug_mode:
self._model.activity_logger.info("RecentOpen: add_list_recent_project_to_ui(), "
"Add list recent project to user interface")
self.clear_item_layout()
for index, path in enumerate(self._model.recent_project_list):
config = self._model.model_config.load_json_file_project(path)
frame = QFrame()
frame.setFrameShape(QFrame.Shape.Box) # Set the shape of the frame
frame.setLineWidth(50) # Set the width of the frame line
frame.setStyleSheet("QToolTip {\n"
"font: 9pt \"Segoe UI\";\n"
"color: rgb(0, 0, 0);\n"
"background-image: none;\n"
"background-color: rgb(238, 238, 236);\n"
"background-repeat: no-repeat;\n"
"border: none;\n"
"border-left: 5px solid rgb(40, 44, 52);\n"
"text-align: left;\n"
"padding-left: 8px;\n"
"margin: 0px;\n"
"}\n")
vertical_layout_1 = QtWidgets.QVBoxLayout(frame)
vertical_layout_1.setObjectName("vertical_layout_1")
vertical_layout_1.setSpacing(1)
horizontal_layout = QtWidgets.QHBoxLayout()
horizontal_layout.setObjectName("horizontal_layout")
horizontal_layout.setSpacing(1)
vertical_layout_2 = QtWidgets.QVBoxLayout()
vertical_layout_2.setObjectName("vertical_layout_2")
vertical_layout_2.setSpacing(1)
push_button_open = QtWidgets.QPushButton(os.path.basename(str(config["Media_path"])))
push_button_open.setObjectName(str(index))
push_button_open.setStyleSheet(self._stylesheet.stylesheet_button_additional("install"))
push_button_open.clicked.connect(self.click_push_button_open)
push_button_open.setMaximumWidth(180)
push_button_open.setMinimumWidth(180)
push_button_open.setMinimumHeight(30)
_translate = QtCore.QCoreApplication.translate
try:
push_button_open.setToolTip(_translate("MainWindow", f"Path : {config['Media_path']}"))
except KeyError as e:
print(f"KeyError occurred while setting tooltip: {e}")
vertical_layout_2.addWidget(push_button_open)
horizontal_layout.addLayout(vertical_layout_2)
push_button_close = QtWidgets.QPushButton("X")
push_button_close.setObjectName(str(index))
push_button_close.setStyleSheet(self._stylesheet.stylesheet_button_additional("remove"))
push_button_close.clicked.connect(self.click_push_button_delete)
push_button_close.setMaximumWidth(30)
push_button_close.setMinimumWidth(30)
push_button_close.setMinimumHeight(30)
horizontal_layout.addWidget(push_button_close)
vertical_layout_1.addLayout(horizontal_layout)
self._ui_object.verticalLayout_recent_open.addWidget(frame)
[docs]
def clear_item_layout(self):
"""
Clear the items from the layout.
Remove all items from the recent projects layout in the user interface.
"""
if self._model.debug_mode:
self._model.activity_logger.info("RecentOpen: clear_item_layout(), "
"OnClick clear item project in user interface")
while self._ui_object.verticalLayout_recent_open.count():
item = self._ui_object.verticalLayout_recent_open.takeAt(0)
widget = item.widget()
widget.deleteLater()
[docs]
def open_image_from_directory(self, index):
"""
Open an image from the selected directory.
If the image is already part of the recent projects, it opens that project. Otherwise, it sets the selected
image as the main project, updates the model configuration accordingly, and displays the image in the user
interface.
Arg:
index (int): The index of the selected image in the directory.
Return:
bool: True if the operation is successful, False otherwise.
"""
if self._model.debug_mode:
self._model.activity_logger.info("RecentOpen: open_recent_image_directory(), "
"Process open project image from directory image")
status, index_recent, _ = self.detect_available_on_recent(str(self.directory_open_list[index]))
if status:
self.open_recent_image(index_recent)
return True
self._ui_object.checkBox_reverse_view.setChecked(False)
if self.directory_open_list[index].endswith(('.mp4', '.MOV', '.avi')):
parameter_name = self._model.select_parameter_name()
else:
parameter_name = self._model.read_camera_type(self.directory_open_list[index])
if parameter_name is None:
parameter_name = self._model.select_parameter_name()
if parameter_name is None:
return False
self._model.main_config["Media_path"] = self.directory_open_list[index]
self._model.main_config["Cam_type"] = "opencv_usb_cam"
self._model.main_config["Source_type"] = "Load Media"
self._model.main_config["Parameter_name"] = parameter_name
if os.path.exists(self._model.main_config["Media_path"]):
self._controller.update_model_and_create_original_image(self._model.main_config["Source_type"],
self._model.main_config["Cam_type"],
self._model.main_config["Media_path"],
self._model.main_config["Parameter_name"])
else:
self._controller.ctrl_message_box.display_message_box(message="Image is not available, "
"please check again! \n"
f"{self._model.main_config['Media_path']}",
type_mode="warning")
return False
self.add_list_recent_project_to_ui()
return True
[docs]
def open_recent_image(self, index):
"""
Open a recent project image.
Arg:
index (int): The index of the recent project to open.
"""
if self._model.debug_mode:
self._model.activity_logger.info("RecentOpen: open_recent_image(), "
"Process open recent project image")
self._ui_object.checkBox_reverse_view.setChecked(False)
config = self._model.model_config.load_json_file_project(self._model.recent_project_list[index])
self._model.model_config.update_main_config_from_recent(config)
cam_type = config["Cam_type"]
source_type = config["Source_type"]
source_path = config["Media_path"]
parameter_name = config["Parameter_name"]
if os.path.exists(source_path):
self._model.view_properties.rotate_degree = config["Rotate_result"]
self._model.recenter_mode_view = config["Recenter_view"]
self._model.mode_view = config["Recent_view"]
self._model.active_view_mode = config["Active_view"]
self._model.set_image_resize = False
self._controller.timer.stop()
self._controller.ctrl_stylesheet.reset_style(self._ui_object.btn_fisheye_view)
self._controller.ctrl_stylesheet.reset_style(self._ui_object.btn_anypoint_view)
self._controller.ctrl_stylesheet.reset_style(self._ui_object.btn_panorama_view)
self._controller.update_model_and_create_original_image(source_type, cam_type, source_path, parameter_name)
else:
self._controller.ctrl_message_box.display_message_box(message="Image is not available, "
"please check again! \n"
f"{source_path}",
type_mode="warning")
self.add_list_recent_project_to_ui()
[docs]
def remove_recent_image(self, index):
"""
Remove a recent project image.
Arg:
index (int): The index of the recent project to remove.
"""
if self._model.debug_mode:
self._model.activity_logger.info("RecentOpen: remove_recent_image(), "
"Process remove recent project image")
config_name = self._model.recent_project_list[index]
self._model.model_config.remove_json_file_project(config_name)
self._model.model_config.read_recent_config_path()
self.add_list_recent_project_to_ui()
if index == 0 and len(self._model.recent_project_list) != 0:
self.open_recent_image(0)
if len(self._model.recent_project_list) == 0:
self._controller.onclick_clear_user_interface()
self._controller.onclick_button_setting_menu(1)