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