Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/320.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Kivy属性错误-对象没有属性-尝试用kv语言连接小部件_Python_Python 3.x_Kivy - Fatal编程技术网

Python Kivy属性错误-对象没有属性-尝试用kv语言连接小部件

Python Kivy属性错误-对象没有属性-尝试用kv语言连接小部件,python,python-3.x,kivy,Python,Python 3.x,Kivy,在尝试连接Kivy中的小部件时,我似乎遇到了不停的问题。我读过,但我的情况没有直接报道 我有两个不同的“选择器”并排排列,如下所示: 每个选择器都是自己的类,由KeySigChooserContainer持有。我想根据KeySigChooserContainer的大小调整按钮的大小,以便按钮具有一致的大小。这是通过以下方式实现的: ChooserButton: ... width: root.parent.width * (3/32) 但是我不喜欢使用parentreferen

在尝试连接Kivy中的小部件时,我似乎遇到了不停的问题。我读过,但我的情况没有直接报道

我有两个不同的“选择器”并排排列,如下所示:

每个选择器都是自己的类,由KeySigChooserContainer持有。我想根据KeySigChooserContainer的大小调整按钮的大小,以便按钮具有一致的大小。这是通过以下方式实现的:

ChooserButton:
    ...
    width: root.parent.width * (3/32)
但是我不喜欢使用
parent
reference;随着应用程序的复杂性不断增加,我更愿意使用直接引用来提高灵活性。但是当我试着和你一起做的时候

<RootNoteChooser>:
    ...
    BoxLayout:
        ...
        ChooserButton:
            ...
            width: root.box.width * (3/32)

<ModeChooser>:
    ...
    BoxLayout:
        ...
        ChooserButton:
            ...
            width: root.box.width * (3/32)

<KeySigChooserContainer>:
    BoxLayout:
        id: box
        RootNoteChooser:
            box: box
        ModeChooser:
            box: box

如果希望
BoxLayout
的子项是
BoxLayout
宽度的某一部分,则可以使用
size\u hint
(这就是它的预期用途)。例如,在
rootnotechoser
中:

<RootNoteChooser>:
    note_text: note_text
    BoxLayout:
        pos_hint: {"center": [0.5, 0.5]}
        orientation: "horizontal"
        ChooserButton:
            text: u'\u25C4'
            size_hint: [3/32, 1]
            #width: root.box.width * (3/32)
            on_press: root.increment_note_idx()
        Label:
            id: note_text
            text: "C"
            size_hint: [26/32, 1]
        ChooserButton:
            text: u'\u25BA'
            size_hint: [3/32, 1]
            #width: root.box.width * (3/32)
            on_press: root.decrement_note_idx()
:
注释文字:注释文字
盒子布局:
位置提示:{“中心”:[0.5,0.5]}
方向:“水平”
选择按钮:
文本:u'\u25C4'
尺寸提示:[3/32,1]
#宽度:root.box.width*(3/32)
按:root.increment\u note\u idx()
标签:
id:注\文本
案文:“C”
尺寸提示:[26/32,1]
选择按钮:
文本:u'\u25BA'
尺寸提示:[3/32,1]
#宽度:root.box.width*(3/32)
按:root.decreation\u note\u idx()

BoxLayout
中,分配了
size\u hint
的子项将在减去其他子项的大小后占据剩余空间的那一部分。因此,在上面的示例中,从父
BoxLayout
中减去
标签
空间,剩余空间在
选择器按钮
之间分配。为
标签添加类似的
size\u提示
,使其更加清晰。

在kivy中设置属性引用时,必须注意的一点是这些属性何时可用。您的原始代码似乎合理,但问题是
rootnotechoser
ModeChooser
box
属性在设置之前被访问。您可以通过定义一个在实际设置其值之前可以使用的属性来解决这个问题。在这种情况下,使用
NumericProperty(0)
将允许代码使用初始值零,即使该值不正确。然后,当(Kivy)指定了正确的值时,它将按照您的预期工作。下面是使用该方法修改的代码版本:

# keysigchooser.py
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivy.uix.floatlayout import FloatLayout

chrom_scale = ['C', 'C#/Db', 'D', 'D#/Eb', 'E', 'F', 'F#/Gb', 'G', 'G#/Ab', 'A', 'A#/Bb', 'B']
chrom_scale2 = ['C', 'C/D', 'D', 'D/E', 'E', 'F', 'F/G', 'G', 'G/A', 'A', 'A/B', 'B']


class ModeChooser(FloatLayout):
    box_width = NumericProperty(0)   # starts off as zero, just so there is  number available


