在 Kivy 中部分可见的下拉菜单 (Python)
Dropdown partially visible in Kivy (Python)
我正在尝试在 Kivy(Python 的 GUI)中创建一个通用菜单栏,但我在使用下拉菜单时遇到了问题。它们只出现了一部分,我不知道为什么(参见子菜单 1):
如果你想查看,这是我的代码:
#!/usr/bin/env python3
from kivy.app import App
#from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.dropdown import DropDown
from kivy.properties import ListProperty, ObjectProperty
#from kivy.uix.actionbar import ActionBar, ActionView, ActionGroup, ActionButton
class MenuItem(ButtonBehavior, Label):
'''Background color, in the format (r, g, b, a).'''
background_color_normal = ListProperty([0.2, 0.2, 0.2, 1])
background_color_down = ListProperty([0.3, 0.3, 0.3, 1])
background_color = ListProperty()
separator_color = ListProperty([0.8, 0.8, 0.8, 1])
pass
class MenuSubmenu(MenuItem):
# The list of submenu items in dropdown menu
submenu = ObjectProperty(None)
def add_widget(self, submenu, **kwargs):
if isinstance(submenu, MenuDropDown):
self.submenu = submenu
super().add_widget(submenu, **kwargs)
def on_release(self, **kwargs):
super().on_release(**kwargs)
self.submenu.open(self)
class MenuDropDown(DropDown):
pass
class MenuButton(MenuItem):
pass
class MenuBar(BoxLayout):
'''Background color, in the format (r, g, b, a).'''
background_color = ListProperty([0.2, 0.2, 0.2, 1])
separator_color = ListProperty([0.8, 0.8, 0.8, 1])
def __init__(self, **kwargs):
self.itemsList = []
super().__init__(**kwargs)
def add_widget(self, item, index=0):
if not isinstance(item, MenuItem):
raise TypeError("MenuBar accepts only MenuItem widgets")
super().add_widget(item, index)
if index == 0:
index = len(self.itemsList)
self.itemsList.insert(index, item)
class MenuApp(App):
def button(self, nb):
print("Button", nb, "triggered")
if __name__ == '__main__':
MenuApp().run()
这里是 kv 文件:
#:kivy 1.0.9
<MenuItem>:
background_color: self.background_color_down if self.state=='down' else self.background_color_normal
color: (1,1,1,1)
canvas.before:
Color:
rgba: self.background_color
Rectangle:
pos: self.pos
size: self.size
canvas.after:
Color:
rgba: self.separator_color
Line:
rectangle: self.x,self.y,self.width,self.height
<MenuBar>:
size_hint_y: None
height: 40
canvas.before:
Color:
rgba: self.background_color
Rectangle:
pos: self.pos
size: self.size
canvas.after:
Color:
rgba: self.separator_color
Line:
points: self.x, self.y, self.x + self.width, self.y
BoxLayout:
orientation: "vertical"
MenuBar:
MenuSubmenu:
text: "Submenu 1"
MenuDropDown:
Button:
text: "Button 11"
on_release: app.button(11)
Button:
text: "Button 12"
on_release: app.button(12)
Button:
text: "Button 13"
on_release: app.button(13)
MenuButton:
text: "Button 2"
on_release: app.button(2)
MenuButton:
text: "Button 3"
on_release: app.button(3)
Button:
text: "Nothing"
background_color: 0.4, 0.4, 0.4, 1
background_normal: ""
如果你知道发生了什么,或者你可以将我重定向到更合适的地方以找到答案,那就太好了。
正如@inclement 所警告的那样,我没有正确使用下拉菜单。您不应该在 kv 文件中直接添加下拉小部件。
所以我查看了 actionbar
在 kivy 中是如何工作的(解释 here and source code here),结果我更新了我的菜单栏。
这是我的 python 代码:
#!/usr/bin/env python3
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.dropdown import DropDown
from kivy.uix.spinner import Spinner
from kivy.properties import ListProperty, ObjectProperty,\
StringProperty, BooleanProperty, NumericProperty
#from kivy.uix.actionbar import ActionBar, ActionView, ActionGroup, ActionButton
class MenuItem(Widget):
'''Background color, in the format (r, g, b, a).'''
background_color_normal = ListProperty([0.2, 0.2, 0.2, 1])
background_color_down = ListProperty([0.3, 0.3, 0.3, 1])
background_color = ListProperty([])
separator_color = ListProperty([0.8, 0.8, 0.8, 1])
text_color = ListProperty([1,1,1,1])
inside_group = BooleanProperty(False)
pass
class MenuSubmenu(MenuItem, Spinner):
triangle = ListProperty()
def __init__(self, **kwargs):
self.list_menu_item = []
super().__init__(**kwargs)
self.dropdown_cls = MenuDropDown
def add_widget(self, item):
self.list_menu_item.append(item)
self.show_submenu()
def show_submenu(self):
self.clear_widgets()
for item in self.list_menu_item:
item.inside_group = True
self._dropdown.add_widget(item)
def _build_dropdown(self, *largs):
if self._dropdown:
self._dropdown.unbind(on_dismiss=self._toggle_dropdown)
self._dropdown.dismiss()
self._dropdown = None
self._dropdown = self.dropdown_cls()
self._dropdown.bind(on_dismiss=self._toggle_dropdown)
def _update_dropdown(self, *largs):
pass
def _toggle_dropdown(self, *largs):
self.is_open = not self.is_open
ddn = self._dropdown
ddn.size_hint_x = None
if not ddn.container:
return
children = ddn.container.children
if children:
ddn.width = max(self.width, max(c.width for c in children))
else:
ddn.width = self.width
for item in children:
item.size_hint_y = None
item.height = max([self.height, 48])
def clear_widgets(self):
self._dropdown.clear_widgets()
class MenuDropDown(DropDown):
pass
class MenuButton(MenuItem,Button):
icon = StringProperty(None, allownone=True)
pass
class MenuEmptySpace(MenuItem):
pass
class MenuBar(BoxLayout):
'''Background color, in the format (r, g, b, a).'''
background_color = ListProperty([0.2, 0.2, 0.2, 1])
separator_color = ListProperty([0.8, 0.8, 0.8, 1])
def __init__(self, **kwargs):
self.itemsList = []
super().__init__(**kwargs)
def add_widget(self, item, index=0):
if not isinstance(item, MenuItem):
raise TypeError("MenuBar accepts only MenuItem widgets")
super().add_widget(item, index)
if index == 0:
index = len(self.itemsList)
self.itemsList.insert(index, item)
if __name__ == '__main__':
class MenuApp(App):
def button(self, nb):
print("Button", nb, "triggered")
MenuApp().run()
这里是对应的文件 menu.kv
:
#:kivy 1.0.9
<MenuButton>:
size_hint_x: None if not root.inside_group else 1
width: self.texture_size[0] + 32
Image:
allow_stretch: True
opacity: 1 if root.icon else 0
source: root.icon
pos: root.x + 4, root.y + 4
size: root.width - 8, root.height - 8
<MenuSubmenu>:
size_hint_x: None
width: self.texture_size[0] + 32
triangle: (self.right-14, self.y+7, self.right-7, self.y+7, self.right-7, self.y+14)
canvas.after:
Color:
rgba: self.separator_color
Triangle:
points: self.triangle
<MenuButton,MenuSubmenu>:
background_normal: ""
background_down: ""
background_color: self.background_color_down if self.state=='down' else self.background_color_normal
color: self.text_color
<MenuEmptySpace>:
size_hint_x: 1
<MenuItem>:
background_color: self.background_color_normal
canvas.before:
Color:
rgba: self.background_color
Rectangle:
pos: self.pos
size: self.size
canvas.after:
Color:
rgba: self.separator_color
Line:
rectangle: self.x,self.y,self.width,self.height
<MenuBar>:
size_hint_y: None
height: 48
canvas.before:
Color:
rgba: self.background_color
Rectangle:
pos: self.pos
size: self.size
canvas.after:
Color:
rgba: self.separator_color
Line:
rectangle: self.x, self.y, self.width, self.height
BoxLayout:
orientation: "vertical"
MenuBar:
MenuButton:
icon: "/home/matthieu/internships/singapore/drawings/joytube_color_small.png"
width: 100
MenuButton:
text: "Button 1"
on_release: app.button(1)
MenuSubmenu:
text: "Submenu 2"
MenuButton:
text: "Button 21"
on_release: app.button(21)
MenuButton:
text: "Button 22"
on_release: app.button(22)
MenuButton:
text: "Button 23"
on_release: app.button(23)
MenuEmptySpace:
MenuSubmenu:
text: "Submenu 3"
MenuButton:
text: "Button 31"
on_release: app.button(31)
Button:
text: "Nothing"
background_color: 0.4, 0.4, 0.4, 1
background_normal: ""
MenuBar:
height: 30
希望对其他想制作菜单栏的人有所帮助。
我正在尝试在 Kivy(Python 的 GUI)中创建一个通用菜单栏,但我在使用下拉菜单时遇到了问题。它们只出现了一部分,我不知道为什么(参见子菜单 1):
如果你想查看,这是我的代码:
#!/usr/bin/env python3
from kivy.app import App
#from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.dropdown import DropDown
from kivy.properties import ListProperty, ObjectProperty
#from kivy.uix.actionbar import ActionBar, ActionView, ActionGroup, ActionButton
class MenuItem(ButtonBehavior, Label):
'''Background color, in the format (r, g, b, a).'''
background_color_normal = ListProperty([0.2, 0.2, 0.2, 1])
background_color_down = ListProperty([0.3, 0.3, 0.3, 1])
background_color = ListProperty()
separator_color = ListProperty([0.8, 0.8, 0.8, 1])
pass
class MenuSubmenu(MenuItem):
# The list of submenu items in dropdown menu
submenu = ObjectProperty(None)
def add_widget(self, submenu, **kwargs):
if isinstance(submenu, MenuDropDown):
self.submenu = submenu
super().add_widget(submenu, **kwargs)
def on_release(self, **kwargs):
super().on_release(**kwargs)
self.submenu.open(self)
class MenuDropDown(DropDown):
pass
class MenuButton(MenuItem):
pass
class MenuBar(BoxLayout):
'''Background color, in the format (r, g, b, a).'''
background_color = ListProperty([0.2, 0.2, 0.2, 1])
separator_color = ListProperty([0.8, 0.8, 0.8, 1])
def __init__(self, **kwargs):
self.itemsList = []
super().__init__(**kwargs)
def add_widget(self, item, index=0):
if not isinstance(item, MenuItem):
raise TypeError("MenuBar accepts only MenuItem widgets")
super().add_widget(item, index)
if index == 0:
index = len(self.itemsList)
self.itemsList.insert(index, item)
class MenuApp(App):
def button(self, nb):
print("Button", nb, "triggered")
if __name__ == '__main__':
MenuApp().run()
这里是 kv 文件:
#:kivy 1.0.9
<MenuItem>:
background_color: self.background_color_down if self.state=='down' else self.background_color_normal
color: (1,1,1,1)
canvas.before:
Color:
rgba: self.background_color
Rectangle:
pos: self.pos
size: self.size
canvas.after:
Color:
rgba: self.separator_color
Line:
rectangle: self.x,self.y,self.width,self.height
<MenuBar>:
size_hint_y: None
height: 40
canvas.before:
Color:
rgba: self.background_color
Rectangle:
pos: self.pos
size: self.size
canvas.after:
Color:
rgba: self.separator_color
Line:
points: self.x, self.y, self.x + self.width, self.y
BoxLayout:
orientation: "vertical"
MenuBar:
MenuSubmenu:
text: "Submenu 1"
MenuDropDown:
Button:
text: "Button 11"
on_release: app.button(11)
Button:
text: "Button 12"
on_release: app.button(12)
Button:
text: "Button 13"
on_release: app.button(13)
MenuButton:
text: "Button 2"
on_release: app.button(2)
MenuButton:
text: "Button 3"
on_release: app.button(3)
Button:
text: "Nothing"
background_color: 0.4, 0.4, 0.4, 1
background_normal: ""
如果你知道发生了什么,或者你可以将我重定向到更合适的地方以找到答案,那就太好了。
正如@inclement 所警告的那样,我没有正确使用下拉菜单。您不应该在 kv 文件中直接添加下拉小部件。
所以我查看了 actionbar
在 kivy 中是如何工作的(解释 here and source code here),结果我更新了我的菜单栏。
这是我的 python 代码:
#!/usr/bin/env python3
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.dropdown import DropDown
from kivy.uix.spinner import Spinner
from kivy.properties import ListProperty, ObjectProperty,\
StringProperty, BooleanProperty, NumericProperty
#from kivy.uix.actionbar import ActionBar, ActionView, ActionGroup, ActionButton
class MenuItem(Widget):
'''Background color, in the format (r, g, b, a).'''
background_color_normal = ListProperty([0.2, 0.2, 0.2, 1])
background_color_down = ListProperty([0.3, 0.3, 0.3, 1])
background_color = ListProperty([])
separator_color = ListProperty([0.8, 0.8, 0.8, 1])
text_color = ListProperty([1,1,1,1])
inside_group = BooleanProperty(False)
pass
class MenuSubmenu(MenuItem, Spinner):
triangle = ListProperty()
def __init__(self, **kwargs):
self.list_menu_item = []
super().__init__(**kwargs)
self.dropdown_cls = MenuDropDown
def add_widget(self, item):
self.list_menu_item.append(item)
self.show_submenu()
def show_submenu(self):
self.clear_widgets()
for item in self.list_menu_item:
item.inside_group = True
self._dropdown.add_widget(item)
def _build_dropdown(self, *largs):
if self._dropdown:
self._dropdown.unbind(on_dismiss=self._toggle_dropdown)
self._dropdown.dismiss()
self._dropdown = None
self._dropdown = self.dropdown_cls()
self._dropdown.bind(on_dismiss=self._toggle_dropdown)
def _update_dropdown(self, *largs):
pass
def _toggle_dropdown(self, *largs):
self.is_open = not self.is_open
ddn = self._dropdown
ddn.size_hint_x = None
if not ddn.container:
return
children = ddn.container.children
if children:
ddn.width = max(self.width, max(c.width for c in children))
else:
ddn.width = self.width
for item in children:
item.size_hint_y = None
item.height = max([self.height, 48])
def clear_widgets(self):
self._dropdown.clear_widgets()
class MenuDropDown(DropDown):
pass
class MenuButton(MenuItem,Button):
icon = StringProperty(None, allownone=True)
pass
class MenuEmptySpace(MenuItem):
pass
class MenuBar(BoxLayout):
'''Background color, in the format (r, g, b, a).'''
background_color = ListProperty([0.2, 0.2, 0.2, 1])
separator_color = ListProperty([0.8, 0.8, 0.8, 1])
def __init__(self, **kwargs):
self.itemsList = []
super().__init__(**kwargs)
def add_widget(self, item, index=0):
if not isinstance(item, MenuItem):
raise TypeError("MenuBar accepts only MenuItem widgets")
super().add_widget(item, index)
if index == 0:
index = len(self.itemsList)
self.itemsList.insert(index, item)
if __name__ == '__main__':
class MenuApp(App):
def button(self, nb):
print("Button", nb, "triggered")
MenuApp().run()
这里是对应的文件 menu.kv
:
#:kivy 1.0.9
<MenuButton>:
size_hint_x: None if not root.inside_group else 1
width: self.texture_size[0] + 32
Image:
allow_stretch: True
opacity: 1 if root.icon else 0
source: root.icon
pos: root.x + 4, root.y + 4
size: root.width - 8, root.height - 8
<MenuSubmenu>:
size_hint_x: None
width: self.texture_size[0] + 32
triangle: (self.right-14, self.y+7, self.right-7, self.y+7, self.right-7, self.y+14)
canvas.after:
Color:
rgba: self.separator_color
Triangle:
points: self.triangle
<MenuButton,MenuSubmenu>:
background_normal: ""
background_down: ""
background_color: self.background_color_down if self.state=='down' else self.background_color_normal
color: self.text_color
<MenuEmptySpace>:
size_hint_x: 1
<MenuItem>:
background_color: self.background_color_normal
canvas.before:
Color:
rgba: self.background_color
Rectangle:
pos: self.pos
size: self.size
canvas.after:
Color:
rgba: self.separator_color
Line:
rectangle: self.x,self.y,self.width,self.height
<MenuBar>:
size_hint_y: None
height: 48
canvas.before:
Color:
rgba: self.background_color
Rectangle:
pos: self.pos
size: self.size
canvas.after:
Color:
rgba: self.separator_color
Line:
rectangle: self.x, self.y, self.width, self.height
BoxLayout:
orientation: "vertical"
MenuBar:
MenuButton:
icon: "/home/matthieu/internships/singapore/drawings/joytube_color_small.png"
width: 100
MenuButton:
text: "Button 1"
on_release: app.button(1)
MenuSubmenu:
text: "Submenu 2"
MenuButton:
text: "Button 21"
on_release: app.button(21)
MenuButton:
text: "Button 22"
on_release: app.button(22)
MenuButton:
text: "Button 23"
on_release: app.button(23)
MenuEmptySpace:
MenuSubmenu:
text: "Submenu 3"
MenuButton:
text: "Button 31"
on_release: app.button(31)
Button:
text: "Nothing"
background_color: 0.4, 0.4, 0.4, 1
background_normal: ""
MenuBar:
height: 30
希望对其他想制作菜单栏的人有所帮助。