Как использовать выпадающий виджет в киве с классом питона
Итак, я думаю, что должно быть по крайней мере два способа, которыми у меня могло бы быть выпадающее меню на этой странице, но я не могу заставить ни работать. Я новичок в kivy и программировании в целом, но я прочитал документацию и кажется, что я просто не понимаю.
Я создал следующий пример:
import kivy
kivy.require('1.7.2') # replace with your current kivy version !
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty
from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown
class CustomDropDown(DropDown):
pass
class HomeScreen(Screen):
translateInput = ObjectProperty(None)
translateButton = ObjectProperty(None)
translateLabel = ObjectProperty(None)
top_layout = ObjectProperty(None)
dd_btn = ObjectProperty(None)
drop_down = CustomDropDown()
#notes_dropdown = ObjectProperty(None)
dropdown = DropDown()
notes = ['Features', 'Suggestions', 'Abreviations', 'Miscellaneous']
for note in notes:
# when adding widgets, we need to specify the height manually (disabling
# the size_hint_y) so the dropdown can calculate the area it needs.
btn = Button(text='%r' % note, size_hint_y=None, height=30)
# for each button, attach a callback that will call the select() method
# on the dropdown. We'll pass the text of the button as the data of the
# selection.
btn.bind(on_release=lambda btn: dropdown.select(btn.text))
# then add the button inside the dropdown
dropdown.add_widget(btn)
# create a big main button
mainbutton = Button(text='Usage Notes 2', size_hint=(1, 1))
# show the dropdown menu when the main button is released
# note: all the bind() calls pass the instance of the caller (here, the
# mainbutton instance) as the first argument of the callback (here,
# dropdown.open.).
mainbutton.bind(on_release=dropdown.open)
#dd_btn.bind(on_release=dropdown.open)
# one last thing, listen for the selection in the dropdown list and
# assign the data to the button text.
dropdown.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', x))
#dropdown.bind(on_select=lambda instance, x: setattr(dd_btn, 'text', x))
#top_layout.add_widget(mainbutton)
class dropdApp(App):
def build(self):
return HomeScreen()
if __name__ == '__main__':
dropdApp().run()
И это соответствующий файл.kv:
#:kivy 1.7.2
<CustomDropDown>:
Button:
text: 'My first Item'
size_hint_y: None
height: 44
on_release: root.select('item1')
Label:
text: 'Unselectable item'
size_hint_y: None
height: 44
Button:
text: 'My second Item'
size_hint_y: None
height: 44
on_release: root.select('item2')
<HomeScreen>:
id: home_screen
translateInput: translateInputID
translateButton: translateButtonID
translateLabel: labelID
top_layout: topLayoutID
#notes_dropdown: notesDropDownID
dd_btn: btn_ddID
orientation: 'vertical'
FloatLayout:
size_hint: 1, .95
TextInput:
id: translateInputID
text: 'cove'
#text: 'ﻰﺸَﻣ'
font_name: "data/fonts/DejaVuSans.ttf"
background_color: 1, 1, 1, 1
size_hint: .75, .1
multiline: False
pos_hint: {'x': .125, 'y': .45}
text_size: self.size
valign: 'middle'
halign: 'center'
padding: 5
Button:
id: translateButtonID
text: 'Translate'
pos_hint: {'x': .35, 'y': .35}
size_hint: .3, .08
valign: 'middle'
halign: 'center'
text_size: self.size
on_release: root.translateButtonPressed()
#on_press: root.manager.current = 'resultsscreen'
Label:
id: labelID
text: 'Translator'
text_size: self.size
valign: 'middle'
halign: 'center'
pos_hint: {'x': .3, 'y': .75}
size_hint: .4, .2
#font_name: "simpo.ttf"
#font_name: "5thgradecursive.ttf"
#font_name: "AGA-Rasheeq-Regular.ttf"
font_name: "data/fonts/DejaVuSans.ttf"
font_size: 50
BoxLayout:
id: topLayoutID
#cols: 2
size_hint: 1, .05
pos_hint: {'x': 0, 'y': .95}
Button:
#id: notesDropDownID
id: btn_ddID
text: 'Usage Notes'
on_release: root.drop_down.open
Button:
text: 'About'
Первое раскрывающееся меню должно быть прикреплено к уже созданной кнопке "Замечания по использованию" в нижней части файла kv. Он присоединен к классу в python "CustomDropDown" и соответствующему в файле kv. Вы можете заметить, что этот пример прямо из документации kivy. Я подумал, создав линию:
drop_down = CustomDropDown()
он позволил мне манипулировать им как на python, так и на стороне kivy, но, как вы могли заметить, когда вы запустили его и нажали "Usage Notes", ничего не происходит.
Второе выпадающее меню создается в python, также следуя документации kivy. Я думаю, что это создаст третью кнопку в верхней части приложения, под названием "Usage Notes 2". Я просто получаю сообщение об ошибке, когда пытаюсь добавить его. Прямо сейчас строка (53 в моем файле):
top_layout.add_widget(mainbutton)
закомментировано, или приложение даже не запустится, вызывая ошибку:
AttributeError: 'kivy.properties.ObjectProperty' object has no attribute 'add_widget'
Который я понимаю, что я установил
top_layout = ObjectProperty(None)
но это то, что kivy предлагает сделать для многих виджетов, и я сделал со многими другими без этой ошибки.
Я уверен, что один или оба из этих вопросов просты для кого-то там. Что мне не хватает?
2 ответа
Первое раскрывающееся меню должно быть прикреплено к уже созданной кнопке "Замечания по использованию" в нижней части файла kv. Он присоединен к классу в python "CustomDropDown" и соответствующему в файле kv.
В этой части у вас есть правильная основная идея, но вы допустили две основные ошибки.
Во-первых, ваш класс определен неправильно, вы, как правило, почти никогда не хотите размещать код на уровне класса, вместо этого он должен идти в методах класса. Если вы хотите, чтобы он запускался при создании экземпляра класса, вы должны поместить его в __init__
метод. Мой вставленный код ниже показывает простое изменение, необходимое для этого. Вы также должны изменить drop_down
в self.drop_down
для того, чтобы установить атрибут класса, а не просто сделать локальную переменную.
Вы иногда хотите сделать переменные в переменной класса, которые доступны для всех экземпляров класса. Свойства Kivy являются примером этого и имеют особое поведение, определяющее их поведение в реальных экземплярах классов. Тем не менее, это исключение из нормы, а не то, что вы хотите сделать с большей частью вашего кода.
На самом деле, я не совсем уверен в деталях того, как ваш код дает сбой (не уверен насчет порядка / времени выполнения), но в конечном итоге код не запускается должным образом, и виджеты не инициализируются должным образом. Это все хорошо, если вы следуете нормальному __init__
процедура.
Вторая ошибка в том, что ваш файл kv имеет on_release: root.drop_down.open
, Проблема в том, что все в правой части двоеточия kv - чистый python, и в этом случае вы не вызываете функцию, вы просто указываете ее имя и ничего не происходит. Вам нужно заменить это на root.drop_down.open(self)
чтобы получить правильное поведение, так как метод open нуждается в виджете в качестве аргумента.
Второе выпадающее меню создается в python, также следуя документации kivy. Я думаю, что это создаст третью кнопку в верхней части приложения, под названием "Usage Notes 2". Я просто получаю сообщение об ошибке, когда пытаюсь добавить его. Прямо сейчас строка (53 в моем файле):
Это еще один признак того, что ваш код неправильно находится на уровне класса, а не в __init__
метод. Проблема состоит в том, что top_layout является ObjectProperty и появляется только как обычный объект в вашем классе kv из-за особого способа, которым Property управляет своим интерфейсом в экземплярах класса. Когда ваш код находится на уровне класса, он не видит этот специальный интерфейс, потому что он на самом деле не запускается из экземпляра класса (то же самое, что приводит ко всем другим проблемам), поэтому он выдает ошибку, потому что видит ObjectProperty, а не его содержание.
С измененным кодом, как показано ниже, добавление виджета работает нормально, потому что __init__
запускается из экземпляра класса и может взаимодействовать со свойством как обычный атрибут.
Я внес следующие изменения, и оба раскрывающихся списка работают правильно:
import kivy
kivy.require('1.7.2') # replace with your current kivy version !
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty
from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown
class CustomDropDown(DropDown):
for i in range(5):
print i
class HomeScreen(Screen):
translateInput = ObjectProperty(None)
translateButton = ObjectProperty(None)
translateLabel = ObjectProperty(None)
top_layout = ObjectProperty(None)
dd_btn = ObjectProperty(None)
def __init__(self, *args, **kwargs):
super(HomeScreen, self).__init__(*args, **kwargs)
self.drop_down = CustomDropDown()
#notes_dropdown = ObjectProperty(None)
dropdown = DropDown()
notes = ['Features', 'Suggestions', 'Abreviations', 'Miscellaneous']
for note in notes:
# when adding widgets, we need to specify the height manually (disabling
# the size_hint_y) so the dropdown can calculate the area it needs.
btn = Button(text='%r' % note, size_hint_y=None, height=30)
# for each button, attach a callback that will call the select() method
# on the dropdown. We'll pass the text of the button as the data of the
# selection.
btn.bind(on_release=lambda btn: dropdown.select(btn.text))
# then add the button inside the dropdown
dropdown.add_widget(btn)
# create a big main button
mainbutton = Button(text='Usage Notes 2', size_hint=(1, 1))
print 'yay'
# show the dropdown menu when the main button is released
# note: all the bind() calls pass the instance of the caller (here, the
# mainbutton instance) as the first argument of the callback (here,
# dropdown.open.).
mainbutton.bind(on_release=dropdown.open)
#dd_btn.bind(on_release=dropdown.open)
# one last thing, listen for the selection in the dropdown list and
# assign the data to the button text.
dropdown.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', x))
#dropdown.bind(on_select=lambda instance, x: setattr(dd_btn, 'text', x))
self.top_layout.add_widget(mainbutton)
class dropdApp(App):
def build(self):
return HomeScreen()
if __name__ == '__main__':
dropdApp().run()
киловольт:
<CustomDropDown>:
Button:
text: 'My first Item'
size_hint_y: None
height: 44
on_release: root.select('item1')
Label:
text: 'Unselectable item'
size_hint_y: None
height: 44
Button:
text: 'My second Item'
size_hint_y: None
height: 44
on_release: root.select('item2')
<HomeScreen>:
id: home_screen
translateInput: translateInputID
translateButton: translateButtonID
translateLabel: labelID
top_layout: topLayoutID
#notes_dropdown: notesDropDownID
dd_btn: btn_ddID
orientation: 'vertical'
FloatLayout:
size_hint: 1, .95
TextInput:
id: translateInputID
text: 'cove'
#text: 'ﻰﺸَﻣ'
font_name: "data/fonts/DejaVuSans.ttf"
background_color: 1, 1, 1, 1
size_hint: .75, .1
multiline: False
pos_hint: {'x': .125, 'y': .45}
text_size: self.size
valign: 'middle'
halign: 'center'
padding: 5
Button:
id: translateButtonID
text: 'Translate'
pos_hint: {'x': .35, 'y': .35}
size_hint: .3, .08
valign: 'middle'
halign: 'center'
text_size: self.size
on_release: root.translateButtonPressed()
#on_press: root.manager.current = 'resultsscreen'
Label:
id: labelID
text: 'Translator'
text_size: self.size
valign: 'middle'
halign: 'center'
pos_hint: {'x': .3, 'y': .75}
size_hint: .4, .2
#font_name: "simpo.ttf"
#font_name: "5thgradecursive.ttf"
#font_name: "AGA-Rasheeq-Regular.ttf"
font_name: "data/fonts/DejaVuSans.ttf"
font_size: 50
BoxLayout:
id: topLayoutID
#cols: 2
size_hint: 1, .05
pos_hint: {'x': 0, 'y': .95}
Button:
#id: notesDropDownID
id: btn_ddID
text: 'Usage Notes'
on_release: root.drop_down.open(self)
Button:
text: 'About'
Я публикую это здесь в надежде, что это поможет другим начинающим киви, которые просто хотят выпадающую кнопку, которую они могут поместить в файл.kv.
class DropBut(Button):
def __init__(self, **kwargs):
super(DropBut, self).__init__(**kwargs)
self.drop_list = None
self.drop_list = DropDown()
types = ['Item1', 'Item2', 'Item3', 'Item4', 'Item5', 'Item6']
for i in types:
btn = Button(text=i, size_hint_y=None, height=50)
btn.bind(on_release=lambda btn: self.drop_list.select(btn.text))
self.drop_list.add_widget(btn)
self.bind(on_release=self.drop_list.open)
self.drop_list.bind(on_select=lambda instance, x: setattr(self, 'text', x))