You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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()

问题分析与修复方案

核心问题点

  1. 全局变量作用域错误:load()函数内定义的全局变量answer无法被MyPopup类正确访问,且Root.answer是类属性访问方式,不是实例属性,逻辑错误。
  2. 弹窗触发时机错误:原KV中点击Load按钮立刻打开弹窗,但此时用户还未完成图片点击选色,弹窗提前弹出,无法获取颜色值。
  3. 颜色格式不匹配:OpenCV读取的颜色是BGR格式,而webcolors使用RGB格式,原代码直接对比会导致颜色识别错误。
  4. 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()

修改说明

  1. 添加颜色属性:在Root类中新增color_name属性,用于存储识别后的颜色名称,替代全局变量。
  2. 修复颜色格式:将cv2读取的BGR颜色转换为RGB格式后再进行对比,确保颜色识别准确。
  3. 调整弹窗触发时机:移除KV中Load按钮的弹窗触发逻辑,改为在load()函数中,用户完成选色后再打开弹窗,并传递颜色名称。
  4. 简化文件名处理:直接取列表第一个元素(FileChooser返回的是列表),避免复杂的字符串替换。
  5. 优化弹窗定义:简化MyPopup类,直接通过text属性接收颜色值,KV中绑定该属性到Label。

内容的提问来源于stack exchange,提问作者Furkan Yener

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 02:45:34