import os
import re
import shutil
import sys
import cv2
import yaml
from PyQt6 import QtWidgets, QtGui, QtCore
from pathlib import Path
from .control_plugin_collection import PluginCollection
[docs]
class PluginManager(object):
def __init__(self, main_control):
"""Initializes the PluginManager object.
Args:
main_control: A reference to the main control object.
Attributes:
main_control: A reference to the main control object.
plugin: A PluginCollection object that manages the plugins.
apps_activated: A list of booleans indicating whether each plugin is activated.
index: An integer representing the index of the currently selected plugin.
"""
super().__init__()
self.main_control = main_control
self.plugin = PluginCollection("plugins")
self.apps_activated = None
self.index = None
self.widget = None
self.text_title = None
self.init_available_plugin()
self.init_cached_file()
self.connect_to_event()
[docs]
def connect_to_event(self):
"""
Connects the main window buttons to their corresponding actions.
This method is responsible for setting up event connections between the buttons in the main window and their corresponding actions. Specifically, the following connections are established:
- When the "Add Plugins" button is clicked, the `install_new_plugin` method is called.
- When the "Delete Plugins" button is clicked, the `action_delete_apps` method is called.
- When the "Close" button is clicked, the `main_control.back_to_home` method is called.
- When the "Help" button is clicked, the `help_menu_plugin` method is called.
Note: You should replace the description of the actions to match their actual functionality.
"""
# self.main_control.ui.add_plugins_button.clicked.connect(self.install_new_plugin)
self.main_control.ui.delete_plugins_button.clicked.connect(self.action_delete_apps)
self.main_control.ui.close_plugin_button.clicked.connect(self.main_control.back_to_home)
self.main_control.ui.help_plugins_button.clicked.connect(self.help_menu_plugin)
self.main_control.ui.open_in_new_window_plugins.clicked.connect(self.open_plugins_in_new_window)
[docs]
def init_cached_file(self):
file_path = os.path.join(os.getcwd(), "models", "cached", "plugin_cached.yaml")
if not os.path.exists(file_path):
config = {"plugin_run": None}
with open(file_path, "w") as outfile:
yaml.dump(config, outfile)
[docs]
def init_available_plugin(self):
"""Initializes the available plugins by adding buttons for each one to the UI.
Clears any existing buttons from the UI layout, then iterates over the list of available plugins
and adds a button for each one. The icon for each plugin is retrieved using the `get_icon_` method
of the `PluginCollection` object.
Returns:
None
"""
self.main_control.ui.add_plugins_button.hide()
for i in range(self.main_control.ui.layout_application.count()):
self.main_control.ui.layout_application.itemAt(i).widget().close()
for i in range(len(self.plugin.name_application)):
icon = self.plugin.get_icon_(i)
button = self.add_btn_apps_plugin(icon, self.plugin.name_application[i])
button.clicked.connect(self.open_plugin_apps)
self.main_control.ui.layout_application.addWidget(button)
[docs]
def install_new_plugin(self):
"""Opens a file dialog for selecting a new plugin folder and installs it into the plugin store.
Args:
None
Returns:
None
Raises:
None
"""
options = QtWidgets.QFileDialog.Option.DontUseNativeDialog
dir_plugin = QtWidgets.QFileDialog.getExistingDirectory(None,
'Select Application Folder', "../plugin_store", options)
if dir_plugin:
original = dir_plugin
name_plug = os.path.basename(os.path.normpath(original))
path_file = os.path.dirname(os.path.realpath(__file__))
target = path_file + '/plugins/'
name_exist = Path(target + name_plug)
if name_exist.exists():
QtWidgets.QMessageBox.warning(None, "Warning !!", "Plugins already exist!!")
else:
listApp = self.plugin.name_application
self.main_control.model.copy_directory(original, target)
self.plugin.reload_plugins()
newList = self.plugin.name_application
name = [item for item in newList if item not in listApp]
def listToString(listIn):
return " ".join(listIn)
index = newList.index(listToString(name))
icon = self.plugin.get_icon_(index)
button = self.add_btn_apps_plugin(icon, self.plugin.name_application[index])
button.clicked.connect(self.open_plugin_apps)
self.main_control.ui.layout_application.addWidget(button)
self.pop_up_message_box("Plugins was successfully added!!")
[docs]
def add_plugin_by_github(self, name):
path_file = os.path.dirname(os.path.realpath(__file__))
target = path_file + '/plugins/'
name_exist = Path(target + name)
if name_exist.exists():
QtWidgets.QMessageBox.warning(None, "Warning !!", "Plugins already exist!!")
return False
else:
return True
[docs]
def refresh_the_plugin_available(self):
listApp = self.plugin.name_application
self.plugin.reload_plugins()
newList = self.plugin.name_application
name = [item for item in newList if item not in listApp]
def listToString(listIn):
return " ".join(listIn)
index = newList.index(listToString(name))
icon = self.plugin.get_icon_(index)
button = self.add_btn_apps_plugin(icon, self.plugin.name_application[index])
button.clicked.connect(self.open_plugin_apps)
self.main_control.ui.layout_application.addWidget(button)
self.pop_up_message_box("Plugins was successfully added!!")
[docs]
def open_pinned_plugin(self, index):
try:
self.index = index
self.main_control.ui.delete_plugins_button.show()
self.main_control.ui.close_plugin_button.show()
for i in range(self.main_control.ui.layout_plugin.count()):
self.main_control.ui.layout_plugin.itemAt(i).widget().close()
self.widget = self.plugin.get_widget(self.index, self.main_control.model)
self.main_control.ui.layout_plugin.addWidget(self.widget)
self.main_control.ui.widget_container_content.setCurrentIndex(1)
self.main_control.ui.frame_btn_moilapp.hide()
self.main_control.ui.frame_button_view.hide()
self.main_control.ui.label_plugin_name.show()
self.main_control.ui.open_in_new_window_plugins.show()
self.text_title = ' '.join(''.join(sent) for sent in re.findall('.[^A-Z]*',
self.plugin.name_application[self.index]))
if self.text_title is not None or self.text_title != "":
self.main_control.ui.label_plugin_name.setText(self.text_title)
self.apps_activated = self.plugin.name_application[self.index]
except:
file_path = os.path.join(os.getcwd(), "models", "cached", "plugin_cached.yaml")
with open(file_path, "r") as file:
config = yaml.safe_load(file)
config["plugin_run"] = None
with open(file_path, "w") as outfile:
yaml.dump(config, outfile, default_flow_style=False)
self.main_control.ui.delete_plugins_button.hide()
self.main_control.ui.label_plugin_name.hide()
self.main_control.ui.close_plugin_button.hide()
self.main_control.ui.open_in_new_window_plugins.hide()
self.index = None
[docs]
def open_plugin_apps(self):
"""
Opens the selected plugin application and displays its widget in the plugin layout.
Raises:
IndexError: If the selected plugin index is not found in `self.plugin.name_application`.
Side Effects:
- Sets `self.index` to the index of the selected plugin.
- Shows the "Delete Plugins" and "Close Plugin" buttons in the main control UI.
- Closes all widgets in the plugin layout before adding the selected plugin's widget.
- Changes the current widget container to the plugin layout.
- Hides the "MOIL App" and "View" buttons in the main control UI.
- Sets `self.apps_activated` to the selected plugin's name.
"""
button = self.main_control.sender()
# button.setStyleSheet("background-color: rgb(25,25,25)")
index = self.plugin.name_application.index(button.objectName())
if index != self.index:
self.index = self.plugin.name_application.index(button.objectName())
self.main_control.ui.delete_plugins_button.show()
self.main_control.ui.close_plugin_button.show()
self.main_control.ui.add_plugins_button.hide()
for i in range(self.main_control.ui.layout_plugin.count()):
self.main_control.ui.layout_plugin.itemAt(i).widget().close()
if self.plugin.set_always_pop_up(self.index):
self.open_plugins_in_new_window()
else:
self.widget = self.plugin.get_widget(self.index, self.main_control.model)
self.main_control.ui.layout_plugin.addWidget(self.widget)
self.main_control.ui.widget_container_content.setCurrentIndex(1)
self.main_control.ui.frame_btn_moilapp.hide()
self.main_control.ui.frame_button_view.hide()
self.main_control.ui.label_plugin_name.show()
self.main_control.ui.open_in_new_window_plugins.show()
self.text_title = ' '.join(''.join(sent) for sent in re.findall('.[^A-Z]*', button.objectName()))
if self.text_title is not None or self.text_title != "":
self.main_control.ui.label_plugin_name.setText(self.text_title)
self.apps_activated = button.objectName()
file_path = os.path.join(os.getcwd(), "models", "cached", "plugin_cached.yaml")
with open(file_path, "r") as file:
config = yaml.safe_load(file)
config["plugin_run"] = self.index
with open(file_path, "w") as outfile:
yaml.dump(config, outfile, default_flow_style=False)
else:
self.widget.raise_()
if self.widget.isMinimized():
self.widget.showMaximized()
QtWidgets.QMessageBox.information(None, "Information", "Plugins already opened!!")
[docs]
def open_plugins_in_new_window(self):
self.main_control.ui.add_plugins_button.show()
self.main_control.ui.stackedWidget_2.setCurrentIndex(0)
for i in range(self.main_control.ui.layout_plugin.count()):
self.main_control.ui.layout_plugin.itemAt(i).widget().close()
self.widget = self.plugin.get_widget(self.index, self.main_control.model)
def close_event(event):
msgBox = QtWidgets.QMessageBox(None)
reply = msgBox.question(None, 'Quit', 'Are you sure you want to quit?',
QtWidgets.QMessageBox.StandardButton.Yes |
QtWidgets.QMessageBox.StandardButton.No,
QtWidgets.QMessageBox.StandardButton.No)
if reply == QtWidgets.QMessageBox.StandardButton.Yes:
event.accept()
self.index = None
else:
event.ignore()
# def destroy_window():
# reply = QtWidgets.QMessageBox.question(self.widget, 'Quit', 'Are you sure you want to quit?',
# QtWidgets.QMessageBox.StandardButton.Yes |
# QtWidgets.QMessageBox.StandardButton.No,
# QtWidgets.QMessageBox.StandardButton.No)
# if reply == QtWidgets.QMessageBox.StandardButton.Yes:
# self.widget.close()
# self.index = None
# shortcut = QtGui.QShortcut(QtCore.Qt.Key.Key_Escape, self.widget)
# shortcut.activated.connect(close_event)
self.widget.closeEvent = close_event
self.widget.setWindowTitle(self.text_title)
self.widget.show()
self.main_control.ui.widget_container_content.setCurrentIndex(0)
self.main_control.ui.frame_btn_moilapp.show()
self.main_control.ui.frame_button_view.show()
self.main_control.ui.delete_plugins_button.hide()
self.main_control.ui.label_plugin_name.hide()
self.main_control.ui.close_plugin_button.hide()
self.main_control.ui.open_in_new_window_plugins.hide()
[docs]
def add_btn_apps_plugin(self, icon_, name):
"""Create and return a QPushButton widget with an icon and a name.
Args:
icon_: A string specifying the path of the icon file.
name: A string specifying the name of the plugin.
Returns:
A QPushButton widget with the specified icon and name.
"""
button = QtWidgets.QPushButton()
button.setObjectName(name)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(button.sizePolicy().hasHeightForWidth())
button.setSizePolicy(sizePolicy)
button.setMinimumSize(QtCore.QSize(40, 25))
button.setMaximumSize(QtCore.QSize(35, 16777215))
text_title = ' '.join(''.join(sent) for sent in re.findall('.[^A-Z]*', name))
button.setToolTip(text_title)
button.setStatusTip(text_title)
button.setIconSize(QtCore.QSize(30, 30))
if icon_ is not None:
if self.main_control.model.theme == "light":
icon = QtGui.QIcon(icon_)
button.setIcon(icon)
else:
# auto generate invert color image under developing
icon = QtGui.QIcon(icon_)
button.setIcon(icon)
return button
[docs]
def action_delete_apps(self):
"""
Deletes the currently activated plugin application.
Args:
None
Returns:
None
Raises:
None
"""
index = self.plugin.name_application.index(self.apps_activated)
self.delete_apps(index)
[docs]
def delete_apps(self, index):
"""Delete a plugin application from the system.
Args:
index (int): The index of the application to be deleted in the list of available plugins.
Returns:
None
Raises:
None
The function prompts the user with a confirmation message, and if the user confirms, deletes the plugin
application from the system. The function then reloads the list of available plugins, initializes the available
plugin UI, and displays a success message.
"""
name = self.plugin.name_application[index]
path = self.plugin.path_folder[index]
path = path.split(".")[1]
path_file = os.path.dirname(os.path.realpath(__file__))
path = path_file + '/plugins/' + path
reply = QtWidgets.QMessageBox.question(None, 'Message',
"Are you sure want to delete \n" +
name + " application ?\n",
QtWidgets.QMessageBox.StandardButton.Yes |
QtWidgets.QMessageBox.StandardButton.No,
QtWidgets.QMessageBox.StandardButton.No)
if reply == QtWidgets.QMessageBox.StandardButton.Yes:
shutil.rmtree(path, ignore_errors=True)
self.plugin.reload_plugins()
self.init_available_plugin()
self.pop_up_message_box("Plugins was successfully deleted !!")
self.main_control.back_to_home()
[docs]
@classmethod
def pop_up_message_box(cls, message=""):
"""Displays a message box with the given message.
Args:
message: The message to display in the message box.
Returns:
None
Raises:
None
"""
msg = QtWidgets.QMessageBox()
msg.setIcon(QtWidgets.QMessageBox.Icon.Information)
msg.setStyleSheet("font-family: Segoe UI; font-size:14px;")
msg.setWindowTitle("Information")
# setting message for Message Box
msg.setText("Information !! \n\n" + message)
msg.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok)
msg.show()
def close_msg():
msg.done(0)
QtCore.QTimer.singleShot(6000, close_msg)