Changed the DockerInterface to use a dictionary for containers and added a get_container_by_id method. Added a popup menu and reorganized actions elements in the UI. Also added some logic to the activation and deactivation of ui elements.

This commit is contained in:
José Carlos Cuevas 2014-07-11 12:25:14 +02:00
parent f2ca1b36c6
commit 165f079c61
4 changed files with 264 additions and 76 deletions

View file

@ -2,6 +2,54 @@
<!-- Generated with glade 3.16.1 -->
<interface>
<requires lib="gtk+" version="3.10"/>
<object class="GtkActionGroup" id="ContainerActionGroup">
<child>
<object class="GtkAction" id="start_action">
<property name="label" translatable="yes">Start</property>
<property name="short_label" translatable="yes">Start</property>
<property name="tooltip" translatable="yes">Start a selected stopped container.</property>
<property name="stock_id">gtk-media-play</property>
<property name="always_show_image">True</property>
<signal name="activate" handler="on_start_action_activate" swapped="no"/>
</object>
</child>
<child>
<object class="GtkAction" id="stop_action">
<property name="label" translatable="yes">Stop</property>
<property name="short_label" translatable="yes">Stop</property>
<property name="stock_id">gtk-media-stop</property>
<property name="always_show_image">True</property>
<signal name="activate" handler="on_stop_action_activate" swapped="no"/>
</object>
</child>
<child>
<object class="GtkAction" id="attach_action">
<property name="label" translatable="yes">Attach</property>
<property name="short_label" translatable="yes">Attach</property>
<property name="stock_id">gtk-jump-to</property>
<property name="always_show_image">True</property>
<signal name="activate" handler="on_attach_action_activate" swapped="no"/>
</object>
</child>
<child>
<object class="GtkAction" id="log_action">
<property name="label" translatable="yes">Log</property>
<property name="short_label" translatable="yes">Log</property>
<property name="stock_id">gtk-file</property>
<property name="always_show_image">True</property>
<signal name="activate" handler="on_log_action_activate" swapped="no"/>
</object>
</child>
<child>
<object class="GtkAction" id="delete_container_action">
<property name="label" translatable="yes">Delete</property>
<property name="short_label" translatable="yes">Delete</property>
<property name="tooltip" translatable="yes">Delete the selected container.</property>
<property name="stock_id">gtk-delete</property>
<property name="always_show_image">True</property>
</object>
</child>
</object>
<object class="GtkListStore" id="ContainerList">
<columns>
<!-- column-name Name -->
@ -12,6 +60,8 @@
<column type="gchararray"/>
<!-- column-name Status -->
<column type="gchararray"/>
<!-- column-name FullID -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkImage" id="FileStockImage">
@ -29,6 +79,35 @@
<property name="can_focus">False</property>
<property name="stock">gtk-jump-to</property>
</object>
<object class="GtkActionGroup" id="ImagesActionGroup">
<child>
<object class="GtkAction" id="build_action">
<property name="label" translatable="yes">Build</property>
<property name="short_label" translatable="yes">Build</property>
<property name="stock_id">gtk-properties</property>
<property name="always_show_image">True</property>
<signal name="activate" handler="on_build_action_activate" swapped="no"/>
</object>
</child>
<child>
<object class="GtkAction" id="runimage_action">
<property name="label" translatable="yes">Run</property>
<property name="short_label" translatable="yes">Run</property>
<property name="stock_id">gtk-execute</property>
<property name="always_show_image">True</property>
<signal name="activate" handler="on_runimage_action_activate" swapped="no"/>
</object>
</child>
<child>
<object class="GtkAction" id="remove_image_action">
<property name="label" translatable="yes">Remove</property>
<property name="short_label" translatable="yes">Remove</property>
<property name="stock_id">gtk-delete</property>
<property name="always_show_image">True</property>
<signal name="activate" handler="on_remove_image_action_activate" swapped="no"/>
</object>
</child>
</object>
<object class="GtkListStore" id="ImagesList">
<columns>
<!-- column-name Repository -->
@ -48,20 +127,6 @@
<property name="can_focus">False</property>
<property name="stock">gtk-media-play</property>
</object>
<object class="GtkAction" id="attach_action">
<property name="label" translatable="yes">Attach</property>
<property name="short_label" translatable="yes">Attach</property>
<property name="stock_id">gtk-jump-to</property>
<property name="always_show_image">True</property>
<signal name="activate" handler="on_attach_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="build_action">
<property name="label" translatable="yes">Build</property>
<property name="short_label" translatable="yes">Build</property>
<property name="stock_id">gtk-properties</property>
<property name="always_show_image">True</property>
<signal name="activate" handler="on_build_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="connect_action">
<property name="label" translatable="yes">Connect</property>
<property name="short_label" translatable="yes">Connect</property>
@ -69,13 +134,6 @@
<property name="always_show_image">True</property>
<signal name="activate" handler="on_connect_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="log_action">
<property name="label" translatable="yes">Log</property>
<property name="short_label" translatable="yes">Log</property>
<property name="stock_id">gtk-file</property>
<property name="always_show_image">True</property>
<signal name="activate" handler="on_log_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="preferences_action">
<property name="label" translatable="yes">Preferences</property>
<property name="short_label" translatable="yes">Preferences</property>
@ -91,35 +149,6 @@
<property name="always_show_image">True</property>
<signal name="activate" handler="on_refresh_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="remove_image_action">
<property name="label" translatable="yes">Remove</property>
<property name="short_label" translatable="yes">Remove</property>
<property name="stock_id">gtk-delete</property>
<property name="always_show_image">True</property>
<signal name="activate" handler="on_remove_image_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="runimage_action">
<property name="label" translatable="yes">Run</property>
<property name="short_label" translatable="yes">Run</property>
<property name="stock_id">gtk-execute</property>
<property name="always_show_image">True</property>
<signal name="activate" handler="on_runimage_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="start_action">
<property name="label" translatable="yes">Start</property>
<property name="short_label" translatable="yes">Start</property>
<property name="tooltip" translatable="yes">Start a selected stopped container.</property>
<property name="stock_id">gtk-media-play</property>
<property name="always_show_image">True</property>
<signal name="activate" handler="on_start_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="stop_action">
<property name="label" translatable="yes">Stop</property>
<property name="short_label" translatable="yes">Stop</property>
<property name="stock_id">gtk-media-stop</property>
<property name="always_show_image">True</property>
<signal name="activate" handler="on_stop_action_activate" swapped="no"/>
</object>
<object class="GtkWindow" id="MainWindow">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Stevedore</property>
@ -285,6 +314,35 @@
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkSeparator" id="separator1">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkButton" id="DeleteContainerButton">
<property name="label">gtk-delete</property>
<property name="use_action_appearance">True</property>
<property name="related_action">delete_container_action</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<property name="yalign">0.56999999284744263</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
@ -306,8 +364,11 @@
<property name="search_column">0</property>
<property name="show_expanders">False</property>
<property name="enable_grid_lines">both</property>
<signal name="button-press-event" handler="on_ContainerListView_button_press_event" swapped="no"/>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection1"/>
<object class="GtkTreeSelection" id="container_view_selection">
<signal name="changed" handler="on_container_view_selection_changed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="ContainerName">
@ -414,23 +475,6 @@
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="RemoveImageButton">
<property name="label">gtk-delete</property>
<property name="use_action_appearance">True</property>
<property name="related_action">remove_image_action</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="RunImageButton">
<property name="label">gtk-execute</property>
@ -442,12 +486,40 @@
<property name="use_stock">True</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkSeparator" id="separator2">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="RemoveImageButton">
<property name="label">gtk-delete</property>
<property name="use_action_appearance">True</property>
<property name="related_action">remove_image_action</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>

