Merge branch 'master' into master

This commit is contained in:
Salem Yaslem 2024-09-24 12:46:34 +03:00 committed by GitHub
commit 6549c0397a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

98
main.py
View File

@ -68,10 +68,12 @@ class DockerGUI(QMainWindow):
# Create tabs
self.containers_tab = QWidget()
self.images_tab = QWidget()
self.networks_tab = QWidget()
self.volumes_tab = QWidget()
self.tab_widget.addTab(self.containers_tab, "Containers")
self.tab_widget.addTab(self.images_tab, "Images")
self.tab_widget.addTab(self.networks_tab, "Networks")
self.tab_widget.addTab(self.volumes_tab, "Volumes")
@ -80,6 +82,7 @@ class DockerGUI(QMainWindow):
# Create tree widgets for each tab
self.containers_tree = self.create_tree_widget(["ID", "Name", "Image", "Status", "Ports"])
self.images_tree = self.create_tree_widget(["ID", "Repository", "Tag", "Size"])
self.networks_tree = self.create_tree_widget(["ID", "Name", "Driver"])
self.volumes_tree = self.create_tree_widget(["Name", "Driver", "Mountpoint"])
@ -87,6 +90,7 @@ class DockerGUI(QMainWindow):
# Add tree widgets to tabs
self.setup_tab(self.containers_tab, self.containers_tree, "Search containers...")
self.setup_tab(self.images_tab, self.images_tree, "Search images...")
self.setup_tab(self.networks_tab, self.networks_tree, "Search networks...")
self.setup_tab(self.volumes_tab, self.volumes_tree, "Search volumes...")
@ -108,11 +112,16 @@ class DockerGUI(QMainWindow):
tree.setContextMenuPolicy(Qt.CustomContextMenu)
tree.customContextMenuRequested.connect(self.show_context_menu)
tree.header().setSectionResizeMode(QHeaderView.Interactive)
tree.header().setSortIndicator(0, Qt.DescendingOrder)
tree.header().setSortIndicatorShown(True)
tree.header().sortIndicatorChanged.connect(lambda col, order: self.sort_tree_widget(tree, col, order))
return tree
def sort_tree_widget(self, tree, column, order):
tree.sortItems(column, order)
def setup_tab(self, tab, tree, search_placeholder):
layout = QVBoxLayout(tab)
# Add search bar
search_bar = QLineEdit()
search_bar.setPlaceholderText(search_placeholder)
@ -159,6 +168,15 @@ class DockerGUI(QMainWindow):
self.remove_action.triggered.connect(self.remove_container)
self.toolbar.addAction(self.remove_action)
# Image-specific actions
self.pull_image_action = QAction(QIcon.fromTheme("download"), "Pull Image", self)
self.pull_image_action.triggered.connect(self.pull_image)
self.toolbar.addAction(self.pull_image_action)
self.remove_image_action = QAction(QIcon.fromTheme("edit-delete"), "Remove Image", self)
self.remove_image_action.triggered.connect(self.remove_image)
self.toolbar.addAction(self.remove_image_action)
# Network-specific actions
self.create_network_action = QAction(QIcon.fromTheme("list-add"), "Create Network", self)
self.create_network_action.triggered.connect(self.create_network)
@ -192,6 +210,8 @@ class DockerGUI(QMainWindow):
self.create_volume_action.setVisible(False)
self.remove_volume_action.setVisible(False)
self.terminal_action.setVisible(False)
self.pull_image_action.setVisible(False)
self.remove_image_action.setVisible(False)
# Show actions based on the current tab
if index == 0: # Containers tab
@ -199,10 +219,13 @@ class DockerGUI(QMainWindow):
self.stop_action.setVisible(True)
self.remove_action.setVisible(True)
self.terminal_action.setVisible(True)
elif index == 1: # Networks tab
elif index == 1: # Images tab
self.pull_image_action.setVisible(True)
self.remove_image_action.setVisible(True)
elif index == 2: # Networks tab
self.create_network_action.setVisible(True)
self.remove_network_action.setVisible(True)
elif index == 2: # Volumes tab
elif index == 3: # Volumes tab
self.create_volume_action.setVisible(True)
self.remove_volume_action.setVisible(True)
@ -250,6 +273,13 @@ class DockerGUI(QMainWindow):
context_menu.addAction(start_action)
context_menu.addAction(stop_action)
context_menu.addAction(remove_action)
elif current_tab == self.images_tab:
pull_action = QAction("Pull", self)
pull_action.triggered.connect(self.pull_image)
remove_action = QAction("Remove", self)
remove_action.triggered.connect(self.remove_image)
context_menu.addAction(pull_action)
context_menu.addAction(remove_action)
elif current_tab == self.networks_tab:
remove_action = QAction("Remove", self)
remove_action.triggered.connect(lambda: self.handle_action("Remove"))
@ -261,8 +291,6 @@ class DockerGUI(QMainWindow):
context_menu.exec_(current_tab.mapToGlobal(position))
# Connect the triggered signal of the context menu actions to a handler
def setup_auto_refresh(self):
self.refresh_timer = QTimer(self)
self.refresh_timer.timeout.connect(self.refresh_data)
@ -305,6 +333,7 @@ class DockerGUI(QMainWindow):
def refresh_data(self):
self.refresh_containers()
self.refresh_images()
self.refresh_networks()
self.refresh_volumes()
@ -324,6 +353,7 @@ class DockerGUI(QMainWindow):
status_widget = StatusDelegate(status)
self.containers_tree.addTopLevelItem(item)
self.containers_tree.setItemWidget(item, 3, status_widget)
self.filter_tree(self.containers_tree, self.containers_tab.findChild(QLineEdit).text())
self.restore_selection(self.containers_tree, selected_items)
except subprocess.CalledProcessError as e:
print(f"Error refreshing containers: {e.output.decode()}")
@ -333,17 +363,41 @@ class DockerGUI(QMainWindow):
print(f"Unexpected error refreshing containers: {str(e)}")
QTimer.singleShot(0, lambda: self.containers_tree.verticalScrollBar().setValue(scroll_position))
def refresh_images(self):
scroll_position = self.images_tree.verticalScrollBar().value()
selected_items = self.get_selected_items(self.images_tree)
self.images_tree.clear()
try:
output = subprocess.check_output(["docker", "images", "--format", "{{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.Size}}"], stderr=subprocess.STDOUT)
if output.strip():
images = output.decode().strip().split("\n")
for image in images:
id, repository, tag, size = image.split("\t")
item = QTreeWidgetItem([id, repository, tag, size])
self.images_tree.addTopLevelItem(item)
self.sort_tree_widget(self.images_tree, 0, Qt.DescendingOrder)
self.filter_tree(self.images_tree, self.images_tab.findChild(QLineEdit).text())
self.restore_selection(self.images_tree, selected_items)
except subprocess.CalledProcessError as e:
print(f"Error refreshing images: {e.output.decode()}")
except Exception as e:
print(f"Unexpected error refreshing images: {str(e)}")
QTimer.singleShot(0, lambda: self.images_tree.verticalScrollBar().setValue(scroll_position))
def refresh_networks(self):
scroll_position = self.networks_tree.verticalScrollBar().value()
selected_items = self.get_selected_items(self.networks_tree)
self.networks_tree.clear()
try:
output = subprocess.check_output(["docker", "network", "ls", "--format", "{{.ID}}\\t{{.Name}}\\t{{.Driver}}"], stderr=subprocess.STDOUT)
if output.strip():
networks = output.decode().strip().split("\n")
for network in networks:
id, name, driver = network.split("\t")
item = QTreeWidgetItem([id, name, driver])
self.networks_tree.addTopLevelItem(item)
self.filter_tree(self.networks_tree, self.networks_tab.findChild(QLineEdit).text())
self.restore_selection(self.networks_tree, selected_items)
except subprocess.CalledProcessError as e:
print(f"Error refreshing networks: {e.output.decode()}")
@ -363,6 +417,7 @@ class DockerGUI(QMainWindow):
name, driver, mountpoint = volume.split("\t")
item = QTreeWidgetItem([name, driver, mountpoint])
self.volumes_tree.addTopLevelItem(item)
self.filter_tree(self.volumes_tree, self.volumes_tab.findChild(QLineEdit).text())
self.restore_selection(self.volumes_tree, selected_items)
except subprocess.CalledProcessError as e:
print(f"Error refreshing volumes: {e.output.decode()}")
@ -431,6 +486,39 @@ class DockerGUI(QMainWindow):
self.refresh_containers()
def pull_image(self):
image_name, ok = QInputDialog.getText(self, "Pull Image", "Enter image name (e.g., ubuntu:latest):")
if ok and image_name:
try:
subprocess.run(["docker", "pull", image_name], check=True)
print(f"Pulled image: {image_name}")
QMessageBox.information(self, "Success", f"Image '{image_name}' pulled successfully.")
except subprocess.CalledProcessError as e:
QMessageBox.critical(self, "Error", f"Failed to pull image: {e}")
self.refresh_images()
def remove_image(self):
selected_items = self.images_tree.selectedItems()
if not selected_items:
QMessageBox.warning(self, "No Selection", "Please select an image to remove.")
return
for item in selected_items:
image_id = item.text(0)
reply = QMessageBox.question(self, "Confirm Removal",
f"Are you sure you want to remove image {image_id}?",
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
if reply == QMessageBox.Yes:
try:
subprocess.run(["docker", "rmi", image_id], check=True)
print(f"Removed image: {image_id}")
QMessageBox.information(self, "Success", f"Image '{image_id}' removed successfully.")
except subprocess.CalledProcessError as e:
QMessageBox.critical(self, "Error", f"Failed to remove image {image_id}: {e}")
self.refresh_images()
def create_network(self):
network_name, ok = QInputDialog.getText(self, "Create Network", "Enter network name:")
if ok and network_name: