From a7a2831a1b048dd0e68229c26126c5bfbe61d5fb Mon Sep 17 00:00:00 2001 From: raik199x Date: Sat, 13 Apr 2024 21:56:00 +0300 Subject: [PATCH] Added info about variables type, return type. Make signle code style for everything. zero sum commit. QMessage box bug fix --- CloudStorages/abstract_cloud_storage.py | 4 +- CloudStorages/cloud_storage_interface.py | 14 ++-- CloudStorages/mega.py | 2 +- DeepLearning/dataset_parser.py | 40 ++++----- DeepLearning/model.py | 14 ++-- ImageProcessing/face_detection.py | 8 +- camera.py | 10 +-- config_parser.py | 12 +-- connector.py | 16 ++-- gui/custom_widgets/abstract_storage_cloud.py | 22 ++--- gui/custom_widgets/add_storage_dialog.py | 7 +- gui/custom_widgets/dark_style_button.py | 20 ++--- .../learning_statistics_table.py | 6 +- gui/mainwindow.py | 24 +++--- gui/tabs/abstract_tab.py | 6 +- gui/tabs/camera_tab.py | 42 +++++----- gui/tabs/chat_tab.py | 55 ++++++------ gui/tabs/learning_tab.py | 54 ++++++------ gui/tabs/settings_tab.py | 83 +++++++++---------- gui/tabs/storage_tab.py | 14 ++-- gui/workers/chatter_worker.py | 9 +- gui/workers/learning_worker.py | 62 +++++++------- gui/workers/storage_worker.py | 8 +- poe.py | 14 +--- pyproject.toml | 3 + .../delete_undetected_faces_from_dataset.py | 2 +- tests/main.py | 14 ++-- 27 files changed, 283 insertions(+), 282 deletions(-) diff --git a/CloudStorages/abstract_cloud_storage.py b/CloudStorages/abstract_cloud_storage.py index ac4432a..91afa77 100644 --- a/CloudStorages/abstract_cloud_storage.py +++ b/CloudStorages/abstract_cloud_storage.py @@ -15,8 +15,8 @@ def __init__(self): self.cloud_storage_name = str() # Flags (only one of 2 must be set to true in child class) - self.isAuthViaToken = False - self.isAuthViaCredentials = False + self.is_auth_via_token = False + self.is_auth_via_credentials = False def checkDataFolderExistence(self) -> bool: raise NotImplementedError("checkProjectFolderExistence is not implemented for current class") diff --git a/CloudStorages/cloud_storage_interface.py b/CloudStorages/cloud_storage_interface.py index 09a059b..31e65f5 100644 --- a/CloudStorages/cloud_storage_interface.py +++ b/CloudStorages/cloud_storage_interface.py @@ -18,7 +18,7 @@ def __init__(self): def checkAuthFields(self, cloud_class, email: str, password: str, token: str) -> str: return_result = str() - if cloud_class.isAuthViaCredentials: + if cloud_class.is_auth_via_credentials: return_result = return_result + "Empty email field\n" if len(email) == 0 else "" return_result = return_result + "Empty password field\n" if len(password) == 0 else "" return_result = ( @@ -27,7 +27,7 @@ def checkAuthFields(self, cloud_class, email: str, password: str, token: str) -> else "" ) return_result = return_result + "Ambiguous email format, no @ symbol\n" if email.find("@") == -1 else "" - if cloud_class.isAuthViaToken: + if cloud_class.is_auth_via_token: return_result = return_result + "Empty token\n" if len(token) == 0 else "" if len(return_result) == 0: @@ -43,9 +43,9 @@ def tryLogin(self, cloud_class, email: str, password: str, token: str) -> str: signal.alarm(self.timeout_duration) try: - if cloud_class.isAuthViaCredentials: + if cloud_class.is_auth_via_credentials: result = cloud_class.loginViaCredentials(email, password) - elif cloud_class.isAuthViaToken: + elif cloud_class.is_auth_via_token: result = cloud_class.loginViaToken(token) except TimeoutError: return self.function_timeout_code @@ -55,9 +55,9 @@ def tryLogin(self, cloud_class, email: str, password: str, token: str) -> str: return result def getAuthFields(self, cloud_class, email: str, password: str, token: str) -> dict: - if cloud_class.isAuthViaCredentials: + if cloud_class.is_auth_via_credentials: return {"email": email, "password": password} - if cloud_class.isAuthViaToken: + if cloud_class.is_auth_via_token: return {"token": token} return self.fail_code @@ -65,3 +65,5 @@ def getStorageInstance(self, cloud_provider: str): if cloud_provider == self.mega_cloud_name: return MegaCloud() return self.unknown_code + + diff --git a/CloudStorages/mega.py b/CloudStorages/mega.py index 675ca77..c7cb43c 100644 --- a/CloudStorages/mega.py +++ b/CloudStorages/mega.py @@ -12,7 +12,7 @@ def __init__(self): self.deleted_file_name = "trashed.zip" # Setting up vars - self.isAuthViaCredentials = True + self.is_auth_via_credentials = True self.cloud_storage_name = "Mega" def loginViaToken(self, token) -> str: diff --git a/DeepLearning/dataset_parser.py b/DeepLearning/dataset_parser.py index 2b3da65..dd5c6ae 100644 --- a/DeepLearning/dataset_parser.py +++ b/DeepLearning/dataset_parser.py @@ -13,7 +13,7 @@ def __init__(self, value: list, emotion: str, expected: list): class DatasetParser: def __init__(self, path_to_dataset_folder: str): self.dataset_path = path_to_dataset_folder - self.isParserLoaded = False + self.is_parser_loaded = False # Dataset constants self.angry = "angry" @@ -37,8 +37,8 @@ def __init__(self, path_to_dataset_folder: str): } # fmt: skip # Dataset info - self.forTesting = "test/" - self.forLearning = "train/" + self.for_testing = "test/" + self.for_learning = "train/" # Interactions options self.shuffled = "Shuffled full dataset (default strategy)" @@ -50,11 +50,11 @@ def __init__(self, path_to_dataset_folder: str): self.learning_set_dict = dict() self.testing_set_dict = dict() - def GetAmountOfFilesInFolder(self, path: str) -> int: + def getAmountOfFilesInFolder(self, path: str) -> int: return len(os.listdir(path)) - def ReloadEmotion(self, forWhat: str, emotion_type: str) -> None: - full_path = os.path.join(self.dataset_path, forWhat, emotion_type) + def reloadEmotion(self, for_what: str, emotion_type: str) -> None: + full_path = os.path.join(self.dataset_path, for_what, emotion_type) # Reading emotion current_emotion_list = list() # Preparing list for all images in this emotion files_in_folder = os.listdir(full_path) @@ -62,12 +62,12 @@ def ReloadEmotion(self, forWhat: str, emotion_type: str) -> None: image = cv2.imread(os.path.join(full_path, file), cv2.IMREAD_GRAYSCALE) current_emotion_list.append(image) - if forWhat == self.forLearning: + if for_what == self.for_learning: self.learning_set_dict[emotion_type] = current_emotion_list - elif forWhat == self.forTesting: + elif for_what == self.for_testing: self.testing_set_dict[emotion_type] = current_emotion_list - def ParseFolderWithEmotions(self, folder_with_emotions: str) -> dict(): + def parseFolderWithEmotions(self, folder_with_emotions: str) -> dict(): """Parses a folder of emotions and returns a dictionary of emotions to lists of images. This function iterates over every emotion in the folder, checks if the emotion exists, and if so, @@ -95,7 +95,7 @@ def ParseFolderWithEmotions(self, folder_with_emotions: str) -> dict(): emotions_dict[emotion] = current_emotion_list # saving list to dict return emotions_dict - def LoadDatasetIntoRam(self) -> int: + def loadDatasetIntoRam(self) -> int: """Loads the dataset into RAM. This function checks if the dataset folder exists and is a directory. @@ -113,32 +113,32 @@ def LoadDatasetIntoRam(self) -> int: return -1 # Loading learning set - path_to_learning = os.path.join(self.dataset_path, self.forLearning) + path_to_learning = os.path.join(self.dataset_path, self.for_learning) if os.path.exists(path_to_learning) and os.path.isdir(path_to_learning): - self.learning_set_dict = self.ParseFolderWithEmotions(path_to_learning) + self.learning_set_dict = self.parseFolderWithEmotions(path_to_learning) # Loading testing set - path_to_testing = os.path.join(self.dataset_path, self.forTesting) + path_to_testing = os.path.join(self.dataset_path, self.for_testing) if os.path.exists(path_to_testing) and os.path.isdir(path_to_testing): - self.testing_set_dict = self.ParseFolderWithEmotions(path_to_testing) + self.testing_set_dict = self.parseFolderWithEmotions(path_to_testing) - self.isParserLoaded = True + self.is_parser_loaded = True return 0 - def getDatasetData(self, forWhat: str, needToShuffle: bool) -> DatasetData: - if not self.isParserLoaded: + def getDatasetData(self, for_what: str, need_to_shuffle: bool) -> DatasetData: + if not self.is_parser_loaded: return None - if forWhat != self.forLearning and forWhat != self.forTesting: + if for_what != self.for_learning and for_what != self.for_testing: return None - target_dict = self.learning_set_dict if forWhat == self.forLearning else self.testing_set_dict + target_dict = self.learning_set_dict if for_what == self.for_learning else self.testing_set_dict result_list = list() for emotion in self.emotion_list: for data in target_dict[emotion]: result_list.append(DatasetData(data, emotion, self.emotion_expected_dict[emotion])) - if needToShuffle: + if need_to_shuffle: random.shuffle(result_list) return result_list diff --git a/DeepLearning/model.py b/DeepLearning/model.py index 59bfeb4..77844c2 100644 --- a/DeepLearning/model.py +++ b/DeepLearning/model.py @@ -40,13 +40,13 @@ def __init__(self): ) self.loss_fn = torch.nn.CrossEntropyLoss() # Multi class classification, includes nn.LogSoftmax and nn.NLLLoss - self.optimizer = self.CreateOptimizer() + self.optimizer = self.createOptimizer() - def CreateOptimizer(self): # To stop messing optimizers after load and creation + def createOptimizer(self): # To stop messing optimizers after load and creation # return torch.optim.Adam(self.parameters(), lr=learning_rate) # Most effective Adam and SGD return torch.optim.SGD(self.parameters(), lr=learning_rate) - def TrainEpoch(self, tensor: torch.tensor, expected_tensor: torch.tensor) -> torch.Tensor: + def trainEpoch(self, tensor: torch.tensor, expected_tensor: torch.tensor) -> torch.Tensor: self.train() # 1. Forward pass @@ -66,7 +66,7 @@ def TrainEpoch(self, tensor: torch.tensor, expected_tensor: torch.tensor) -> tor return classification_result - def TestingEpoch(self, tensor: torch.tensor, expected_tensor: torch.tensor) -> dict[str:int, str:int]: + def testingEpoch(self, tensor: torch.tensor, expected_tensor: torch.tensor) -> dict[str:int, str:int]: self.eval() with torch.inference_mode(): classification_result = self(tensor) @@ -78,15 +78,15 @@ def TestingEpoch(self, tensor: torch.tensor, expected_tensor: torch.tensor) -> d "IsPredictedRight": torch.argmax(classification_result) == torch.argmax(expected_tensor), } - def BackupModel(self, folder_path: str, file_name: str) -> None: + def backupModel(self, folder_path: str, file_name: str) -> None: MODEL_PATH = Path(folder_path) MODEL_PATH.mkdir(parents=True, exist_ok=True) torch.save(self.state_dict(), os.path.join(folder_path, file_name)) - def LoadModel(self, path_to_model: str) -> None: + def loadModel(self, path_to_model: str) -> None: self.load_state_dict(torch.load(path_to_model, map_location=torch.device(pytorch_device))) self.eval() - self.optimizer = self.CreateOptimizer() # optimizer must be recreated after load + self.optimizer = self.createOptimizer() # optimizer must be recreated after load def accuracy(self, expected_result: torch.Tensor, model_result: torch.Tensor) -> int: correct = torch.eq(expected_result, model_result).sum().item() diff --git a/ImageProcessing/face_detection.py b/ImageProcessing/face_detection.py index 9df692c..ff2d336 100644 --- a/ImageProcessing/face_detection.py +++ b/ImageProcessing/face_detection.py @@ -9,11 +9,11 @@ def __init__(self, pretrained_face_detector_path: str, image_improvement_level: self.detector = dlib.cnn_face_detection_model_v1(pretrained_face_detector_path) self.image_improvement_level = image_improvement_level - def DetectFaces(self, image: np.array) -> dlib.mmod_rectangles: + def detectFaces(self, image: np.array) -> dlib.mmod_rectangles: gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) return self.detector(gray, self.image_improvement_level) - def ConvertDlibToList( + def convertDlibToList( self, face: dlib.mmod_rectangle, original_image: np.array ) -> list[tuple[int, int], tuple[int, int]]: # extract the starting and ending (x, y)-coordinates of the @@ -34,7 +34,7 @@ def ConvertDlibToList( face_coordinates.append((endX, endY)) return face_coordinates - def DrawFaceBox(self, image: np.array, face: list[tuple[int, int], tuple[int, int]]) -> np.array: + def drawFaceBox(self, image: np.array, face: list[tuple[int, int], tuple[int, int]]) -> np.array: if len(face) != 2: print("face coordinates is messed up") #! use logging system return None @@ -47,7 +47,7 @@ def DrawFaceBox(self, image: np.array, face: list[tuple[int, int], tuple[int, in ) return result_image - def CropFaceBox(self, image: np.array, face: list[tuple[int, int], tuple[int, int]]) -> np.array: + def cropFaceBox(self, image: np.array, face: list[tuple[int, int], tuple[int, int]]) -> np.array: if len(face) != 2: print("face coordinates is messed up") #! use logging system return None diff --git a/camera.py b/camera.py index f5005c7..cea4f41 100644 --- a/camera.py +++ b/camera.py @@ -64,20 +64,20 @@ def listPorts(self) -> list: index += 1 return camera_indexes - def readImage(self, path_to_image) -> np.array: + def readImage(self, path_to_image: str) -> np.array: return cv2.imread(path_to_image) def resizeImage(self, image: np.array, new_size: tuple[int, int]) -> np.array: return cv2.resize(image, new_size) - def getImageRGB(self, image): + def getImageRGB(self, image: np.array) -> np.array: return cv2.cvtColor(image, cv2.COLOR_BGR2RGB) def putText( self, - image, - text, - position, + image: np.array, + text: str, + position: int, font=default_text_font, font_scale=default_text_font_scale, color=default_text_color, diff --git a/config_parser.py b/config_parser.py index 0f47d52..92c20cc 100644 --- a/config_parser.py +++ b/config_parser.py @@ -22,11 +22,11 @@ def __init__(self): self.poe_lat_cookie = "lat_cookie" self.poe_chat_id = "chatId" - def saveConfig(self): + def saveConfig(self) -> None: with open(self.config_name, "w") as config_file: self.config.write(config_file) - def deletePoeCredentials(self): + def deletePoeCredentials(self) -> None: self.config.remove_section(self.poe_index) self.saveConfig() @@ -40,7 +40,7 @@ def removeFieldFromSection(self, section: str, field: str): self.config.remove_option(section, field) self.saveConfig() - def isEntryExist(self, section_name) -> bool: + def isEntryExist(self, section_name: str) -> bool: isExist = True try: self.config[section_name] @@ -59,7 +59,7 @@ def addPoeAccount(self, token_dict: dict) -> None: self.config[self.poe_index] = token_dict self.saveConfig() - def setPoeChatId(self, chatId: str or int): + def setPoeChatId(self, chatId: str or int) -> None: self.config[self.poe_index][self.poe_chat_id] = str(chatId) self.saveConfig() @@ -101,11 +101,11 @@ def getStorageEntrees(self) -> list[list]: if cloud_instance == cloud_interface.unknown_code: continue - if cloud_instance.isAuthViaCredentials: + if cloud_instance.is_auth_via_credentials: result = cloud_instance.loginViaCredentials( self.config[each_section]["email"], self.config[each_section]["password"] ) - elif cloud_instance.isAuthViaToken: + elif cloud_instance.is_auth_via_token: result = cloud_instance.loginViaToken(each_section["token"]) if result == cloud_instance.success_code: diff --git a/connector.py b/connector.py index ef766ed..e267ecd 100644 --- a/connector.py +++ b/connector.py @@ -7,16 +7,16 @@ class Connector: def __init__(self): - self.Parser = DatasetParser("stub") # Empty parser + self.parser = DatasetParser("stub") # Empty parser - def IsGrayscale(self, image: np.array) -> bool: + def isGrayscale(self, image: np.array) -> bool: # Check the number of channels in the image return image.ndim == 2 or (image.ndim == 3 and image.shape[2] == 1) - def ImageIntoTensor(self, original_image: np.array) -> torch.tensor: + def imageIntoTensor(self, original_image: np.array) -> torch.tensor: # Checking if image is already gray image = original_image.copy() - if not self.IsGrayscale(image): + if not self.isGrayscale(image): image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # Now checking if image is suitable size @@ -28,13 +28,13 @@ def ImageIntoTensor(self, original_image: np.array) -> torch.tensor: # Converting to tensor return torch.from_numpy(image).to(torch.float32).to(pytorch_device) - def ClassificationResultIntoEmotion(self, class_result: torch.tensor) -> str: + def classificationResultIntoEmotion(self, class_result: torch.tensor) -> str: result_index = torch.argmax(class_result) - for emotion in self.Parser.emotion_list: - expect = self.Parser.emotion_expected_dict[emotion] + for emotion in self.parser.emotion_list: + expect = self.parser.emotion_expected_dict[emotion] if expect.index(max(expect)) == result_index: return emotion raise IndexError("Tensor list is bigger than expected list, so could not find emotion") - def expectListIntoTensor(self, expect_list) -> torch.tensor: + def expectListIntoTensor(self, expect_list: list) -> torch.tensor: return torch.from_numpy(np.array(expect_list)).to(torch.float32).to(pytorch_device) diff --git a/gui/custom_widgets/abstract_storage_cloud.py b/gui/custom_widgets/abstract_storage_cloud.py index 1b6f73a..ef2fe87 100644 --- a/gui/custom_widgets/abstract_storage_cloud.py +++ b/gui/custom_widgets/abstract_storage_cloud.py @@ -11,9 +11,9 @@ class AbstractStorageSignals(QObject): class AbstractStorageWidget(QWidget): - def __init__(self, ParentClass, storage_name: str, icon_path: str, storage_class): + def __init__(self, parent_class, storage_name: str, icon_path: str, storage_class): super().__init__() - self.ParentClass = ParentClass + self.parent_class = parent_class self.signals = AbstractStorageSignals() self.storage_name = storage_name self.cloud_storage = storage_class @@ -87,37 +87,37 @@ def __init__(self, ParentClass, storage_name: str, icon_path: str, storage_class main_layout.addWidget(container_widget) self.refreshPressed() - def isStorageBusy(self): - if self.ParentClass.is_storage_busy: + def isStorageBusy(self) -> bool: + if self.parent_class.is_storage_busy: QMessageBox.warning(self, "Warning", "Storage is currently busy") return True return False - def removePressed(self): + def removePressed(self) -> None: if self.isStorageBusy(): return self.cloud_storage.removeDataFolder() self.refreshPressed() - def pullPressed(self): + def pullPressed(self) -> None: if self.isStorageBusy(): return worker = StorageWorker(self, StorageWorkerTasks().task_pull) - self.ParentClass.threadpool.start(worker) + self.parent_class.threadpool.start(worker) - def pushPressed(self): + def pushPressed(self) -> None: if self.isStorageBusy(): return worker = StorageWorker(self, StorageWorkerTasks().task_push) - self.ParentClass.threadpool.start(worker) + self.parent_class.threadpool.start(worker) - def refreshPressed(self, force_check=False): + def refreshPressed(self, force_check=False) -> None: if not force_check and self.isStorageBusy(): return result = self.cloud_storage.checkDataFolderExistence() self.label_status.setText(self.folder_found if result else self.folder_not_found) - def logoutPressed(self): + def logoutPressed(self) -> None: if self.isStorageBusy(): return self.signals.delete_widget.emit(self.cloud_storage.cloud_storage_name, self.storage_name) diff --git a/gui/custom_widgets/add_storage_dialog.py b/gui/custom_widgets/add_storage_dialog.py index ec5af07..32d4b06 100644 --- a/gui/custom_widgets/add_storage_dialog.py +++ b/gui/custom_widgets/add_storage_dialog.py @@ -66,6 +66,7 @@ def recreateLineEdits(self) -> None: self.line_edit_email_input.setValidator(self.regular_expression_no_spaces) self.line_edit_password_input = QLineEdit() + self.line_edit_password_input.setEchoMode(QLineEdit.Password) self.line_edit_token_input = QLineEdit() def confirmClicked(self) -> None: @@ -159,14 +160,14 @@ def getCredentialsWidget(self, service_name: str) -> QWidget: self.label_status.setText("Unknown cloud storage provider") self.label_status.setStyleSheet("color: red;") return result_widget - if cloud_storage.isAuthViaCredentials: + if cloud_storage.is_auth_via_credentials: widget_layout.addLayout(self.getCredentialsInput()) - if cloud_storage.isAuthViaToken: + if cloud_storage.is_auth_via_token: widget_layout.addLayout(self.getTokenInput()) return result_widget - def comboBoxItemChanged(self, index) -> None: + def comboBoxItemChanged(self, index: int) -> None: self.label_status.setText("Nothing bad so far") self.credentials_widget.setParent(None) self.credentials_widget.deleteLater() diff --git a/gui/custom_widgets/dark_style_button.py b/gui/custom_widgets/dark_style_button.py index d5ba674..28cc2a8 100644 --- a/gui/custom_widgets/dark_style_button.py +++ b/gui/custom_widgets/dark_style_button.py @@ -1,5 +1,5 @@ from PySide6.QtWidgets import QPushButton -from PySide6.QtGui import QPainter, QColor, QPen +from PySide6.QtGui import QPainter, QColor, QPen, QPaintEvent, QMouseEvent from PySide6.QtCore import Qt @@ -13,27 +13,27 @@ def __init__(self, text, parent=None): self.hovered_button_color = QColor("#000") self.clicked_button_color = QColor("#464646") # Flags - self.isButtonClicked = False + self.is_button_clicked = False - def redrawBackgroundColor(self, painter, bg_color): + def redrawBackgroundColor(self, painter: QPainter, bg_color: QColor) -> None: # 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"): + def redrawText(self, painter: QPainter, align=Qt.AlignCenter, text="None") -> None: # Draw the text painter.setPen(QColor("#FFF")) painter.drawText(self.rect(), align, text) - def paintEvent(self, event): + def paintEvent(self, event: QPaintEvent) -> None: painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.setRenderHint(QPainter.TextAntialiasing) # Determine the background color based on the hover state - if self.isButtonClicked: + if self.is_button_clicked: bg_color = self.disabled_button_color elif self.underMouse() and self.isEnabled(): bg_color = self.hovered_button_color @@ -47,14 +47,14 @@ def paintEvent(self, event): # Draw the text self.redrawText(painter, Qt.AlignCenter, self.text()) - def mousePressEvent(self, event): + def mousePressEvent(self, event: QMouseEvent) -> None: if event.button() == Qt.LeftButton: - self.isButtonClicked = True + self.is_button_clicked = True self.repaint() super().mousePressEvent(event) - def mouseReleaseEvent(self, event): + def mouseReleaseEvent(self, event: QMouseEvent) -> None: if event.button() == Qt.LeftButton: - self.isButtonClicked = False + self.is_button_clicked = False self.repaint() super().mouseReleaseEvent(event) diff --git a/gui/custom_widgets/learning_statistics_table.py b/gui/custom_widgets/learning_statistics_table.py index 1b4f656..c48e53a 100644 --- a/gui/custom_widgets/learning_statistics_table.py +++ b/gui/custom_widgets/learning_statistics_table.py @@ -17,17 +17,17 @@ def __init__(self, parser: DatasetParser): self.reloadTable(parser) self.setShowGrid(True) - def reloadTable(self, parser: DatasetParser): + def reloadTable(self, parser: DatasetParser) -> None: self.amount_of_emotions = len(parser.emotion_list) self.model.setRowCount(self.amount_of_emotions) for num, emotion in enumerate(parser.emotion_list): self.model.setItem(num, 0, QStandardItem(emotion)) self.model.setItem(num, 1, QStandardItem("no iterations")) - content = str(len(parser.testing_set_dict[emotion])) if parser.isParserLoaded else "0" + content = str(len(parser.testing_set_dict[emotion])) if parser.is_parser_loaded else "0" self.model.setItem(num, 2, QStandardItem(content)) self.model.setItem(num, 3, QStandardItem("no iterations")) - def set_data(self, guessed_right: list[int, ...], average_values: list[float, ...]): + def setData(self, guessed_right: list[int, ...], average_values: list[float, ...]) -> None: for num in range(0, self.amount_of_emotions): self.model.setItem(num, 1, QStandardItem(str(guessed_right[num]))) self.model.setItem(num, 3, QStandardItem(str(round(average_values[num], 5)))) diff --git a/gui/mainwindow.py b/gui/mainwindow.py index 1ab7a71..d35cdd4 100644 --- a/gui/mainwindow.py +++ b/gui/mainwindow.py @@ -22,10 +22,10 @@ def __init__(self): self.setWindowTitle("Emotion classification app @raik199x") self.setStyleSheet("background-color: #303030; color: white") - self.mainTabWidget = QTabWidget(self) - self.mainTabWidget.currentChanged.connect(self.TabChanged) + self.main_tab_widget = QTabWidget(self) + self.main_tab_widget.currentChanged.connect(self.tabChanged) self.setMinimumWidth(500) - self.setCentralWidget(self.mainTabWidget) + self.setCentralWidget(self.main_tab_widget) # Pytorch part (must be loaded before tabs) self.emotion_classification_model = self.createEmotionRecognitionModel() @@ -36,7 +36,7 @@ def __init__(self): self.is_storage_busy = False # Forbids using commands in storage tab self.is_model_learning = False # Forbids changing model settings - self.FaceDetector = FaceDetector(pretrained_face_detector, 0) # dlib face detector + self.face_detector = FaceDetector(pretrained_face_detector, 0) # dlib face detector self.parser = self.reloadParser() # dataset parser self.threadpool = QThreadPool() # Configurable threads self.camera_toolkit = CameraFacade() @@ -57,14 +57,14 @@ def __init__(self): break # moved to another for since triggers changedTab signals and throws error of missing var for tab in self.list_of_tabs: - self.mainTabWidget.addTab(tab, tab.tab_name) + self.main_tab_widget.addTab(tab, tab.tab_name) - def TabChanged(self, index): + def tabChanged(self, index: int) -> None: if self.camera_toolkit.isCapturing(): self.camera_toolkit.stopCapture() self.list_of_tabs[self.camera_tab_index].timer.stop() - self.list_of_tabs[index].UserSelectedTab() + self.list_of_tabs[index].userSelectedTab() def reloadParser(self) -> DatasetParser: return DatasetParser(dataset_folder_path) @@ -79,17 +79,17 @@ def detectCurrentEmotion(self) -> str: if frame is None: return None - dlib_faces = self.FaceDetector.DetectFaces(frame) + dlib_faces = self.face_detector.detectFaces(frame) if len(dlib_faces) == 0: return None dlib_face = dlib_faces[0] # taking only first face dlib_face = dlib_face.rect - coordinates = self.FaceDetector.ConvertDlibToList(dlib_face, frame) - cropped_frame = self.FaceDetector.CropFaceBox(frame, coordinates) - tensor = self.connector.ImageIntoTensor(cropped_frame) + coordinates = self.face_detector.convertDlibToList(dlib_face, frame) + cropped_frame = self.face_detector.cropFaceBox(frame, coordinates) + tensor = self.connector.imageIntoTensor(cropped_frame) class_result = self.emotion_classification_model(tensor) - return self.connector.ClassificationResultIntoEmotion(class_result) + return self.connector.classificationResultIntoEmotion(class_result) def ShowWindow(): diff --git a/gui/tabs/abstract_tab.py b/gui/tabs/abstract_tab.py index 75c0399..98d4ac9 100644 --- a/gui/tabs/abstract_tab.py +++ b/gui/tabs/abstract_tab.py @@ -2,10 +2,10 @@ class AbstractTabWidget(QWidget): - def __init__(self, ParentClass, tab_name): + def __init__(self, parent_class, tab_name): super().__init__() - self.ParentClass = ParentClass + self.parent_class = parent_class self.tab_name = tab_name - def UserSelectedTab(self): + def userSelectedTab(self) -> None: raise NotImplementedError("Please Implement this method") diff --git a/gui/tabs/camera_tab.py b/gui/tabs/camera_tab.py index 4deb3fe..6d65fbd 100644 --- a/gui/tabs/camera_tab.py +++ b/gui/tabs/camera_tab.py @@ -9,11 +9,11 @@ class CameraTab(AbstractTabWidget): - def __init__(self, ParentClass, tab_name): - super().__init__(ParentClass, tab_name) + def __init__(self, parent_class, tab_name): + super().__init__(parent_class, tab_name) self.connector = connector.Connector() - self.camera_toolkit = ParentClass.camera_toolkit + self.camera_toolkit = parent_class.camera_toolkit # Create the layout and add the image label to it main_layout = QVBoxLayout() @@ -21,8 +21,8 @@ def __init__(self, ParentClass, tab_name): upper_layout = QHBoxLayout() # start of upper_layout checkbox_layout = QVBoxLayout() # start of checkbox_layout - self.checkbox_display_faceBox = QCheckBox("Display face box") - checkbox_layout.addWidget(self.checkbox_display_faceBox) + self.checkbox_display_face_box = QCheckBox("Display face box") + checkbox_layout.addWidget(self.checkbox_display_face_box) self.checkbox_display_emotion = QCheckBox("Display Emotion") self.checkbox_display_emotion.stateChanged.connect(self.displayEmotionCheckboxStatusChanged) @@ -50,13 +50,13 @@ def __init__(self, ParentClass, tab_name): # Create the timer for updating the video feed self.timer = QTimer(self) - self.timer.timeout.connect(self.update_frame) + self.timer.timeout.connect(self.updateFrame) - def continueCapturePressed(self): + def continueCapturePressed(self) -> None: self.timer.start(30) self.button_continue_capture.setDisabled(True) - def analyzeImagePressed(self): + def analyzeImagePressed(self) -> None: self.timer.stop() file_dialog = QFileDialog() file_path, _ = file_dialog.getOpenFileName(self, "Select pretrained model file") @@ -78,29 +78,29 @@ def analyzeImagePressed(self): if image.shape[0] > 800 or image.shape[1] > 800: # fitting into app image = self.camera_toolkit.resizeImage(image, (800, 800)) - self.update_frame(image) + self.updateFrame(image) self.button_continue_capture.setDisabled(False) - def update_frame(self, frame=None) -> np.array: + def updateFrame(self, frame=None) -> np.array: # Read the frame from the camera if frame is None: frame = self.camera_toolkit.readFrameFromCamera() if frame is None: return - if self.checkbox_display_emotion.isChecked() or self.checkbox_display_faceBox.isChecked(): - dlib_faces = self.ParentClass.FaceDetector.DetectFaces(frame) + if self.checkbox_display_emotion.isChecked() or self.checkbox_display_face_box.isChecked(): + dlib_faces = self.parent_class.face_detector.detectFaces(frame) for dlib_face in dlib_faces: dlib_face = dlib_face.rect - coordinates = self.ParentClass.FaceDetector.ConvertDlibToList(dlib_face, frame) + coordinates = self.parent_class.face_detector.convertDlibToList(dlib_face, frame) if self.checkbox_display_emotion.isChecked(): - cropped_image = self.ParentClass.FaceDetector.CropFaceBox(frame, coordinates) - tensor = self.connector.ImageIntoTensor(cropped_image) - class_result = self.ParentClass.emotion_classification_model(tensor) - emotion_name = self.connector.ClassificationResultIntoEmotion(class_result) + cropped_image = self.parent_class.face_detector.cropFaceBox(frame, coordinates) + tensor = self.connector.imageIntoTensor(cropped_image) + class_result = self.parent_class.emotion_classification_model(tensor) + emotion_name = self.connector.classificationResultIntoEmotion(class_result) frame = self.camera_toolkit.putText(frame, emotion_name, coordinates[0]) - if self.checkbox_display_faceBox.isChecked(): - frame = self.ParentClass.FaceDetector.DrawFaceBox(frame, coordinates) + if self.checkbox_display_face_box.isChecked(): + frame = self.parent_class.face_detector.drawFaceBox(frame, coordinates) # Convert the frame to RGB format frame_rgb = self.camera_toolkit.getImageRGB(frame) @@ -119,11 +119,11 @@ def update_frame(self, frame=None) -> np.array: return frame def displayEmotionCheckboxStatusChanged(self) -> None: - if self.checkbox_display_emotion.isChecked and not self.ParentClass.is_model_loaded: + if self.checkbox_display_emotion.isChecked and not self.parent_class.is_model_loaded: self.checkbox_display_emotion.setChecked(False) QMessageBox.warning(self, "Warning", "Model is not loaded, cannot display emotion") - def UserSelectedTab(self) -> None: + def userSelectedTab(self) -> None: # Open the camera using OpenCV self.camera_toolkit.startCapture() self.continueCapturePressed() diff --git a/gui/tabs/chat_tab.py b/gui/tabs/chat_tab.py index 4009f20..723b372 100644 --- a/gui/tabs/chat_tab.py +++ b/gui/tabs/chat_tab.py @@ -59,7 +59,7 @@ def __init__(self): layout_end_buttons.addWidget(self.button_confirm) main_layout.addLayout(layout_end_buttons) - def onHelpTokenPressed(self): + def onHelpTokenPressed(self) -> None: QMessageBox.information( self, "Finding Your Token", @@ -72,7 +72,7 @@ def onHelpTokenPressed(self): *. If you don't see p-lat cookie, clear site cookies and login again, you should be able to see this.""", ) - def onConfirmPressed(self): + def onConfirmPressed(self) -> None: poe = PoeFacade() poe.setToken(self.line_edit_b_cookie.text(), self.line_edit_lat_cookie.text()) if poe.login() == poe.fail_code: @@ -81,7 +81,7 @@ def onConfirmPressed(self): self.poe_api = poe self.close() - def onCancelPressed(self): + def onCancelPressed(self) -> None: self.close() @@ -92,8 +92,8 @@ def __init__(self, ParentClass, tab_name): self.emotion_stat = [] self.resetEmotionStat() self.camera_toolkit = ParentClass.camera_toolkit - self.isCapturingResponse = False - self.isChatSaved = False + self.is_capturing_response = False + self.is_chat_saved = False self.poe_api = None # fromWho variants @@ -210,7 +210,7 @@ def __init__(self, ParentClass, tab_name): self.recoverChatHistory() self.reloadAccountStatus() - def insertNewlines(self, message, limit): + def insertNewlines(self, message: str, limit: int) -> str: result = str() words = message.split(" ") current_limit_value = 0 @@ -249,14 +249,14 @@ def setChatStatus(self, chat_status: str) -> None: new_stylesheet = self.label_status_of_chat_styleSheet + "background-color: blue;" self.label_status_of_chat.setStyleSheet(new_stylesheet) self.label_status_of_chat.setText(chat_status) - self.isCapturingResponse = True + self.is_capturing_response = True else: new_stylesheet = self.label_status_of_chat_styleSheet + "background-color: green;" self.label_status_of_chat.setStyleSheet(new_stylesheet) self.label_status_of_chat.setText(self.status_wait_user_input) - self.isCapturingResponse = False + self.is_capturing_response = False - def reloadAccountStatus(self): + def reloadAccountStatus(self) -> None: if self.poe_api is None: self.account_status.setText("Login required: ") self.account_status.setStyleSheet("color: yellow") @@ -268,13 +268,14 @@ def reloadAccountStatus(self): self.button_login_poe.setEnabled(False) self.button_logout_poe.setEnabled(True) - def eventFilter(self, obj, event): + def eventFilter(self, obj: QTextEdit, event: QEvent) -> bool: if event.type() == QEvent.KeyPress and obj is self.input_message_area: if ( event.key() == Qt.Key_Return and not event.modifiers() & Qt.ShiftModifier and self.input_message_area.hasFocus() ): self.onSendMessagePressed() return True + return super().eventFilter(obj, event) def onLogIntoAccountPressed(self) -> None: @@ -287,7 +288,7 @@ def onLogIntoAccountPressed(self) -> None: project_config.addPoeAccount(login.poe_api.tokens) def onLogoutPressed(self) -> None: - if self.isCapturingResponse: + if self.is_capturing_response: QMessageBox.warning(self, "Wait", "Bot is still answering, please wait.") return project_config = ProjectConfig() @@ -299,7 +300,7 @@ def onSendMessagePressed(self) -> None: message = self.input_message_area.toPlainText() if len(message) == 0 or not self.poe_api.isLogged(): return - if self.isCapturingResponse: + if self.is_capturing_response: QMessageBox.warning(self, "Wait", "Bot is still answering, please wait.") return @@ -307,7 +308,7 @@ def onSendMessagePressed(self) -> None: # Creating real message emotion_stat_str = "[ " - for num, emotion in enumerate(self.ParentClass.parser.emotion_list): + for num, emotion in enumerate(self.parent_class.parser.emotion_list): emotion_stat_str += emotion + ": " + str(self.emotion_stat[num]) if num != len(emotion) - 1: emotion_stat_str += ", " @@ -318,9 +319,9 @@ def onSendMessagePressed(self) -> None: worker = ChatterWorker(self, message) worker.signals.create_message_bubble_signal.connect(self.insertBubble) worker.setTask(worker.task_send_message) - self.ParentClass.threadpool.start(worker) + self.parent_class.threadpool.start(worker) - def onDeleteChatPressed(self): + def onDeleteChatPressed(self) -> None: result = QMessageBox.question( self, "Last chance", @@ -332,7 +333,7 @@ def onDeleteChatPressed(self): self.poe_api.deleteCurrentChat() self.poe_api.chatId = None - self.isChatSaved = False + self.is_chat_saved = False config = ProjectConfig() config.removeFieldFromSection(config.poe_index, config.poe_chat_id) @@ -345,37 +346,37 @@ def onDeleteChatPressed(self): QSpacerItem(1, self.message_container.height(), QSizePolicy.Minimum, QSizePolicy.Expanding) ) - def onInputMessageTextChanged(self): + def onInputMessageTextChanged(self) -> None: if len(self.input_message_area.toPlainText()) == 0: self.resetEmotionStat() self.button_send.setEnabled(False) return self.button_send.setEnabled(True) - current_emotion = self.ParentClass.detectCurrentEmotion() + current_emotion = self.parent_class.detectCurrentEmotion() if current_emotion is None: return - self.emotion_stat[self.ParentClass.parser.emotion_list.index(current_emotion)] += 1 + self.emotion_stat[self.parent_class.parser.emotion_list.index(current_emotion)] += 1 - def recoverChatHistory(self): + def recoverChatHistory(self) -> None: # Setting up worker worker = ChatterWorker(self, "stub") worker.signals.create_message_bubble_signal.connect(self.insertBubble) worker.setTask(worker.task_recover_chat) - self.ParentClass.threadpool.start(worker) - self.isCapturingResponse = True + self.parent_class.threadpool.start(worker) + self.is_capturing_response = True @Slot() def insertBubble(self, message: str, sender: str) -> None: self.messages_layout.addWidget(self.createMessageBubble(message, sender)) - if not self.isChatSaved: + if not self.is_chat_saved: config = ProjectConfig() config.setPoeChatId(self.poe_api.chatId) - self.isChatSaved = True + self.is_chat_saved = True - def resetEmotionStat(self): - self.emotion_stat = [0] * len(self.ParentClass.parser.emotion_list) + def resetEmotionStat(self) -> None: + self.emotion_stat = [0] * len(self.parent_class.parser.emotion_list) - def UserSelectedTab(self): + def userSelectedTab(self) -> None: self.camera_toolkit.startCapture() diff --git a/gui/tabs/learning_tab.py b/gui/tabs/learning_tab.py index 6132a5b..9154d61 100644 --- a/gui/tabs/learning_tab.py +++ b/gui/tabs/learning_tab.py @@ -28,11 +28,11 @@ def __init__(self, ParentClass, tab_name): 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_start_model_train.clicked.connect(self.onStartButtonPressed) self.button_stop_model_train = DarkStyleButton("Stop training") self.button_stop_model_train.setEnabled(False) - self.button_stop_model_train.clicked.connect(self.UserPressedStopButton) + self.button_stop_model_train.clicked.connect(self.onStopButtonPressed) buttons_start_stop_layout = QHBoxLayout() # start of buttons_start_stop_layout buttons_start_stop_layout.addWidget(self.button_start_model_train) @@ -46,10 +46,10 @@ def __init__(self, ParentClass, tab_name): self.combobox_dataset_interaction = QComboBox() self.combobox_dataset_interaction.addItems( [ - self.ParentClass.parser.shuffled, - self.ParentClass.parser.not_shuffled, - self.ParentClass.parser.full_emotion, - self.ParentClass.parser.one_img_emotion, + self.parent_class.parser.shuffled, + self.parent_class.parser.not_shuffled, + self.parent_class.parser.full_emotion, + self.parent_class.parser.one_img_emotion, ] ) dataset_interaction_layout.addWidget(self.combobox_dataset_interaction) @@ -73,7 +73,7 @@ def __init__(self, ParentClass, tab_name): self.main_vertical_layout.addLayout(layout_statistics) # Statistics table - self.table_statistics = LearningStatisticsTable(self.ParentClass.parser) + self.table_statistics = LearningStatisticsTable(self.parent_class.parser) self.table_statistics.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.table_statistics.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.table_statistics.setEditTriggers(QAbstractItemView.NoEditTriggers) @@ -92,11 +92,11 @@ def __init__(self, ParentClass, tab_name): self.statistics_plot.addLegend() self.main_vertical_layout.addWidget(self.statistics_plot) - def analyzeProgramStatus(self): + def analyzeProgramStatus(self) -> None: error_message = "Model is not loaded. Check settings tab." - if self.ParentClass.is_model_loaded: + if self.parent_class.is_model_loaded: error_message = "Dataset is not loaded. Check settings tab." - if self.ParentClass.parser.isParserLoaded: + if self.parent_class.parser.is_parser_loaded: self.label_model_train_status.setText("Ready for training") self.button_start_model_train.setEnabled(True) return @@ -105,39 +105,39 @@ def analyzeProgramStatus(self): self.button_start_model_train.setEnabled(False) self.button_stop_model_train.setEnabled(False) - def UserPressedStartButton(self) -> None: + def onStartButtonPressed(self) -> None: self.button_start_model_train.setEnabled(False) self.button_stop_model_train.setEnabled(True) - self.ParentClass.is_model_learning = True - self.table_statistics.reloadTable(self.ParentClass.parser) + self.parent_class.is_model_learning = True + self.table_statistics.reloadTable(self.parent_class.parser) # Resetting stats self.current_epoch = 0 self.current_emotion = "None" # Setting up worker - worker = LearningWorker(self.ParentClass, self.combobox_dataset_interaction.currentText(), self.ParentClass.parser) - worker.signals.redo_plots_signal.connect(self.UpdatePlots) - worker.signals.update_epoch_stats_signal.connect(self.UpdateEpochStat) - worker.signals.update_emotion_classification_result_signal.connect(self.UpdateClassificationResults) - self.ParentClass.threadpool.start(worker) + worker = LearningWorker(self.parent_class, self.combobox_dataset_interaction.currentText(), self.parent_class.parser) + worker.signals.redo_plots_signal.connect(self.updatePlots) + worker.signals.update_epoch_stats_signal.connect(self.updateEpochStat) + worker.signals.update_emotion_classification_result_signal.connect(self.updateClassificationResults) + self.parent_class.threadpool.start(worker) self.label_model_train_status.setText("Model is running") - def UserPressedStopButton(self) -> None: + def onStopButtonPressed(self) -> None: self.button_start_model_train.setEnabled(True) self.button_stop_model_train.setEnabled(False) - self.ParentClass.is_model_learning = False - self.ParentClass.threadpool.waitForDone() # Waiting until model successfully saves itself + self.parent_class.is_model_learning = False + self.parent_class.threadpool.waitForDone() # Waiting until model successfully saves itself self.analyzeProgramStatus() - def UserSelectedTab(self) -> None: - if self.ParentClass.is_model_learning: + def userSelectedTab(self) -> None: + if self.parent_class.is_model_learning: return self.analyzeProgramStatus() @Slot() - def UpdatePlots(self, loss_results: list[float, ...], accuracy_results: list[float, ...]) -> None: + def updatePlots(self, loss_results: list[float, ...], accuracy_results: list[float, ...]) -> None: self.statistics_plot.clear() self.statistics_plot.plot( @@ -146,7 +146,7 @@ def UpdatePlots(self, loss_results: list[float, ...], accuracy_results: list[flo self.statistics_plot.plot(range(0, len(loss_results)), loss_results, name="Loss", pen=self.loss_coef_pen) @Slot() - def UpdateEpochStat( + def updateEpochStat( self, current_epoch: int, current_emotion: str, last_expected: list[float, ...], last_tensor: list[int, ...] ) -> None: self.label_epoch_num.setText(str(current_epoch)) @@ -155,5 +155,5 @@ def UpdateEpochStat( self.label_last_tensor.setText(str(last_tensor)) @Slot() - def UpdateClassificationResults(self, guessed_right: list[int, ...], emotions_average_loss: list[float, ...]) -> None: - self.table_statistics.set_data(guessed_right, emotions_average_loss) + def updateClassificationResults(self, guessed_right: list[int, ...], emotions_average_loss: list[float, ...]) -> None: + self.table_statistics.setData(guessed_right, emotions_average_loss) diff --git a/gui/tabs/settings_tab.py b/gui/tabs/settings_tab.py index d225a1b..ac6dcc8 100644 --- a/gui/tabs/settings_tab.py +++ b/gui/tabs/settings_tab.py @@ -1,5 +1,4 @@ from PySide6.QtWidgets import QFileDialog, QMessageBox, QVBoxLayout, QHBoxLayout, QLabel, QTextBrowser, QComboBox -from PySide6.QtCore import QThreadPool from gui.tabs.abstract_tab import AbstractTabWidget from gui.custom_widgets.dark_style_button import DarkStyleButton from torch import __version__ as pytorch_version @@ -14,20 +13,18 @@ class SettingsTab(AbstractTabWidget): def __init__(self, ParentClass, tab_name): super().__init__(ParentClass, tab_name) self.main_vertical_layout = QVBoxLayout(self) - self.threadpool = QThreadPool() # Just for stats self.label_model_load_status = QLabel() - # Checking default model path - self.SetLoadModelStatus(False) + self.setLoadModelStatus(False) if os.path.exists(pretrained_emotion_recognition_model): - self.ParentClass.emotion_classification_model.LoadModel(pretrained_emotion_recognition_model) - self.SetLoadModelStatus(True) + self.parent_class.emotion_classification_model.loadModel(pretrained_emotion_recognition_model) + self.setLoadModelStatus(True) self.button_set_model_path = DarkStyleButton("Load pretrained model") - self.button_set_model_path.clicked.connect(self.OnLoadModelButtonClicked) + self.button_set_model_path.clicked.connect(self.onLoadModelButtonClicked) self.button_create_new_model = DarkStyleButton("Create new model") - self.button_create_new_model.clicked.connect(self.OnCreateModelButtonClicked) + self.button_create_new_model.clicked.connect(self.onCreateModelButtonClicked) layout_model_status = QHBoxLayout() layout_model_status.addWidget(self.label_model_load_status) @@ -42,7 +39,7 @@ def __init__(self, ParentClass, tab_name): self.button_load_parser.clicked.connect(self.loadDatasetPressed) self.button_unload_parser = DarkStyleButton("Unload dataset") self.button_unload_parser.clicked.connect(self.unloadDatasetPressed) - self.SetLoadParserStatus() + self.setLoadParserStatus() layout_parser_status.addWidget(self.label_parser_status) layout_parser_status.addWidget(self.button_load_parser) @@ -63,24 +60,24 @@ def __init__(self, ParentClass, tab_name): layout_camera_setting = QHBoxLayout() # start of layout_camera_setting layout_camera_setting.addWidget(QLabel("Set working camera: ")) - self.comboBox_working_cameras = QComboBox() - self.comboBox_working_cameras.currentIndexChanged.connect(self.onSelectedCameraChanged) + self.combobox_working_cameras = QComboBox() + self.combobox_working_cameras.currentIndexChanged.connect(self.onSelectedCameraChanged) self.onReloadCameraPortsPressed() - layout_camera_setting.addWidget(self.comboBox_working_cameras) + layout_camera_setting.addWidget(self.combobox_working_cameras) reload_cameras_button = DarkStyleButton("Reload list of cameras") reload_cameras_button.clicked.connect(self.onReloadCameraPortsPressed) layout_camera_setting.addWidget(reload_cameras_button) self.main_vertical_layout.addLayout(layout_camera_setting) # end of layout_camera_setting # outputting info - self.textBrowser_current_run_info = QTextBrowser() - self.textBrowser_current_run_info.setText( + self.text_browser_current_run_info = QTextBrowser() + self.text_browser_current_run_info.setText( "Pytorch version:\t" + pytorch_version + "\n" + "Model is running on:\t" + pytorch_device ) - self.main_vertical_layout.addWidget(self.textBrowser_current_run_info) + self.main_vertical_layout.addWidget(self.text_browser_current_run_info) - def OnLoadModelButtonClicked(self) -> None: - if self.ParentClass.is_model_learning: + def onLoadModelButtonClicked(self) -> None: + if self.parent_class.is_model_learning: QMessageBox.warning(self, "Model is busy learning now") return @@ -94,11 +91,11 @@ def OnLoadModelButtonClicked(self) -> None: QMessageBox.warning(self, "warning", "Selected path cannot be found") return - self.SetLoadModelStatus(False) # Unloading model, so if any error occurs it wont be usable - self.ParentClass.emotion_classification_model.LoadModel(file_path) # Trying to load model - self.SetLoadModelStatus(True) # If success, function will continue and this command will run + self.setLoadModelStatus(False) # Unloading model, so if any error occurs it wont be usable + self.parent_class.emotion_classification_model.loadModel(file_path) # Trying to load model + self.setLoadModelStatus(True) # If success, function will continue and this command will run - def OnCreateModelButtonClicked(self) -> None: + def onCreateModelButtonClicked(self) -> None: if os.path.exists(pretrained_emotion_recognition_model): result = QMessageBox.question( self, @@ -111,22 +108,22 @@ def OnCreateModelButtonClicked(self) -> None: return os.remove(pretrained_emotion_recognition_model) - self.ParentClass.emotion_classification_model = self.ParentClass.createEmotionRecognitionModel() - self.ParentClass.emotion_classification_model.BackupModel("", pretrained_emotion_recognition_model) - self.SetLoadModelStatus(True) + self.parent_class.emotion_classification_model = self.parent_class.createEmotionRecognitionModel() + self.parent_class.emotion_classification_model.backupModel("", pretrained_emotion_recognition_model) + self.setLoadModelStatus(True) - def SetLoadModelStatus(self, status: bool) -> None: + def setLoadModelStatus(self, status: bool) -> None: if status: self.label_model_load_status.setText("Model is loaded") self.label_model_load_status.setStyleSheet("color: green") - self.ParentClass.is_model_loaded = True + self.parent_class.is_model_loaded = True else: self.label_model_load_status.setText("Model is not loaded") self.label_model_load_status.setStyleSheet("color: yellow") - self.ParentClass.is_model_loaded = False + self.parent_class.is_model_loaded = False - def SetLoadParserStatus(self) -> None: - if self.ParentClass.parser.isParserLoaded: + def setLoadParserStatus(self) -> None: + if self.parent_class.parser.is_parser_loaded: self.label_parser_status.setText("Parser is loaded") self.label_parser_status.setStyleSheet("color: green") self.button_load_parser.setEnabled(False) @@ -141,18 +138,18 @@ def loadDatasetPressed(self) -> None: if not os.path.exists(dataset_folder_path): QMessageBox.warning(self, "warning", f"Cannot load since dataset folder cannot be found {dataset_folder_path}") return - self.ParentClass.parser.LoadDatasetIntoRam() - self.SetLoadParserStatus() + self.parent_class.parser.loadDatasetIntoRam() + self.setLoadParserStatus() def unloadDatasetPressed(self) -> None: - self.ParentClass.parser = self.ParentClass.reloadParser() - self.SetLoadParserStatus() + self.parent_class.parser = self.parent_class.reloadParser() + self.setLoadParserStatus() def deleteLocalDataFolderPressed(self) -> None: if not os.path.exists(data_folder_path): - QMessageBox.info(self, "Fail", "Folder is not exist") + QMessageBox.information(self, "Fail", "Folder is not exist") return - if self.ParentClass.is_model_learning: + if self.parent_class.is_model_learning: QMessageBox.warning(self, "Fail", "Cannot delete local folder while model is learning") return result = QMessageBox.question( @@ -163,8 +160,8 @@ def deleteLocalDataFolderPressed(self) -> None: ) if result == QMessageBox.No: # Operation canceled return - self.ParentClass.parser = self.ParentClass.reloadParser() - self.SetLoadParserStatus() + self.parent_class.parser = self.parent_class.reloadParser() + self.setLoadParserStatus() shutil.rmtree(data_folder_path) def createLocalDataFolderPressed(self) -> None: @@ -174,13 +171,13 @@ def createLocalDataFolderPressed(self) -> None: os.mkdir(data_folder_path) def onSelectedCameraChanged(self) -> None: - self.ParentClass.camera_toolkit.target_camera_index = self.comboBox_working_cameras.currentIndex() + self.parent_class.camera_toolkit.target_camera_index = self.combobox_working_cameras.currentIndex() def onReloadCameraPortsPressed(self) -> None: - available_ports = self.ParentClass.camera_toolkit.listPorts() - camera_names = self.ParentClass.camera_toolkit.getCameraNamesInList(available_ports) - self.comboBox_working_cameras.clear() - self.comboBox_working_cameras.addItems(camera_names) + available_ports = self.parent_class.camera_toolkit.listPorts() + camera_names = self.parent_class.camera_toolkit.getCameraNamesInList(available_ports) + self.combobox_working_cameras.clear() + self.combobox_working_cameras.addItems(camera_names) - def UserSelectedTab(self) -> None: + def userSelectedTab(self) -> None: pass diff --git a/gui/tabs/storage_tab.py b/gui/tabs/storage_tab.py index b7e6180..7f10760 100644 --- a/gui/tabs/storage_tab.py +++ b/gui/tabs/storage_tab.py @@ -2,7 +2,7 @@ from gui.custom_widgets.abstract_storage_cloud import AbstractStorageWidget from gui.custom_widgets.dark_style_button import DarkStyleButton from gui.custom_widgets.add_storage_dialog import AddStorageDialog -from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout, QScrollArea, QWidget, QSpacerItem, QSizePolicy +from PySide6.QtWidgets import QVBoxLayout, QHBoxLayout, QScrollArea, QWidget, QSpacerItem, QSizePolicy, QLayout from PySide6.QtCore import Slot from shared import assets_folder_path from CloudStorages.cloud_storage_interface import CloudStorageInterface @@ -46,20 +46,20 @@ def __init__(self, ParentClass, tab_name): verticalSpacer = QSpacerItem(1, self.height(), QSizePolicy.Minimum, QSizePolicy.Expanding) self.content_layout.addSpacerItem(verticalSpacer) - def clearLayout(self, layout): + def clearLayout(self, layout: QLayout) -> None: while layout.count(): item = layout.takeAt(0) widget = item.widget() if widget is not None: widget.deleteLater() - def addCloudWidget(self, cloud_class, storage_name: str): + def addCloudWidget(self, cloud_class, storage_name: str) -> None: path_to_icon = assets_folder_path + self.assets_dict[cloud_class.cloud_storage_name] - cloud_widget = AbstractStorageWidget(self.ParentClass, storage_name, path_to_icon, cloud_class) + cloud_widget = AbstractStorageWidget(self.parent_class, storage_name, path_to_icon, cloud_class) cloud_widget.signals.delete_widget.connect(self.removeStorage) self.storage_widgets_layout.addWidget(cloud_widget) - def refreshStorages(self): + def refreshStorages(self) -> None: self.clearLayout(self.storage_widgets_layout) # Getting storages config = ProjectConfig() @@ -68,7 +68,7 @@ def refreshStorages(self): self.addCloudWidget(entry[0], entry[1]) @Slot() - def removeStorage(self, provider_name: str, storage_name: str): + def removeStorage(self, provider_name: str, storage_name: str) -> None: config = ProjectConfig() config.deleteStorageEntry(provider_name, storage_name) self.refreshStorages() @@ -79,5 +79,5 @@ def addStoragePressed(self) -> None: if window.return_instance is not None: self.addCloudWidget(window.return_instance, window.return_storage_name) - def UserSelectedTab(self) -> None: + def userSelectedTab(self) -> None: pass diff --git a/gui/workers/chatter_worker.py b/gui/workers/chatter_worker.py index efc3ae7..2084410 100644 --- a/gui/workers/chatter_worker.py +++ b/gui/workers/chatter_worker.py @@ -6,7 +6,7 @@ class ChatterSignals(QObject): class ChatterWorker(QRunnable): - def __init__(self, chat_tab, message_to_send): + def __init__(self, chat_tab, message_to_send: str): super(ChatterWorker, self).__init__(chat_tab, message_to_send) self.parent_tab = chat_tab @@ -18,18 +18,19 @@ def __init__(self, chat_tab, message_to_send): self.task_send_message = "Send" self.task_recover_chat = "Recover" - def setTask(self, task: str): + def setTask(self, task: str) -> None: self.task = task @Slot() - def run(self): + def run(self) -> None: self.parent_tab.setChatStatus(self.parent_tab.status_getting_response) if self.task == self.task_send_message: response = self.parent_tab.poe_api.sendMessage(self.message_to_send) self.signals.create_message_bubble_signal.emit(response, self.parent_tab.from_bot) elif self.task == self.task_recover_chat: - ignore_messages_amount = 2 # We need to hide first two messages + #! Current default bot has 3 welcome messages, in future need to place this variable in config + ignore_messages_amount = 3 history = self.parent_tab.poe_api.getChatPreviousMessages() for num, message in enumerate(history): if num < ignore_messages_amount: diff --git a/gui/workers/learning_worker.py b/gui/workers/learning_worker.py index 10ec9e9..8d0dbea 100644 --- a/gui/workers/learning_worker.py +++ b/gui/workers/learning_worker.py @@ -15,9 +15,9 @@ class LearningWorker(QRunnable): Worker thread that runs learning function """ - def __init__(self, MainWindowClass, dataset_interaction_approach: str, parser: DatasetParser): + def __init__(self, main_window, dataset_interaction_approach: str, parser: DatasetParser): super(LearningWorker, self).__init__() - self.MainWindowClass = MainWindowClass + self.main_window = main_window self.dataset_interaction_approach = dataset_interaction_approach self.signals = LearningWorkerSignals() self.parser = parser # This will be full copy of parser @@ -33,16 +33,16 @@ def __init__(self, MainWindowClass, dataset_interaction_approach: str, parser: D self.parser.one_img_emotion: self.imageFromEmotionBasedOnLoss, self.parser.full_emotion: self.fullEmotionBasedOnLoss, } - self.isBasedOnLoss = False + self.is_based_on_loss = False - def UpdateAndSave(self, expected_list: list[int, ...], tensor_result: [float, ...]): + def updateAndSave(self, expected_list: list[int, ...], tensor_result: list[float, ...]) -> None: tensor_result = [round(num, 5) for num in tensor_result] self.signals.update_epoch_stats_signal.emit( self.current_epoch, self.last_triggered_emotion, expected_list, tensor_result ) - self.MainWindowClass.emotion_classification_model.BackupModel("", pretrained_emotion_recognition_model) + self.main_window.emotion_classification_model.backupModel("", pretrained_emotion_recognition_model) - def TestingModel(self) -> list: + def testingModel(self) -> list: # Stats collector accuracy_results = list() loss_fn_results = list() @@ -54,9 +54,9 @@ def TestingModel(self) -> list: predicted_right = 0 current_loss_coefficients = list() # Storing all loss coefficient for current emotion for test_value in self.parser.testing_set_dict[emotion]: - tensor = self.connector.ImageIntoTensor(test_value) + tensor = self.connector.imageIntoTensor(test_value) - testing_result = self.MainWindowClass.emotion_classification_model.TestingEpoch(tensor, expect_list) + testing_result = self.main_window.emotion_classification_model.testingEpoch(tensor, expect_list) # Collecting stats accuracy_results.append(testing_result["Accuracy"]) @@ -64,7 +64,7 @@ def TestingModel(self) -> list: current_loss_coefficients.append(testing_result["Loss"]) predicted_right = predicted_right + 1 if testing_result["IsPredictedRight"] else 0 - if not self.MainWindowClass.is_model_learning: # If user pressed stop button + if not self.main_window.is_model_learning: # If user pressed stop button return self.code_interrupt # Calculating average loss and saving results @@ -76,7 +76,7 @@ def TestingModel(self) -> list: self.signals.update_emotion_classification_result_signal.emit(emotions_classification_result, emotion_average_loss) return emotion_average_loss - def fullDatasetLearning(self): + def fullDatasetLearning(self) -> None: """Train model using whole dataset (no difference if shuffled or not) Interacts with pre-converted dataset into tensors which might be shuffled or not and @@ -84,64 +84,66 @@ def fullDatasetLearning(self): """ for data in self.shuffled_learning_data: - result_tensor = self.MainWindowClass.emotion_classification_model.TrainEpoch(data.value, data.expected) - if not self.MainWindowClass.is_model_learning: + result_tensor = self.main_window.emotion_classification_model.trainEpoch(data.value, data.expected) + if not self.main_window.is_model_learning: break # Updating and sending results self.current_epoch = self.current_epoch + 1 - self.UpdateAndSave(data.expected.tolist(), result_tensor.tolist()) + self.updateAndSave(data.expected.tolist(), result_tensor.tolist()) - def fullEmotionBasedOnLoss(self): + def fullEmotionBasedOnLoss(self) -> None: self.last_triggered_emotion = self.next_emotion_name expect_list = self.parser.emotion_expected_dict[self.next_emotion_name] expect_tensor = self.connector.expectListIntoTensor(expect_list) for train_value in self.parser.learning_set_dict[self.next_emotion_name]: - tensor = self.connector.ImageIntoTensor(train_value) - result_tensor = self.MainWindowClass.emotion_classification_model.TrainEpoch(tensor, expect_tensor) - if not self.MainWindowClass.is_model_learning: + tensor = self.connector.imageIntoTensor(train_value) + result_tensor = self.main_window.emotion_classification_model.trainEpoch(tensor, expect_tensor) + if not self.main_window.is_model_learning: return self.current_epoch = self.current_epoch + 1 - self.UpdateAndSave(expect_list, result_tensor.tolist()) + self.updateAndSave(expect_list, result_tensor.tolist()) - def imageFromEmotionBasedOnLoss(self): + def imageFromEmotionBasedOnLoss(self) -> None: self.last_triggered_emotion = self.next_emotion_name if len(self.parser.learning_set_dict[self.next_emotion_name]) == 0: - self.parser.ReloadEmotion(self.parser.forLearning, self.next_emotion_name) + self.parser.reloadEmotion(self.parser.for_learning, self.next_emotion_name) expect_list = self.parser.emotion_expected_dict[self.next_emotion_name] expect_tensor = self.connector.expectListIntoTensor(expect_list) train_value = self.parser.learning_set_dict[self.next_emotion_name][-1] self.parser.learning_set_dict[self.next_emotion_name].pop() - tensor = self.connector.ImageIntoTensor(train_value) - result_tensor = self.MainWindowClass.emotion_classification_model.TrainEpoch(tensor, expect_tensor) + tensor = self.connector.imageIntoTensor(train_value) + result_tensor = self.main_window.emotion_classification_model.trainEpoch(tensor, expect_tensor) self.current_epoch = self.current_epoch + 1 - self.UpdateAndSave(expect_list, result_tensor.tolist()) + self.updateAndSave(expect_list, result_tensor.tolist()) @Slot() - def run(self): + def run(self) -> None: # learning data if ( self.dataset_interaction_approach == self.parser.shuffled or self.dataset_interaction_approach == self.parser.not_shuffled ): self.last_triggered_emotion = "All" - isShuffleRequired = True if self.dataset_interaction_approach == self.parser.shuffled else False - self.shuffled_learning_data = self.parser.getDatasetData(self.parser.forLearning, needToShuffle=isShuffleRequired) + is_shuffle_required = True if self.dataset_interaction_approach == self.parser.shuffled else False + self.shuffled_learning_data = self.parser.getDatasetData( + self.parser.for_learning, need_to_shuffle=is_shuffle_required + ) for data in self.shuffled_learning_data: - data.value = self.connector.ImageIntoTensor(data.value) + data.value = self.connector.imageIntoTensor(data.value) data.expected = self.connector.expectListIntoTensor(data.expected) - while self.MainWindowClass.is_model_learning: - return_value = self.TestingModel() + while self.main_window.is_model_learning: + return_value = self.testingModel() if return_value == self.code_interrupt: break # self.next_emotion_name will be used if learning function based on loss self.next_emotion_name = self.parser.emotion_list[return_value.index(max(return_value))] self.learning_function_dict[self.dataset_interaction_approach]() - self.MainWindowClass.emotion_classification_model.eval() + self.main_window.emotion_classification_model.eval() diff --git a/gui/workers/storage_worker.py b/gui/workers/storage_worker.py index 6fa4b54..7d8d91f 100644 --- a/gui/workers/storage_worker.py +++ b/gui/workers/storage_worker.py @@ -16,14 +16,14 @@ def __init__(self, abstract_storage_parent, action: str): self.action = action @Slot() - def run(self): - self.parent.ParentClass.is_storage_busy = True + def run(self) -> None: + self.parent.parent_class.is_storage_busy = True self.parent.label_status.setText("Storage busy...") if self.action == self.possible_tasks.task_push: self.parent.cloud_storage.pushDataFolder() elif self.action == self.possible_tasks.task_pull: - self.cloud_storage.pullDataFolder() + self.parent.cloud_storage.pullDataFolder() self.parent.refreshPressed(force_check=True) - self.parent.ParentClass.is_storage_busy = False + self.parent.parent_class.is_storage_busy = False diff --git a/poe.py b/poe.py index 098359c..176ea35 100644 --- a/poe.py +++ b/poe.py @@ -19,13 +19,13 @@ def __init__(self): self.client = None self.dict_keys = PoeDictKeys() - def setToken(self, b_or_token, lat=None): + def setToken(self, b_or_token: str or dict, lat=None) -> None: if isinstance(b_or_token, dict): self.tokens = b_or_token else: self.tokens = {self.dict_keys.b_cookie_dict_key: b_or_token, self.dict_keys.lat_cookie_dict_key: lat} - def login(self): + def login(self) -> str: if self.tokens is None: return @@ -40,13 +40,7 @@ def login(self): def isLogged(self) -> bool: return True if self.client is not None else False - def getChats(self): - if not self.isLogged: - return - - print(self.client.get_chat_history()["data"]) - - def initProjectSpecificChat(self): + def initProjectSpecificChat(self) -> None: message = """Every time i send message you'll get statistics of user emotions while typing the message, it will look like this: [happy: 10 ...], user does not see this statistics, you must analyze user emotion every time and depending on them generate answer for user. @@ -68,7 +62,7 @@ def sendMessage(self, message: str) -> str: return chunk["text"] - def deleteCurrentChat(self): + def deleteCurrentChat(self) -> None: self.client.delete_chat(self.default_bot, self.chatId) def getChatPreviousMessages(self) -> dict: diff --git a/pyproject.toml b/pyproject.toml index 5a71c24..25cdb7e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,7 @@ ignore = [] # Allow fix for all enabled rules (when `--fix`) is provided. fixable = ["ALL"] unfixable = [] +task-tags = ["TODO"] # Allow unused variables when underscore-prefixed. dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" @@ -45,4 +46,6 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" quote-style = "double" indent-style = "space" line-ending = "auto" +docstring-code-format = true +docstring-code-line-length = 120 skip-magic-trailing-comma = true diff --git a/scripts/delete_undetected_faces_from_dataset.py b/scripts/delete_undetected_faces_from_dataset.py index 74362c7..a2f642d 100644 --- a/scripts/delete_undetected_faces_from_dataset.py +++ b/scripts/delete_undetected_faces_from_dataset.py @@ -20,7 +20,7 @@ for num, file in enumerate(files_in_folder): image = cv2.imread(path_to_dataset + folder + file) image = cv2.resize(image, (200, 200)) - faces = face_detector.DetectFaces(image) + faces = face_detector.detectFaces(image) if len(faces) != 1: count = count + 1 os.remove(path_to_dataset + folder + file) diff --git a/tests/main.py b/tests/main.py index e81ca32..1dde79d 100644 --- a/tests/main.py +++ b/tests/main.py @@ -24,33 +24,33 @@ # Pre-detecting face (pretrained model does not requires testing :D) sample_image = cv2.imread(sample_image_path) sample_image = cv2.resize(sample_image, (400, 400)) # increasing image size since 48x48 is to small to see landmarks -faces = face_detector.DetectFaces(sample_image) +faces = face_detector.detectFaces(sample_image) faceRect = faces[0].rect -coordinates_for_sample_image = face_detector.ConvertDlibToList(faceRect, sample_image) +coordinates_for_sample_image = face_detector.convertDlibToList(faceRect, sample_image) class ImageProcessingTesting(unittest.TestCase): def test_DrawingFaceBox(self): - image = face_detector.DrawFaceBox(sample_image, coordinates_for_sample_image) + image = face_detector.drawFaceBox(sample_image, coordinates_for_sample_image) self.assertIsNotNone(image) cv2.imwrite(test_result_folder_name + "DrawingFaceBox.jpg", image) return image def test_CroppingFaceBox(self): - image = face_detector.CropFaceBox(sample_image, coordinates_for_sample_image) + image = face_detector.cropFaceBox(sample_image, coordinates_for_sample_image) self.assertIsNotNone(image) cv2.imwrite(test_result_folder_name + "CroppingFaceBox.jpg", image) def test_CroppingDrawingFaceBox(self): - image = face_detector.DrawFaceBox(sample_image, coordinates_for_sample_image) + image = face_detector.drawFaceBox(sample_image, coordinates_for_sample_image) self.assertIsNotNone(image) - image = face_detector.CropFaceBox(image, coordinates_for_sample_image) + image = face_detector.cropFaceBox(image, coordinates_for_sample_image) self.assertIsNotNone(image) cv2.imwrite(test_result_folder_name + "CroppedDrawingFaceBox.jpg", image) def test_DrawingBox(self): image = self.test_DrawingFaceBox() - result_image = face_detector.DrawFaceBox(image, coordinates_for_sample_image) + result_image = face_detector.drawFaceBox(image, coordinates_for_sample_image) cv2.imwrite(test_result_folder_name + "DrawingBoxLandmark.jpg", result_image)