controls/DiscoveryPanel.py
changeset 4039 9ff455817691
parent 3935 bc51b5a2daa9
equal deleted inserted replaced
4038:4b47b4ce0f12 4039:9ff455817691
    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)
   244             #     svcname = self.getColumnText(self.LatestSelection, 0)
   172             #     svcname = self.getColumnText(self.LatestSelection, 0)
   245             #     connect_type = self.getColumnText(self.LatestSelection, 1)
   173             #     connect_type = self.getColumnText(self.LatestSelection, 1)
   246             #     return str("MDNS://%s" % svcname)
   174             #     return str("MDNS://%s" % svcname)
   247         return None
   175         return None
   248 
   176 
       
   177     def removeService(self, name):
       
   178         wx.CallAfter(self._removeService, name)
       
   179 
   249     def _removeService(self, name):
   180     def _removeService(self, name):
   250         '''
   181         '''
   251         called when a service with the desired type goes offline.
   182         called when a service with the desired type goes offline.
   252         '''
   183         '''
   253 
   184 
   261 
   192 
   262             if item_name == name:
   193             if item_name == name:
   263                 self.ServicesList.DeleteItem(idx)
   194                 self.ServicesList.DeleteItem(idx)
   264                 break
   195                 break
   265 
   196 
       
   197     def addService(self, typename, ip, port, name):
       
   198         wx.CallAfter(self._addService, typename, ip, port, name)
   266 
   199 
   267     def _addService(self, typename, ip, port, name):
   200     def _addService(self, typename, ip, port, name):
   268         '''
   201         '''
   269         called when a service with the desired type is discovered.
   202         called when a service with the desired type is discovered.
   270         '''
   203         '''