class MeasureMachinePopup(GridLayout):
    done = ObjectProperty(None)
    stepText = StringProperty("Step 1 of 10")
    numberOfTimesTestCutRun = -2
    kinematicsType = 'Quadrilateral'

    def establishDataConnection(self, data):
        '''

        Sets up the data connection between this popup and the shared data object

        '''

        self.data = data

        self.setSprocketsVertical.data = data
        self.setSprocketsVertical.carousel = self.carousel

        self.measureOutChains.data = data
        self.measureOutChains.carousel = self.carousel
        self.measureOutChains.text = "Now we are going to measure out the chains and reattach the sled\n\nHook the first link of the right chain on the vertical tooth of the right sprocket\n as shown in the picture below\n\nThe left chain does not need to be moved, it can be left partly extended\n\nThe correct length of first the left and then the right chain will be measured out\n\nOnce both chains are finished attach the sled, then press Next\nPressing Next will move the sled to the center of the sheet.\n\nBe sure to keep an eye on the chains during this process to ensure that they do not become tangled\naround the sprocket. The motors are very powerful and the machine can damage itself this way"

        self.triangularCalibration.data = data
        self.triangularCalibration.carousel = self.carousel

    def backBtn(self, *args):
        '''

        Runs when the back button is pressed

        '''

        if self.carousel.index == 10 and self.kinematicsType == 'Quadrilateral':  #if we're at the test cut for quadrilateral and we want to go back to choosing kinematics type
            self.carousel.load_slide(self.carousel.slides[8])
        elif self.carousel.index == 11 and self.kinematicsType == 'Triangular':  #if we're at the last step and need to go back but but we want to go back to the triangular kinematics test cut
            self.carousel.load_slide(self.carousel.slides[9])
        else:
            self.carousel.load_previous()

    def fwdBtn(self, *args):
        '''

        Runs when the skip button is pressed

        '''

        if self.carousel.index == 8 and self.kinematicsType == 'Quadrilateral':  #If the kinematics type is quadrilateral skip to the quadrilateral test
            self.carousel.load_slide(self.carousel.slides[10])
        elif self.carousel.index == 9 and self.kinematicsType == 'Triangular':  #If we're in the cut test shape triangular and we want to skip to the end
            self.carousel.load_slide(self.carousel.slides[11])
        else:
            self.carousel.load_next()

    def slideJustChanged(self):

        if self.carousel.index == 0:
            #begin notes
            self.goBackBtn.disabled = True
            self.stepText = "Step 1 of 10"

        if self.carousel.index == 1:
            #pointing one sprocket up
            self.goBackBtn.disabled = False
            self.stepText = "Step 2 of 10"

        if self.carousel.index == 2:
            #measuring distance between motors
            self.data.measureRequest = self.readMotorSpacing
            self.stepText = "Step 3 of 10"

        if self.carousel.index == 3:
            #measure sled spacing
            self.stepText = "Step 4 of 10"
            pass

        if self.carousel.index == 4:
            #measure vertical distance to wood
            self.data.measureRequest = self.readVerticalOffset
            self.stepText = "Step 5 of 10"

        if self.carousel.index == 5:
            #review calculations
            self.updateReviewValuesText()
            self.stepText = "Step 6 of 10"

        if self.carousel.index == 6:
            #Calibrate chain lengths
            self.stepText = "Step 7 of 10"

        if self.carousel.index == 7:
            #set up z-axis
            if int(self.data.config.get('Maslow Settings', 'zAxis')) == 1:
                self.zAxisActiveSwitch.active = True
            else:
                self.zAxisActiveSwitch.active = False
            self.stepText = "Step 8 of 10"

        if self.carousel.index == 8:
            #Choose kinematics type
            self.stepText = "Step 9 of 10"

        if self.carousel.index == 9:
            #Cut test shape triangular
            self.data.pushSettings()
            self.stepText = "Step 10 of 10"
            self.goFwdBtn.disabled = False

            #if we're not supposed to be in triangular calibration go to the next page
            if self.kinematicsType != 'Triangular':
                self.carousel.load_next()

        if self.carousel.index == 10:
            #Cut test shape quadrilateral
            self.data.pushSettings()
            self.goFwdBtn.disabled = False
            self.stepText = "Step 10 of 10"

            #if we're not supposed to be in quadratic calibration go to finished
            if self.kinematicsType == 'Triangular':
                self.carousel.load_next()

        if self.carousel.index == 11:
            #Final finish step
            self.goFwdBtn.disabled = True
            finishString = "Your machine is now calibrated!\n\nCongratulations!\n\nThe final calibration values are:\n"
            finishString = finishString + "\nDistance between motors: " + self.data.config.get(
                'Maslow Settings', 'motorSpacingX') + "mm"
            finishString = finishString + "\nVertical motor offset: " + self.data.config.get(
                'Maslow Settings', 'motorOffsetY') + "mm"
            finishString = finishString + "\nKinematics type: " + self.data.config.get(
                'Advanced Settings', 'kinematicsType')
            if self.data.config.get('Advanced Settings',
                                    'kinematicsType') == 'Triangular':
                finishString = finishString + "\nRotation radius: " + self.data.config.get(
                    'Advanced Settings', 'rotationRadius') + "mm"
            else:
                finishString = finishString + "\nSled mount spacing: " + self.data.config.get(
                    'Maslow Settings', 'sledWidth') + "mm"

            self.finishText.text = finishString

    def begin(self):

        self.carousel.load_next()

    def defineInitialState(self):
        '''

        Ensure that the calibration process begins with known initial conditions for where the axis
        think that they are are by setting both to zero. This prevents strange behavior when rotating
        each sprocket to 12:00

        '''
        self.data.gcode_queue.put("B06 L0 R0 ")
        self.carousel.load_next()

    def extendLeft(self, dist):
        self.data.gcode_queue.put("G91 ")
        self.data.gcode_queue.put("B09 L" + str(dist) + " ")
        self.data.gcode_queue.put("G90 ")

    def retractLeft(self, dist):
        self.data.gcode_queue.put("G91 ")
        self.data.gcode_queue.put("B09 L-" + str(dist) + " ")
        self.data.gcode_queue.put("G90 ")

    def measureLeft(self):
        self.data.gcode_queue.put("B10 L")

    def readMotorSpacing(self, dist):

        dist = dist - 2 * 6.35  #subtract off the extra two links

        print "Read motor spacing: " + str(dist)
        self.data.config.set('Maslow Settings', 'motorSpacingX', str(dist))
        self.data.config.write()

        self.extendLeft(15)

        self.carousel.load_next()

    def readVerticalOffset(self, dist):
        print "vertical offset measured at: " + str(dist)
        self.data.config.set('Maslow Settings', 'motorOffsetY', str(dist))
        self.data.config.write()

        #keep updating the values shown because sometimes it takes a while for the settings to write
        from kivy.clock import Clock
        Clock.schedule_once(self.updateReviewValuesText, .1)
        Clock.schedule_once(self.updateReviewValuesText, .2)
        Clock.schedule_once(self.updateReviewValuesText, .3)
        Clock.schedule_once(self.updateReviewValuesText, .4)

        self.carousel.load_next()

    def textInputPopup(self, target):
        self.targetWidget = target

        self.popupContent = TouchNumberInput(done=self.dismiss_popup,
                                             data=self.data)
        self._popup = Popup(title="Number of links",
                            content=self.popupContent,
                            size_hint=(0.9, 0.9))
        self._popup.open()
        if global_variables._keyboard:
            global_variables._keyboard.bind(on_key_down=self.keydown_popup)
            self._popup.bind(on_dismiss=self.ondismiss_popup)

    def dismiss_popup(self):
        try:
            tempfloat = float(self.popupContent.textInput.text)
            self.targetWidget.text = str(
                tempfloat
            )  # Update displayed text using standard numeric format
        except:
            pass  # If what was entered cannot be converted to a number, leave the value the same
        self._popup.dismiss()

    def ondismiss_popup(self, event):
        if global_variables._keyboard:
            global_variables._keyboard.unbind(on_key_down=self.keydown_popup)

    def keydown_popup(self, keyboard, keycode, text, modifiers):
        if (keycode[1] == '0') or (keycode[1] == 'numpad0'):
            self.popupContent.addText('0')
        elif (keycode[1] == '1') or (keycode[1] == 'numpad1'):
            self.popupContent.addText('1')
        elif (keycode[1] == '2') or (keycode[1] == 'numpad2'):
            self.popupContent.addText('2')
        elif (keycode[1] == '3') or (keycode[1] == 'numpad3'):
            self.popupContent.addText('3')
        elif (keycode[1] == '4') or (keycode[1] == 'numpad4'):
            self.popupContent.addText('4')
        elif (keycode[1] == '5') or (keycode[1] == 'numpad5'):
            self.popupContent.addText('5')
        elif (keycode[1] == '6') or (keycode[1] == 'numpad6'):
            self.popupContent.addText('6')
        elif (keycode[1] == '7') or (keycode[1] == 'numpad7'):
            self.popupContent.addText('7')
        elif (keycode[1] == '8') or (keycode[1] == 'numpad8'):
            self.popupContent.addText('8')
        elif (keycode[1] == '9') or (keycode[1] == 'numpad9'):
            self.popupContent.addText('9')
        elif (keycode[1] == '.') or (keycode[1] == 'numpaddecimal'):
            self.popupContent.addText('.')
        elif (keycode[1] == 'backspace'):
            self.popupContent.textInput.text = self.popupContent.textInput.text[:
                                                                                -1]
        elif (keycode[1] == 'enter') or (keycode[1] == 'numpadenter'):
            self.popupContent.done()
        elif (keycode[1] == 'escape'
              ):  # abort entering a number, keep the old number
            self.popupContent.textInput.text = ''  # clear text so it isn't converted to a number
            self.popupContent.done()
        return True  # always swallow keypresses since this is a modal dialog

    def countLinks(self):
        print "counting links, dist: "

        try:
            dist = float(self.linksTextInput.text) * 6.35
        except:
            self.carousel.load_next()
            return

        self.data.config.set('Maslow Settings', 'sledWidth', str(dist))
        self.data.config.write()

        self.carousel.load_next()

    def calibrateChainLengths(self):
        print "calibrating"
        self.data.gcode_queue.put("B02 ")

    def enableZaxis(self, *args):
        '''

        Triggered when the switch to enable the z-axis is touched

        '''
        self.data.config.set('Maslow Settings', 'zAxis',
                             int(self.zAxisActiveSwitch.active))
        self.data.config.write()

    def pullChainTight(self):
        #pull the left chain tight
        self.data.gcode_queue.put("B11 S50 T3 ")

    def updateReviewValuesText(self, *args):
        '''

        Update the text which displays the measured values

        '''
        self.reviewNumbers.text = "Let's review the measurements we've made so far to make sure they look correct\n\nMotor Spacing: " + str(
            self.data.config.get('Maslow Settings', 'motorSpacingX')
        ) + "mm\nSled Mount Spacing: " + str(
            self.data.config.get('Maslow Settings', 'sledWidth')
        ) + "mm\nVertical Offset: " + str(
            self.data.config.get('Maslow Settings', 'motorOffsetY')
        ) + "mm\n\nYou can go back and re-do any of these numbers if you would like"

    def finishChainCalibration(self, *args):
        #adjust chain lengths to put the sled in the center
        self.data.gcode_queue.put("B15 ")
        self.carousel.load_next()

    def setKinematicsType(self, kinematicsType, *args):
        '''

        Update kinematics to the value shown in the drop down and move to the next step

        '''
        self.kinematicsType = kinematicsType

        print "Kinematics set to: "
        print self.kinematicsType

        self.data.config.set('Advanced Settings', 'kinematicsType',
                             self.kinematicsType)
        self.data.config.write()

        if self.kinematicsType == 'Triangular':
            try:
                #Get the value if it's already there...
                rotationRadius = self.data.config.get('Advanced Settings',
                                                      'rotationRadius')
                print "Rotation radius is " + str(rotationRadius)
            except:
                #Set up a good initial guess for the radius
                print "Rotation radius set to 260"
                self.data.config.set('Advanced Settings', 'rotationRadius',
                                     260)
                self.data.config.write()
            self.carousel.load_next()
        else:

            self.carousel.load_slide(self.carousel.slides[10])

    def cutTestPatern(self):

        #Credit for this test pattern to DavidLang
        self.data.units = "MM"
        self.data.gcode_queue.put("G21 ")
        self.data.gcode_queue.put("G90  ")
        self.data.gcode_queue.put("G40 ")

        self.data.gcode_queue.put("G0 Z5 ")
        self.data.gcode_queue.put("G0 X0 Y0  ")
        self.data.gcode_queue.put("G17 ")

        #(defines the center)
        self.data.gcode_queue.put("G0 X" +
                                  str(18 * self.numberOfTimesTestCutRun) +
                                  " Y" +
                                  str(-18 * self.numberOfTimesTestCutRun) +
                                  "  ")
        self.data.gcode_queue.put("G91 ")

        self.data.gcode_queue.put("G0 X-300 Y300  ")
        self.data.gcode_queue.put("G1 Z-7 F500  ")
        self.data.gcode_queue.put("G1 Y18  ")
        self.data.gcode_queue.put("G1 Z7  ")
        self.data.gcode_queue.put("G0 X600 Y-18 ")
        self.data.gcode_queue.put("G1 Z-7  ")
        self.data.gcode_queue.put("G1 Y18  ")
        self.data.gcode_queue.put("G1 X-18 ")
        self.data.gcode_queue.put("G1 Z7 ")
        self.data.gcode_queue.put("G0 X18 Y-600 ")
        self.data.gcode_queue.put("G1 Z-7  ")
        self.data.gcode_queue.put("G1 X-18  ")
        self.data.gcode_queue.put("G1 Z7  ")
        self.data.gcode_queue.put("G0 X-600 ")
        self.data.gcode_queue.put("G90  ")

        self.numberOfTimesTestCutRun = self.numberOfTimesTestCutRun + 1
        self.cutBtn.text = "Re-Cut Test\nPattern"
        self.cutBtn.disabled = True
        self.horizMeasure.disabled = False
        self.vertMeasure.disabled = False
        self.unitsBtn.disabled = False
        self.enterValues.disabled = False

    def enterTestPaternValues(self):

        dif = 0

        try:
            dif = float(self.horizMeasure.text) - float(self.vertMeasure.text)
        except:
            self.data.message_queue.put(
                "Message: Couldn't make that into a number")
            return

        if self.unitsBtn.text == 'Inches':
            dif = dif * 25.4

        acceptableTolerance = .5

        if abs(dif) < acceptableTolerance:  #if we're fully calibrated
            self.carousel.load_next()
        else:
            amtToChange = .9 * dif
            newSledSpacing = float(
                self.data.config.get('Maslow Settings',
                                     'sledWidth')) + amtToChange
            print "Now trying spacing: " + str(newSledSpacing)
            self.data.config.set('Maslow Settings', 'sledWidth',
                                 str(newSledSpacing))
            self.data.config.write()
            self.cutBtn.disabled = False
            self.data.pushSettings()

    def stopCut(self):
        self.data.quick_queue.put("!")
        with self.data.gcode_queue.mutex:
            self.data.gcode_queue.queue.clear()

    def switchUnits(self):
        if self.unitsBtn.text == 'MM':
            self.unitsBtn.text = 'Inches'
        else:
            self.unitsBtn.text = 'MM'

    def moveZ(self, dist):
        '''

        Move the z-axis the specified distance

        '''
        self.data.units = "MM"
        self.data.gcode_queue.put("G21 ")
        self.data.gcode_queue.put("G91 G00 Z" + str(dist) + " G90 ")

    def zeroZ(self):
        '''

        Define the z-axis to be currently at height 0

        '''
        self.data.gcode_queue.put("G10 Z0 ")
        self.carousel.load_next()
