kivy: "Exception: Shader didnt link" 当被 gpiozero 回调调用时,而不是被 kivy.uix.button 回调调用
kivy: "Exception: Shader didnt link" when called by gpiozero callback, but not by kivy.uix.button callback
我正在使用 kivy 中的摄像头编写 GUI,但不确定为什么我的代码无法运行。我有一个相机源,以及两种从中捕获图片的方法:一种由 gpiozero
when_pressed
回调触发,另一种由 kivy.uix.button
on_press
回调触发。
kivy.uix.button
回调成功捕获图像,但 gpiozero
回调显示 Exception: Shader didnt link, check info log.
,无法保存图像,然后使相机画面变黑(尽管图像以后仍然可以使用成功选项捕获)。为什么一个回调有效而另一个无效?
这里是相关代码,对应终端输出。我用 # ALL CAPS COMMENTS
注释了终端输出。 (我的代码是受kivy docs camera example的启发,同样捕获成功)。
main.py
import kivy
#kivy.require('1.11.1')
# Uncomment these lines to see all the messages
#from kivy.logger import Logger
#import logging
#Logger.setLevel(logging.TRACE)
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.uix.camera import Camera
from kivy.core.window import Window
from gpiozero import Button as gpiozeroButton # renamed to avoid conflict w/ kivy.uix.button
import time
Window.fullscreen = 'auto' # uses display's current resolution
capture_btn = gpiozeroButton(pin=13, pull_up=False) # set up GPIO
class RootWidget(FloatLayout):
def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)
capture_btn.when_pressed = self.capture # initialize callback for GPIO button
def capture(self):
print('Capture step 1')
camera = self.ids['camera']
print('Capture step 2')
timestr = time.strftime("%Y%m%d_%H%M%S")
print('Capture step 3')
camera.export_to_png("IMG_{}.png".format(timestr))
print("Captured")
class LifterApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
LifterApp().run()
lifter.kv
# #:kivy 1.11.1
<RootWidget>:
Camera:
id: camera
resolution: (640, 480)
play: True
Button:
text: "capture"
pos_hint: {'x':0.0, 'y':0.0}
size_hint: (0.2, 0.2)
on_press: root.capture()
终端输出(带注释)
[INFO ] [Logger ] Record log in /home/pi/.kivy/logs/kivy_20-12-30_27.txt
[INFO ] [Kivy ] v1.11.1
[INFO ] [Kivy ] Installed at "/usr/local/lib/python3.7/dist-packages/kivy/__init__.py"
[INFO ] [Python ] v3.7.3 (default, Dec 20 2019, 18:57:59)
[GCC 8.3.0]
[INFO ] [Python ] Interpreter at "/usr/bin/python3"
[INFO ] [Factory ] 184 symbols loaded
[INFO ] [Image ] Providers: img_tex, img_dds, img_sdl2, img_pil, img_gif (img_ffpyplayer ignored)
[INFO ] [Text ] Provider: sdl2(['text_pango'] ignored)
[INFO ] [Camera ] Provider: picamera
[INFO ] [Window ] Provider: sdl2(['window_egl_rpi'] ignored)
[INFO ] [GL ] Using the "OpenGL" graphics system
[INFO ] [GL ] Backend used <sdl2>
[INFO ] [GL ] OpenGL version <b'3.1 Mesa 19.3.2'>
[INFO ] [GL ] OpenGL vendor <b'VMware, Inc.'>
[INFO ] [GL ] OpenGL renderer <b'llvmpipe (LLVM 9.0.1, 128 bits)'>
[INFO ] [GL ] OpenGL parsed version: 3, 1
[INFO ] [GL ] Shading version <b'1.40'>
[INFO ] [GL ] Texture max size <8192>
[INFO ] [GL ] Texture max units <32>
[INFO ] [Window ] auto add sdl2 input provider
[INFO ] [Window ] virtual keyboard not allowed, single mode, not docked
[INFO ] [ProbeSysfs ] device match: /dev/input/event0
[INFO ] [MTD ] Read event from </dev/input/event0>
[INFO ] [ProbeSysfs ] device match: /dev/input/event0
[INFO ] [HIDInput ] Read event from </dev/input/event0>
[INFO ] [Base ] Start application main loop
[INFO ] [MTD ] </dev/input/event0> range position X is 0 - 800
[INFO ] [MTD ] </dev/input/event0> range position Y is 0 - 480
[INFO ] [HIDMotionEvent] using <WaveShare WS170120>
[INFO ] [MTD ] </dev/input/event0> range touch major is 0 - 0
[INFO ] [HIDMotionEvent] <WaveShare WS170120> range ABS X position is 0 - 800
[INFO ] [MTD ] </dev/input/event0> range touch minor is 0 - 0
[INFO ] [HIDMotionEvent] <WaveShare WS170120> range ABS Y position is 0 - 480
[INFO ] [MTD ] </dev/input/event0> range pressure is 0 - 255
[INFO ] [HIDMotionEvent] <WaveShare WS170120> range ABS pressure is 0 - 255
[INFO ] [MTD ] </dev/input/event0> axes invertion: X is 0, Y is 0
[INFO ] [HIDMotionEvent] <WaveShare WS170120> range position X is 0 - 800
[INFO ] [MTD ] </dev/input/event0> rotation set to 0
[INFO ] [HIDMotionEvent] <WaveShare WS170120> range position Y is 0 - 480
[INFO ] [HIDMotionEvent] <WaveShare WS170120> range pressure is 0 - 255
[INFO ] [GL ] NPOT texture support is available
Capture step 1 # TRIGGERED BY KIVY.UIX.BUTTON ON_PRESS CALLBACK
Capture step 2
Capture step 3
Captured
Capture step 1 # TRIGGERED BY GPIOZERO WHEN_PRESSED CALLBACK
Capture step 2
Capture step 3
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/gpiozero/pins/rpigpio.py", line 244, in _call_when_changed
super(RPiGPIOPin, self)._call_when_changed()
File "/usr/lib/python3/dist-packages/gpiozero/pins/local.py", line 143, in _call_when_changed
self.state if state is None else state)
File "/usr/lib/python3/dist-packages/gpiozero/pins/pi.py", line 293, in _call_when_changed
method(ticks, state)
File "/usr/lib/python3/dist-packages/gpiozero/input_devices.py", line 197, in _pin_changed
self._fire_events(ticks, bool(self._state_to_value(state)))
File "/usr/lib/python3/dist-packages/gpiozero/mixins.py", line 368, in _fire_events
self._fire_activated()
File "/usr/lib/python3/dist-packages/gpiozero/mixins.py", line 397, in _fire_activated
super(HoldMixin, self)._fire_activated()
File "/usr/lib/python3/dist-packages/gpiozero/mixins.py", line 344, in _fire_activated
self.when_activated()
File "main.py", line 35, in capture
camera.export_to_png("IMG_{}.png".format(timestr))
File "/usr/local/lib/python3.7/dist-packages/kivy/uix/widget.py", line 727, in export_to_png
self.export_as_image().save(filename, flipped=False)
File "/usr/local/lib/python3.7/dist-packages/kivy/uix/widget.py", line 744, in export_as_image
with_stencilbuffer=True)
File "kivy/graphics/fbo.pyx", line 152, in kivy.graphics.fbo.Fbo.__init__
File "kivy/graphics/instructions.pyx", line 777, in kivy.graphics.instructions.RenderContext.__init__
File "kivy/graphics/shader.pyx", line 184, in kivy.graphics.shader.Shader.__init__
File "kivy/graphics/shader.pyx", line 701, in kivy.graphics.shader.Shader.vs.__set__
File "kivy/graphics/shader.pyx", line 557, in kivy.graphics.shader.Shader.build_vertex
File "kivy/graphics/shader.pyx", line 587, in kivy.graphics.shader.Shader.link_program
Exception: Shader didnt link, check info log.
^C[INFO ] [Base ] Leaving application in progress...
Traceback (most recent call last):
File "main.py", line 46, in <module>
LifterApp().run()
File "/usr/local/lib/python3.7/dist-packages/kivy/app.py", line 855, in run
runTouchApp()
File "/usr/local/lib/python3.7/dist-packages/kivy/base.py", line 504, in runTouchApp
EventLoop.window.mainloop()
File "/usr/local/lib/python3.7/dist-packages/kivy/core/window/window_sdl2.py", line 747, in mainloop
self._mainloop()
File "/usr/local/lib/python3.7/dist-packages/kivy/core/window/window_sdl2.py", line 479, in _mainloop
EventLoop.idle()
File "/usr/local/lib/python3.7/dist-packages/kivy/base.py", line 339, in idle
Clock.tick()
File "/usr/local/lib/python3.7/dist-packages/kivy/clock.py", line 591, in tick
self._process_events()
File "kivy/_clock.pyx", line 384, in kivy._clock.CyClockBase._process_events
File "kivy/_clock.pyx", line 414, in kivy._clock.CyClockBase._process_events
File "kivy/_clock.pyx", line 412, in kivy._clock.CyClockBase._process_events
File "kivy/_clock.pyx", line 167, in kivy._clock.ClockEvent.tick
File "/usr/local/lib/python3.7/dist-packages/kivy/core/camera/camera_picamera.py", line 71, in _update
self._camera.capture(output, self._format, use_video_port=True)
File "/usr/lib/python3/dist-packages/picamera/camera.py", line 1421, in capture
if not encoder.wait(self.CAPTURE_TIMEOUT):
File "/usr/lib/python3/dist-packages/picamera/encoders.py", line 393, in wait
result = self.event.wait(timeout)
File "/usr/lib/python3.7/threading.py", line 552, in wait
signaled = self._cond.wait(timeout)
File "/usr/lib/python3.7/threading.py", line 300, in wait
gotit = waiter.acquire(True, timeout)
KeyboardInterrupt
我创建了一个解决方法,但它很糟糕,所以我更喜欢更好的解决方案...
...或至少解释为什么 gpiozero
when_pressed
表现不佳。
我的解决方法是使用私有 __capture()
函数,它充当 Clock.schedule_once(self.capture)
的包装器。 capture_btn.when_pressed
将使用此私有包装器回调来避免直接调用有问题的代码,并且 capture()
的任何其他用途将照常使用 public 一个,因为在不与 [= 交互时它可以正常工作11=].
In the following code I would prefer for capture()
and __capture()
to have swapped names since that would fit better with the philosophy of private functions, but unfortunately that's throwing AttributeError: 'RootWidget' object has no attribute '__capture'
when I try that and I don't know how to fix that (using _RootWidget__capture
isn't helping). The lines commented with ##
illustrate my preferred but nonfunctional way.
main.py
import kivy
#kivy.require('1.11.1')
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.uix.camera import Camera
from kivy.core.window import Window
from kivy.clock import Clock
from gpiozero import Button as gpiozeroButton # renamed to avoid conflict w/ kivy.uix.button
import time
Window.fullscreen = 'auto' # uses display's current resolution
capture_btn = gpiozeroButton(pin=13, pull_up=False) # set up GPIO
class RootWidget(FloatLayout):
def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)
## capture_btn.when_pressed = self.capture
capture_btn.when_pressed = self.__capture # initialize callback for GPIO button
## def __capture(self):
def capture(self, dt=None):
print('Capture step 1')
camera = self.ids['camera']
print('Capture step 2')
timestr = time.strftime("%Y%m%d_%H%M%S")
print('Capture step 3')
camera.export_to_png("IMG_{}.png".format(timestr))
print("Captured")
## def capture(self):
def __capture(self): # FIXME this function shouldn't be needed (see function description)
'''
Since camera.export_to_png() throws "Exception: Shader didnt link" when called
by a gpiozero when_pressed callback but not when scheduled using kivy.clock, I
decided to create this wrapper function to circumvent the issue (I'm not sure
of the underlying problem, but this is a workaround in the meantime).
'''
## Clock.schedule_once(self.__capture)
Clock.schedule_once(self.capture)
class LifterApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
LifterApp().run()
lifter.kv
lifter.kv
与问题中的文字没有变化。
您可以使用 kivy.clock.mainthread
装饰器来替换您的 __wrapper
函数。
from kivy.clock import mainthread
...
class RootWidget(FloatLayout):
...
@mainthread
def capture(self):
...
此外,这也摆脱了您不需要的 dt 参数,因为它保留了函数的签名。
最初的问题当然是管理 when_pressed
的代码在一个线程中运行,因此在任何 opengl 操作中都表现不佳(你得到一个“shader didn't link”,但是你还不如在另一台计算机上崩溃,或者什么都没有),因为 opengl 不应该从多个线程使用,主线程装饰器通过使用 Clock 委托给主线程,避免了这个问题。
我正在使用 kivy 中的摄像头编写 GUI,但不确定为什么我的代码无法运行。我有一个相机源,以及两种从中捕获图片的方法:一种由 gpiozero
when_pressed
回调触发,另一种由 kivy.uix.button
on_press
回调触发。
kivy.uix.button
回调成功捕获图像,但 gpiozero
回调显示 Exception: Shader didnt link, check info log.
,无法保存图像,然后使相机画面变黑(尽管图像以后仍然可以使用成功选项捕获)。为什么一个回调有效而另一个无效?
这里是相关代码,对应终端输出。我用 # ALL CAPS COMMENTS
注释了终端输出。 (我的代码是受kivy docs camera example的启发,同样捕获成功)。
main.py
import kivy
#kivy.require('1.11.1')
# Uncomment these lines to see all the messages
#from kivy.logger import Logger
#import logging
#Logger.setLevel(logging.TRACE)
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.uix.camera import Camera
from kivy.core.window import Window
from gpiozero import Button as gpiozeroButton # renamed to avoid conflict w/ kivy.uix.button
import time
Window.fullscreen = 'auto' # uses display's current resolution
capture_btn = gpiozeroButton(pin=13, pull_up=False) # set up GPIO
class RootWidget(FloatLayout):
def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)
capture_btn.when_pressed = self.capture # initialize callback for GPIO button
def capture(self):
print('Capture step 1')
camera = self.ids['camera']
print('Capture step 2')
timestr = time.strftime("%Y%m%d_%H%M%S")
print('Capture step 3')
camera.export_to_png("IMG_{}.png".format(timestr))
print("Captured")
class LifterApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
LifterApp().run()
lifter.kv
# #:kivy 1.11.1
<RootWidget>:
Camera:
id: camera
resolution: (640, 480)
play: True
Button:
text: "capture"
pos_hint: {'x':0.0, 'y':0.0}
size_hint: (0.2, 0.2)
on_press: root.capture()
终端输出(带注释)
[INFO ] [Logger ] Record log in /home/pi/.kivy/logs/kivy_20-12-30_27.txt
[INFO ] [Kivy ] v1.11.1
[INFO ] [Kivy ] Installed at "/usr/local/lib/python3.7/dist-packages/kivy/__init__.py"
[INFO ] [Python ] v3.7.3 (default, Dec 20 2019, 18:57:59)
[GCC 8.3.0]
[INFO ] [Python ] Interpreter at "/usr/bin/python3"
[INFO ] [Factory ] 184 symbols loaded
[INFO ] [Image ] Providers: img_tex, img_dds, img_sdl2, img_pil, img_gif (img_ffpyplayer ignored)
[INFO ] [Text ] Provider: sdl2(['text_pango'] ignored)
[INFO ] [Camera ] Provider: picamera
[INFO ] [Window ] Provider: sdl2(['window_egl_rpi'] ignored)
[INFO ] [GL ] Using the "OpenGL" graphics system
[INFO ] [GL ] Backend used <sdl2>
[INFO ] [GL ] OpenGL version <b'3.1 Mesa 19.3.2'>
[INFO ] [GL ] OpenGL vendor <b'VMware, Inc.'>
[INFO ] [GL ] OpenGL renderer <b'llvmpipe (LLVM 9.0.1, 128 bits)'>
[INFO ] [GL ] OpenGL parsed version: 3, 1
[INFO ] [GL ] Shading version <b'1.40'>
[INFO ] [GL ] Texture max size <8192>
[INFO ] [GL ] Texture max units <32>
[INFO ] [Window ] auto add sdl2 input provider
[INFO ] [Window ] virtual keyboard not allowed, single mode, not docked
[INFO ] [ProbeSysfs ] device match: /dev/input/event0
[INFO ] [MTD ] Read event from </dev/input/event0>
[INFO ] [ProbeSysfs ] device match: /dev/input/event0
[INFO ] [HIDInput ] Read event from </dev/input/event0>
[INFO ] [Base ] Start application main loop
[INFO ] [MTD ] </dev/input/event0> range position X is 0 - 800
[INFO ] [MTD ] </dev/input/event0> range position Y is 0 - 480
[INFO ] [HIDMotionEvent] using <WaveShare WS170120>
[INFO ] [MTD ] </dev/input/event0> range touch major is 0 - 0
[INFO ] [HIDMotionEvent] <WaveShare WS170120> range ABS X position is 0 - 800
[INFO ] [MTD ] </dev/input/event0> range touch minor is 0 - 0
[INFO ] [HIDMotionEvent] <WaveShare WS170120> range ABS Y position is 0 - 480
[INFO ] [MTD ] </dev/input/event0> range pressure is 0 - 255
[INFO ] [HIDMotionEvent] <WaveShare WS170120> range ABS pressure is 0 - 255
[INFO ] [MTD ] </dev/input/event0> axes invertion: X is 0, Y is 0
[INFO ] [HIDMotionEvent] <WaveShare WS170120> range position X is 0 - 800
[INFO ] [MTD ] </dev/input/event0> rotation set to 0
[INFO ] [HIDMotionEvent] <WaveShare WS170120> range position Y is 0 - 480
[INFO ] [HIDMotionEvent] <WaveShare WS170120> range pressure is 0 - 255
[INFO ] [GL ] NPOT texture support is available
Capture step 1 # TRIGGERED BY KIVY.UIX.BUTTON ON_PRESS CALLBACK
Capture step 2
Capture step 3
Captured
Capture step 1 # TRIGGERED BY GPIOZERO WHEN_PRESSED CALLBACK
Capture step 2
Capture step 3
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/gpiozero/pins/rpigpio.py", line 244, in _call_when_changed
super(RPiGPIOPin, self)._call_when_changed()
File "/usr/lib/python3/dist-packages/gpiozero/pins/local.py", line 143, in _call_when_changed
self.state if state is None else state)
File "/usr/lib/python3/dist-packages/gpiozero/pins/pi.py", line 293, in _call_when_changed
method(ticks, state)
File "/usr/lib/python3/dist-packages/gpiozero/input_devices.py", line 197, in _pin_changed
self._fire_events(ticks, bool(self._state_to_value(state)))
File "/usr/lib/python3/dist-packages/gpiozero/mixins.py", line 368, in _fire_events
self._fire_activated()
File "/usr/lib/python3/dist-packages/gpiozero/mixins.py", line 397, in _fire_activated
super(HoldMixin, self)._fire_activated()
File "/usr/lib/python3/dist-packages/gpiozero/mixins.py", line 344, in _fire_activated
self.when_activated()
File "main.py", line 35, in capture
camera.export_to_png("IMG_{}.png".format(timestr))
File "/usr/local/lib/python3.7/dist-packages/kivy/uix/widget.py", line 727, in export_to_png
self.export_as_image().save(filename, flipped=False)
File "/usr/local/lib/python3.7/dist-packages/kivy/uix/widget.py", line 744, in export_as_image
with_stencilbuffer=True)
File "kivy/graphics/fbo.pyx", line 152, in kivy.graphics.fbo.Fbo.__init__
File "kivy/graphics/instructions.pyx", line 777, in kivy.graphics.instructions.RenderContext.__init__
File "kivy/graphics/shader.pyx", line 184, in kivy.graphics.shader.Shader.__init__
File "kivy/graphics/shader.pyx", line 701, in kivy.graphics.shader.Shader.vs.__set__
File "kivy/graphics/shader.pyx", line 557, in kivy.graphics.shader.Shader.build_vertex
File "kivy/graphics/shader.pyx", line 587, in kivy.graphics.shader.Shader.link_program
Exception: Shader didnt link, check info log.
^C[INFO ] [Base ] Leaving application in progress...
Traceback (most recent call last):
File "main.py", line 46, in <module>
LifterApp().run()
File "/usr/local/lib/python3.7/dist-packages/kivy/app.py", line 855, in run
runTouchApp()
File "/usr/local/lib/python3.7/dist-packages/kivy/base.py", line 504, in runTouchApp
EventLoop.window.mainloop()
File "/usr/local/lib/python3.7/dist-packages/kivy/core/window/window_sdl2.py", line 747, in mainloop
self._mainloop()
File "/usr/local/lib/python3.7/dist-packages/kivy/core/window/window_sdl2.py", line 479, in _mainloop
EventLoop.idle()
File "/usr/local/lib/python3.7/dist-packages/kivy/base.py", line 339, in idle
Clock.tick()
File "/usr/local/lib/python3.7/dist-packages/kivy/clock.py", line 591, in tick
self._process_events()
File "kivy/_clock.pyx", line 384, in kivy._clock.CyClockBase._process_events
File "kivy/_clock.pyx", line 414, in kivy._clock.CyClockBase._process_events
File "kivy/_clock.pyx", line 412, in kivy._clock.CyClockBase._process_events
File "kivy/_clock.pyx", line 167, in kivy._clock.ClockEvent.tick
File "/usr/local/lib/python3.7/dist-packages/kivy/core/camera/camera_picamera.py", line 71, in _update
self._camera.capture(output, self._format, use_video_port=True)
File "/usr/lib/python3/dist-packages/picamera/camera.py", line 1421, in capture
if not encoder.wait(self.CAPTURE_TIMEOUT):
File "/usr/lib/python3/dist-packages/picamera/encoders.py", line 393, in wait
result = self.event.wait(timeout)
File "/usr/lib/python3.7/threading.py", line 552, in wait
signaled = self._cond.wait(timeout)
File "/usr/lib/python3.7/threading.py", line 300, in wait
gotit = waiter.acquire(True, timeout)
KeyboardInterrupt
我创建了一个解决方法,但它很糟糕,所以我更喜欢更好的解决方案...
...或至少解释为什么 gpiozero
when_pressed
表现不佳。
我的解决方法是使用私有 __capture()
函数,它充当 Clock.schedule_once(self.capture)
的包装器。 capture_btn.when_pressed
将使用此私有包装器回调来避免直接调用有问题的代码,并且 capture()
的任何其他用途将照常使用 public 一个,因为在不与 [= 交互时它可以正常工作11=].
In the following code I would prefer for
capture()
and__capture()
to have swapped names since that would fit better with the philosophy of private functions, but unfortunately that's throwingAttributeError: 'RootWidget' object has no attribute '__capture'
when I try that and I don't know how to fix that (using_RootWidget__capture
isn't helping). The lines commented with##
illustrate my preferred but nonfunctional way.
main.py
import kivy
#kivy.require('1.11.1')
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.uix.camera import Camera
from kivy.core.window import Window
from kivy.clock import Clock
from gpiozero import Button as gpiozeroButton # renamed to avoid conflict w/ kivy.uix.button
import time
Window.fullscreen = 'auto' # uses display's current resolution
capture_btn = gpiozeroButton(pin=13, pull_up=False) # set up GPIO
class RootWidget(FloatLayout):
def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)
## capture_btn.when_pressed = self.capture
capture_btn.when_pressed = self.__capture # initialize callback for GPIO button
## def __capture(self):
def capture(self, dt=None):
print('Capture step 1')
camera = self.ids['camera']
print('Capture step 2')
timestr = time.strftime("%Y%m%d_%H%M%S")
print('Capture step 3')
camera.export_to_png("IMG_{}.png".format(timestr))
print("Captured")
## def capture(self):
def __capture(self): # FIXME this function shouldn't be needed (see function description)
'''
Since camera.export_to_png() throws "Exception: Shader didnt link" when called
by a gpiozero when_pressed callback but not when scheduled using kivy.clock, I
decided to create this wrapper function to circumvent the issue (I'm not sure
of the underlying problem, but this is a workaround in the meantime).
'''
## Clock.schedule_once(self.__capture)
Clock.schedule_once(self.capture)
class LifterApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
LifterApp().run()
lifter.kv
lifter.kv
与问题中的文字没有变化。
您可以使用 kivy.clock.mainthread
装饰器来替换您的 __wrapper
函数。
from kivy.clock import mainthread
...
class RootWidget(FloatLayout):
...
@mainthread
def capture(self):
...
此外,这也摆脱了您不需要的 dt 参数,因为它保留了函数的签名。
最初的问题当然是管理 when_pressed
的代码在一个线程中运行,因此在任何 opengl 操作中都表现不佳(你得到一个“shader didn't link”,但是你还不如在另一台计算机上崩溃,或者什么都没有),因为 opengl 不应该从多个线程使用,主线程装饰器通过使用 Clock 委托给主线程,避免了这个问题。