Example #1
0
class Cockpit(ttkFrame):
    '''
    Remote controller GUI 
    '''
    
    KEY_ANG_SPEED = "ang-speed"
    KEY_ANGLES = "angles"
    KEY_ACCEL = "accel"
    
    PID_KEYS = ["P", "I", "D"]

    DEFAULT_DRONE_IP = "192.168.1.130"
    DEFAULT_DRONE_PORT = 2121

    DIR_NONE = 0
    DIR_VERTICAL = 1
    DIR_HORIZONTAL = 2
    
    MAX_ACCEL = 10.0 #TODO angles. Replace by m/s²
    MAX_ACCEL_Z = 0.1 #m/s²
    MAX_ANGLE_SPEED = 50.0 #º/s

    def __init__(self, parent, isDummy = False, droneIp = DEFAULT_DRONE_IP, dronePort = DEFAULT_DRONE_PORT):
        '''
        Constructor
        '''
        ttkFrame.__init__(self, parent)
        
        self._started = IntVar()
        self._integralsEnabled = IntVar()
        self._target = [0.0] * 4        
        
        self._selectedPidConstats = "--"
        self._pidConstants = {
                              Cockpit.KEY_ANG_SPEED:{
                                           "X":{
                                                "P": 0.0,
                                                "I": 0.0,
                                                "D": 0.0
                                                },
                                           "Y":{
                                                "P": 0.0,
                                                "I": 0.0,
                                                "D": 0.0
                                                },
                                           "Z":{
                                                "P": 0.0,
                                                "I": 0.0,
                                                "D": 0.0
                                                }
                                           },
                               Cockpit.KEY_ANGLES: {
                                          "X":{
                                                "P": 0.0,
                                                "I": 0.0,
                                                "D": 0.0
                                                },
                                           "Y":{
                                                "P": 0.0,
                                                "I": 0.0,
                                                "D": 0.0
                                                }
                                          },
                               Cockpit.KEY_ACCEL:{
                                           "X":{
                                                "P": 0.0,
                                                "I": 0.0,
                                                "D": 0.0
                                                },
                                           "Y":{
                                                "P": 0.0,
                                                "I": 0.0,
                                                "D": 0.0
                                                },
                                            "Z":{
                                                "P": 0.0,
                                                "I": 0.0,
                                                "D": 0.0
                                                 }
                                          }
                              }
        
        self.parent = parent

        self.initUI()

        self._controlKeysLocked = False

        if not isDummy:
            self._link = INetLink(droneIp, dronePort)
        else:
            self._link = ConsoleLink()
            
        self._link.open()

        self._updateInfoThread = Thread(target=self._updateInfo)
        self._updateInfoThreadRunning = False
        self._readingState = False

        self._start()
        
            
    def initUI(self):
        
        self.parent.title("Drone control")
        self.style = Style()
        self.style.theme_use("default")
        
        self.pack(fill=BOTH, expand=1)
        
        self.parent.bind_all("<Key>", self._keyDown)
        self.parent.bind_all("<KeyRelease>", self._keyUp)

        if system() == "Linux":
            self.parent.bind_all("<Button-4>", self._onMouseWheelUp)
            self.parent.bind_all("<Button-5>", self._onMouseWheelDown)

        else:
            #case of Windows
            self.parent.bind_all("<MouseWheel>", self._onMouseWheel)
        
        #Commands        
        commandsFrame = tkFrame(self)
        commandsFrame.grid(column=0, row=0, sticky="WE")
        
        self._startedCB = Checkbutton(commandsFrame, text="On", variable=self._started, command=self._startedCBChanged)
        self._startedCB.pack(side=LEFT, padx=4)
        
        self._integralsCB = Checkbutton(commandsFrame, text="Int.", variable=self._integralsEnabled, \
                                        command=self._integralsCBChanged, state=DISABLED)
        self._integralsCB.pack(side=LEFT, padx=4)
        
        self._quitButton = Button(commandsFrame, text="Quit", command=self.exit)
        self._quitButton.pack(side=LEFT, padx=2, pady=2)
        
#         self._angleLbl = Label(commandsFrame, text="Angle")
#         self._angleLbl.pack(side=LEFT, padx=4)
#         
#         self._angleEntry = Entry(commandsFrame, state=DISABLED)
#         self._angleEntry.pack(side=LEFT)
        
        #Info
        infoFrame = tkFrame(self)
        infoFrame.grid(column=1, row=1, sticky="E", padx=4)
        
        #Throttle
        Label(infoFrame, text="Throttle").grid(column=0, row=0, sticky="WE")        
        self._throttleTexts = [StringVar(),StringVar(),StringVar(),StringVar()]
        Entry(infoFrame, textvariable=self._throttleTexts[3], state=DISABLED, width=5).grid(column=0, row=1)                
        Entry(infoFrame, textvariable=self._throttleTexts[0], state=DISABLED, width=5).grid(column=1, row=1)
        Entry(infoFrame, textvariable=self._throttleTexts[2], state=DISABLED, width=5).grid(column=0, row=2)
        Entry(infoFrame, textvariable=self._throttleTexts[1], state=DISABLED, width=5).grid(column=1, row=2)
        
        #Angles
        Label(infoFrame, text="Angles").grid(column=0, row=3, sticky="WE")        
        self._angleTexts = [StringVar(),StringVar(),StringVar()]
        for index in range(3):
            Entry(infoFrame, textvariable=self._angleTexts[index], state=DISABLED, width=5).grid(column=index, row=4)
               
        #Accels
        Label(infoFrame, text="Accels").grid(column=0, row=5, sticky="WE")
        self._accelTexts = [StringVar(),StringVar(),StringVar()]
        for index in range(3):
            Entry(infoFrame, textvariable=self._accelTexts[index], state=DISABLED, width=5).grid(column=index, row=6)
        
        #Speeds
        Label(infoFrame, text="Speeds").grid(column=0, row=7, sticky="WE")
        self._speedTexts = [StringVar(),StringVar(),StringVar()]
        for index in range(3):
            Entry(infoFrame, textvariable=self._speedTexts[index], state=DISABLED, width=5).grid(column=index, row=8)
        
        #Height
        Label(infoFrame, text="Height").grid(column=0, row=9, sticky="W")
        self._heightText = StringVar()
        Entry(infoFrame, state=DISABLED, width=5).grid(column=1, row=9)
        
        #control
        
        controlFrame = tkFrame(self)
        controlFrame.grid(column=0, row=1, sticky="W")
        
        self._throttle = DoubleVar()
        self._thrustScale = Scale(controlFrame, orient=VERTICAL, from_=100.0, to=-100.0, \
                            tickinterval=0, variable=self._throttle, \
                            length=200, showvalue=1, \
                            state=DISABLED,
                            command=self._onThrustScaleChanged)
        self._thrustScale.bind("<Double-Button-1>", self._onThrustScaleDoubleButton1, "+")
        self._thrustScale.grid(column=0)
        
        self._shiftCanvas = Canvas(controlFrame, bg="white", height=400, width=400, \
                             relief=SUNKEN)
        self._shiftCanvas.bind("<Button-1>", self._onMouseButton1)
        #self._shiftCanvas.bind("<ButtonRelease-1>", self._onMouseButtonRelease1)
        self._shiftCanvas.bind("<B1-Motion>", self._onMouseButton1Motion)
        self._shiftCanvas.bind("<Double-Button-1>", self._onMouseDoubleButton1)

        self._shiftCanvas.bind("<Button-3>", self._onMouseButton3)
        #self._shiftCanvas.bind("<ButtonRelease-3>", self._onMouseButtonRelease3)
        self._shiftCanvas.bind("<B3-Motion>", self._onMouseButton3Motion)

        self._shiftCanvas.grid(row=0,column=1, padx=2, pady=2)
        self._shiftCanvas.create_oval(2, 2, 400, 400, outline="#ff0000")
        self._shiftCanvas.create_line(201, 2, 201, 400, fill="#ff0000")
        self._shiftCanvas.create_line(2, 201, 400, 201, fill="#ff0000")
        self._shiftMarker = self._shiftCanvas.create_oval(197, 197, 205, 205, outline="#0000ff", fill="#0000ff")
        
        self._yaw = DoubleVar()
        self._yawScale = Scale(controlFrame, orient=HORIZONTAL, from_=-100.0, to=100.0, \
                            tickinterval=0, variable=self._yaw, \
                            length=200, showvalue=1, \
                            command=self._onYawScaleChanged)
        self._yawScale.bind("<Double-Button-1>", self._onYawScaleDoubleButton1, "+")
        self._yawScale.grid(row=1, column=1)
        
        self._controlKeyActive = False
        

        #PID calibration

        pidCalibrationFrame = tkFrame(self)
        pidCalibrationFrame.grid(column=0, row=2, sticky="WE");

        self._pidSelected = StringVar()
        self._pidSelected.set("--")
        self._pidListBox = OptionMenu(pidCalibrationFrame, self._pidSelected, "--", \
                                      Cockpit.KEY_ANG_SPEED, Cockpit.KEY_ANGLES, Cockpit.KEY_ACCEL, \
                                       command=self._onPidListBoxChanged)
        self._pidListBox.pack(side=LEFT, padx=2)
        self._pidListBox.config(width=10)
        
        self._axisSelected = StringVar()
        self._axisSelected.set("--")
        self._axisListBox = OptionMenu(pidCalibrationFrame, self._axisSelected, "--", "X", "Y", "Z", \
                                       command=self._onAxisListBoxChanged)
        self._axisListBox.pack(side=LEFT, padx=2)
        self._axisListBox.config(state=DISABLED)

        Label(pidCalibrationFrame, text="P").pack(side=LEFT, padx=(14, 2))

        self._pidPString = StringVar()
        self._pidPString.set("0.00")
        self._pidPSpinbox = Spinbox(pidCalibrationFrame, width=5, from_=0.0, to=100.0, increment=0.01, state=DISABLED, \
                                         textvariable=self._pidPString, command=self._onPidSpinboxChanged)
        self._pidPSpinbox.pack(side=LEFT, padx=2)

        Label(pidCalibrationFrame, text="I").pack(side=LEFT, padx=(14, 2))

        self._pidIString = StringVar()
        self._pidIString.set("0.00")
        self._pidISpinbox = Spinbox(pidCalibrationFrame, width=5, from_=0.0, to=100.0, increment=0.01, state=DISABLED, \
                                         textvariable=self._pidIString, command=self._onPidSpinboxChanged)
        self._pidISpinbox.pack(side=LEFT, padx=2)
        
        Label(pidCalibrationFrame, text="D").pack(side=LEFT, padx=(14, 2))
        
        self._pidDString = StringVar()
        self._pidDString.set("0.00")
        self._pidDSpinbox = Spinbox(pidCalibrationFrame, width=5, from_=0.0, to=100.0, increment=0.01, state=DISABLED, \
                                         textvariable=self._pidDString, command=self._onPidSpinboxChanged)
        self._pidDSpinbox.pack(side=LEFT, padx=2)
        
        #debug
        debugFrame = tkFrame(self)
        debugFrame.grid(column=0, row=3, sticky="WE")
        
        self._debugMsg = Message(debugFrame, anchor="nw", justify=LEFT, relief=SUNKEN, width=300)
        self._debugMsg.pack(fill=BOTH, expand=1)



    def _start(self):

        self._readDroneConfig()
        
    
    def exit(self):
        
        self._link.send({"key": "close", "data": None})
        
        self._stopUpdateInfoThread()
        
        self._link.close()

        self.quit()


    def _updateTarget(self):
        
        markerCoords = self._shiftCanvas.coords(self._shiftMarker)
        coords = ((markerCoords[0] + markerCoords[2]) / 2, (markerCoords[1] + markerCoords[3]) / 2)
        
        self._target[1] = float(coords[0] - 201) * Cockpit.MAX_ACCEL / 200.0
        self._target[0] = float(coords[1] - 201) * Cockpit.MAX_ACCEL / 200.0      
        #Remote control uses clockwise angle, but the drone's referece system uses counter-clockwise angle
        self._target[2] = -self._yaw.get() * Cockpit.MAX_ANGLE_SPEED / 100.0
        
        self._target[3] = self._throttle.get() * Cockpit.MAX_ACCEL_Z / 100.0
        
        self._sendTarget() 
    
        
    def _keyDown(self, event):

        if event.keysym == "Escape":            
            self._throttle.set(0)
            self._started.set(0)
            self._thrustScale.config(state=DISABLED)
            self._stopUpdateInfoThread()
            self._sendIsStarted()
            
        elif event.keysym.startswith("Control"):            
            self._controlKeyActive = True
            
        elif not self._controlKeysLocked and self._controlKeyActive:
            
            if event.keysym == "Up":
                self._thrustScaleUp()
                
            elif event.keysym == "Down":
                self._thrustScaleDown()
                
            elif event.keysym == "Left":
                self._yawLeft()
                
            elif event.keysym == "Right":
                self._yawRight()
                
            elif event.keysym == "space":
                self._yawReset()
                self._thrustReset()
                
        elif not self._controlKeysLocked and not self._controlKeyActive:
            
            if event.keysym == "Up":
                self._moveShiftCanvasMarker((0,-5))
                
            elif event.keysym == "Down":
                self._moveShiftCanvasMarker((0,5))
                
            elif event.keysym == "Left":
                self._moveShiftCanvasMarker((-5,0))
                
            elif event.keysym == "Right":
                self._moveShiftCanvasMarker((5,0))
                
            elif event.keysym == "space":
                self._resetShiftCanvasMarker()                
    
    
    def _keyUp(self, eventArgs):
        
        if eventArgs.keysym.startswith("Control"):
            self._controlKeyActive = False
            
    
    def _onMouseButton1(self, eventArgs):

        self._lastMouseCoords = (eventArgs.x, eventArgs.y)

        
    def _onMouseButtonRelease1(self, eventArgs):

        self._shiftCanvas.coords(self._shiftMarker, 197, 197, 205, 205)

    
    def _limitCoordsToSize(self, coords, size):
        
        if coords[0] > size:
            x = size
        
        elif coords[0] < 0:
            x = 0
            
        else:
            x = coords[0]
            
            
        if coords[1] > size:
            y = size
            
        elif coords[1] < 0:
            y = 0
            
        else:
            y = coords[1]
            
        
        return (x,y)
    
    
    def _moveShiftCanvasMarker(self, shift):

        lastCoords = self._shiftCanvas.coords(self._shiftMarker)
        newCoords = (lastCoords[0] + shift[0], lastCoords[1] + shift[1])        
        newCoords = self._limitCoordsToSize(newCoords, 400)
    
        self._shiftCanvas.coords(self._shiftMarker, newCoords[0], newCoords[1], newCoords[0] + 8, newCoords[1] + 8)
        self._updateTarget()
    
    
    def _resetShiftCanvasMarker(self):
    
        self._shiftCanvas.coords(self._shiftMarker, 197, 197, 205, 205)
        self._updateTarget()
        
    
    def _onMouseButton1Motion(self, eventArgs):

        deltaCoords = (eventArgs.x - self._lastMouseCoords[0], eventArgs.y - self._lastMouseCoords[1])
        self._moveShiftCanvasMarker(deltaCoords)
        self._lastMouseCoords = (eventArgs.x, eventArgs.y)
  
      
    def _onMouseDoubleButton1(self, eventArgs):
        
        self._resetShiftCanvasMarker()        
            

    def _onMouseButton3(self, eventArgs):

        self._lastMouseCoords = (eventArgs.x, eventArgs.y)
        self._mouseDirection = Cockpit.DIR_NONE

        
    def _onMouseButtonRelease3(self, eventArgs):

        self._shiftCanvas.coords(self._shiftMarker, 197, 197, 205, 205)

        
    def _onMouseButton3Motion(self, eventArgs):

        deltaCoords = (eventArgs.x - self._lastMouseCoords[0], eventArgs.y - self._lastMouseCoords[1])

        if self._mouseDirection == Cockpit.DIR_NONE:
            if abs(deltaCoords[0]) > abs(deltaCoords[1]):
                self._mouseDirection = Cockpit.DIR_HORIZONTAL
            else:
                self._mouseDirection = Cockpit.DIR_VERTICAL

        if self._mouseDirection == Cockpit.DIR_HORIZONTAL:
            deltaCoords = (deltaCoords[0], 0)
        else:
            deltaCoords = (0, deltaCoords[1])

        self._moveShiftCanvasMarker(deltaCoords)
        self._lastMouseCoords = (eventArgs.x, eventArgs.y)
        
    
    def _thrustScaleUp(self):

        if self._started.get(): 
            newValue = self._thrustScale.get() + 1
            self._thrustScale.set(newValue)
            
            self._updateTarget()
    
    
    def _thrustScaleDown(self):
        
        if self._started.get():
            newValue = self._thrustScale.get() - 1
            self._thrustScale.set(newValue)
            
            self._updateTarget()
            
    
    def _thrustReset(self):
        
        if self._started.get():
            self._thrustScale.set(0.0)
            
            self._updateTarget()
            
            
    def _onThrustScaleDoubleButton1(self, eventArgs):
        
        self._thrustReset()
        
        return "break"
        
    
    def _yawRight(self):
        
        newValue = self._yaw.get() + 1
        self._yaw.set(newValue)
        self._updateTarget()
            

    def _yawLeft(self):
        
        newValue = self._yaw.get() - 1
        self._yaw.set(newValue)
        self._updateTarget()
        
        
    def _yawReset(self):
        
        self._yaw.set(0)
        self._updateTarget()
        
        
    def _onMouseWheelUp(self, eventArgs):
        
        if not self._controlKeyActive:
            self._thrustScaleUp()
            
        else:
            self._yawRight()
            

    def _onMouseWheelDown(self, eventArgs):

        if not self._controlKeyActive:
            self._thrustScaleDown()
            
        else:
            self._yawLeft()
    

    def _onMouseWheel(self, eventArgs):

        factor = int(eventArgs.delta/120)

        if not self._controlKeyActive:
        
            if self._started.get():
                newValue = self._thrustScale.get() + factor
                self._thrustScale.set(newValue)
                
                self._updateTarget()
        else:
            newValue = self._yaw.get() + factor
            self._yaw.set(newValue)
            self._updateTarget()

    
    def _onYawScaleChanged(self, eventArgs):
        
        self._updateTarget()
    
    
    def _onYawScaleDoubleButton1(self, eventArgs):
        
        self._yawReset()
        
        return "break"
        
    
    def _startedCBChanged(self):
        
        if not self._started.get():
            self._throttle.set(0)
            self._thrustScale.config(state=DISABLED)            
            self._integralsCB.config(state=DISABLED)
            self._stopUpdateInfoThread()
        else:
            self._thrustScale.config(state="normal")            
            self._integralsCB.config(state="normal")
            self._startUpdateInfoThread()
            
        self._sendIsStarted()
     
    
    def _integralsCBChanged(self):
    
        self._link.send({"key": "integrals", "data":self._integralsEnabled.get() != 0})
            
     
    def _onThrustScaleChanged(self, eventArgs):
        
        self._updateTarget()
    

    def _sendTarget(self):
        
        self._link.send({"key": "target", "data": self._target})
        
        
    def _sendIsStarted(self):
        
        isStarted = self._started.get() != 0        
        self._link.send({"key": "is-started", "data": isStarted})
        

    def _sendPidCalibrationData(self):

        if self._pidSelected.get() != "--" and self._axisSelected.get() != "--":

            pidData = {
                "pid": self._pidSelected.get(),
                "axis": self._axisSelected.get(), 
                "p": float(self._pidPSpinbox.get()),
                "i": float(self._pidISpinbox.get()),
                "d": float(self._pidDSpinbox.get())}
        
            self._link.send({"key": "pid-calibration", "data": pidData})


    def _updatePidCalibrationData(self):

        pid = self._pidSelected.get()
        axis = self._axisSelected.get()

        if pid != "--" and axis != "--":
             
            self._pidConstants[pid][axis]["P"] = float(self._pidPSpinbox.get())
            self._pidConstants[pid][axis]["I"] = float(self._pidISpinbox.get())
            self._pidConstants[pid][axis]["D"] = float(self._pidDSpinbox.get())
            

    def _readDroneConfig(self):

        self._link.send({"key": "read-drone-config", "data": None}, self._onDroneConfigRead)


    def _readDroneState(self):
        
        if not self._readingState:
            self._readingState = True
            self._link.send({"key": "read-drone-state", "data": None}, self._onDroneStateRead)


    def _readPidConfigItem(self, message, cockpitKey, axises, configKeys):
        
        for i in range(len(axises)):
            for j in range(len(Cockpit.PID_KEYS)):
                self._pidConstants[cockpitKey][axises[i]][Cockpit.PID_KEYS[j]] = message[configKeys[j]][i]
                

    def _onDroneConfigRead(self, message):

        #TODO Show current configuration within the GUI (at least relevant settings)
        if message:
            
            #Angle-speeds
            self._readPidConfigItem(message, Cockpit.KEY_ANG_SPEED, ["X", "Y", "Z"], \
                                    [Configuration.PID_ANGLES_SPEED_KP, \
                                     Configuration.PID_ANGLES_SPEED_KI, \
                                     Configuration.PID_ANGLES_SPEED_KD])
            
            #Angles
            self._readPidConfigItem(message, Cockpit.KEY_ANGLES, ["X", "Y"], \
                                    [Configuration.PID_ANGLES_KP, \
                                     Configuration.PID_ANGLES_KI, \
                                     Configuration.PID_ANGLES_KD])
                        
            #Accels
            self._readPidConfigItem(message, Cockpit.KEY_ACCEL, ["X", "Y", "Z"], \
                                    [Configuration.PID_ACCEL_KP, \
                                     Configuration.PID_ACCEL_KI, \
                                     Configuration.PID_ACCEL_KD])
        

    def _onDroneStateRead(self, state):
        
        if state:
            for index in range(4):
                self._throttleTexts[index].set("{0:.3f}".format(state["_throttles"][index]))
                
            for index in range(3):
                self._accelTexts[index].set("{0:.3f}".format(state["_accels"][index]))
                self._angleTexts[index].set("{0:.3f}".format(state["_angles"][index]))
                
        else:
            self._stopUpdateInfoThread()
            
        self._readingState = False
   

    def _onPidSpinboxChanged(self):

        self._updatePidCalibrationData()
        self._sendPidCalibrationData()

    
    def _onPidListBoxChanged(self, pid):
        
        self._axisSelected.set("--")
        
        self._pidPString.set("--")
        self._pidIString.set("--")
        self._pidDString.set("--")

        self._pidPSpinbox.config(state=DISABLED)
        self._pidISpinbox.config(state=DISABLED)
        self._pidDSpinbox.config(state=DISABLED)

        self._selectedPidConstats = pid

        if pid == "--":
            self._axisListBox.config(state=DISABLED)
            self._controlKeysLocked = False
                       
        else:
            self._axisListBox.config(state="normal")
            self._controlKeysLocked = True


    def _onAxisListBoxChanged(self, axis):
        
        if axis == "--" or (self._selectedPidConstats == Cockpit.KEY_ANGLES and axis == "Z"):
            
            self._pidPString.set("--")
            self._pidIString.set("--")
            self._pidDString.set("--")
            
            self._pidPSpinbox.config(state=DISABLED)
            self._pidISpinbox.config(state=DISABLED)
            self._pidDSpinbox.config(state=DISABLED)
            
            self._controlKeysLocked = axis != "--"
            
        else:
            
            self._pidPString.set("{:.2f}".format(self._pidConstants[self._selectedPidConstats][axis]["P"]))
            self._pidIString.set("{:.2f}".format(self._pidConstants[self._selectedPidConstats][axis]["I"]))
            self._pidDString.set("{:.2f}".format(self._pidConstants[self._selectedPidConstats][axis]["D"]))
            
            self._pidPSpinbox.config(state="normal")
            self._pidISpinbox.config(state="normal")
            self._pidDSpinbox.config(state="normal")
            
            self._controlKeysLocked = True

            
    def _updateInfo(self):
        
        while self._updateInfoThreadRunning:
            
            self._readDroneState()
            
            time.sleep(1.0)
            

    def _startUpdateInfoThread(self):
        
        self._updateInfoThreadRunning = True
        if not self._updateInfoThread.isAlive():                
            self._updateInfoThread.start()
        
            
    def _stopUpdateInfoThread(self):
        
        self._updateInfoThreadRunning = False
        if self._updateInfoThread.isAlive():
            self._updateInfoThread.join()
