src/eric7/CondaInterface/CondaPackagesWidget.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 8881
54e42bc2437a
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2019 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the conda packages management widget.
8 """
9
10 import os
11
12 from PyQt6.QtCore import pyqtSlot, Qt
13 from PyQt6.QtWidgets import (
14 QWidget, QToolButton, QMenu, QTreeWidgetItem, QApplication, QLineEdit,
15 QDialog
16 )
17
18 from EricWidgets import EricFileDialog, EricMessageBox, EricTextInputDialog
19 from EricWidgets.EricApplication import ericApp
20 from EricGui.EricOverrideCursor import EricOverrideCursor
21
22 from .Ui_CondaPackagesWidget import Ui_CondaPackagesWidget
23
24 import UI.PixmapCache
25
26 import CondaInterface
27
28
29 class CondaPackagesWidget(QWidget, Ui_CondaPackagesWidget):
30 """
31 Class implementing the conda packages management widget.
32 """
33 # Role definition of packages list
34 PackageVersionRole = Qt.ItemDataRole.UserRole + 1
35 PackageBuildRole = Qt.ItemDataRole.UserRole + 2
36
37 # Role definitions of search results list
38 PackageDetailedDataRole = Qt.ItemDataRole.UserRole + 1
39
40 def __init__(self, conda, parent=None):
41 """
42 Constructor
43
44 @param conda reference to the conda interface
45 @type Conda
46 @param parent reference to the parent widget
47 @type QWidget
48 """
49 super().__init__(parent)
50 self.setupUi(self)
51
52 self.layout().setContentsMargins(0, 3, 0, 0)
53
54 self.__conda = conda
55
56 if not CondaInterface.isCondaAvailable():
57 self.baseWidget.hide()
58 self.searchWidget.hide()
59
60 else:
61 self.notAvailableWidget.hide()
62
63 self.__initCondaInterface()
64
65 def __initCondaInterface(self):
66 """
67 Private method to initialize the conda interface elements.
68 """
69 self.statusLabel.hide()
70
71 self.condaMenuButton.setObjectName(
72 "conda_supermenu_button")
73 self.condaMenuButton.setIcon(UI.PixmapCache.getIcon("superMenu"))
74 self.condaMenuButton.setToolTip(self.tr("Conda Menu"))
75 self.condaMenuButton.setPopupMode(
76 QToolButton.ToolButtonPopupMode.InstantPopup)
77 self.condaMenuButton.setToolButtonStyle(
78 Qt.ToolButtonStyle.ToolButtonIconOnly)
79 self.condaMenuButton.setFocusPolicy(Qt.FocusPolicy.NoFocus)
80 self.condaMenuButton.setAutoRaise(True)
81 self.condaMenuButton.setShowMenuInside(True)
82
83 self.refreshButton.setIcon(UI.PixmapCache.getIcon("reload"))
84 self.upgradeButton.setIcon(UI.PixmapCache.getIcon("1uparrow"))
85 self.upgradeAllButton.setIcon(UI.PixmapCache.getIcon("2uparrow"))
86 self.uninstallButton.setIcon(UI.PixmapCache.getIcon("minus"))
87 self.searchToggleButton.setIcon(UI.PixmapCache.getIcon("find"))
88 self.searchButton.setIcon(UI.PixmapCache.getIcon("findNext"))
89 self.installButton.setIcon(UI.PixmapCache.getIcon("plus"))
90 self.showDetailsButton.setIcon(UI.PixmapCache.getIcon("info"))
91
92 if CondaInterface.condaVersion() >= (4, 4, 0):
93 self.searchOptionsWidget.hide()
94 else:
95 self.platformComboBox.addItems(sorted([
96 "", "win-32", "win-64", "osx-64", "linux-32", "linux-64",
97 ]))
98
99 self.__initCondaMenu()
100 self.__populateEnvironments()
101 self.__updateActionButtons()
102
103 self.searchWidget.hide()
104
105 self.__conda.condaEnvironmentCreated.connect(
106 self.on_refreshButton_clicked)
107 self.__conda.condaEnvironmentRemoved.connect(
108 self.on_refreshButton_clicked)
109
110 def __populateEnvironments(self):
111 """
112 Private method to get a list of environments and populate the selector.
113 """
114 environments = [("", "")] + sorted(
115 self.__conda.getCondaEnvironmentsList())
116 for environment in environments:
117 self.environmentsComboBox.addItem(environment[0], environment[1])
118
119 def __initCondaMenu(self):
120 """
121 Private method to create the super menu and attach it to the super
122 menu button.
123 """
124 self.__condaMenu = QMenu(self)
125 self.__envActs = []
126
127 self.__cleanMenu = QMenu(self.tr("Clean"), self)
128 self.__cleanMenu.addAction(
129 self.tr("All"), lambda: self.__conda.cleanConda("all"))
130 self.__cleanMenu.addAction(
131 self.tr("Cache"), lambda: self.__conda.cleanConda("index-cache"))
132 self.__cleanMenu.addAction(
133 self.tr("Lock Files"),
134 lambda: self.__conda.cleanConda("lock"))
135 self.__cleanMenu.addAction(
136 self.tr("Packages"), lambda: self.__conda.cleanConda("packages"))
137 self.__cleanMenu.addAction(
138 self.tr("Tarballs"), lambda: self.__conda.cleanConda("tarballs"))
139
140 self.__condaMenu.addAction(
141 self.tr("About Conda..."), self.__aboutConda)
142 self.__condaMenu.addSeparator()
143 self.__condaMenu.addAction(
144 self.tr("Update Conda"), self.__conda.updateConda)
145 self.__condaMenu.addSeparator()
146 self.__envActs.append(self.__condaMenu.addAction(
147 self.tr("Install Packages"), self.__installPackages))
148 self.__envActs.append(self.__condaMenu.addAction(
149 self.tr("Install Requirements"), self.__installRequirements))
150 self.__condaMenu.addSeparator()
151 self.__envActs.append(self.__condaMenu.addAction(
152 self.tr("Generate Requirements"), self.__generateRequirements))
153 self.__condaMenu.addSeparator()
154 self.__condaMenu.addAction(
155 self.tr("Create Environment from Requirements"),
156 self.__createEnvironment)
157 self.__envActs.append(self.__condaMenu.addAction(
158 self.tr("Clone Environment"), self.__cloneEnvironment))
159 self.__deleteEnvAct = self.__condaMenu.addAction(
160 self.tr("Delete Environment"), self.__deleteEnvironment)
161 self.__condaMenu.addSeparator()
162 self.__condaMenu.addMenu(self.__cleanMenu)
163 self.__condaMenu.addSeparator()
164 self.__condaMenu.addAction(
165 self.tr("Edit User Configuration..."),
166 self.__editUserConfiguration)
167 self.__condaMenu.addSeparator()
168 self.__condaMenu.addAction(
169 self.tr("Configure..."), self.__condaConfigure)
170
171 self.condaMenuButton.setMenu(self.__condaMenu)
172
173 self.__condaMenu.aboutToShow.connect(self.__aboutToShowCondaMenu)
174
175 def __selectedUpdateableItems(self):
176 """
177 Private method to get a list of selected items that can be updated.
178
179 @return list of selected items that can be updated
180 @rtype list of QTreeWidgetItem
181 """
182 return [
183 itm for itm in self.packagesList.selectedItems()
184 if bool(itm.text(2))
185 ]
186
187 def __allUpdateableItems(self):
188 """
189 Private method to get a list of all items that can be updated.
190
191 @return list of all items that can be updated
192 @rtype list of QTreeWidgetItem
193 """
194 updateableItems = []
195 for index in range(self.packagesList.topLevelItemCount()):
196 itm = self.packagesList.topLevelItem(index)
197 if itm.text(2):
198 updateableItems.append(itm)
199
200 return updateableItems
201
202 def __updateActionButtons(self):
203 """
204 Private method to set the state of the action buttons.
205 """
206 self.upgradeButton.setEnabled(
207 bool(self.__selectedUpdateableItems()))
208 self.uninstallButton.setEnabled(
209 bool(self.packagesList.selectedItems()))
210 self.upgradeAllButton.setEnabled(
211 bool(self.__allUpdateableItems()))
212
213 @pyqtSlot(int)
214 def on_environmentsComboBox_currentIndexChanged(self, index):
215 """
216 Private slot handling the selection of a conda environment.
217
218 @param index index of the selected conda environment
219 @type int
220 """
221 self.packagesList.clear()
222 prefix = self.environmentsComboBox.itemData(index)
223 if prefix:
224 self.statusLabel.show()
225 self.statusLabel.setText(self.tr("Getting installed packages..."))
226
227 with EricOverrideCursor():
228 # 1. populate with installed packages
229 self.packagesList.setUpdatesEnabled(False)
230 installedPackages = self.__conda.getInstalledPackages(
231 prefix=prefix)
232 for package, version, build in installedPackages:
233 itm = QTreeWidgetItem(self.packagesList,
234 [package, version])
235 itm.setData(1, self.PackageVersionRole, version)
236 itm.setData(1, self.PackageBuildRole, build)
237 self.packagesList.setUpdatesEnabled(True)
238 self.statusLabel.setText(
239 self.tr("Getting outdated packages..."))
240 QApplication.processEvents()
241
242 # 2. update with update information
243 self.packagesList.setUpdatesEnabled(False)
244 updateablePackages = self.__conda.getUpdateablePackages(
245 prefix=prefix)
246 for package, version, build in updateablePackages:
247 items = self.packagesList.findItems(
248 package,
249 Qt.MatchFlag.MatchExactly |
250 Qt.MatchFlag.MatchCaseSensitive)
251 if items:
252 itm = items[0]
253 itm.setText(2, version)
254 itm.setData(2, self.PackageVersionRole, version)
255 itm.setData(2, self.PackageBuildRole, build)
256 if itm.data(1, self.PackageVersionRole) == version:
257 # build must be different, show in version display
258 itm.setText(1, self.tr("{0} (Build: {1})").format(
259 itm.data(1, self.PackageVersionRole),
260 itm.data(1, self.PackageBuildRole),
261 ))
262 itm.setText(2, self.tr("{0} (Build: {1})").format(
263 itm.data(2, self.PackageVersionRole),
264 itm.data(2, self.PackageBuildRole),
265 ))
266
267 self.packagesList.sortItems(0, Qt.SortOrder.AscendingOrder)
268 for col in range(self.packagesList.columnCount()):
269 self.packagesList.resizeColumnToContents(col)
270 self.packagesList.setUpdatesEnabled(True)
271 self.statusLabel.hide()
272
273 self.__updateActionButtons()
274 self.__updateSearchActionButtons()
275
276 @pyqtSlot()
277 def on_packagesList_itemSelectionChanged(self):
278 """
279 Private slot to handle the selection of some items..
280 """
281 self.__updateActionButtons()
282
283 @pyqtSlot()
284 def on_refreshButton_clicked(self):
285 """
286 Private slot to refresh the display.
287 """
288 currentEnvironment = self.environmentsComboBox.currentText()
289 self.environmentsComboBox.clear()
290 self.packagesList.clear()
291
292 with EricOverrideCursor():
293 self.__populateEnvironments()
294
295 index = self.environmentsComboBox.findText(
296 currentEnvironment,
297 Qt.MatchFlag.MatchExactly | Qt.MatchFlag.MatchCaseSensitive)
298 if index != -1:
299 self.environmentsComboBox.setCurrentIndex(index)
300
301 self.__updateActionButtons()
302
303 @pyqtSlot()
304 def on_upgradeButton_clicked(self):
305 """
306 Private slot to upgrade selected packages of the selected environment.
307 """
308 packages = [itm.text(0) for itm in self.__selectedUpdateableItems()]
309 if packages:
310 prefix = self.environmentsComboBox.itemData(
311 self.environmentsComboBox.currentIndex())
312 ok = self.__conda.updatePackages(packages, prefix=prefix)
313 if ok:
314 self.on_refreshButton_clicked()
315
316 @pyqtSlot()
317 def on_upgradeAllButton_clicked(self):
318 """
319 Private slot to upgrade all packages of the selected environment.
320 """
321 prefix = self.environmentsComboBox.itemData(
322 self.environmentsComboBox.currentIndex())
323 ok = self.__conda.updateAllPackages(prefix=prefix)
324 if ok:
325 self.on_refreshButton_clicked()
326
327 @pyqtSlot()
328 def on_uninstallButton_clicked(self):
329 """
330 Private slot to remove selected packages of the selected environment.
331 """
332 packages = [itm.text(0) for itm in self.packagesList.selectedItems()]
333 if packages:
334 prefix = self.environmentsComboBox.itemData(
335 self.environmentsComboBox.currentIndex())
336 ok = self.__conda.uninstallPackages(packages, prefix=prefix)
337 if ok:
338 self.on_refreshButton_clicked()
339
340 #######################################################################
341 ## Search widget related methods below
342 #######################################################################
343
344 def __updateSearchActionButtons(self):
345 """
346 Private method to update the action button states of the search widget.
347 """
348 enable = len(self.searchResultList.selectedItems()) == 1
349 self.installButton.setEnabled(
350 enable and self.environmentsComboBox.currentIndex() > 0)
351 self.showDetailsButton.setEnabled(
352 enable and bool(self.searchResultList.selectedItems()[0].parent()))
353
354 def __doSearch(self):
355 """
356 Private method to search for packages.
357 """
358 self.searchResultList.clear()
359 pattern = self.searchEdit.text()
360 if pattern:
361 with EricOverrideCursor():
362 prefix = (
363 ""
364 if CondaInterface.condaVersion() >= (4, 4, 0) else
365 self.environmentsComboBox.itemData(
366 self.environmentsComboBox.currentIndex())
367 )
368 ok, result = self.__conda.searchPackages(
369 pattern,
370 fullNameOnly=self.fullNameButton.isChecked(),
371 packageSpec=self.packageSpecButton.isChecked(),
372 platform=self.platformComboBox.currentText(),
373 prefix=prefix,
374 )
375
376 if ok and result:
377 self.searchResultList.setUpdatesEnabled(False)
378 for package in result:
379 itm = QTreeWidgetItem(self.searchResultList,
380 [package])
381 itm.setExpanded(False)
382 for detail in result[package]:
383 version = detail["version"]
384 build = detail["build"]
385 if "subdir" in detail:
386 platform = detail["subdir"]
387 elif "platform" in detail:
388 platform = detail["platform"]
389 else:
390 platform = ""
391 citm = QTreeWidgetItem(
392 itm, ["", version, build, platform])
393 citm.setData(0, self.PackageDetailedDataRole,
394 detail)
395
396 self.searchResultList.sortItems(
397 0, Qt.SortOrder.AscendingOrder)
398 self.searchResultList.resizeColumnToContents(0)
399 self.searchResultList.setUpdatesEnabled(True)
400 if not ok:
401 try:
402 message = result["message"]
403 except KeyError:
404 message = result["error"]
405 EricMessageBox.warning(
406 self,
407 self.tr("Conda Search Package Error"),
408 message)
409
410 def __showDetails(self, item):
411 """
412 Private method to show a dialog with details about a package item.
413
414 @param item reference to the package item
415 @type QTreeWidgetItem
416 """
417 details = item.data(0, self.PackageDetailedDataRole)
418 if details:
419 from .CondaPackageDetailsWidget import CondaPackageDetailsDialog
420 dlg = CondaPackageDetailsDialog(details, self)
421 dlg.exec()
422
423 @pyqtSlot(str)
424 def on_searchEdit_textChanged(self, txt):
425 """
426 Private slot handling changes of the entered search specification.
427
428 @param txt current search entry
429 @type str
430 """
431 self.searchButton.setEnabled(bool(txt))
432
433 @pyqtSlot()
434 def on_searchEdit_returnPressed(self):
435 """
436 Private slot handling the user pressing the Return button in the
437 search edit.
438 """
439 self.__doSearch()
440
441 @pyqtSlot()
442 def on_searchButton_clicked(self):
443 """
444 Private slot handling the press of the search button.
445 """
446 self.__doSearch()
447
448 @pyqtSlot()
449 def on_installButton_clicked(self):
450 """
451 Private slot to install a selected package.
452 """
453 if len(self.searchResultList.selectedItems()) == 1:
454 item = self.searchResultList.selectedItems()[0]
455 if item.parent() is None:
456 # it is just the package item
457 package = item.text(0)
458 else:
459 # item with version and build
460 package = "{0}={1}={2}".format(
461 item.parent().text(0),
462 item.text(1),
463 item.text(2),
464 )
465
466 prefix = self.environmentsComboBox.itemData(
467 self.environmentsComboBox.currentIndex())
468 ok = self.__conda.installPackages([package], prefix=prefix)
469 if ok:
470 self.on_refreshButton_clicked()
471
472 @pyqtSlot()
473 def on_showDetailsButton_clicked(self):
474 """
475 Private slot handling the 'Show Details' button.
476 """
477 item = self.searchResultList.selectedItems()[0]
478 self.__showDetails(item)
479
480 @pyqtSlot()
481 def on_searchResultList_itemSelectionChanged(self):
482 """
483 Private slot handling a change of selected search results.
484 """
485 self.__updateSearchActionButtons()
486
487 @pyqtSlot(QTreeWidgetItem)
488 def on_searchResultList_itemExpanded(self, item):
489 """
490 Private slot handling the expansion of an item.
491
492 @param item reference to the expanded item
493 @type QTreeWidgetItem
494 """
495 for col in range(1, self.searchResultList.columnCount()):
496 self.searchResultList.resizeColumnToContents(col)
497
498 @pyqtSlot(QTreeWidgetItem, int)
499 def on_searchResultList_itemDoubleClicked(self, item, column):
500 """
501 Private slot handling a double click of an item.
502
503 @param item reference to the item that was double clicked
504 @type QTreeWidgetItem
505 @param column column of the double click
506 @type int
507 """
508 if item.parent() is not None:
509 self.__showDetails(item)
510
511 @pyqtSlot(bool)
512 def on_searchToggleButton_toggled(self, checked):
513 """
514 Private slot to togle the search widget.
515
516 @param checked state of the search widget button
517 @type bool
518 """
519 self.searchWidget.setVisible(checked)
520
521 if checked:
522 self.searchEdit.setFocus(Qt.FocusReason.OtherFocusReason)
523 self.searchEdit.selectAll()
524
525 self.__updateSearchActionButtons()
526
527 #######################################################################
528 ## Menu related methods below
529 #######################################################################
530
531 @pyqtSlot()
532 def __aboutToShowCondaMenu(self):
533 """
534 Private slot to handle the conda menu about to be shown.
535 """
536 selectedEnvironment = self.environmentsComboBox.currentText()
537 enable = selectedEnvironment not in [""]
538 for act in self.__envActs:
539 act.setEnabled(enable)
540
541 self.__deleteEnvAct.setEnabled(
542 selectedEnvironment not in ["", self.__conda.RootName])
543
544 @pyqtSlot()
545 def __aboutConda(self):
546 """
547 Private slot to show some information about the conda installation.
548 """
549 infoDict = self.__conda.getCondaInformation()
550
551 from .CondaInfoDialog import CondaInfoDialog
552 dlg = CondaInfoDialog(infoDict, self)
553 dlg.exec()
554
555 @pyqtSlot()
556 def __installPackages(self):
557 """
558 Private slot to install packages.
559 """
560 prefix = self.environmentsComboBox.itemData(
561 self.environmentsComboBox.currentIndex())
562 if prefix:
563 ok, packageSpecs = EricTextInputDialog.getText(
564 self,
565 self.tr("Install Packages"),
566 self.tr("Package Specifications (separated by whitespace):"),
567 QLineEdit.EchoMode.Normal,
568 minimumWidth=600)
569 if ok and packageSpecs.strip():
570 packages = [p.strip() for p in packageSpecs.split()]
571 ok = self.__conda.installPackages(packages, prefix=prefix)
572 if ok:
573 self.on_refreshButton_clicked()
574
575 @pyqtSlot()
576 def __installRequirements(self):
577 """
578 Private slot to install packages from requirements files.
579 """
580 prefix = self.environmentsComboBox.itemData(
581 self.environmentsComboBox.currentIndex())
582 if prefix:
583 requirements = EricFileDialog.getOpenFileNames(
584 self,
585 self.tr("Install Packages"),
586 "",
587 self.tr("Text Files (*.txt);;All Files (*)"))
588 if requirements:
589 args = []
590 for requirement in requirements:
591 args.extend(["--file", requirement])
592 ok = self.__conda.installPackages(args, prefix=prefix)
593 if ok:
594 self.on_refreshButton_clicked()
595
596 @pyqtSlot()
597 def __generateRequirements(self):
598 """
599 Private slot to generate a requirements file.
600 """
601 prefix = self.environmentsComboBox.itemData(
602 self.environmentsComboBox.currentIndex())
603 if prefix:
604 env = self.environmentsComboBox.currentText()
605
606 from .CondaExportDialog import CondaExportDialog
607
608 self.__requirementsDialog = CondaExportDialog(
609 self.__conda, env, prefix)
610 self.__requirementsDialog.show()
611 QApplication.processEvents()
612 self.__requirementsDialog.start()
613
614 @pyqtSlot()
615 def __cloneEnvironment(self):
616 """
617 Private slot to clone a conda environment.
618 """
619 from .CondaNewEnvironmentDataDialog import (
620 CondaNewEnvironmentDataDialog)
621
622 prefix = self.environmentsComboBox.itemData(
623 self.environmentsComboBox.currentIndex())
624 if prefix:
625 dlg = CondaNewEnvironmentDataDialog(self.tr("Clone Environment"),
626 False, self)
627 if dlg.exec() == QDialog.DialogCode.Accepted:
628 virtEnvName, envName, _ = dlg.getData()
629 args = [
630 "--name",
631 envName.strip(),
632 "--clone",
633 prefix,
634 ]
635 ok, prefix, interpreter = self.__conda.createCondaEnvironment(
636 args)
637 if ok:
638 ericApp().getObject("VirtualEnvManager").addVirtualEnv(
639 virtEnvName, prefix, interpreter, isConda=True)
640
641 @pyqtSlot()
642 def __createEnvironment(self):
643 """
644 Private slot to create a conda environment from a requirements file.
645 """
646 from .CondaNewEnvironmentDataDialog import (
647 CondaNewEnvironmentDataDialog)
648
649 dlg = CondaNewEnvironmentDataDialog(self.tr("Create Environment"),
650 True, self)
651 if dlg.exec() == QDialog.DialogCode.Accepted:
652 virtEnvName, envName, requirements = dlg.getData()
653 args = [
654 "--name",
655 envName.strip(),
656 "--file",
657 requirements,
658 ]
659 ok, prefix, interpreter = self.__conda.createCondaEnvironment(args)
660 if ok:
661 ericApp().getObject("VirtualEnvManager").addVirtualEnv(
662 virtEnvName, prefix, interpreter, isConda=True)
663
664 @pyqtSlot()
665 def __deleteEnvironment(self):
666 """
667 Private slot to delete a conda environment.
668 """
669 envName = self.environmentsComboBox.currentText()
670 ok = EricMessageBox.yesNo(
671 self,
672 self.tr("Delete Environment"),
673 self.tr("""<p>Shall the environment <b>{0}</b> really be"""
674 """ deleted?</p>""").format(envName)
675 )
676 if ok:
677 self.__conda.removeCondaEnvironment(name=envName)
678
679 @pyqtSlot()
680 def __editUserConfiguration(self):
681 """
682 Private slot to edit the user configuration.
683 """
684 from QScintilla.MiniEditor import MiniEditor
685
686 cfgFile = CondaInterface.userConfiguration()
687 if not cfgFile:
688 return
689
690 if not os.path.exists(cfgFile):
691 self.__conda.writeDefaultConfiguration()
692
693 # check, if the destination is writeable
694 if not os.access(cfgFile, os.W_OK):
695 EricMessageBox.critical(
696 None,
697 self.tr("Edit Configuration"),
698 self.tr("""The configuration file "{0}" does not exist"""
699 """ or is not writable.""").format(cfgFile))
700 return
701
702 self.__editor = MiniEditor(cfgFile, "YAML")
703 self.__editor.show()
704
705 @pyqtSlot()
706 def __condaConfigure(self):
707 """
708 Private slot to open the configuration page.
709 """
710 ericApp().getObject("UserInterface").showPreferences("condaPage")
711
712 @pyqtSlot()
713 def on_recheckButton_clicked(self):
714 """
715 Private slot to re-check the availability of conda and adjust the
716 interface if it became available.
717 """
718 if CondaInterface.isCondaAvailable():
719 self.__initCondaInterface()
720
721 self.notAvailableWidget.hide()
722 self.baseWidget.show()

eric ide

mercurial