diff --git a/CloudStorages/__init__.py b/CloudStorages/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/CloudStorages/mega.py b/CloudStorages/mega.py new file mode 100644 index 0000000..f04eab2 --- /dev/null +++ b/CloudStorages/mega.py @@ -0,0 +1,24 @@ +import mega + +class MegaCloud: + def __init__(self): + self.mega = mega.Mega() + self.account = None + + def LoginToAccount(self, email: str, password: str) -> str: + try: + self.account = self.mega.login(email, password) + except Exception as e: + return e + + return "success" + + def GetAccountInfo(self) -> dict(): + quota = self.account.get_quota() + space = self.account.get_storage_space(giga=True) + return {"quota": quota, "space": space} + + def GetFiles(self): + files = self.account.get_files() + return files + diff --git a/README.md b/README.md index e11bac2..f16abbc 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,8 @@ if ~~crossed~~ means left undone - [x] Camera capture - [ ] Create chat tab - [x] Real time camera - - [ ] Create cloud storage sync tab + - [x] Create cloud storage sync tab + - [ ] Create button to add storages - [ ] Poe.api (chatting with bots) - [x] ~~Camera module (using cv2)~~ (reason: currently camera function are not shared between modules, so they are written directly in gui module) @@ -41,6 +42,7 @@ if ~~crossed~~ means left undone - [x] Downloaded face detection and landmark detection models - [ ] Backup dataset and pre-trained modules on mega.nz - [x] Dlib, set up for using cuda +- [ ] Add config file for app (probably ini or json) ## Requirements diff --git a/assets/information.png b/assets/information.png new file mode 100644 index 0000000..5739c77 Binary files /dev/null and b/assets/information.png differ diff --git a/assets/mark.png b/assets/mark.png new file mode 100644 index 0000000..7d6320b Binary files /dev/null and b/assets/mark.png differ diff --git a/assets/mega-icon-logo.png b/assets/mega-icon-logo.png new file mode 100644 index 0000000..21a974f Binary files /dev/null and b/assets/mega-icon-logo.png differ diff --git a/assets/warning.png b/assets/warning.png new file mode 100644 index 0000000..208e18b Binary files /dev/null and b/assets/warning.png differ diff --git a/shared.py b/shared.py index 04ecae4..84d195c 100644 --- a/shared.py +++ b/shared.py @@ -1,7 +1,9 @@ from sys import argv from os import path +from PySide6.QtCore import QSize - +# Paths +assets_folder_path = "assets/" data_folder_path = "emotion_recognition_data/" dataset_folder_path = data_folder_path + "dataset/" pretrained_face_landmarks_predictor_model = data_folder_path + "shape_predictor_68_face_landmarks.dat" @@ -20,3 +22,6 @@ def GetRelativePath(var): # Image drawing settings drawing_color = (0, 255, 0) # Default green drawing_thickness = 1 + +# Gui +icon_size = QSize(40, 40) \ No newline at end of file diff --git a/ui/gui/custom_widgets/abstract_storage_cloud.py b/ui/gui/custom_widgets/abstract_storage_cloud.py new file mode 100644 index 0000000..5341174 --- /dev/null +++ b/ui/gui/custom_widgets/abstract_storage_cloud.py @@ -0,0 +1,59 @@ +from PySide6.QtWidgets import QWidget, QLabel, QVBoxLayout, QHBoxLayout, QFrame +from PySide6.QtGui import QPixmap +from PySide6.QtCore import Qt +from ui.gui.custom_widgets.dark_style_button import DarkStyleButton +from shared import icon_size, assets_folder_path + + +class AbstractStorageWidget(QWidget): + def __init__(self, icon_path: str, storage_class): + super().__init__() + main_layout = QVBoxLayout() + self.setLayout(main_layout) + self.setFixedHeight(85) + + container_widget = QFrame() + container_widget.setStyleSheet("background-color: #272727; border-radius: 10px;") + basic_info = QHBoxLayout(container_widget) + + cloud_storage_icon = QLabel() + cloud_storage_icon.setPixmap(QPixmap(icon_path).scaled(icon_size)) + cloud_storage_icon.setAlignment(Qt.AlignCenter) + + overview_layout = QVBoxLayout() + self.label_status = QLabel("Nothing to do now") + self.label_status.setAlignment(Qt.AlignRight) + + buttons_layout = QHBoxLayout() # start buttons layout + + button_push = DarkStyleButton("Push") + button_push.setToolTip("Pushes folder to the cloud") + button_push.clicked.connect(self.pushPressed) + + button_pull = DarkStyleButton("Pull") + button_pull.setToolTip("Pulls folder from the cloud") + button_pull.clicked.connect(self.pullPressed) + + buttons_layout.addWidget(button_push) + buttons_layout.addWidget(button_pull) # end buttons layout + + overview_layout.addWidget(self.label_status) + overview_layout.addLayout(buttons_layout) + + self.current_status_icon = QLabel() + self.current_status_icon.setPixmap(QPixmap(assets_folder_path + "information.png").scaled(icon_size)) #! IMPLEMENT + self.current_status_icon.setAlignment(Qt.AlignCenter) + + basic_info.addWidget(cloud_storage_icon) + basic_info.addLayout(overview_layout) + basic_info.addWidget(self.current_status_icon) + + main_layout.addWidget(container_widget) + + self.cloud_storage = storage_class + + def pullPressed(self): + print("Button pull pressed") + + def pushPressed(self): + print("Button push pressed") diff --git a/ui/gui/custom_widgets/dark_style_button.py b/ui/gui/custom_widgets/dark_style_button.py new file mode 100644 index 0000000..d5ba674 --- /dev/null +++ b/ui/gui/custom_widgets/dark_style_button.py @@ -0,0 +1,60 @@ +from PySide6.QtWidgets import QPushButton +from PySide6.QtGui import QPainter, QColor, QPen +from PySide6.QtCore import Qt + + +class DarkStyleButton(QPushButton): + def __init__(self, text, parent=None): + super().__init__(text, parent) + self.setStyleSheet("border: none; padding: 5px;") + # Background color of button + self.active_button_color = QColor("#303030") + self.disabled_button_color = QColor("#464646") + self.hovered_button_color = QColor("#000") + self.clicked_button_color = QColor("#464646") + # Flags + self.isButtonClicked = False + + def redrawBackgroundColor(self, painter, bg_color): + # Draw the rounded background + rounded_rect = self.rect().adjusted(1, 1, -1, -1) + painter.setBrush(bg_color) + painter.setPen(QPen(QColor("#FFF"), 1)) + painter.drawRoundedRect(rounded_rect, 10, 10) + + def redrawText(self, painter, align=Qt.AlignCenter, text="None"): + # Draw the text + painter.setPen(QColor("#FFF")) + painter.drawText(self.rect(), align, text) + + def paintEvent(self, event): + painter = QPainter(self) + painter.setRenderHint(QPainter.Antialiasing) + painter.setRenderHint(QPainter.TextAntialiasing) + + # Determine the background color based on the hover state + if self.isButtonClicked: + bg_color = self.disabled_button_color + elif self.underMouse() and self.isEnabled(): + bg_color = self.hovered_button_color + elif not self.isEnabled(): + bg_color = self.disabled_button_color + else: + bg_color = self.active_button_color + + # Draw the rounded background + self.redrawBackgroundColor(painter, bg_color) + # Draw the text + self.redrawText(painter, Qt.AlignCenter, self.text()) + + def mousePressEvent(self, event): + if event.button() == Qt.LeftButton: + self.isButtonClicked = True + self.repaint() + super().mousePressEvent(event) + + def mouseReleaseEvent(self, event): + if event.button() == Qt.LeftButton: + self.isButtonClicked = False + self.repaint() + super().mouseReleaseEvent(event) diff --git a/ui/gui/mainwindow.py b/ui/gui/mainwindow.py index 054e235..9c017fe 100644 --- a/ui/gui/mainwindow.py +++ b/ui/gui/mainwindow.py @@ -2,6 +2,7 @@ from ui.gui.tabs.learning_tab import LearningTab from ui.gui.tabs.settings_tab import SettingsTab from ui.gui.tabs.camera_tab import CameraTab +from ui.gui.tabs.storage_tab import StorageTab from DeepLearning.model import EmotionClassificationModel from DeepLearning.settings import pytorch_device @@ -27,15 +28,20 @@ def __init__(self): self.is_model_learning = False # Forbids changing model settings # Tabs - self.list_of_tabs = [SettingsTab(self, "Settings"), LearningTab(self, "Learning"), CameraTab(self, "Camera")] - for tab in self.list_of_tabs: + self.list_of_tabs = [ + SettingsTab(self, "Settings"), + StorageTab(self, "Storages"), + LearningTab(self, "Learning"), + CameraTab(self, "Camera"), + ] + for index, tab in enumerate(self.list_of_tabs): self.mainTabWidget.addTab(tab, tab.tab_name) def TabChanged(self, index): # TODO: remove hardcode if self.list_of_tabs[index].tab_name != "Camera": - if self.list_of_tabs[2].capture.isOpened(): - self.list_of_tabs[2].capture.release() + if self.list_of_tabs[3].capture.isOpened(): + self.list_of_tabs[3].capture.release() self.list_of_tabs[index].UserSelectedTab() diff --git a/ui/gui/tabs/learning_tab.py b/ui/gui/tabs/learning_tab.py index 7953540..5ec869c 100644 --- a/ui/gui/tabs/learning_tab.py +++ b/ui/gui/tabs/learning_tab.py @@ -1,19 +1,11 @@ -from PySide6.QtWidgets import ( - QLabel, - QVBoxLayout, - QHBoxLayout, - QPushButton, - QFormLayout, - QSizePolicy, - QHeaderView, - QAbstractItemView, -) +from PySide6.QtWidgets import QLabel, QVBoxLayout, QHBoxLayout, QFormLayout, QSizePolicy, QHeaderView, QAbstractItemView from PySide6.QtCore import QThreadPool, Slot, Qt from ui.gui.tabs.abstract_tab import AbstractTabWidget from ui.gui.workers.learning_worker import LearningWorker from ui.gui.custom_widgets.learning_statistics_table import LearningStatisticsTable +from ui.gui.custom_widgets.dark_style_button import DarkStyleButton from DeepLearning.dataset_parser import DatasetParser -from shared import dataset_folder +from shared import dataset_folder_path import pyqtgraph as pg @@ -32,11 +24,11 @@ def __init__(self, ParentClass, tab_name): self.label_model_train_status.setAlignment(Qt.AlignCenter) self.label_model_train_status.setStyleSheet("font-weight: bold; font-size: 16px") - self.button_start_model_train = QPushButton("Start training") + self.button_start_model_train = DarkStyleButton("Start training") self.button_start_model_train.setEnabled(False) self.button_start_model_train.clicked.connect(self.UserPressedStartButton) - self.button_stop_model_train = QPushButton("Stop training") + self.button_stop_model_train = DarkStyleButton("Stop training") self.button_stop_model_train.setEnabled(False) self.button_stop_model_train.clicked.connect(self.UserPressedStopButton) @@ -48,7 +40,7 @@ def __init__(self, ParentClass, tab_name): self.main_vertical_layout.addLayout(buttons_start_stop_layout) # Statistics epoch - self.parser = DatasetParser(dataset_folder) + self.parser = DatasetParser(dataset_folder_path) if not self.parser.LoadDatasetIntoRam() == 0: print("UNHANDLED ERROR") # TODO exit(1) diff --git a/ui/gui/tabs/settings_tab.py b/ui/gui/tabs/settings_tab.py index 9349ac2..69c2002 100644 --- a/ui/gui/tabs/settings_tab.py +++ b/ui/gui/tabs/settings_tab.py @@ -1,6 +1,7 @@ -from PySide6.QtWidgets import QFileDialog, QMessageBox, QPushButton, QVBoxLayout, QHBoxLayout, QLabel, QTextBrowser +from PySide6.QtWidgets import QFileDialog, QMessageBox, QVBoxLayout, QHBoxLayout, QLabel, QTextBrowser from PySide6.QtCore import QThreadPool from ui.gui.tabs.abstract_tab import AbstractTabWidget +from ui.gui.custom_widgets.dark_style_button import DarkStyleButton from torch import __version__ as pytorch_version from DeepLearning.settings import pytorch_device from shared import pretrained_emotion_recognition_model, GetRelativePath @@ -24,9 +25,9 @@ def __init__(self, ParentClass, tab_name): self.ParentClass.emotion_classification_model.LoadModel(PretrainedModelRelativePath) self.SetLoadModelStatus(True) - self.button_set_model_path = QPushButton("Load pretrained model") + self.button_set_model_path = DarkStyleButton("Load pretrained model") self.button_set_model_path.clicked.connect(self.OnLoadModelButtonClicked) - self.button_create_new_model = QPushButton("Create new model") + self.button_create_new_model = DarkStyleButton("Create new model") self.button_create_new_model.clicked.connect(self.OnCreateModelButtonClicked) layout_model_statue = QHBoxLayout() diff --git a/ui/gui/tabs/storage_tab.py b/ui/gui/tabs/storage_tab.py new file mode 100644 index 0000000..7ce53a8 --- /dev/null +++ b/ui/gui/tabs/storage_tab.py @@ -0,0 +1,35 @@ +from ui.gui.tabs.abstract_tab import AbstractTabWidget +from ui.gui.custom_widgets.abstract_storage_cloud import AbstractStorageWidget +from PySide6.QtWidgets import QVBoxLayout, QLabel, QScrollArea, QWidget, QSpacerItem, QSizePolicy +from PySide6.QtCore import Qt +from shared import assets_folder_path +from CloudStorages.mega import MegaCloud + + +class StorageTab(AbstractTabWidget): + def __init__(self, ParentClass, tab_name): + super().__init__(ParentClass, tab_name) + main_layout = QVBoxLayout() + self.setLayout(main_layout) + + scroll_area = QScrollArea() + main_layout.addWidget(scroll_area) + scroll_area.setWidgetResizable(True) + + content_widget = QWidget() + scroll_area.setWidget(content_widget) + + self.content_layout = QVBoxLayout(content_widget) + + self.status_label = QLabel() + self.status_label.setText("Nothing to do now") + self.status_label.setAlignment(Qt.AlignCenter) + self.status_label.setStyleSheet("font-size: 16px; font-weight: bold") + + self.content_layout.addWidget(self.status_label) + self.content_layout.addWidget(AbstractStorageWidget(assets_folder_path + "mega-icon-logo.png", MegaCloud())) + verticalSpacer = QSpacerItem(1, self.height(), QSizePolicy.Minimum, QSizePolicy.Expanding) + self.content_layout.addSpacerItem(verticalSpacer) + + def UserSelectedTab(self) -> None: + pass