Example #2
0
class TkPictureFrame(Frame):
    def __init__(self, x, y, master=None):
        Frame.__init__(self, master)
        self.photo = None
        self.resolution = (x, y)
        #The center of the Canvas is 0, 0. Find the center so
        #we can draw the image properly.
        self.center = ( x/2, y/2)
        #Setup the canvas
        self.picture = Canvas(self, width=x, height=y)
        #Place the canvas in the Grid.
        self.picture.grid(row=0,column=0,columnspan=2)
        #Camera check button control.
        self.checkButton = Checkbutton(self, text='Camera?',\
            command=self.toggleCamera)
        #Place it on the grid.
        self.checkButton.grid(row=1,column=0)
        #Spinbox to set FPS
        self.fpsSpin = Spinbox(self, text="FPS", from_=2, to=30,\
            command=self.fpsSpinCallback)
        self.fpsSpin.grid(row=1, column=1)
        #Set framerate
        self.fpsSpinCallback()
        #To determine if the camera is running
        self.capturing = False

    def fpsSpinCallback(self):
        self.fps = int(self.fpsSpin.get())

    def changePic(self, photo):
        #Make a reference to the old photo for removal
        self.oldphoto = self.photo
        #Setup the new photo
        self.photo = photo
        #Draw the new photo over the old photo
        self.picture.create_image(self.center,image=self.photo)
        #Remove the old photo
        self.picture.delete(self.oldphoto)
        #Enable the checkbox
        self.checkButton.config(state="normal")


    def timedDisable(self, widget):
        #Disable a widget for 2 seconds.
        widget.config(state="disabled")
        time.sleep(2)
        widget.config(state="normal")

    def threadTimeDisable(self, widget):
        #Run the timed disable in a thread to avoid lockups.
        thread.start_new_thread(self.timedDisable, (widget,))

    def startCamera(self):
        #Disable the checkbox and fps spinner.
        self.checkButton.config(state="disabled")
        self.fpsSpin.config(state="disabled")
        #Start the camera
        thread.start_new_thread(self.setupCamera, self.resolution)
        self.capturing = True

    def stopCamera(self):
        #Disable the checkbox for a duration.
        self.threadTimeDisable(self.checkButton)
        #Enable the spinner.
        self.fpsSpin.config(state="normal")
        #Clear the canvas.
        self.capturing = False
        self.picture.delete("all")
        

    def toggleCamera(self):
        if self.capturing:
            self.stopCamera()    
        else:
            self.startCamera()

    def setupCamera(self, x, y):
        with picamera.PiCamera() as camera:
            camera.resolution = (x, y)
            camera.framerate = self.fps
            stream = io.BytesIO()
            for each in camera.capture_continuous(stream, format='jpeg'):
                # Truncate the stream to the current position (in case
                # prior iterations output a longer image)
                each.truncate()
                #Rewind the stream
                each.seek(0)
                #Open the image stream
                image = Image.open(each)
                photo = ImageTk.PhotoImage(image)
                #Break out of the loop if not capturing
                if not self.capturing:
                    break
                #Update the canvas
                self.changePic(photo)
                #Reset playback to the beginning for the next image.
                each.seek(0)
Example #3
0
class FirmwareParms:
	def __init__(self, app, prtr, settings, log):
		self.app = app
		self.printer = prtr
		self.settings = settings
		self.log = log
		self.readingFirmware = False
		self.reportOnly = False
		self.top = None

		self.parameters = {}
		self.groups = {}
		groups = []
		for s in FirmwareSettings:
			grp = s.split('_')[0]
			if grp not in groups:
				if grp not in grporder:
					print "Unknown group: %s" % grp
				else:
					groups.append(grp)
					g = self.groups[grp] = paramGroup(grp, grpinfo[grp][GRPLABEL])
			else:
				g = self.groups[grp]
				
			p = self.parameters[s] = param(s, pinfo[s][PLABEL], eepVal = self.settings.firmware[s], width=pinfo[s][PWIDTH])
			g.addParam(p)
			
			
	def isActive(self):
		return self.top != None

	def start(self, reportOnly=False):
		self.got92 = False
		self.got201 = False
		self.got203 = False
		self.got204 = False
		self.got205 = False
		self.got206 = False
		self.got301 = False
		self.reportOnly = reportOnly
		
		self.app.readingFirmware = True 
		self.printer.send_now("M503")
		
	def checkComplete(self):
		if self.got92 and self.got201 and self.got203 and self.got204 and self.got204 and self.got206 and self.got301:
			self.app.readingFirmware = False;
			self.app.event_generate(MWM_FIRMWARECOMPLETE)
			return True
		else:
			return False
	
	def m92(self, x, y, z, e):
		self.parameters['m92_x'].setFlash(x)
		self.parameters['m92_y'].setFlash(y)
		self.parameters['m92_z'].setFlash(z)
		self.parameters['m92_e'].setFlash(e)
		self.got92 = True
		return self.checkComplete()
		
	def m201(self, x, y, z, e):
		self.parameters['m201_x'].setFlash(x)
		self.parameters['m201_y'].setFlash(y)
		self.parameters['m201_z'].setFlash(z)
		self.parameters['m201_e'].setFlash(e)
		self.got201 = True
		return self.checkComplete()
		
	def m203(self, x, y, z, e):
		self.parameters['m203_x'].setFlash(x)
		self.parameters['m203_y'].setFlash(y)
		self.parameters['m203_z'].setFlash(z)
		self.parameters['m203_e'].setFlash(e)
		self.got203 = True
		return self.checkComplete()
		
	def m204(self, s, t):
		self.parameters['m204_s'].setFlash(s)
		self.parameters['m204_t'].setFlash(t)
		self.got204 = True
		return self.checkComplete()
		
	def m205(self, s, t, b, x, z, e):
		self.parameters['m205_s'].setFlash(s)
		self.parameters['m205_t'].setFlash(t)
		self.parameters['m205_b'].setFlash(b)
		self.parameters['m205_x'].setFlash(x)
		self.parameters['m205_z'].setFlash(z)
		self.parameters['m205_e'].setFlash(e)
		self.got205 = True
		return self.checkComplete()

	def m206(self, x, y, z):
		self.parameters['m206_x'].setFlash(x)
		self.parameters['m206_y'].setFlash(y)
		self.parameters['m206_z'].setFlash(z)
		self.got206 = True
		return self.checkComplete()

	def m301(self, p, i, d):
		self.parameters['m301_p'].setFlash(p)
		self.parameters['m301_i'].setFlash(i)
		self.parameters['m301_d'].setFlash(d)
		self.got301 = True
		return self.checkComplete()

	def reportComplete(self):
		if self.reportOnly:
			return self.parameters
		
		if self.top != None:
			self.updateFlashDisplay()
			if self.readingFirmware:
				self.readingFirmware = False
				for s in FirmwareSettings:
					p = self.parameters[s]
					v = p.getFlash()
					p.setEEProm(v)
					self.settings.firmware[s] = v
				self.settings.setModified()
				self.updateEEPromDisplay()
			return None
		
		self.top = Toplevel(self.app)
		self.top.protocol('WM_DELETE_WINDOW', self.delTop)
		self.top.title("Firmware Parameters")
		
		gf = LabelFrame(self.top, text="")
		gf.grid(row=1, column=1, rowspan=len(grporder), padx=10, pady=10, sticky=N+S+E+W)
		
		l = Label(gf, text="Profiles:")
		l.grid(row=1, column=1, columnspan=2, pady=10)
		
		self.profList = Listbox(gf)
		self.profList.grid(row=2, column=1, columnspan=2, sticky=N+S+E+W)
		self.yScroll  =  Scrollbar (gf, orient=VERTICAL)
		self.yScroll.grid (row=2, column=3, sticky=N+S)
		self.profList.config(yscrollcommand=self.yScroll.set)
		self.yScroll.config(command=self.profList.yview)
		
		self.profList.bind('<ButtonRelease-1>', self.profSel)
	
		l = Label(gf, text=" ")
		l.grid(row=3, column=1, columnspan=2, pady=10)

		l = Label(gf, text="New Profile Name")
		l.grid(row=4, column=1, columnspan=2)
		self.newProf = Entry(gf, width=12)
		self.newProf.grid(row=5, column=1, columnspan=2)
		
		self.cbOWrite = IntVar()
		self.cbOverWrite = Checkbutton(gf, text="Over-Write",  variable=self.cbOWrite)
		self.cbOverWrite.grid(row=6, column=1, sticky=W)
		
		self.bSave = Button(gf, width=12, text="Save", command = self.doSave)
		self.bSave.grid(row=6, column=2)

		l = Label(gf, text=" ")
		l.grid(row=7, column=1, columnspan=2, pady=10)
		
		self.cbCDel = IntVar()
		self.cbConfirmDelete = Checkbutton(gf, text="Delete",  variable=self.cbCDel,	command=self.cbDoConfirmDelete, state=DISABLED)
		self.cbConfirmDelete.grid(row=8, column=1, sticky=W)
		
		self.bDel = Button(gf, width=12, text="Delete", command = self.doDelete, state=DISABLED)
		self.bDel.grid(row=8, column=2)
		
		gf = LabelFrame(self.top, text="")
		gf.grid(row=len(grporder)+1, column=1, sticky=N+S+E+W, padx=10)
		
		self.bToEEProm = Button(gf, text='Copy Flash -> EEPROM', command=self.doFlashToEEProm, width = 20) 
		self.bToEEProm.place(relx=0.5, rely=0.5, y=-20, anchor=CENTER)
		
		self.bFromEEProm = Button(gf, text='Copy EEPROM -> Flash', command=self.doEEPromToFlash, width = 20) 
		self.bFromEEProm.place(relx=0.5, rely=0.5, y=20, anchor=CENTER)

		self.entryWidgets = {}
		self.buttons = {}
		self.allButtons = {}
		self.flashWidgets = {}
		self.eepromWidgets = {}
		
		f = Frame(self.top)
		f.grid(row=1, column=2, columnspan=4, sticky=N+W+E+W)
		t = Label(f, text="Flash                    EEProm", width=68, anchor=E)
		t.grid(row=1, column=1, sticky=E)
		
		Row = 2

		for g in grporder:
			glf = LabelFrame(self.top, text=self.groups[g].getLabel(), width=10, height=10)
			glf.grid(row=Row, column = 2, columnspan=4, sticky=N+W+E+S)
			gRow = 1
			for p in self.groups[g]:
				t = Label(glf, text=p.getLabel(), width=18, anchor=E)
				t.grid(row=gRow, column=1)
				w = self.entryWidgets[p.getTag()] = Entry(glf, width=10, justify=RIGHT)
				w.grid(row=gRow, column=2)
				w = self.buttons[p.getTag()] = Button(glf, text="->", width=2, height=1,
						command=lambda prm=p: self.profButton(prm))
				w.grid(row=gRow, column=3)
				
				l = Label(glf, text=p.displayFlash(), width=14, anchor=E)
				l.grid(row=gRow, column=5)
				self.flashWidgets[p.getTag()] = l
				
				l = Label(glf, text=p.displayEEProm(), width=14, anchor=E)
				l.grid(row=gRow, column=6)
				self.eepromWidgets[p.getTag()] = l
				
				gRow += 1
			
			w = self.allButtons[g] = Button(glf, text="All\n->",
					command=lambda prm=g: self.allButton(self.groups[prm]))
			w.grid(row=1, column=4, rowspan=gRow-1, sticky=N+E+W+S)
			Row += 1
		
		self.loadProfiles()
		
		self.top.geometry("850x920+50+50")
		return None
		
	def loadProfiles(self, name=None):
		self.profList.delete(0, END)
		self.profList.insert(END, EMPTY)
		self.profiles = self.settings.getProfiles()
		match = 0
		px = 1
		for pn in self.profiles:
			self.profList.insert(END, pn)
			if pn == name:
				match = px
			px += 1
			
		self.profList.selection_set(match)
		self.profSel()
	
	def cbDoConfirmDelete(self):
		if self.cbCDel.get() == 1:
			self.bDel.config(state=NORMAL)
		else:
			self.bDel.config(state=DISABLED)
			
	def doDelete(self):
		try:
			item = int(self.profList.curselection()[0])
		except:
			item = 0
			
		if item == 0:
			return
		
		prof = self.profiles[item-1]
		self.settings.delProfile(prof)
		self.loadProfiles()
		
	def profSel(self, *arg):
		try:
			item = int(self.profList.curselection()[0])
		except:
			item = 0
		
		if item == 0:
			self.cbConfirmDelete.config(state=DISABLED)
			self.bDel.config(state=DISABLED)
			for s in FirmwareSettings:
				p = self.parameters[s]
				p.setProf(None)
		else:		
			self.cbConfirmDelete.config(state=NORMAL)
			self.cbCDel.set(0)
			prof = self.profiles[item-1]
			po = self.settings.getProfile(prof)
			for s in FirmwareSettings:
				p = self.parameters[s]
				p.setProf(po.getAttrib(s))
			
		self.updateProfDisplay()
		
	def doSave(self):
		np = self.newProf.get().strip()
		
		if np == "":
			showerror("Write Error", "New Profile name required", parent=self.top)
			return
		
		if np in self.profiles and self.cbOWrite.get() == 0:
			showerror("Write Error", "Must check over-write for an existing profile", parent=self.top)
			return

		p = self.settings.getProfile(np)
		newProf = False
		if p == None:
			p = self.settings.addProfile(np)
			newProf = True
			
		for s in FirmwareSettings:
			val = self.entryWidgets[s].get()
		
			if self.valueValid(val):
				p.setAttrib(s, float(val))
			else:
				p.delAttrib(s)
				
		if newProf:
			self.loadProfiles(p.getName())
			

	def doFlashToEEProm(self):
		for s in FirmwareSettings:
			p = self.parameters[s]
			v = p.getFlash()
			p.setEEProm(v)
			self.settings.firmware[s] = v
			self.eepromWidgets[s].config(text=self.parameters[s].displayEEProm())
		self.settings.setModified()
		self.log.logMsg("Committing to EEProm")
		self.printer.send_now("M500")
		
	def updateFlashDisplay(self):
		for s in FirmwareSettings:
			self.flashWidgets[s].config(text=self.parameters[s].displayFlash())
		
	def updateEEPromDisplay(self):
		for s in FirmwareSettings:
			self.eepromWidgets[s].config(text=self.parameters[s].displayEEProm())
		
	def updateProfDisplay(self):
		for s in FirmwareSettings:
			self.entryWidgets[s].delete(0, END)
			self.entryWidgets[s].insert(0, self.parameters[s].displayProf())
	
	def doEEPromToFlash(self):
		self.readingFirmware = True
		self.got92 = False
		self.got201 = False
		self.got203 = False
		self.got204 = False
		self.got205 = False
		self.got206 = False
		self.got301 = False
		
		self.app.readingFirmware = True 
		self.printer.send_now("M501")
		
	def valueValid(self, v):
		if v == None: return False
		if v == "": return False
		try:
			vf = float(v)
			return True
		except:
			return False
	
	def profButton(self, p):
		ptag = p.getTag()
		cmd, pn = ptag.split('_')
		val = self.entryWidgets[ptag].get()
		
		if self.valueValid(val):
			vf = float(val)
			self.parameters[ptag].setFlash(vf)
			dspv = self.parameters[ptag].displayFlash().strip()
			self.flashWidgets[ptag].config(text=dspv)
			self.printer.send_now("%s %s%s" % (cmd.upper(), pn.upper(), dspv))
			self.log.logMsg("setting %s - %s to %s" 
						% (self.groups[cmd].getLabel(), self.parameters[ptag].getLabel(), dspv))
		else:
			self.log.logMsg("Bad parameter value")
		
	def allButton(self, g):
		gtag = g.getTag()
		
		cmd = gtag.upper()
		
		hasParams = False
		
		for p in self.groups[gtag]:
			ptag = p.getTag()
			pn = p.tag.split('_')[1]
			val = self.entryWidgets[ptag].get()
			if self.valueValid(val):
				hasParams = True
				vf = float(val)
				self.parameters[ptag].setFlash(vf)
				dspv = self.parameters[ptag].displayFlash().strip()
				self.flashWidgets[ptag].config(text=dspv)
				self.log.logMsg("setting %s - %s to %s" 
						% (self.groups[gtag].getLabel(), self.parameters[ptag].getLabel(), dspv))
				cmd += " %s%s" % (pn.upper(), dspv)
			else:
				self.log.logMsg("Bad parameter value: %s" % self.parameters[ptag].getLabel())

		if hasParams:				
			self.printer.send_now(cmd)
		else:
			self.log.logMsg("No parameters entered")

	def delTop(self):
		self.top.destroy()
		self.top = None