Python Kivy:滚动视图和文本定位
我正在尝试使用 kivy 开发一款应用程序。首先的目的是从鸡尾酒列表中选择一种鸡尾酒。我也使用纯python,没有kv语言。到目前为止,我很高兴我以某种方式成功地在线翻译了所有 kv 示例和说明,但现在我克服了两个我无法解决的问题。
问题1:Scrollview 并没有真正滚动。它显示动画,但总是跳回顶部。我读了很多建议将高度设置为minimum_height,但仍然没有努力。也许与自我有关。 ...事情不起作用,但我无法弄清楚。
问题 2:当我按下其中一个按钮时,它会显示一个新屏幕,其中包含鸡尾酒的描述(将被扩展)。到目前为止一切顺利,但如果我返回菜单(左上角箭头)并选择另一种鸡尾酒,标签就会改变位置。但只有在第一次之后,它才会停留在相同(错误)的位置。
我删除了很多代码,但由于我假设问题位于元素定义中的某个位置,因此我将它们保留在代码示例中,因此它很长,但我希望人们仍然可以使用它来进行故障排除。
import kivy
from kivy.config import Config
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.button import Button
from kivy.properties import ListProperty
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import Rectangle
from functools import partial
kivy.require('2.0.0')
Config.set('graphics', 'width', '380')
Config.set('graphics', 'height', '730')
cocktails = [["AA"],["BB"],["CC"],["DD"],["EE"],["FF"],["GG"],["HH"],["II"],["JJ"],["KK"],["LL"],["MM"]]
cur_cocktail = 0
class VBoxLayout(BoxLayout):
def __init__(self):
super().__init__(orientation="vertical")
class TopRowBoxLayout(BoxLayout):
def __init__(self):
super().__init__(size_hint_x=1, size_hint_y=0.08, orientation="horizontal")
class ColoredBackgroundMixin:
_color = None
_rect = None
background_color = ListProperty([0.0, 0.0, 0.0, 1.0])
def __init__(self, *, background_color, **kwargs):
super().__init__(**kwargs)
with self.canvas.before:
self.background_color = background_color
self._color = Color(*background_color)
self._rect = Rectangle(size=self.size, pos=self.pos)
self.bind(size=self._update_rect, pos=self._update_rect, background_color=self._update_rect)
def _update_rect(self, instance, value):
self._color.rgba = instance.background_color
self._rect.pos = instance.pos
self._rect.size = instance.size
class ColoredLabel(ColoredBackgroundMixin, Label):
pass
class TopRowBackButton(Button):
def __init__(self):
super().__init__(size_hint_x=0.15, size_hint_y=1, text="<-", background_normal="", background_color=(0.55, 0.12, 0.12, 1))
class TopRowFiller(ColoredLabel):
def __init__(self):
super().__init__(size_hint_x=0.85, size_hint_y=1, text="", background_color=[0.55, 0.12, 0.12, 1])
class TopRowFillerFull(ColoredLabel):
def __init__(self):
super().__init__(background_color=[0.55, 0.12, 0.12, 1], text="")
class StdScrollView(ScrollView):
def __init__(self):
super().__init__(do_scroll_x=False, do_scroll_y=True)
class MainLayout(GridLayout):
def __init__(self):
super().__init__(cols=1, padding=(0.06*self.width, 0.06*self.width), spacing=0.02*self.width,
size_hint_y=None, height=self.minimum_height)
class StdButton(Button):
def __init__(self, text):
self.text = text
super().__init__(text=self.text, size_hint=(1, None), height=0.6*self.width)
class ChooseScreen(Screen):
def __init__(self, **kwargs):
super(ChooseScreen, self).__init__(**kwargs)
self.define_layout()
def define_layout(self):
self.base_layout = VBoxLayout()
self.add_widget(self.base_layout)
self.header = TopRowBoxLayout()
self.base_layout.add_widget(self.header)
self.header.add_widget(TopRowFillerFull())
self.scroll_area = StdScrollView()
self.base_layout.add_widget(self.scroll_area)
self.main_layout = MainLayout()
self.scroll_area.add_widget(self.main_layout)
self.buttons = []
for i in range(0, len(cocktails)):
self.t = cocktails[i][0]
self.buttons.append("")
self.buttons[i] = StdButton(text=self.t)
self.buttons[i].bind(on_press=partial(self.switch, self.t))
self.main_layout.add_widget(self.buttons[i])
def switch(self, name, *args):
pass
global cur_cocktail
for i in range(0,len(cocktails)):
if cocktails[i][0] == name:
cur_cocktail = i
print(cocktails[i][0])
self.manager.get_screen("display").define_layout()
self.manager.current = 'display'
class DisplayScreen(Screen):
def __init__(self, **kwargs):
super(DisplayScreen, self).__init__(**kwargs)
def define_layout(self):
self.base_layout = VBoxLayout()
self.add_widget(self.base_layout)
self.header = TopRowBoxLayout()
self.base_layout.add_widget(self.header)
self.back_button = TopRowBackButton()
self.back_button.bind(on_press=self.back_to_menu)
self.header.add_widget(self.back_button)
self.header.add_widget(TopRowFiller())
self.scroll_area = StdScrollView()
self.base_layout.add_widget(self.scroll_area)
self.main_layout = MainLayout()
self.scroll_area.add_widget(self.main_layout)
self.main_layout.add_widget(Label(text=""))
self.text = Label(text=cocktails[cur_cocktail][0], bold=True, underline=True, size_hint = (1, None), height=0.6*self.width)
self.main_layout.add_widget(self.text)
def back_to_menu(self,*args):
self.manager.current = "choose"
self.clear_widgets()
class CocktailApp(App):
def build(self):
self.sm = sm = ScreenManager()
sm.add_widget(ChooseScreen(name='choose'))
sm.add_widget(DisplayScreen(name='display'))
return sm
if __name__ == '__main__':
CocktailApp().run()
让我知道是否需要对代码进行更多准备。
编辑1
虽然约翰·安德森的评论指出了这个问题,但有一段时间我仍然无法找到解决方案。我缺少的是 MainLayout 绑定的精确定义。二传手在这里发挥了作用。
class MainLayout(GridLayout):
def __init__(self):
super().__init__(cols=1, padding=(0.06*self.width, 0.06*self.width), spacing=0.02*self.width,
size_hint_y=None)
self.bind(minimum_height=self.setter('height'))
不管怎样,我仍然不明白我在问题2中描述的事情,即标签的移动。所以我仍然希望在这里得到一些进一步的解释。
I am trying to get kind of an app with kivy. The purpose to start with is to choose a cocktail from a list of cocktails. I am also using pure python, no kv language. So far I was glad that I somehow managed to translate all the kv examples and instructions online, but now I conquered two problems, which I am not able to solve.
Problem 1: The Scrollview is not really scrolling. It shows the animation, but always jumps back to the top. I read a lot of advices to set the height to the minimum_height, but still no effort. Maybe something with the self. ... thing is not working but I can not figure it out.
Problem 2: When I push one of the buttons, it shows a new screen with a description of the cocktail (which will be extended). So far so good, but if I go back to the menu (arrow top left), and choose another cocktail, the Label changed position. But only after the first time, after that it stays at the same (wrong) position.
I removed a lot of my code, but as I assume the problem is located somewhere in the definitions of my elements, I kept them in the Code example, so it is quite long, but I hope one can still use it for troubleshooting.
import kivy
from kivy.config import Config
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.button import Button
from kivy.properties import ListProperty
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import Rectangle
from functools import partial
kivy.require('2.0.0')
Config.set('graphics', 'width', '380')
Config.set('graphics', 'height', '730')
cocktails = [["AA"],["BB"],["CC"],["DD"],["EE"],["FF"],["GG"],["HH"],["II"],["JJ"],["KK"],["LL"],["MM"]]
cur_cocktail = 0
class VBoxLayout(BoxLayout):
def __init__(self):
super().__init__(orientation="vertical")
class TopRowBoxLayout(BoxLayout):
def __init__(self):
super().__init__(size_hint_x=1, size_hint_y=0.08, orientation="horizontal")
class ColoredBackgroundMixin:
_color = None
_rect = None
background_color = ListProperty([0.0, 0.0, 0.0, 1.0])
def __init__(self, *, background_color, **kwargs):
super().__init__(**kwargs)
with self.canvas.before:
self.background_color = background_color
self._color = Color(*background_color)
self._rect = Rectangle(size=self.size, pos=self.pos)
self.bind(size=self._update_rect, pos=self._update_rect, background_color=self._update_rect)
def _update_rect(self, instance, value):
self._color.rgba = instance.background_color
self._rect.pos = instance.pos
self._rect.size = instance.size
class ColoredLabel(ColoredBackgroundMixin, Label):
pass
class TopRowBackButton(Button):
def __init__(self):
super().__init__(size_hint_x=0.15, size_hint_y=1, text="<-", background_normal="", background_color=(0.55, 0.12, 0.12, 1))
class TopRowFiller(ColoredLabel):
def __init__(self):
super().__init__(size_hint_x=0.85, size_hint_y=1, text="", background_color=[0.55, 0.12, 0.12, 1])
class TopRowFillerFull(ColoredLabel):
def __init__(self):
super().__init__(background_color=[0.55, 0.12, 0.12, 1], text="")
class StdScrollView(ScrollView):
def __init__(self):
super().__init__(do_scroll_x=False, do_scroll_y=True)
class MainLayout(GridLayout):
def __init__(self):
super().__init__(cols=1, padding=(0.06*self.width, 0.06*self.width), spacing=0.02*self.width,
size_hint_y=None, height=self.minimum_height)
class StdButton(Button):
def __init__(self, text):
self.text = text
super().__init__(text=self.text, size_hint=(1, None), height=0.6*self.width)
class ChooseScreen(Screen):
def __init__(self, **kwargs):
super(ChooseScreen, self).__init__(**kwargs)
self.define_layout()
def define_layout(self):
self.base_layout = VBoxLayout()
self.add_widget(self.base_layout)
self.header = TopRowBoxLayout()
self.base_layout.add_widget(self.header)
self.header.add_widget(TopRowFillerFull())
self.scroll_area = StdScrollView()
self.base_layout.add_widget(self.scroll_area)
self.main_layout = MainLayout()
self.scroll_area.add_widget(self.main_layout)
self.buttons = []
for i in range(0, len(cocktails)):
self.t = cocktails[i][0]
self.buttons.append("")
self.buttons[i] = StdButton(text=self.t)
self.buttons[i].bind(on_press=partial(self.switch, self.t))
self.main_layout.add_widget(self.buttons[i])
def switch(self, name, *args):
pass
global cur_cocktail
for i in range(0,len(cocktails)):
if cocktails[i][0] == name:
cur_cocktail = i
print(cocktails[i][0])
self.manager.get_screen("display").define_layout()
self.manager.current = 'display'
class DisplayScreen(Screen):
def __init__(self, **kwargs):
super(DisplayScreen, self).__init__(**kwargs)
def define_layout(self):
self.base_layout = VBoxLayout()
self.add_widget(self.base_layout)
self.header = TopRowBoxLayout()
self.base_layout.add_widget(self.header)
self.back_button = TopRowBackButton()
self.back_button.bind(on_press=self.back_to_menu)
self.header.add_widget(self.back_button)
self.header.add_widget(TopRowFiller())
self.scroll_area = StdScrollView()
self.base_layout.add_widget(self.scroll_area)
self.main_layout = MainLayout()
self.scroll_area.add_widget(self.main_layout)
self.main_layout.add_widget(Label(text=""))
self.text = Label(text=cocktails[cur_cocktail][0], bold=True, underline=True, size_hint = (1, None), height=0.6*self.width)
self.main_layout.add_widget(self.text)
def back_to_menu(self,*args):
self.manager.current = "choose"
self.clear_widgets()
class CocktailApp(App):
def build(self):
self.sm = sm = ScreenManager()
sm.add_widget(ChooseScreen(name='choose'))
sm.add_widget(DisplayScreen(name='display'))
return sm
if __name__ == '__main__':
CocktailApp().run()
Let me know if some more preparation on the code has to be done.
EDIT 1
While the comment of John Anderson pointed out the problem, for some time I was still not able to figure out a solution. What I was missing is the precise definition of the binding of the MainLayout. The setter did the trick here.
class MainLayout(GridLayout):
def __init__(self):
super().__init__(cols=1, padding=(0.06*self.width, 0.06*self.width), spacing=0.02*self.width,
size_hint_y=None)
self.bind(minimum_height=self.setter('height'))
Anyway, I still not understand the thing I described in problem 2, the shift of the labels. So I would still appreciate some further explanation here.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论