-
-
Notifications
You must be signed in to change notification settings - Fork 677
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue with VKeyboard - Inaccurate Key Presses and Input Handling with KivyMD TextField #1753
Comments
Minimal Reproducible ExampleThis script is a minimal reproducible example of a KivyMD-based graphical user interface (GUI) application. It demonstrates the following features:
The main purpose of this example is to show how to create a user interface with KivyMD that includes interactive elements such as buttons, text fields, and a virtual keyboard, while ensuring the interface is responsive and user-friendly. 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() |
Hello @dcl920108, were you able to fix this? I'm currently experiencing the same issue. |
Hi @Tickets14 I haven't fully resolved the issue yet, but I have discovered a few possible approaches that might help fix it. Right now, I'm trying to understand how vkeyboard integrates and works together with native Kivy elements. You can find the example provided by Kivy here: Kivy Keyboard Examples. I hope to solve it this week. At the moment, I am able to press the keys accurately, but there are still some issues left. If I make any progress, I'll update this thread with the latest information. |
Hi @Tickets14 problem solved! The problem was solved by adding the following lines to configure the keyboard behavior: from kivy.config import Config
Config.set('kivy', 'keyboard_mode', 'systemanddock')
Config.set('kivy', 'keyboard_layout', './numeric.json') How it solved the problem:
Note: It is also important to upgrade Kivy and KivyMD to the latest versions. This helps avoid compatibility issues and makes sure that the latest bug fixes and features are included, improving the overall stability and behavior of virtual keyboard support. |
Removed Manual Configuration of VKeyboard Layout: In the original code, you manually created a self.keyboard = VKeyboard()
self.keyboard.layout_path = "./" # Set the keyboard layout file path
self.keyboard.layout = 'numeric.json' # Use only the numeric keyboard layout This manual approach led to issues in managing the keyboard configuration and caused inconsistencies in how the keyboard appeared and behaved. By removing this and instead using the global configuration ( |
Description:
I am encountering an issue using
VKeyboard
in my KivyMD application where the virtual key presses do not register correctly, and the input values are not being entered properly into the KivyMDTextField
.Here are the main issues:
TextField
:VKeyboard
are not properly reflected in the KivyMDTextField
.on_key_up
), the value does not appear correctly in the input field, or it does not get entered at all.Code Extract:
Below is the part of the code dealing with the virtual keyboard (
VKeyboard
) and the KivyMDTextField
:Issues Summary:
Key Press Mismatch:
VKeyboard
, the displayed or registered key is incorrect. For example, pressing "4" may result in "6" being registered.Input Issue with
TextField
:on_key_up
event, the value is not correctly entered into the KivyMDTextField
.Expected Behavior:
TextField
.Actual Behavior:
TextField
.Environment:
Steps to Reproduce:
VKeyboard
with a numeric layout (numeric.json
) as shown above.VKeyboard
to aTextField
using the focus event (on_focus
) to control the visibility of the virtual keyboard.TextField
.Additional Information:
numeric.json
) used in this example contains a simple numeric keypad layout.on_key_up
) confirms the mismatch between the key press and the input value.I would appreciate any advice or suggestions on how to resolve this issue. Thank you!
The text was updated successfully, but these errors were encountered: