Kivy:获取小部件ID并通过独特属性访问小部件

时间:2016-03-04 09:41:01

标签: python widget kivy

我是Kivy的新手,我有这个小小的演示片段来演示我的问题:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder


class KivyGuiApp(App):
    def build(self):
        return root_widget


class MyBox(BoxLayout):
    def print_ids(self, *args):
        print("\nids:")
        for widget in self.walk():
            print("{} -> {}".format(widget, widget.id))
    def print_names(self, *args):
        print("\nnames:")
        for widget in self.walk():
            print("{} -> {}".format(widget, widget.name))



root_widget = Builder.load_string("""
MyBox:
    id: screen_manager
    name: 'screen_manager'

    SimpleLayout:
        id: simple_layout
        name: 'simple_layout'


<SimpleLayout@BoxLayout>:
    id: simple_layout_rule
    name: 'simple_layout_rule'
    Button:
        id: button_ids
        name: 'button_ids'
        text: 'print ids to console'
        on_release: app.root.print_ids()
    Button:
        id: button_names
        name: 'button_names'
        text: 'print names to console'
        on_release: app.root.print_names()
""")


if __name__ == '__main__':
    KivyGuiApp().run()

因此,当您运行代码时,将有两个按钮:

  • 首先遍历所有小部件并打印其名称(按预期工作 - 返回每个小部件的“名称”),
  • 第二个按钮也可以遍历所有小部件,但不是名字打印它们的ID(不起作用 - 每个ID都返回None)。

我的问题是:

  1. 'id'不是'name'的财产吗?
  2. 如何从python端访问每个小部件的id?
  3. 奖金问题:

    1. 我可以通过它的id“全局”访问小部件(假设所有id都是唯一的)吗? “全局”是指例如从“MyBox:”小部件访问(在上面的代码中)id而不引用父子,而只是通过id(或者可能是每个小部件唯一的任何其他属性)。我正在考虑创建所有小部件的字典{id:widget object}以便于访问,除非有其他方式我不知道? 要强调 - 我试图避免通过子 - 父方式引用(当你想稍后更改小部件树时相当混乱)并且我想用.kv语言生成小部件。 那么最好的方法是什么呢?
    2. 修改

      所以这是我能想到的最简单的方法来通过唯一的“全局”id来引用小部件。

      首先,我创建了一个将由我的App类继承的类:

      class KivyWidgetInterface():
          ''' interface for  global widget access '''
          global_widgets = {}
          def register_widget(self, widget_object):
              ''' registers widget only if it has unique gid '''
              if widget_object.gid not in self.global_widgets:
                  self.global_widgets[widget_object.gid] = widget_object
      
          def get_widget(self, widget_gid):
              ''' returns widget if it is registered '''
              if widget_gid in self.global_widgets:
                  return self.global_widgets[widget_gid]
              else:
                  return None
      

      因此,仅当小部件具有gid(小部件类变量)时才会注册小部件,并且它是唯一的。这样我就可以在这个dict中只存储重要的小部件。此外,它可以从.kv和python端轻松访问。

      现在我创建gid变量并将它们注册到.kv中的dict:

      <PickDirectory>:
          gid: 'pick_directory'
          on_pos: app.register_widget(self)
          on_selection: app.get_widget('filelist').some_func()
      <FileListView>:
          gid: 'filelist'
          on_pos: app.register_widget(self)   
          Button:
              name: 'not important'
          Button:
              gid: 'tab_browse_button1'
              on_pos: app.register_widget(self)
      
      实际困扰我的事情是,我在这个“全球”字典中用'on_pos'事件注册我的小部件...我真的不喜欢,但我找不到任何可靠的方式来调用寄存器在窗口小部件初始化之后的方法(on_pos在初始阶段之后立即被调用,当窗口小部件被定位时,后来非常少,所以...似乎是最不用的方式,通过我对kivy api的知识,订单窗口小部件被初始化使用.kv语言等;所以如果有更好的方法,我会对任何指针非常粗糙。)

      无论如何,这样我就可以轻松地将任何事件绑定到任何类中的任何方法,直接来自.kv

      要记住的一件事是gid(全局id)需要在全球范围内是独一无二的,但我没有发现比在本地保持id唯一更令人不安(这对我来说可能同样甚至更加困惑)。正如我所说的 - 我想以不同的方式注册小部件,但我找不到任何其他可靠的方法来做到这一点(而且我发现Clock不可靠这样的东西)。

3 个答案:

答案 0 :(得分:3)

  

Isn&#39; id&#39;一个属性就像&#39; name&#39;?

不,ids是一种特殊的语法,只存在于kv语言中,或者可以通过python(self.ids.idname)中规则的根小部件访问。 是一个id属性,但我不确定它在任何地方实际使用过,它似乎主要是出于传统原因而存在。我想我们正考虑将其删除。

  

如何从python端访问每个小部件的id?

通过根小部件的ids属性。例如,在MyBox方法中,您可以编写self.ids.simple_layout来获取SimpleLayout。

  

我正在考虑创建所有小部件的字典{id:widget object}以便于访问

这通常不可能,因为多个小部件可以具有相同的ID。这就是为什么它们是规则本地的,并通过规则的根小部件进行访问。

如果您愿意,您可以自己构建这样的列表,或者考虑重复,或者说您不会创建它们。

答案 1 :(得分:3)

实际上,没有。您的小部件中的name是一个变量,根据文档,id只是一个小部件引用,weakref。也许python docs会帮助您了解它的工作原理。你所做的是在小部件内打印id,而不是变量“id”。

kivy docs中解释说,在解析kv之后,会将ids收集到ObservableDict中。 id的作用类似于python dict键id:Widget,但只有通过字典(ids)才能访问。我认为kv解析器只是将所有id都带入dict,并且只对它创建的dict有效。

Button:
    id: test
    text: 'self.id'
#or
Button:
    id: 'test'
    text: 'self.id'

即使它写成字符串,也没有任何变化。所以我希望解析器的行为如下:抓取id:后的整个单词,转换为字符串,附加到ids字典<id_string>:Widget_weakref,忘记了id .kv如果它再次与.kv一起使用,则忽略它。因此,当直接调用id(而非类似字典的d [key])时,它的行为类似于空/ None变量。我希望我是对的。

回答第二个和第三个问题:

如果您的意思是id MyBox直接访问小部件,例如SimpleLayout,则为是。

python class:

self.ids.simple_layout

kv MyBox规则:

MyBox:
    id: screen_manager
    name: 'screen_manager'
    BoxLayout:
        Label:
            id: my_label
            text: 'test'
        Button:
            text: 'button'
            on_release: self.text = root.ids.my_label.text

但是,要像python全局工作那样通过id来访问所有小部件,这是不可能的。您需要先访问类/小部件,然后再访问其ids字典

答案 2 :(得分:0)

最近我一直在问自己一个类似的问题:如何访问另一个小部件中的属性。我自己找到了答案,我在这里发帖,因为我发现它不是很直观。

这段代码的想法很简单:当点击一个按钮时,它会改变另一个类的子窗口小部件中的属性。

通常在相同的小部件树内进行调用时,它非常简单,可以通过简单的self.ids.simple_layout(或appself或{{1 }})。

root

棘手的部分是from kivy.app import App from kivy.lang import Builder from kivy.uix.boxlayout import BoxLayout from kivy.uix.label import Label from kivy.uix.button import Button kv = """ <Test>: Button: text: "Access label" on_press: root.access_label() MyWidget: id: my_widget <MyWidget> Label: id: my_label text: "not yet accessed" """ Builder.load_string(kv) class MyWidget(BoxLayout): pass class Test(BoxLayout): def access_label(self): self.ids.my_widget.ids.my_label.text = 'Accessed!' class TestApp(App): def build(self): return Test() if __name__ == '__main__': TestApp().run() ,而且self.ids.my_widget.ids.my_label.text必须位于根小部件而不是my_widget定义中。

我不确定我是否完全理解自己,但据我所知,似乎在用另一个类定义另一个小部件时,它会创建另一个树,这意味着:

  • 必须在主树中分配id
  • 两棵树没有链接,访问属性或功能时必须使用两次<My_widget>

如果我错了,请纠正我