首页 > 解决方案 > 添加标签到 IM/聊天屏幕 kivy

问题描述

我正在尝试在我的 kivy 应用程序中制作一个屏幕,两个用户可以在其中互相聊天,但我无法在屏幕上显示聊天消息。

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

我已使用此答案将标签添加到屏幕的左侧(用户发送的消息): 标签一直在 ScrollView 屏幕 x 轴 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

标签: kivykivy-language

解决方案


BoxLayout我认为您可以使用 a而不是 a来做您想做的事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()

我添加spacing并消除了不再需要BoxLayout的空。Labels

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


推荐阅读