Open dcl920108 opened 6 days ago
This 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()
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!