29 |
34 |
30 class Project(QObject): |
35 class Project(QObject): |
31 """ |
36 """ |
32 Class implementing the Flask project support. |
37 Class implementing the Flask project support. |
33 """ |
38 """ |
|
39 |
34 def __init__(self, plugin, iconSuffix, parent=None): |
40 def __init__(self, plugin, iconSuffix, parent=None): |
35 """ |
41 """ |
36 Constructor |
42 Constructor |
37 |
43 |
38 @param plugin reference to the plugin object |
44 @param plugin reference to the plugin object |
39 @type ProjectFlaskPlugin |
45 @type ProjectFlaskPlugin |
40 @param iconSuffix suffix for the icons |
46 @param iconSuffix suffix for the icons |
41 @type str |
47 @type str |
42 @param parent parent |
48 @param parent parent |
43 @type QObject |
49 @type QObject |
44 """ |
50 """ |
45 super().__init__(parent) |
51 super().__init__(parent) |
46 |
52 |
47 self.__plugin = plugin |
53 self.__plugin = plugin |
48 self.__iconSuffix = iconSuffix |
54 self.__iconSuffix = iconSuffix |
49 self.__ui = parent |
55 self.__ui = parent |
50 |
56 |
51 self.__ericProject = ericApp().getObject("Project") |
57 self.__ericProject = ericApp().getObject("Project") |
52 self.__virtualEnvManager = ericApp().getObject("VirtualEnvManager") |
58 self.__virtualEnvManager = ericApp().getObject("VirtualEnvManager") |
53 |
59 |
54 self.__menus = {} # dictionary with references to menus |
60 self.__menus = {} # dictionary with references to menus |
55 self.__formsBrowser = None |
61 self.__formsBrowser = None |
56 self.__hooksInstalled = False |
62 self.__hooksInstalled = False |
57 |
63 |
58 self.__serverDialog = None |
64 self.__serverDialog = None |
59 self.__routesDialog = None |
65 self.__routesDialog = None |
60 self.__shellProcess = None |
66 self.__shellProcess = None |
61 |
67 |
62 self.__projectData = { |
68 self.__projectData = { |
63 "flask": {}, |
69 "flask": {}, |
64 "flask-babel": {}, |
70 "flask-babel": {}, |
65 "flask-migrate": {}, |
71 "flask-migrate": {}, |
66 } |
72 } |
67 |
73 |
68 self.__flaskVersions = { |
74 self.__flaskVersions = { |
69 "python": "", |
75 "python": "", |
70 "flask": "", |
76 "flask": "", |
71 "werkzeug": "", |
77 "werkzeug": "", |
72 } |
78 } |
73 |
79 |
74 self.__capabilities = {} |
80 self.__capabilities = {} |
75 |
81 |
76 self.__pybabelProject = PyBabelProject(self.__plugin, self, self) |
82 self.__pybabelProject = PyBabelProject(self.__plugin, self, self) |
77 self.__migrateProject = MigrateProject(self.__plugin, self, self) |
83 self.__migrateProject = MigrateProject(self.__plugin, self, self) |
78 |
84 |
79 def initActions(self): |
85 def initActions(self): |
80 """ |
86 """ |
81 Public method to define the Flask actions. |
87 Public method to define the Flask actions. |
82 """ |
88 """ |
83 self.actions = [] |
89 self.actions = [] |
84 |
90 |
85 ############################## |
91 ############################## |
86 ## run actions below ## |
92 ## run actions below ## |
87 ############################## |
93 ############################## |
88 |
94 |
89 self.runServerAct = EricAction( |
95 self.runServerAct = EricAction( |
90 self.tr('Run Server'), |
96 self.tr("Run Server"), |
91 self.tr('Run &Server'), |
97 self.tr("Run &Server"), |
92 0, 0, |
98 0, |
93 self, 'flask_run_server') |
99 0, |
94 self.runServerAct.setStatusTip(self.tr( |
100 self, |
95 'Starts the Flask Web server')) |
101 "flask_run_server", |
96 self.runServerAct.setWhatsThis(self.tr( |
102 ) |
97 """<b>Run Server</b>""" |
103 self.runServerAct.setStatusTip(self.tr("Starts the Flask Web server")) |
98 """<p>Starts the Flask Web server.</p>""" |
104 self.runServerAct.setWhatsThis( |
99 )) |
105 self.tr("""<b>Run Server</b>""" """<p>Starts the Flask Web server.</p>""") |
|
106 ) |
100 self.runServerAct.triggered.connect(self.__runServer) |
107 self.runServerAct.triggered.connect(self.__runServer) |
101 self.actions.append(self.runServerAct) |
108 self.actions.append(self.runServerAct) |
102 |
109 |
103 self.runDevServerAct = EricAction( |
110 self.runDevServerAct = EricAction( |
104 self.tr('Run Development Server'), |
111 self.tr("Run Development Server"), |
105 self.tr('Run &Development Server'), |
112 self.tr("Run &Development Server"), |
106 0, 0, |
113 0, |
107 self, 'flask_run_dev_server') |
114 0, |
108 self.runDevServerAct.setStatusTip(self.tr( |
115 self, |
109 'Starts the Flask Web server in development mode')) |
116 "flask_run_dev_server", |
110 self.runDevServerAct.setWhatsThis(self.tr( |
117 ) |
111 """<b>Run Development Server</b>""" |
118 self.runDevServerAct.setStatusTip( |
112 """<p>Starts the Flask Web server in development mode.</p>""" |
119 self.tr("Starts the Flask Web server in development mode") |
113 )) |
120 ) |
|
121 self.runDevServerAct.setWhatsThis( |
|
122 self.tr( |
|
123 """<b>Run Development Server</b>""" |
|
124 """<p>Starts the Flask Web server in development mode.</p>""" |
|
125 ) |
|
126 ) |
114 self.runDevServerAct.triggered.connect(self.__runDevelopmentServer) |
127 self.runDevServerAct.triggered.connect(self.__runDevelopmentServer) |
115 self.actions.append(self.runDevServerAct) |
128 self.actions.append(self.runDevServerAct) |
116 |
129 |
117 self.askForServerOptionsAct = EricAction( |
130 self.askForServerOptionsAct = EricAction( |
118 self.tr('Ask for Server Start Options'), |
131 self.tr("Ask for Server Start Options"), |
119 self.tr('Ask for Server Start Options'), |
132 self.tr("Ask for Server Start Options"), |
120 0, 0, |
133 0, |
121 self, 'flask_ask_server_options') |
134 0, |
122 self.askForServerOptionsAct.setStatusTip(self.tr( |
135 self, |
123 'Ask for server start options')) |
136 "flask_ask_server_options", |
124 self.askForServerOptionsAct.setWhatsThis(self.tr( |
137 ) |
125 """<b>Ask for Server Start Options</b>""" |
138 self.askForServerOptionsAct.setStatusTip( |
126 """<p>Asks for server start options before the Flask Web server""" |
139 self.tr("Ask for server start options") |
127 """ is started. If this is unchecked, the server is started with""" |
140 ) |
128 """ default parameters.</p>""" |
141 self.askForServerOptionsAct.setWhatsThis( |
129 )) |
142 self.tr( |
|
143 """<b>Ask for Server Start Options</b>""" |
|
144 """<p>Asks for server start options before the Flask Web server""" |
|
145 """ is started. If this is unchecked, the server is started with""" |
|
146 """ default parameters.</p>""" |
|
147 ) |
|
148 ) |
130 self.askForServerOptionsAct.setCheckable(True) |
149 self.askForServerOptionsAct.setCheckable(True) |
131 self.actions.append(self.askForServerOptionsAct) |
150 self.actions.append(self.askForServerOptionsAct) |
132 |
151 |
133 ############################### |
152 ############################### |
134 ## shell action below ## |
153 ## shell action below ## |
135 ############################### |
154 ############################### |
136 |
155 |
137 self.runPythonShellAct = EricAction( |
156 self.runPythonShellAct = EricAction( |
138 self.tr('Start Flask Python Console'), |
157 self.tr("Start Flask Python Console"), |
139 self.tr('Start Flask &Python Console'), |
158 self.tr("Start Flask &Python Console"), |
140 0, 0, |
159 0, |
141 self, 'flask_python_console') |
160 0, |
142 self.runPythonShellAct.setStatusTip(self.tr( |
161 self, |
143 'Starts an interactive Python interpreter')) |
162 "flask_python_console", |
144 self.runPythonShellAct.setWhatsThis(self.tr( |
163 ) |
145 """<b>Start Flask Python Console</b>""" |
164 self.runPythonShellAct.setStatusTip( |
146 """<p>Starts an interactive Python interpreter.</p>""" |
165 self.tr("Starts an interactive Python interpreter") |
147 )) |
166 ) |
|
167 self.runPythonShellAct.setWhatsThis( |
|
168 self.tr( |
|
169 """<b>Start Flask Python Console</b>""" |
|
170 """<p>Starts an interactive Python interpreter.</p>""" |
|
171 ) |
|
172 ) |
148 self.runPythonShellAct.triggered.connect(self.__runPythonShell) |
173 self.runPythonShellAct.triggered.connect(self.__runPythonShell) |
149 self.actions.append(self.runPythonShellAct) |
174 self.actions.append(self.runPythonShellAct) |
150 |
175 |
151 ################################ |
176 ################################ |
152 ## routes action below ## |
177 ## routes action below ## |
153 ################################ |
178 ################################ |
154 |
179 |
155 self.showRoutesAct = EricAction( |
180 self.showRoutesAct = EricAction( |
156 self.tr('Show Routes'), |
181 self.tr("Show Routes"), |
157 self.tr('Show &Routes'), |
182 self.tr("Show &Routes"), |
158 0, 0, |
183 0, |
159 self, 'flask_show_routes') |
184 0, |
160 self.showRoutesAct.setStatusTip(self.tr( |
185 self, |
161 'Shows a dialog with the routes of the flask app')) |
186 "flask_show_routes", |
162 self.showRoutesAct.setWhatsThis(self.tr( |
187 ) |
163 """<b>Show Routes</b>""" |
188 self.showRoutesAct.setStatusTip( |
164 """<p>Shows a dialog with the routes of the flask app.</p>""" |
189 self.tr("Shows a dialog with the routes of the flask app") |
165 )) |
190 ) |
|
191 self.showRoutesAct.setWhatsThis( |
|
192 self.tr( |
|
193 """<b>Show Routes</b>""" |
|
194 """<p>Shows a dialog with the routes of the flask app.</p>""" |
|
195 ) |
|
196 ) |
166 self.showRoutesAct.triggered.connect(self.__showRoutes) |
197 self.showRoutesAct.triggered.connect(self.__showRoutes) |
167 self.actions.append(self.showRoutesAct) |
198 self.actions.append(self.showRoutesAct) |
168 |
199 |
169 ################################## |
200 ################################## |
170 ## documentation action below ## |
201 ## documentation action below ## |
171 ################################## |
202 ################################## |
172 |
203 |
173 self.documentationAct = EricAction( |
204 self.documentationAct = EricAction( |
174 self.tr('Documentation'), |
205 self.tr("Documentation"), |
175 self.tr('D&ocumentation'), |
206 self.tr("D&ocumentation"), |
176 0, 0, |
207 0, |
177 self, 'flask_documentation') |
208 0, |
178 self.documentationAct.setStatusTip(self.tr( |
209 self, |
179 'Shows the help viewer with the Flask documentation')) |
210 "flask_documentation", |
180 self.documentationAct.setWhatsThis(self.tr( |
211 ) |
181 """<b>Documentation</b>""" |
212 self.documentationAct.setStatusTip( |
182 """<p>Shows the help viewer with the Flask documentation.</p>""" |
213 self.tr("Shows the help viewer with the Flask documentation") |
183 )) |
214 ) |
|
215 self.documentationAct.setWhatsThis( |
|
216 self.tr( |
|
217 """<b>Documentation</b>""" |
|
218 """<p>Shows the help viewer with the Flask documentation.</p>""" |
|
219 ) |
|
220 ) |
184 self.documentationAct.triggered.connect(self.__showDocumentation) |
221 self.documentationAct.triggered.connect(self.__showDocumentation) |
185 self.actions.append(self.documentationAct) |
222 self.actions.append(self.documentationAct) |
186 |
223 |
187 ############################## |
224 ############################## |
188 ## about action below ## |
225 ## about action below ## |
189 ############################## |
226 ############################## |
190 |
227 |
191 self.aboutFlaskAct = EricAction( |
228 self.aboutFlaskAct = EricAction( |
192 self.tr('About Flask'), |
229 self.tr("About Flask"), self.tr("About &Flask"), 0, 0, self, "flask_about" |
193 self.tr('About &Flask'), |
230 ) |
194 0, 0, |
231 self.aboutFlaskAct.setStatusTip(self.tr("Shows some information about Flask")) |
195 self, 'flask_about') |
232 self.aboutFlaskAct.setWhatsThis( |
196 self.aboutFlaskAct.setStatusTip(self.tr( |
233 self.tr( |
197 'Shows some information about Flask')) |
234 """<b>About Flask</b>""" |
198 self.aboutFlaskAct.setWhatsThis(self.tr( |
235 """<p>Shows some information about Flask.</p>""" |
199 """<b>About Flask</b>""" |
236 ) |
200 """<p>Shows some information about Flask.</p>""" |
237 ) |
201 )) |
|
202 self.aboutFlaskAct.triggered.connect(self.__flaskInfo) |
238 self.aboutFlaskAct.triggered.connect(self.__flaskInfo) |
203 self.actions.append(self.aboutFlaskAct) |
239 self.actions.append(self.aboutFlaskAct) |
204 |
240 |
205 self.__pybabelProject.initActions() |
241 self.__pybabelProject.initActions() |
206 self.__migrateProject.initActions() |
242 self.__migrateProject.initActions() |
207 |
243 |
208 ###################################### |
244 ###################################### |
209 ## configuration action below ## |
245 ## configuration action below ## |
210 ###################################### |
246 ###################################### |
211 |
247 |
212 self.flaskConfigAct = EricAction( |
248 self.flaskConfigAct = EricAction( |
213 self.tr('Configure Flask for Project'), |
249 self.tr("Configure Flask for Project"), |
214 self.tr('Configure Flask for &Project'), |
250 self.tr("Configure Flask for &Project"), |
215 0, 0, |
251 0, |
216 self, 'flask_config_for_project') |
252 0, |
217 self.flaskConfigAct.setStatusTip(self.tr( |
253 self, |
218 'Shows a dialog to edit the project specific flask configuration')) |
254 "flask_config_for_project", |
219 self.flaskConfigAct.setWhatsThis(self.tr( |
255 ) |
220 """<b>Configure Flask for Project</b>""" |
256 self.flaskConfigAct.setStatusTip( |
221 """<p>Shows a dialog to edit the project specific flask""" |
257 self.tr("Shows a dialog to edit the project specific flask configuration") |
222 """ configuration.</p>""" |
258 ) |
223 )) |
259 self.flaskConfigAct.setWhatsThis( |
224 self.flaskConfigAct.triggered.connect( |
260 self.tr( |
225 self.__configureFlaskForProject) |
261 """<b>Configure Flask for Project</b>""" |
|
262 """<p>Shows a dialog to edit the project specific flask""" |
|
263 """ configuration.</p>""" |
|
264 ) |
|
265 ) |
|
266 self.flaskConfigAct.triggered.connect(self.__configureFlaskForProject) |
226 self.actions.append(self.flaskConfigAct) |
267 self.actions.append(self.flaskConfigAct) |
227 |
268 |
228 def initMenu(self): |
269 def initMenu(self): |
229 """ |
270 """ |
230 Public method to initialize the Flask menu. |
271 Public method to initialize the Flask menu. |
231 |
272 |
232 @return the menu generated |
273 @return the menu generated |
233 @rtype QMenu |
274 @rtype QMenu |
234 """ |
275 """ |
235 self.__menus = {} # clear menus references |
276 self.__menus = {} # clear menus references |
236 |
277 |
237 self.__menus["flask-babel"] = self.__pybabelProject.initMenu() |
278 self.__menus["flask-babel"] = self.__pybabelProject.initMenu() |
238 self.__menus["flask-migrate"] = self.__migrateProject.initMenu() |
279 self.__menus["flask-migrate"] = self.__migrateProject.initMenu() |
239 |
280 |
240 menu = QMenu(self.tr('&Flask'), self.__ui) |
281 menu = QMenu(self.tr("&Flask"), self.__ui) |
241 menu.setTearOffEnabled(True) |
282 menu.setTearOffEnabled(True) |
242 |
283 |
243 menu.addAction(self.flaskConfigAct) |
284 menu.addAction(self.flaskConfigAct) |
244 menu.addSeparator() |
285 menu.addSeparator() |
245 menu.addAction(self.runServerAct) |
286 menu.addAction(self.runServerAct) |
246 menu.addAction(self.runDevServerAct) |
287 menu.addAction(self.runDevServerAct) |
247 menu.addAction(self.askForServerOptionsAct) |
288 menu.addAction(self.askForServerOptionsAct) |
255 menu.addMenu(self.__menus["flask-babel"]) |
296 menu.addMenu(self.__menus["flask-babel"]) |
256 menu.addSeparator() |
297 menu.addSeparator() |
257 menu.addAction(self.documentationAct) |
298 menu.addAction(self.documentationAct) |
258 menu.addSeparator() |
299 menu.addSeparator() |
259 menu.addAction(self.aboutFlaskAct) |
300 menu.addAction(self.aboutFlaskAct) |
260 |
301 |
261 self.__menus["main"] = menu |
302 self.__menus["main"] = menu |
262 |
303 |
263 return menu |
304 return menu |
264 |
305 |
265 def getMenu(self, name): |
306 def getMenu(self, name): |
266 """ |
307 """ |
267 Public method to get a reference to the requested menu. |
308 Public method to get a reference to the requested menu. |
268 |
309 |
269 @param name name of the menu |
310 @param name name of the menu |
270 @type str |
311 @type str |
271 @return reference to the menu or None, if no menu with the given |
312 @return reference to the menu or None, if no menu with the given |
272 name exists |
313 name exists |
273 @rtype QMenu or None |
314 @rtype QMenu or None |
274 """ |
315 """ |
275 if name in self.__menus: |
316 if name in self.__menus: |
276 return self.__menus[name] |
317 return self.__menus[name] |
277 else: |
318 else: |
278 return None |
319 return None |
279 |
320 |
280 def getMenuNames(self): |
321 def getMenuNames(self): |
281 """ |
322 """ |
282 Public method to get the names of all menus. |
323 Public method to get the names of all menus. |
283 |
324 |
284 @return menu names |
325 @return menu names |
285 @rtype list of str |
326 @rtype list of str |
286 """ |
327 """ |
287 return list(self.__menus.keys()) |
328 return list(self.__menus.keys()) |
288 |
329 |
289 def projectOpenedHooks(self): |
330 def projectOpenedHooks(self): |
290 """ |
331 """ |
291 Public method to add our hook methods. |
332 Public method to add our hook methods. |
292 """ |
333 """ |
293 if self.__ericProject.getProjectType() == "Flask": |
334 if self.__ericProject.getProjectType() == "Flask": |
294 self.__formsBrowser = ( |
335 self.__formsBrowser = ( |
295 ericApp().getObject("ProjectBrowser") |
336 ericApp().getObject("ProjectBrowser").getProjectBrowser("forms") |
296 .getProjectBrowser("forms")) |
337 ) |
297 self.__formsBrowser.addHookMethodAndMenuEntry( |
338 self.__formsBrowser.addHookMethodAndMenuEntry( |
298 "newForm", self.newForm, self.tr("New template...")) |
339 "newForm", self.newForm, self.tr("New template...") |
299 |
340 ) |
|
341 |
300 self.__determineCapabilities() |
342 self.__determineCapabilities() |
301 self.__setDebugEnvironment() |
343 self.__setDebugEnvironment() |
302 |
344 |
303 self.__pybabelProject.projectOpenedHooks() |
345 self.__pybabelProject.projectOpenedHooks() |
304 |
346 |
305 self.__hooksInstalled = True |
347 self.__hooksInstalled = True |
306 |
348 |
307 def projectClosedHooks(self): |
349 def projectClosedHooks(self): |
308 """ |
350 """ |
309 Public method to remove our hook methods. |
351 Public method to remove our hook methods. |
310 """ |
352 """ |
311 self.__pybabelProject.projectClosedHooks() |
353 self.__pybabelProject.projectClosedHooks() |
312 |
354 |
313 if self.__hooksInstalled: |
355 if self.__hooksInstalled: |
314 self.__formsBrowser.removeHookMethod("newForm") |
356 self.__formsBrowser.removeHookMethod("newForm") |
315 self.__formsBrowser = None |
357 self.__formsBrowser = None |
316 |
358 |
317 self.__hooksInstalled = False |
359 self.__hooksInstalled = False |
318 |
360 |
319 def newForm(self, dirPath): |
361 def newForm(self, dirPath): |
320 """ |
362 """ |
321 Public method to create a new form. |
363 Public method to create a new form. |
322 |
364 |
323 @param dirPath full directory path for the new form file |
365 @param dirPath full directory path for the new form file |
324 @type str |
366 @type str |
325 """ |
367 """ |
326 from .FormSelectionDialog import FormSelectionDialog |
368 from .FormSelectionDialog import FormSelectionDialog |
327 |
369 |
328 dlg = FormSelectionDialog() |
370 dlg = FormSelectionDialog() |
329 if dlg.exec() == QDialog.DialogCode.Accepted: |
371 if dlg.exec() == QDialog.DialogCode.Accepted: |
330 template = dlg.getTemplateText() |
372 template = dlg.getTemplateText() |
331 |
373 |
332 fileFilters = self.tr( |
374 fileFilters = self.tr( |
333 "HTML Files (*.html);;" |
375 "HTML Files (*.html);;" "HTML Files (*.htm);;" "All Files (*)" |
334 "HTML Files (*.htm);;" |
376 ) |
335 "All Files (*)") |
|
336 fname, selectedFilter = EricFileDialog.getSaveFileNameAndFilter( |
377 fname, selectedFilter = EricFileDialog.getSaveFileNameAndFilter( |
337 self.__ui, |
378 self.__ui, |
338 self.tr("New Form"), |
379 self.tr("New Form"), |
339 dirPath, |
380 dirPath, |
340 fileFilters, |
381 fileFilters, |
341 None, |
382 None, |
342 EricFileDialog.Options(EricFileDialog.DontConfirmOverwrite)) |
383 EricFileDialog.Options(EricFileDialog.DontConfirmOverwrite), |
|
384 ) |
343 if fname: |
385 if fname: |
344 ext = QFileInfo(fname).suffix() |
386 ext = QFileInfo(fname).suffix() |
345 if not ext: |
387 if not ext: |
346 ex = selectedFilter.split("(*")[1].split(")")[0] |
388 ex = selectedFilter.split("(*")[1].split(")")[0] |
347 if ex: |
389 if ex: |
348 fname += ex |
390 fname += ex |
349 |
391 |
350 if os.path.exists(fname): |
392 if os.path.exists(fname): |
351 res = EricMessageBox.yesNo( |
393 res = EricMessageBox.yesNo( |
352 self.__ui, |
394 self.__ui, |
353 self.tr("New Form"), |
395 self.tr("New Form"), |
354 self.tr("""The file already exists! Overwrite""" |
396 self.tr("""The file already exists! Overwrite""" """ it?"""), |
355 """ it?"""), |
397 icon=EricMessageBox.Warning, |
356 icon=EricMessageBox.Warning) |
398 ) |
357 if not res: |
399 if not res: |
358 # user selected to not overwrite |
400 # user selected to not overwrite |
359 return |
401 return |
360 |
402 |
361 try: |
403 try: |
362 with open(fname, "w", encoding="utf-8") as f: |
404 with open(fname, "w", encoding="utf-8") as f: |
363 f.write(template) |
405 f.write(template) |
364 except OSError as err: |
406 except OSError as err: |
365 EricMessageBox.critical( |
407 EricMessageBox.critical( |
366 self.__ui, |
408 self.__ui, |
367 self.tr("New Form"), |
409 self.tr("New Form"), |
368 self.tr("<p>The new form file <b>{0}</b> could" |
410 self.tr( |
369 " not be created.</p><p>Problem: {1}</p>") |
411 "<p>The new form file <b>{0}</b> could" |
370 .format(fname, str(err))) |
412 " not be created.</p><p>Problem: {1}</p>" |
|
413 ).format(fname, str(err)), |
|
414 ) |
371 return |
415 return |
372 |
416 |
373 self.__ericProject.appendFile(fname) |
417 self.__ericProject.appendFile(fname) |
374 self.__formsBrowser.sourceFile.emit(fname) |
418 self.__formsBrowser.sourceFile.emit(fname) |
375 |
419 |
376 ################################################################## |
420 ################################################################## |
377 ## methods below implement virtual environment handling |
421 ## methods below implement virtual environment handling |
378 ################################################################## |
422 ################################################################## |
379 |
423 |
380 def getVirtualEnvironment(self): |
424 def getVirtualEnvironment(self): |
381 """ |
425 """ |
382 Public method to get the path of the virtual environment. |
426 Public method to get the path of the virtual environment. |
383 |
427 |
384 @return path of the virtual environment |
428 @return path of the virtual environment |
385 @rtype str |
429 @rtype str |
386 """ |
430 """ |
387 language = self.__ericProject.getProjectLanguage() |
431 language = self.__ericProject.getProjectLanguage() |
388 if language == "Python3": |
432 if language == "Python3": |
389 # get project specific virtual environment name |
433 # get project specific virtual environment name |
390 venvName = self.getData("flask", "virtual_environment_name") |
434 venvName = self.getData("flask", "virtual_environment_name") |
391 if not venvName: |
435 if not venvName: |
392 venvName = self.__plugin.getPreferences( |
436 venvName = self.__plugin.getPreferences("VirtualEnvironmentNamePy3") |
393 "VirtualEnvironmentNamePy3") |
|
394 else: |
437 else: |
395 venvName = "" |
438 venvName = "" |
396 virtEnv = ( |
439 virtEnv = ( |
397 self.__virtualEnvManager.getVirtualenvDirectory(venvName) |
440 self.__virtualEnvManager.getVirtualenvDirectory(venvName) |
398 if venvName else |
441 if venvName |
399 "" |
442 else "" |
400 ) |
443 ) |
401 |
444 |
402 if virtEnv and not os.path.exists(virtEnv): |
445 if virtEnv and not os.path.exists(virtEnv): |
403 virtEnv = "" |
446 virtEnv = "" |
404 |
447 |
405 return virtEnv # __IGNORE_WARNING_M834__ |
448 return virtEnv # __IGNORE_WARNING_M834__ |
406 |
449 |
407 def getVirtualenvInterpreter(self): |
450 def getVirtualenvInterpreter(self): |
408 """ |
451 """ |
409 Public method to get the path of the Python interpreter to be used |
452 Public method to get the path of the Python interpreter to be used |
410 with the current project. |
453 with the current project. |
411 |
454 |
412 @return path of the Python interpreter |
455 @return path of the Python interpreter |
413 @rtype str |
456 @rtype str |
414 """ |
457 """ |
415 return self.getFullCommand("python") |
458 return self.getFullCommand("python") |
416 |
459 |
417 def getFullCommand(self, command, virtualEnvPath=None): |
460 def getFullCommand(self, command, virtualEnvPath=None): |
418 """ |
461 """ |
419 Public method to get the full command for a given command name. |
462 Public method to get the full command for a given command name. |
420 |
463 |
421 @param command command name |
464 @param command command name |
422 @type str |
465 @type str |
423 @param virtualEnvPath path of the virtual environment |
466 @param virtualEnvPath path of the virtual environment |
424 @type str |
467 @type str |
425 @return full command |
468 @return full command |
426 @rtype str |
469 @rtype str |
427 """ |
470 """ |
428 virtualEnv = virtualEnvPath or self.getVirtualEnvironment() |
471 virtualEnv = virtualEnvPath or self.getVirtualEnvironment() |
429 fullCmds = ( |
472 fullCmds = ( |
430 [os.path.join(virtualEnv, "Scripts", command + '.exe'), |
473 [ |
431 os.path.join(virtualEnv, "bin", command + '.exe'), |
474 os.path.join(virtualEnv, "Scripts", command + ".exe"), |
432 command] # fall back to just cmd |
475 os.path.join(virtualEnv, "bin", command + ".exe"), |
433 if isWindowsPlatform() else |
476 command, |
434 [os.path.join(virtualEnv, "bin", command), |
477 ] # fall back to just cmd |
435 os.path.join(virtualEnv, "local", "bin", command), |
478 if isWindowsPlatform() |
436 Utilities.getExecutablePath(command), |
479 else [ |
437 command] # fall back to just cmd |
480 os.path.join(virtualEnv, "bin", command), |
|
481 os.path.join(virtualEnv, "local", "bin", command), |
|
482 Utilities.getExecutablePath(command), |
|
483 command, |
|
484 ] # fall back to just cmd |
438 ) |
485 ) |
439 for command in fullCmds: |
486 for command in fullCmds: |
440 if os.path.exists(command): |
487 if os.path.exists(command): |
441 break |
488 break |
442 return command |
489 return command |
443 |
490 |
444 ################################################################## |
491 ################################################################## |
445 ## methods below implement general functionality |
492 ## methods below implement general functionality |
446 ################################################################## |
493 ################################################################## |
447 |
494 |
448 def projectClosed(self): |
495 def projectClosed(self): |
449 """ |
496 """ |
450 Public method to handle the closing of a project. |
497 Public method to handle the closing of a project. |
451 """ |
498 """ |
452 for dlg in (self.__serverDialog, self.__routesDialog): |
499 for dlg in (self.__serverDialog, self.__routesDialog): |
453 if dlg is not None: |
500 if dlg is not None: |
454 dlg.close() |
501 dlg.close() |
455 |
502 |
456 self.__migrateProject.projectClosed() |
503 self.__migrateProject.projectClosed() |
457 |
504 |
458 def supportedPythonVariants(self): |
505 def supportedPythonVariants(self): |
459 """ |
506 """ |
460 Public method to get the supported Python variants. |
507 Public method to get the supported Python variants. |
461 |
508 |
462 @return list of supported Python variants |
509 @return list of supported Python variants |
463 @rtype list of str |
510 @rtype list of str |
464 """ |
511 """ |
465 variants = [] |
512 variants = [] |
466 |
513 |
467 virtEnv = self.getVirtualEnvironment() |
514 virtEnv = self.getVirtualEnvironment() |
468 if virtEnv: |
515 if virtEnv: |
469 fullCmd = self.getFlaskCommand() |
516 fullCmd = self.getFlaskCommand() |
470 if fullCmd: |
517 if fullCmd: |
471 variants.append("Python3") |
518 variants.append("Python3") |
582 workdir, app = self.getApplication() |
632 workdir, app = self.getApplication() |
583 env = QProcessEnvironment.systemEnvironment() |
633 env = QProcessEnvironment.systemEnvironment() |
584 env.insert("FLASK_APP", app) |
634 env.insert("FLASK_APP", app) |
585 if development: |
635 if development: |
586 env.insert("FLASK_ENV", "development") |
636 env.insert("FLASK_ENV", "development") |
587 |
637 |
588 return workdir, env |
638 return workdir, env |
589 |
639 |
590 def getApplication(self): |
640 def getApplication(self): |
591 """ |
641 """ |
592 Public method to determine the application name and the respective |
642 Public method to determine the application name and the respective |
593 working directory. |
643 working directory. |
594 |
644 |
595 @return tuple containing the working directory and the application name |
645 @return tuple containing the working directory and the application name |
596 @rtype tuple of (str, str) |
646 @rtype tuple of (str, str) |
597 """ |
647 """ |
598 mainScript = self.__ericProject.getMainScript(normalized=True) |
648 mainScript = self.__ericProject.getMainScript(normalized=True) |
599 if not mainScript: |
649 if not mainScript: |
600 EricMessageBox.critical( |
650 EricMessageBox.critical( |
601 self.__ui, |
651 self.__ui, |
602 self.tr("Prepare Environment"), |
652 self.tr("Prepare Environment"), |
603 self.tr("""The project has no configured main script""" |
653 self.tr( |
604 """ (= Flask application). Aborting...""")) |
654 """The project has no configured main script""" |
|
655 """ (= Flask application). Aborting...""" |
|
656 ), |
|
657 ) |
605 return "", None |
658 return "", None |
606 |
659 |
607 scriptPath, scriptName = os.path.split(mainScript) |
660 scriptPath, scriptName = os.path.split(mainScript) |
608 if scriptName == "__init__.py": |
661 if scriptName == "__init__.py": |
609 workdir, app = os.path.split(scriptPath) |
662 workdir, app = os.path.split(scriptPath) |
610 else: |
663 else: |
611 workdir, app = scriptPath, scriptName |
664 workdir, app = scriptPath, scriptName |
612 return workdir, app |
665 return workdir, app |
613 |
666 |
614 def getData(self, category, key): |
667 def getData(self, category, key): |
615 """ |
668 """ |
616 Public method to get data stored in the project store. |
669 Public method to get data stored in the project store. |
617 |
670 |
618 @param category data category |
671 @param category data category |
619 @type str |
672 @type str |
620 @param key data key |
673 @param key data key |
621 @type str |
674 @type str |
622 @return referenced data |
675 @return referenced data |
623 @rtype any |
676 @rtype any |
624 """ |
677 """ |
625 if category not in self.__projectData: |
678 if category not in self.__projectData: |
626 self.__projectData[category] = {} |
679 self.__projectData[category] = {} |
627 |
680 |
628 if not self.__projectData[category]: |
681 if not self.__projectData[category]: |
629 data = self.__ericProject.getData( |
682 data = self.__ericProject.getData("PROJECTTYPESPECIFICDATA", category) |
630 "PROJECTTYPESPECIFICDATA", category) |
|
631 if data is not None: |
683 if data is not None: |
632 self.__projectData[category] = data |
684 self.__projectData[category] = data |
633 |
685 |
634 data = self.__projectData[category] |
686 data = self.__projectData[category] |
635 if not key: |
687 if not key: |
636 # return complete category dictionary |
688 # return complete category dictionary |
637 return data |
689 return data |
638 elif key in data: |
690 elif key in data: |
639 # return individual entry |
691 # return individual entry |
640 return data[key] |
692 return data[key] |
641 else: |
693 else: |
642 # failure |
694 # failure |
643 return None |
695 return None |
644 |
696 |
645 def setData(self, category, key, value): |
697 def setData(self, category, key, value): |
646 """ |
698 """ |
647 Public method to store data in the project store. |
699 Public method to store data in the project store. |
648 |
700 |
649 @param category data category |
701 @param category data category |
650 @type str |
702 @type str |
651 @param key data key |
703 @param key data key |
652 @type str |
704 @type str |
653 @param value data to be stored |
705 @param value data to be stored |
654 @type any (serializable type) |
706 @type any (serializable type) |
655 """ |
707 """ |
656 if category not in self.__projectData: |
708 if category not in self.__projectData: |
657 self.__projectData[category] = {} |
709 self.__projectData[category] = {} |
658 |
710 |
659 if not self.__projectData[category]: |
711 if not self.__projectData[category]: |
660 data = self.__ericProject.getData( |
712 data = self.__ericProject.getData("PROJECTTYPESPECIFICDATA", category) |
661 "PROJECTTYPESPECIFICDATA", category) |
|
662 if data is not None: |
713 if data is not None: |
663 self.__projectData[category] = data |
714 self.__projectData[category] = data |
664 |
715 |
665 if not key: |
716 if not key: |
666 # update the complete category |
717 # update the complete category |
667 self.__projectData[category] = value |
718 self.__projectData[category] = value |
668 else: |
719 else: |
669 # update individual entry |
720 # update individual entry |
670 self.__projectData[category][key] = value |
721 self.__projectData[category][key] = value |
671 |
722 |
672 self.__ericProject.setData( |
723 self.__ericProject.setData( |
673 "PROJECTTYPESPECIFICDATA", category, self.__projectData[category]) |
724 "PROJECTTYPESPECIFICDATA", category, self.__projectData[category] |
674 |
725 ) |
|
726 |
675 def __determineCapabilities(self): |
727 def __determineCapabilities(self): |
676 """ |
728 """ |
677 Private method to determine capabilities provided by supported |
729 Private method to determine capabilities provided by supported |
678 extensions. |
730 extensions. |
679 """ |
731 """ |
680 # 1. support for flask-babel (i.e. pybabel) |
732 # 1. support for flask-babel (i.e. pybabel) |
681 self.__pybabelProject.determineCapability() |
733 self.__pybabelProject.determineCapability() |
682 |
734 |
683 # 2. support for flask-migrate |
735 # 2. support for flask-migrate |
684 self.__migrateProject.determineCapability() |
736 self.__migrateProject.determineCapability() |
685 |
737 |
686 def hasCapability(self, key): |
738 def hasCapability(self, key): |
687 """ |
739 """ |
688 Public method to check, if a capability is available. |
740 Public method to check, if a capability is available. |
689 |
741 |
690 @param key key of the capability to check |
742 @param key key of the capability to check |
691 @type str |
743 @type str |
692 @return flag indicating the availability of the capability |
744 @return flag indicating the availability of the capability |
693 @rtype bool |
745 @rtype bool |
694 """ |
746 """ |
695 try: |
747 try: |
696 return self.__capabilities[key] |
748 return self.__capabilities[key] |
697 except KeyError: |
749 except KeyError: |
698 return False |
750 return False |
699 |
751 |
700 def setCapability(self, key, available): |
752 def setCapability(self, key, available): |
701 """ |
753 """ |
702 Public method to set the availability status of a capability. |
754 Public method to set the availability status of a capability. |
703 |
755 |
704 @param key key of the capability to set |
756 @param key key of the capability to set |
705 @type str |
757 @type str |
706 @param available flag indicating the availability of the capability |
758 @param available flag indicating the availability of the capability |
707 @type bool |
759 @type bool |
708 """ |
760 """ |
709 self.__capabilities[key] = available |
761 self.__capabilities[key] = available |
710 |
762 |
711 ################################################################## |
763 ################################################################## |
712 ## slots below implements project specific flask configuration |
764 ## slots below implements project specific flask configuration |
713 ################################################################## |
765 ################################################################## |
714 |
766 |
715 @pyqtSlot() |
767 @pyqtSlot() |
716 def __configureFlaskForProject(self): |
768 def __configureFlaskForProject(self): |
717 """ |
769 """ |
718 Private slot to configure the project specific flask parameters. |
770 Private slot to configure the project specific flask parameters. |
719 """ |
771 """ |
720 from .FlaskConfigDialog import FlaskConfigDialog |
772 from .FlaskConfigDialog import FlaskConfigDialog |
721 |
773 |
722 config = self.getData("flask", "") |
774 config = self.getData("flask", "") |
723 dlg = FlaskConfigDialog(config, self) |
775 dlg = FlaskConfigDialog(config, self) |
724 if dlg.exec() == QDialog.DialogCode.Accepted: |
776 if dlg.exec() == QDialog.DialogCode.Accepted: |
725 config = dlg.getConfiguration() |
777 config = dlg.getConfiguration() |
726 self.setData("flask", "", config) |
778 self.setData("flask", "", config) |
727 self.__setIgnoreVirtualEnvironment() |
779 self.__setIgnoreVirtualEnvironment() |
728 self.__setDebugEnvironment() |
780 self.__setDebugEnvironment() |
729 |
781 |
730 self.__migrateProject.determineCapability() |
782 self.__migrateProject.determineCapability() |
731 |
783 |
732 self.__pybabelProject.determineCapability() |
784 self.__pybabelProject.determineCapability() |
733 self.projectClosedHooks() |
785 self.projectClosedHooks() |
734 self.projectOpenedHooks() |
786 self.projectOpenedHooks() |
735 |
787 |
736 def __setIgnoreVirtualEnvironment(self): |
788 def __setIgnoreVirtualEnvironment(self): |
737 """ |
789 """ |
738 Private method to add an embedded project specific virtual environment |
790 Private method to add an embedded project specific virtual environment |
739 to the list of ignore files/directories. |
791 to the list of ignore files/directories. |
740 """ |
792 """ |
741 virtenvName = self.getData("flask", "virtual_environment_name") |
793 virtenvName = self.getData("flask", "virtual_environment_name") |
742 if virtenvName: |
794 if virtenvName: |
743 virtenvPath = self.getVirtualEnvironment() |
795 virtenvPath = self.getVirtualEnvironment() |
744 if self.__ericProject.startswithProjectPath(virtenvPath): |
796 if self.__ericProject.startswithProjectPath(virtenvPath): |
745 relVirtenvPath = self.__ericProject.getRelativeUniversalPath( |
797 relVirtenvPath = self.__ericProject.getRelativeUniversalPath( |
746 virtenvPath) |
798 virtenvPath |
|
799 ) |
747 if relVirtenvPath not in self.__ericProject.pdata["FILETYPES"]: |
800 if relVirtenvPath not in self.__ericProject.pdata["FILETYPES"]: |
748 self.__ericProject.pdata["FILETYPES"][relVirtenvPath] = ( |
801 self.__ericProject.pdata["FILETYPES"][relVirtenvPath] = "__IGNORE__" |
749 "__IGNORE__" |
|
750 ) |
|
751 self.__ericProject.setDirty(True) |
802 self.__ericProject.setDirty(True) |
752 |
803 |
753 def __setDebugEnvironment(self): |
804 def __setDebugEnvironment(self): |
754 """ |
805 """ |
755 Private method to set the virtual environment as the selected debug |
806 Private method to set the virtual environment as the selected debug |
756 environment. |
807 environment. |
757 """ |
808 """ |
758 language = self.__ericProject.getProjectLanguage() |
809 language = self.__ericProject.getProjectLanguage() |
759 if language == "Python3": |
810 if language == "Python3": |
760 # get project specific virtual environment name |
811 # get project specific virtual environment name |
761 venvName = self.getData("flask", "virtual_environment_name") |
812 venvName = self.getData("flask", "virtual_environment_name") |
762 if not venvName: |
813 if not venvName: |
763 venvName = self.__plugin.getPreferences( |
814 venvName = self.__plugin.getPreferences("VirtualEnvironmentNamePy3") |
764 "VirtualEnvironmentNamePy3") |
|
765 if venvName: |
815 if venvName: |
766 self.__ericProject.debugProperties["VIRTUALENV"] = venvName |
816 self.__ericProject.debugProperties["VIRTUALENV"] = venvName |
767 |
817 |
768 ################################################################## |
818 ################################################################## |
769 ## slot below implements documentation function |
819 ## slot below implements documentation function |
770 ################################################################## |
820 ################################################################## |
771 |
821 |
772 def __showDocumentation(self): |
822 def __showDocumentation(self): |
773 """ |
823 """ |
774 Private slot to show the helpviewer with the Flask documentation. |
824 Private slot to show the helpviewer with the Flask documentation. |
775 """ |
825 """ |
776 page = self.__plugin.getPreferences("FlaskDocUrl") |
826 page = self.__plugin.getPreferences("FlaskDocUrl") |
777 self.__ui.launchHelpViewer(page) |
827 self.__ui.launchHelpViewer(page) |
778 |
828 |
779 ################################################################## |
829 ################################################################## |
780 ## slots below implement run functions for the server |
830 ## slots below implement run functions for the server |
781 ################################################################## |
831 ################################################################## |
782 |
832 |
783 @pyqtSlot() |
833 @pyqtSlot() |
784 def __runServer(self, development=False): |
834 def __runServer(self, development=False): |
785 """ |
835 """ |
786 Private slot to start the Flask Web server. |
836 Private slot to start the Flask Web server. |
787 |
837 |
788 @param development flag indicating development mode |
838 @param development flag indicating development mode |
789 @type bool |
839 @type bool |
790 """ |
840 """ |
791 from .RunServerDialog import RunServerDialog |
841 from .RunServerDialog import RunServerDialog |
792 |
842 |
793 if self.__serverDialog is not None: |
843 if self.__serverDialog is not None: |
794 self.__serverDialog.close() |
844 self.__serverDialog.close() |
795 |
845 |
796 askForOptions = self.askForServerOptionsAct.isChecked() |
846 askForOptions = self.askForServerOptionsAct.isChecked() |
797 dlg = RunServerDialog(self.__plugin, self) |
847 dlg = RunServerDialog(self.__plugin, self) |
798 if dlg.startServer(development=development, |
848 if dlg.startServer(development=development, askForOptions=askForOptions): |
799 askForOptions=askForOptions): |
|
800 dlg.show() |
849 dlg.show() |
801 self.__serverDialog = dlg |
850 self.__serverDialog = dlg |
802 |
851 |
803 @pyqtSlot() |
852 @pyqtSlot() |
804 def __runDevelopmentServer(self): |
853 def __runDevelopmentServer(self): |
805 """ |
854 """ |
806 Private slot to start the Flask Web server in development mode. |
855 Private slot to start the Flask Web server in development mode. |
807 """ |
856 """ |
808 self.__runServer(development=True) |
857 self.__runServer(development=True) |
809 |
858 |
810 ################################################################## |
859 ################################################################## |
811 ## slots below implement functions for the flask console |
860 ## slots below implement functions for the flask console |
812 ################################################################## |
861 ################################################################## |
813 |
862 |
814 @pyqtSlot() |
863 @pyqtSlot() |
815 def __runPythonShell(self): |
864 def __runPythonShell(self): |
816 """ |
865 """ |
817 Private slot to start a Python console in the app context. |
866 Private slot to start a Python console in the app context. |
818 """ |
867 """ |
819 workdir, env = self.prepareRuntimeEnvironment() |
868 workdir, env = self.prepareRuntimeEnvironment() |
820 if env is not None: |
869 if env is not None: |
821 command = self.getFlaskCommand() |
870 command = self.getFlaskCommand() |
822 |
871 |
823 consoleCmd = self.__plugin.getPreferences("ConsoleCommand") |
872 consoleCmd = self.__plugin.getPreferences("ConsoleCommand") |
824 if consoleCmd: |
873 if consoleCmd: |
825 self.__terminatePythonShell() |
874 self.__terminatePythonShell() |
826 |
875 |
827 args = Utilities.parseOptionString(consoleCmd) |
876 args = Utilities.parseOptionString(consoleCmd) |
828 args[0] = Utilities.getExecutablePath(args[0]) |
877 args[0] = Utilities.getExecutablePath(args[0]) |
829 args += [command, "shell"] |
878 args += [command, "shell"] |
830 |
879 |
831 self.__shellProcess = QProcess() |
880 self.__shellProcess = QProcess() |
832 self.__shellProcess.setProcessEnvironment(env) |
881 self.__shellProcess.setProcessEnvironment(env) |
833 self.__shellProcess.setWorkingDirectory(workdir) |
882 self.__shellProcess.setWorkingDirectory(workdir) |
834 self.__shellProcess.finished.connect( |
883 self.__shellProcess.finished.connect(self.__shellProcessFinished) |
835 self.__shellProcessFinished) |
884 |
836 |
|
837 self.__shellProcess.start(args[0], args[1:]) |
885 self.__shellProcess.start(args[0], args[1:]) |
838 self.__shellProcess.waitForStarted(10000) |
886 self.__shellProcess.waitForStarted(10000) |
839 |
887 |
840 @pyqtSlot() |
888 @pyqtSlot() |
841 def __shellProcessFinished(self): |
889 def __shellProcessFinished(self): |
842 """ |
890 """ |
843 Private slot connected to the finished signal. |
891 Private slot connected to the finished signal. |
844 """ |
892 """ |
845 self.__shellProcess = None |
893 self.__shellProcess = None |
846 |
894 |
847 def __terminatePythonShell(self): |
895 def __terminatePythonShell(self): |
848 """ |
896 """ |
849 Private method to terminate the current Python console. |
897 Private method to terminate the current Python console. |
850 """ |
898 """ |
851 if ( |
899 if ( |
852 self.__shellProcess is not None and |
900 self.__shellProcess is not None |
853 self.__shellProcess.state() != QProcess.ProcessState.NotRunning |
901 and self.__shellProcess.state() != QProcess.ProcessState.NotRunning |
854 ): |
902 ): |
855 self.__shellProcess.terminate() |
903 self.__shellProcess.terminate() |
856 QTimer.singleShot(2000, self.__shellProcess.kill) |
904 QTimer.singleShot(2000, self.__shellProcess.kill) |
857 self.__shellProcess.waitForFinished(3000) |
905 self.__shellProcess.waitForFinished(3000) |
858 |
906 |
859 ################################################################## |
907 ################################################################## |
860 ## slots below implement various debugging functions |
908 ## slots below implement various debugging functions |
861 ################################################################## |
909 ################################################################## |
862 |
910 |
863 @pyqtSlot() |
911 @pyqtSlot() |
864 def __showRoutes(self): |
912 def __showRoutes(self): |
865 """ |
913 """ |
866 Private slot showing all URL dispatch routes. |
914 Private slot showing all URL dispatch routes. |
867 """ |
915 """ |
868 from .RoutesDialog import RoutesDialog |
916 from .RoutesDialog import RoutesDialog |
869 |
917 |
870 if self.__routesDialog is not None: |
918 if self.__routesDialog is not None: |
871 self.__routesDialog.close() |
919 self.__routesDialog.close() |
872 |
920 |
873 dlg = RoutesDialog(self) |
921 dlg = RoutesDialog(self) |
874 if dlg.showRoutes(): |
922 if dlg.showRoutes(): |
875 dlg.show() |
923 dlg.show() |
876 self.__routesDialog = dlg |
924 self.__routesDialog = dlg |