Kivy/KivyMD中如何在.kv与非.kv定义的屏幕间切换
核心错误
你当前代码的根本问题是将CameraScreen定义为MDApp子类。Kivy/KivyMD 程序全局仅允许一个运行中的App实例,切换屏幕时调用CameraScreen().run()本质是启动第二个独立程序,完全不符合ScreenManager的屏幕切换逻辑。
正确实现方式是将CameraScreen改为和HomeScreen、ManageDataScreen一致的Screen子类,把摄像头相关逻辑迁移到该Screen类中,界面部分通过kv文件定义,所有屏幕统一交给ScreenManager管理。
修正步骤
1. 替换Python主文件代码
from kivymd.app import MDApp from kivy.uix.screenmanager import Screen from kivy.uix.image import Image from kivy.lang import Builder from kivy.graphics.texture import Texture from kivy.clock import Clock import cv2 import pytesseract class HomeScreen(Screen): pass class ManageDataScreen(Screen): pass # CameraScreen改为继承Screen,不再作为独立App class CameraScreen(Screen): def on_enter(self, *args): # 进入屏幕时初始化摄像头、启动帧读取调度 self.capture = cv2.VideoCapture(0) self.video_clock = Clock.schedule_interval(self.load_video, 1.0/30.0) def on_leave(self, *args): # 离开屏幕时释放资源,避免后台占用摄像头 self.video_clock.cancel() self.capture.release() def load_video(self, *args): ret, frame = self.capture.read() if not ret: return self.image_frame = frame buffer = cv2.flip(frame, 0).tobytes() texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt='bgr') texture.blit_buffer(buffer, colorfmt='bgr', bufferfmt='ubyte') self.ids.camera_image.texture = texture def take_picture(self, *args): image_name = "img_TIMESTAMP.png" img = cv2.cvtColor(self.image_frame, cv2.COLOR_BGR2GRAY) img = cv2.GaussianBlur(img, (3, 3), 0) img = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1] text_data = pytesseract.image_to_string(img, lang='eng', config="--psm 6") self.ids.ocr_result_label.text = text_data cv2.imshow("cv2 final image", img) cv2.imwrite(image_name, self.image_frame) GUI = Builder.load_file("kv/main.kv") class MainApp(MDApp): def build(self): return GUI def change_screen(self, screen_name): screen_manager = self.root.ids['screen_manager'] # 所有屏幕统一通过ScreenManager切换,不再启动新App screen_manager.current = screen_name if __name__ == '__main__': pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe" MainApp().run()
2. 修改kv/main.kv文件
添加摄像头屏幕的kv引入,同时将CameraScreen注册到ScreenManager中:
#:include kv/home_screen.kv #:include kv/manage_data_screen.kv #:include kv/camera_screen.kv GridLayout: cols: 1 ScreenManager: id: screen_manager HomeScreen: name: "home_screen" id: home_screen ManageDataScreen: name: "manage_data_screen" id: manage_data_screen CameraScreen: name: "camera_screen" id: camera_screen
3. 新建kv/camera_screen.kv文件
将原Python代码中build方法内的界面布局转为kv语法,控件id和Python代码中的引用保持一致:
<CameraScreen>: MDBoxLayout: orientation: "vertical" Image: id: camera_image MDLabel: id: ocr_result_label text: "" MDRaisedButton: text: "Capture" pos_hint: {"center_x": .5, "center_y": .5} size_hint: None, None on_press: root.take_picture()
注意事项
- 所有屏幕切换直接调用
app.change_screen("目标屏幕name")即可,比如切摄像头屏传"camera_screen",返回主界面传"home_screen" - 原代码中
cv2.flip(frame,0).tostring()在新版OpenCV中已弃用,替换为tobytes()避免运行警告 - 确保kv文件夹和Python主文件在同一目录下,kv文件路径和main.kv内的include路径完全匹配
- 新增的屏幕生命周期逻辑会在切出摄像头页面时自动释放摄像头、停止帧读取调度,避免后台空跑消耗性能
内容的提问来源于stack exchange,提问作者Cole Miller
相关产品推荐
相关产品推荐

