22 # You should have received a copy of the GNU General Public License |
22 # You should have received a copy of the GNU General Public License |
23 # along with this program; if not, write to the Free Software |
23 # along with this program; if not, write to the Free Software |
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
25 |
25 |
26 |
26 |
27 import socket |
|
28 import weakref |
|
29 import wx |
27 import wx |
30 import wx.lib.mixins.listctrl as listmix |
28 import wx.lib.mixins.listctrl as listmix |
31 from zeroconf import ServiceBrowser, Zeroconf, get_all_addresses |
29 from connectors.ZeroConfListener import ZeroConfListenerClass |
32 |
|
33 service_type = '_Beremiz._tcp.local.' |
|
34 |
|
35 class ZeroConfListenerClass: |
|
36 def __init__(self, dialog): |
|
37 self.dialog = weakref.ref(dialog) |
|
38 self.ZeroConfInstance = Zeroconf() |
|
39 self.Browser = ServiceBrowser(self.ZeroConfInstance, service_type, self) |
|
40 |
|
41 def clean(self): |
|
42 if self.Browser is not None: |
|
43 self.Browser.cancel() |
|
44 self.Browser = None |
|
45 if self.ZeroConfInstance is not None: |
|
46 self.ZeroConfInstance.close() |
|
47 self.ZeroConfInstance = None |
|
48 |
|
49 def __del__(self): |
|
50 self.clean() |
|
51 |
|
52 def update_service(self, zeroconf, _type, name): |
|
53 self.remove_service(zeroconf, _type, name) |
|
54 self.add_service(zeroconf, _type, name) |
|
55 |
|
56 def add_service(self, zeroconf, _type, name): |
|
57 info = self.ZeroConfInstance.get_service_info(_type, name) |
|
58 if info is None: |
|
59 return |
|
60 typename = info.properties.get(b"protocol", None).decode() |
|
61 ip = str(info.parsed_addresses()[0]) |
|
62 port = info.port |
|
63 |
|
64 wx.CallAfter(self._add_service, typename, ip, port, name) |
|
65 |
|
66 def _add_service(self, typename, ip, port, name): |
|
67 dialog = self.dialog() |
|
68 if not dialog: return |
|
69 dialog._addService(typename, ip, port, name) |
|
70 |
|
71 def remove_service(self, zeroconf, _type, name): |
|
72 wx.CallAfter(self._remove_service, name) |
|
73 |
|
74 def _remove_service(self, name): |
|
75 dialog = self.dialog() |
|
76 if not dialog: return |
|
77 dialog._removeService(name) |
|
78 |
30 |
79 class AutoWidthListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin): |
31 class AutoWidthListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin): |
80 def __init__(self, parent, name, pos=wx.DefaultPosition, |
32 def __init__(self, parent, name, pos=wx.DefaultPosition, |
81 size=wx.DefaultSize, style=0): |
33 size=wx.DefaultSize, style=0): |
82 wx.ListCtrl.__init__(self, parent, wx.ID_ANY, pos, size, style, name=name) |
34 wx.ListCtrl.__init__(self, parent, wx.ID_ANY, pos, size, style, name=name) |
83 listmix.ListCtrlAutoWidthMixin.__init__(self) |
35 listmix.ListCtrlAutoWidthMixin.__init__(self) |
84 |
36 |
85 |
|
86 class DiscoveryPanel(wx.Panel, listmix.ColumnSorterMixin): |
37 class DiscoveryPanel(wx.Panel, listmix.ColumnSorterMixin): |
87 |
38 |
88 def _init_coll_MainSizer_Items(self, parent): |
39 def _init_coll_MainSizer_Items(self, parent): |
89 parent.Add(self.staticText1, 0, border=20, flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.GROW) |
40 parent.Add(self.staticText1, 0, border=20, flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.GROW) |
90 parent.Add(self.ServicesList, 0, border=20, flag=wx.LEFT | wx.RIGHT | wx.GROW) |
41 parent.Add(self.ServicesList, 0, border=20, flag=wx.LEFT | wx.RIGHT | wx.GROW) |
116 def _init_list_ctrl(self): |
67 def _init_list_ctrl(self): |
117 # Set up list control |
68 # Set up list control |
118 self.ServicesList = AutoWidthListCtrl( |
69 self.ServicesList = AutoWidthListCtrl( |
119 name='ServicesList', parent=self, pos=wx.Point(0, 0), size=wx.Size(0, 0), |
70 name='ServicesList', parent=self, pos=wx.Point(0, 0), size=wx.Size(0, 0), |
120 style=wx.LC_REPORT | wx.LC_EDIT_LABELS | wx.LC_SORT_ASCENDING | wx.LC_SINGLE_SEL) |
71 style=wx.LC_REPORT | wx.LC_EDIT_LABELS | wx.LC_SORT_ASCENDING | wx.LC_SINGLE_SEL) |
121 self.ServicesList.InsertColumn(0, _('NAME')) |
72 for col, (label, width) in enumerate([ |
122 self.ServicesList.InsertColumn(1, _('TYPE')) |
73 (_('NAME'), 150), (_('TYPE'), 150), (_('IP'), 150), (_('PORT'), 150)]): |
123 self.ServicesList.InsertColumn(2, _('IP')) |
74 self.ServicesList.InsertColumn(col, label) |
124 self.ServicesList.InsertColumn(3, _('PORT')) |
|
125 self.ServicesList.SetColumnWidth(0, 150) |
|
126 self.ServicesList.SetColumnWidth(1, 150) |
|
127 self.ServicesList.SetColumnWidth(2, 150) |
|
128 self.ServicesList.SetColumnWidth(3, 150) |
|
129 self.ServicesList.SetInitialSize(wx.Size(-1, 300)) |
75 self.ServicesList.SetInitialSize(wx.Size(-1, 300)) |
|
76 |
130 self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, self.ServicesList) |
77 self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, self.ServicesList) |
131 self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, self.ServicesList) |
78 self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, self.ServicesList) |
132 |
79 |
133 def _init_ctrls(self, prnt): |
80 def _init_ctrls(self, prnt): |
134 self.staticText1 = wx.StaticText( |
81 self.staticText1 = wx.StaticText( |
154 self._init_list_ctrl() |
101 self._init_list_ctrl() |
155 listmix.ColumnSorterMixin.__init__(self, 4) |
102 listmix.ColumnSorterMixin.__init__(self, 4) |
156 |
103 |
157 self._init_ctrls(parent) |
104 self._init_ctrls(parent) |
158 |
105 |
|
106 self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy) |
|
107 |
159 self.itemDataMap = {} |
108 self.itemDataMap = {} |
160 self.nextItemId = 0 |
109 self.nextItemId = 0 |
161 |
110 |
162 self.URI = None |
111 self.URI = None |
163 |
112 |
|
113 self.LatestSelection = None |
|
114 |
|
115 self.ZeroConfListener = None |
|
116 |
164 self.RefreshList() |
117 self.RefreshList() |
165 self.LatestSelection = None |
118 |
166 |
|
167 self.IfacesMonitorState = None |
|
168 self.IfacesMonitorTimer = wx.Timer(self) |
|
169 self.IfacesMonitorTimer.Start(2000) |
|
170 self.ZeroConfListener = None |
|
171 self.Bind(wx.EVT_TIMER, self.IfacesMonitor, self.IfacesMonitorTimer) |
|
172 |
119 |
173 def _cleanup(self): |
120 def _cleanup(self): |
174 if self.IfacesMonitorTimer is not None: |
|
175 self.IfacesMonitorTimer.Stop() |
|
176 self.IfacesMonitorTimer = None |
|
177 if self.ZeroConfListener is not None: |
121 if self.ZeroConfListener is not None: |
178 self.ZeroConfListener.clean() |
122 self.ZeroConfListener.stop() |
179 self.ZeroConfListener = None |
123 self.ZeroConfListener = None |
180 |
124 |
181 def __del__(self): |
125 def OnDestroy(self, event): |
182 self._cleanup() |
126 self._cleanup() |
183 |
|
184 def Destroy(self): |
|
185 self._cleanup() |
|
186 wx.Panel.Destroy(self) |
|
187 |
|
188 def IfacesMonitor(self, event): |
|
189 NewState = get_all_addresses() |
|
190 |
|
191 if self.IfacesMonitorState != NewState: |
|
192 if self.IfacesMonitorState is not None: |
|
193 # refresh only if a new address appeared |
|
194 for addr in NewState: |
|
195 if addr not in self.IfacesMonitorState: |
|
196 self.RefreshList() |
|
197 break |
|
198 self.IfacesMonitorState = NewState |
|
199 event.Skip() |
127 event.Skip() |
200 |
128 |
201 def RefreshList(self): |
129 def RefreshList(self): |
202 self.ServicesList.DeleteAllItems() |
130 self.ServicesList.DeleteAllItems() |
203 self.ZeroConfListener = ZeroConfListenerClass(self) |
131 self.ZeroConfListener = ZeroConfListenerClass(self) |