class MeasureDistBetweenMotors(GridLayout):
    '''
    
    Provides a standard interface for measuring the distance between the motors. Assumes that both motors are in position zero at the begining
    
    '''
    data = ObjectProperty(None)

    def textInputPopup(self, target):
        self.targetWidget = target

        self.popupContent = TouchNumberInput(done=self.dismiss_popup,
                                             data=self.data)
        self.popupContent.forceUnitsMM()
        self._popup = Popup(title="Set distance to move chain",
                            content=self.popupContent,
                            size_hint=(0.9, 0.9))
        self._popup.open()
        if global_variables._keyboard:
            global_variables._keyboard.bind(on_key_down=self.keydown_popup)
            self._popup.bind(on_dismiss=self.ondismiss_popup)

    def ondismiss_popup(self, event):
        if global_variables._keyboard:
            global_variables._keyboard.unbind(on_key_down=self.keydown_popup)

    def keydown_popup(self, keyboard, keycode, text, modifiers):
        if (keycode[1] == '0') or (keycode[1] == 'numpad0'):
            self.popupContent.addText('0')
        elif (keycode[1] == '1') or (keycode[1] == 'numpad1'):
            self.popupContent.addText('1')
        elif (keycode[1] == '2') or (keycode[1] == 'numpad2'):
            self.popupContent.addText('2')
        elif (keycode[1] == '3') or (keycode[1] == 'numpad3'):
            self.popupContent.addText('3')
        elif (keycode[1] == '4') or (keycode[1] == 'numpad4'):
            self.popupContent.addText('4')
        elif (keycode[1] == '5') or (keycode[1] == 'numpad5'):
            self.popupContent.addText('5')
        elif (keycode[1] == '6') or (keycode[1] == 'numpad6'):
            self.popupContent.addText('6')
        elif (keycode[1] == '7') or (keycode[1] == 'numpad7'):
            self.popupContent.addText('7')
        elif (keycode[1] == '8') or (keycode[1] == 'numpad8'):
            self.popupContent.addText('8')
        elif (keycode[1] == '9') or (keycode[1] == 'numpad9'):
            self.popupContent.addText('9')
        elif (keycode[1] == '.') or (keycode[1] == 'numpaddecimal'):
            self.popupContent.addText('.')
        elif (keycode[1] == 'backspace'):
            self.popupContent.textInput.text = self.popupContent.textInput.text[:
                                                                                -1]
        elif (keycode[1] == 'enter') or (keycode[1] == 'numpadenter'):
            self.popupContent.done()
        elif (keycode[1] == 'escape'
              ):  # abort entering a number, keep the old number
            self.popupContent.textInput.text = ''  # clear text so it isn't converted to a number
            self.popupContent.done()
        return True  # always swallow keypresses since this is a modal dialog

    def dismiss_popup(self):
        try:
            tempfloat = float(self.popupContent.textInput.text)
            self.targetWidget.text = str(
                tempfloat
            )  # Update displayed text using standard numeric format
            #self.distText.text = "Dist (" + self.data.units + "):"
        except:
            pass  # If what was entered cannot be converted to a number, leave the value the same
        self._popup.dismiss()

    def stopCut(self):
        self.data.quick_queue.put("!")
        with self.data.gcode_queue.mutex:
            self.data.gcode_queue.queue.clear()

    def extend(self):
        dist = float(self.distToMove.text)
        self.data.gcode_queue.put("G91 ")
        self.data.gcode_queue.put("B09 L" + str(dist) + " ")
        self.data.gcode_queue.put("G90 ")

    def retract(self):
        dist = float(self.distToMove.text)
        self.data.gcode_queue.put("G91 ")
        self.data.gcode_queue.put("B09 L" + str(-1 * dist) + " ")
        self.data.gcode_queue.put("G90 ")

    def measureLeft(self):
        self.data.gcode_queue.put("B10 L")

    def readMotorSpacing(self, dist):
        dist = dist - 2 * 6.35  #subtract off the extra two links

        print "Read motor spacing: " + str(dist)
        self.data.config.set('Maslow Settings', 'motorSpacingX', str(dist))

        #put some slack in the chain
        self.data.gcode_queue.put("G91 ")
        self.data.gcode_queue.put("B09 L10 ")
        self.data.gcode_queue.put("G90 ")

        self.readyToMoveOn()

    def pullChainTightAndMeasure(self):
        #pull the left chain tight
        self.data.gcode_queue.put("B11 S255 T3 L ")
        #request a measurement
        self.data.gcode_queue.put("B10 L")

    def on_Enter(self):
        '''
        
        This function runs when the step is entered
        
        '''
        self.data = App.get_running_app().data
        self.data.measureRequest = self.readMotorSpacing

        self.originalChainOverSproketDir = App.get_running_app(
        ).data.config.get('Advanced Settings', 'chainOverSprocket')

        #pretend we are in the "Top" configuration during this step
        App.get_running_app().data.config.set('Advanced Settings',
                                              'chainOverSprocket', 'Top')

        #set the threshold for warning that the machine is off target to 200mm esentially turning it off. We dont' want this to trigger when pulling the chain tight
        self.data.gcode_queue.put("$42=200 ")

    def on_Exit(self):
        '''
        
        This function run when the process is completed or quit is pressed
        
        '''

        #Restore original chain over sprocket direction
        App.get_running_app().data.config.set('Advanced Settings',
                                              'chainOverSprocket',
                                              self.originalChainOverSproketDir)
        #restore all settings to the values stored in the current settings file
        self.data.gcode_queue.put("$$ ")
class VertDistToMotorsGuess(GridLayout):
    readyToMoveOn   = ObjectProperty(None)
    
    
    def on_Enter(self):
        '''
        
        This function runs when the step is entered
        
        '''
        self.data = App.get_running_app().data
    
    def textInputPopup(self, target):
        self.targetWidget = target

        self.popupContent = TouchNumberInput(done=self.dismiss_popup, data = self.data)
        self.popupContent.forceUnitsMM()
        self._popup = Popup(title="Set distance to move chain", content=self.popupContent,
                            size_hint=(0.9, 0.9))
        self._popup.open()
        if global_variables._keyboard:
            global_variables._keyboard.bind(on_key_down=self.keydown_popup)
            self._popup.bind(on_dismiss=self.ondismiss_popup)
    
    def ondismiss_popup(self, event):
        if global_variables._keyboard:
           global_variables._keyboard.unbind(on_key_down=self.keydown_popup)
        
    def keydown_popup(self, keyboard, keycode, text, modifiers):
        if (keycode[1] == '0') or (keycode[1] =='numpad0'):
            self.popupContent.addText('0')
        elif (keycode[1] == '1') or (keycode[1] =='numpad1'):
            self.popupContent.addText('1')
        elif (keycode[1] == '2') or (keycode[1] =='numpad2'):
            self.popupContent.addText('2')
        elif (keycode[1] == '3') or (keycode[1] =='numpad3'):
            self.popupContent.addText('3')
        elif (keycode[1] == '4') or (keycode[1] =='numpad4'):
            self.popupContent.addText('4')
        elif (keycode[1] == '5') or (keycode[1] =='numpad5'):
            self.popupContent.addText('5')
        elif (keycode[1] == '6') or (keycode[1] =='numpad6'):
            self.popupContent.addText('6')
        elif (keycode[1] == '7') or (keycode[1] =='numpad7'):
            self.popupContent.addText('7')
        elif (keycode[1] == '8') or (keycode[1] =='numpad8'):
            self.popupContent.addText('8')
        elif (keycode[1] == '9') or (keycode[1] =='numpad9'):
            self.popupContent.addText('9')
        elif (keycode[1] == '.') or (keycode[1] =='numpaddecimal'):
            self.popupContent.addText('.')
        elif (keycode[1] == 'backspace'):
            self.popupContent.textInput.text = self.popupContent.textInput.text[:-1]
        elif (keycode[1] == 'enter') or (keycode[1] =='numpadenter'):
            self.popupContent.done()
        elif (keycode[1] == 'escape'):     # abort entering a number, keep the old number
            self.popupContent.textInput.text = ''    # clear text so it isn't converted to a number
            self.popupContent.done()
        return True     # always swallow keypresses since this is a modal dialog
    
    def dismiss_popup(self):
        try:
            tempfloat = float(self.popupContent.textInput.text)
            self.targetWidget.text = str(tempfloat)  # Update displayed text using standard numeric format
            #self.distText.text = "Dist (" + self.data.units + "):"
        except:
            pass  # If what was entered cannot be converted to a number, leave the value the same
        self._popup.dismiss()
    
    def enterValues(self):
        try:
            dist = float(self.enterMeasurement.text)
            
            if dist > 300:
                self.data.config.set('Maslow Settings', 'motorOffsetY', str(dist))
            else:
                self.data.message_queue.put("Message: Warning: The value you entered for the distance between the motors and the top of the work area is very small. This may cause too much strain on the motors at the top of the sheet.")
            self.readyToMoveOn()
        except:
            self.data.message_queue.put("Message: Couldn't convert that to a number...")
    
    def on_Exit(self):
        '''
        
        This function run when the step is completed
        
        '''
        
        pass
        
        
Пример #4
0
class FrontPage(Screen, MakesmithInitFuncs):
    textconsole = ObjectProperty(None)
    connectmenu = ObjectProperty(
        None)  #make ConnectMenu object accessible at this scope
    gcodecanvas = ObjectProperty(None)
    screenControls = ObjectProperty(None)

    connectionStatus = StringProperty("Not Connected")

    xReadoutPos = StringProperty("0 mm")
    yReadoutPos = StringProperty("0 mm")
    zReadoutPos = StringProperty("0 mm")

    percentComplete = StringProperty("0.0%")

    numericalPosX = 0.0
    numericalPosY = 0.0

    stepsizeval = 0
    zStepSizeVal = .1

    consoleText = StringProperty(" ")

    units = StringProperty("MM")
    gcodeLineNumber = StringProperty('0')

    def __init__(self, data, **kwargs):
        super(FrontPage, self).__init__(**kwargs)
        self.data = data

    def buildReadoutString(self, value):
        '''
        
        Generate the string for the the digital position readout
        
        '''

        targetStringLength = 8
        string = '%.2f' % (value)

        numberOfSpacesToPad = int(1.5 * (targetStringLength - len(string)))

        string = ' ' * numberOfSpacesToPad + string

        return string

    def setPosReadout(self, xPos, yPos, zPos):
        self.xReadoutPos = self.buildReadoutString(xPos)
        self.yReadoutPos = self.buildReadoutString(yPos)
        self.zReadoutPos = self.buildReadoutString(zPos)
        self.numericalPosX = xPos
        self.numericalPosY = yPos

    def setUpData(self, data):
        self.gcodecanvas.setUpData(data)
        self.screenControls.setUpData(data)
        self.data.bind(connectionStatus=self.updateConnectionStatus)
        self.data.bind(units=self.onUnitsSwitch)
        self.data.bind(gcodeIndex=self.onIndexMove)
        self.data.bind(gcodeFile=self.onGcodeFileChange)
        self.data.bind(uploadFlag=self.onUploadFlagChange)

    def updateConnectionStatus(self, callback, connected):

        if connected:
            self.connectionStatus = "Connected"
        else:
            self.connectionStatus = "Connection Lost"

    def switchUnits(self):
        if self.data.units == "INCHES":
            self.data.units = "MM"
        else:
            self.data.units = "INCHES"

    def onUnitsSwitch(self, callback, newUnits):
        self.units = newUnits
        INCHESTOMM = 1 / 25.4
        MMTOINCHES = 25.4

        if newUnits == "INCHES":
            self.data.gcode_queue.put('G20 ')
            self.moveDistInput.text = str(float(self.moveDistInput.text) / 25)
            self.data.tolerance = 0.020
        else:
            self.data.gcode_queue.put('G21 ')
            self.moveDistInput.text = str(float(self.moveDistInput.text) * 25)
            self.data.tolerance = 0.5

    def onIndexMove(self, callback, newIndex):
        self.gcodeLineNumber = str(newIndex)
        self.percentComplete = '%.1f' % (100 *
                                         (float(newIndex) /
                                          (len(self.data.gcode) - 1))) + "%"

    def onGcodeFileChange(self, callback, newGcode):
        pass

    def onUploadFlagChange(self, callback, newFlagValue):
        if self.data.uploadFlag is 0 and self.data.gcodeIndex > 1:  #if the machine is stopped partway through a file
            self.holdBtn.text = "CONTINUE"
        else:
            self.holdBtn.text = "HOLD"

    def moveGcodeZ(self, moves):
        '''
        Move the gcode index by z moves
        '''

        dist = 0

        for index, zMove in enumerate(self.data.zMoves):
            if moves > 0 and zMove > self.data.gcodeIndex:
                dist = self.data.zMoves[index + moves -
                                        1] - self.data.gcodeIndex
                break
            if moves < 0 and zMove < self.data.gcodeIndex:
                dist = self.data.zMoves[index + moves +
                                        1] - self.data.gcodeIndex

        self.moveGcodeIndex(dist)

    def moveGcodeIndex(self, dist):
        '''
        Move the gcode index by a dist number of lines
        '''
        maxIndex = len(self.data.gcode) - 1
        targetIndex = self.data.gcodeIndex + dist

        #check to see if we are still within the length of the file
        if maxIndex < 0:  #break if there is no data to read
            return
        elif targetIndex < 0:  #negative index not allowed
            self.data.gcodeIndex = 0
        elif targetIndex > maxIndex:  #reading past the end of the file is not allowed
            self.data.gcodeIndex = maxIndex
        else:
            self.data.gcodeIndex = targetIndex

        gCodeLine = self.data.gcode[self.data.gcodeIndex]

        xTarget = 0
        yTarget = 0

        try:
            x = re.search("X(?=.)([+-]?([0-9]*)(\.([0-9]+))?)", gCodeLine)
            if x:
                xTarget = float(x.groups()[0])
            else:
                if self.data.units == "INCHES":
                    xTarget = self.gcodecanvas.positionIndicator.pos[0] / 25.4
                else:
                    xTarget = self.gcodecanvas.positionIndicator.pos[0]

            y = re.search("Y(?=.)([+-]?([0-9]*)(\.([0-9]+))?)", gCodeLine)
            if y:
                yTarget = float(y.groups()[0])
            else:
                if self.data.units == "INCHES":
                    yTarget = self.gcodecanvas.positionIndicator.pos[1] / 25.4
                else:
                    yTarget = self.gcodecanvas.positionIndicator.pos[1]

            self.gcodecanvas.positionIndicator.setPos(xTarget, yTarget,
                                                      self.data.units)
        except:
            print "Unable to update position for new gcode line"

    def pause(self):
        if self.holdBtn.text == "HOLD":
            self.data.uploadFlag = 0
            print("Run Paused")
        else:
            self.data.uploadFlag = 1
            self.data.quick_queue.put(
                "~")  #send cycle resume command to unpause the machine
            print("Run Resumed")

    def jmpsize(self):
        try:
            self.stepsizeval = float(self.moveDistInput.text)
        except:
            pass

    def test(self):
        print "test has no current function"

    def upLeft(self):
        self.jmpsize()
        self.data.gcode_queue.put("G91 G00 X" + str(-1 * self.stepsizeval) +
                                  " Y" + str(self.stepsizeval) + " G90 ")

    def upRight(self):
        self.jmpsize()
        self.data.gcode_queue.put("G91 G00 X" + str(self.stepsizeval) + " Y" +
                                  str(self.stepsizeval) + " G90 ")

    def up(self):
        self.jmpsize()
        self.data.gcode_queue.put("G91 G00 Y" + str(self.stepsizeval) +
                                  " G90 ")

    def left(self):
        self.jmpsize()
        self.data.gcode_queue.put("G91 G0 X" + str(-1 * self.stepsizeval) +
                                  " G90 ")

    def right(self):
        self.jmpsize()
        self.data.gcode_queue.put("G91 G0 X" + str(self.stepsizeval) + " G90 ")

    def downLeft(self):
        self.jmpsize()
        self.data.gcode_queue.put("G91 G00 X" + str(-1 * self.stepsizeval) +
                                  " Y" + str(-1 * self.stepsizeval) + " G90 ")

    def down(self):
        self.jmpsize()
        self.data.gcode_queue.put("G91 G00 Y" + str(-1 * self.stepsizeval) +
                                  " G90 ")

    def downRight(self):
        self.jmpsize()
        self.data.gcode_queue.put("G91 G00 X" + str(self.stepsizeval) + " Y" +
                                  str(-1 * self.stepsizeval) + " G90 ")

    def zAxisPopup(self):
        self.popupContent = ZAxisPopupContent(done=self.dismissZAxisPopup)
        self.popupContent.data = self.data
        self.popupContent.initialize(self.zStepSizeVal)
        self._popup = Popup(title="Z-Axis",
                            content=self.popupContent,
                            size_hint=(0.5, 0.5))
        self._popup.open()

    def dismissZAxisPopup(self):
        '''
        
        Close The Z-Axis Pop-up
        
        '''
        try:
            self.zStepSizeVal = float(self.popupContent.distBtn.text)
        except:
            pass
        self._popup.dismiss()

    def home(self):
        '''
        
        Return the machine to it's home position. (0,0) is the default unless the 
        origin has been moved by the user.
        
        '''

        self.data.gcode_queue.put("G90  ")

        #if the machine has a z-axis lift it then go home
        if int(self.data.config.get('Maslow Settings', 'zAxis')):
            if self.units == "INCHES":
                self.data.gcode_queue.put("G00 Z.25 ")
            else:
                self.data.gcode_queue.put("G00 Z5.0 ")

            self.data.gcode_queue.put("G00 X" + str(self.data.gcodeShift[0]) +
                                      " Y" + str(self.data.gcodeShift[1]) +
                                      " ")

            self.data.gcode_queue.put("G00 Z0 ")
        #if the machine does not have a z-axis, just go home
        else:
            self.data.gcode_queue.put("G00 X" + str(self.data.gcodeShift[0]) +
                                      " Y" + str(self.data.gcodeShift[1]) +
                                      " ")

    def moveOrigin(self):
        '''
        
        Move the gcode origin to the current location
        
        '''
        self.data.gcodeShift = [self.numericalPosX, self.numericalPosY]
        self.data.config.set('Advanced Settings', 'homeX',
                             str(self.numericalPosX))
        self.data.config.set('Advanced Settings', 'homeY',
                             str(self.numericalPosY))
        self.data.config.write()

    def startRun(self):

        self.data.uploadFlag = 1
        self.sendLine()

    def sendLine(self):
        try:
            self.data.gcode_queue.put(self.data.gcode[self.data.gcodeIndex])
            self.data.gcodeIndex = self.data.gcodeIndex + 1
        except:
            print "gcode run complete"
            self.gcodecanvas.uploadFlag = 0
            self.data.gcodeIndex = 0

    def stopRun(self):
        self.data.uploadFlag = 0
        self.data.gcodeIndex = 0
        self.data.quick_queue.put("!")
        with self.data.gcode_queue.mutex:
            self.data.gcode_queue.queue.clear()
        self.onUploadFlagChange(self.stopRun, 0)
        print("Gode Stopped")

    def textInputPopup(self, target):

        self.targetWidget = target

        self.popupContent = TouchNumberInput(done=self.dismiss_popup)
        self._popup = Popup(title="Change increment size of machine movement",
                            content=self.popupContent,
                            size_hint=(0.9, 0.9))
        self._popup.open()
        if global_variables._keyboard:
            global_variables._keyboard.bind(on_key_down=self.keydown_popup)
            self._popup.bind(on_dismiss=self.ondismiss_popup)

    def ondismiss_popup(self, event):
        if global_variables._keyboard:
            global_variables._keyboard.unbind(on_key_down=self.keydown_popup)

    def keydown_popup(self, keyboard, keycode, text, modifiers):
        if (keycode[1] == '0') or (keycode[1] == 'numpad0'):
            self.popupContent.addText('0')
        elif (keycode[1] == '1') or (keycode[1] == 'numpad1'):
            self.popupContent.addText('1')
        elif (keycode[1] == '2') or (keycode[1] == 'numpad2'):
            self.popupContent.addText('2')
        elif (keycode[1] == '3') or (keycode[1] == 'numpad3'):
            self.popupContent.addText('3')
        elif (keycode[1] == '4') or (keycode[1] == 'numpad4'):
            self.popupContent.addText('4')
        elif (keycode[1] == '5') or (keycode[1] == 'numpad5'):
            self.popupContent.addText('5')
        elif (keycode[1] == '6') or (keycode[1] == 'numpad6'):
            self.popupContent.addText('6')
        elif (keycode[1] == '7') or (keycode[1] == 'numpad7'):
            self.popupContent.addText('7')
        elif (keycode[1] == '8') or (keycode[1] == 'numpad8'):
            self.popupContent.addText('8')
        elif (keycode[1] == '9') or (keycode[1] == 'numpad9'):
            self.popupContent.addText('9')
        elif (keycode[1] == '.') or (keycode[1] == 'numpaddecimal'):
            self.popupContent.addText('.')
        elif (keycode[1] == 'backspace'):
            self.popupContent.textInput.text = self.popupContent.textInput.text[:
                                                                                -1]
        elif (keycode[1] == 'enter') or (keycode[1] == 'numpadenter'):
            self.popupContent.done()
        elif (keycode[1] == 'escape'
              ):  # abort entering a number, keep the old number
            self.popupContent.textInput.text = ''  # clear text so it isn't converted to a number
            self.popupContent.done()
        return True  # always swallow keypresses since this is a modal dialog

    def dismiss_popup(self):
        '''
        
        Close The Pop-up
        
        '''
        try:
            tempfloat = float(self.popupContent.textInput.text)
            self.targetWidget.text = str(
                tempfloat
            )  # Update displayed text using standard numeric format
        except:
            pass  #If what was entered cannot be converted to a number, leave the value the same
        self._popup.dismiss()

    def gotoLinePopup(self):

        self.popupContent = TouchNumberInput(done=self.dismiss_gotoLinePopup)
        self._popup = Popup(title="Go to gcode line",
                            content=self.popupContent,
                            size_hint=(0.9, 0.9))
        self._popup.open()
        if global_variables._keyboard:
            global_variables._keyboard.bind(on_key_down=self.keydown_popup)
            self._popup.bind(on_dismiss=self.ondismiss_popup)

    def dismiss_gotoLinePopup(self):
        '''
        
        Close The Pop-up
        
        '''
        try:
            line = int(float(self.popupContent.textInput.text))
            if line < 0:
                self.data.gcodeIndex = 0
            elif line > len(self.data.gcode):
                self.data.gcodeIndex = len(self.data.gcode)
            else:
                self.data.gcodeIndex = line

        except:
            pass  #If what was entered cannot be converted to a number, leave the value the same
        self._popup.dismiss()

    def macro(self, index):
        '''
        Execute user defined macro
        '''
        self.data.gcode_queue.put(
            self.data.config.get('Maslow Settings', 'macro' + str(index)))
class RotationRadiusGuess(GridLayout):
    readyToMoveOn = ObjectProperty(None)

    def on_Enter(self):
        '''
        
        This function runs when the step is entered
        
        '''
        self.data = App.get_running_app().data

    def textInputPopup(self, target):
        self.targetWidget = target

        self.popupContent = TouchNumberInput(done=self.dismiss_popup,
                                             data=self.data)
        self.popupContent.forceUnitsMM()
        self._popup = Popup(title="Set rotation radius",
                            content=self.popupContent,
                            size_hint=(0.9, 0.9))
        self._popup.open()
        if global_variables._keyboard:
            global_variables._keyboard.bind(on_key_down=self.keydown_popup)
            self._popup.bind(on_dismiss=self.ondismiss_popup)

    def ondismiss_popup(self, event):
        if global_variables._keyboard:
            global_variables._keyboard.unbind(on_key_down=self.keydown_popup)

    def keydown_popup(self, keyboard, keycode, text, modifiers):
        if (keycode[1] == '0') or (keycode[1] == 'numpad0'):
            self.popupContent.addText('0')
        elif (keycode[1] == '1') or (keycode[1] == 'numpad1'):
            self.popupContent.addText('1')
        elif (keycode[1] == '2') or (keycode[1] == 'numpad2'):
            self.popupContent.addText('2')
        elif (keycode[1] == '3') or (keycode[1] == 'numpad3'):
            self.popupContent.addText('3')
        elif (keycode[1] == '4') or (keycode[1] == 'numpad4'):
            self.popupContent.addText('4')
        elif (keycode[1] == '5') or (keycode[1] == 'numpad5'):
            self.popupContent.addText('5')
        elif (keycode[1] == '6') or (keycode[1] == 'numpad6'):
            self.popupContent.addText('6')
        elif (keycode[1] == '7') or (keycode[1] == 'numpad7'):
            self.popupContent.addText('7')
        elif (keycode[1] == '8') or (keycode[1] == 'numpad8'):
            self.popupContent.addText('8')
        elif (keycode[1] == '9') or (keycode[1] == 'numpad9'):
            self.popupContent.addText('9')
        elif (keycode[1] == '.') or (keycode[1] == 'numpaddecimal'):
            self.popupContent.addText('.')
        elif (keycode[1] == 'backspace'):
            self.popupContent.textInput.text = self.popupContent.textInput.text[:
                                                                                -1]
        elif (keycode[1] == 'enter') or (keycode[1] == 'numpadenter'):
            self.popupContent.done()
        elif (keycode[1] == 'escape'
              ):  # abort entering a number, keep the old number
            self.popupContent.textInput.text = ''  # clear text so it isn't converted to a number
            self.popupContent.done()
        return True  # always swallow keypresses since this is a modal dialog

    def dismiss_popup(self):
        try:
            tempfloat = float(self.popupContent.textInput.text)
            self.targetWidget.text = str(
                tempfloat
            )  # Update displayed text using standard numeric format
            #self.distText.text = "Dist (" + self.data.units + "):"
        except:
            pass  # If what was entered cannot be converted to a number, leave the value the same
        self._popup.dismiss()

    def enterValues(self):
        try:
            dist = float(self.enterMeasurement.text)
            self.data.config.set('Advanced Settings', 'rotationRadius',
                                 str(dist))
            print "setting rotation radius to: " + str(dist)
            print self.data.config.get('Advanced Settings', 'rotationRadius')
            self.readyToMoveOn()
        except:
            self.data.message_queue.put(
                "Message: Couldn't convert that to a number...")

    def on_Exit(self):
        '''
        
        This function run when the step is completed
        
        '''

        pass
Пример #6
0
class FrontPage(Screen, MakesmithInitFuncs):
    textconsole = ObjectProperty(None)
    connectmenu = ObjectProperty(
        None)  #make ConnectMenu object accessible at this scope
    gcodecanvas = ObjectProperty(None)
    screenControls = ObjectProperty(None)

    connectionStatus = StringProperty("Not Connected")

    xReadoutPos = StringProperty("0 mm")
    yReadoutPos = StringProperty("0 mm")
    zReadoutPos = StringProperty("0 mm")
    ReadoutVel = StringProperty(" 0 mm/m")
    gcodeVel = StringProperty(" 0 mm/m")
    percentComplete = StringProperty("0.0%")

    numericalPosX = 0.0
    numericalPosY = 0.0

    previousPosX = 0.0
    previousPosY = 0.0

    lastpos = (0, 0, 0)
    lasttime = 0.0
    tick = 0

    stepsizeval = 0

    consoleText = StringProperty(" ")

    units = StringProperty("MM")
    gcodeLineNumber = StringProperty('0')

    data = Data()

    def __init__(self, data, **kwargs):
        super(FrontPage, self).__init__(**kwargs)
        self.data = data

        self.upLeftArrow.btnBackground = self.data.iconPath + 'UpLeftArrow.png'
        self.upArrow.btnBackground = self.data.iconPath + 'UpArrow.png'
        self.upRightArrow.btnBackground = self.data.iconPath + 'UpRightArrow.png'
        self.leftArrow.btnBackground = self.data.iconPath + 'LeftArrow.png'
        self.homeBtn.btnBackground = self.data.iconPath + 'Home.png'
        self.rightArrow.btnBackground = self.data.iconPath + 'RightArrow.png'
        self.downLeftArrow.btnBackground = self.data.iconPath + 'DownLeftArrow.png'
        self.downArrow.btnBackground = self.data.iconPath + 'DownArrow.png'
        self.downRightArrow.btnBackground = self.data.iconPath + 'DownRightArrow.png'

        self.macro1Btn.btnBackground = self.data.iconPath + 'Generic.png'
        self.macro1Btn.textColor = self.data.fontColor
        self.macro2Btn.btnBackground = self.data.iconPath + 'Generic.png'
        self.macro2Btn.textColor = self.data.fontColor
        self.zAxisBtn.btnBackground = self.data.iconPath + 'Generic.png'
        self.zAxisBtn.textColor = self.data.fontColor
        self.moveDistInput.btnBackground = self.data.iconPath + 'Generic.png'
        self.moveDistInput.textColor = self.data.fontColor
        self.unitsBtn.btnBackground = self.data.iconPath + 'Generic.png'
        self.unitsBtn.textColor = self.data.fontColor
        self.defHomeBtn.btnBackground = self.data.iconPath + 'Generic.png'
        self.defHomeBtn.textColor = self.data.fontColor
        self.zRight.btnBackground = self.data.iconPath + 'Generic.png'
        self.zRight.textColor = self.data.fontColor
        self.zLeft.btnBackground = self.data.iconPath + 'Generic.png'
        self.zLeft.textColor = self.data.fontColor
        self.oneLeft.btnBackground = self.data.iconPath + 'Generic.png'
        self.oneLeft.textColor = self.data.fontColor
        self.oneRight.btnBackground = self.data.iconPath + 'Generic.png'
        self.oneRight.textColor = self.data.fontColor

        self.run.btnBackground = self.data.iconPath + 'RunGreen.png'
        self.holdBtn.btnBackground = self.data.iconPath + 'HoldYellow.png'
        self.holdBtn.secretText = "HOLD"
        self.stopBtn.btnBackground = self.data.iconPath + 'StopRed.png'

        self.goTo.btnBackground = self.data.iconPath + 'GoTo.png'

    def buildReadoutString(self, value):
        '''
        
        Generate the string for the the digital position readout
        
        '''

        targetStringLength = 8
        string = '%.2f' % (value)

        numberOfSpacesToPad = int(1.5 * (targetStringLength - len(string)))

        string = ' ' * numberOfSpacesToPad + string

        return string

    def setPosReadout(self, xPos, yPos, zPos):
        self.xReadoutPos = self.buildReadoutString(xPos)
        self.yReadoutPos = self.buildReadoutString(yPos)
        self.zReadoutPos = self.buildReadoutString(zPos)

        #So other widgets can access the position
        self.data.zReadoutPos = zPos

        #Current Velocity is done here now, not in the firmware.
        self.tick += 1
        if self.tick >= 4:  #Can't do this every time... it's too noisy, so we do it every 5rd time (0.1s).
            self.tick = 0
            if self.lasttime <> 0.0:
                try:
                    delta = sqrt((xPos - self.lastpos[0]) *
                                 (xPos - self.lastpos[0]) +
                                 (yPos - self.lastpos[1]) *
                                 (yPos - self.lastpos[1]) +
                                 (zPos - self.lastpos[2]) *
                                 (zPos - self.lastpos[2]))
                    Vel = delta / (time() -
                                   self.lasttime) * 60.0  #In XXXX/minute
                except:
                    print "unable to compute velocity"
                    Vel = 0
            else:
                Vel = 0

            self.lasttime = time()
            self.lastpos = (xPos, yPos, zPos)

            self.ReadoutVel = self.buildReadoutString(Vel)
        self.numericalPosX = xPos
        self.numericalPosY = yPos

        #ToDo: Do we want to start logging errors if self.RedoutVel < self.gcodeVel?  How do we know if we're supposed to be moving?

    def setUpData(self, data):
        self.gcodecanvas.setUpData(data)
        self.screenControls.setUpData(data)
        self.screenControls.setButtonAppearance()
        self.data.bind(connectionStatus=self.updateConnectionStatus)
        self.data.bind(units=self.onUnitsSwitch)
        self.data.bind(gcodeIndex=self.onIndexMove)
        self.data.bind(gcodeFile=self.onGcodeFileChange)
        self.data.bind(uploadFlag=self.onUploadFlagChange)
        self.update_macro_titles()

    def updateConnectionStatus(self, callback, connected):
        if connected:
            self.connectionStatus = "Connected"
        else:
            self.connectionStatus = "Connection Lost"

    def switchUnits(self):
        if self.data.units == "INCHES":
            self.data.units = "MM"
        else:
            self.data.units = "INCHES"

    def onUnitsSwitch(self, callback, newUnits):
        self.units = newUnits
        INCHESTOMM = 1 / 25.4
        MMTOINCHES = 25.4

        if newUnits == "INCHES":
            self.data.gcode_queue.put('G20 ')
            self.moveDistInput.text = "{0:.2f}".format(
                float(self.moveDistInput.text) / MMTOINCHES)
            self.data.tolerance = 0.020
        else:
            self.data.gcode_queue.put('G21 ')
            self.moveDistInput.text = "{0:.2f}".format(
                float(self.moveDistInput.text) / INCHESTOMM)
            self.data.tolerance = 0.5

    def onIndexMove(self, callback, newIndex):
        self.gcodeLineNumber = str(newIndex)
        self.percentComplete = '%.1f' % (100 *
                                         (float(newIndex) /
                                          (len(self.data.gcode) - 1))) + "%"
        if newIndex >= 1:
            gCodeLine = self.data.gcode[
                newIndex -
                1]  #We're executing newIndex-1... about to send newIndex
            F = re.search("F(?=.)(([ ]*)?[+-]?([0-9]*)(\.([0-9]+))?)",
                          gCodeLine)
            if F:
                self.gcodeVel = F.groups()[
                    0]  #Otherwise, it stays what it was...

    def onGcodeFileChange(self, callback, newGcode):
        pass

    def onUploadFlagChange(self, callback, newFlagValue):
        if self.data.uploadFlag is 0 and self.data.gcodeIndex > 1:  #if the machine is stopped partway through a file
            self.holdBtn.secretText = "CONTINUE"
            self.holdBtn.btnBackground = self.data.iconPath + 'ContinueYellow.png'
        else:
            self.holdBtn.secretText = "HOLD"
            self.holdBtn.btnBackground = self.data.iconPath + 'HoldYellow.png'

    def moveGcodeZ(self, moves):
        '''
        Move the gcode index by z moves
        '''

        dist = 0

        for index, zMove in enumerate(self.data.zMoves):
            if moves > 0 and zMove > self.data.gcodeIndex:
                dist = self.data.zMoves[index + moves -
                                        1] - self.data.gcodeIndex
                break
            if moves < 0 and zMove < self.data.gcodeIndex:
                dist = self.data.zMoves[index + moves +
                                        1] - self.data.gcodeIndex

        self.moveGcodeIndex(dist)

    def moveGcodeIndex(self, dist):
        '''
        Move the gcode index by a dist number of lines
        '''
        maxIndex = len(self.data.gcode) - 1
        targetIndex = self.data.gcodeIndex + dist

        #check to see if we are still within the length of the file
        if maxIndex < 0:  #break if there is no data to read
            return
        elif targetIndex < 0:  #negative index not allowed
            self.data.gcodeIndex = 0
        elif targetIndex > maxIndex:  #reading past the end of the file is not allowed
            self.data.gcodeIndex = maxIndex
        else:
            self.data.gcodeIndex = targetIndex

        gCodeLine = self.data.gcode[self.data.gcodeIndex]

        xTarget = 0
        yTarget = 0

        try:
            x = re.search("X(?=.)([+-]?([0-9]*)(\.([0-9]+))?)", gCodeLine)
            if x:
                xTarget = float(x.groups()[0])
                self.previousPosX = xTarget
            else:
                xTarget = self.previousPosX

            y = re.search("Y(?=.)([+-]?([0-9]*)(\.([0-9]+))?)", gCodeLine)
            if y:
                yTarget = float(y.groups()[0])
                self.previousPosY = yTarget
            else:
                yTarget = self.previousPosY

            self.gcodecanvas.positionIndicator.setPos(xTarget, yTarget,
                                                      self.data.units)
        except:
            print "Unable to update position for new gcode line"

    def pause(self):
        if self.holdBtn.secretText == "HOLD":
            self.data.uploadFlag = 0
            print("Run Paused")
        else:
            self.data.uploadFlag = 1
            self.data.quick_queue.put(
                "~")  #send cycle resume command to unpause the machine
            print("Run Resumed")

    def jmpsize(self):
        try:
            self.stepsizeval = float(self.moveDistInput.text)
        except:
            pass

    def test(self):
        print "test has no current function"

    def upLeft(self):
        self.jmpsize()
        self.data.gcode_queue.put("G91 G00 X" + str(-1 * self.stepsizeval) +
                                  " Y" + str(self.stepsizeval) + " G90 ")
        self.gcodeVel = "[MAN]"

    def upRight(self):
        self.jmpsize()
        self.data.gcode_queue.put("G91 G00 X" + str(self.stepsizeval) + " Y" +
                                  str(self.stepsizeval) + " G90 ")
        self.gcodeVel = "[MAN]"

    def up(self):
        self.jmpsize()
        self.data.gcode_queue.put("G91 G00 Y" + str(self.stepsizeval) +
                                  " G90 ")
        self.gcodeVel = "[MAN]"

    def left(self):
        self.jmpsize()
        self.data.gcode_queue.put("G91 G0 X" + str(-1 * self.stepsizeval) +
                                  " G90 ")
        self.gcodeVel = "[MAN]"

    def right(self):
        self.jmpsize()
        self.data.gcode_queue.put("G91 G0 X" + str(self.stepsizeval) + " G90 ")
        self.gcodeVel = "[MAN]"

    def downLeft(self):
        self.jmpsize()
        self.data.gcode_queue.put("G91 G00 X" + str(-1 * self.stepsizeval) +
                                  " Y" + str(-1 * self.stepsizeval) + " G90 ")
        self.gcodeVel = "[MAN]"

    def down(self):
        self.jmpsize()
        self.data.gcode_queue.put("G91 G00 Y" + str(-1 * self.stepsizeval) +
                                  " G90 ")
        self.gcodeVel = "[MAN]"

    def downRight(self):
        self.jmpsize()
        self.data.gcode_queue.put("G91 G00 X" + str(self.stepsizeval) + " Y" +
                                  str(-1 * self.stepsizeval) + " G90 ")
        self.gcodeVel = "[MAN]"

    def zAxisPopup(self):
        self.popupContent = ZAxisPopupContent(done=self.dismissZAxisPopup)
        self.popupContent.data = self.data
        self.popupContent.initialize()
        self._popup = Popup(title="Z-Axis",
                            content=self.popupContent,
                            size_hint=(0.5, 0.5))
        self._popup.open()

    def dismissZAxisPopup(self):
        '''
        
        Close The Z-Axis Pop-up
        
        '''
        self._popup.dismiss()

    def home(self):
        '''
        
        Return the machine to it's home position. (0,0) is the default unless the 
        origin has been moved by the user.
        
        '''

        self.data.gcode_queue.put("G90  ")
        self.gcodeVel = "[MAN]"

        safeHeightMM = float(
            self.data.config.get('Maslow Settings', 'zAxisSafeHeight'))
        safeHeightInches = safeHeightMM / 25.5
        if self.data.units == "INCHES":
            self.data.gcode_queue.put("G00 Z" + '%.3f' % (safeHeightInches))
        else:
            self.data.gcode_queue.put("G00 Z" + str(safeHeightMM))

        self.data.gcode_queue.put("G00 X" + str(self.data.gcodeShift[0]) +
                                  " Y" + str(self.data.gcodeShift[1]) + " ")

        self.data.gcode_queue.put("G00 Z0 ")

    def moveOrigin(self):
        '''
        
        Move the gcode origin to the current location
        
        '''
        self.data.gcodeShift = [self.numericalPosX, self.numericalPosY]
        self.data.config.set('Advanced Settings', 'homeX',
                             str(self.numericalPosX))
        self.data.config.set('Advanced Settings', 'homeY',
                             str(self.numericalPosY))

    def startRun(self):

        self.data.uploadFlag = 1
        self.sendLine()

    def sendLine(self):
        try:
            self.data.gcode_queue.put(self.data.gcode[self.data.gcodeIndex])
            self.data.gcodeIndex = self.data.gcodeIndex + 1
        except:
            print "gcode run complete"
            self.gcodecanvas.uploadFlag = 0
            self.data.gcodeIndex = 0

    def stopRun(self):
        self.data.uploadFlag = 0
        self.data.gcodeIndex = 0
        self.data.quick_queue.put("!")
        with self.data.gcode_queue.mutex:
            self.data.gcode_queue.queue.clear()
        self.onUploadFlagChange(self.stopRun, 0)
        print("Gcode Stopped")

    def textInputPopup(self, target):

        self.targetWidget = target

        self.popupContent = TouchNumberInput(done=self.dismiss_popup,
                                             data=self.data)
        self._popup = Popup(title="Change increment size of machine movement",
                            content=self.popupContent,
                            size_hint=(0.9, 0.9))
        self._popup.open()
        if global_variables._keyboard:
            global_variables._keyboard.bind(on_key_down=self.keydown_popup)
            self._popup.bind(on_dismiss=self.ondismiss_popup)

    def ondismiss_popup(self, event):
        if global_variables._keyboard:
            global_variables._keyboard.unbind(on_key_down=self.keydown_popup)

    def keydown_popup(self, keyboard, keycode, text, modifiers):
        if (keycode[1] == '0') or (keycode[1] == 'numpad0'):
            self.popupContent.addText('0')
        elif (keycode[1] == '1') or (keycode[1] == 'numpad1'):
            self.popupContent.addText('1')
        elif (keycode[1] == '2') or (keycode[1] == 'numpad2'):
            self.popupContent.addText('2')
        elif (keycode[1] == '3') or (keycode[1] == 'numpad3'):
            self.popupContent.addText('3')
        elif (keycode[1] == '4') or (keycode[1] == 'numpad4'):
            self.popupContent.addText('4')
        elif (keycode[1] == '5') or (keycode[1] == 'numpad5'):
            self.popupContent.addText('5')
        elif (keycode[1] == '6') or (keycode[1] == 'numpad6'):
            self.popupContent.addText('6')
        elif (keycode[1] == '7') or (keycode[1] == 'numpad7'):
            self.popupContent.addText('7')
        elif (keycode[1] == '8') or (keycode[1] == 'numpad8'):
            self.popupContent.addText('8')
        elif (keycode[1] == '9') or (keycode[1] == 'numpad9'):
            self.popupContent.addText('9')
        elif (keycode[1] == '.') or (keycode[1] == 'numpaddecimal'):
            self.popupContent.addText('.')
        elif (keycode[1] == 'backspace'):
            self.popupContent.textInput.text = self.popupContent.textInput.text[:
                                                                                -1]
        elif (keycode[1] == 'enter') or (keycode[1] == 'numpadenter'):
            self.popupContent.done()
        elif (keycode[1] == 'escape'
              ):  # abort entering a number, keep the old number
            self.popupContent.textInput.text = ''  # clear text so it isn't converted to a number
            self.popupContent.done()
        return True  # always swallow keypresses since this is a modal dialog

    def dismiss_popup(self):
        '''
        
        Close The Pop-up
        
        '''
        try:
            tempfloat = float(self.popupContent.textInput.text)
            self.targetWidget.text = str(
                tempfloat
            )  # Update displayed text using standard numeric format
        except:
            pass  #If what was entered cannot be converted to a number, leave the value the same
        self._popup.dismiss()

    def gotoLinePopup(self):

        self.popupContent = TouchNumberInput(done=self.dismiss_gotoLinePopup,
                                             data=self.data)
        self._popup = Popup(title="Go to gcode line",
                            content=self.popupContent,
                            size_hint=(0.9, 0.9))
        self._popup.open()
        if global_variables._keyboard:
            global_variables._keyboard.bind(on_key_down=self.keydown_popup)
            self._popup.bind(on_dismiss=self.ondismiss_popup)

    def dismiss_gotoLinePopup(self):
        '''
        
        Close The Pop-up
        
        '''
        try:
            line = int(float(self.popupContent.textInput.text))
            if line < 0:
                self.data.gcodeIndex = 0
            elif line > len(self.data.gcode):
                self.data.gcodeIndex = len(self.data.gcode)
            else:
                self.data.gcodeIndex = line

        except:
            pass  #If what was entered cannot be converted to a number, leave the value the same
        self._popup.dismiss()

    def macro(self, index):
        '''
        Execute user defined macro
        '''
        self.data.gcode_queue.put(
            self.data.config.get('Maslow Settings', 'macro' + str(index)))

    def update_macro_titles(self):
        self.macro1Btn.text = self.data.config.get('Maslow Settings',
                                                   'macro1_title')
        self.macro2Btn.text = self.data.config.get('Maslow Settings',
                                                   'macro2_title')
