【问题标题】:Adding labels to IM/Chat screen kivy添加标签到 IM/聊天屏幕 kivy
【发布时间】:2020-05-15 21:14:20
【问题描述】:

我正在尝试在我的 kivy 应用程序中制作一个屏幕,让两个用户可以互相聊天,但我在如何在屏幕上显示聊天消息时遇到了问题。

大致目标示例:(向下滚动页面约 60% 以查看) https://medium.com/flutter-community/building-a-chat-app-with-flutter-and-firebase-from-scratch-9eaa7f41782e

我已使用此答案在屏幕左侧添加标签(用户发送的消息): Label keeps running off edge of ScrollView screen x axis Kivy

但我现在正试图弄清楚如何在屏幕右侧添加标签(用户收到的消息)。我假设我需要一个有两列的GridLayout,我需要先添加一个白色标签,然后再添加消息标签,然后它应该出现在右侧。我无法让标签尽可能地显示在屏幕上。

我也无法根据每个标签中的文本量调整标签大小,我想我可能需要将每一行添加为新的GridLayout

肯定已经用 kivy 完成了类似的事情,并且有一个指南可以遵循,但我已经四处搜索,我只能找到老式的 UI 聊天屏幕设计。

py 文件

import kivy
from kivymd.app import MDApp
from kivy.properties import ObjectProperty, StringProperty, NumericProperty, ListProperty
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.core.text import Label as CoreLabel
from kivymd.uix.label import MDLabel
from kivymd.uix.button import MDFillRoundFlatButton, MDRoundFlatIconButton, MDRaisedButton, MDTextButton, MDIconButton
from kivy.core.window import Window

Window.size = (481, 600)
Window.clearcolor = (1,1,1,1)

class Chat(Screen):
    chat_layout = ObjectProperty(None)
    def send_message(self):
        if self.ids.message.text:
            lab1 = Label()

            # calculate max allowable width in the GridLayout
            max_width = self.chat_layout.width - self.chat_layout.spacing[0] - self.chat_layout.padding[0] - \
                        self.chat_layout.padding[2]

            # specify font and font_size (so that the CoreLabel uses the same)
            self.chat_layout.add_widget(
                SmoothLabel.create_sized_label(text=self.ids.message.text, max_width=max_width, font_name='Roboto',
                                               font_size=15))

            self.chat_layout.add_widget(lab1)
            self.ids.message.text = ""
        else:
            pass

    def recv_message(self):
        if self.ids.message.text:
            # calculate max allowable width in the GridLayout
            max_width = self.chat_layout.width - self.chat_layout.spacing[0] - self.chat_layout.padding[0] - \
                        self.chat_layout.padding[2]

            # specify font and font_size (so that the CoreLabel uses the same)
            chat_label = SmoothLabel.create_sized_label(text=self.ids.message.text, max_width=max_width, font_name='Roboto',
                                               font_size=15)
            lab1 = Label(width=max_width)
            self.chat_layout.add_widget(lab1)
            self.chat_layout.add_widget(chat_label)
            self.ids.message.text = ""
        else:
            pass

class SmoothLabel(Label):
    @staticmethod
    def create_sized_label(**kwargs):
        max_width = kwargs.pop('max_width', 0)
        if max_width <= 0:
            # just create a SmoothLabel without a text_size
            return SmoothLabel(**kwargs)

        # calculate what the SmoothLabel size will be
        core_label = CoreLabel(padding=[10,10], **kwargs)  # use same padding as SmoothLabel
        core_label.refresh()

        if core_label.width > max_width:
            # width is too big, use text_size to force wrapping
            return SmoothLabel(text_size=(max_width,None), **kwargs)
        else:
            # width is OK, no need for text_size
            return SmoothLabel(**kwargs)

class WindowManager(ScreenManager):
    pass


class MyApp(MDApp):
    def build(self):
        kv = Builder.load_file("kivy.kv")
        self.sm = WindowManager()

        screens = [Chat(name="chat")]
        for screen in screens:
            self.sm.add_widget(screen)

        self.sm.current = "chat"
        return self.sm


if __name__ == '__main__':
    MyApp().run()

kv文件

<Chat>:
    name: "chat"
    chat_layout: chat_layout

    GridLayout:
        cols: 1
        GridLayout:
            cols: 2
            id: chat_layout
            padding: 15

        GridLayout:
            cols: 3
            MDRaisedButton:
                on_release: root.send_message()
                text: "send message"
            TextInput:
                id: message
                size_hint: None, None
                size: 150,50
                hint_text:
                    "Send message"
            MDRaisedButton:
                on_release: root.recv_message()
                text: "receive message"

<SmoothLabel>:
    size_hint: None, None
    size: self.texture_size
    padding: 10, 10
    multiline: True
    background_color: 0,0,0,0
    background_normal: ""
    back_color: 1,0,1,1
    border_radius: [6]
    canvas.before:
        Color:
            rgba: 0.2,0.6,1,1 #This changes the label colour
        RoundedRectangle:
            size: self.size
            pos: self.pos
            radius: self.border_radius

【问题讨论】:

  • 我尝试打印chat_label 的各种宽度属性(size,size_hint_x,width,texture_size)但它们都返回None。我在想如果我能得到chat_label的宽度,那么我可以事先添加一个空白标签,即max_width减去chat_label的宽度

标签: kivy kivy-language


【解决方案1】:

