PipxInterface/PipxWidget.py

changeset 31
17e37d4ebe42
parent 23
4c18addf12b2
child 32
b7a3ae7519ba
--- a/PipxInterface/PipxWidget.py	Sat Jul 27 17:39:00 2024 +0200
+++ b/PipxInterface/PipxWidget.py	Sat Jul 27 19:39:32 2024 +0200
@@ -9,6 +9,8 @@
 
 import os
 
+import psutil
+
 from PyQt6.QtCore import Qt, pyqtSlot
 from PyQt6.QtWidgets import QDialog, QMenu, QTreeWidgetItem, QWidget
 
@@ -339,6 +341,7 @@
                 latestVersion = self.__pipx.checkPackageOutdated(package)
                 if latestVersion is not None:
                     self.__markPackageOutdated(itm, latestVersion)
+                # TODO: check outdated dependencies (configurable)
         self.__resizePackagesColumns()
 
     @pyqtSlot()
@@ -346,17 +349,42 @@
         """
         Private slot to upgrade the selected package.
         """
-        package = self.__selectedPackages()[0]
-        self.__pipx.upgradePackage(package)
-        self.on_refreshButton_clicked()
+        packageItem = self.__selectedPackageItems()[0]
+        runningApps = self.__getRunningApps(self.__packageApps(packageItem))
+        if runningApps:
+            EricMessageBox.warning(
+                self,
+                self.tr("Upgrade Selected Package"),
+                self.tr(
+                    "<p>The selected package cannot be upgraded because some of its"
+                    " apps are running.</p><ul><li>{0}</li></ul><p>Stop these apps"
+                    " and try again.</p>"
+                ).format("</li><li>".join(runningApps)),
+            )
+        else:
+            package = packageItem.text(PipxWidget.PackageColumn)
+            self.__pipx.upgradePackage(package)
+            self.on_refreshButton_clicked()
 
     @pyqtSlot()
     def __upgradeAllPackages(self):
         """
         Private slot to upgrade all packages.
         """
-        self.__pipx.upgradeAllPackages()
-        self.on_refreshButton_clicked()
+        runningApps = self.__getAllRunningApps()
+        if runningApps:
+            EricMessageBox.warning(
+                self,
+                self.tr("Upgrade All Packages"),
+                self.tr(
+                    "<p>The packages cannot be upgraded because some of their apps are"
+                    " running.</p><ul><li>{0}</li></ul><p>Stop these apps and try"
+                    " again.</p>"
+                ).format("</li><li>".join(runningApps)),
+            )
+        else:
+            self.__pipx.upgradeAllPackages()
+            self.on_refreshButton_clicked()
 
     @pyqtSlot()
     def __upgradeSharedLibs(self):
@@ -459,6 +487,7 @@
                 version, latestVersion
             ),
         )
+        item.setIcon(PipxWidget.VersionColumn, EricPixmapCache.getIcon("upgrade"))
 
     def __populatePackages(self):
         """
@@ -548,7 +577,7 @@
         Private method to determine the list of selected packages.
 
         @return list of selected packages
-        @rtype list of QTreeWidgetItem
+        @rtype list of str
         """
         packages = []
 
@@ -558,3 +587,67 @@
                 packages.append(itm.text(PipxWidget.PackageColumn))
 
         return packages
+
+    def __selectedPackageItems(self):
+        """
+        Private method to determine the list of selected package items.
+
+        @return list of selected package items
+        @rtype list of QTreeWidgetItem
+        """
+        packageItems = []
+
+        for row in range(self.packagesList.topLevelItemCount()):
+            itm = self.packagesList.topLevelItem(row)
+            if itm.isSelected():
+                packageItems.append(itm)
+
+        return packageItems
+
+    def __packageApps(self, packageItem):
+        """
+        Private method to determine the apps belonging to a package item.
+
+        @param packageItem reference to the package item
+        @type QTreeWidgetItem
+        @return list of app names
+        @rtype list of str
+        """
+        apps = []
+        
+        for row in range(packageItem.childCount()):
+            apps.append(packageItem.child(row).text(0))
+        
+        return apps
+
+    def __getRunningApps(self, apps):
+        """
+        Private method to determine, which app of the given list of apps is running.
+
+        @param apps list of apps to check
+        @type str
+        @return set of running apps
+        @rtype set of str
+        """
+        runningApps = set()
+        
+        for proc in psutil.process_iter(["name"]):
+            if proc.info["name"] in apps:
+                runningApps.add(proc.info["name"])
+
+        return runningApps
+
+    def __getAllRunningApps(self):
+        """
+        Private method to determine all running pipx managed apps.
+        
+        @return set of running apps
+        @rtype set of str
+        """
+        allApps = []
+
+        for topRow in range(self.packagesList.topLevelItemCount()):
+            topItm = self.packagesList.topLevelItem(topRow)
+            allApps.extend(self.__packageApps(topItm))
+
+        return self.__getRunningApps(allApps)

eric ide

mercurial