|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2006 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the Debugger General configuration page. |
|
8 """ |
|
9 |
|
10 import re |
|
11 import socket |
|
12 |
|
13 from PyQt5.QtCore import pyqtSlot, Qt, QAbstractItemModel, QModelIndex |
|
14 from PyQt5.QtGui import QBrush, QColor |
|
15 from PyQt5.QtWidgets import QLineEdit, QInputDialog |
|
16 from PyQt5.QtNetwork import QNetworkInterface, QAbstractSocket, QHostAddress |
|
17 |
|
18 from E5Gui.E5Application import e5App |
|
19 from E5Gui.E5Completers import E5FileCompleter, E5DirCompleter |
|
20 from E5Gui import E5MessageBox |
|
21 |
|
22 from .ConfigurationPageBase import ConfigurationPageBase |
|
23 from .Ui_DebuggerGeneralPage import Ui_DebuggerGeneralPage |
|
24 |
|
25 import Preferences |
|
26 import Utilities |
|
27 |
|
28 |
|
29 class DebuggerGeneralPage(ConfigurationPageBase, Ui_DebuggerGeneralPage): |
|
30 """ |
|
31 Class implementing the Debugger General configuration page. |
|
32 """ |
|
33 def __init__(self): |
|
34 """ |
|
35 Constructor |
|
36 """ |
|
37 super().__init__() |
|
38 self.setupUi(self) |
|
39 self.setObjectName("DebuggerGeneralPage") |
|
40 |
|
41 t = self.execLineEdit.whatsThis() |
|
42 if t: |
|
43 t += Utilities.getPercentReplacementHelp() |
|
44 self.execLineEdit.setWhatsThis(t) |
|
45 |
|
46 try: |
|
47 backends = e5App().getObject("DebugServer").getSupportedLanguages() |
|
48 for backend in sorted(backends): |
|
49 self.passiveDbgBackendCombo.addItem(backend) |
|
50 except KeyError: |
|
51 self.passiveDbgGroup.setEnabled(False) |
|
52 |
|
53 t = self.consoleDbgEdit.whatsThis() |
|
54 if t: |
|
55 t += Utilities.getPercentReplacementHelp() |
|
56 self.consoleDbgEdit.setWhatsThis(t) |
|
57 |
|
58 self.consoleDbgCompleter = E5FileCompleter(self.consoleDbgEdit) |
|
59 self.dbgTranslationLocalCompleter = E5DirCompleter( |
|
60 self.dbgTranslationLocalEdit) |
|
61 |
|
62 # set initial values |
|
63 interfaces = [] |
|
64 networkInterfaces = QNetworkInterface.allInterfaces() |
|
65 for networkInterface in networkInterfaces: |
|
66 addressEntries = networkInterface.addressEntries() |
|
67 if len(addressEntries) > 0: |
|
68 for addressEntry in addressEntries: |
|
69 if ( |
|
70 ":" in addressEntry.ip().toString() and |
|
71 not socket.has_ipv6 |
|
72 ): |
|
73 continue # IPv6 not supported by Python |
|
74 interfaces.append( |
|
75 "{0} ({1})".format( |
|
76 networkInterface.humanReadableName(), |
|
77 addressEntry.ip().toString())) |
|
78 self.interfacesCombo.addItems(interfaces) |
|
79 interface = Preferences.getDebugger("NetworkInterface") |
|
80 if not socket.has_ipv6: |
|
81 # IPv6 not supported by Python |
|
82 self.all6InterfacesButton.setEnabled(False) |
|
83 if interface == "allv6": |
|
84 interface = "all" |
|
85 if interface == "all": |
|
86 self.allInterfacesButton.setChecked(True) |
|
87 elif interface == "allv6": |
|
88 self.all6InterfacesButton.setChecked(True) |
|
89 else: |
|
90 self.selectedInterfaceButton.setChecked(True) |
|
91 index = -1 |
|
92 for i in range(len(interfaces)): |
|
93 if ( |
|
94 re.fullmatch(".*{0}.*".format(interface), interfaces[i]) |
|
95 ): |
|
96 index = i |
|
97 break |
|
98 self.interfacesCombo.setCurrentIndex(index) |
|
99 |
|
100 self.allowedHostsList.addItems( |
|
101 Preferences.getDebugger("AllowedHosts")) |
|
102 |
|
103 self.remoteDebuggerGroup.setChecked( |
|
104 Preferences.getDebugger("RemoteDbgEnabled")) |
|
105 self.hostLineEdit.setText( |
|
106 Preferences.getDebugger("RemoteHost")) |
|
107 self.execLineEdit.setText( |
|
108 Preferences.getDebugger("RemoteExecution")) |
|
109 |
|
110 if self.passiveDbgGroup.isEnabled(): |
|
111 self.passiveDbgCheckBox.setChecked( |
|
112 Preferences.getDebugger("PassiveDbgEnabled")) |
|
113 self.passiveDbgPortSpinBox.setValue( |
|
114 Preferences.getDebugger("PassiveDbgPort")) |
|
115 index = self.passiveDbgBackendCombo.findText( |
|
116 Preferences.getDebugger("PassiveDbgType")) |
|
117 if index == -1: |
|
118 index = 0 |
|
119 self.passiveDbgBackendCombo.setCurrentIndex(index) |
|
120 |
|
121 self.debugEnvironReplaceCheckBox.setChecked( |
|
122 Preferences.getDebugger("DebugEnvironmentReplace")) |
|
123 self.debugEnvironEdit.setText( |
|
124 Preferences.getDebugger("DebugEnvironment")) |
|
125 self.automaticResetCheckBox.setChecked( |
|
126 Preferences.getDebugger("AutomaticReset")) |
|
127 self.debugAutoSaveScriptsCheckBox.setChecked( |
|
128 Preferences.getDebugger("Autosave")) |
|
129 self.consoleDebuggerGroup.setChecked( |
|
130 Preferences.getDebugger("ConsoleDbgEnabled")) |
|
131 self.consoleDbgEdit.setText( |
|
132 Preferences.getDebugger("ConsoleDbgCommand")) |
|
133 self.dbgPathTranslationGroup.setChecked( |
|
134 Preferences.getDebugger("PathTranslation")) |
|
135 self.dbgTranslationRemoteEdit.setText( |
|
136 Preferences.getDebugger("PathTranslationRemote")) |
|
137 self.dbgTranslationLocalEdit.setText( |
|
138 Preferences.getDebugger("PathTranslationLocal")) |
|
139 self.multiprocessCheckBox.setChecked( |
|
140 Preferences.getDebugger("MultiProcessEnabled")) |
|
141 self.debugThreeStateBreakPoint.setChecked( |
|
142 Preferences.getDebugger("ThreeStateBreakPoints")) |
|
143 self.recentFilesSpinBox.setValue( |
|
144 Preferences.getDebugger("RecentNumber")) |
|
145 self.exceptionBreakCheckBox.setChecked( |
|
146 Preferences.getDebugger("BreakAlways")) |
|
147 self.exceptionShellCheckBox.setChecked( |
|
148 Preferences.getDebugger("ShowExceptionInShell")) |
|
149 self.maxSizeSpinBox.setValue( |
|
150 Preferences.getDebugger("MaxVariableSize")) |
|
151 # Set the colours for debug viewer backgrounds |
|
152 self.previewMdl = PreviewModel() |
|
153 self.preView.setModel(self.previewMdl) |
|
154 self.colourChanged.connect(self.previewMdl.setColor) |
|
155 self.initColour("BgColorNew", self.backgroundNewButton, |
|
156 Preferences.getDebugger, hasAlpha=True) |
|
157 self.initColour("BgColorChanged", self.backgroundChangedButton, |
|
158 Preferences.getDebugger, hasAlpha=True) |
|
159 |
|
160 self.autoViewSourcecodeCheckBox.setChecked( |
|
161 Preferences.getDebugger("AutoViewSourceCode")) |
|
162 |
|
163 def save(self): |
|
164 """ |
|
165 Public slot to save the Debugger General (1) configuration. |
|
166 """ |
|
167 Preferences.setDebugger( |
|
168 "RemoteDbgEnabled", |
|
169 self.remoteDebuggerGroup.isChecked()) |
|
170 Preferences.setDebugger( |
|
171 "RemoteHost", |
|
172 self.hostLineEdit.text()) |
|
173 Preferences.setDebugger( |
|
174 "RemoteExecution", |
|
175 self.execLineEdit.text()) |
|
176 |
|
177 Preferences.setDebugger( |
|
178 "PassiveDbgEnabled", |
|
179 self.passiveDbgCheckBox.isChecked()) |
|
180 Preferences.setDebugger( |
|
181 "PassiveDbgPort", |
|
182 self.passiveDbgPortSpinBox.value()) |
|
183 Preferences.setDebugger( |
|
184 "PassiveDbgType", |
|
185 self.passiveDbgBackendCombo.currentText()) |
|
186 |
|
187 if self.allInterfacesButton.isChecked(): |
|
188 Preferences.setDebugger("NetworkInterface", "all") |
|
189 elif self.all6InterfacesButton.isChecked(): |
|
190 Preferences.setDebugger("NetworkInterface", "allv6") |
|
191 else: |
|
192 interface = self.interfacesCombo.currentText() |
|
193 interface = interface.split("(")[1].split(")")[0] |
|
194 if not interface: |
|
195 Preferences.setDebugger("NetworkInterface", "all") |
|
196 else: |
|
197 Preferences.setDebugger("NetworkInterface", interface) |
|
198 |
|
199 allowedHosts = [] |
|
200 for row in range(self.allowedHostsList.count()): |
|
201 allowedHosts.append(self.allowedHostsList.item(row).text()) |
|
202 Preferences.setDebugger("AllowedHosts", allowedHosts) |
|
203 |
|
204 Preferences.setDebugger( |
|
205 "DebugEnvironmentReplace", |
|
206 self.debugEnvironReplaceCheckBox.isChecked()) |
|
207 Preferences.setDebugger( |
|
208 "DebugEnvironment", |
|
209 self.debugEnvironEdit.text()) |
|
210 Preferences.setDebugger( |
|
211 "AutomaticReset", |
|
212 self.automaticResetCheckBox.isChecked()) |
|
213 Preferences.setDebugger( |
|
214 "Autosave", |
|
215 self.debugAutoSaveScriptsCheckBox.isChecked()) |
|
216 Preferences.setDebugger( |
|
217 "ConsoleDbgEnabled", |
|
218 self.consoleDebuggerGroup.isChecked()) |
|
219 Preferences.setDebugger( |
|
220 "ConsoleDbgCommand", |
|
221 self.consoleDbgEdit.text()) |
|
222 Preferences.setDebugger( |
|
223 "PathTranslation", |
|
224 self.dbgPathTranslationGroup.isChecked()) |
|
225 Preferences.setDebugger( |
|
226 "PathTranslationRemote", |
|
227 self.dbgTranslationRemoteEdit.text()) |
|
228 Preferences.setDebugger( |
|
229 "PathTranslationLocal", |
|
230 self.dbgTranslationLocalEdit.text()) |
|
231 Preferences.setDebugger( |
|
232 "MultiProcessEnabled", |
|
233 self.multiprocessCheckBox.isChecked()) |
|
234 Preferences.setDebugger( |
|
235 "ThreeStateBreakPoints", |
|
236 self.debugThreeStateBreakPoint.isChecked()) |
|
237 Preferences.setDebugger( |
|
238 "RecentNumber", |
|
239 self.recentFilesSpinBox.value()) |
|
240 Preferences.setDebugger( |
|
241 "BreakAlways", |
|
242 self.exceptionBreakCheckBox.isChecked()) |
|
243 Preferences.setDebugger( |
|
244 "ShowExceptionInShell", |
|
245 self.exceptionShellCheckBox.isChecked()) |
|
246 Preferences.setDebugger( |
|
247 "MaxVariableSize", |
|
248 self.maxSizeSpinBox.value()) |
|
249 # Store background colors for debug viewer |
|
250 self.saveColours(Preferences.setDebugger) |
|
251 |
|
252 Preferences.setDebugger( |
|
253 "AutoViewSourceCode", |
|
254 self.autoViewSourcecodeCheckBox.isChecked()) |
|
255 |
|
256 def on_allowedHostsList_currentItemChanged(self, current, previous): |
|
257 """ |
|
258 Private method set the state of the edit and delete button. |
|
259 |
|
260 @param current new current item (QListWidgetItem) |
|
261 @param previous previous current item (QListWidgetItem) |
|
262 """ |
|
263 self.editAllowedHostButton.setEnabled(current is not None) |
|
264 self.deleteAllowedHostButton.setEnabled(current is not None) |
|
265 |
|
266 @pyqtSlot() |
|
267 def on_addAllowedHostButton_clicked(self): |
|
268 """ |
|
269 Private slot called to add a new allowed host. |
|
270 """ |
|
271 allowedHost, ok = QInputDialog.getText( |
|
272 None, |
|
273 self.tr("Add allowed host"), |
|
274 self.tr("Enter the IP address of an allowed host"), |
|
275 QLineEdit.EchoMode.Normal) |
|
276 if ok and allowedHost: |
|
277 if QHostAddress(allowedHost).protocol() in [ |
|
278 QAbstractSocket.NetworkLayerProtocol.IPv4Protocol, |
|
279 QAbstractSocket.NetworkLayerProtocol.IPv6Protocol |
|
280 ]: |
|
281 self.allowedHostsList.addItem(allowedHost) |
|
282 else: |
|
283 E5MessageBox.critical( |
|
284 self, |
|
285 self.tr("Add allowed host"), |
|
286 self.tr( |
|
287 """<p>The entered address <b>{0}</b> is not""" |
|
288 """ a valid IP v4 or IP v6 address.""" |
|
289 """ Aborting...</p>""") |
|
290 .format(allowedHost)) |
|
291 |
|
292 @pyqtSlot() |
|
293 def on_deleteAllowedHostButton_clicked(self): |
|
294 """ |
|
295 Private slot called to delete an allowed host. |
|
296 """ |
|
297 self.allowedHostsList.takeItem(self.allowedHostsList.currentRow()) |
|
298 |
|
299 @pyqtSlot() |
|
300 def on_editAllowedHostButton_clicked(self): |
|
301 """ |
|
302 Private slot called to edit an allowed host. |
|
303 """ |
|
304 allowedHost = self.allowedHostsList.currentItem().text() |
|
305 allowedHost, ok = QInputDialog.getText( |
|
306 None, |
|
307 self.tr("Edit allowed host"), |
|
308 self.tr("Enter the IP address of an allowed host"), |
|
309 QLineEdit.EchoMode.Normal, |
|
310 allowedHost) |
|
311 if ok and allowedHost: |
|
312 if QHostAddress(allowedHost).protocol() in [ |
|
313 QAbstractSocket.NetworkLayerProtocol.IPv4Protocol, |
|
314 QAbstractSocket.NetworkLayerProtocol.IPv6Protocol |
|
315 ]: |
|
316 self.allowedHostsList.currentItem().setText(allowedHost) |
|
317 else: |
|
318 E5MessageBox.critical( |
|
319 self, |
|
320 self.tr("Edit allowed host"), |
|
321 self.tr( |
|
322 """<p>The entered address <b>{0}</b> is not""" |
|
323 """ a valid IP v4 or IP v6 address.""" |
|
324 """ Aborting...</p>""") |
|
325 .format(allowedHost)) |
|
326 |
|
327 |
|
328 class PreviewModel(QAbstractItemModel): |
|
329 """ |
|
330 Class to show an example of the selected background colours for the debug |
|
331 viewer. |
|
332 """ |
|
333 def __init__(self): |
|
334 """ |
|
335 Constructor |
|
336 """ |
|
337 super().__init__() |
|
338 self.bgColorNew = QBrush(QColor('#FFFFFF')) |
|
339 self.bgColorChanged = QBrush(QColor('#FFFFFF')) |
|
340 |
|
341 def setColor(self, key, bgcolour): |
|
342 """ |
|
343 Public slot to update the background colour indexed by key. |
|
344 |
|
345 @param key the name of background |
|
346 @type str |
|
347 @param bgcolour the new background colour |
|
348 @type QColor |
|
349 """ |
|
350 if key == 'BgColorNew': |
|
351 self.bgColorNew = QBrush(bgcolour) |
|
352 else: |
|
353 self.bgColorChanged = QBrush(bgcolour) |
|
354 |
|
355 # Force update of preview view |
|
356 idxStart = self.index(0, 0, QModelIndex()) |
|
357 idxEnd = self.index(0, 2, QModelIndex()) |
|
358 self.dataChanged.emit(idxStart, idxEnd) |
|
359 |
|
360 def index(self, row, column, parent=QModelIndex()): |
|
361 """ |
|
362 Public Qt slot to get the index of item at row:column of parent. |
|
363 |
|
364 @param row number of rows |
|
365 @type int |
|
366 @param column number of columns |
|
367 @type int |
|
368 @param parent the model parent |
|
369 @type QModelIndex |
|
370 @return new model index for child |
|
371 @rtype QModelIndex |
|
372 """ |
|
373 if not self.hasIndex(row, column, parent): |
|
374 return QModelIndex() |
|
375 |
|
376 return self.createIndex(row, column, None) |
|
377 |
|
378 def parent(self, child): |
|
379 """ |
|
380 Public Qt slot to get the parent of the given child. |
|
381 |
|
382 @param child the model child node |
|
383 @type QModelIndex |
|
384 @return new model index for parent |
|
385 @rtype QModelIndex |
|
386 """ |
|
387 return QModelIndex() |
|
388 |
|
389 def columnCount(self, parent=QModelIndex()): |
|
390 """ |
|
391 Public Qt slot to get the column count. |
|
392 |
|
393 @param parent the model parent |
|
394 @type QModelIndex |
|
395 @return number of columns |
|
396 @rtype int |
|
397 """ |
|
398 return 1 |
|
399 |
|
400 def rowCount(self, parent=QModelIndex()): |
|
401 """ |
|
402 Public Qt slot to get the row count. |
|
403 |
|
404 @param parent the model parent |
|
405 @type QModelIndex |
|
406 @return number of rows |
|
407 @rtype int |
|
408 """ |
|
409 return 4 |
|
410 |
|
411 def flags(self, index): |
|
412 """ |
|
413 Public Qt slot to get the item flags. |
|
414 |
|
415 @param index of item |
|
416 @type QModelIndex |
|
417 @return item flags |
|
418 @rtype QtCore.Qt.ItemFlag |
|
419 """ |
|
420 return Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable |
|
421 |
|
422 def data(self, index, role=Qt.ItemDataRole.DisplayRole): |
|
423 """ |
|
424 Public Qt slot get the role data of item. |
|
425 |
|
426 @param index the model index |
|
427 @type QModelIndex |
|
428 @param role the requested data role |
|
429 @type QtCore.Qt.ItemDataRole |
|
430 @return role data of item |
|
431 @rtype str, QBrush or None |
|
432 """ |
|
433 if role == Qt.ItemDataRole.DisplayRole: |
|
434 return self.tr("Variable Name") |
|
435 elif role == Qt.ItemDataRole.BackgroundRole: |
|
436 if index.row() >= 2: |
|
437 return self.bgColorChanged |
|
438 else: |
|
439 return self.bgColorNew |
|
440 |
|
441 return None |
|
442 |
|
443 |
|
444 def create(dlg): |
|
445 """ |
|
446 Module function to create the configuration page. |
|
447 |
|
448 @param dlg reference to the configuration dialog |
|
449 @return reference to the instantiated page (ConfigurationPageBase) |
|
450 """ |
|
451 return DebuggerGeneralPage() |
|
452 |
|
453 # |
|
454 # eflag: noqa = M822 |