Reputation: 7
I have bounded a function call to a Kivy button, which builds and starts two threads. I am using a Kivy label to show that I am indeed entering the function 'my_function()' and going through to the end. It seems that the threads aren't being built properly.
The two threads each aim to turn a stepper motor. I have tested these motors and they work, but when I try to place the code in a function and apply 'self' on the variables nothing happens on button press except the label at the end is updated.
Python:
import kivy
kivy.require('1.9.1')
from kivy.app import App
from kivy.uix.screenmanager import Screenmanager, Screen
from kivy.properties import StringProperty
from Adafruit_MotorHAT import Adafruit_MotorHAT, Adafruit_DCMotor, Adafruit_StepperMotor
import threading
import random
import time
import atexit
class ScreenManager(ScreenManager):
pass
class StartMenu(Screen):
pass
This is tried and true code:
class MyScreen(Screen):
entered = StringProperty("Not Entered")
# create a default object, no changes to I2C address or frequency
mh = Adafruit_MotorHAT()
# create empty threads (these will hold the stepper 1 and 2 threads)
st1 = Threading.thread()
st2 = Threading.thread()
# recommended for auto-disabling motors on shutdown!
def turnOffMotors(mh):
mh.getMotor(1).run(Adafruit_MotorHAT.RELEASE)
mh.getMotor(2).run(Adafruit_MotorHAT.RELEASE)
mh.getMotor(3).run(Adafruit_MotorHAT.RELEASE)
mh.getMotor(4).run(Adafruit_MotorHAT.RELEASE)
atexit.register(turnOffMotors(mh))
myStepper1 = mh.getStepper(200, 1) # 200 steps/rev, motor port #1
myStepper2 = mh.getStepper(200, 2) # 200 steps/rev, motor port #1
myStepper1.setSpeed(60) # 30 RPM
myStepper2.setSpeed(60) # 30 RPM
stepstyles = [Adafruit_MotorHAT.SINGLE, Adafruit_MotorHAT.DOUBLE, Adafruit_MotorHAT.INTERLEAVE, Adafruit_MotorHAT.MICROSTEP]
def stepper_worker(stepper, numsteps, direction, style):
stepper.step(numsteps, direction, style)
Placing code in my_function() and applying 'self'; threads no longer work. Repeat code for second thread. I want to emphasize that this code works when not inside a function and not using 'self':
def my_function(self, *args):
if (True):
time.sleep(0.005)
if not self.st1.isAlive():
randomdir = random.randint(0, 1)
if (randomdir == 0):
dir = Adafruit_MotorHAT.FORWARD
else:
dir = Adafruit_MotorHAT.BACKWARD
randomsteps = random.randint(10,50)
self.st1 = threading.Thread(target=self.stepper_worker, args=(self.myStepper1, randomsteps, dir, self.stepstyles[random.randint(0,3)],))
self.st1.start()
self.entered = "Entered"
Build:
class MyApp(App):
def build(self):
return ScreenManager()
if __name__ == "__main__":
MyApp().run()
Kivy: my.kv
I left out some unnecessary details, such as layout and widget size.
#:kivy 1.9.1
<ScreenManager>:
StartMenu:
MyScreen:
<StartMenu>:
name: 'StartMenu'
Button:
on_release:
root.manager.current = 'MyScreen'
<MyScreen>:
name: 'MyScreen'
Label:
text: root.entered
Button:
on_release:
root.my_function()
Thanks for your time!
Upvotes: 0
Views: 517
Reputation: 8066
You are defining stepper_worker as a method under MyScreen, so the first parameter it will get will be an instance of MyScreen.
In you example, you should define it as a function or add the self parameter first. I assume that your thread won't start because you are sending too many parameters
# len(args) == 4 so you have too many parameters (the first is fixed to "self")
self.st1 = threading.Thread(target=self.stepper_worker, args=(self.myStepper1, randomsteps, dir, self.stepstyles[random.randint(0,3)],))
possible fix as mention a above:
#added self...
def stepper_worker(self, stepper, numsteps, direction, style):
stepper.step(numsteps, direction, style)
I hope this will help. also see if you really need to define these methods under MyScreen...
Also notice that if the threads want to access kivy UI they need to do it via calls to Clock (so you'll have thread safety)
Upvotes: 0