Skip to content

Commit 19150dc

Browse files
committed
Initial commit
1 parent 37930cd commit 19150dc

File tree

10 files changed

+1445
-0
lines changed

10 files changed

+1445
-0
lines changed

controller.py

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
"""Controller for the NFA ShotGrid Project Creator, written by Mervin van Brakel (2024)"""
2+
3+
from __future__ import annotations
4+
5+
from PySide2 import QtCore
6+
7+
from model import ProjectCreatorModel
8+
9+
10+
class ProjectCreatorController:
11+
"""Controller for the ShotGrid Project Creator.
12+
13+
This controller handles all interactions between the view and the model.
14+
It also creates the model.
15+
16+
Attributes:
17+
view: The ProjectCreatorView class."""
18+
19+
def __init__(self, view):
20+
"""Initializes the controller class and creates the model."""
21+
self.view = view
22+
self.model = ProjectCreatorModel()
23+
24+
def connect_to_shotgrid(self) -> None:
25+
"""Starts the ShotGrid model connection on a seperate thread."""
26+
self.view.start_widget.hide()
27+
self.view.layout.addWidget(self.view.get_loading_widget())
28+
29+
self.shotgrid_connection_thread = ShotGridConnectionThread(self.model)
30+
self.shotgrid_connection_thread.connection_response_received.connect(
31+
self.connection_response_received
32+
)
33+
self.shotgrid_connection_thread.start()
34+
35+
def connection_response_received(
36+
self, connection_information: tuple(bool, str)
37+
) -> None:
38+
"""Runs when the ShotGrid connection thread is finished.
39+
Shows error if needed, else moves on to finding the username.
40+
41+
Args:
42+
connection_information: ShotGrid connection information
43+
"""
44+
connected, error = connection_information
45+
46+
if connected:
47+
self.find_username()
48+
else:
49+
self.view.loading_widget.hide()
50+
self.view.layout.addWidget(self.view.get_error_widget())
51+
self.view.error_text.setText(
52+
f"Error: {error}. Please contact a pipeliner if problem persist."
53+
)
54+
55+
def find_username(self) -> None:
56+
"""Checks the username with the model. If we can't find a ShotGrid username,
57+
we show the username selection sceen to the user.
58+
"""
59+
self.view.loading_widget.hide()
60+
61+
shotgrid_user = self.model.get_shotgrid_user_from_computer_username()
62+
63+
if shotgrid_user:
64+
self.model.set_user_information(shotgrid_user)
65+
self.view.layout.addWidget(
66+
self.view.get_main_widget(
67+
shotgrid_user.get("name"), self.model.usernames
68+
)
69+
)
70+
else:
71+
self.view.layout.addWidget(
72+
self.view.get_username_widget(self.model.usernames)
73+
)
74+
75+
def validate_username(self) -> None:
76+
"""Checks if user submitted username is in ShotGrid. Moves on to next
77+
step if username is correct."""
78+
shotgrid_user = self.model.get_shotgrid_user(
79+
self.view.username_lineedit.text()
80+
)
81+
82+
if not shotgrid_user:
83+
self.view.username_validation_text.setStyleSheet(
84+
"color: '#FF3E3E'; font-size: 12px;"
85+
)
86+
self.view.username_validation_text.setText(
87+
"Could not find user in ShotGrid database."
88+
)
89+
self.view.username_validation_text.show()
90+
91+
else:
92+
self.view.username_widget.hide()
93+
self.view.layout.addWidget(
94+
self.view.get_main_widget(
95+
shotgrid_user.get("name"), self.model.usernames
96+
)
97+
)
98+
self.model.set_user_information(shotgrid_user)
99+
100+
def validate_project_name(self, project_name: str) -> None:
101+
"""Validates project name and updates view."""
102+
validated, message = self.model.validate_project_name(project_name)
103+
project_name_validation_text = self.view.project_name_validation_text
104+
105+
project_name_validation_text.setText(message)
106+
project_name_validation_text.setStyleSheet(
107+
f"color: {'#8BFF3E' if validated else '#FF3E3E'}; font-size: 12px;"
108+
)
109+
project_name_validation_text.show()
110+
111+
def set_production_code_yes(self) -> None:
112+
"""Switches production code to yes and informs the model."""
113+
self.view.production_code_yes_button.setChecked(True)
114+
self.view.production_code_no_button.setChecked(False)
115+
self.model.set_has_production_code(True)
116+
self.view.production_code_enter_text.setText(
117+
"Enter the production code below."
118+
)
119+
120+
self.validate_project_code(self.view.project_code_lineedit.text())
121+
122+
def set_production_code_no(self) -> None:
123+
"""Switches production code to no and informs the model."""
124+
self.view.production_code_no_button.setChecked(True)
125+
self.view.production_code_yes_button.setChecked(False)
126+
self.model.set_has_production_code(False)
127+
self.view.production_code_enter_text.setText(
128+
"Come up with a three-letter code for your project. (e.g. ABC)"
129+
)
130+
131+
self.validate_project_code(self.view.project_code_lineedit.text())
132+
133+
def validate_project_code(self, project_code: str) -> None:
134+
"""Validates project name and updates view.
135+
136+
Args:
137+
project_code: String project code, either P#### or ABC.
138+
"""
139+
validated, message = self.model.validate_project_code(project_code)
140+
production_code_validation_text = (
141+
self.view.production_code_validation_text
142+
)
143+
144+
production_code_validation_text.setText(message)
145+
production_code_validation_text.setStyleSheet(
146+
f"color: {'#8BFF3E' if validated else '#FF3E3E'}; font-size: 12px;"
147+
)
148+
production_code_validation_text.show()
149+
150+
def add_supervisor(self) -> None:
151+
"""Tries to add the supervisor from the LineEdit to the list of supervisors."""
152+
validated, username, message = self.model.add_supervisor(
153+
self.view.supervisors_lineedit.text()
154+
)
155+
supervisors_validation_text = self.view.supervisors_validation_text
156+
157+
if validated:
158+
self.view.supervisors_list.insertItem(0, username)
159+
self.view.supervisors_list.setCurrentIndex(0)
160+
self.view.supervisors_lineedit.setText("")
161+
162+
supervisors_validation_text.setText(message)
163+
supervisors_validation_text.setStyleSheet(
164+
f"color: {'#8BFF3E' if validated else '#FF3E3E'}; font-size: 12px;"
165+
)
166+
supervisors_validation_text.show()
167+
168+
def remove_supervisor(self) -> None:
169+
"""Tries to remove a supervisor from the list."""
170+
removed, message = self.model.remove_supervisor(
171+
self.view.supervisors_list.currentText()
172+
)
173+
supervisors_validation_text = self.view.supervisors_validation_text
174+
175+
if removed:
176+
self.view.supervisors_list.removeItem(
177+
self.view.supervisors_list.findText(
178+
self.view.supervisors_list.currentText()
179+
)
180+
)
181+
182+
supervisors_validation_text.setText(message)
183+
supervisors_validation_text.setStyleSheet(
184+
f"color: {'#8BFF3E' if removed else '#FF3E3E'}; font-size: 12px;"
185+
)
186+
supervisors_validation_text.show()
187+
188+
def set_render_engine(self, render_engine: str) -> None:
189+
"""Informs the model of the new render engine choice."""
190+
self.model.set_render_engine(render_engine)
191+
192+
def set_project_type_fiction(self) -> None:
193+
"""Sets the project type to fiction and informs the model."""
194+
self.view.project_type_fiction_button.setChecked(True)
195+
self.view.project_type_documentary_button.setChecked(False)
196+
self.model.set_project_type("Fiction")
197+
198+
def set_project_type_documentary(self) -> None:
199+
"""Sets the project type to documentary and informs the model."""
200+
self.view.project_type_documentary_button.setChecked(True)
201+
self.view.project_type_fiction_button.setChecked(False)
202+
self.model.set_project_type("Documentary")
203+
204+
def set_fps(self, fps: int) -> None:
205+
"""Informs the model of the new FPS."""
206+
self.model.set_fps(fps)
207+
208+
def create_project(self) -> None:
209+
"""Validates, then creates the project."""
210+
validated, message = self.model.validate_project()
211+
212+
if not validated:
213+
self.view.project_validation_text.setText(message)
214+
self.view.project_validation_text.setStyleSheet(
215+
"color: '#FF3E3E'; font-size: 12px;"
216+
)
217+
self.view.project_validation_text.show()
218+
return
219+
220+
self.view.main_widget.hide()
221+
self.view.loading_text.setText("Creating project...")
222+
self.view.loading_widget.show()
223+
224+
self.project_creation_thread = ProjectCreationThread(self.model)
225+
self.project_creation_thread.project_creation_finished.connect(
226+
self.project_creation_finished
227+
)
228+
self.project_creation_thread.start()
229+
230+
def project_creation_finished(self, project_information: tuple) -> None:
231+
"""Runs when project creation is finished on the seperate thread.
232+
233+
Args:
234+
project_information: Whether or not creation was successful and error/link
235+
"""
236+
created, message = project_information
237+
self.view.loading_widget.hide()
238+
239+
if not created:
240+
self.view.main_widget.hide()
241+
self.view.layout.addWidget(self.view.get_error_widget())
242+
self.view.error_text.setText(
243+
f"Error: {message}. Please contact a pipeliner if problem persist."
244+
)
245+
return
246+
247+
self.view.layout.addWidget(
248+
self.view.get_project_creation_successful_widget(message)
249+
)
250+
251+
252+
class ShotGridConnectionThread(QtCore.QThread):
253+
"""Class for connecting to ShotGrid on a seperate thread
254+
so the UI doesn't freeze."""
255+
256+
connection_response_received = QtCore.Signal(object)
257+
258+
def __init__(self, model):
259+
super().__init__()
260+
self.model = model
261+
262+
def run(self):
263+
connection_information = self.model.connect_to_shotgrid()
264+
self.connection_response_received.emit(connection_information)
265+
266+
267+
class ProjectCreationThread(QtCore.QThread):
268+
"""Class for creating the ShotGrid project on a seperate thread
269+
so the UI doesn't freeze."""
270+
271+
project_creation_finished = QtCore.Signal(object)
272+
273+
def __init__(self, model):
274+
super().__init__()
275+
self.model = model
276+
277+
def run(self):
278+
created_project_information = self.model.create_project()
279+
self.project_creation_finished.emit(created_project_information)

main.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""Main start file for the NFA ShotGrid Project Creator, written by Mervin van Brakel (2024)"""
2+
3+
import sys
4+
5+
from PySide2 import QtWidgets
6+
7+
from view import ProjectCreatorView
8+
9+
if __name__ == "__main__":
10+
app = QtWidgets.QApplication(sys.argv)
11+
12+
widget = ProjectCreatorView()
13+
widget.show()
14+
15+
sys.exit(app.exec_())

0 commit comments

Comments
 (0)