class RootNoteChooser(FloatLayout):
    box_width = NumericProperty(0)   # starts off as zero, just so there is  number available
    note_idx = NumericProperty(0)

    def increment_note_idx(self):
        self.note_idx = (self.note_idx + 1) % 12

    def decrement_note_idx(self):
        self.note_idx = (self.note_idx - 1) % 12

    def on_note_idx(self, instance, value):
        self.note_text.text = chrom_scale[self.note_idx]


class KeySigChooserContainer(FloatLayout):
    def on_size(self, instance, value):
        target_ratio = 60/20
        width, height = self.size
        # check which size is the limiting factor
        if width / height > target_ratio:
            # window is "wider" than targeted, so the limitation is the height.
            self.ids.box.height = height
            self.ids.box.width = height * target_ratio
        else:
            self.ids.box.width = width
            self.ids.box.height = width / target_ratio

Builder.load_string('''
# keysigchooser.kv
<ChooserButton@Button>:
    font_name: "Arial"
    font_size: self.width
    border: [2, 2, 2, 2]


<RootNoteChooser>:
    note_text: note_text
    BoxLayout:
        pos_hint: {"center": [0.5, 0.5]}
        orientation: "horizontal"
        ChooserButton:
            text: u'\u25C4'
            size_hint: [None, 1]
            width: root.box_width * (3/32)
            on_press: root.increment_note_idx()
        Label:
            id: note_text
            text: "C"
        ChooserButton:
            text: u'\u25BA'
            size_hint: [None, 1]
            width: root.box_width * (3/32)
            on_press: root.decrement_note_idx()


<ModeChooser>:
    BoxLayout:
        pos_hint: {"center": [0.5, 0.5]}
        orientation: "horizontal"
        ChooserButton:
            text: u'\u25C4'
            size_hint: [None, 1]
            width: root.box_width * (3/32)
        Label:
            text: "Major"
        ChooserButton:
            text: u'\u25BA'
            size_hint: [None, 1]
            width: root.box_width * (3/32)


<KeySigChooserContainer>:
    BoxLayout:
        id: box
        pos_hint: {"center": [0.5, 0.5]}
        size_hint: [None, None]
        orientation: "horizontal"
        RootNoteChooser:
            id: rootnotechooser
            box_width: box.width   # this sets the box_width
            size_hint: [0.4, 1]
            canvas:
                Color:
                    rgba: [1, 0, 0, 0.5]
                Rectangle:
                    pos: self.pos
                    size: self.size
        ModeChooser:
            id: modechooser
            box_width: box.width   # this sets the box_width
            size_hint: [0.6, 1]
            canvas:
                Color:
                    rgba: [0, 1, 0, 0.5]
                Rectangle:
                    pos: self.pos
                    size: self.size
''')
class KeySigChooserApp(App):
    def build(self):
        return KeySigChooserContainer()


if __name__ == "__main__":
    KeySigChooserApp().run()
还有一种方法是从
kv
文件中删除
规则,并将这些规则的内容直接放在
规则的
模式选择器
根注释选择器
部分下。这将允许您使用以下方法设置
ChooserButton
宽度:

width: box.width * (3/32)

与您的原始代码类似。

对-但我希望RootNoteChooser和ModeChooser的宽度不同,ChooserButtons的宽度相同(如果不清楚,请抱歉)。无论我是直接使用
size\u hint
还是
width
,按钮的宽度都需要基于KeySigChooser的宽度,这是所有ChooserButtons的共同父项。我知道这是可行的,但我觉得这更像是一种解决方法,而不是答案。这样做对吗?创建已经存在的属性(
box.width
)似乎是一种不好的做法。使用
root.parent.width
是可行的,因此保存这些“选择器”的BoxLayout显然存在。但看起来和规则在创建属性的各自实例之前执行(
box:box
)。有没有更好的方法来构造kv文件以避免此问题?(感谢您花时间编写这些答案!)在我上面的回答中,我添加了两种可选方法来实现您的目标。您对第四个选项有何看法:在和规则中,设置
box:self.parent
以使
box
存在。然后,稍后实例化时,
将被附加到它的
id
所覆盖(我认为)。这允许我们仍然使用
box.width
(而不是
box\u width
),并且kv文件仍然可以为每个类划分为规则。@marcus\u afailius,我认为这会起作用,但我认为您需要使用
root.box.width
,而不是
box.width
。有许多不同的方法可以达到相同的结果!!谢谢使用
root.box.width
是我目前的做法。在我的主应用程序中,
box
在小部件树中的位置实际上比我在这里解释的要高,所以这似乎是最干净的选择。不过,我并不特别喜欢这些解决方案,因为它们似乎都有点老套。。但也许它就是这样。我会让这篇文章停留一段时间,看看是否有其他人有意见,否则我会更新我的OP并将其标记为答案。再次感谢您的洞察力!
<RootNoteChooser>:
    note_text: note_text
    BoxLayout:
        pos_hint: {"center": [0.5, 0.5]}
        orientation: "horizontal"
        ChooserButton:
            text: u'\u25C4'
            size_hint: [3/32, 1]
            #width: root.box.width * (3/32)
            on_press: root.increment_note_idx()
        Label:
            id: note_text
            text: "C"
            size_hint: [26/32, 1]
        ChooserButton:
            text: u'\u25BA'
            size_hint: [3/32, 1]
            #width: root.box.width * (3/32)
            on_press: root.decrement_note_idx()