class ZAxisPopupContent(GridLayout):
    done   = ObjectProperty(None)
    
    def initialize(self, zStepSizeVal):
        '''
        
        Initialize the z-axis popup
        
        '''
        self.unitsBtn.text = self.data.units
        self.distBtn.text  = str(zStepSizeVal)
    
    def setDist(self):
        self.popupContent = TouchNumberInput(done=self.dismiss_popup)
        self._popup = Popup(title="Change increment size of machine movement", content=self.popupContent,
                            size_hint=(0.9, 0.9))
        self._popup.open()
        if global_variables._keyboard:
            global_variables._keyboard.bind(on_key_down=self.keydown_popup)
            self._popup.bind(on_dismiss=self.ondismiss_popup)

    def ondismiss_popup(self, event):
        if global_variables._keyboard:
            global_variables._keyboard.unbind(on_key_down=self.keydown_popup)

    def keydown_popup(self, keyboard, keycode, text, modifiers):
        if (keycode[1] == '0') or (keycode[1] =='numpad0'):
            self.popupContent.addText('0')
        elif (keycode[1] == '1') or (keycode[1] =='numpad1'):
            self.popupContent.addText('1')
        elif (keycode[1] == '2') or (keycode[1] =='numpad2'):
            self.popupContent.addText('2')
        elif (keycode[1] == '3') or (keycode[1] =='numpad3'):
            self.popupContent.addText('3')
        elif (keycode[1] == '4') or (keycode[1] =='numpad4'):
            self.popupContent.addText('4')
        elif (keycode[1] == '5') or (keycode[1] =='numpad5'):
            self.popupContent.addText('5')
        elif (keycode[1] == '6') or (keycode[1] =='numpad6'):
            self.popupContent.addText('6')
        elif (keycode[1] == '7') or (keycode[1] =='numpad7'):
            self.popupContent.addText('7')
        elif (keycode[1] == '8') or (keycode[1] =='numpad8'):
            self.popupContent.addText('8')
        elif (keycode[1] == '9') or (keycode[1] =='numpad9'):
            self.popupContent.addText('9')
        elif (keycode[1] == '.') or (keycode[1] =='numpaddecimal'):
            self.popupContent.addText('.')
        elif (keycode[1] == 'backspace'):
            self.popupContent.textInput.text = self.popupContent.textInput.text[:-1]         
        elif (keycode[1] == 'enter') or (keycode[1] =='numpadenter'):
            self.popupContent.done()
        elif (keycode[1] == 'escape'):     # abort entering a number, keep the old number
            self.popupContent.textInput.text = ''    # clear text so it isn't converted to a number
            self.popupContent.done()
        return True     # always swallow keypresses since this is a modal dialog
    
    def units(self):
        '''
        
        Toggle the machine units.
        
        '''
        if self.data.units == "INCHES":
            self.data.units = "MM"
            self.distBtn.text = str(25*float(self.distBtn.text))
        else:
            self.data.units = "INCHES"
            
            self.distBtn.text = str(float(self.distBtn.text)/25)
        
        self.unitsBtn.text = self.data.units
    
    def zIn(self):
        '''
        
        Move the z-axis in
        
        '''
        self.data.gcode_queue.put("G91 G00 Z" + str(-1*float(self.distBtn.text)) + " G90 ")
    
    def zOut(self):
        '''
        
        Move the z-axis out
        
        '''
        self.data.gcode_queue.put("G91 G00 Z" + str(self.distBtn.text) + " G90 ")
    
    def zero(self):
        '''
        
        Define the z-axis to be currently at height 0
        
        '''
        self.data.gcode_queue.put("G10 Z0 ")
    
    def dismiss_popup(self):
        '''
        
        Close The Pop-up to enter distance information
        
        '''
        try:
            tempfloat = float(self.popupContent.textInput.text)
            self.distBtn.text = str(tempfloat)  # Update displayed text using standard numeric format
        except:
            pass                                                             #If what was entered cannot be converted to a number, leave the value the same
        self._popup.dismiss()
