如何在Kivy中从其他类函数向弹窗标签添加识别的颜色名称?
问题:将点击像素的颜色名称显示在Kivy弹窗中
需求流程:
- 从电脑选择PNG/JPG/JPEG格式的图片
- 显示图片后接收鼠标点击输入
- 打开弹窗展示点击像素对应的颜色名称(通过
closest_colour()函数识别)
尝试用全局变量传递颜色名称但未成功,原代码如下:
原Python代码
from kivy.app import App from kivy.uix.floatlayout import FloatLayout from kivy.factory import Factory from kivy.properties import ObjectProperty, StringProperty from kivy.uix.popup import Popup import cv2 import webcolors class LoadDialog(FloatLayout): load = ObjectProperty(None) cancel = ObjectProperty(None) class Root(FloatLayout): loadfile = ObjectProperty(None) def dismiss_popup(self): self._popup.dismiss() def show_load(self): content = LoadDialog(load=self.load, cancel=self.dismiss_popup) self._popup = Popup(title="Load file", content=content, size_hint=(0.9, 0.9)) self._popup.open() def load (self,path, filename): filename=str(filename) filename = filename.replace("[", "") filename = filename.replace("]", "") filename = filename.replace("\\\\", "\\") filename = filename.replace("'", "") print(filename) img=cv2.imread(filename) cv2.imshow('image', img) self.dismiss_popup() def closest_colour(requested_colour): min_colours = {} for key, name in webcolors.CSS3_HEX_TO_NAMES.items(): b_c, g_c, r_c = webcolors.hex_to_rgb(key) rd = (r_c - requested_colour[0]) ** 2 gd = (g_c - requested_colour[1]) ** 2 bd = (b_c - requested_colour[2]) ** 2 min_colours[(rd + gd + bd)] = name return min_colours[min(min_colours.keys())] def click_event(event, x, y, flags, params): if event == cv2.EVENT_LBUTTONDOWN: print(x, ' ', y) requested_colour=img[y,x] global answer answer=closest_colour(requested_colour) print(closest_colour(requested_colour)) print(img[y,x]) cv2.destroyAllWindows() cv2.setMouseCallback('image', click_event) cv2.waitKey(0) return answer class MyPopup(Popup): PopUp=ObjectProperty(None) text = StringProperty(Root.answer) def MyPopup(self): content = MyPopup(PopUp=self.PopUp) self._popup = Popup(title="Color Found!!", content=content, size_hint=(0.2, 0.4)) self._popup.open() class main(App): pass Factory.register('Root', cls=Root) Factory.register('LoadDialog', cls=LoadDialog) Factory.register('MyPopup', cls=MyPopup) if __name__ == '__main__': main().run()
原KV代码
#:kivy 1.1.0 #:import Factory kivy.factory.Factory Root: FloatLayout: Button: id: load size_hint:(1,.1) pos_hint:{'x':0, 'y':0} text: 'Load' on_release: root.show_load() background_color :(0, 0, 1, 1) Image: id: img size_hint:(1,.9) pos_hint:{'x':0, 'y':.1} source: 'palet.jpg' size: self.texture_size keep_ratio: False allow_stretch: True <LoadDialog>: id:load_dialog BoxLayout: size: root.size pos: root.pos orientation: "vertical" FileChooserIconView: id: filechooser filters:['*.png','*.jpg','*.jpeg'] BoxLayout: size_hint_y: None height: 30 Button: text: "Cancel" on_release: root.cancel() Button: text: "Load" on_release: root.load(filechooser.path, filechooser.selection) on_release: Factory.MyPopup().open() <MyPopup@Popup> auto_dismiss: False size_hint:0.4,0.2 pos_hint:{"x":0.3,"y":0.4} title:"Color Found!!" BoxLayout: Label: text:root.text font_size:24 Button: text:"Ok" width:.5 size_hint:0.2,0.2 pos_hint:{"x":0.2,"y":0} on_release: root.dismiss()
问题分析与修复方案
核心问题点
- 全局变量作用域错误:
load()函数内定义的全局变量answer无法被MyPopup类正确访问,且Root.answer是类属性访问方式,不是实例属性,逻辑错误。 - 弹窗触发时机错误:原KV中点击Load按钮立刻打开弹窗,但此时用户还未完成图片点击选色,弹窗提前弹出,无法获取颜色值。
- 颜色格式不匹配:OpenCV读取的颜色是BGR格式,而
webcolors使用RGB格式,原代码直接对比会导致颜色识别错误。 MyPopup类定义冗余:既继承Popup又自定义同名构造方法,且text属性初始化逻辑错误。
修改后的代码
Python代码
from kivy.app import App from kivy.uix.floatlayout import FloatLayout from kivy.factory import Factory from kivy.properties import ObjectProperty, StringProperty from kivy.uix.popup import Popup import cv2 import webcolors class LoadDialog(FloatLayout): load = ObjectProperty(None) cancel = ObjectProperty(None) class Root(FloatLayout): loadfile = ObjectProperty(None) # 添加存储颜色名称的属性 color_name = StringProperty("") def dismiss_popup(self): self._popup.dismiss() def show_load(self): content = LoadDialog(load=self.load, cancel=self.dismiss_popup) self._popup = Popup(title="Load file", content=content, size_hint=(0.9, 0.9)) self._popup.open() def load(self, path, filename): # 简化文件名处理 filename = filename[0] if isinstance(filename, list) else str(filename) print(filename) img = cv2.imread(filename) cv2.imshow('image', img) self.dismiss_popup() self.color_name = "" def closest_colour(requested_colour): min_colours = {} # 转换为RGB格式(cv2是BGR) r, g, b = requested_colour[2], requested_colour[1], requested_colour[0] for key, name in webcolors.CSS3_HEX_TO_NAMES.items(): rc, gc, bc = webcolors.hex_to_rgb(key) # 计算颜色距离 distance = (rc - r) ** 2 + (gc - g) ** 2 + (bc - b) ** 2 min_colours[distance] = name return min_colours[min(min_colours.keys())] def click_event(event, x, y, flags, params): if event == cv2.EVENT_LBUTTONDOWN: print(f"坐标: {x}, {y}") requested_colour = img[y, x] # 更新Root实例的color_name属性 self.color_name = closest_colour(requested_colour) print(f"识别颜色: {self.color_name}") print(f"BGR值: {requested_colour}") cv2.destroyAllWindows() cv2.setMouseCallback('image', click_event) cv2.waitKey(0) # 选色完成后打开弹窗 if self.color_name: Factory.MyPopup(text=self.color_name).open() class MyPopup(Popup): # 定义text属性用于接收颜色名称 text = StringProperty("") class main(App): pass Factory.register('Root', cls=Root) Factory.register('LoadDialog', cls=LoadDialog) Factory.register('MyPopup', cls=MyPopup) if __name__ == '__main__': main().run()
KV代码
#:kivy 1.1.0 #:import Factory kivy.factory.Factory Root: FloatLayout: Button: id: load size_hint:(1,.1) pos_hint:{'x':0, 'y':0} text: 'Load' on_release: root.show_load() background_color :(0, 0, 1, 1) Image: id: img size_hint:(1,.9) pos_hint:{'x':0, 'y':.1} source: 'palet.jpg' size: self.texture_size keep_ratio: False allow_stretch: True <LoadDialog>: id:load_dialog BoxLayout: size: root.size pos: root.pos orientation: "vertical" FileChooserIconView: id: filechooser filters:['*.png','*.jpg','*.jpeg'] BoxLayout: size_hint_y: None height: 30 Button: text: "Cancel" on_release: root.cancel() Button: text: "Load" on_release: root.load(filechooser.path, filechooser.selection) <MyPopup@Popup> auto_dismiss: False size_hint:0.4,0.2 pos_hint:{"x":0.3,"y":0.4} title:"Color Found!!" BoxLayout: Label: text: root.text font_size:24 Button: text:"Ok" size_hint:0.2,1 on_release: root.dismiss()
修改说明
- 添加颜色属性:在
Root类中新增color_name属性,用于存储识别后的颜色名称,替代全局变量。 - 修复颜色格式:将cv2读取的BGR颜色转换为RGB格式后再进行对比,确保颜色识别准确。
- 调整弹窗触发时机:移除KV中Load按钮的弹窗触发逻辑,改为在
load()函数中,用户完成选色后再打开弹窗,并传递颜色名称。 - 简化文件名处理:直接取列表第一个元素(FileChooser返回的是列表),避免复杂的字符串替换。
- 优化弹窗定义:简化
MyPopup类,直接通过text属性接收颜色值,KV中绑定该属性到Label。
内容的提问来源于stack exchange,提问作者Furkan Yener
相关产品推荐
相关产品推荐

