Skip to content
This repository was archived by the owner on Jun 24, 2024. It is now read-only.

Commit 34028bc

Browse files
authored
Merge pull request #43 from MemerGamer/40-implement-the-design-ui
Resolve "Implement the design UI"
2 parents 50f09c4 + f5f0aa7 commit 34028bc

File tree

13 files changed

+619
-992
lines changed

13 files changed

+619
-992
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
"conventionalCommits.scopes": [
33
"videoeditor",
44
"ci/cd"
5-
]
5+
],
6+
"python.analysis.typeCheckingMode": "basic"
67
}

docs/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Welcome to ManimStudio's documentation!
1515
src/core/project_creation_dialog
1616
src/core/project_opening_dialog
1717
src/utils/logger_utility
18-
src/ui/form_ui
18+
src/ui/welcome_ui
1919
src/ui/settings_ui
2020
src/ui/videoeditor_ui
2121
src/ui/project_creation_dialog_ui

docs/src/ui/form_ui.rst

Lines changed: 0 additions & 7 deletions
This file was deleted.

docs/src/ui/welcome_ui.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
welcome_ui module
2+
=================
3+
4+
.. automodule:: welcome_ui
5+
:members:
6+
:undoc-members:
7+
:show-inheritance:

src/core/main.py

Lines changed: 143 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1+
from datetime import datetime
12
import sys
23
import json
34
from pathlib import Path
4-
from PySide6.QtWidgets import (
5-
QApplication,
6-
QWidget,
7-
QDialog,
5+
from typing import Optional, Dict, List
6+
from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QDialog, QHeaderView
7+
from PySide6.QtCore import Signal, Qt
8+
from PySide6.QtGui import (
9+
QPixmap,
10+
QResizeEvent,
11+
QAction,
12+
QStandardItemModel,
13+
QStandardItem,
814
)
9-
from PySide6.QtCore import Signal
10-
from PySide6.QtGui import QPixmap
11-
from PySide6.QtCore import Qt
12-
from PySide6.QtGui import QPixmap
1315
from PySide6.QtWidgets import QSizePolicy
1416

1517

@@ -19,106 +21,177 @@
1921
sys.path.append(str(parent_dir))
2022

2123

22-
from settings import (
24+
# UI imports
25+
from src.ui.settings_ui import Ui_Form # noqa: E402
26+
from src.ui.videoeditor_ui import Ui_Form as Ui_VideoEditor # noqa: E402
27+
from src.ui.welcome_ui import Ui_MainWindow # noqa: E402
28+
29+
# Core imports
30+
from src.core.project_creation_dialog import ProjectCreationDialog # noqa: E402
31+
from src.core.project_opening_dialog import ProjectOpeningDialog # noqa: E402
32+
from src.core.settings import ( # noqa: E402
2333
load_settings,
2434
load_themes,
2535
load_current_theme,
2636
update_settings,
37+
get_recent_project_paths,
2738
)
2839

29-
# UI imports
30-
from src.ui.settings_ui import Ui_Form
31-
from src.ui.videoeditor_ui import Ui_Form as Ui_VideoEditor
32-
from src.ui.form_ui import Ui_Main
33-
34-
# Core imports
35-
from src.core.project_creation_dialog import ProjectCreationDialog
36-
from src.core.project_opening_dialog import ProjectOpeningDialog
37-
38-
# Logger
39-
from src.utils.logger_utility import logger
40+
# Utils imports
41+
from src.utils.logger_utility import logger # noqa: E402
4042

4143

42-
class Main(QWidget):
44+
class Main(QMainWindow):
4345
"""Main class for the application"""
4446

4547
styleSheetUpdated = Signal(str)
4648
videoEditorOpened = Signal(str)
4749

4850
@logger.catch
49-
def __init__(self, parent=None):
51+
def __init__(self, parent: Optional[QWidget] = None) -> None:
5052
"""Initializer"""
5153
super().__init__(parent)
5254
self.customStyleSheet = ""
53-
self.settings = load_settings()
54-
self.themes = load_themes()
55-
self.current_theme = load_current_theme()
55+
self.settings: Dict = load_settings()
56+
self.themes: Dict = load_themes()
57+
self.current_theme: Dict = load_current_theme()
5658
logger.info("Main window initialized")
5759
self.load_ui()
5860

5961
@logger.catch
60-
def resizeEvent(self, event):
62+
def resizeEvent(self, event: QResizeEvent) -> None:
6163
"""Resize event for the main window"""
6264
super().resizeEvent(event)
6365
self.update_image()
66+
self.update_table_view()
6467

6568
@logger.catch
66-
def update_image(self):
69+
def update_image(self) -> None:
6770
"""Update the image based on the theme and resize event."""
6871

6972
if (
7073
"latte" in self.current_theme["name"].lower()
7174
or "light" in self.current_theme["name"].lower()
7275
):
73-
image_path = "docs/_static/ManimStudioLogoLight.png"
76+
image_path: str = "docs/_static/ManimStudioLogoLight.png"
7477
else:
75-
image_path = "docs/_static/ManimStudioLogoDark.png"
78+
image_path: str = "docs/_static/ManimStudioLogoDark.png"
7679

77-
pixmap = QPixmap(image_path)
80+
pixmap: QPixmap = QPixmap(image_path)
7881
if not pixmap.isNull():
79-
scaledPixmap = pixmap.scaled(
80-
self.ui.label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation
82+
scaledPixmap: QPixmap = pixmap.scaled(
83+
self.ui.label.size(),
84+
Qt.AspectRatioMode.KeepAspectRatio,
85+
Qt.TransformationMode.SmoothTransformation,
8186
)
8287
self.ui.label.setPixmap(scaledPixmap)
8388

84-
self.ui.label.setAlignment(Qt.AlignCenter)
89+
self.ui.label.setAlignment(Qt.AlignmentFlag.AlignCenter)
8590

8691
@logger.catch
87-
def apply_stylesheet(self):
92+
def update_table_view(self) -> None:
93+
"""Update the table view based on the theme and resize event."""
94+
# Resize to fit the window, expand if needed
95+
self.ui.recentProjectsTableView.horizontalHeader().setSectionResizeMode(
96+
QHeaderView.ResizeMode.Stretch
97+
)
98+
99+
@logger.catch
100+
def apply_stylesheet(self) -> None:
88101
"""Apply the stylesheet to the main window and update the image based on the theme"""
89102

90103
# Set the custom stylesheet based on the current theme
91-
self.customStyleSheet = f"background-color: {self.current_theme['background']}; color: {self.current_theme['font']}; border-color: {self.current_theme['primary']}; font-size: {self.settings['fontSize']}px; font-family: {self.settings['fontFamily']}; "
104+
self.customStyleSheet: str = f"background-color: {self.current_theme['background']}; color: {self.current_theme['font']}; border-color: {self.current_theme['primary']}; font-size: {self.settings['fontSize']}px; font-family: {self.settings['fontFamily']}; "
92105
self.setStyleSheet(self.customStyleSheet)
93106

107+
self.customMenubarStylesheet = str(
108+
f"background-color: {self.current_theme['primary']}; color: {self.current_theme['font']}; border-color: {self.current_theme['primary']}; font-size: {self.settings['fontSize']}px; font-family: {self.settings['fontFamily']}; "
109+
)
110+
self.ui.menubar.setStyleSheet(self.customMenubarStylesheet)
111+
94112
# Update the image
95113
self.update_image()
96114

97115
self.styleSheetUpdated.emit(self.customStyleSheet)
98116
logger.info("Stylesheet applied")
99117

100118
@logger.catch
101-
def load_ui(self):
119+
def load_ui(self) -> None:
102120
"""Load the UI from the .ui file"""
103121

104-
self.ui = Ui_Main()
122+
self.ui: Ui_MainWindow = Ui_MainWindow()
105123
self.ui.setupUi(self)
106124

107-
self.ui.label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
125+
self.ui.label.setSizePolicy(
126+
QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
127+
)
128+
108129
# Apply the theme
109130
self.apply_stylesheet()
110131

111-
self.ui.settingsBtn.clicked.connect(self.open_settings_dialog)
112-
self.ui.newProjectBtn.clicked.connect(self.showProjectCreationDialog)
113-
self.ui.openProjectBtn.clicked.connect(self.showProjectOpenDialog)
132+
# Create new menubar item
133+
settings_action = QAction("Settings", self)
134+
settings_action.triggered.connect(self.open_settings_dialog)
135+
self.ui.menubar.addAction(settings_action)
136+
137+
self.ui.newProjectBtn.clicked.connect(self.show_project_creation_dialog)
138+
self.ui.openProjectBtn.clicked.connect(self.show_project_open_dialog)
139+
140+
# Populate the recent projects list with the 10 latest projects
141+
self.populate_recent_projects()
142+
self.ui.recentProjectsTableView.doubleClicked.connect(
143+
self.open_project_from_list
144+
)
114145

115146
logger.info("UI loaded")
116147

117148
@logger.catch
118-
def open_settings_dialog(self):
149+
def populate_recent_projects(self):
150+
# Create a model with 3 columns
151+
model = QStandardItemModel(0, 3, self)
152+
model.setHorizontalHeaderLabels(["Project Path", "Last Modified", "Size"])
153+
154+
for project in get_recent_project_paths():
155+
# Convert the last_modified timestamp to a human-readable format
156+
last_modified_date = datetime.fromtimestamp(
157+
project["last_modified"]
158+
).strftime("%Y-%m-%d %H:%M:%S")
159+
# Format the size in a more readable format, e.g., in KB, MB
160+
size_kb = project["size"] / 1024 # Convert size to KB
161+
if size_kb < 1024:
162+
size_str = f"{size_kb:.2f} KB"
163+
else:
164+
size_mb = size_kb / 1024
165+
size_str = f"{size_mb:.2f} MB"
166+
167+
# Create items for each column
168+
path_item = QStandardItem(project["path"])
169+
modified_item = QStandardItem(last_modified_date)
170+
size_item = QStandardItem(size_str)
171+
172+
# Append the row to the model
173+
model.appendRow([path_item, modified_item, size_item])
174+
175+
# Set the model to the table view
176+
self.ui.recentProjectsTableView.setModel(model)
177+
# Resize columns to fit content
178+
self.ui.recentProjectsTableView.resizeColumnsToContents()
179+
180+
@logger.catch
181+
def open_project_from_list(self, index):
182+
# Retrieve the project path from the model item at the clicked index
183+
model = self.ui.recentProjectsTableView.model()
184+
project_path = model.data(
185+
model.index(index.row(), 0)
186+
) # Assuming the project path is in the first column
187+
# Now you can call the method to open the project
188+
self.open_video_editor(project_path)
189+
190+
@logger.catch
191+
def open_settings_dialog(self) -> None:
119192
"""Open the settings dialog"""
120-
self.settingsDialog = QDialog()
121-
self.uiSettings = Ui_Form()
193+
self.settingsDialog: QDialog = QDialog()
194+
self.uiSettings: Ui_Form = Ui_Form()
122195
self.uiSettings.setupUi(self.settingsDialog)
123196

124197
# Change window title
@@ -154,25 +227,27 @@ def open_settings_dialog(self):
154227
self.settingsDialog.exec()
155228

156229
@logger.catch
157-
def update_settings_from_dialog(self):
230+
def update_settings_from_dialog(self) -> None:
158231
"""Update the settings from the dialog, and update the UI"""
159232

160233
# Get recentProjects array from the current settings
161-
recentProjectPaths = self.settings.get("recentProjectPaths", [])
234+
recentProjectPaths: List[str] = self.settings.get("recentProjectPaths", [])
162235

163236
# Get recentProjectCreationPaths array from the current settings
164-
recentProjectCreationPaths = self.settings.get("recentProjectCreationPaths", [])
237+
recentProjectCreationPaths: List[str] = self.settings.get(
238+
"recentProjectCreationPaths", []
239+
)
165240

166241
# Get the current values from the dialog
167-
fontSize = self.uiSettings.fontSizeSpinBox.value()
168-
fontFamily = self.uiSettings.fontComboBox.currentText()
242+
fontSize: int = self.uiSettings.fontSizeSpinBox.value()
243+
fontFamily: str = self.uiSettings.fontComboBox.currentText()
169244

170245
# Extract full theme data from the selected item in the combobox
171-
theme_data_json = self.uiSettings.themeComboBox.currentData()
172-
selected_theme = json.loads(theme_data_json)
246+
theme_data_json: str = self.uiSettings.themeComboBox.currentData()
247+
selected_theme: Dict = json.loads(theme_data_json)
173248

174249
# Create a new settings object
175-
new_settings = {
250+
new_settings: Dict = {
176251
"fontSize": fontSize,
177252
"fontFamily": fontFamily,
178253
"theme": selected_theme,
@@ -190,12 +265,12 @@ def update_settings_from_dialog(self):
190265
self.apply_stylesheet()
191266

192267
@logger.catch
193-
def showProjectCreationDialog(self):
268+
def show_project_creation_dialog(self) -> None:
194269
"""Show the project creation dialog"""
195270
try:
196-
dialog = ProjectCreationDialog(self)
271+
dialog: ProjectCreationDialog = ProjectCreationDialog(self)
197272
dialog.projectCreated.connect(
198-
self.openVideoEditor
273+
self.open_video_editor
199274
) # Connect to the new method
200275
self.videoEditorOpened.connect(
201276
dialog.close
@@ -210,11 +285,11 @@ def showProjectCreationDialog(self):
210285
logger.error(f"Error showing project creation dialog: {e}")
211286

212287
@logger.catch
213-
def showProjectOpenDialog(self):
288+
def show_project_open_dialog(self) -> None:
214289
"""Show the project open dialog"""
215290
try:
216-
dialog = ProjectOpeningDialog(self)
217-
dialog.projectSelected.connect(self.openVideoEditor)
291+
dialog: ProjectOpeningDialog = ProjectOpeningDialog(self)
292+
dialog.projectSelected.connect(self.open_video_editor)
218293
self.videoEditorOpened.connect(
219294
dialog.close
220295
) # Close dialog when VideoEditor opens
@@ -226,19 +301,24 @@ def showProjectOpenDialog(self):
226301
logger.error(f"Error showing project open dialog: {e}")
227302

228303
@logger.catch
229-
def openVideoEditor(self, projectFilePath):
304+
def open_video_editor(self, project_file_path: str) -> None:
230305
"""Open the video editor with the project file"""
231306
try:
232-
self.videoEditor = QDialog()
233-
self.uiVideoEditor = Ui_VideoEditor()
307+
self.videoEditor: QDialog = QDialog()
308+
self.uiVideoEditor: Ui_VideoEditor = Ui_VideoEditor()
234309
self.uiVideoEditor.setupUi(self.videoEditor)
310+
235311
# Apply the theme
236312
self.videoEditor.setStyleSheet(self.customStyleSheet)
313+
237314
# Change window title as current project name with file path
238-
self.videoEditor.setWindowTitle(f"Manim Studio - {projectFilePath}")
315+
self.videoEditor.setWindowTitle(f"Manim Studio - {project_file_path}")
239316

240317
# Emit the signal after VideoEditor dialog is opened
241-
self.videoEditorOpened.emit(projectFilePath)
318+
self.videoEditorOpened.emit(project_file_path)
319+
320+
self.close()
321+
242322
self.videoEditor.exec()
243323
except Exception as e:
244324
logger.error(f"Error opening video editor: {e}")

0 commit comments

Comments
 (0)