etisserant@203: # -*- coding: utf-8 -*- etisserant@203: etisserant@203: #This file is part of Beremiz, a Integrated Development Environment for etisserant@203: #programming IEC 61131-3 automates supporting plcopen standard and CanFestival. etisserant@203: # etisserant@203: #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD etisserant@203: # etisserant@203: #See COPYING file for copyrights details. etisserant@203: # etisserant@203: #This library is free software; you can redistribute it and/or etisserant@203: #modify it under the terms of the GNU General Public etisserant@203: #License as published by the Free Software Foundation; either etisserant@203: #version 2.1 of the License, or (at your option) any later version. etisserant@203: # etisserant@203: #This library is distributed in the hope that it will be useful, etisserant@203: #but WITHOUT ANY WARRANTY; without even the implied warranty of etisserant@203: #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU etisserant@203: #General Public License for more details. etisserant@203: # etisserant@203: #You should have received a copy of the GNU General Public etisserant@203: #License along with this library; if not, write to the Free Software etisserant@203: #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA etisserant@203: etisserant@203: import wx btaylor@357: from Zeroconf import * etisserant@203: import socket etisserant@203: import wx.lib.mixins.listctrl as listmix etisserant@203: b@375: class AutoWidthListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin): etisserant@203: def __init__(self, parent, ID, pos=wx.DefaultPosition, etisserant@203: size=wx.DefaultSize, style=0): etisserant@203: wx.ListCtrl.__init__(self, parent, ID, pos, size, style) etisserant@203: listmix.ListCtrlAutoWidthMixin.__init__(self) etisserant@203: etisserant@203: class DiscoveryDialog(wx.Dialog, listmix.ColumnSorterMixin): laurent@361: def __init__(self, parent, id=-1, title=_('Service Discovery')): etisserant@203: self.my_result=None etisserant@203: wx.Dialog.__init__(self, parent, id, title, size=(600,600), style=wx.DEFAULT_DIALOG_STYLE) etisserant@203: b@375: # set up dialog sizer b@375: etisserant@221: sizer = wx.FlexGridSizer(2, 1, 2, 2) # rows, cols, vgap, hgap etisserant@221: sizer.AddGrowableRow(0) etisserant@221: sizer.AddGrowableCol(0) etisserant@221: b@375: # set up list control b@375: b@375: self.list = AutoWidthListCtrl(self, -1, b@375: #pos=(50,20), b@375: #size=(500,300), b@375: style=wx.LC_REPORT b@375: | wx.LC_EDIT_LABELS b@375: | wx.LC_SORT_ASCENDING b@375: | wx.LC_SINGLE_SEL b@375: ) etisserant@221: sizer.Add(self.list, 1, wx.EXPAND) etisserant@221: etisserant@221: btsizer = wx.FlexGridSizer(1, 6, 2, 2) # rows, cols, vgap, hgap etisserant@221: etisserant@221: sizer.Add(btsizer, 1, wx.EXPAND) etisserant@221: etisserant@203: self.PopulateList() etisserant@203: etisserant@203: self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, self.list) etisserant@203: self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, self.list) etisserant@203: b@375: # set up buttons b@375: etisserant@221: local_id = wx.NewId() laurent@361: b = wx.Button(self, local_id, _("Refresh")) etisserant@221: self.Bind(wx.EVT_BUTTON, self.OnRefreshButton, b) etisserant@221: btsizer.Add(b) etisserant@203: etisserant@221: btsizer.AddSpacer(0) etisserant@221: btsizer.AddGrowableCol(1) etisserant@203: etisserant@221: local_id = wx.NewId() laurent@361: b = wx.Button(self, local_id, _("Local")) etisserant@221: self.Bind(wx.EVT_BUTTON, self.ChooseLocalID, b) etisserant@221: btsizer.Add(b) etisserant@221: etisserant@221: btsizer.AddSpacer(0) etisserant@221: btsizer.AddGrowableCol(3) etisserant@221: laurent@361: b = wx.Button(self, wx.ID_CANCEL, _("Cancel")) btaylor@357: self.Bind(wx.EVT_BUTTON, self.OnCancel, b) etisserant@221: btsizer.Add(b) etisserant@221: laurent@361: b = wx.Button(self, wx.ID_OK, _("OK")) btaylor@357: self.Bind(wx.EVT_BUTTON, self.OnOk, b) etisserant@221: b.SetDefault() etisserant@221: btsizer.Add(b) etisserant@221: etisserant@221: self.SetSizer(sizer) etisserant@203: etisserant@203: listmix.ColumnSorterMixin.__init__(self, 4) etisserant@221: b@375: # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py b@375: self.itemDataMap = {} b@375: b@375: # a counter used to assign a unique id to each listctrl item b@375: self.nextItemId = 0 b@375: greg@262: self.browser = None etisserant@221: self.zConfInstance = Zeroconf() etisserant@221: self.RefreshList() etisserant@221: btaylor@357: self.Bind(wx.EVT_CLOSE, self.OnClose) btaylor@357: etisserant@221: def RefreshList(self): b@377: type = "_PYRO._tcp.local." greg@262: self.browser = ServiceBrowser(self.zConfInstance, type, self) etisserant@221: etisserant@221: def OnRefreshButton(self, event): etisserant@221: self.list.DeleteAllItems() greg@262: self.browser.cancel() etisserant@221: self.RefreshList() etisserant@221: btaylor@357: def OnClose(self, event): btaylor@357: self.zConfInstance.close() btaylor@357: event.Skip() btaylor@357: btaylor@357: def OnCancel(self, event): btaylor@357: self.zConfInstance.close() btaylor@357: event.Skip() btaylor@357: btaylor@357: def OnOk(self, event): btaylor@357: self.zConfInstance.close() btaylor@357: event.Skip() btaylor@357: etisserant@203: # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py etisserant@203: def GetListCtrl(self): etisserant@203: return self.list etisserant@203: etisserant@203: def PopulateList(self): etisserant@221: self.list.InsertColumn(0, 'NAME') etisserant@203: self.list.InsertColumn(1, 'TYPE') etisserant@203: self.list.InsertColumn(2, 'IP') etisserant@203: self.list.InsertColumn(3, 'PORT') etisserant@203: self.list.SetColumnWidth(0, 150) etisserant@203: self.list.SetColumnWidth(1, 150) etisserant@203: self.list.SetColumnWidth(2, 150) etisserant@203: self.list.SetColumnWidth(3, 150) etisserant@203: etisserant@203: def getColumnText(self, index, col): etisserant@203: item = self.list.GetItem(index, col) etisserant@203: return item.GetText() etisserant@203: etisserant@203: def OnItemSelected(self, event): etisserant@203: self.currentItem = event.m_itemIndex etisserant@221: self.setresult() etisserant@203: event.Skip() etisserant@203: etisserant@203: def OnItemActivated(self, event): etisserant@203: self.currentItem = event.m_itemIndex etisserant@221: self.setresult() etisserant@221: self.Close() etisserant@203: event.Skip() etisserant@203: etisserant@221: def setresult(self): etisserant@203: connect_type = self.getColumnText(self.currentItem, 1) etisserant@203: connect_address = self.getColumnText(self.currentItem, 2) etisserant@203: connect_port = self.getColumnText(self.currentItem, 3) etisserant@203: etisserant@203: uri = self.CreateURI(connect_type, connect_address, connect_port) etisserant@203: self.my_result=uri etisserant@203: etisserant@203: def GetResult(self): etisserant@203: return self.my_result etisserant@203: btaylor@357: def removeService(self, zeroconf, type, name): b@375: ''' b@375: called when a service with the desired type goes offline. b@375: ''' b@375: b@375: # loop through the list items looking for the service that went offline b@375: for idx in xrange(self.list.GetItemCount()): b@375: # this is the unique identifier assigned to the item b@375: item_id = self.list.GetItemData(idx) b@375: b@375: # this is the full typename that was received by addService b@375: item_name = self.itemDataMap[item_id][4] b@375: b@375: if item_name == name: b@375: self.list.DeleteItem(idx) b@375: break btaylor@357: greg@262: def addService(self, zeroconf, type, name): b@375: ''' b@375: called when a service with the desired type is discovered. b@375: ''' b@375: btaylor@357: info = self.zConfInstance.getServiceInfo(type, name) b@374: b@374: svcname = name.split(".")[0] etisserant@203: typename = type.split(".")[0][1:] b@374: ip = str(socket.inet_ntoa(info.getAddress())) b@374: port = info.getPort() b@374: etisserant@203: num_items = self.list.GetItemCount() b@375: b@375: # display the new data in the list b@374: new_item = self.list.InsertStringItem(num_items, svcname) b@374: self.list.SetStringItem(new_item, 1, "%s" % typename) b@374: self.list.SetStringItem(new_item, 2, "%s" % ip) b@375: self.list.SetStringItem(new_item, 3, "%s" % port) b@375: b@375: # record the new data for the ColumnSorterMixin b@375: # we assign every list item a unique id (that won't change when items b@375: # are added or removed) b@375: self.list.SetItemData(new_item, self.nextItemId) b@375: b@375: # the value of each column has to be stored in the itemDataMap b@375: # so that ColumnSorterMixin knows how to sort the column. b@375: b@375: # "name" is included at the end so that self.removeService b@375: # can access it. b@375: self.itemDataMap[self.nextItemId] = [ svcname, typename, ip, port, name ] b@375: b@375: self.nextItemId += 1 etisserant@203: etisserant@203: def CreateURI(self, connect_type, connect_address, connect_port): etisserant@203: uri = "%s://%s:%s"%(connect_type, connect_address, connect_port) etisserant@203: return uri etisserant@203: etisserant@221: def ChooseLocalID(self, event): etisserant@277: self.my_result = "LOCAL://" etisserant@203: self.Close()