【问题标题】:Python threads not executing properly on function call (Kivy)Python 线程在函数调用时未正确执行 (Kivy)
【发布时间】:2016-01-23 01:21:22
【问题描述】:

我已将函数调用绑定到 Kivy 按钮,该按钮构建并启动两个线程。我正在使用 Kivy 标签来表明我确实在输入函数“my_function()”并一直执行到最后。似乎没有正确构建线程。

这两个线程每个都旨在转动步进电机。我已经测试了这些电机并且它们可以工作,但是当我尝试将代码放在一个函数中并在变量上应用“self”时,除了最后的标签被更新之外,按钮按下没有任何反应。

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

这是经过验证的真实代码:

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)

将代码放入 my_function() 并应用 'self';线程不再工作。重复第二个线程的代码。我想强调的是,这段代码在不在函数内部且不使用“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"

构建:

class MyApp(App):
    def build(self):
        return ScreenManager()

if __name__ == "__main__":
    MyApp().run()

基维:my.kv

我省略了一些不必要的细节,例如布局和小部件大小。

#: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()

感谢您的宝贵时间!

【问题讨论】:

    标签: python multithreading kivy


    【解决方案1】:

    您将 stepper_worker 定义为 MyScreen 下的一个方法,因此它将获得的第一个参数将是 MyScreen 的一个实例。

    在您的示例中,您应该将其定义为函数或首先添加 self 参数。我假设您的线程不会启动,因为您发送的参数太多

    # 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)],))
    

    上面提到的可能修复:

    #added self...
    def stepper_worker(self, stepper, numsteps, direction, style):
        stepper.step(numsteps, direction, style)
    

    我希望这会有所帮助。也看看你是否真的需要在 MyScreen 下定义这些方法...

    还要注意,如果线程想要访问 kivy 用户界面,他们需要通过调用 Clock 来完成(这样您就可以获得线程安全)

    【讨论】:

    • 在 stepper_worker 中将 self 作为第一个参数添加已修复!谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-24
    • 2021-07-12
    • 1970-01-01
    • 1970-01-01
    • 2014-08-20
    • 2013-03-10
    • 1970-01-01
    相关资源
    最近更新 更多