Кажется, у меня возникают проблемы при попытке подключить виджеты в Kivy. Я прочитал это полезное руководство , но моя ситуация непосредственно не освещена.
У меня есть 2 разных "селектора" рядом, как это:
Каждый из них будет своим собственным классом, поддерживаемым KeySigChooserContainer. Я хочу, чтобы размер кнопок основывался на размере KeySigChooserContainer, чтобы кнопки имели одинаковый размер. Это достигается с помощью
ChooserButton:
...
width: root.parent.width * (3/32)
, но я не люблю использовать ссылку parent
;Я бы предпочел использовать прямую ссылку на гибкость, поскольку приложение становится все сложнее. Но когда я пытаюсь сделать это с
<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
, я получаю ошибку атрибута: AttributeError: 'RootNoteChooser' object has no attribute 'box'
Я использовал подобную технику в другом месте в моем проекте, поэтому я понятия не имею, почему это не так. не работаетЯ также пытался сделать box
ObjectProperty в классах RootNoteChooser и ModeChooser, но это не работает.
# keysigchooser.py
from kivy.app import App
from kivy.properties import NumericProperty, ObjectProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.relativelayout import RelativeLayout
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):
pass
class RootNoteChooser(FloatLayout):
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
class KeySigChooserApp(App):
def build(self):
return KeySigChooserContainer()
if __name__ == "__main__":
KeySigChooserApp().run()
# 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: box
size_hint: [0.4, 1]
canvas:
Color:
rgba: [1, 0, 0, 0.5]
Rectangle:
pos: self.pos
size: self.size
ModeChooser:
id: modechooser
box: box
size_hint: [0.6, 1]
canvas:
Color:
rgba: [0, 1, 0, 0.5]
Rectangle:
pos: self.pos
size: self.size
Очевидно, что здесь что-то не хватает ... любая помощь приветствуется.
ОБНОВЛЕНИЕ Кажется, это одна из тех ситуаций, когда нет замечательного способа решения проблемы. Благодаря @JohnAnderson вот что я узнал:
- Идентификатор ограничен в области действия правилом, в котором он объявлен.
- самое внешнееВиджет применяет правила kv ко всем своим внутренним виджетам до применения любых других правил
- Правила всегда применяются до экземпляров.
Проблема здесья использую атрибут (box
) в <RootNoteChooser>
и <ModeChooser>
правилах , но этот атрибут создается в экземпляре из RootNoteChooser
и ModeChooser
,Поскольку правила применяются первыми, box
еще не существует.
Обходное решение, которое я использую для этого, заключается в том, чтобы также создать атрибут box
в обоих правилах и установить для него что-то, что делаетсмысл (и не вызовет ошибку). Затем в экземплярах RootNoteChooser
и ModeChooser
(в правиле <KeySigChooser>
) box
будет сброшен на нужный объект. Вот суть этого:
<RootNoteChooser>:
box: self.parent # Initially we'll set it to something reasonable.
BoxLayout:
...
ChooserButton:
...
width: root.box.width * (3/32)
<ModeChooser>:
box: self.parent # Initially we'll set it to something reasonable.
BoxLayout:
...
ChooserButton:
...
width: root.box.width * (3/32)
<KeySigChooserContainer>:
BoxLayout:
id: box
RootNoteChooser:
box: box # Now box attribute is correct, and can be pointed at any
ModeChooser:
box: box # id that is within this rule.