#!/usr/bin/python # # Python GUI for the BlimpBot, based on the wxPython Joystick demo. # # Look for the OnJoystick method to see where the joystick # events are handled. import math import wx from blimpbot import * from ir_sensor_panel import * from joypanel import * from joybuttons import * from axispanel import * from height_panel import HeightPanel from fake_interface import FakeInterface # Joystick buttons (Microsoft controller) BUTTON_A = 0b000000001 BUTTON_B = 0b000000010 BUTTON_C = 0b000000100 BUTTON_X = 0b000001000 BUTTON_Y = 0b000010000 BUTTON_Z = 0b000100000 BUTTON_LEFT_TRIGGER = 0b001000000 BUTTON_RIGHT_TRIGGER = 0b010000000 BUTTON_DPAD_ARROW = 0b100000000 # Timer ID (just pick some number) TIMER_ID = 50 # Number of milliseconds between joystick updates POLLING_PERIOD = 50 #---------------------------------------------------------------------------- class BlimpControlApp(wx.App): """ Acts as a controller for the application. Here's where the joystick code resides. """ def __init__(self): wx.App.__init__(self, redirect=False) # Create our BlimpBot object if len(sys.argv) > 1 and sys.argv[1] == "fake": self.bot = BlimpBot(FakeInterface()) else: self.bot = BlimpBot() # Load BlimpBot configuration self.config_file = os.path.join(os.path.dirname(__file__), 'config.ini') if os.path.isfile(self.config_file): f = open(self.config_file) options = dict([line.split('=', 1) for line in f]) f.close() self.bot.load_config(options) # Give this method to the BlimpControlPanel, so it can # initialize our joystick without exposing the implementation # details to the panel. def create_stick(window): # Try to grab the control. If we get it, capture the stick. # Otherwise, throw up an exception message and play stupid. try: self.stick = wx.Joystick() # TODO: try polling the joystick without capturing events. except NotImplementedError, v: wx.MessageBox(str(v), "Exception Message") self.stick = None return self.stick # Create the control panel self.win = BlimpControlPanel(create_stick, self.bot, self) self.init_polling_timer() # Show the panel self.win.Show() def save_blimpbot_config(self): options = {} self.bot.save_config(options) f = open(self.config_file, 'w') for option, value in options.iteritems(): f.write("%s=%s\n" % (option, value)) f.close() def init_polling_timer(self): """ Sets up the polling timer, which generates joystick events """ if not self.stick: return self.timer = wx.Timer(self.win, TIMER_ID) self.timer.Start(POLLING_PERIOD) wx.EVT_TIMER(self.win, TIMER_ID, self.on_joystick) def halt_timer(evt): self.timer.Stop() self.win.Destroy() wx.EVT_CLOSE(self.win, halt_timer) def run(self): """ Runs the app's main loop. """ self.MainLoop() def on_joystick(self, evt): """ Happens on a joystick event. """ if not self.stick: return # Update the GUI self.win.update() # Calculate left and right fan velocities x = self.stick.GetPosition().x x = (x-32767) * 2 / 65535. y = self.stick.GetPosition().y y = -(y-32767) * 2 / 65535. self.bot.set_fan_vel(FAN_LEFT, max(min(y - x, 1), -1)) self.bot.set_fan_vel(FAN_RIGHT, max(min(y + x, 1), -1)) # Handle button presses t = self.stick.GetButtonState() if (t & BUTTON_X): self.bot.set_fan_vel(FAN_VERTICAL, 1) elif (t & BUTTON_A): self.bot.set_fan_vel(FAN_VERTICAL, -1) else: self.bot.set_fan_vel(FAN_VERTICAL, 0) if (t & BUTTON_B): self.bot.start_height_control() elif (t & BUTTON_C): self.bot.stop_height_control() if (t & BUTTON_Y): self.bot.start() elif (t & BUTTON_Z): self.bot.stop() def OnExit(self): self.stick = None TBFLAGS = ( wx.TB_HORIZONTAL | wx.NO_BORDER | wx.TB_FLAT | wx.TB_TEXT ) class BlimpControlPanel(wx.Frame): """ The main GUI window, containing the user interface components. """ def __init__(self, get_stick_callback, bot, app): wx.Frame.__init__(self, None, -1, 'BlimpBot GUI') self.stick = get_stick_callback(self) self.bot = bot self.app = app # A GridBagSizer lays out items on a grid. sizer = wx.GridBagSizer(2,2) # Add status bar for help self.CreateStatusBar() # Create the tool bar at the top of the screen. self.create_toolbar() # Joystick panel self.joy = JoyPanel(self, self.stick) sizer.Add(self.joy, (1, 0), (1, 1), wx.ALL | wx.GROW, 2) # Axis panel, showing values of the joystick axes self.axes = AxisPanel(self, self.stick) sizer.Add(self.axes, (2, 0), (1, 3), wx.ALL | wx.GROW, 2) # Button panels shows the state of buttons self.buttons = JoyButtons(self, self.stick) sizer.Add(self.buttons, (3, 0), (1, 3), wx.ALL | wx.EXPAND | wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL, 1) # IR sensor panel shows IR sensor values self.ir_sensors = IRSensorPanel(self, bot) sizer.Add(self.ir_sensors, (4, 0), (1, 3), wx.ALL | wx.GROW, 2) # Height panel shows P I D values self.height_panel = HeightPanel(self, bot) sizer.Add(self.height_panel, (5, 0), (1, 3), wx.ALL | wx.GROW, 2) # Make the sizer put the panels and stuff in the right places. self.SetSizer(sizer) sizer.Fit(self) def create_toolbar(self): # Add control toolbar tb = self.CreateToolBar( TBFLAGS ) tsize = (16, 16) # Save Calibration ----------------------------------- save_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE, wx.ART_TOOLBAR, tsize) tb.AddLabelTool(10, "Save Calibration", save_bmp, \ shortHelp="Save Calibration", \ longHelp="Saves the PID controller and filter values.") def on_save(evt): self.app.save_blimpbot_config() self.Bind(wx.EVT_TOOL, on_save, id=10) # Start Blimp ----------------------------------- start_bmp = wx.ArtProvider.GetBitmap(wx.ART_GO_UP, wx.ART_TOOLBAR, tsize) tb.AddLabelTool(20, "Start", start_bmp, \ shortHelp="Starts the blimp.", \ longHelp="Starts the blimp.") def on_start(evt): self.bot.start() self.Bind(wx.EVT_TOOL, on_start, id=20) # Stop Blimp ----------------------------------- stop_bmp = wx.ArtProvider.GetBitmap(wx.ART_GO_DOWN, wx.ART_TOOLBAR, tsize) tb.AddLabelTool(30, "Stop", stop_bmp, \ shortHelp="Stops the blimp.", \ longHelp="Stops the blimp.") def on_stop(evt): self.bot.stop() self.Bind(wx.EVT_TOOL, on_stop, id=30) # Start Height Control ----------------------------------- stop_bmp = wx.ArtProvider.GetBitmap(wx.ART_GO_DOWN, wx.ART_TOOLBAR, tsize) tb.AddLabelTool(40, "Height Ctrl On", stop_bmp, \ shortHelp="Starts height control.", \ longHelp="Starts height control.") def on_hc_start(evt): self.bot.start_height_control() self.Bind(wx.EVT_TOOL, on_hc_start, id=40) # Stop Height Control ----------------------------------- stop_bmp = wx.ArtProvider.GetBitmap(wx.ART_GO_DOWN, wx.ART_TOOLBAR, tsize) tb.AddLabelTool(50, "Height Ctrl Off", stop_bmp, \ shortHelp="Stops height control.", \ longHelp="Stops height control.") def on_hc_stop(evt): self.bot.stop_height_control() self.Bind(wx.EVT_TOOL, on_hc_stop, id=50) # Make the toolbar buttons appear! tb.Realize() def update(self): self.axes.Update() self.joy.Update() self.buttons.Update() # Make sure we can use the joystick # TODO: actually check for a joystick... haveJoystick = True if wx.Platform == "__WXMAC__": haveJoystick = False # This if-condition is true if this file is launched as the main file, # not included from another Python file: if __name__ == '__main__': # Create the app print "Starting app..." app = BlimpControlApp() app.run() # blocks until the app exits