class MeasureDistBetweenMotors(GridLayout):
    '''
    
    Provides a standard interface for measuring the distance between the motors. Assumes that both motors are in position zero at the begining
    
    '''
    data                         =   ObjectProperty(None)
    
    def textInputPopup(self, target):
        self.targetWidget = target

        self.popupContent = TouchNumberInput(done=self.dismiss_popup, data = self.data)
        self.popupContent.forceUnitsMM()
        self._popup = Popup(title="Set distance to move chain", content=self.popupContent,
                            size_hint=(0.9, 0.9))
        self._popup.open()
        if global_variables._keyboard:
            global_variables._keyboard.bind(on_key_down=self.keydown_popup)
            self._popup.bind(on_dismiss=self.ondismiss_popup)
    
    def ondismiss_popup(self, event):
        if global_variables._keyboard:
           global_variables._keyboard.unbind(on_key_down=self.keydown_popup)
        
    def keydown_popup(self, keyboard, keycode, text, modifiers):
        if (keycode[1] == '0') or (keycode[1] =='numpad0'):
            self.popupContent.addText('0')
        elif (keycode[1] == '1') or (keycode[1] =='numpad1'):
            self.popupContent.addText('1')
        elif (keycode[1] == '2') or (keycode[1] =='numpad2'):
            self.popupContent.addText('2')
        elif (keycode[1] == '3') or (keycode[1] =='numpad3'):
            self.popupContent.addText('3')
        elif (keycode[1] == '4') or (keycode[1] =='numpad4'):
            self.popupContent.addText('4')
        elif (keycode[1] == '5') or (keycode[1] =='numpad5'):
            self.popupContent.addText('5')
        elif (keycode[1] == '6') or (keycode[1] =='numpad6'):
            self.popupContent.addText('6')
        elif (keycode[1] == '7') or (keycode[1] =='numpad7'):
            self.popupContent.addText('7')
        elif (keycode[1] == '8') or (keycode[1] =='numpad8'):
            self.popupContent.addText('8')
        elif (keycode[1] == '9') or (keycode[1] =='numpad9'):
            self.popupContent.addText('9')
        elif (keycode[1] == '.') or (keycode[1] =='numpaddecimal'):
            self.popupContent.addText('.')
        elif (keycode[1] == 'backspace'):
            self.popupContent.textInput.text = self.popupContent.textInput.text[:-1]
        elif (keycode[1] == 'enter') or (keycode[1] =='numpadenter'):
            self.popupContent.done()
        elif (keycode[1] == 'escape'):     # abort entering a number, keep the old number
            self.popupContent.textInput.text = ''    # clear text so it isn't converted to a number
            self.popupContent.done()
        return True     # always swallow keypresses since this is a modal dialog
    
    def dismiss_popup(self):
        try:
            tempfloat = float(self.popupContent.textInput.text)
            self.targetWidget.text = str(tempfloat)  # Update displayed text using standard numeric format
            #self.distText.text = "Dist (" + self.data.units + "):"
        except:
            pass  # If what was entered cannot be converted to a number, leave the value the same
        self._popup.dismiss()
    
    def stopCut(self):
        self.data.quick_queue.put("!")
        with self.data.gcode_queue.mutex:
            self.data.gcode_queue.queue.clear()
    
    def extend(self):
        dist = float(self.distToMove.text)
        self.data.gcode_queue.put("G91 ")
        self.data.gcode_queue.put("B09 L" + str(dist) + " ")
        self.data.gcode_queue.put("G90 ")
    
    def retract(self):
        dist = float(self.distToMove.text)
        self.data.gcode_queue.put("G91 ")
        self.data.gcode_queue.put("B09 L" + str(-1*dist) + " ")
        self.data.gcode_queue.put("G90 ")
    
    def measureLeft(self):
        self.data.gcode_queue.put("B10 L")
    
    def readMotorSpacing(self, dist):
        dist = dist - 2*6.35                                #subtract off the extra two links

        print "Read motor spacing: " + str(dist)
        self.data.config.set('Maslow Settings', 'motorSpacingX', str(dist))
        
        #put some slack in the chain
        self.data.gcode_queue.put("G91 ")
        self.data.gcode_queue.put("B09 L10 ")
        self.data.gcode_queue.put("G90 ")
        
        self.readyToMoveOn()
    
    def pullChainTightAndMeasure(self):
        #pull the left chain tight
        self.data.gcode_queue.put("B11 S255 T3 L ")
        #request a measurement
        self.data.gcode_queue.put("B10 L")
    
    def on_Enter(self):
        '''
        
        This function runs when the step is entered
        
        '''
        self.data = App.get_running_app().data
        self.data.measureRequest = self.readMotorSpacing
        
        self.originalChainOverSproketDir = App.get_running_app().data.config.get('Advanced Settings', 'chainOverSprocket')
        
        #pretend we are in the "Top" configuration during this step
        App.get_running_app().data.config.set('Advanced Settings', 'chainOverSprocket', 'Top')
        
        #set the threshold for warning that the machine is off target to 200mm essentially turning it off. We don't want this to trigger when pulling the chain tight
        self.data.gcode_queue.put("$42=2000 ")
    
    def on_Exit(self):
        '''
        
        This function run when the process is completed or quit is pressed
        
        '''
        
        
        #Restore original chain over sprocket direction
        App.get_running_app().data.config.set('Advanced Settings', 'chainOverSprocket', self.originalChainOverSproketDir)
        #restore all settings to the values stored in the current settings file 
        self.data.gcode_queue.put("$$ ")