discovery.py
changeset 392 6617d3fb43e2
parent 379 71c441104cac
child 399 77e23bf04c33
equal deleted inserted replaced
389:bde723abfdfc 392:6617d3fb43e2
    25 from Zeroconf import *
    25 from Zeroconf import *
    26 import socket
    26 import socket
    27 import  wx.lib.mixins.listctrl  as  listmix
    27 import  wx.lib.mixins.listctrl  as  listmix
    28 
    28 
    29 class AutoWidthListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
    29 class AutoWidthListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
    30     def __init__(self, parent, ID, pos=wx.DefaultPosition,
    30     def __init__(self, parent, id, name, pos=wx.DefaultPosition,
    31                  size=wx.DefaultSize, style=0):
    31                  size=wx.DefaultSize, style=0):
    32         wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
    32         wx.ListCtrl.__init__(self, parent, id, pos, size, style, name=name)
    33         listmix.ListCtrlAutoWidthMixin.__init__(self)
    33         listmix.ListCtrlAutoWidthMixin.__init__(self)
    34 
    34 
       
    35 [ID_DISCOVERYDIALOG, ID_DISCOVERYDIALOGSTATICTEXT1, 
       
    36  ID_DISCOVERYDIALOGSERVICESLIST, ID_DISCOVERYDIALOGREFRESHBUTTON, 
       
    37  ID_DISCOVERYDIALOGLOCALBUTTON, 
       
    38 ] = [wx.NewId() for _init_ctrls in range(5)]
       
    39 
    35 class DiscoveryDialog(wx.Dialog, listmix.ColumnSorterMixin):
    40 class DiscoveryDialog(wx.Dialog, listmix.ColumnSorterMixin):
    36     def __init__(self, parent, id=-1, title=_('Service Discovery')):
    41     
    37         self.my_result=None
    42     def _init_coll_MainSizer_Items(self, parent):
    38         wx.Dialog.__init__(self, parent, id, title, size=(600,600), style=wx.DEFAULT_DIALOG_STYLE)
    43         parent.AddWindow(self.staticText1, 0, border=20, flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW)
    39 
    44         parent.AddWindow(self.ServicesList, 0, border=20, flag=wx.LEFT|wx.RIGHT|wx.GROW)
    40         # set up dialog sizer
    45         parent.AddSizer(self.ButtonGridSizer, 0, border=20, flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.GROW)
    41 
    46         
    42         sizer = wx.FlexGridSizer(2, 1, 2, 2)  # rows, cols, vgap, hgap
    47     def _init_coll_MainSizer_Growables(self, parent):
    43         sizer.AddGrowableRow(0)
    48         parent.AddGrowableCol(0)
    44         sizer.AddGrowableCol(0)
    49         parent.AddGrowableRow(1)
    45 
    50     
    46         # set up list control
    51     def _init_coll_ButtonGridSizer_Items(self, parent):
    47 
    52         parent.AddWindow(self.RefreshButton, 0, border=0, flag=0)
    48         self.list = AutoWidthListCtrl(self, -1,
    53         parent.AddWindow(self.LocalButton, 0, border=0, flag=0)
    49                                       #pos=(50,20), 
    54         parent.AddSizer(self.ButtonSizer, 0, border=0, flag=0)
    50                                       #size=(500,300),
    55         
    51                                       style=wx.LC_REPORT 
    56     def _init_coll_ButtonGridSizer_Growables(self, parent):
    52                                      | wx.LC_EDIT_LABELS
    57         parent.AddGrowableCol(0)
    53                                      | wx.LC_SORT_ASCENDING
    58         parent.AddGrowableCol(1)
    54                                      | wx.LC_SINGLE_SEL 
    59         parent.AddGrowableRow(1)
    55                                      )
    60     
    56         sizer.Add(self.list, 1, wx.EXPAND)
    61     def _init_sizers(self):
    57 
    62         self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10)
    58         btsizer = wx.FlexGridSizer(1, 6, 2, 2)  # rows, cols, vgap, hgap
    63         self.ButtonGridSizer = wx.FlexGridSizer(cols=3, hgap=5, rows=1, vgap=0)
    59         
    64         
    60         sizer.Add(btsizer, 1, wx.EXPAND)
    65         self._init_coll_MainSizer_Items(self.MainSizer)
    61 
    66         self._init_coll_MainSizer_Growables(self.MainSizer)
    62         self.PopulateList()
    67         self._init_coll_ButtonGridSizer_Items(self.ButtonGridSizer)
    63 
    68         self._init_coll_ButtonGridSizer_Growables(self.ButtonGridSizer)
    64         self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, self.list)
    69         
    65         self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, self.list)
    70         self.SetSizer(self.MainSizer)
    66 
    71     
    67         # set up buttons
    72     def _init_ctrls(self, prnt):
    68 
    73         wx.Dialog.__init__(self, id=ID_DISCOVERYDIALOG, 
    69         local_id = wx.NewId()
    74               name='DiscoveryDialog', parent=prnt,  
    70         b = wx.Button(self, local_id, _("Refresh"))
    75               size=wx.Size(600, 600), style=wx.DEFAULT_DIALOG_STYLE,
    71         self.Bind(wx.EVT_BUTTON, self.OnRefreshButton, b)
    76               title='Service Discovery')
    72         btsizer.Add(b)
    77         
    73 
    78         self.staticText1 = wx.StaticText(id=ID_DISCOVERYDIALOGSTATICTEXT1,
    74         btsizer.AddSpacer(0)
    79               label=_('Services available:'), name='staticText1', parent=self,
    75         btsizer.AddGrowableCol(1)
    80               pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
    76 
    81         
    77         local_id = wx.NewId()
    82         # Set up list control
    78         b = wx.Button(self, local_id, _("Local"))
    83         self.ServicesList = AutoWidthListCtrl(id=ID_DISCOVERYDIALOGSERVICESLIST,
    79         self.Bind(wx.EVT_BUTTON, self.ChooseLocalID, b)
    84               name='ServicesList', parent=self, pos=wx.Point(0, 0), size=wx.Size(0, 0), 
    80         btsizer.Add(b)
    85               style=wx.LC_REPORT|wx.LC_EDIT_LABELS|wx.LC_SORT_ASCENDING|wx.LC_SINGLE_SEL)
    81 
    86         self.ServicesList.InsertColumn(0, 'NAME')
    82         btsizer.AddSpacer(0)
    87         self.ServicesList.InsertColumn(1, 'TYPE')
    83         btsizer.AddGrowableCol(3)
    88         self.ServicesList.InsertColumn(2, 'IP')
    84 
    89         self.ServicesList.InsertColumn(3, 'PORT')
    85         b = wx.Button(self, wx.ID_CANCEL, _("Cancel"))
    90         self.ServicesList.SetColumnWidth(0, 150)
    86         self.Bind(wx.EVT_BUTTON, self.OnCancel, b)
    91         self.ServicesList.SetColumnWidth(1, 150)
    87         btsizer.Add(b)
    92         self.ServicesList.SetColumnWidth(2, 150)
    88 
    93         self.ServicesList.SetColumnWidth(3, 150)
    89         b = wx.Button(self, wx.ID_OK, _("OK"))
    94         self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, id=ID_DISCOVERYDIALOGSERVICESLIST)
    90         self.Bind(wx.EVT_BUTTON, self.OnOk, b)
    95         self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, id=ID_DISCOVERYDIALOGSERVICESLIST)
    91         b.SetDefault()
    96         
    92         btsizer.Add(b)
       
    93 
       
    94         self.SetSizer(sizer)
       
    95 
       
    96         listmix.ColumnSorterMixin.__init__(self, 4)
    97         listmix.ColumnSorterMixin.__init__(self, 4)
    97 
    98         
    98         # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
    99         self.RefreshButton = wx.Button(id=ID_DISCOVERYDIALOGREFRESHBUTTON,
       
   100               label=_('Refresh'), name='RefreshButton', parent=self,
       
   101               pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
       
   102         self.Bind(wx.EVT_BUTTON, self.OnRefreshButton, id=ID_DISCOVERYDIALOGREFRESHBUTTON)
       
   103         
       
   104         self.LocalButton = wx.Button(id=ID_DISCOVERYDIALOGLOCALBUTTON,
       
   105               label=_('Local'), name='LocalButton', parent=self,
       
   106               pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
       
   107         self.Bind(wx.EVT_BUTTON, self.OnLocalButton, id=ID_DISCOVERYDIALOGLOCALBUTTON)
       
   108         
       
   109         self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTER)
       
   110         
       
   111         self._init_sizers()
       
   112         
       
   113     def __init__(self, parent):
       
   114         self._init_ctrls(parent)
       
   115         
    99         self.itemDataMap = {}
   116         self.itemDataMap = {}
   100 
       
   101         # a counter used to assign a unique id to each listctrl item
       
   102         self.nextItemId = 0
   117         self.nextItemId = 0
   103 
   118         
   104         self.browser = None
   119         self.URI = None
   105         self.zConfInstance = Zeroconf()
   120         self.Browser = None
       
   121         
       
   122         self.ZeroConfInstance = Zeroconf()
   106         self.RefreshList()
   123         self.RefreshList()
   107 
   124         
   108         self.Bind(wx.EVT_CLOSE, self.OnClose)
   125     def __del__(self):
   109 
   126         self.Browser.cancel()
       
   127         self.ZeroConfInstance.close()
       
   128         
   110     def RefreshList(self):
   129     def RefreshList(self):
   111         type = "_PYRO._tcp.local."
   130         self.Browser = ServiceBrowser(self.ZeroConfInstance, "_PYRO._tcp.local.", self)        
   112         self.browser = ServiceBrowser(self.zConfInstance, type, self)        
       
   113 
   131 
   114     def OnRefreshButton(self, event):
   132     def OnRefreshButton(self, event):
   115         self.list.DeleteAllItems()
   133         self.ServicesList.DeleteAllItems()
   116         self.browser.cancel()
   134         self.Browser.cancel()
   117         self.RefreshList()
   135         self.RefreshList()
   118 
   136 
   119     def OnClose(self, event):
   137     def OnLocalButton(self, event):
   120         self.zConfInstance.close()
   138         self.URI = "LOCAL://"
   121         event.Skip()
   139         self.EndModal(wx.ID_OK)
   122 
       
   123     def OnCancel(self, event):
       
   124         self.zConfInstance.close()
       
   125         event.Skip()
       
   126 
       
   127     def OnOk(self, event):
       
   128         self.zConfInstance.close()
       
   129         event.Skip()
   140         event.Skip()
   130 
   141 
   131     # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
   142     # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
   132     def GetListCtrl(self):
   143     def GetListCtrl(self):
   133         return self.list
   144         return self.ServicesList
   134 
       
   135     def PopulateList(self):
       
   136         self.list.InsertColumn(0, 'NAME')
       
   137         self.list.InsertColumn(1, 'TYPE')
       
   138         self.list.InsertColumn(2, 'IP')
       
   139         self.list.InsertColumn(3, 'PORT')
       
   140         self.list.SetColumnWidth(0, 150)
       
   141         self.list.SetColumnWidth(1, 150)
       
   142         self.list.SetColumnWidth(2, 150)
       
   143         self.list.SetColumnWidth(3, 150)
       
   144 
   145 
   145     def getColumnText(self, index, col):
   146     def getColumnText(self, index, col):
   146         item = self.list.GetItem(index, col)
   147         item = self.ServicesList.GetItem(index, col)
   147         return item.GetText()
   148         return item.GetText()
   148 
   149 
   149     def OnItemSelected(self, event):
   150     def OnItemSelected(self, event):
   150         self.currentItem = event.m_itemIndex
   151         self.SetURI(event.m_itemIndex)
   151         self.setresult()
       
   152         event.Skip()
   152         event.Skip()
   153 
   153 
   154     def OnItemActivated(self, event):
   154     def OnItemActivated(self, event):
   155         self.currentItem = event.m_itemIndex
   155         self.SetURI(event.m_itemIndex)
   156         self.setresult()
   156         self.EndModal(wx.ID_OK)
   157         self.Close()
       
   158         event.Skip()
   157         event.Skip()
   159 
   158 
   160     def setresult(self):
   159     def SetURI(self, idx):
   161         connect_type = self.getColumnText(self.currentItem, 1)
   160         connect_type = self.getColumnText(idx, 1)
   162         connect_address = self.getColumnText(self.currentItem, 2)
   161         connect_address = self.getColumnText(idx, 2)
   163         connect_port = self.getColumnText(self.currentItem, 3)
   162         connect_port = self.getColumnText(idx, 3)
   164         
   163         
   165         uri = self.CreateURI(connect_type, connect_address, connect_port)
   164         self.URI = "%s://%s:%s"%(connect_type, connect_address, connect_port)
   166         self.my_result=uri
   165 
   167 
   166     def GetURI(self):
   168     def GetResult(self):
   167         return self.URI
   169         return self.my_result
       
   170         
   168         
   171     def removeService(self, zeroconf, type, name):
   169     def removeService(self, zeroconf, type, name):
   172         '''
   170         '''
   173         called when a service with the desired type goes offline.
   171         called when a service with the desired type goes offline.
   174         '''
   172         '''
   175 
   173         
   176         # loop through the list items looking for the service that went offline
   174         # loop through the list items looking for the service that went offline
   177         for idx in xrange(self.list.GetItemCount()):
   175         for idx in xrange(self.ServicesList.GetItemCount()):
   178             # this is the unique identifier assigned to the item
   176             # this is the unique identifier assigned to the item
   179             item_id = self.list.GetItemData(idx)
   177             item_id = self.ServicesList.GetItemData(idx)
   180 
   178 
   181             # this is the full typename that was received by addService
   179             # this is the full typename that was received by addService
   182             item_name = self.itemDataMap[item_id][4]
   180             item_name = self.itemDataMap[item_id][4]
   183 
   181 
   184             if item_name == name:
   182             if item_name == name:
   185                 self.list.DeleteItem(idx)
   183                 self.ServicesList.DeleteItem(idx)
   186                 break
   184                 break
   187 
   185         
   188     def addService(self, zeroconf, type, name):
   186     def addService(self, zeroconf, type, name):
   189         '''
   187         '''
   190         called when a service with the desired type is discovered.
   188         called when a service with the desired type is discovered.
   191         '''
   189         '''
   192 
   190         info = self.ZeroConfInstance.getServiceInfo(type, name)
   193         info = self.zConfInstance.getServiceInfo(type, name)
       
   194 
   191 
   195         svcname  = name.split(".")[0]
   192         svcname  = name.split(".")[0]
   196         typename = type.split(".")[0][1:]
   193         typename = type.split(".")[0][1:]
   197         ip       = str(socket.inet_ntoa(info.getAddress()))
   194         ip       = str(socket.inet_ntoa(info.getAddress()))
   198         port     = info.getPort()
   195         port     = info.getPort()
   199 
   196 
   200         num_items = self.list.GetItemCount()
   197         num_items = self.ServicesList.GetItemCount()
   201 
   198 
   202         # display the new data in the list
   199         # display the new data in the list
   203         new_item = self.list.InsertStringItem(num_items, svcname)
   200         new_item = self.ServicesList.InsertStringItem(num_items, svcname)
   204         self.list.SetStringItem(new_item, 1, "%s" % typename)
   201         self.ServicesList.SetStringItem(new_item, 1, "%s" % typename)
   205         self.list.SetStringItem(new_item, 2, "%s" % ip)
   202         self.ServicesList.SetStringItem(new_item, 2, "%s" % ip)
   206         self.list.SetStringItem(new_item, 3, "%s" % port)
   203         self.ServicesList.SetStringItem(new_item, 3, "%s" % port)
   207 
   204 
   208         # record the new data for the ColumnSorterMixin
   205         # record the new data for the ColumnSorterMixin
   209         # we assign every list item a unique id (that won't change when items
   206         # we assign every list item a unique id (that won't change when items
   210         # are added or removed)
   207         # are added or removed)
   211         self.list.SetItemData(new_item, self.nextItemId)
   208         self.ServicesList.SetItemData(new_item, self.nextItemId)
   212  
   209  
   213         # the value of each column has to be stored in the itemDataMap
   210         # the value of each column has to be stored in the itemDataMap
   214         # so that ColumnSorterMixin knows how to sort the column.
   211         # so that ColumnSorterMixin knows how to sort the column.
   215 
   212 
   216         # "name" is included at the end so that self.removeService
   213         # "name" is included at the end so that self.removeService
   217         # can access it.
   214         # can access it.
   218         self.itemDataMap[self.nextItemId] = [ svcname, typename, ip, port, name ]
   215         self.itemDataMap[self.nextItemId] = [ svcname, typename, ip, port, name ]
   219 
   216 
   220         self.nextItemId += 1
   217         self.nextItemId += 1
   221 
   218         
   222     def CreateURI(self, connect_type, connect_address, connect_port):
       
   223         uri = "%s://%s:%s"%(connect_type, connect_address, connect_port)
       
   224         return uri
       
   225 
       
   226     def ChooseLocalID(self, event):
       
   227         self.my_result = "LOCAL://"
       
   228         self.Close()