class MainFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, title="signalk client", size=(1000, 600)) self.value_list = [] self.client = SignalKClientFromArgs(sys.argv, True, self.on_con) self.host_port = self.client.host_port self.client.autoreconnect = False ssizer = wx.FlexGridSizer(0, 1, 0, 0) ssizer.AddGrowableRow(0) ssizer.AddGrowableCol(0) ssizer.SetFlexibleDirection(wx.BOTH) ssizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) self.scrolledWindow = wx.ScrolledWindow(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.HSCROLL | wx.VSCROLL) self.scrolledWindow.SetScrollRate(5, 5) sizer = wx.FlexGridSizer(0, 3, 0, 0) sizer.AddGrowableCol(2) sizer.SetFlexibleDirection(wx.BOTH) sizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) self.values = {} self.controls = {} self.sliderrange = {} self.value_list = self.client.list_values() self.on_con(self.client) for name in sorted(self.value_list): sizer.Add(wx.StaticText(self.scrolledWindow, wx.ID_ANY, name), 0, wx.ALL, 5) self.values[name] = wx.StaticText(self.scrolledWindow, wx.ID_ANY) sizer.Add(self.values[name], 0, wx.ALL, 5) t = self.value_list[name]['type'] if t == 'Property': tb = wx.TextCtrl(self.scrolledWindow, wx.ID_ANY) sizer.Add(tb) self.controls[name] = tb elif t == 'BooleanProperty': def proc(): # encapsulate to fix scope cb = wx.CheckBox(self.scrolledWindow, wx.ID_ANY, '') sizer.Add(cb, 0, wx.EXPAND) self.controls[name] = cb cbname = name def oncheck(event): self.client.set(cbname, cb.GetValue()) cb.Bind(wx.EVT_CHECKBOX, oncheck) proc() elif t == 'RangeProperty': useSlider = True def proc(): r = self.value_list[name]['min'], self.value_list[name][ 'max'] if useSlider: s = wx.Slider(self.scrolledWindow) s.SetRange(0, 1000) else: s = wx.SpinCtrlDouble(self.scrolledWindow) s.SetRange(r[0], r[1]) s.SetIncrement(min(1, (r[1] - r[0]) / 100.0)) s.SetDigits(-math.log(s.GetIncrement()) / math.log(10) + 1) sizer.Add(s, 0, wx.EXPAND) self.controls[name] = s sname = name def onspin(event): if useSlider: v = s.GetValue() / 1000.0 * (r[1] - r[0]) + r[0] self.client.set(sname, v) else: self.client.set(sname, s.GetValue()) if useSlider: s.Bind(wx.EVT_SLIDER, onspin) self.sliderrange[name] = r else: s.Bind(wx.EVT_SPINCTRLDOUBLE, onspin) proc() elif t == 'EnumProperty': def proc(): c = wx.Choice(self.scrolledWindow, wx.ID_ANY) for choice in self.value_list[name]['choices']: c.Append(str(choice)) sizer.Add(c, 0, wx.EXPAND) self.controls[name] = c cname = name def onchoice(event): self.client.set(cname, str(c.GetStringSelection())) c.Bind(wx.EVT_CHOICE, onchoice) proc() elif t == 'ResettableValue': def proc(): b = wx.Button(self.scrolledWindow, wx.ID_ANY, 'Reset') sizer.Add(b, 0, wx.EXPAND) bname = name def onclick(event): self.client.set(bname, 0) b.Bind(wx.EVT_BUTTON, onclick) proc() else: sizer.Add(wx.StaticText(self.scrolledWindow, wx.ID_ANY, '')) self.scrolledWindow.SetSizer(sizer) self.scrolledWindow.Layout() sizer.Fit(self.scrolledWindow) ssizer.Add(self.scrolledWindow, 1, wx.EXPAND | wx.ALL, 5) bsizer = wx.FlexGridSizer(1, 0, 0, 0) self.bRefresh = wx.Button(self, wx.ID_ANY, 'Refresh') self.bRefresh.Bind(wx.EVT_BUTTON, self.Refresh) bsizer.Add(self.bRefresh) self.bScope = wx.Button(self, wx.ID_ANY, 'Scope') self.bScope.Bind( wx.EVT_BUTTON, lambda event: subprocess.Popen([ 'python', os.path.abspath(os.path.dirname(__file__)) + '/' + 'scope_wx.py' ] + sys.argv[1:])) bsizer.Add(self.bScope) self.bClose = wx.Button(self, wx.ID_ANY, 'Close') self.bClose.Bind(wx.EVT_BUTTON, exit) bsizer.Add(self.bClose) ssizer.Add(bsizer, 1, wx.EXPAND) self.SetSizer(ssizer) self.Layout() self.timer = wx.Timer(self, wx.ID_ANY) self.timer.Start(500) self.Bind(wx.EVT_TIMER, self.receive_messages, id=wx.ID_ANY) self.Refresh() def Refresh(self): for name in self.value_list: self.client.get(name) def on_con(self, client): self.SetTitle("signalk client - Connected") for name in sorted(self.value_list): t = self.value_list[name]['type'] if t != 'SensorValue': client.watch(name) else: client.get(name) def receive_messages(self, event): if not self.client: try: host, port = self.host_port self.client = SignalKClient(self.on_con, host, port, autoreconnect=False) self.timer.Start(100) except socket.error: self.timer.Start(1000) return while True: result = False try: result = self.client.receive() except ConnectionLost: self.SetTitle("signalk client - Disconnected") self.client = False return except: pass if not result: break for name in result: if not 'value' in result[name]: print 'no value', result raise 'no value' value = round3(result[name]['value']) strvalue = str(value) if len(strvalue) > 50: strvalue = strvalue[:47] + '...' self.values[name].SetLabel(strvalue) if name in self.controls: try: if str(type(self.controls[name]) ) == "<class 'wx._controls.Choice'>": if not self.controls[name].SetStringSelection( value): print 'warning, invalid choice value specified' elif str(type(self.controls[name]) ) == "<class 'wx._controls.Slider'>": r = self.sliderrange[name] self.controls[name].SetValue( float(value - r[0]) / (r[1] - r[0]) * 1000) else: self.controls[name].SetValue(value) except: self.controls[name].SetValue(str(value)) size = self.GetSize() self.Fit() self.SetSize(size)
class AutopilotControlApp(App): def build(self): self.client = False self.connect(3) Clock.schedule_interval(self.connect, 3) Clock.schedule_interval(self.update, .1) self.enabled = False self.mode = 'compass' self.heading_command = 0 self.heading = 0 self._anim = None self.texture = Image(source='compass.png').texture self.control = AutopilotControl() return self.control def connect(self, dt): if self.client: return watchlist = [ 'ap.enabled', 'ap.mode', 'ap.heading', 'ap.heading_command' ] def on_con(client): for name in watchlist: client.watch(name) try: self.client = SignalKClient(on_con, autoreconnect=True) pass except: return def update(self, dt): if not self.client: return result = self.client.receive() for msg in result: value = result[msg]['value'] if msg == 'ap.enabled' or msg == 'ap.mode': if msg == 'ap.enabled': self.enabled = value else: self.mode = value color = 'ff0000' if self.enabled: colors = { 'compass': '******', 'gps': '0000ff', 'wind': 'ffff00' } color = colors[self.mode] self.control.ap.text = '[color=' + color + ']AP' elif msg == 'ap.heading': self.control.heading_label.text = str(value) self.control.compass.heading = value #self.control.compass.canvas.needs_redraw = 1 elif msg == 'ap.heading_command': self.control.heading_command_label.text = str(value) self.heading_command = value def onAP(self): if not self.client: return self.client.set('servo.raw_command', False) if self.enabled: self.client.set('ap.heading_command', self.heading) self.client.set('ap.enabled', True) else: self.client.set('servo.command', 0) self.client.set('ap.enabled', False)