# keysigchooser.py
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivy.uix.floatlayout import FloatLayout

chrom_scale = ['C', 'C#/Db', 'D', 'D#/Eb', 'E', 'F', 'F#/Gb', 'G', 'G#/Ab', 'A', 'A#/Bb', 'B']
chrom_scale2 = ['C', 'C/D', 'D', 'D/E', 'E', 'F', 'F/G', 'G', 'G/A', 'A', 'A/B', 'B']


class ModeChooser(FloatLayout):
    box_width = NumericProperty(0)   # starts off as zero, just so there is  number available


class RootNoteChooser(FloatLayout):
    box_width = NumericProperty(0)   # starts off as zero, just so there is  number available
    note_idx = NumericProperty(0)

    def increment_note_idx(self):
        self.note_idx = (self.note_idx + 1) % 12

    def decrement_note_idx(self):
        self.note_idx = (self.note_idx - 1) % 12

    def on_note_idx(self, instance, value):
        self.note_text.text = chrom_scale[self.note_idx]


class KeySigChooserContainer(FloatLayout):
    def on_size(self, instance, value):
        target_ratio = 60/20
        width, height = self.size
        # check which size is the limiting factor
        if width / height > target_ratio:
            # window is "wider" than targeted, so the limitation is the height.
            self.ids.box.height = height
            self.ids.box.width = height * target_ratio
        else:
            self.ids.box.width = width
            self.ids.box.height = width / target_ratio

Builder.load_string('''
# keysigchooser.kv
<ChooserButton@Button>:
    font_name: "Arial"
    font_size: self.width
    border: [2, 2, 2, 2]


<RootNoteChooser>:
    note_text: note_text
    BoxLayout:
        pos_hint: {"center": [0.5, 0.5]}
        orientation: "horizontal"
        ChooserButton:
            text: u'\u25C4'
            size_hint: [None, 1]
            width: root.box_width * (3/32)
            on_press: root.increment_note_idx()
        Label:
            id: note_text
            text: "C"
        ChooserButton:
            text: u'\u25BA'
            size_hint: [None, 1]
            width: root.box_width * (3/32)
            on_press: root.decrement_note_idx()


<ModeChooser>:
    BoxLayout:
        pos_hint: {"center": [0.5, 0.5]}
        orientation: "horizontal"
        ChooserButton:
            text: u'\u25C4'
            size_hint: [None, 1]
            width: root.box_width * (3/32)
        Label:
            text: "Major"
        ChooserButton:
            text: u'\u25BA'
            size_hint: [None, 1]
            width: root.box_width * (3/32)


<KeySigChooserContainer>:
    BoxLayout:
        id: box
        pos_hint: {"center": [0.5, 0.5]}
        size_hint: [None, None]
        orientation: "horizontal"
        RootNoteChooser:
            id: rootnotechooser
            box_width: box.width   # this sets the box_width
            size_hint: [0.4, 1]
            canvas:
                Color:
                    rgba: [1, 0, 0, 0.5]
                Rectangle:
                    pos: self.pos
                    size: self.size
        ModeChooser:
            id: modechooser
            box_width: box.width   # this sets the box_width
            size_hint: [0.6, 1]
            canvas:
                Color:
                    rgba: [0, 1, 0, 0.5]
                Rectangle:
                    pos: self.pos
                    size: self.size
''')
class KeySigChooserApp(App):
    def build(self):
        return KeySigChooserContainer()


if __name__ == "__main__":
    KeySigChooserApp().run()
    # set button sizes
    self.ids.rootnotechooser.ids.butt1.width = self.ids.box.width * 3/32
    self.ids.rootnotechooser.ids.butt2.width = self.ids.box.width * 3/32
    self.ids.modechooser.ids.butt1.width = self.ids.box.width * 3/32
    self.ids.modechooser.ids.butt2.width = self.ids.box.width * 3/32
width: box.width * (3/32)