"""
This module defines the ModelApps class, which serves as a central control unit for managing various functionalities within an application.
The ModelApps class integrates several utility classes and modules for tasks such as image processing, camera streaming,
configuration management, and more.
"""
import os
import cv2
import numpy as np
from src.models.theme import STYLE_DARK_MODE, STYLE_LIGHT_MODE
from .moilutils import MoilUtils
from .model_stylesheet import ModelStylesheet
from .thread_screen_capture import UpdaterImage
from .model_moildev import ModelMoildev
from .model_view_anypoint import ModelViewAnypoint
from .model_view_panorama import ModelViewPanorama
from .model_view_recenter import ModelRecenter
from .model_github import ModelGitConfig
from .model_zoom_area import ModelZoomArea
from .model_video_control import ModelVideoConfig
from .model_log_activity import ModelLog
from .model_save_image import ModelSave
from .model_config import ModelConfigApps
from .model_view_properties import ModelViewProperties
from .model_icon_and_pixmap import GetResourcesIcon
[docs]
class ModelApps(MoilUtils):
"""
ModelApps class serves as the central control unit for managing various functionalities within an application.
Attributes:
_theme (str): The current theme of the application, either 'light' or 'dark'.
_mode_view (str): The current mode of view in the application.
_recenter_mode_view (bool): Flag indicating whether recenter mode is active.
_activity_logger: Logger for logging activities within the application.
_thread_screen_capture: Thread for capturing screen images.
_debug_mode (bool): Flag indicating whether debug mode is active.
_main_config (dict): Main configuration settings for the application.
_recent_project_config (dict): Configuration settings for recent projects.
_recent_project_list (list): List of recent projects.
_image_ori_resize: Resized version of the original image.
_image_ori_used: The version of the original image currently being used.
_image_original: The original image.
_image_recenter: Recentered image.
_source_usable (bool): Flag indicating whether the image source is usable.
Methods:
- __init__: Initializes the ModelApps class.
- open_streaming_camera: Opens a streaming camera with the specified media source.
- load_video_source: Loads a video source.
- load_image_source: Loads an image source.
- check_if_the_source_is_usable: Checks if the source is usable for processing.
- process_image_ori: Processes the original image based on the rotation angle specified in the main configuration.
- change_resolution_image: Changes the resolution of the image.
- save_main_config_update: Saves the updated main configuration.
"""
def __init__(self):
super().__init__()
self._theme = "light"
self._mode_view = "FisheyeView"
self._recenter_mode_view = False
self._activity_logger = None
self._thread_screen_capture = None
self._debug_mode = False
self._main_config = {}
self._recent_project_config = {}
self._recent_project_list = []
self._image_ori_resize = None
self._image_ori_used = None
self._image_original = None
self._image_recenter = None
self._source_usable = False
self._image_result = None
self._set_image_resize = False
self.model_config = ModelConfigApps(self)
self.main_config = self.model_config.get_config_file()
self.view_properties = ModelViewProperties(self)
self.set_moildev = ModelMoildev(self)
self.view_anypoint = ModelViewAnypoint(self)
self.view_panorama = ModelViewPanorama(self)
self.view_recenter = ModelRecenter(self)
self.view_zoom_area = ModelZoomArea(self)
self.github_config = ModelGitConfig(self)
self.video_config = ModelVideoConfig(self)
self.get_stylesheet = ModelStylesheet(self)
self.saving_media = ModelSave(self)
self.log_activity = ModelLog(self)
self.src_icon = GetResourcesIcon()
self.update_scree_image = UpdaterImage()
self.update_scree_image.worker.main()
self.update_scree_image.thread.start()
@property
def theme_mode(self):
"""
Get the current theme mode. Can be 'light' or 'dark'
"""
try:
return self._theme
except Exception as e:
self.activity_logger.error(f"ModelApps.theme_mode(): Error: {str(e)}")
@theme_mode.setter
def theme_mode(self, value):
"""
Set the theme mode. Must be 'light' or 'dark'.
"""
try:
if value not in ["light", "dark"]:
raise ValueError("Invalid theme. Theme must be 'light' or 'dark'.")
self._theme = value
except Exception as e:
self.activity_logger.error(f"ModelApps.theme_mode(): Error: {str(e)}")
@property
def mode_view(self):
"""
Get the current mode view from the main configuration.
"""
try:
return self.main_config["Recent_view"]
except Exception as e:
self.activity_logger.error(f"ModelApps.mode_view(): Error: {str(e)}")
@mode_view.setter
def mode_view(self, mode):
"""
Set the mode view in the main configuration and save the update.
"""
try:
self.main_config["Recent_view"] = mode
self.save_main_config_update()
except Exception as e:
self.activity_logger.error(f"ModelApps.mode_view(): Error: {str(e)}")
@property
def recenter_mode_view(self):
"""
Get the state of the recenter mode view.
"""
try:
return self._recenter_mode_view
except Exception as e:
self.activity_logger.error(f"ModelApps.recenter_mode_view(): Error: {str(e)}")
@recenter_mode_view.setter
def recenter_mode_view(self, state):
"""
Set the state of the recenter mode view in the main configuration.
"""
try:
self.main_config["Recenter_view"] = state
self._recenter_mode_view = state
except Exception as e:
self.activity_logger.error(f"ModelApps.recenter_mode_view(): Error: {str(e)}")
@property
def active_view_mode(self):
"""
Get the current active view mode from the main configuration.
"""
try:
return self.main_config["Active_view"]
except Exception as e:
self.activity_logger.error(f"ModelApps.active_view_mode(): Error: {str(e)}")
@active_view_mode.setter
def active_view_mode(self, state):
"""
Set the active view mode in the main configuration and save the update.
"""
try:
self.main_config["Active_view"] = state
self.save_main_config_update()
except Exception as e:
self.activity_logger.error(f"ModelApps.active_view_mode(): Error: {str(e)}")
@property
def open_directory_path(self):
"""
Get the open directory path from the main configuration.
"""
try:
return self.main_config["Open_directory"]
except Exception as e:
self.activity_logger.error(f"ModelApps.open_directory_path(): Error: {str(e)}")
@open_directory_path.setter
def open_directory_path(self, value):
"""
Set the open directory path in the main configuration.
"""
try:
self.main_config["Open_directory"] = value
except Exception as e:
self.activity_logger.error(f"ModelApps.open_directory_path(): Error: {str(e)}")
@property
def style_light_mode(self):
"""
Get the style configuration for light mode.
"""
try:
return STYLE_LIGHT_MODE
except Exception as e:
self.activity_logger.error(f"ModelApps.style_light_mode(): Error: {str(e)}")
@property
def style_dark_mode(self):
"""
Get the style configuration for dark mode.
"""
try:
return STYLE_DARK_MODE
except Exception as e:
self.activity_logger.error(f"ModelApps.style_dark_mode(): Error: {str(e)}")
@property
def activity_logger(self):
"""Get the current activity logger."""
try:
return self._activity_logger
except Exception as e:
self.activity_logger.error(f"ModelApps.activity_logger(): Error: {str(e)}")
@activity_logger.setter
def activity_logger(self, logger):
"""
Set the activity logger.
"""
try:
self._activity_logger = logger
except Exception as e:
self.activity_logger.error(f"ModelApps.activity_logger(): Error: {str(e)}")
@property
def debug_mode(self):
"""Get the current state of debug mode."""
try:
return self._debug_mode
except Exception as e:
self.activity_logger.error(f"ModelApps.debug_mode(): Error: {str(e)}")
@debug_mode.setter
def debug_mode(self, value):
"""
Set the state of debug mode.
"""
try:
self._debug_mode = value
except Exception as e:
self.activity_logger.error(f"ModelApps.debug_mode(): Error: {str(e)}")
@property
def image_original(self):
"""
Get the original image.
"""
try:
return self._image_original
except Exception as e:
self.activity_logger.error(f"ModelApps.image_original(): Error: {str(e)}")
@image_original.setter
def image_original(self, value):
"""
Set the original image and update the used image.
"""
try:
self._image_original = value
self._image_ori_used = self._image_original
except Exception as e:
self.activity_logger.error(f"ModelApps.image_original(): Error: {str(e)}")
@property
def image_ori_used(self):
"""
Get the currently used original image. If none, set it to the original image.
"""
try:
if self._image_ori_used is None:
self._image_ori_used = self.image_original
return self._image_ori_used
except Exception as e:
self.activity_logger.error(f"ModelApps.image_ori_used(): Error: {str(e)}")
@image_ori_used.setter
def image_ori_used(self, value):
"""
Set the currently used original image.
"""
try:
self._image_ori_used = value
except Exception as e:
self.activity_logger.error(f"ModelApps.image_ori_used(): Error: {str(e)}")
@property
def image_recenter(self):
"""
Get the current state of image recentering.
"""
try:
return self._image_recenter
except Exception as e:
self.activity_logger.error(f"ModelApps.image_recenter(): Error: {str(e)}")
@image_recenter.setter
def image_recenter(self, value):
"""
Set the state of image recentering.
"""
try:
self._image_recenter = value
except Exception as e:
self.activity_logger.error(f"ModelApps.image_recenter(): Error: {str(e)}")
@property
def image_result(self):
"""
Get the current result image.
"""
try:
return self._image_result
except Exception as e:
self.activity_logger.error(f"ModelApps.image_result(): Error: {str(e)}")
@image_result.setter
def image_result(self, value):
"""
Set the current result image.
"""
try:
self._image_result = value
except Exception as e:
self.activity_logger.error(f"ModelApps.image_result(): Error: {str(e)}")
@property
def set_image_resize(self):
try:
return self._set_image_resize
except Exception as e:
self.activity_logger.error(f"ModelApps.set_image_resize(): Error: {str(e)}")
@set_image_resize.setter
def set_image_resize(self, value):
try:
self._set_image_resize = value
except Exception as e:
self.activity_logger.error(f"ModelApps.set_image_resize(): Error: {str(e)}")
@property
def main_config(self):
"""
Get the main configuration.
"""
try:
return self._main_config
except Exception as e:
self.activity_logger.error(f"ModelApps.main_config(): Error: {str(e)}")
@main_config.setter
def main_config(self, config_file):
"""
Set the main configuration by loading it from a JSON file.
"""
try:
self._main_config = self.model_config.load_json_file(config_file)
except Exception as e:
self.activity_logger.error(f"ModelApps.main_config(): Error: {str(e)}")
@property
def recent_project_list(self):
"""
Get the list of recent projects.
"""
try:
return self._recent_project_list
except Exception as e:
self.activity_logger.error(f"ModelApps.recent_project_list(): Error: {str(e)}")
@recent_project_list.setter
def recent_project_list(self, value):
"""Set the list of recent projects."""
try:
self._recent_project_list = value
except Exception as e:
self.activity_logger.error(f"ModelApps.recent_project_list(): Error: {str(e)}")
@property
def source_usable(self):
"""
Get the current state of source usability.
"""
try:
return self._source_usable
except Exception as e:
self.activity_logger.error(f"ModelApps.source_usable(): Error: {str(e)}")
[docs]
def save_main_config_update(self):
"""
Save the updated main configuration.
\
Return:
None
"""
try:
if self.debug_mode:
self.activity_logger.info("ModelApps: save_main_config_update(), "
"Save main config that updated")
self.model_config.save_update_main_config(self.main_config)
except Exception as e:
self.activity_logger.error(f"ModelApps.save_main_config_update(): Error: {str(e)}")
[docs]
def open_streaming_camera(self, media_source):
"""
Open a streaming camera with the specified media source.
The media source can be a camera URL or an integer representing the camera index.
Arg:
media_source (str or int): The media source for opening the camera.
Return:
cv2.VideoCapture: The capture object for the streaming camera.
"""
try:
if self.debug_mode:
self.activity_logger.info(
"ModelApps: open_media(), Opening media with media_source: {}".format(media_source))
if os.name == 'nt':
if isinstance(media_source, int):
cap = cv2.VideoCapture(media_source, cv2.CAP_DSHOW)
else:
cap = cv2.VideoCapture(media_source)
else:
cap = cv2.VideoCapture(media_source)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, self.set_moildev.moildev_main.image_width)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.set_moildev.moildev_main.image_height)
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter.fourcc('M', 'J', 'P', 'G'))
return cap
except Exception as e:
self.activity_logger.error(f"ModelApps.open_streaming_camera(): Error: {str(e)}")
[docs]
def load_video_source(self, media_source):
"""
Load a video source.
Open a video capture from the specified media source.
The media source can be a file path, URL, or an integer representing the camera index.
Arg:
media_source (str or int): The media source for opening the video capture.
Return:
cv2.VideoCapture: The capture object for the video source.
"""
try:
return cv2.VideoCapture(media_source)
except Exception as e:
self.activity_logger.error(f"ModelApps.load_video_source(): Error: {str(e)}")
[docs]
def load_image_source(self, media_source):
"""
Load an image source.
Reads an image from the specified media source.
The media source should be a file path to the image.
Arg:
media_source (str): The file path of the image to load.
Return:
numpy.ndarray: The loaded image.
"""
try:
return cv2.imread(media_source)
except Exception as e:
self.activity_logger.error(f"ModelApps.load_image_source(): Error: {str(e)}")
[docs]
def check_if_the_source_is_usable(self, source):
"""
Check if the source is usable.
Checks if the provided source (either a cv2.VideoCapture object or a numpy array) is usable for processing.
If the width or height is less than or equal to 640x480, the source is considered not usable.
Arg:
source (cv2.VideoCapture or numpy.ndarray): The source to check.
Return:
None
"""
try:
if isinstance(source, cv2.VideoCapture):
width = source.get(cv2.CAP_PROP_FRAME_WIDTH) # float `width`
height = source.get(cv2.CAP_PROP_FRAME_HEIGHT)
elif isinstance(source, np.ndarray):
width = source.shape[0]
height = source.shape[1]
else:
print("Unsupported media type.")
if width <= 640 or height <= 480:
self._source_usable = False
else:
self._source_usable = True
except Exception as e:
self.activity_logger.error(f"ModelApps.check_if_the_source_is_usable(): Error: {str(e)}")
[docs]
def process_image_ori(self):
"""
Process the original image.
Processes the original image based on the rotation angle specified in the main configuration.
If the image has been resized, the resized version is used; otherwise, the original image is used.
Return:
numpy.ndarray: The processed original image.
"""
try:
angle = self.main_config['Image_original']['rotate']
if self._image_ori_resize is None:
image_ori_used = self.rotate_image(self._image_original.copy(), angle)
else:
image_ori_used = self.rotate_image(self._image_ori_resize.copy(), angle)
return image_ori_used
except Exception as e:
self.activity_logger.error(f"ModelApps.process_image_ori(): Error: {str(e)}")
[docs]
def change_resolution_image(self):
"""
Change the resolution of the image.
Resizes the original image to the resolution specified in the main configuration.
Updates the resized image attribute (_image_ori_resize) and checks if the MoilDev instance needs to be recreated.
Return:
None
"""
try:
self.resize_image_resolution()
if self._image_ori_resize.shape[1] == self.image_original.shape[1]:
if self.image_original.shape[1] != self.set_moildev.moildev_main.image_width:
self.set_moildev.create_moildev(True)
self._set_image_resize = False
self._image_ori_resize = None
else:
self.set_moildev.create_moildev(True)
self._set_image_resize = True
except Exception as e:
self.activity_logger.error(f"ModelApps.change_resolution_image(): Error: {str(e)}")
[docs]
def resize_image_resolution(self):
try:
image = cv2.resize(self.image_original.copy(),
self.set_moildev.resolution_option[self.set_moildev.current_resolution_index],
cv2.INTER_AREA)
self._image_ori_resize = image
self._image_ori_used = image
except Exception as e:
self.activity_logger.error(f"ModelApps.resize_image_resolution(): Error: {str(e)}")