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() |
|