我认为您可以通过使用BoxLayout 而不是GridLayout 并使用pos_hint 将聊天标签定位在左侧或右侧来做您想做的事情:

import kivy
from kivymd.app import MDApp
from kivy.properties import ObjectProperty, StringProperty, NumericProperty, ListProperty
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.core.text import Label as CoreLabel
from kivymd.uix.label import MDLabel
from kivymd.uix.button import MDFillRoundFlatButton, MDRoundFlatIconButton, MDRaisedButton, MDTextButton, MDIconButton
from kivy.core.window import Window

Window.size = (481, 600)
Window.clearcolor = (1,1,1,1)

class Chat(Screen):
    chat_layout = ObjectProperty(None)
    def send_message(self):
        if self.ids.message.text:
            # calculate max allowable width in the BoxLayout
            max_width = (self.chat_layout.width - self.chat_layout.spacing - self.chat_layout.padding[0] - \
                        self.chat_layout.padding[2]) * 0.75

            # specify font and font_size (so that the CoreLabel uses the same)
            self.chat_layout.add_widget(
                SmoothLabel.create_sized_label(text=self.ids.message.text, max_width=max_width, font_name='Roboto',
                                               font_size=15, pos_hint={'x':0}))
            self.ids.message.text = ""
            self.ids.scroll.scroll_y = 0  # make sure last message is visible
        else:
            pass

    def recv_message(self):
        if self.ids.message.text:
            # calculate max allowable width in the BoxLayout
            max_width = (self.chat_layout.width - self.chat_layout.spacing - self.chat_layout.padding[0] - \
                        self.chat_layout.padding[2]) * 0.75

            # specify font and font_size (so that the CoreLabel uses the same)
            self.chat_layout.add_widget(
                SmoothLabel.create_sized_label(text=self.ids.message.text, max_width=max_width, font_name='Roboto',
                                               font_size=15, pos_hint={'right':1}))
            self.ids.message.text = ""
            self.ids.scroll.scroll_y = 0  # make sure last message is visible
        else:
            pass

class SmoothLabel(Label):
    @staticmethod
    def create_sized_label(**kwargs):
        max_width = kwargs.pop('max_width', 0)
        if max_width <= 0:
            # just create a SmoothLabel without a text_size
            return SmoothLabel(**kwargs)

        # calculate what the SmoothLabel size will be
        core_label = CoreLabel(padding=[10,10], **kwargs)  # use same padding as SmoothLabel
        core_label.refresh()

        if core_label.width > max_width:
            # width is too big, use text_size to force wrapping
            return SmoothLabel(text_size=(max_width,None), **kwargs)
        else:
            # width is OK, no need for text_size
            return SmoothLabel(**kwargs)


class WindowManager(ScreenManager):
    pass

kv_str = '''
<Chat>:
    name: "chat"
    chat_layout: chat_layout

    GridLayout:
        cols: 1
        ScrollView:
            id: scroll
            BoxLayout:
                orientation: 'vertical'
                id: chat_layout
                padding: 15
                spacing: 15
                size_hint: 1, None
                height: self.minimum_height

        GridLayout:
            cols: 3
            MDRaisedButton:
                on_release: root.send_message()
                text: "send message"
            TextInput:
                id: message
                size_hint: None, None
                size: 150,50
                hint_text:
                    "Send message"
            MDRaisedButton:
                on_release: root.recv_message()
                text: "receive message"

<SmoothLabel>:
    size_hint: None, None
    size: self.texture_size
    padding: 10, 10
    multiline: True
    background_color: 0,0,0,0
    background_normal: ""
    back_color: 1,0,1,1
    border_radius: [6]
    canvas.before:
        Color:
            rgba: 0.2,0.6,1,1 #This changes the label colour
        RoundedRectangle:
            size: self.size
            pos: self.pos
            radius: self.border_radius
'''

class MyApp(MDApp):
    def build(self):
        # kv = Builder.load_file("kivy.kv")
        Builder.load_string(kv_str)
        self.sm = WindowManager()

        screens = [Chat(name="chat")]
        for screen in screens:
            self.sm.add_widget(screen)

        self.sm.current = "chat"
        return self.sm


if __name__ == '__main__':
    MyApp().run()

我在BoxLayout 中添加了spacing,并删除了不再需要的空Labels

我使用Builder.load_string() 代替load_file() 只是为了我自己的方便。

【讨论】:

  • 谢谢,我没有意识到你可以定位BoxLayout 小部件,这让它变得简单多了。唯一的问题是chat_label 宽度始终设置为max_width 与这个表达式text_size=(max_width, None),我想让它考虑到标签中有多少文本,所以如果没有它可能会更窄里面有很多文字。我想我需要做类似text_size=(chat_label.width, None), max_width=max_width 的事情,但我知道chat_label.width 不是我指的它的宽度。我该怎么做?
  • 糟糕,抱歉,我误解了您的问题,并认为您希望 chat_label 的宽度始终相同。您可以恢复您的create_sized_label() 方法并使用该方法计算chat_label 的宽度。
  • 我已经尝试添加回create_sized_label(),例如chat_label = SmoothLabel.create_sized_label(text=self.ids.message.text, text_size=(max_width, None), font_name='Roboto', font_size=15, pos_hint={'right':1}),但它没有改变任何东西,我可能实施错误 - 我不完全理解create_sized_label如何调整大小。
  • 我已经更新了我的答案,让它再次使用create_sized_label()
  • 谢谢,不知道发生了什么变化,但现在可以正常使用
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多