)
158 | Mari: tk-mari
159 | Maya: tk-maya
160 | Motion Builder: tk-motionbuilder
161 | Photoshop: tk-ps
162 | Nuke: tk-nuke
163 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | SHOTGUN PIPELINE TOOLKIT SOURCE CODE LICENSE
2 |
3 | Version: 7/07/2013
4 |
5 | Shotgun Software Inc. ("Company") provides the Shotgun Pipeline Toolkit,
6 | software, including source code, in this package or repository folder (the
7 | "Shotgun Toolkit Code") subject to your acceptance of and compliance with
8 | the following terms and conditions (the "License Terms"). By accessing,
9 | downloading, copying, using or modifying any of the Shotgun Toolkit Code,
10 | you agree to these License Terms.
11 |
12 | Eligibility
13 |
14 | The following license to the Shotgun Toolkit Code is valid only if and while
15 | you are a customer of Company in good standing with either: (a) a current,
16 | paid-up (or free-for-evaluation) subscription or fixed-term license for
17 | Company's Shotgun Platform; or (b) a perpetual license and current, paid-up
18 | maintenance and support contract for the Shotgun Platform.
19 |
20 | Shotgun Toolkit Code License
21 |
22 | Subject to the eligibility criteria above and your compliance with these
23 | License Terms, Company grants to you a non-exclusive, limited license to
24 | reproduce, use, and make derivative works of (including by compiling object
25 | code versions of) the Shotgun Toolkit Code solely for your non-commercial or
26 | internal business purposes in connection with your authorized use of the
27 | Shotgun Platform.
28 |
29 | Company reserves all rights in the Shotgun Toolkit Code not expressly granted
30 | above. These License Terms do not grant or require Company to grant, by
31 | implication, estoppel, or otherwise, any other licenses or rights with respect
32 | to the Shotgun Toolkit Code or any of Company's other software or intellectual
33 | property rights. You agree not to take any action with respect to the Shotgun
34 | Toolkit Code that is not expressly authorized above.
35 |
36 | You must keep intact (and, in the case of copies, reproduce) all copyright
37 | and other proprietary notices, including all references to and copies of these
38 | License Terms, as originally included on, in, or with the Shotgun Toolkit
39 | Code. You must ensure that all derivative works you make of the Shotgun
40 | Toolkit Code contain or are accompanied by comparable and conspicuous notices
41 | that the underlying Shotgun Toolkit Code is the confidential information of
42 | Company and is subject to Company's copyrights and these License Terms.
43 |
44 | No Redistribution or Disclosure
45 |
46 | You acknowledge that the Shotgun Toolkit Code is and contains proprietary and
47 | trade-secret information of Company. You may not distribute, disclose to any
48 | third party, operate for the benefit of third parties (for example, on a
49 | hosted basis), or otherwise commercially exploit the Shotgun Toolkit Code or
50 | any portion or derivative work thereof without Company's separate and express
51 | written consent. For purposes of this restriction, third parties do not
52 | include your employees or agents acting on your behalf who are bound to abide
53 | by these License Terms.
54 |
55 | No Warranties or Support
56 |
57 | The Shotgun Toolkit Code is provided "AS IS" and with all faults. Company
58 | makes no warranties whatsoever, whether express, implied, or otherwise,
59 | concerning the Shotgun Toolkit Code. Company has no obligation to provide
60 | maintenance or technical support for the Shotgun Toolkit Code (unless
61 | otherwise expressly agreed in a separate written agreement between you and
62 | Company).
63 |
64 | Liability
65 |
66 | You agree to be solely responsible for your use and modifications of the
67 | Shotgun Toolkit Code, and for any harm or liability arising out of such use
68 | or modifications, including but not limited to any liability for infringement
69 | of third-party intellectual property rights.
70 |
71 | To the fullest extent permitted under applicable law, you agree that: (a)
72 | Company will not be liable under these License Terms or otherwise for any
73 | direct, indirect, incidental, special, consequential, or exemplary damages,
74 | including but not limited to damages for loss of profits, goodwill, use, data
75 | or other intangible losses, in relation to the Shotgun Toolkit Code or your
76 | use or inability to use the Shotgun Toolkit Code, even if Company has been
77 | advised of the possibility of such damages; and (b) in any event, Company's
78 | aggregate liability under these License Terms or in connection with the
79 | Shotgun Toolkit Code, regardless of the form of action and under any theory
80 | (whether in contract, tort, statutory, or otherwise), will not exceed the
81 | greater of $50 or the amount (if any) that you actually paid for access to
82 | the Shotgun Toolkit Code.
83 |
84 | Ownership
85 |
86 | Company retains sole and exclusive ownership of the Shotgun Toolkit Code and
87 | all copyright and other intellectual property rights therein. You will own any
88 | derivative works you make to the Shotgun Toolkit Code, subject to: (a) the
89 | preceding sentence; and (b) the provisions below regarding ownership of any
90 | code you elect to contribute to Company.
91 |
92 | Contributions
93 |
94 | The following terms apply to any derivative works of the Shotgun Toolkit Code
95 | (or any other materials) that you choose to contribute to Company.
96 |
97 | For good and valuable consideration, receipt of which is acknowledged, you
98 | hereby transfer and assign to Company your entire right, title, and interest
99 | (including all rights under copyright) in: (a) any software code,
100 | documentation, and/or other materials that you deliver to Company for
101 | inclusion in, improvement of, use with, or documentation of Company's software
102 | program(s), including but not limited to any code, documentation, and/or other
103 | materials identified in a contribution form you submit to Company in an
104 | applicable form designated by Company; and (b) any future revisions of such
105 | code, documentation, and/or other materials that you make hereafter. The code,
106 | documentation, other materials, and future revisions described above are
107 | collectively referred to below as the "Contribution."
108 |
109 | As used below, the "Company Programs" means and includes the Company software
110 | program(s) identified on any contribution form you submit to Company, and any
111 | other software into which Company incorporates or with which Company uses or
112 | distributes the Contribution or any version or portion thereof.
113 |
114 | Company grants you a non-exclusive right to continue to modify, make
115 | derivative works of, reproduce, and use the Contribution for your
116 | non-commercial or internal business purposes, and to further Company's
117 | development of Company Programs. This grant does not: (a) limit Company's
118 | rights, (b) grant you any rights with respect to the Company Programs; nor
119 | (c) permit you to distribute, operate for the benefit of third parties (for
120 | example, on a hosted basis), or otherwise commercially exploit the
121 | Contribution.
122 |
123 | You acknowledge that if Company elects to distribute the Contribution or any
124 | version or portion thereof, it may do so on any basis that it chooses
125 | (including under any proprietary or open-source licensing terms), without
126 | further compensation to you.
127 |
128 | You agree that if you have or acquire hereafter any patent or interface
129 | copyright or other intellectual property interest dominating the Contribution
130 | or any Company Programs (or use thereof), such dominating interest will not be
131 | used to undermine the effect of the assignment set forth above. Accordingly,
132 | Company and its direct and indirect licensees are licensed to make, use, sell,
133 | distribute, and otherwise exploit, in the Company Programs and their future
134 | versions and derivative works, without royalty or limitation, the subject
135 | matter of the dominating interest. This license provision will be binding on
136 | you and on any assignees of, or other successors to, the dominating interest.
137 |
138 | You hereby represent and warrant that you are the sole copyright holder for
139 | the Contribution and that you have the right and power to enter into this
140 | contract. You shall indemnify and hold harmless Company and its officers,
141 | employees, and agents against any and all claims, actions or damages
142 | (including attorney's reasonable fees) asserted by or paid to any party on
143 | account of a breach or alleged breach of the foregoing warranty. You make no
144 | other express or implied warranty (including without limitation any warranty
145 | of merchantability or fitness for a particular purpose) regarding the
146 | Contribution.
--------------------------------------------------------------------------------
/resources/dialog.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Form
4 |
5 |
6 |
7 | 0
8 | 0
9 | 658
10 | 302
11 |
12 |
13 |
14 | Form
15 |
16 |
17 | -
18 |
19 |
20 |
21 | 0
22 |
23 |
24 |
25 |
26 | -
27 |
28 |
29 | Qt::Horizontal
30 |
31 |
32 |
33 | -
34 |
35 |
36 |
-
37 |
38 |
-
39 |
40 | Select Application
41 |
42 |
43 |
44 |
45 | -
46 |
47 |
48 | Extra Tokens:
49 |
50 |
51 |
52 | -
53 |
54 |
55 |
56 | 0
57 | 0
58 |
59 |
60 |
61 | Application:
62 |
63 |
64 |
65 | -
66 |
67 |
-
68 |
69 | Select Template
70 |
71 |
72 |
73 |
74 | -
75 |
76 |
77 | Toolkit Template:
78 |
79 |
80 |
81 | -
82 |
83 |
84 |
85 | 0
86 |
87 |
88 |
89 |
90 | -
91 |
92 |
93 | Template Description:
94 |
95 |
96 |
97 | -
98 |
99 |
100 |
101 |
102 |
103 |
104 | -
105 |
106 |
107 | Qt::Horizontal
108 |
109 |
110 |
111 |
112 |
113 |
114 | -
115 |
116 |
117 | Qt::Horizontal
118 |
119 |
120 |
121 | -
122 |
123 |
124 |
-
125 |
126 |
127 |
128 |
129 |
130 | true
131 |
132 |
133 |
134 | -
135 |
136 |
137 | color: rgb(67, 131, 168);
138 | font: 75 10pt "Arial";
139 |
140 |
141 | Directory Path:
142 |
143 |
144 |
145 | -
146 |
147 |
148 | color: rgb(67, 131, 168);
149 | font: 75 10pt "Arial";
150 |
151 |
152 | File Name:
153 |
154 |
155 |
156 | -
157 |
158 |
159 |
160 | 0
161 | 0
162 |
163 |
164 |
165 |
166 | 35
167 | 16777215
168 |
169 |
170 |
171 | Copy Path to Clipboard
172 |
173 |
174 |
175 |
176 |
177 |
178 | :/res/copy.png:/res/copy.png
179 |
180 |
181 |
182 | -
183 |
184 |
185 | color: rgb(67, 131, 168);
186 | font: 75 10pt "Arial";
187 |
188 |
189 | File Path:
190 |
191 |
192 |
193 | -
194 |
195 |
196 |
197 | 0
198 | 0
199 |
200 |
201 |
202 |
203 | 35
204 | 16777215
205 |
206 |
207 |
208 | Copy Path to Clipboard
209 |
210 |
211 |
212 |
213 |
214 |
215 | :/res/copy.png:/res/copy.png
216 |
217 |
218 |
219 | -
220 |
221 |
222 | Create File on Disk
223 |
224 |
225 |
226 |
227 |
228 |
229 | :/res/plus.png:/res/plus.png
230 |
231 |
232 |
233 | -
234 |
235 |
236 |
237 | 0
238 | 0
239 |
240 |
241 |
242 |
243 | 35
244 | 16777215
245 |
246 |
247 |
248 | Copy Path to Clipboard
249 |
250 |
251 |
252 |
253 |
254 |
255 | :/res/copy.png:/res/copy.png
256 |
257 |
258 |
259 | -
260 |
261 |
262 | Open in File Browser
263 |
264 |
265 |
266 |
267 |
268 |
269 | :/res/folder.png:/res/folder.png
270 |
271 |
272 |
273 | -
274 |
275 |
276 | Open in File Browser
277 |
278 |
279 |
280 |
281 |
282 |
283 | :/res/folder.png:/res/folder.png
284 |
285 |
286 |
287 | -
288 |
289 |
290 |
291 |
292 |
293 | true
294 |
295 |
296 |
297 | -
298 |
299 |
300 |
301 |
302 |
303 | true
304 |
305 |
306 |
307 |
308 |
309 |
310 | -
311 |
312 |
313 | Qt::Horizontal
314 |
315 |
316 |
317 | -
318 |
319 |
-
320 |
321 |
322 | color: rgb(67, 131, 168);
323 | font: 75 10pt "Arial";
324 |
325 |
326 | Copy File To File Path
327 |
328 |
329 |
330 | -
331 |
332 |
333 | -
334 |
335 |
336 | Browse to a file to copy
337 |
338 |
339 |
340 |
341 |
342 |
343 | :/res/folder.png:/res/folder.png
344 |
345 |
346 |
347 | -
348 |
349 |
350 | Copy given file (left) to the file path described above
351 |
352 |
353 |
354 |
355 |
356 |
357 | :/res/copy.png:/res/copy.png
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
--------------------------------------------------------------------------------
/python/app/ui/dialog.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | ################################################################################
4 | ## Form generated from reading UI file 'dialog.ui'
5 | ##
6 | ## Created by: Qt User Interface Compiler version 5.15.2
7 | ##
8 | ## WARNING! All changes made in this file will be lost when recompiling UI file!
9 | ################################################################################
10 |
11 | from PySide2.QtCore import *
12 | from PySide2.QtGui import *
13 | from PySide2.QtWidgets import *
14 |
15 | from . import resources_rc
16 |
17 | class Ui_Form(object):
18 | def setupUi(self, Form):
19 | if not Form.objectName():
20 | Form.setObjectName(u"Form")
21 | Form.resize(658, 302)
22 | self.verticalLayout = QVBoxLayout(Form)
23 | self.verticalLayout.setObjectName(u"verticalLayout")
24 | self.contentWidgetHolder = QWidget(Form)
25 | self.contentWidgetHolder.setObjectName(u"contentWidgetHolder")
26 | self.contentWidgetVerticalLayout = QVBoxLayout(self.contentWidgetHolder)
27 | self.contentWidgetVerticalLayout.setContentsMargins(0, 0, 0, 0)
28 | self.contentWidgetVerticalLayout.setObjectName(u"contentWidgetVerticalLayout")
29 |
30 | self.verticalLayout.addWidget(self.contentWidgetHolder)
31 |
32 | self.line_3 = QFrame(Form)
33 | self.line_3.setObjectName(u"line_3")
34 | self.line_3.setFrameShape(QFrame.HLine)
35 | self.line_3.setFrameShadow(QFrame.Sunken)
36 |
37 | self.verticalLayout.addWidget(self.line_3)
38 |
39 | self.widget_2 = QWidget(Form)
40 | self.widget_2.setObjectName(u"widget_2")
41 | self.gridLayout = QGridLayout(self.widget_2)
42 | self.gridLayout.setObjectName(u"gridLayout")
43 | self.appComboBox = QComboBox(self.widget_2)
44 | self.appComboBox.addItem("")
45 | self.appComboBox.setObjectName(u"appComboBox")
46 |
47 | self.gridLayout.addWidget(self.appComboBox, 0, 1, 1, 1)
48 |
49 | self.label_3 = QLabel(self.widget_2)
50 | self.label_3.setObjectName(u"label_3")
51 |
52 | self.gridLayout.addWidget(self.label_3, 4, 0, 1, 1)
53 |
54 | self.label = QLabel(self.widget_2)
55 | self.label.setObjectName(u"label")
56 | sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
57 | sizePolicy.setHorizontalStretch(0)
58 | sizePolicy.setVerticalStretch(0)
59 | sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
60 | self.label.setSizePolicy(sizePolicy)
61 |
62 | self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
63 |
64 | self.tkTemplateComboBox = QComboBox(self.widget_2)
65 | self.tkTemplateComboBox.addItem("")
66 | self.tkTemplateComboBox.setObjectName(u"tkTemplateComboBox")
67 |
68 | self.gridLayout.addWidget(self.tkTemplateComboBox, 1, 1, 1, 1)
69 |
70 | self.label_7 = QLabel(self.widget_2)
71 | self.label_7.setObjectName(u"label_7")
72 |
73 | self.gridLayout.addWidget(self.label_7, 1, 0, 1, 1)
74 |
75 | self.extraTokensWidget = QWidget(self.widget_2)
76 | self.extraTokensWidget.setObjectName(u"extraTokensWidget")
77 | self.extraTokensWidgetLayout = QGridLayout(self.extraTokensWidget)
78 | self.extraTokensWidgetLayout.setContentsMargins(0, 0, 0, 0)
79 | self.extraTokensWidgetLayout.setObjectName(u"extraTokensWidgetLayout")
80 |
81 | self.gridLayout.addWidget(self.extraTokensWidget, 4, 1, 1, 1)
82 |
83 | self.label_8 = QLabel(self.widget_2)
84 | self.label_8.setObjectName(u"label_8")
85 |
86 | self.gridLayout.addWidget(self.label_8, 2, 0, 1, 1)
87 |
88 | self.descriptionLabel = QLabel(self.widget_2)
89 | self.descriptionLabel.setObjectName(u"descriptionLabel")
90 |
91 | self.gridLayout.addWidget(self.descriptionLabel, 2, 1, 1, 1)
92 |
93 | self.line_4 = QFrame(self.widget_2)
94 | self.line_4.setObjectName(u"line_4")
95 | self.line_4.setFrameShape(QFrame.HLine)
96 | self.line_4.setFrameShadow(QFrame.Sunken)
97 |
98 | self.gridLayout.addWidget(self.line_4, 3, 0, 1, 2)
99 |
100 |
101 | self.verticalLayout.addWidget(self.widget_2)
102 |
103 | self.line_2 = QFrame(Form)
104 | self.line_2.setObjectName(u"line_2")
105 | self.line_2.setFrameShape(QFrame.HLine)
106 | self.line_2.setFrameShadow(QFrame.Sunken)
107 |
108 | self.verticalLayout.addWidget(self.line_2)
109 |
110 | self.filePathWidgetGroup = QWidget(Form)
111 | self.filePathWidgetGroup.setObjectName(u"filePathWidgetGroup")
112 | self.gridLayout_3 = QGridLayout(self.filePathWidgetGroup)
113 | self.gridLayout_3.setObjectName(u"gridLayout_3")
114 | self.filePathLineEdit = QLineEdit(self.filePathWidgetGroup)
115 | self.filePathLineEdit.setObjectName(u"filePathLineEdit")
116 | self.filePathLineEdit.setReadOnly(True)
117 |
118 | self.gridLayout_3.addWidget(self.filePathLineEdit, 2, 1, 1, 1)
119 |
120 | self.label_5 = QLabel(self.filePathWidgetGroup)
121 | self.label_5.setObjectName(u"label_5")
122 | self.label_5.setStyleSheet(u"color: rgb(67, 131, 168);\n"
123 | "font: 75 10pt \"Arial\";")
124 |
125 | self.gridLayout_3.addWidget(self.label_5, 1, 0, 1, 1)
126 |
127 | self.label_4 = QLabel(self.filePathWidgetGroup)
128 | self.label_4.setObjectName(u"label_4")
129 | self.label_4.setStyleSheet(u"color: rgb(67, 131, 168);\n"
130 | "font: 75 10pt \"Arial\";")
131 |
132 | self.gridLayout_3.addWidget(self.label_4, 0, 0, 1, 1)
133 |
134 | self.directoryPathCopyButton = QPushButton(self.filePathWidgetGroup)
135 | self.directoryPathCopyButton.setObjectName(u"directoryPathCopyButton")
136 | sizePolicy1 = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
137 | sizePolicy1.setHorizontalStretch(0)
138 | sizePolicy1.setVerticalStretch(0)
139 | sizePolicy1.setHeightForWidth(self.directoryPathCopyButton.sizePolicy().hasHeightForWidth())
140 | self.directoryPathCopyButton.setSizePolicy(sizePolicy1)
141 | self.directoryPathCopyButton.setMaximumSize(QSize(35, 16777215))
142 | icon = QIcon()
143 | icon.addFile(u":/res/copy.png", QSize(), QIcon.Normal, QIcon.Off)
144 | self.directoryPathCopyButton.setIcon(icon)
145 |
146 | self.gridLayout_3.addWidget(self.directoryPathCopyButton, 1, 4, 1, 1)
147 |
148 | self.label_6 = QLabel(self.filePathWidgetGroup)
149 | self.label_6.setObjectName(u"label_6")
150 | self.label_6.setStyleSheet(u"color: rgb(67, 131, 168);\n"
151 | "font: 75 10pt \"Arial\";")
152 |
153 | self.gridLayout_3.addWidget(self.label_6, 2, 0, 1, 1)
154 |
155 | self.fileNameCopyButton = QPushButton(self.filePathWidgetGroup)
156 | self.fileNameCopyButton.setObjectName(u"fileNameCopyButton")
157 | sizePolicy1.setHeightForWidth(self.fileNameCopyButton.sizePolicy().hasHeightForWidth())
158 | self.fileNameCopyButton.setSizePolicy(sizePolicy1)
159 | self.fileNameCopyButton.setMaximumSize(QSize(35, 16777215))
160 | self.fileNameCopyButton.setIcon(icon)
161 |
162 | self.gridLayout_3.addWidget(self.fileNameCopyButton, 0, 4, 1, 1)
163 |
164 | self.createFileButton = QPushButton(self.filePathWidgetGroup)
165 | self.createFileButton.setObjectName(u"createFileButton")
166 | icon1 = QIcon()
167 | icon1.addFile(u":/res/plus.png", QSize(), QIcon.Normal, QIcon.Off)
168 | self.createFileButton.setIcon(icon1)
169 |
170 | self.gridLayout_3.addWidget(self.createFileButton, 2, 2, 1, 1)
171 |
172 | self.filePathCopyButton = QPushButton(self.filePathWidgetGroup)
173 | self.filePathCopyButton.setObjectName(u"filePathCopyButton")
174 | sizePolicy1.setHeightForWidth(self.filePathCopyButton.sizePolicy().hasHeightForWidth())
175 | self.filePathCopyButton.setSizePolicy(sizePolicy1)
176 | self.filePathCopyButton.setMaximumSize(QSize(35, 16777215))
177 | self.filePathCopyButton.setIcon(icon)
178 |
179 | self.gridLayout_3.addWidget(self.filePathCopyButton, 2, 4, 1, 1)
180 |
181 | self.filePathOpenButton = QPushButton(self.filePathWidgetGroup)
182 | self.filePathOpenButton.setObjectName(u"filePathOpenButton")
183 | icon2 = QIcon()
184 | icon2.addFile(u":/res/folder.png", QSize(), QIcon.Normal, QIcon.Off)
185 | self.filePathOpenButton.setIcon(icon2)
186 |
187 | self.gridLayout_3.addWidget(self.filePathOpenButton, 2, 3, 1, 1)
188 |
189 | self.directoryPathOpenButton = QPushButton(self.filePathWidgetGroup)
190 | self.directoryPathOpenButton.setObjectName(u"directoryPathOpenButton")
191 | self.directoryPathOpenButton.setIcon(icon2)
192 |
193 | self.gridLayout_3.addWidget(self.directoryPathOpenButton, 1, 3, 1, 1)
194 |
195 | self.dirPathLineEdit = QLineEdit(self.filePathWidgetGroup)
196 | self.dirPathLineEdit.setObjectName(u"dirPathLineEdit")
197 | self.dirPathLineEdit.setReadOnly(True)
198 |
199 | self.gridLayout_3.addWidget(self.dirPathLineEdit, 1, 1, 1, 2)
200 |
201 | self.fileNameLineEdit = QLineEdit(self.filePathWidgetGroup)
202 | self.fileNameLineEdit.setObjectName(u"fileNameLineEdit")
203 | self.fileNameLineEdit.setReadOnly(True)
204 |
205 | self.gridLayout_3.addWidget(self.fileNameLineEdit, 0, 1, 1, 3)
206 |
207 |
208 | self.verticalLayout.addWidget(self.filePathWidgetGroup)
209 |
210 | self.line_5 = QFrame(Form)
211 | self.line_5.setObjectName(u"line_5")
212 | self.line_5.setFrameShape(QFrame.HLine)
213 | self.line_5.setFrameShadow(QFrame.Sunken)
214 |
215 | self.verticalLayout.addWidget(self.line_5)
216 |
217 | self.horizontalLayout = QHBoxLayout()
218 | self.horizontalLayout.setObjectName(u"horizontalLayout")
219 | self.label_9 = QLabel(Form)
220 | self.label_9.setObjectName(u"label_9")
221 | self.label_9.setStyleSheet(u"color: rgb(67, 131, 168);\n"
222 | "font: 75 10pt \"Arial\";")
223 |
224 | self.horizontalLayout.addWidget(self.label_9)
225 |
226 | self.copyFileLineEdit = QLineEdit(Form)
227 | self.copyFileLineEdit.setObjectName(u"copyFileLineEdit")
228 |
229 | self.horizontalLayout.addWidget(self.copyFileLineEdit)
230 |
231 | self.copyFilePathOpenButton = QPushButton(Form)
232 | self.copyFilePathOpenButton.setObjectName(u"copyFilePathOpenButton")
233 | self.copyFilePathOpenButton.setIcon(icon2)
234 |
235 | self.horizontalLayout.addWidget(self.copyFilePathOpenButton)
236 |
237 | self.copyFileToFileButton = QPushButton(Form)
238 | self.copyFileToFileButton.setObjectName(u"copyFileToFileButton")
239 | self.copyFileToFileButton.setIcon(icon)
240 |
241 | self.horizontalLayout.addWidget(self.copyFileToFileButton)
242 |
243 |
244 | self.verticalLayout.addLayout(self.horizontalLayout)
245 |
246 |
247 | self.retranslateUi(Form)
248 |
249 | QMetaObject.connectSlotsByName(Form)
250 | # setupUi
251 |
252 | def retranslateUi(self, Form):
253 | Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None))
254 | self.appComboBox.setItemText(0, QCoreApplication.translate("Form", u"Select Application", None))
255 |
256 | self.label_3.setText(QCoreApplication.translate("Form", u"Extra Tokens:", None))
257 | self.label.setText(QCoreApplication.translate("Form", u"Application:", None))
258 | self.tkTemplateComboBox.setItemText(0, QCoreApplication.translate("Form", u"Select Template", None))
259 |
260 | self.label_7.setText(QCoreApplication.translate("Form", u"Toolkit Template:", None))
261 | self.label_8.setText(QCoreApplication.translate("Form", u"Template Description:", None))
262 | self.descriptionLabel.setText("")
263 | self.filePathLineEdit.setText("")
264 | self.label_5.setText(QCoreApplication.translate("Form", u"Directory Path:", None))
265 | self.label_4.setText(QCoreApplication.translate("Form", u"File Name:", None))
266 | #if QT_CONFIG(tooltip)
267 | self.directoryPathCopyButton.setToolTip(QCoreApplication.translate("Form", u"Copy Path to Clipboard", None))
268 | #endif // QT_CONFIG(tooltip)
269 | self.directoryPathCopyButton.setText("")
270 | self.label_6.setText(QCoreApplication.translate("Form", u"File Path:", None))
271 | #if QT_CONFIG(tooltip)
272 | self.fileNameCopyButton.setToolTip(QCoreApplication.translate("Form", u"Copy Path to Clipboard", None))
273 | #endif // QT_CONFIG(tooltip)
274 | self.fileNameCopyButton.setText("")
275 | #if QT_CONFIG(tooltip)
276 | self.createFileButton.setToolTip(QCoreApplication.translate("Form", u"Create File on Disk", None))
277 | #endif // QT_CONFIG(tooltip)
278 | self.createFileButton.setText("")
279 | #if QT_CONFIG(tooltip)
280 | self.filePathCopyButton.setToolTip(QCoreApplication.translate("Form", u"Copy Path to Clipboard", None))
281 | #endif // QT_CONFIG(tooltip)
282 | self.filePathCopyButton.setText("")
283 | #if QT_CONFIG(tooltip)
284 | self.filePathOpenButton.setToolTip(QCoreApplication.translate("Form", u"Open in File Browser", None))
285 | #endif // QT_CONFIG(tooltip)
286 | self.filePathOpenButton.setText("")
287 | #if QT_CONFIG(tooltip)
288 | self.directoryPathOpenButton.setToolTip(QCoreApplication.translate("Form", u"Open in File Browser", None))
289 | #endif // QT_CONFIG(tooltip)
290 | self.directoryPathOpenButton.setText("")
291 | self.dirPathLineEdit.setText("")
292 | self.fileNameLineEdit.setText("")
293 | self.label_9.setText(QCoreApplication.translate("Form", u"Copy File To File Path", None))
294 | #if QT_CONFIG(tooltip)
295 | self.copyFilePathOpenButton.setToolTip(QCoreApplication.translate("Form", u"Browse to a file to copy", None))
296 | #endif // QT_CONFIG(tooltip)
297 | self.copyFilePathOpenButton.setText("")
298 | #if QT_CONFIG(tooltip)
299 | self.copyFileToFileButton.setToolTip(QCoreApplication.translate("Form", u"Copy given file (left) to the file path described above", None))
300 | #endif // QT_CONFIG(tooltip)
301 | self.copyFileToFileButton.setText("")
302 | # retranslateUi
303 |
304 |
--------------------------------------------------------------------------------
/python/app/dialog.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2013 Shotgun Software Inc.
2 | #
3 | # CONFIDENTIAL AND PROPRIETARY
4 | #
5 | # This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit
6 | # Source Code License included in this distribution package. See LICENSE.
7 | # By accessing, using, copying or modifying this work you indicate your
8 | # agreement to the Shotgun Pipeline Toolkit Source Code License. All rights
9 | # not expressly granted therein are reserved by Shotgun Software Inc.
10 |
11 | import sys
12 | import os
13 | import re
14 | import shutil
15 | import subprocess
16 | from pprint import pprint, pformat
17 |
18 | import sgtk
19 | # # by importing QT from sgtk rather than directly, we ensure that
20 | # # the code will be compatible with both PySide and PyQt.
21 | from sgtk.platform.qt import QtCore, QtGui
22 | from .ui.dialog import Ui_Form
23 |
24 | # import the context_selector module from the qtwidgets framework
25 | context_selector = sgtk.platform.import_framework("tk-framework-qtwidgets", "context_selector")
26 |
27 | # import the context_selector module from the qtwidgets framework
28 | overlay_widget = sgtk.platform.import_framework("tk-framework-qtwidgets", "overlay_widget")
29 |
30 | # import the task_manager module from shotgunutils framework
31 | task_manager = sgtk.platform.import_framework("tk-framework-shotgunutils", "task_manager")
32 |
33 | # import the shotgun_globals module from shotgunutils framework
34 | shotgun_globals = sgtk.platform.import_framework("tk-framework-shotgunutils", "shotgun_globals")
35 |
36 |
37 | def show_dialog(app_instance):
38 | """
39 | Shows the main dialog window.
40 | """
41 | # in order to handle UIs seamlessly, each toolkit engine has methods for launching
42 | # different types of windows. By using these methods, your windows will be correctly
43 | # decorated and handled in a consistent fashion by the system.
44 |
45 | # we pass the dialog class to this method and leave the actual construction
46 | # to be carried out by toolkit.
47 | app_instance.engine.show_dialog("Naming Convention Tool", app_instance, AppDialog)
48 |
49 |
50 | class AppDialog(QtGui.QWidget):
51 | """
52 | Main application dialog window
53 | """
54 |
55 | def __init__(self):
56 | """
57 | Constructor
58 | """
59 | # first, call the base class and let it do its thing.
60 | QtGui.QWidget.__init__(self)
61 |
62 | self.context = None
63 | self.template = None
64 | self.fields = None
65 | self.ctx = None
66 |
67 | # Logging
68 | self.log = sgtk.platform.get_logger(__name__)
69 |
70 | # now load in the UI that was created in the UI designer
71 | self.ui = Ui_Form()
72 | self.ui.setupUi(self)
73 |
74 | # most of the useful accessors are available through the Application class instance
75 | # it is often handy to keep a reference to this. You can get it via the following method:
76 | self._app = sgtk.platform.current_bundle()
77 |
78 | # Get Application definitions
79 | self.applications = self._app.get_setting("tk-engines")
80 |
81 | self.template_definitions = self._app.get_setting("template_definitions")
82 |
83 | self.custom_entity_name_remap = self._app.get_setting("custom_entity_name_remap")
84 |
85 | self.restrict_entity_types = self._app.get_setting("restrict_entity_types")
86 | if self.restrict_entity_types:
87 | self.log.info('restrict_entity_types to: {}'.format(', '.join(self.restrict_entity_types)))
88 |
89 | self.restrict_entity_types_by_link = self._app.get_setting("restrict_entity_types_by_link")
90 |
91 | if self.restrict_entity_types_by_link:
92 | if 'entity' not in self.restrict_entity_types_by_link or 'field' not in self.restrict_entity_types_by_link:
93 | raise ValueError('"entity" or "field" key not defined for restrict_entity_types_by_link')
94 |
95 | self.log.info('restrict_entity_types_by_link to entity: {}, field: {}'.format(self.restrict_entity_types_by_link['entity'],
96 | self.restrict_entity_types_by_link['field']))
97 |
98 | if self.restrict_entity_types and self.restrict_entity_types_by_link:
99 | raise ValueError('Please use either restrict_entity_types_by_link OR restrict_entity_types, but not both.')
100 |
101 | # via the self._app handle we can for example access:
102 | # - The engine, via self._app.engine
103 | # - A Shotgun API instance, via self._app.shotgun
104 | # - A tk API instance, via self._app.tk
105 |
106 | # create a background task manager for each of our components to use
107 | self._task_manager = task_manager.BackgroundTaskManager(self)
108 | self._task_manager.start_processing()
109 |
110 | # register it with the globals module so that it can
111 | # use it to fetch data
112 | shotgun_globals.register_bg_task_manager(self._task_manager)
113 |
114 | self._context_widget = context_selector.ContextWidget(self)
115 | self._context_widget.set_up(self._task_manager)
116 |
117 | # Disable "Link" widget in context selector so end user have to use "Task" widget instead
118 | self._context_widget.ui.link_label.setEnabled(True)
119 | self._context_widget.ui.link_search.setEnabled(True)
120 | self._context_widget.ui.link_display.setEnabled(True)
121 | self._context_widget.ui.link_search_btn.setEnabled(True)
122 |
123 | self._context_widget.set_task_tooltip('Type in the name of the Asset or Shot you are working on')
124 |
125 | self._overlay_widget = overlay_widget.ShotgunOverlayWidget(self)
126 |
127 | # Specify what entries should show up in the list of links when using
128 | # the auto completer. In this case, we only show entity types that are
129 | # allowed for the PublishedFile.entity field. You can provide an
130 | # explicit list with the `restrict_entity_types()` method.
131 |
132 | if self.restrict_entity_types_by_link:
133 | self._context_widget.restrict_entity_types_by_link(self.restrict_entity_types_by_link['entity'],
134 | self.restrict_entity_types_by_link['field'])
135 |
136 | if self.restrict_entity_types:
137 | self._context_widget.restrict_entity_types(self.restrict_entity_types)
138 |
139 | # You can set the tooltip for each sub widget for context selection.
140 | # This helps describe to the user why they're choosing a task or link.
141 | self._context_widget.set_task_tooltip(
142 | "The task that the selected item will be associated with the Shotgun entity being acted upon.
")
143 |
144 | self._context_widget.set_link_tooltip(
145 | "The link that the selected item will be associated with the Shotgun entity being acted upon.
")
146 |
147 | # connect the signal emitted by the selector widget when a context is
148 | # selected. The connected callable should accept a context object.
149 | # self._context_widget.context_changed.connect(self._on_item_context_change)
150 |
151 | self.ui.contentWidgetVerticalLayout.addWidget(self._context_widget)
152 |
153 | # you can set a context using the `set_context()` method. Here we set it
154 | # to the current bundle's context
155 | self._context_widget.set_context(sgtk.platform.current_bundle().context)
156 | self._context_widget.context_changed.connect(self.define_tk_context)
157 | # self._context_widget.context_changed.connect(self.updateUi)
158 |
159 | # Connections
160 | self.ui.appComboBox.currentIndexChanged.connect(self.update_ui)
161 | self.ui.tkTemplateComboBox.currentIndexChanged.connect(self.update_ui)
162 |
163 | func = lambda x=self.ui.fileNameLineEdit: self.copy_path_to_clipboard(x)
164 | self.ui.fileNameCopyButton.released.connect(func)
165 |
166 | func = lambda x=self.ui.dirPathLineEdit: self.copy_path_to_clipboard(x)
167 | self.ui.directoryPathCopyButton.released.connect(func)
168 |
169 | func = lambda x=self.ui.filePathLineEdit: self.copy_path_to_clipboard(x)
170 | self.ui.filePathCopyButton.released.connect(func)
171 |
172 | func = lambda x=self.ui.dirPathLineEdit: self.open_file_path(x)
173 | self.ui.directoryPathOpenButton.released.connect(func)
174 |
175 | func = lambda x=self.ui.filePathLineEdit: self.open_file_path(x)
176 | self.ui.filePathOpenButton.released.connect(func)
177 |
178 | func = lambda x=self.ui.filePathLineEdit: self.create_file_on_disk(x)
179 | self.ui.createFileButton.released.connect(func)
180 |
181 | self.ui.copyFilePathOpenButton.released.connect(self.browse_file)
182 | self.ui.copyFileToFileButton.released.connect(self.copy_file_to_file_path)
183 |
184 | self.update_applications()
185 |
186 | def update_applications(self):
187 | self.ui.appComboBox.clear()
188 |
189 | self.ui.appComboBox.addItem(QtGui.QIcon(':/res/block.png'), 'Select Application')
190 |
191 | app_keys = list(self.applications.keys())
192 | app_keys.sort()
193 |
194 | for appKey in app_keys:
195 | self.ui.appComboBox.addItem(QtGui.QIcon(':/res/sg_logo.png'), appKey, self.applications[appKey])
196 |
197 | self.ui.appComboBox.currentIndexChanged.connect(self.update_tk_context)
198 | self.ui.appComboBox.currentIndexChanged.connect(self.update_tk_templates)
199 |
200 | def update_tk_templates(self):
201 | if not self.context:
202 | self.log.error('TK context is not defined, unable to update TK templates.')
203 | return
204 |
205 | tk = self._app.context.sgtk
206 |
207 | if not tk:
208 | self.log.error('Unable to get TK instance, unable to update TK templates.')
209 | return
210 |
211 | templates = tk.templates
212 |
213 | # Support non-engine template definitions. If None, then use engine key name instead. ie: Data > data > take_data
214 | app_name = str(self.ui.appComboBox.itemData(self.ui.appComboBox.currentIndex()))
215 | if app_name:
216 | app_name = app_name.lower().replace(' ', '').replace('tk-', '')
217 | else:
218 | app_name = self.ui.appComboBox.currentText().lower()
219 |
220 | entity_type = self.context.entity['type']
221 |
222 | # Remap entity name
223 | entity_type = self.custom_entity_name_remap.get(entity_type, entity_type).lower()
224 |
225 | regex = '%s_%s' % (entity_type, app_name)
226 |
227 | active_templates = {}
228 |
229 | for k, v in templates.iteritems():
230 | if re.search(regex, k):
231 | active_templates[k] = v
232 |
233 | self.ui.tkTemplateComboBox.clear()
234 |
235 | active_keys = active_templates.keys()
236 | active_keys.sort()
237 |
238 | self.ui.tkTemplateComboBox.addItem(QtGui.QIcon(':/res/block.png'), 'Select Template')
239 |
240 | for key in active_keys:
241 | key_title = key.replace(entity_type, '').replace('_', ' ').title()
242 | self.ui.tkTemplateComboBox.addItem(QtGui.QIcon(':/res/sg_logo.png'), key_title, active_templates[key])
243 |
244 | # Default to "work" template if available
245 | index = self.ui.tkTemplateComboBox.findText('Work', QtCore.Qt.MatchContains)
246 | if index > -1:
247 | self.ui.tkTemplateComboBox.setCurrentIndex(index)
248 |
249 | self.ui.tkTemplateComboBox.currentIndexChanged.connect(self.update_template_definition)
250 |
251 | self.update_template_definition()
252 |
253 | def update_template_definition(self):
254 | """
255 | Show template definition if a match
256 | :return: None
257 | """
258 |
259 | curr_templ = self.ui.tkTemplateComboBox.itemText(self.ui.tkTemplateComboBox.currentIndex())
260 | self.ui.descriptionLabel.setText('')
261 |
262 | for k, v in self.template_definitions.iteritems():
263 | if re.search(k, curr_templ, re.IGNORECASE):
264 | self.ui.descriptionLabel.setText(v)
265 |
266 | def define_tk_context(self, context):
267 | if not context.task:
268 | return
269 |
270 | self.ui.appComboBox.setCurrentIndex(0)
271 | self.ui.tkTemplateComboBox.setCurrentIndex(0)
272 | self.clear_extra_token_widgets()
273 |
274 | self.context = context
275 | self.log.info('Context, source: %s' % self.context.source_entity)
276 | self.log.info('Context, task: %s' % self.context.task)
277 |
278 | self.update_ui()
279 |
280 | def update_tk_context(self):
281 | """
282 | Register selected context with Toolkit
283 | :return:
284 | """
285 |
286 | if self.ui.appComboBox.currentIndex() == 0:
287 | return
288 |
289 | tkengine = self.ui.appComboBox.itemData(self.ui.appComboBox.currentIndex())
290 |
291 | # Try to get as deep into valid context
292 | if self.context.task:
293 | typ = self.context.task['type']
294 | idd = self.context.task['id']
295 |
296 | elif self.context.step:
297 | typ = self.context.step['type']
298 | idd = self.context.step['id']
299 |
300 | elif self.context.source_entity:
301 | typ = self.context.source_entity['type']
302 | idd = self.context.source_entity['id']
303 |
304 | try:
305 | self._overlay_widget.show_message('Registering context with Toolkit, please wait...
')
306 | QtGui.QApplication.instance().processEvents()
307 | self.log.info('Creating folder structure on disk')
308 | self._app.sgtk.create_filesystem_structure(typ, idd, engine=tkengine)
309 |
310 | except:
311 | pass
312 |
313 | finally:
314 | self._overlay_widget.hide()
315 |
316 | self.ctx = self._app.sgtk.context_from_entity(typ, idd)
317 |
318 | def update_template_output_paths(self):
319 | if self.ui.tkTemplateComboBox.currentIndex() == 0:
320 | return
321 |
322 | self.template = self.ui.tkTemplateComboBox.itemData(self.ui.tkTemplateComboBox.currentIndex())
323 |
324 | if not self.template:
325 | return
326 |
327 | # Update template definition label
328 | self.ui.tkTemplateComboBox.setToolTip(self.template.definition)
329 |
330 | self.fields = self.ctx.as_template_fields(self.template)
331 |
332 | missing_keys = self.template.missing_keys(self.fields)
333 |
334 | self.update_extra_tokens_widgets(self.template, missing_keys)
335 |
336 | def update_extra_tokens_widgets(self, template, missing_keys):
337 | missing_keys_dict = {}
338 |
339 | for key in missing_keys:
340 | if key in template.keys:
341 | keyObj = template.keys[key]
342 | missing_keys_dict[key] = keyObj
343 |
344 | parent = self.ui.extraTokensWidget
345 | parent_lay = self.ui.extraTokensWidgetLayout
346 |
347 | self.clear_extra_token_widgets()
348 |
349 | keys = missing_keys_dict.keys()
350 | keys.sort()
351 |
352 | row_cnt = 0
353 |
354 | if not keys:
355 | label = QtGui.QLabel('No Extra Keys found...', parent=parent)
356 | parent_lay.addWidget(label, row_cnt, 0)
357 |
358 | else:
359 | for key in keys:
360 | label = QtGui.QLabel(str(key), parent=parent)
361 | lineedit = QtGui.QLineEdit(str(missing_keys_dict[key].default), parent=parent)
362 | lineedit.data = missing_keys_dict[key] # Not ideal to store to object, but QLabel has no data storage method
363 |
364 | lineedit.editingFinished.connect(self.update_template_file_path)
365 |
366 | parent_lay.addWidget(label, row_cnt, 0)
367 | parent_lay.addWidget(lineedit, row_cnt, 1)
368 |
369 | row_cnt += 1
370 |
371 | def get_extra_token_definitions(self):
372 | """
373 | Loop through all child qlabels gathering user input values
374 | :return:
375 | """
376 | parent = self.ui.extraTokensWidget
377 |
378 | missing_keys = {}
379 | valid = True
380 |
381 | for widget in parent.children():
382 | if type(widget) == QtGui.QLineEdit:
383 |
384 | value = widget.text()
385 |
386 | if not value:
387 | widget.setStyleSheet('border: 1px solid red; border-radius: 6px;')
388 | valid = False
389 | else:
390 | widget.setStyleSheet('')
391 |
392 | if type(widget.data) == sgtk.templatekey.IntegerKey:
393 | missing_keys[widget.data.name] = int(value)
394 |
395 | elif type(widget.data) == sgtk.templatekey.StringKey:
396 | missing_keys[widget.data.name] = value
397 |
398 | return missing_keys, valid
399 |
400 | def clear_extra_token_widgets(self):
401 | parent = self.ui.extraTokensWidget
402 |
403 | # Delete child widgets of parent
404 | for widget in parent.children():
405 | if type(widget) == QtGui.QGridLayout:
406 | continue
407 | widget.deleteLater()
408 |
409 | def update_ui(self):
410 | # Disable widgets from user input
411 | self.ui.filePathWidgetGroup.setEnabled(False)
412 | self.ui.appComboBox.setEnabled(False)
413 | self.ui.tkTemplateComboBox.setEnabled(False)
414 | self.ui.fileNameLineEdit.setText('')
415 | self.ui.dirPathLineEdit.setText('')
416 | self.ui.filePathLineEdit.setText('')
417 | self.ui.copyFileLineEdit.setText('')
418 |
419 | if self.context:
420 | # Application combobox
421 | self.ui.appComboBox.setEnabled(True)
422 | if self.ui.appComboBox.currentIndex() < 1:
423 | self.ui.appComboBox.setStyleSheet('border: 1px solid blue; border-radius: 6px;')
424 | return
425 | else:
426 | self.ui.appComboBox.setStyleSheet('')
427 |
428 | # Template combobox
429 | self.clear_extra_token_widgets()
430 | self.ui.tkTemplateComboBox.setEnabled(True)
431 | if self.ui.tkTemplateComboBox.currentIndex() < 1:
432 | self.ui.tkTemplateComboBox.setStyleSheet('border: 1px solid blue; border-radius: 6px;')
433 | return
434 | else:
435 | self.ui.tkTemplateComboBox.setStyleSheet('')
436 |
437 | self.update_template_output_paths()
438 |
439 | self.update_template_file_path()
440 |
441 | def update_template_file_path(self):
442 | missing_keys, valid = self.get_extra_token_definitions()
443 |
444 | # Invalid state means that not all extra tokens are valid or missing
445 | if not valid:
446 | self.ui.filePathWidgetGroup.setEnabled(False)
447 | return
448 |
449 | self.ui.filePathWidgetGroup.setEnabled(True)
450 |
451 | self.fields.update(missing_keys)
452 |
453 | filepath = self.template.apply_fields(self.fields)
454 |
455 | # replace any frame padding with value
456 | try:
457 | filepath = filepath % 1
458 |
459 | except:
460 | pass
461 |
462 | if self.is_path_file(filepath):
463 | self.ui.fileNameLineEdit.setText(os.path.basename(filepath))
464 | else:
465 | self.ui.fileNameLineEdit.setText('Template is a directory')
466 |
467 | self.ui.dirPathLineEdit.setText(os.path.dirname(filepath))
468 | self.ui.filePathLineEdit.setText(filepath)
469 |
470 | def sanitize_file_path(self, path):
471 | illegalchars = ['<', '>', '|', '*', '"', '?']
472 |
473 | for x in illegalchars:
474 | path = path.replace(x, '')
475 |
476 | return path
477 |
478 | def copy_path_to_clipboard(self, widget):
479 | """
480 |
481 | :param widget: Widget to get path from to copy to clipboard
482 | :return:
483 | """
484 | cb = QtGui.QApplication.clipboard()
485 | cb.clear(mode=cb.Clipboard)
486 | cb.setText(self.sanitize_file_path(widget.text()), mode=cb.Clipboard)
487 |
488 | def open_file_path(self, widget):
489 |
490 | path = widget.text()
491 | path = self.sanitize_file_path(path)
492 |
493 | if os.path.isfile(path):
494 | path = os.path.dirname(path)
495 |
496 | while True:
497 | if os.path.exists(path):
498 | break
499 | path = os.path.dirname(path)
500 |
501 | if sys.platform == 'darwin':
502 | subprocess.call(["open", "-R", path])
503 |
504 | elif sys.platform == 'win32':
505 | os.startfile(path)
506 |
507 | elif sys.platform == 'linux2':
508 | subprocess.Popen(['xdg-open', path])
509 |
510 | def browse_file(self):
511 | file_name = QtGui.QFileDialog.getOpenFileName()
512 | self.ui.copyFileLineEdit.setText(file_name[0])
513 |
514 | def create_file_on_disk(self, widget):
515 | path = widget.text()
516 | path = self.sanitize_file_path(path)
517 |
518 | is_file = self.is_path_file(path)
519 |
520 | try:
521 | if not os.path.exists(path):
522 |
523 | if is_file:
524 | dirpath = os.path.dirname(path)
525 | else:
526 | dirpath = path
527 |
528 | # Create directory structure first
529 | if not os.path.exists(dirpath):
530 | os.makedirs(dirpath)
531 | self.log.info('Created directories: %s' % dirpath)
532 |
533 | if is_file:
534 | file(path, 'w').close()
535 | self.log.info('Created file: %s' % path)
536 |
537 | QtGui.QMessageBox.information(self, 'Success!',
538 | 'Created directory/file:
{}'.format(path),
539 | QtGui.QMessageBox.Ok)
540 |
541 | except Exception as err:
542 | QtGui.QMessageBox.critical(self, 'Failure!',
543 | 'The directory/file failed to be created: {}
{}'.format(path,
544 | str(err)),
545 | QtGui.QMessageBox.Ok)
546 | self.log.error('Failed to create directory/file: {}'.format(path))
547 | self.log.error(str(err))
548 |
549 | @staticmethod
550 | def is_path_file(path):
551 | """
552 | Check if the path has a file extension, assume its a file, if not its a directory
553 | :param path: str - directory/file path
554 | :return: bool
555 | """
556 | tmp = os.path.splitext(path)
557 | if len(tmp) > 1 and tmp[1]:
558 | return True
559 |
560 | return False
561 |
562 | def copy_file_to_file_path(self):
563 | src_path = self.ui.copyFileLineEdit.text()
564 | dst_path = self.ui.filePathLineEdit.text()
565 |
566 | if not os.path.exists(src_path):
567 | self.log.warn('File doesnt not exist on disk, unable to copy: %s' % src_path)
568 | return
569 |
570 | # Check if file exists
571 | if os.path.exists(dst_path):
572 | reply = QtGui.QMessageBox.question(self, 'File Exists',
573 | 'The file already exists, do you want to overwrite it?',
574 | QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
575 |
576 | if reply == QtGui.QMessageBox.Yes:
577 | try:
578 | os.remove(dst_path)
579 | self.log.info('Removed existing file: {}'.format(dst_path))
580 | except Exception as err:
581 | self.log.error('Failed to remove existing file: %s'.format(dst_path))
582 | self.log.error(str(err))
583 | else:
584 | return
585 |
586 | # Copy file
587 | try:
588 | self.log.info('Copying: {} > {}'.format(src_path, dst_path))
589 |
590 | dirname = os.path.dirname(dst_path)
591 | if not os.path.exists(dirname):
592 | os.makedirs(dirname)
593 |
594 | shutil.copyfile(src_path, dst_path)
595 |
596 | QtGui.QMessageBox.information(self, 'Success!',
597 | 'The file was copied successfully
',
598 | QtGui.QMessageBox.Ok)
599 |
600 | self.ui.copyFileLineEdit.clear()
601 |
602 | except Exception as err:
603 | QtGui.QMessageBox.critical(self, 'Failure!',
604 | 'The file failed to be copied
{}'.format(str(err)),
605 | QtGui.QMessageBox.Ok)
606 | self.log.error('Failed to copy: {} > {}'.format(src_path, dst_path))
607 | self.log.error(str(err))
608 |
609 | def closeEvent(self, event):
610 | """
611 | Executed when the main dialog is closed.
612 | All worker threads and other things which need a proper shutdown
613 | need to be called here.
614 | """
615 |
616 | self.log.debug("CloseEvent Received. Begin shutting down UI.")
617 |
618 | # register the data fetcher with the global schema manager
619 | shotgun_globals.unregister_bg_task_manager(self._task_manager)
620 |
621 | try:
622 | # shut down main threadpool
623 | self._task_manager.shut_down()
624 | except Exception:
625 | self.log.exception("Error running closeEvent()")
626 |
627 | # ensure the context widget's recent contexts are saved
628 | self._context_widget.save_recent_contexts()
629 |
630 | # def _on_item_context_change(self, context):
631 | # """
632 | # This method is connected above to the `context_changed` signal emitted
633 | # by the context selector widget.
634 | #
635 | # For demo purposes, we simply display the context in a label.
636 | # """
637 | # self._context_lbl.setText("Context set to: %s" % (context,))
638 | #
639 | # # typically the context would be set by some external process. for now,
640 | # # we'll just re-set the context based on what was selected. this will
641 | # # have the added effect of populating the "recent" items in the drop
642 | # # down list
643 | # self._context_widget.set_context(context)
644 |
645 | # def _enable_editing(self, checked):
646 | # """
647 | # This method is connected above to the toggle button to show switching
648 | # between enabling and disabling editing of the context.
649 | # """
650 | #
651 | # self._context_lbl.setText("")
652 | #
653 | # if checked:
654 | # # enable editing and show a message to the user
655 | # self._context_widget.enable_editing(
656 | # True,
657 | # "Editing is now enabled."
658 | # )
659 | # else:
660 | # # disable editing and show a message to the user
661 | # self._context_widget.enable_editing(
662 | # False,
663 | # "Editing is now disabled."
664 | # )
665 |
--------------------------------------------------------------------------------