Commit 39eddf69 authored by Michael Weinrich's avatar Michael Weinrich
Browse files

first basic version of graphical user interface

parent a5440103
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
"""
Base class for the GUI.
# Copyright 2007, Michael Weinrich <testsuite@michael-weinrich.de>
@license: Licensed under the MIT license
http://opensource.org/licenses/mit-license.php
@author: Michael Weinrich <testsuite@michael-weinrich.de>
@copyright: Copyright 2007, Michael Weinrich
"""
import time
import sys
import os
from twisted.internet import reactor, task
import gtk
import gobject
import gtk, gtk.glade, gobject
from upntest.base import UPnT
import upntest
import louie
import time
import sys
class UPnTGUI:
UPnT = None
"""Class variable for a single instance of UPnT."""
def delete_event(self, widget, event, data=None):
return False
def __init__(self, config):
"""
Create the window and setup the underlying UPnT instance.
"""
# Try to locate the glade file
gladefile = "upntest.glade"
tryfiles = [gladefile, os.path.join(os.path.dirname(sys.argv[0]), gladefile), config.get('glade_dir', '') + gladefile]
for file in tryfiles:
if os.access(file, os.F_OK):
gladefile = file
break
self.windowname = "windowMain"
self.wTree = gtk.glade.XML(gladefile, self.windowname)
self.window = self.wTree.get_widget(self.windowname)
dic = {
"onDestroy" : self.shutdown,
"onExecuteTest": self.executeTests,
"onToolButtonQuit": self.shutdown,
"onActivateDeviceRow": self.showDetails
}
self.wTree.signal_autoconnect(dic)
# TreView for the devices
self.device_icon = gtk.gdk.pixbuf_new_from_file_at_size('/home/micxer/development/UPnT/gnome-fs-client.svg', 24, 24)
self.service_icon = gtk.gdk.pixbuf_new_from_file_at_size('/home/micxer/development/UPnT/icon_webservice.gif', 24, 24)
self.getWidget("treeviewDeviceTree").set_headers_visible(False)
imgrenderer = gtk.CellRendererPixbuf()
textrenderer = gtk.CellRendererText()
textrenderer.set_property('xalign', 0.0)
column = gtk.TreeViewColumn()
column.pack_start(imgrenderer, False)
column.pack_start(textrenderer, True)
column.set_attributes(imgrenderer, pixbuf=0)
column.set_attributes(textrenderer, text=1)
self.getWidget("treeviewDeviceTree").append_column(column)
# TreeModel for device tree
self.deviceTreeModel = gtk.TreeStore(gtk.gdk.Pixbuf,
str,
object)
self.getWidget("treeviewDeviceTree").set_model(self.deviceTreeModel)
"""Device var that holds the device that is tested when the Execute
Tests button is hit"""
self.deviceToTest = None
def quitApplication(self, widget, data=None):
self.window.destroy()
def destroy(self, widget, data=None):
reactor.stop()
#gtk.main_quit()
reactor.crash()
del self.UPnT
# TreeView for information about selected device
textrenderer = gtk.CellRendererText()
column = gtk.TreeViewColumn()
column.pack_start(textrenderer, True)
column.set_attributes(textrenderer, text=0)
column.set_title('Name')
column.set_min_width(150)
textrenderer = gtk.CellRendererText()
self.getWidget("treeviewDetails").append_column(column)
column = gtk.TreeViewColumn()
column.pack_start(textrenderer, True)
column.set_attributes(textrenderer, text=1)
column.set_title('Value')
self.getWidget("treeviewDetails").append_column(column)
# ListStore for the device and service details
model = gtk.ListStore(str, str)
self.getWidget("treeviewDetails").set_model(model)
def updateUpnpDevices(self, ip):
model = self.upnp_devices_treeview.get_model()
# buffer of the textview at the bottom showing all messages
self.messageTextBuffer = self.getWidget('textviewMessages').get_buffer()
# we are in GUI mode
config['GUI'] = True
# get all the signals
louie.connect(self.updateMessageBox, 'UPnT.infoMessage', louie.Any, weak=False)
louie.connect(self.updateUpnpDevices, 'UPnT.host_discovery_end', louie.Any, weak=False)
# initialize UPnT
self.UPnT = UPnT(config)
def getWidget(self, name):
return self.wTree.get_widget(name)
def run(self):
self.window.show()
gobject.idle_add(self.refresh)
gtk.main()
def shutdown(self, widget=None):
reactor.stop()
gtk.main_quit()
def updateUpnpDevices(self, root_device):
"""
Update the device tree after inserting a new device or service node.
"""
print 'Updating tree...'
model = self.getWidget("treeviewDeviceTree").get_model()
iter = model.get_iter_first()
current_parent = None
while iter is not None:
if model.get_value(iter, 1) == ip:
cur_dev = model.get_value(iter, 2)
if cur_dev._host.ip == ip:
current_parent = iter
break
iter = model.iter_next(iter)
if current_parent == None:
current_parent = model.append(None, [self.device_icon, ip])
current_parent = model.append(None, [self.device_icon, root_device._host.ip, root_device.host])
else:
children_iter = model.iter_children(current_parent)
result = children_iter is not None
while result:
result = model.remove(children_iter)
host = self.UPnT._knownHosts[ip]
for uuid, device in host._devices.items():
device_iter = model.append(current_parent, [self.device_icon, device.deviceType + ':' + device.deviceVersion])
for uuid, service in device._services.items():
model.append(device_iter, [self.service_icon, service.serviceType + ':' + service.serviceVersion])
for uuid, device in root_device.host._serverDevices.items():
device_iter = model.append(current_parent, [self.device_icon, device.deviceType + ':' + device.deviceVersion, device])
self.addSubDevicesToTree(model, device_iter, device)
self.addServicesToTree(model, device_iter, device)
def addSubDevicesToTree(self, model, device_iter, device):
"""
Add all subdevices and their services to the tree
"""
for uuid, subdevice in device.subDevices.items():
subdevice_iter = model.append(device_iter, [self.device_icon, subdevice.deviceType + ':' + subdevice.deviceVersion, subdevice])
self.addSubDevicesToTree(model, subdevice_iter, subdevice)
self.addServicesToTree(model, subdevice_iter, subdevice)
def runControlTest(self, widget, data=None):
print 'muh'
def addServicesToTree(self, model, device_iter, device):
"""
Add all services of a device to the tree
"""
for uuid, service in device._services.items():
model.append(device_iter, [self.service_icon, service.serviceType + ':' + service.serviceVersion, service])
def executeTests(self, widget, data=None):
"""
Run the control test on a device (not implemented yet).
"""
if self.deviceToTest is not None:
self.deviceToTest.checkDescriptions(self._serverDevices, self._services)
def updateMessageBox(self, message):
"""
Add the received message to the text box.
"""
self.messageTextBuffer.insert(self.messageTextBuffer.get_end_iter(), message + '\n')
self.refreshUI()
def showDetails(self, treeview):
"""
Show details of a device or service in the TreeView on the right
"""
selected_treemodel, selected_iter = treeview.get_selection().get_selected()
model = self.getWidget("treeviewDetails").get_model()
model.clear()
object = selected_treemodel.get_value(selected_iter, 2)
if isinstance(object, upntest.device.ServerDevice):
device = object
self.deviceToTest = device
model.append(['Device Type', device.deviceType + ':' + device.deviceVersion])
model.append(['Description URL', device.location])
elif isinstance(object, upntest.service.Service):
service = object
model.append(['Service Type', service.serviceType + ':' + service.serviceVersion])
model.append(['scpdURL', service.scpdUrl])
model.append(['controlURL', service.controlUrl])
model.append(['eventSubURL', service.eventSubUrl])
def refreshUI(self):
"""
Refresh the UI to show changes of the controls to the user.
"""
while gtk.events_pending():
gtk.main_iteration()
#reactor.callLater(1, self.refreshUI)
def __init__(self, config):
# create window
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_title("UPnP Test Suite")
self.window.set_size_request(780, 460)
self.window.set_border_width(0)
self.window.connect("delete_event", self.delete_event)
self.window.connect("destroy", self.destroy)
# create the TreeView using TreeStore
model = gtk.TreeStore(
gtk.gdk.Pixbuf,
str)
self.upnp_devices_treeview = gtk.TreeView(model)
self.upnp_devices_treeview.set_size_request(250, 260)
self.upnp_devices_treeview.set_headers_visible(False)
pixbufrenderer = gtk.CellRendererPixbuf()
textrenderer = gtk.CellRendererText()
textrenderer.set_property('xalign', 0.0)
column = gtk.TreeViewColumn()
column.pack_start(pixbufrenderer, False)
column.pack_start(textrenderer, True)
column.set_attributes(pixbufrenderer, pixbuf=0)
column.set_attributes(textrenderer, text=1)
self.upnp_devices_treeview.append_column(column)
self.device_icon = gtk.gdk.pixbuf_new_from_file_at_size('/home/micxer/development/UPnT/gnome-fs-client.svg', 24, 24)
self.service_icon = gtk.gdk.pixbuf_new_from_file_at_size('/home/micxer/development/UPnT/icon_webservice.gif', 24, 24)
self.hbuttonbox = gtk.HButtonBox()
self.hbuttonbox.set_size_request(510, 30)
self.hbuttonbox
#self.hbuttonbox.set_layout(gtk.BUTTONBOX_START)
self.hbuttonbox.set_spacing(4)
self.button = gtk.Button("Test Control")
self.button.connect_object("clicked", self.runControlTest, None)
self.hbuttonbox.pack_start(self.button)
self.button = gtk.Button(stock=gtk.STOCK_QUIT)
self.button.connect("clicked", self.destroy)
self.hbuttonbox.pack_end(self.button)
# TextView for information about selected device
self.device_info_scrolledwindow = gtk.ScrolledWindow()
self.device_info_scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self.device_info_scrolledwindow.set_border_width(1)
self.device_info_scrolledwindow.set_size_request(510, 240)
self.device_info_textview = gtk.TextView()
self.device_info_textview.set_editable(False)
self.messageTextBuffer = self.device_info_textview.get_buffer()
self.device_info_scrolledwindow.add(self.device_info_textview)
# TextView for messages from the suite
self.message_scrolledwindow = gtk.ScrolledWindow()
self.message_scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self.message_scrolledwindow.set_border_width(1)
self.message_scrolledwindow.set_size_request(760, 200)
self.message_textview = gtk.TextView()
self.message_textview.set_editable(False)
self.messageTextBuffer = self.message_textview.get_buffer()
self.message_scrolledwindow.add(self.message_textview)
# Boxes fot the upper elements
self.hpane = gtk.HPaned()
self.hpane.handle_size = 0
self.hpane.add1(self.upnp_devices_treeview)
self.vbox = gtk.VBox(False, 1)
self.hpane.add2(self.vbox)
self.vbox.pack_start(self.device_info_scrolledwindow)
self.vbox.pack_start(self.hbuttonbox)
# pack all the stuff together
self.vpane = gtk.VPaned()
self.vpane.handle_size = 0
self.vpane.add1(self.hpane)
self.vpane.add2(self.message_scrolledwindow)
self.window.add(self.vpane)
self.window.show_all()
# we are in GUI mode
config['GUI'] = True
# initialize UPnT
self.UPnT = UPnT(config)
louie.connect(self.updateMessageBox, 'UPnT.infoMessage', louie.Any, weak=False)
louie.connect(self.updateUpnpDevices, 'UPnT.host_discovery_end', louie.Any, weak=False)
\ No newline at end of file
......@@ -88,6 +88,9 @@ class UPnT(log.Loggable):
if ip == self._coherence.hostname:
return
#if ip == '192.168.82.1':
# return
if not self._knownHosts.has_key(ip):
self._knownHosts[ip] = Host(ip, self._config, self._coherence)
self.info('Host added', ip)
......@@ -96,10 +99,7 @@ class UPnT(log.Loggable):
self._knownHosts[ip].dispatchDiscoveryPacketData(data)
#except Exception, error:
# self.warning(error)
# GUI update signal
louie.send('UPnT.host_discovery_end', None, ip)
def hostRemoved(self, ip):
"""
Remove host from the known hosts list.
......
......@@ -274,21 +274,26 @@ class Host(log.Loggable):
be shown.
@param level: The indentation level of the current call.
"""
if level == 1:
print
if level > 1:
print (level-1)*' ' + '+-D: ' + device.deviceType + ':' + \
device.deviceVersion
else:
print level*' ' + 'D: ' + device.deviceType + ':' + \
device.deviceVersion
#self.debug(device.services)
for _, service in device.services.items():
print level*' ' + '+-S: ' + service.serviceType + ':' + \
service.serviceVersion
#self.debug(device.subDevices)
for _, sub_device in device.subDevices.items():
self.showDeviceTree(result, sub_device, level + 1)
if not self._config.get('GUI', False):
if level == 1:
print
if level > 1:
print (level-1)*' ' + '+-D: ' + device.deviceType + ':' + \
device.deviceVersion
else:
print level*' ' + 'D: ' + device.deviceType + ':' + \
device.deviceVersion
#self.debug(device.services)
for _, service in device.services.items():
print level*' ' + '+-S: ' + service.serviceType + ':' + \
service.serviceVersion
#self.debug(device.subDevices)
for _, sub_device in device.subDevices.items():
self.showDeviceTree(result, sub_device, level + 1)
# GUI update signal
print 'Sending GUI message'
louie.send('UPnT.host_discovery_end', None, device)
def createAndCheckDeviceTree(self):
"""
......
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# Copyright 2007 - Frank Scholz <coherence@beebits.net>
# taken from Coherence source an modified by Michael Weinrich according to the
# needs of UPnT
""" SOAP-lite
some simple functions to implement the SOAP msgs
needed by UPnP with ElementTree
"""
taken from Coherence source an modified by Michael Weinrich according to the
needs of UPnT
inspired by ElementSOAP.py
@license: Licensed under the MIT license
http://opensource.org/licenses/mit-license.php
@author: Michael Weinrich <testsuite@michael-weinrich.de>
@copyright: Copyright 2007 - Frank Scholz <coherence@beebits.net>
"""
from coherence.extern.et import ET
......
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
"""
taken from Coherence source an modified by Michael Weinrich according to the
needs of UPnT
# Copyright 2007 - Frank Scholz <coherence@beebits.net>
# taken from Coherence source an modified by Michael Weinrich according to the
# needs of UPnT
@license: Licensed under the MIT license
http://opensource.org/licenses/mit-license.php
@author: Frank Scholz <coherence@beebits.net>
@copyright: Copyright 2007 - Frank Scholz <coherence@beebits.net>
"""
from coherence.extern.et import ET, namespace_map_update
from lxml import etree
......@@ -29,6 +32,15 @@ class SOAPProxy(log.Loggable):
"""
def __init__(self, url, namespace=None, envelope_attrib=None, header=None, soapaction=None):
"""
Initialise the proxy.
@param url:
@param namespace:
@param envelope_attrib:
@param header:
@param soapaction:
"""
self.url = url
self.namespace = namespace
self.header = header
......@@ -40,6 +52,9 @@ class SOAPProxy(log.Loggable):
"""
Invoke action soapmethod on the remote SOAP server with in_args as
arguments.
@param soapmethod:
@param in_args:
"""
soapaction = self.soapaction or soapmethod
......@@ -65,6 +80,11 @@ class SOAPProxy(log.Loggable):
).addCallbacks(self._cbGotResult, gotError, None, None, [self.url], None)
def _cbGotResult(self, result):
"""
Process the returned result data..
@param result:
"""
#print "_cbGotResult 1", result
page, headers = result
#result = SOAPpy.parseSOAPRPC(page)
......@@ -102,6 +122,11 @@ class SOAPProxy(log.Loggable):
return result
def decode_result(self, element):
"""
Decode the returned result element.
@param element:
"""
type = element.get('{http://www.w3.org/1999/XMLSchema-instance}type')
if type is not None:
try:
......@@ -121,6 +146,11 @@ class SOAPProxy(log.Loggable):
return element.text or ""
def decodeFaultMessage(self, body):
"""
Decode the returned fault message in a human readable format.
@param body:
"""
response = body.find('{http://schemas.xmlsoap.org/soap/envelope/}Fault')
#print 'response: %r' % response
result = {}
......
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.2.2 on Thu Oct 11 22:52:47 2007 by micxer@khitomer-->
<glade-interface>
<widget class="GtkWindow" id="windowMain">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="default_width">1000</property>
<property name="default_height">720</property>
<property name="gravity">GDK_GRAVITY_CENTER</property>
<signal name="destroy" handler="onDestroy"/>
<child>
<widget class="GtkVBox" id="vboxMain">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkToolbar" id="toolbarMain">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="toolbar_style">GTK_TOOLBAR_BOTH_HORIZ</property>
<child>
<widget class="GtkToolButton" id="toolbuttonExecute">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="is_important">True</property>
<property name="label" translatable="yes" comments="Starts tests for selected device">Execute tests</property>
<property name="use_underline">True</property>
<property name="stock_id">gtk-execute</property>
<signal name="clicked" handler="onExecuteTest"/>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
<widget class="GtkToolButton" id="toolbuttonQuit">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="is_important">True</property>
<property name="stock_id">gtk-quit</property>
<signal name="clicked" handler="onToolButtonQuit"/>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
<widget class="GtkVPaned" id="vpanedMain">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="position">180</property>
<child>
<widget class="GtkHPaned" id="hpanedTop">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="position">250</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindowDeviceTree">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<child>
<widget class="GtkTreeView" id="treeviewDeviceTree">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="headers_clickable">True</property>
<signal name="cursor_changed" handler="onActivateDeviceRow"/>
</widget>
</child>
</widget>
<packing>
<property name="resize">False</property>
<property name="shrink">True</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindowDetails">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<child>
<widget class="GtkTreeView" id="treeviewDetails">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="headers_clickable">True</property>
</widget>
</child>
</widget>
<packing>
<property name="resize">True</property>
<property name="shrink">True</property>
</packing>
</child>
</widget>
<packing>
<property name="resize">False</property>
<property name="shrink">True</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindowMessages">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>