描述: 我在 KivyMD 应用程序中使用
VKeyboard
时遇到问题,其中虚拟按键未正确注册,并且输入值未正确输入到 KivyMD TextField
中。
主要问题如下:
按键注册不准确:
无法在
TextField
中输入正确的值:
VKeyboard
的值未正确反映在 KivyMD TextField
中。on_key_up
),但该值并未正确显示在输入字段中,或者根本没有输入。{
"title" : "Numeric",
"description" : "A numeric keypad",
"cols" : 3,
"rows": 4,
"normal_1": [
["7", "7", "7", 1],
["8", "8", "8", 1],
["9", "9", "9", 1]],
"normal_2": [
["4", "4", "4", 1],
["5", "5", "5", 1],
["6", "6", "6", 1]],
"normal_3": [
["1", "1", "1", 1],
["2", "2", "2", 1],
["3", "3", "3", 1]],
"normal_4": [
["0", "0", "0", 1],
[".", ".", ".", 1],
["\u232b", null, "backspace", 1]],
"shift_1": [
["7", "7", "7", 1],
["8", "8", "8", 1],
["9", "9", "9", 1]],
"shift_2": [
["4", "4", "4", 1],
["5", "5", "5", 1],
["6", "6", "6", 1]],
"shift_3": [
["1", "1", "1", 1],
["2", "2", "2", 1],
["3", "3", "3", 1]],
"shift_4": [
["0", "0", "0", 1],
[".", ".", ".", 1],
["\u232b", null, "backspace", 1]]
}
下面是处理虚拟键盘(
VKeyboard
)和KivyMD TextField
的代码部分:
按键不匹配:
VKeyboard
上的某个键时,显示或注册的键不正确。例如,按“4”可能会导致“6”被注册。TextField
输入问题:
on_key_up
事件捕获按键,该值也没有正确输入到 KivyMD TextField
。TextField
中。TextField
中。VKeyboard
) 设置 numeric.json
,如上所示。VKeyboard
) 将 TextField
绑定到 on_focus
来控制虚拟键盘的可见性。TextField
中。numeric.json
) 包含一个简单的数字键盘布局。on_key_up
)确认按键与输入值不匹配。对于如何解决此问题,我将不胜感激。谢谢!
此脚本是基于 KivyMD 的图形用户界面 (GUI) 应用程序的最小可重现示例。它展示了以下功能:
此示例的主要目的是展示如何使用 KivyMD 创建用户界面,其中包括按钮、文本字段和虚拟键盘等交互元素,同时确保界面响应灵敏且用户友好。
from kivy.metrics import dp
from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen
from kivymd.uix.button import MDButton, MDButtonIcon, MDButtonText
from kivymd.uix.label import MDLabel
from kivymd.uix.floatlayout import MDFloatLayout
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.fitimage import FitImage
from kivymd.uix.screenmanager import MDScreenManager
from kivymd.uix.textfield import (
MDTextField,
MDTextFieldLeadingIcon,
MDTextFieldHintText,
MDTextFieldHelperText,
)
from kivy.uix.vkeyboard import VKeyboard
from kivy.clock import Clock
from datetime import datetime
class MainScreen(MDScreen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# 设置浅色主题和主色调
self.theme_cls = MDApp.get_running_app().theme_cls
self.theme_cls.theme_style = "Light"
self.theme_cls.primary_palette = "Green"
# 设置屏幕背景颜色
self.md_bg_color = (1, 1, 1, 1)
# 创建整体布局
layout = MDBoxLayout(orientation="horizontal")
self.left_layout = MDFloatLayout(size_hint=(0.4, 1))
self.right_layout = MDFloatLayout(size_hint=(0.6, 1))
qpcr_button = MDButton(
MDButtonIcon(icon="ruler-square-compass"),
MDButtonText(text="Thermal Cycle", font_style="Title"),
style="elevated",
pos_hint={"center_x": 0.7, "center_y": 0.6},
height="200dp",
size_hint=(3.2, 0.8) # size_hint=(0.4, 0.1)
)
iso_button = MDButton(
MDButtonIcon(icon="liquor"),
MDButtonText(text=" Isothermal ", font_style="Title"),
style="elevated",
pos_hint={"center_x": 1.8, "center_y": 0.6},
height="200dp",
size_hint=(6.4, 1.6) # size_hint=(0.4, 0.1)
)
iso_button.bind(on_press=self.switch_to_isothermal) # 绑定切换屏幕的方法
# 将按钮添加到左侧布局
self.left_layout.add_widget(qpcr_button)
self.left_layout.add_widget(iso_button)
# 创建底部的时间标签
self.date_time_label = MDLabel(
text="YYYY-MM-DD HH:MM:SS",
halign="left",
size_hint=(None, None),
size=(dp(200), dp(40)),
pos_hint={"x": -0.65, "y": 0.01},
)
# 将时间和 Logo 添加到右侧布局
self.right_layout.add_widget(self.date_time_label)
# 将左右布局添加到整体布局中
layout.add_widget(self.left_layout)
layout.add_widget(self.right_layout)
# 将整体布局添加到屏幕中
self.add_widget(layout)
# 定期更新时间
Clock.schedule_interval(self.update_date_time, 1)
def update_date_time(self, dt):
now = datetime.now()
self.date_time_label.text = now.strftime("%Y-%m-%d %H:%M:%S")
def switch_to_isothermal(self, *args):
# 切换到名为 'isothermal' 的屏幕
if self.manager:
print("Switching to AGD screen...") # 添加调试信息
self.manager.current = "isothermal"
class MotorControlScreen(MDScreen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.locked = False
self.data_records = []
self.temperature_records = [] # 用于存储温度记录
self.current_cycle = 0
self.total_cycles = 20
self.num_samples = 1000
self.countdown_time = 360
self.image_widget = None
self.show_temperature_plot = False
# 创建 VKeyboard 对象
self.keyboard = VKeyboard()
self.keyboard.layout_path = "./" # 设置键盘布局文件的路径(假设布局文件位于当前目录下)
self.keyboard.layout = 'numeric.json' # 只使用数字键盘布局
self.keyboard.size_hint = (1, 0.3)
self.keyboard.pos_hint = {"center_x": 0, "y": 0}
self.keyboard.bind(on_key_up=self.on_key_up)
print("Keyboard binding successful") # 添加调试信息
# 调用 build_ui() 并将生成的 screen 作为主界面
self.add_widget(self.build_ui())
def build_ui(self):
screen = MDScreen(md_bg_color=(1, 1, 1, 1))
layout = MDBoxLayout(orientation="horizontal")
self.left_layout = MDFloatLayout(size_hint=(0.4, 1))
self.right_layout = MDFloatLayout(size_hint=(0.6, 1))
back_button = MDButton(
MDButtonIcon(icon="arrow-left"), # 设置图标为返回箭头
MDButtonText(text="Back", font_style="Title"),
style="elevated",
pos_hint={"center_x": 2.2, "center_y": 0.95}, # 左上角位置
size_hint=(0.25, 0.1),
on_release=self.switch_to_main_screen
)
toggle_plot_button = MDButton(
MDButtonIcon(icon="swap-horizontal"),
MDButtonText(text="Switching chart"),
style="elevated",
pos_hint={"center_x": -0.35, "center_y": 0.2},
height="56dp",
size_hint_x=0.6
)
# 添加温度输入框
self.temperature_input = MDTextField(
MDTextFieldLeadingIcon(icon="thermometer"),
MDTextFieldHintText(text="Target Temperature (°C)"),
MDTextFieldHelperText(text="Please enter the target temperature (e.g., 98)", mode="persistent"),
mode="outlined",
size_hint_x=None,
width="240dp",
pos_hint={"center_x": 0.5, "center_y": 0.9},
)
self.temperature_input.bind(focus=self.show_keyboard)
# 添加循环次数输入框
self.cycle_count_input = MDTextField(
MDTextFieldLeadingIcon(icon="repeat"),
MDTextFieldHintText(text="Cycle Count"),
MDTextFieldHelperText(text="Please enter the number of cycles (e.g., 20)", mode="persistent"),
mode="outlined",
size_hint_x=None,
width="240dp",
pos_hint={"center_x": 0.5, "center_y": 0.7},
)
self.cycle_count_input.bind(focus=self.show_keyboard)
# 添加每个循环时间输入框
self.cycle_time_input = MDTextField(
MDTextFieldLeadingIcon(icon="timer"),
MDTextFieldHintText(text="Cycle Time (seconds)"),
MDTextFieldHelperText(text="Please enter the time for each cycle (e.g., 60)", mode="persistent"),
mode="outlined",
size_hint_x=None,
width="240dp",
pos_hint={"center_x": 0.5, "center_y": 0.5},
)
self.cycle_time_input.bind(focus=self.show_keyboard)
# 确认按钮,确认设定并开始实验流程
confirm_button = MDButton(
MDButtonIcon(icon="check"),
MDButtonText(text="Confirm Settings"),
style="elevated",
pos_hint={"center_x": 0.5, "center_y": 0.3},
height="56dp",
size_hint_x=0.6
)
# 绑定按钮到 toggle_plot() 函数,用于切换显示的图表
toggle_plot_button.bind(on_press=self.toggle_plot)
self.left_layout.add_widget(self.temperature_input)
self.left_layout.add_widget(self.cycle_count_input)
self.left_layout.add_widget(self.cycle_time_input)
self.left_layout.add_widget(confirm_button)
self.left_layout.add_widget(back_button)
self.right_layout.add_widget(toggle_plot_button)
self.right_layout.add_widget(self.keyboard)
layout.add_widget(self.left_layout)
layout.add_widget(self.right_layout)
screen.add_widget(layout)
return screen
def show_keyboard(self, instance, value):
if value: # 当输入框被聚焦时显示键盘
self.keyboard.opacity = 1
instance.focus = True # 确保输入框获得焦点
else: # 当失去焦点时隐藏键盘
self.keyboard.opacity = 0
def on_key_up(self, keyboard, keycode, *args):
print(f"Key pressed: {keycode}") # 调试信息
# 确保 keycode 至少包含两个元素,避免索引错误
if len(keycode) < 2:
return
# 检查哪个输入框当前处于聚焦状态
if self.temperature_input.focus:
print("Temperature input focused")
elif self.cycle_count_input.focus:
print("Cycle count input focused")
elif self.cycle_time_input.focus:
print("Cycle time input focused")
# 将输入的值添加到相应的输入框中
if keycode[1] == 'backspace':
# 如果按下的是回删键,则删除输入框中的最后一个字符
if self.temperature_input.focus:
self.temperature_input.text = self.temperature_input.text[:-1]
elif self.cycle_count_input.focus:
self.cycle_count_input.text = self.cycle_count_input.text[:-1]
elif self.cycle_time_input.focus:
self.cycle_time_input.text = self.cycle_time_input.text[:-1]
elif keycode[1].isdigit() or keycode[1].isalpha():
# 如果按下的是数字键或字母键,则将字符添加到相应的输入框
if self.temperature_input.focus:
self.temperature_input.text += keycode[1]
elif self.cycle_count_input.focus:
self.cycle_count_input.text += keycode[1]
elif self.cycle_time_input.focus:
self.cycle_time_input.text += keycode[1]
def switch_to_main_screen(self, instance):
if self.manager:
self.manager.current = "main"
def toggle_plot(self, instance):
self.show_temperature_plot = not self.show_temperature_plot
class MainApp(MDApp):
def build(self):
# 创建 ScreenManager 来管理所有界面
sm = MDScreenManager()
# 添加主界面
main_screen = MainScreen(name="main")
sm.add_widget(main_screen)
# 添加等温控制界面
isothermal_screen = MotorControlScreen(name="isothermal")
sm.add_widget(isothermal_screen)
# 设置初始显示的界面
sm.current = "main"
return sm
# 运行应用程序
if __name__ == "__main__":
MainApp().run()
根据文档:
VKeyboard 是 Kivy 的屏幕键盘。其操作的目的是 对用户透明。直接使用小部件是不行的 推荐。
您只需在主 Python 脚本的开头添加以下代码即可:
from kivy.config import Config # Keep these Config settings at the top
Config.set('kivy', 'keyboard_mode', 'systemanddock')
Config.set('kivy', 'keyboard_layout', './keyboard_layout.json') # setting a custom keyboard layout from local folder
然后,当
TextInput
收到focus
时,虚拟键盘将自动创建,无需任何额外的编码。
所有涉及
self.keyboard
的代码,包括 show_keyboard()
和 on_key_up()
方法,都可以删除。