18
res/ui_builder.xml Normal file
View file

@ -0,0 +1,18 @@
<ui>
<popup name='ContainerStoppedPopup'>
<menuitem action='start_action' />
<menuitem action='log_action' />
<separator />
<menuitem action='delete_container_action' />
</popup>
<popup name='ContainerStartedPopup'>
<menuitem action='stop_action' />
<menuitem action='attach_action' />
<menuitem action='log_action' />
</popup>
<popup name='ImagePopup'>
<menuitem action='build_action' />
<menuitem action='runimage_action' />
<menuitem action='remove_image_action' />
</popup>
</ui>

View file

@ -60,7 +60,7 @@ class DockerContainer(object):
self.command = command
self.created = created
self.names = self.filter_names(names)
self.status = "Unknown"
self.status = DockerContainer.UNKNOWN
self.time_elapsed = 0
self.ports = []
@ -200,7 +200,7 @@ class DockerInterface(object):
"""
Ask our docker server the containers created and their status
"""
self.containers = []
self.containers = dict()
if self.client is not None:
containers = self.client.containers(all = True)
for container in containers:
@ -211,11 +211,21 @@ class DockerInterface(object):
names = container['Names'])
container_object.status_from_string(container['Status'])
self.containers.append(container_object)
self.containers[container['Id']] = container_object
else:
raise DockerNotConnectedException()
def get_container_by_id(self, container_id):
"""
Returns the container that matched container_id
"""
if self.client is None:
raise DockerNotConnectedException()
return self.containers.get(container_id, None)
def build(self, path, tag):
"""
Creates a new docker image from a Dockerfile in the specified path

View file

@ -18,7 +18,7 @@
# Generated with glade2py script
# glade2py script can be found at hocr web site http://hocr.berlios.de
from gi.repository import Gtk, Gio
from gi.repository import Gtk, Gdk, Gio
from pkg_resources import resource_string
from docker_iface import DockerInterface, DockerImage, DockerContainer, DockerNotConnectedException
@ -35,19 +35,31 @@ class MainWindow(Gtk.Application):
# create widget tree ...
self.builder = Gtk.Builder()
self.ui_manager = Gtk.UIManager()
try:
self.builder.add_from_file(resource_string(__name__, 'stevedore.glade'))
self.ui_manager.add_ui_from_file(resource_string(__name__, 'ui_builder.xml'))
except:
self.builder.add_from_file('../res/stevedore.glade')
self.ui_manager.add_ui_from_file('../res/ui_builder.xml')
# connect handlers
self.builder.connect_signals(self)
self.ui_manager.insert_action_group(self.builder.get_object('ContainerActionGroup'))
self.ui_manager.insert_action_group(self.builder.get_object('ImagesActionGroup'))
# Prepare certain widgets
self.status_bar = self.builder.get_object('AppStatus')
self.status_context_id = self.status_bar.get_context_id('main_app')
self.builder.get_object('StartButton').set_sensitive(False)
self.builder.get_object('StopButton').set_sensitive(False)
self.builder.get_object('AttachButton').set_sensitive(False)
self.builder.get_object('LogButton').set_sensitive(False)
self.builder.get_object('DeleteContainerButton').set_sensitive(False)
# Add window to the App and show it
self.window = self.builder.get_object('MainWindow')
self.status_bar = self.builder.get_object('AppStatus')
self.status_context_id = self.status_bar.get_context_id('main_app')
self.add_window(self.window)
self.window.show()
self.set_app_status(u'Not connected to Docker server.')
@ -74,12 +86,14 @@ class MainWindow(Gtk.Application):
image_list.clear()
# And now we populate them back
for container in self.docker.containers:
for container_index in self.docker.containers:
container = self.docker.get_container_by_id(container_index)
status = container.return_status_string()
container_list.append(row = [container.names[0],
unicode(container.image),
unicode(container.container_id)[:12],
status])
status,
unicode(container.container_id)])
for image in self.docker.images:
size_text = self.size_to_human(image.size)
@ -214,6 +228,80 @@ class MainWindow(Gtk.Application):
"""
print "Not implemented: on_runimage_action_activate"
def on_ContainerListView_button_press_event(self, obj, event = None):
"""
This hook checks the mouse interaction with the ContainerListView
"""
if event is not None:
if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3:
container_view = self.builder.get_object('ContainerListView')
selection = container_view.get_selection()
model, selected = selection.get_selected()
# selection is a treeiter
if selected is not None:
selected_container_id = model[selected][4]
container = self.docker.get_container_by_id(selected_container_id)
if container is not None:
if container.status == DockerContainer.EXITED:
popup = self.ui_manager.get_widget('/ContainerStoppedPopup')
elif container.status == DockerContainer.UP:
popup = self.ui_manager.get_widget('/ContainerStartedPopup')
else:
popup = self.ui_manager.get_widget('/ContainerStoppedPopup')
popup.popup(None, None, None, None, event.button, event.time)
return True
def on_container_view_selection_changed(self, selection):
"""
We control which element is selected on the ContainerListView to enable and disable
certain button actions
"""
start_button = self.builder.get_object('StartButton')
stop_button = self.builder.get_object('StopButton')
attach_button = self.builder.get_object('AttachButton')
log_button = self.builder.get_object('LogButton')
delete_container_button = self.builder.get_object('DeleteContainerButton')
model, treeiter = selection.get_selected()
if treeiter is not None:
container_id = model[treeiter][4]
container = self.docker.get_container_by_id(container_id)
if container.status == DockerContainer.UP:
start_button.set_sensitive(False)
stop_button.set_sensitive(True)
attach_button.set_sensitive(True)
log_button.set_sensitive(True)
delete_container_button.set_sensitive(False)
elif container.status == DockerContainer.EXITED:
start_button.set_sensitive(True)
stop_button.set_sensitive(False)
attach_button.set_sensitive(False)
log_button.set_sensitive(True)
delete_container_button.set_sensitive(True)
else:
start_button.set_sensitive(False)
stop_button.set_sensitive(False)
attach_button.set_sensitive(False)
log_button.set_sensitive(True)
delete_container_button.set_sensitive(True)
else:
start_button.set_sensitive(False)
stop_button.set_sensitive(False)
attach_button.set_sensitive(False)
log_button.set_sensitive(False)
delete_container_button.set_sensitive(False)
# run main loop
def main():