src/eric7/MicroPython/MicroPythonWindow.py

branch
eric7
changeset 10518
1682f3203ae5
child 10523
e4069ddd7dc7
equal deleted inserted replaced
10517:aecd5a8c958c 10518:1682f3203ae5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2024 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the standalone MicroPython window.
8 """
9
10 import contextlib
11
12 from PyQt6.QtCore import QSize, Qt, QUrl, pyqtSignal, pyqtSlot
13 from PyQt6.QtGui import QDesktopServices
14 from PyQt6.QtNetwork import QNetworkAccessManager, QNetworkProxyFactory
15 from PyQt6.QtWidgets import QDialog, QSplitter, QWidget
16
17 from eric7 import Preferences
18 from eric7.EricNetwork.EricNetworkProxyFactory import (
19 EricNetworkProxyFactory,
20 proxyAuthenticationRequired,
21 )
22 from eric7.EricWidgets import EricMessageBox
23 from eric7.EricWidgets.EricApplication import ericApp
24 from eric7.EricWidgets.EricMainWindow import EricMainWindow
25 from eric7.EricWidgets.EricSideBar import EricSideBar, EricSideBarSide
26 from eric7.MicroPython.MicroPythonWidget import MicroPythonWidget
27 from eric7.PipInterface.Pip import Pip
28 from eric7.QScintilla.MiniEditor import MiniEditor
29 from eric7.SystemUtilities import FileSystemUtilities
30
31 try:
32 from eric7.EricNetwork.EricSslErrorHandler import (
33 EricSslErrorHandler,
34 EricSslErrorState,
35 )
36
37 SSL_AVAILABLE = True
38 except ImportError:
39 SSL_AVAILABLE = False
40
41
42 class MicroPythonWindow(EricMainWindow):
43 """
44 Class implementing the standalone MicroPython window.
45
46 @signal preferencesChanged() emitted after the preferences were changed
47 """
48
49 preferencesChanged = pyqtSignal()
50
51 def __init__(self, parent=None):
52 """
53 Constructor
54
55 @param parent reference to the parent widget
56 @type QWidget
57 """
58 super().__init__(parent)
59
60 self.__pip = Pip(self)
61
62 # create the window layout
63 self.__mpyWidget = MicroPythonWidget(parent=self, forMPyWindow=True)
64 self.__mpyWidget.aboutToDisconnect.connect(self.__deviceDisconnect)
65
66 self.__bottomSidebar = EricSideBar(
67 EricSideBarSide.SOUTH, Preferences.getUI("IconBarSize")
68 )
69 self.__bottomSidebar.setIconBarColor(Preferences.getUI("IconBarColor"))
70
71 self.__verticalSplitter = QSplitter(Qt.Orientation.Vertical)
72 self.__verticalSplitter.setChildrenCollapsible(False)
73 self.__verticalSplitter.addWidget(self.__mpyWidget)
74 self.__verticalSplitter.addWidget(self.__bottomSidebar)
75 self.setCentralWidget(self.__verticalSplitter)
76
77 self.setWindowTitle(self.tr("MicroPython / CircuitPython Devices"))
78
79 g = Preferences.getGeometry("MPyWindowGeometry")
80 if g.isEmpty():
81 s = QSize(800, 1000)
82 self.resize(s)
83 else:
84 self.restoreGeometry(g)
85 self.__verticalSplitter.setSizes(
86 Preferences.getMicroPython("MPyWindowSplitterSizes")
87 )
88
89 # register the objects
90 ericApp().registerObject("UserInterface", self)
91 ericApp().registerObject("ViewManager", self)
92 ericApp().registerObject("Pip", self.__pip)
93 ericApp().registerObject("MicroPython", self.__mpyWidget)
94
95 # attributes to manage the open editors
96 self.__editors = []
97 self.__activeEditor = None
98
99 ericApp().focusChanged.connect(self.__appFocusChanged)
100
101 # network related setup
102 if Preferences.getUI("UseSystemProxy"):
103 QNetworkProxyFactory.setUseSystemConfiguration(True)
104 else:
105 self.__proxyFactory = EricNetworkProxyFactory()
106 QNetworkProxyFactory.setApplicationProxyFactory(self.__proxyFactory)
107 QNetworkProxyFactory.setUseSystemConfiguration(False)
108
109 self.__networkManager = QNetworkAccessManager(self)
110 self.__networkManager.proxyAuthenticationRequired.connect(
111 proxyAuthenticationRequired
112 )
113 if SSL_AVAILABLE:
114 self.__sslErrorHandler = EricSslErrorHandler(self)
115 self.__networkManager.sslErrors.connect(self.__sslErrors)
116 self.__replies = []
117
118 self.__bottomSidebar.setVisible(False)
119
120 def closeEvent(self, evt):
121 """
122 Protected event handler for the close event.
123
124 @param evt reference to the close event
125 <br />This event is simply accepted after the history has been
126 saved and all window references have been deleted.
127 @type QCloseEvent
128 """
129 Preferences.setGeometry("MPyWindowGeometry", self.saveGeometry())
130 Preferences.setMicroPython(
131 "MPyWindowSplitterSizes", self.__verticalSplitter.sizes()
132 )
133
134 for editor in self.__editors[:]:
135 with contextlib.suppress(RuntimeError):
136 editor.close()
137
138 evt.accept()
139
140 def __sslErrors(self, reply, errors):
141 """
142 Private slot to handle SSL errors.
143
144 @param reply reference to the reply object
145 @type QNetworkReply
146 @param errors list of SSL errors
147 @type list of QSslError
148 """
149 ignored = self.__sslErrorHandler.sslErrorsReply(reply, errors)[0]
150 if ignored == EricSslErrorState.NOT_IGNORED:
151 self.__downloadCancelled = True
152
153 #######################################################################
154 ## Methods below implement user interface methods needed by the
155 ## MicroPython widget.
156 #######################################################################
157
158 def addSideWidget(
159 self,
160 side, # noqa U100
161 widget,
162 icon,
163 label,
164 ):
165 """
166 Public method to add a widget to the sides.
167
168 @param side side to add the widget to
169 @type UserInterfaceSide
170 @param widget reference to the widget to add
171 @type QWidget
172 @param icon icon to be used
173 @type QIcon
174 @param label label text to be shown
175 @type str
176 """
177 self.__bottomSidebar.addTab(widget, icon, label)
178 self.__bottomSidebar.setVisible(True)
179
180 def showSideWidget(self, widget):
181 """
182 Public method to show a specific widget placed in the side widgets.
183
184 @param widget reference to the widget to be shown
185 @type QWidget
186 """
187 index = self.__bottomSidebar.indexOf(widget)
188 if index != -1:
189 self.__bottomSidebar.show()
190 self.__bottomSidebar.setCurrentIndex(index)
191 self.__bottomSidebar.raise_()
192
193 def removeSideWidget(self, widget):
194 """
195 Public method to remove a widget added using addSideWidget().
196
197 @param widget reference to the widget to remove
198 @type QWidget
199 """
200 index = self.__bottomSidebar.indexOf(widget)
201 if index != -1:
202 self.__bottomSidebar.removeTab(index)
203
204 self.__bottomSidebar.setVisible(self.__bottomSidebar.count() > 0)
205
206 def networkAccessManager(self):
207 """
208 Public method to get a reference to the network access manager object.
209
210 @return reference to the network access manager object
211 @rtype QNetworkAccessManager
212 """
213 return self.__networkManager
214
215 def launchHelpViewer(self, url):
216 """
217 Public slot to start the help viewer/web browser.
218
219 @param url URL to be opened
220 @type str or QUrl
221 """
222 started = QDesktopServices.openUrl(QUrl(url))
223 if not started:
224 EricMessageBox.critical(
225 self, self.tr("Open Browser"), self.tr("Could not start a web browser")
226 )
227
228 @pyqtSlot()
229 @pyqtSlot(str)
230 def showPreferences(self, pageName=None):
231 """
232 Public slot to set the preferences.
233
234 @param pageName name of the configuration page to show
235 @type str
236 """
237 from eric7.Preferences.ConfigurationDialog import (
238 ConfigurationDialog,
239 ConfigurationMode,
240 )
241
242 dlg = ConfigurationDialog(
243 None,
244 "Configuration",
245 True,
246 fromEric=True,
247 displayMode=ConfigurationMode.MICROPYTHONMODE,
248 )
249 dlg.show()
250 if pageName is not None:
251 dlg.showConfigurationPageByName(pageName)
252 else:
253 dlg.showConfigurationPageByName("empty")
254 dlg.exec()
255 if dlg.result() == QDialog.DialogCode.Accepted:
256 dlg.setPreferences()
257 Preferences.syncPreferences()
258 self.__preferencesChanged()
259
260 @pyqtSlot()
261 def __preferencesChanged(self):
262 """
263 Private slot to handle a change of the preferences.
264 """
265 self.__bottomSidebar.setIconBarColor(Preferences.getUI("IconBarColor"))
266 self.__bottomSidebar.setIconBarSize(Preferences.getUI("IconBarSize"))
267
268 if Preferences.getUI("UseSystemProxy"):
269 QNetworkProxyFactory.setUseSystemConfiguration(True)
270 else:
271 self.__proxyFactory = EricNetworkProxyFactory()
272 QNetworkProxyFactory.setApplicationProxyFactory(self.__proxyFactory)
273 QNetworkProxyFactory.setUseSystemConfiguration(False)
274
275 self.preferencesChanged.emit()
276
277 #######################################################################
278 ## Methods below implement view manager methods needed by the
279 ## MicroPython widget.
280 #######################################################################
281
282 def activeWindow(self):
283 """
284 Public method to get a reference to the active editor.
285
286 @return reference to the active editor
287 @rtype MiniEditor
288 """
289 return self.__activeEditor
290
291 def getEditor(self, fn):
292 """
293 Public method to return the editor displaying the given file.
294
295 @param fn filename to look for
296 @type str
297 """
298 for editor in self.__editors:
299 if editor.getFileName() == fn:
300 editor.raise_()
301 break
302 else:
303 editor = MiniEditor(filename=fn, parent=self)
304 editor.closing.connect(lambda: self.__editorClosing(editor))
305 editor.show()
306
307 self.__editors.append(editor)
308
309 def newEditorWithText(self, text, language="", fileName=""):
310 """
311 Public method to generate a new editor with a given text and associated file
312 name.
313
314 @param text text for the editor
315 @type str
316 @param language source language (defaults to "")
317 @type str (optional)
318 @param fileName associated file name (defaults to "")
319 @type str (optional)
320 """
321 editor = MiniEditor(filename=fileName, parent=self)
322 editor.closing.connect(lambda: self.__editorClosing(editor))
323 editor.setText(text, filetype=language)
324 editor.setLanguage(fileName)
325 editor.show()
326
327 self.__editors.append(editor)
328
329 def __editorClosing(self, editor):
330 """
331 Private method called, when an editor is closing.
332
333 @param editor reference to the closing editor
334 @type MiniEditor
335 """
336 with contextlib.suppress(ValueError):
337 self.__editors.remove(editor)
338 del editor
339
340 if self.__editors:
341 # make the last one (i.e. most recently opened one) the active editor
342 self.__activeEditor = self.__editors[-1]
343 else:
344 self.__activeEditor = None
345
346 @pyqtSlot(QWidget, QWidget)
347 def __appFocusChanged(self, old, now):
348 """
349 Private slot to track the application focus.
350
351 @param old reference to the widget loosing focus
352 @type QWidget
353 @param now reference to the widget gaining focus
354 @type QWidget
355 """
356 if now is None:
357 return
358
359 for editor in self.__editors:
360 if now in editor.findChildren(QWidget):
361 self.__activeEditor = editor
362 break
363
364 @pyqtSlot()
365 def __deviceDisconnect(self):
366 """
367 Private slot handling the device being disconnected.
368
369 This closes all editors directly connected to the device about to
370 be disconnected.
371 """
372 for editor in self.__editors[:]:
373 if FileSystemUtilities.isDeviceFileName(editor.getFileName()):
374 editor.close()

eric ide

mercurial