Kivy开发ChatGPT客户端:聊天气泡位置错位问题求助
Kivy ChatGPT客户端聊天气泡错位、相邻排列问题修复方案
问题根源分析
- 主界面
ChatInterface错误设置为横向布局,导致输入栏与聊天记录区域并排显示,引发整体布局错位 - 聊天气泡未根据消息角色区分对齐方式,所有气泡挤在同一侧
- 文本标签的宽度限制逻辑错误,导致内容溢出或气泡尺寸异常
- 聊天记录容器缺少内边距和间距,气泡贴边且无间隔
修复后的完整代码
import requests from kivy.app import App from kivy.lang import Builder from kivy.properties import ObjectProperty, StringProperty, NumericProperty from kivy.uix.boxlayout import BoxLayout from kivy.uix.recycleview import RecycleView from kivy.uix.recycleview.views import RecycleDataViewBehavior from kivy.uix.floatlayout import FloatLayout from kivy.uix.label import Label # 窗口配置 from kivy.config import Config Config.set('graphics', 'width', '400') Config.set('graphics', 'height', '600') Config.set('graphics', 'borderless', 0) Config.set('graphics', 'resizable', 0) Config.set('graphics', 'window_state', 'visible') Config.set('graphics', 'minimum_width', '400') Config.set('graphics', 'minimum_height', '600') # 替换为你的OpenAI API地址和密钥 OPENAI_API_URL = "https://api.openai.com/v1/chat/completions" OPENAI_API_KEY = "YOUR_API_KEY" Builder.load_string(""" <ChatBubble>: size_hint_y: None height: self.minimum_height padding: 10 FloatLayout: size_hint_x: None width: min(root.texture_width + 40, root.parent.width - 40) if root.parent else min(root.texture_width + 40, 360) pos_hint: {"x": 0} if root.role == "bot" else {"right": 1} height: root.height canvas.before: Color: rgba: (40/255,116/255,166/255, 1) if root.role == "bot" else (0.15, 0.15, 0.15, 1) RoundedRectangle: pos: self.pos size: self.size radius: [20, 20, 20, 20] if root.role == "bot" else [20, 20, 0, 20] Label: id: message_label text: root.text color: (1, 1, 1, 1) if root.role == "bot" else (0.9, 0.9, 0.9, 1) font_size: 16 text_size: (self.parent.width - 40, None) valign: "top" size_hint_y: None height: self.texture_size[1] + 20 pos: (20, 10) <ChatInterface>: orientation: "vertical" history: history_rv input: input_text canvas.before: Color: rgba: (0.2, 0.2, 0.2, 1) Rectangle: pos: self.pos size: self.size RecycleView: id: history_rv data: [{"text": msg["content"], "role": msg["role"]} for msg in root.messages] viewclass: "ChatBubble" size_hint_y: 1 RecycleBoxLayout: orientation: "vertical" size_hint_y: None height: self.minimum_height spacing: 15 padding: [10, 10, 10, 10] BoxLayout: size_hint_y: None height: 70 padding: [10, 5, 10, 5] spacing: 10 TextInput: id: input_text multiline: False on_text_validate: root.send_message() background_color: 0.3, 0.3, 0.3, 1 padding: [15, 10] Button: text: "Send" on_press: root.send_message() size_hint_x: None width: 80 """) class ChatBubble(RecycleDataViewBehavior, FloatLayout): text = StringProperty() role = StringProperty() texture_width = NumericProperty(0) def on_text(self, instance, value): lbl = self.ids.message_label lbl.text_size = (self.parent.width - 40, None) if self.parent else (360, None) lbl.texture_update() self.texture_width = lbl.texture_size[0] self.height = lbl.texture_size[1] + 40 class ChatInterface(BoxLayout): history = ObjectProperty() input = ObjectProperty() messages = [] def send_message(self): message = self.input.text.strip() if not message: return payload = { "model": "gpt-3.5-turbo", "messages": self.messages + [{"role": "user", "content": message}] } headers = { "Content-Type": "application/json", "Authorization": f"Bearer {OPENAI_API_KEY}" } try: response = requests.post(OPENAI_API_URL, json=payload, headers=headers) response.raise_for_status() completion_data = response.json() completion_message = completion_data["choices"][0]["message"]["content"] assistant_role = completion_data["choices"][0]["message"]["role"] self.messages.append({"role": "user", "content": message}) self.messages.append({"role": "bot", "content": completion_message}) self.history.data = [{"text": msg["content"], "role": msg["role"]} for msg in self.messages] self.input.text = "" self.history.scroll_to(self.history.children[0]) except Exception as e: print(f"请求异常: {e}") class ChatApp(App): def build(self): return ChatInterface() if __name__ == "__main__": ChatApp().run()
关键修改说明
修正主布局方向
将ChatInterface的orientation从horizontal改为vertical,让聊天记录区域在上、输入栏在下,符合常规聊天界面结构。实现气泡左右对齐
- 将
ChatBubble基类改为FloatLayout,通过pos_hint设置对齐规则:bot消息左对齐、用户消息右对齐 - 限制气泡最大宽度为容器宽度减去边距,避免过长消息撑满屏幕
- 将
修复文本布局逻辑
- 动态计算文本所需宽度,自动调整气泡尺寸
- 基于气泡容器宽度限制文本换行,确保内容不会溢出气泡
优化视觉细节
- 给聊天记录容器添加内边距和气泡间距,提升界面层次感
- 修复OpenAI API请求的角色映射,确保消息角色匹配
- 更新聊天记录时直接替换整个数据列表,保证界面实时刷新
内容的提问来源于stack exchange,提问作者user21428851
相关产品推荐
相关产品推荐

