Kivy中如何将树莓派GPIO传感器值传递给ImageProgressBar替代滑块
实现方案
步骤1:准备GPIO读取依赖
推荐使用对新手更友好的gpiozero库操作树莓派引脚,安装命令:
pip install gpiozero rpi.gpio
步骤2:核心逻辑说明
你用的ImageProgressBar组件自带value属性(取值范围0-100),之前是绑定Slider的数值实现更新。你只需要删掉Slider组件,定时读取传感器数值换算为0-100的百分比后,直接赋值给进度条的value属性即可完成更新。
步骤3:修改后的完整运行代码
from kivy.uix.floatlayout import FloatLayout from kivy.uix.image import Image from kivy.properties import OptionProperty, StringProperty, ObjectProperty, ListProperty, AliasProperty, \ BooleanProperty, NumericProperty from kivy.lang.builder import Builder from kivy.clock import Clock # 导入GPIO相关库,根据你实际用的传感器调整导入内容 from gpiozero import MCP3008 # kv info for ImageProgressBar kvstr = """ <CroppedImage> canvas.before: StencilPush Rectangle: pos: self.rectangle_pos size: self.rectangle_size StencilUse canvas.after: StencilUnUse Rectangle: pos: self.rectangle_pos size: self.rectangle_size StencilPop <ImageProgressBar> Image: id: background_progress_image source: root.source_progress if root.draw_progress_background else None color : root.color_progress_background if root.draw_progress_background else [0,0,0,0] pos: root.pos size: root.size allow_stretch: True CroppedImage: id: progress_image source: root.source_progress color: root.color_progress pos: root.pos size: root.size allow_stretch: True Image: id: front_image color: root.color_front source: root.source_front pos: root.pos size: root.size allow_stretch: True Label: id: text_lbl pos: root.pos size: root.size """ class CroppedImage(Image): """CroppedImage : Image widget where texture can be cropped with Stencil Instructions""" # position and size of the stencil mask (internal use) rectangle_pos = ListProperty([0.1, 0.7]) rectangle_size = ListProperty([0, 0]) # direction of cropping (bt -> bottom to top, lr -> left to right, ...) direction = OptionProperty('bt', options=['bt', 'tb', 'lr', 'rl']) # visible percentage of the image [0,100] visible_percent = NumericProperty(0) # minimum position of the portion of the image corresponding to 0% and length of 0% to 100% (in pixel of the image file) min_pos = NumericProperty(0) max_length = NumericProperty(100) def on_visible_percent(self, inst, val): self.update_view() def update_view(self): """update_view : update the pos and size of the rectangle stencil mask""" if self.texture: # get how image is resized (zoom factor) zoom_ratio = self.norm_image_size[1] / float(self.texture.size[1]) # where going from bottom (0%) to top (100%) if self.direction == 'bt': self.rectangle_pos = self.center_x - self.norm_image_size[0] / 2., self.center_y - self.norm_image_size[ 1] / 2. self.rectangle_size = self.norm_image_size[0], ( self.min_pos + self.max_length * self.visible_percent / 100.0) * zoom_ratio # where going from top (0%) to bottom (100%) elif self.direction == 'tb': self.rectangle_pos = self.center_x - self.norm_image_size[0] / 2., ( self.center_y + self.norm_image_size[1] / 2.) - ( self.min_pos + self.max_length * self.visible_percent / 100.0) * zoom_ratio self.rectangle_size = self.norm_image_size[0], ( self.min_pos + self.max_length * self.visible_percent / 100.0) * zoom_ratio # where going from left (0%) to right (100%) elif self.direction == 'lr': self.rectangle_pos = self.center_x - self.norm_image_size[0] / 2., self.center_y - self.norm_image_size[ 1] / 2. self.rectangle_size = (self.min_pos + self.max_length * self.visible_percent / 100.0) * zoom_ratio, \ self.norm_image_size[1] # where going from right (0%) to left (100%) elif self.direction == 'rl': self.rectangle_pos = (self.center_x + self.norm_image_size[0] / 2.) - ( self.min_pos + self.max_length * self.visible_percent / 100.0) * zoom_ratio, self.center_y - \ self.norm_image_size[1] / 2. self.rectangle_size = (self.min_pos + self.max_length * self.visible_percent / 100.0) * zoom_ratio, \ self.norm_image_size[1] def on_direction(self, inst, val): self.update_view() def on_size(self, inst, val): self.update_view() def on_pos(self, inst, val): self.update_view() def __init__(self, **kwargs): super().__init__(**kwargs) class ImageProgressBar(FloatLayout): # Images Properties source_front = StringProperty(None) color_front = ListProperty([1, 1, 1, 1]) source_progress = StringProperty(None) color_progress = ListProperty([0.196, 0.643, 0.808, 1]) draw_progress_background = BooleanProperty(False) color_progress_background = ListProperty([0.035, 0.118, 0.149, 1]) min_pos = NumericProperty(0) max_length = NumericProperty(0) value = NumericProperty(0) direction = OptionProperty('rl', options=['bt', 'tb', 'lr', 'rl']) text_auto = BooleanProperty(True) text = StringProperty('') def get_text_label(self): return self.ids['text_lbl'] def on_text(self, inst, val): if self.text_auto: self.ids['text_lbl'].text = '%d %%' % (val,) else: self.ids['text_lbl'].text = self.text def on_text_auto(self, inst, val): self.ids['progress_image'].update_view() if self.text_auto: self.ids['text_lbl'].text = '%d %%' % (val,) else: self.ids['text_lbl'].text = self.text def on_value(self, inst, val): self.ids['progress_image'].visible_percent = val if self.text_auto: self.ids['text_lbl'].text = '%d %%' % (val,) else: self.ids['text_lbl'].text = self.text def on_direction(self, inst, val): self.ids['progress_image'].direction = val def on_min_pos(self, inst, val): self.ids['progress_image'].min_pos = val def on_max_length(self, inst, val): self.ids['progress_image'].max_length = val def first_update(self): self.ids['progress_image'].update_view() if self.text_auto: self.ids['text_lbl'].text = '%d %%' % (self.value,) else: self.ids['text_lbl'].text = self.text def __init__(self, **kwargs): Builder.load_string(kvstr) super().__init__(**kwargs) Clock.schedule_once(lambda dt: self.first_update(), 2.1) if __name__ == '__main__': from kivy.uix.boxlayout import BoxLayout from kivy.app import App # 去掉了Slider组件,给进度条加了id方便调用 appkvstr = """ #:import simple shapes.simple_tank.info <Main@BoxLayout>: orientation: 'horizontal' padding: 10 BoxLayout: orientation: 'vertical' spacing: 5 ImageProgressBar: id: progress_bar source_front: 'shapes/simple_tank/front.png' source_progress: 'shapes/simple_tank/back.png' min_pos: simple.start max_length: simple.length direction: simple.direction """ class Main(BoxLayout): def __init__(self, **kwargs): super().__init__(**kwargs) # 初始化传感器,这里以接在MCP3008通道0的模拟传感器为例,根据你实际用的传感器修改 self.sensor = MCP3008(channel=0) # 传感器量程最大值,比如你的传感器满量程输出是1024就填1024,根据实际情况调整 self.sensor_max_val = 1 # 定时每秒读取一次传感器更新进度条,间隔可以自己改,单位是秒 Clock.schedule_interval(self.update_progress, 1) def update_progress(self, dt): # 读取传感器数值 sensor_val = self.sensor.value # 换算为0-100的百分比,并且限制范围防止超出 percent = max(0, min(100, (sensor_val / self.sensor_max_val) * 100)) # 赋值给进度条完成更新 self.ids.progress_bar.value = percent class TestApp(App): def __init__(self, **kwargs): Builder.load_string(appkvstr) super().__init__(**kwargs) def build(self): return Main() # 退出时清理GPIO资源 def on_stop(self): from gpiozero import Device Device.close() TestApp().run()
注意事项
- 代码里的传感器初始化、量程数值需要根据你实际使用的传感器型号调整
- 如果传感器读取耗时超过100ms,建议单独开子线程读数值,再通过
Clock.schedule_once回到UI线程更新进度条数值,避免界面卡顿 - 请确保运行代码的用户有GPIO操作权限,否则会报错
内容的提问来源于stack exchange,提问作者Suganthan Arulvelan
相关产品推荐
相关产品推荐

