Files
Kivy_APP/main.py
2025-07-31 10:17:36 +08:00

768 lines
31 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from kivy.properties import ObjectProperty, StringProperty, ListProperty
from datetime import datetime, timedelta
from kivy.core.text import LabelBase
from kivy.metrics import dp
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.image import Image
from kivymd.app import MDApp
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivymd.uix.datatables import MDDataTable
import random
import threading
import csv
from kivymd.uix.textfield import MDTextField
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDFlatButton
from kivymd.uix.dialog import MDDialog
from kivymd.uix.label import MDLabel
from kivy.uix.label import Label
from kivy.utils import platform
from kivy.clock import Clock
from kivy.uix.widget import Widget
from kivy.graphics import Color, Rectangle
from kivy_garden.graph import Graph, LinePlot
import modbus_tk
import modbus_tk.defines as cst
import socket
from modbus_client import ModbusClient
import re
from threading import Thread
from functools import partial
from collections import defaultdict
from random import randint
from kivymd.theming import ThemeManager
LabelBase.register(name="MPoppins", fn_regular="fonts/Chinese/msyh.ttf")
LabelBase.register(name="BPoppins", fn_regular="fonts/Chinese/msyh.ttf")
LabelBase.register(name="RRubik", fn_regular="fonts/Chinese/msyh.ttf")
LabelBase.register(name="RCro", fn_regular="fonts/Chinese/msyh.ttf")
LabelBase.register(name="RPac", fn_regular="fonts/Chinese/msyh.ttf")
class MainScreen(Screen):
pass
class HomeScreen(Screen):
home = ObjectProperty(None)
class LoginScreen(Screen):
login = ObjectProperty(None)
class ProfileScreen(Screen):
profile = ObjectProperty(None)
class ProfileEditScreen(Screen):
profile_edit = ObjectProperty(None)
class HistoryScreen(Screen):
history = ObjectProperty(None)
class ModifyCurrentParamScreen(Screen):
modify_current_param = ObjectProperty(None)
class ModifyVoltageParamScreen(Screen):
modify_voltage_param = ObjectProperty(None)
class ModifyLeakageParamScreen(Screen):
modify_leakage_param = ObjectProperty(None)
class ModifySystemParamScreen(Screen):
modify_system_param = ObjectProperty(None)
class ModifyProtectionParamScreen(Screen):
modify_protection_param = ObjectProperty(None)
class RealTimeCurveScreen(Screen):
real_time_curve = ObjectProperty(None)
class ControlCommandScreen(Screen):
control_command = ObjectProperty(None)
class AboutScreen(Screen):
about = ObjectProperty(None)
# Window.size = (dp(360), dp(680))
class app(MDApp):
wifi_status_text = StringProperty("")
def __init__(self):
super().__init__()
self.signup_branch = None
self.user_name = None
self.rec_grid = None
self.recommend_screen = None
self.book_input = None
self.data_tables = None
self.search_grid = None
self.search_field = None
self.renew_grid = None
self.renewable_books = []
self.renew = self.renewable_books
self.history_grid = None
self.books_history = []
self.history = self.books_history
self.image_label_grid = None
self.books = []
self.items = self.books
self.book_name = None
self.isbn = None
self.a = None
self.back_button = None
self.user = None
self.dialog = None
self.dialog2 = None
self.profile_edit_screen = None
self.edit_email = None
self.edit_prn = None
self.edit_name = None
self.edit_wifi_ssid = None
self.edit_branch = None
self.profile_semester = None
self.profile_branch = None
self.profile_wifi_ssid = None
self.profile_prn = None
self.profile_user_pass = None
self.profile_user_name = None
self.user_no = None
self.user_email = None
self.user_prn = None
self.d2 = None
self.d3 = None
self.d4 = None
self.user_pass = None
self.d1 = None
self.edit_semester = None
self.otp_button = None
self.new_pass = None
self.new_pass1 = None
self.mobile_no = None
self.password = None
self.username = None
self.modbus_master = ModbusClient() # Modbus连接对象
self.modbus_ip = None # Modbus服务器IP
self.modbus_port = None # Modbus服务器端口
self.theme_cls.font_styles["H1"] = ["BPoppins", 96, False, -1.5]
self.theme_cls.font_styles["H2"] = ["BPoppins", 60, False, -0.5]
self.theme_cls.font_styles["H3"] = ["BPoppins", 48, False, 0]
self.theme_cls.font_styles["H4"] = ["BPoppins", 34, False, 0.25]
self.theme_cls.font_styles["H5"] = ["BPoppins", 25, False, 0.15]
self.theme_cls.font_styles["H6"] = ["BPoppins", 16, False, 0.15]
self.theme_cls.font_styles["Subtitle1"] = ["BPoppins", 16, False, 0.15]
self.theme_cls.font_styles["Subtitle2"] = ["BPoppins", 14, False, 0.1]
self.theme_cls.font_styles["Body1"] = ["BPoppins", 16, False, 0.5]
self.theme_cls.font_styles["Body2"] = ["BPoppins", 14, False, 0.25]
self.theme_cls.font_styles["Button"] = ["BPoppins", 14, True, 1.25]
self.theme_cls.font_styles["Caption"] = ["BPoppins", 13, False, 0.15]
self.theme_cls.font_styles["Overline"] = ["BPoppins", 10, True, 1.5]
def on_text_field_focus(self, instance, value):
if value:
# 当MDTextField获得焦点时
# 获取ScrollView和MDList的引用
scroll_view = self.root.get_screen('modify_current_param').ids.real_time_scroll_view
md_list = self.root.get_screen('modify_current_param').ids.real_time_md_list
# 计算MDTextField在MDList中的相对位置
# instance.y 是MDTextField在父控件BoxLayout中的y坐标
# instance.parent.y 是BoxLayout在MDList中的y坐标
# 所以MDTextField在MDList中的y坐标是 instance.y + instance.parent.y
text_field_y_in_mdlist = instance.y + instance.parent.y
# 计算需要滚动的目标位置
# 目标是让MDTextField的底部与ScrollView的底部对齐或者至少在可见区域内
# scroll_view.height 是ScrollView的可见高度
# md_list.height 是MDList的总高度
# scroll_view.scroll_y 的范围是0到10表示底部1表示顶部
# 计算MDTextField相对于MDList顶部的距离
relative_y = md_list.height - text_field_y_in_mdlist - instance.height
# 将相对距离转换为scroll_y的值
# 确保不会滚动到超出范围
if md_list.height > scroll_view.height:
target_scroll_y = relative_y / (md_list.height - scroll_view.height)
# 限制target_scroll_y在0到1之间
target_scroll_y = max(0, min(1, target_scroll_y))
scroll_view.scroll_y = target_scroll_y
def build(self):
Builder.load_file('kv/app.kv')
screen_manager = ScreenManager()
screen_manager.add_widget(MainScreen(name="main"))
screen_manager.add_widget(HomeScreen(name="home"))
screen_manager.add_widget(LoginScreen(name="login"))
screen_manager.add_widget(HistoryScreen(name="history"))
screen_manager.add_widget(ProfileScreen(name="profile"))
screen_manager.add_widget(ProfileEditScreen(name="profile_edit"))
screen_manager.add_widget(ModifyCurrentParamScreen(name="modify_current_param"))
screen_manager.add_widget(ModifyVoltageParamScreen(name="modify_voltage_param"))
screen_manager.add_widget(ModifyLeakageParamScreen(name="modify_leakage_param"))
screen_manager.add_widget(ModifySystemParamScreen(name="modify_system_param"))
screen_manager.add_widget(ModifyProtectionParamScreen(name="modify_protection_param"))
screen_manager.add_widget(RealTimeCurveScreen(name="real_time_curve"))
screen_manager.add_widget(ControlCommandScreen(name="control_command"))
screen_manager.add_widget(AboutScreen(name="about"))
# 添加其他屏幕
return screen_manager
#############################################ALL INPUT TEXT############################################################
def on_start(self):
# 从根窗口中获取名为"login"的屏幕(登录界面)
login_screen = self.root.get_screen("login")
# 从根窗口中获取名为"home"的屏幕(主界面)
home_screen = self.root.get_screen("home")
# 通过界面ID获取主界面中的用户信息组件
self.user = home_screen.ids.user
# 从根窗口中获取名为"profile"的屏幕(个人资料界面)
profile_screen = self.root.get_screen("profile")
# 通过界面ID获取个人资料界面中的姓名展示组件
self.profile_user_name = profile_screen.ids.profile_user_name
# 通过界面ID获取个人资料界面中的邮箱展示组件
self.profile_user_pass = profile_screen.ids.profile_user_pass
# 通过界面ID获取个人资料界面中的PRN身份标识展示组件
self.profile_prn = profile_screen.ids.profile_prn
# 通过界面ID获取个人资料界面中的手机号展示组件
self.profile_wifi_ssid = profile_screen.ids.profile_wifi_ssid
# 通过界面ID获取个人资料界面中的所属部门/专业展示组件
self.profile_branch = profile_screen.ids.profile_branch
# 通过界面ID获取个人资料界面中的年级/学期展示组件
self.profile_semester = profile_screen.ids.profile_semester
# 从根窗口中获取名为"profile_edit"的屏幕(个人资料编辑界面)
self.profile_edit_screen = self.root.get_screen("profile_edit")
# 通过界面ID获取资料编辑界面中的姓名编辑输入框组件
self.edit_name = self.profile_edit_screen.ids.edit_name
# 通过界面ID获取资料编辑界面中的邮箱编辑输入框组件
self.edit_email = self.profile_edit_screen.ids.edit_email
# 通过界面ID获取资料编辑界面中的PRN身份标识编辑输入框组件
self.edit_prn = self.profile_edit_screen.ids.edit_prn
# 通过界面ID获取资料编辑界面中的手机号编辑输入框组件
self.edit_wifi_ssid = self.profile_edit_screen.ids.edit_no
# 通过界面ID获取资料编辑界面中的所属部门/专业编辑选择组件
self.edit_branch = self.profile_edit_screen.ids.edit_branch
# 通过界面ID获取资料编辑界面中的年级/学期编辑选择组件
self.edit_semester = self.profile_edit_screen.ids.edit_sem
self.root.bind(current=self.on_screen_changed)
self.register_update_event = None
self.wifi_update_event = None
def on_screen_changed(self, instance, value):
"""屏幕切换时启动/停止刷新"""
if value == "real_time_curve":
# 进入目标屏幕时启动定时刷新
if not self.register_update_event:
self.register_update_event = Clock.schedule_interval(self.update_register_display, 1)
else:
# 离开时停止刷新
if self.register_update_event:
self.register_update_event.cancel()
self.register_update_event = None
if value == "login":
if not self.wifi_update_event:
self.wifi_update_event = Clock.schedule_interval(self.update_wifi_status, 1)
else:
if self.wifi_update_event:
self.wifi_update_event.cancel()
self.wifi_update_event = None
#############################################@Frequently used functions##################################################
@staticmethod
def change_cursor(is_enter):
"""改变鼠标指针样式"""
# 如果is_enter为True鼠标进入目标区域将鼠标指针改为手型
if is_enter:
Window.set_system_cursor('hand')
# 否则(鼠标离开目标区域),将鼠标指针改回默认箭头样式
else:
Window.set_system_cursor('arrow')
@staticmethod
def toggle_password_visibility(password_field, icon_button):
"""切换密码输入框的可见性状态"""
# 检查密码输入框当前是否处于密码隐藏状态
if password_field.password:
# 如果是隐藏状态,切换为可见状态
password_field.password = False
# 更新图标为"eye"(眼睛图标,表示当前可见)
icon_button.icon = "eye"
else:
# 如果是可见状态,切换为隐藏状态(显示为圆点/星号)
password_field.password = True
# 更新图标为"eye-off"(闭眼图标,表示当前隐藏)
icon_button.icon = "eye-off"
def clear_text(self):
pass
def on_press_back_arrow(self):
pass
############################################@DIALOG BOX most used functions##############################################
# MDDialog box function 'dialog'
def dialog1(self, text, title="Tips"):
close_button = MDFlatButton(
text="关闭",
font_name="MPoppins",
on_release=self.close_dialog
)
content_label = Label(
text=text,
font_name="MPoppins",
color=(0, 0, 0, 1),
size_hint_y=None,
height=dp(30),
halign="center",
valign="middle"
)
content_label.bind(size=content_label.setter('text_size'))
self.dialog = MDDialog(
title=title,
type="custom",
content_cls=content_label,
size_hint=(0.84, None),
buttons=[close_button]
)
self.dialog.open()
def dialog2(self, title, text):
close_button = MDFlatButton(
text="关闭",
font_name="MPoppins",
on_release=self.close_dialog
)
content_label = Label(
text=text,
font_name="MPoppins",
color=(0, 0, 0, 1),
size_hint_y=None,
height=dp(30),
halign="center",
valign="middle"
)
content_label.bind(size=content_label.setter('text_size'))
self.dialog = MDDialog(
title=title,
type="custom",
content_cls=content_label,
size_hint=(0.84, None),
buttons=[close_button]
)
self.dialog.open()
# MDDialog box dismiss function 'close_dialog'
def close_dialog(self, *args):
self.dialog.dismiss()
#############################################Modbus Functions############################################################
def show_modify_dialog(self, label_id, current_value=""):
"""显示修改对话框"""
if not self.dialog2:
# 创建输入框
self.modify_input = MDTextField(
id="modify_input",
hint_text="请输入新值",
input_filter="int", # 保持整数输入限制
text=current_value
)
# 创建对话框
self.dialog2 = MDDialog(
title="修改参数",
type="custom",
content_cls=MDBoxLayout(
self.modify_input,
orientation="vertical",
padding=dp(10),
spacing=dp(10),
size_hint_y=None,
height=dp(100)
),
buttons=[
MDFlatButton(
text="取消",
on_press=lambda x: self.dialog2.dismiss()
),
MDFlatButton(
text="确认",
on_press=lambda x: self.confirm_modify(label_id)
)
]
)
else:
# 重置输入框内容
self.modify_input.text = current_value
self.dialog2.open()
def confirm_modify(self, label_id):
"""确认修改并更新标签,增强鲁棒性"""
try:
new_value = self.modify_input.text
if not new_value:
self.show_dialog("错误", "请输入新值")
return
screen = self.root.get_screen("real_time_curve")
label = screen.ids.get(label_id, None)
if label:
label.text = f"通信超时: {new_value}"
# 调用原有的修改方法,增加异常捕获
try:
self.modify_register(new_value)
except Exception as e:
self.show_dialog("错误", f"寄存器修改失败:{e}")
else:
self.show_dialog("错误", "未找到目标标签")
except Exception as e:
self.show_dialog("错误", f"操作异常:{e}")
finally:
if self.dialog2:
self.dialog2.dismiss()
def read_modbus_registers(self, slave_id=1, address=0, count=1):
"""
通用的Modbus保持寄存器读取函数优化版
:param slave_id: 从机ID
:param address: 寄存器地址
:param count: 读取数量
:return: {'success': bool, 'data': list, 'msg': str}
"""
return self.modbus_master.read_holding_registers(slave_id, address, count)
def modify_register(self, field_widget, input_text):
"""
用户主动修改时,写入指定寄存器并刷新显示
:param field_widget: MDTextField控件对象
:param input_text: 用户输入的值
"""
address = getattr(field_widget, 'comm_address', None)
slave_id = 1 # 可根据实际情况动态获取
if address is None:
self.show_dialog("错误", "未设置通讯地址")
return
if not input_text:
self.show_dialog("错误", "请输入值")
return
try:
value = int(input_text)
result = self.write_modbus_register(slave_id=slave_id, address=address, value=value)
# self.show_dialog("操作结果", result)
self.update_register_display(0)
except ValueError:
self.show_dialog("错误", "请输入有效整数")
def write_modbus_register(self, slave_id=1, address=0, value=None):
"""
通用写入Modbus保持寄存器单个寄存器低耦合版
:param slave_id: 从机ID
:param address: 寄存器地址
:param value: 要写入的值整数0-65535
:return: {'success': bool, 'msg': str}
"""
return self.modbus_master.write_single_register(slave_id, address, value)
def show_dialog(self, title, message):
self.dialog1(message,title)
def update_register_display(self, dt):
real_time_screen = self.root.get_screen("real_time_curve")
widgets = [w for w in real_time_screen.ids.values()
if isinstance(w, MDTextField) and hasattr(w, 'comm_address')]
# 收集未聚焦的寄存器地址
addr_widget_map = {}
for w in widgets:
if not w.focus:
addr = getattr(w, 'comm_address', None)
if addr is not None:
addr_widget_map[addr] = w
if not addr_widget_map:
return
# 分组连续地址段(例如 [1,2,3,10,11,12] -> [[1,2,3], [10,11,12]])
sorted_addresses = sorted(addr_widget_map.keys())
grouped_ranges = []
group = [sorted_addresses[0]]
for addr in sorted_addresses[1:]:
if addr == group[-1] + 1:
group.append(addr)
else:
grouped_ranges.append(group)
group = [addr]
grouped_ranges.append(group)
# 开启线程异步读取每段
for group in grouped_ranges:
start = group[0]
count = group[-1] - start + 1
thread = Thread(target=self._read_and_update_range, args=(start, count, group, addr_widget_map))
thread.start()
def _read_and_update_range(self, start, count, group, addr_widget_map):
try:
result = self.read_modbus_registers(slave_id=1, address=start, count=count)
except Exception as e:
result = {'success': False, 'error': str(e)}
# 在主线程中更新 UI
Clock.schedule_once(partial(self._update_widgets_with_data, start, group, addr_widget_map, result), 0)
def _update_widgets_with_data(self, start, group, addr_widget_map, result, dt):
if result.get('success') and result.get('data'):
data = result['data']
for addr in group:
widget = addr_widget_map.get(addr)
index = addr - start
if widget and 0 <= index < len(data):
new_value = str(data[index])
if widget.text != new_value:
widget.text = new_value
elif widget:
widget.text = "索引错误"
else:
for addr in group:
widget = addr_widget_map.get(addr)
if widget:
widget.text = f"失败: {result.get('error', '读取失败')}"
###################################LoginPageWork-Start#################################################
def update_wifi_status(self, dt):
# 只有当前屏幕为 login 时才更新
if self.root.current != "login":
return
if platform != 'android':
self.wifi_status_text = '非Android设备'
return
try:
from jnius import autoclass, cast
PythonActivity = autoclass('org.kivy.android.PythonActivity')
Context = autoclass('android.content.Context')
activity = PythonActivity.mActivity
WifiManager = autoclass('android.net.wifi.WifiManager')
Formatter = autoclass('android.text.format.Formatter')
wifi_service = activity.getSystemService(Context.WIFI_SERVICE)
wifi_manager = cast('android.net.wifi.WifiManager', wifi_service)
if not wifi_manager.isWifiEnabled():
self.wifi_status_text = 'WiFi 未启用'
return
wifi_info = wifi_manager.getConnectionInfo()
if wifi_info is None:
self.wifi_status_text = 'WiFi 信息不可用'
return
ssid = wifi_info.getSSID()
ssid_display = ssid.strip('"') if ssid else "SSID Unknown"
ip_int = wifi_info.getIpAddress()
ip_str = Formatter.formatIpAddress(ip_int) if ip_int != 0 else "0.0.0.0"
link_speed = wifi_info.getLinkSpeed() # Mbps
bssid = wifi_info.getBSSID()
rssi = wifi_info.getRssi()
self.wifi_status_text = (
f'SSID: {ssid_display}\n'
f'IP: {ip_str}\n'
f'速度: {link_speed} Mbps\n'
f'BSSID: {bssid}\n'
f'信号: {rssi} dBm'
)
except Exception as e:
self.wifi_status_text = f'获取WiFi信息失败\n{e}'
def verify(self, obj=None):
try:
if platform == "android":
from jnius import autoclass
PythonActivity = autoclass('org.kivy.android.PythonActivity')
Context = autoclass('android.content.Context')
activity = PythonActivity.mActivity
wifi_service = activity.getSystemService(Context.WIFI_SERVICE)
wifi_info = wifi_service.getConnectionInfo()
# 获取当前连接的WiFi名称并处理引号和大小写
wifi_id = wifi_info.getSSID().strip('"').lower()
else:
# 非Android平台使用模拟WiFi
wifi_id = "zhizhan-2"
except Exception as e:
self.dialog1(f"获取WiFi信息失败:{e}")
return
try:
with open("data/Users.csv", "r", encoding="utf-8") as file:
# 使用DictReader读取CSV通过列名访问数据
csv_reader = csv.DictReader(file)
for row in csv_reader:
# 获取CSV中存储的WiFi SSID并处理大小写
csv_ssid = row['Wifi_SSID'].strip().lower()
# 精确匹配WiFi SSID
if wifi_id == csv_ssid:
# 通过列名获取用户信息
name = row['User']
prn = row['Reserve'] # 根据实际列名调整
email = row['Reserve'] # 根据实际列名调整
number = row['Reserve'] # 根据实际列名调整
password = row['User_pass']
branch = row['Reserve'] # 根据实际列名调整
semester = row['Reserve'] # 根据实际列名调整
# 更新界面显示的用户信息
self.root.current = "home"
self.user.text = f"[b]Hey! {name}[/b]"
self.profile_user_name.text = self.edit_name.text = name
self.profile_user_pass.text = self.edit_email.text = email
self.profile_prn.text = self.edit_prn.text = prn
self.profile_wifi_ssid.text = row['Wifi_SSID']
self.profile_semester.text = self.edit_semester.text = semester
self.profile_branch.text = self.edit_branch.text = branch
# 连接Modbus设备
self.connect_modbus()
self.dialog1(f"欢迎你,{name}!\n认证成功!")
return
except Exception as e:
self.dialog1(f"读取用户信息失败:{e}")
return
# 认证失败提示
self.dialog1("认证失败,请检查网络或确保手机权限打开定位和连接到目标WiFi")
def connect_modbus(self):
def _connect_in_background():
modbus_ip = None
modbus_port = 502
master = None
try:
# 获取当前WiFi SSID保持不变
if platform == "android":
from jnius import autoclass
PythonActivity = autoclass('org.kivy.android.PythonActivity')
Context = autoclass('android.content.Context')
activity = PythonActivity.mActivity
wifi_service = activity.getSystemService(Context.WIFI_SERVICE)
wifi_info = wifi_service.getConnectionInfo()
current_wifi_id = wifi_info.getSSID().strip('"').lower()
else:
current_wifi_id = "zhizhan-2"
# 读取CSV配置保持不变
with open("data/Users.csv", "r", encoding="utf-8") as file:
csv_reader = csv.DictReader(file)
for row in csv_reader:
csv_ssid = row['Wifi_SSID'].strip().lower()
if current_wifi_id == csv_ssid:
modbus_ip = row['Modbus_IP']
modbus_port = int(row['Modbus_Port']) if row['Modbus_Port'] else 502
break
if not modbus_ip:
Clock.schedule_once(lambda dt: self.dialog1("未找到匹配的Modbus配置"))
return
# 断开现有连接(保持不变)
self.modbus_master.disconnect()
# 创建新连接并测试(保持不变)
connect_result = self.modbus_master.connect(modbus_ip, modbus_port)
if not connect_result['success']:
Clock.schedule_once(lambda dt: self.dialog1(connect_result['msg']))
return
print(f"Modbus连接成功 (modbus-tk)\nIP: {modbus_ip}\n端口: {modbus_port}")
Clock.schedule_once(lambda dt: self.dialog1(f"Modbus连接成功\nIP: {modbus_ip}\n端口: {modbus_port}"))
except FileNotFoundError as e:
# 修复:将异常信息转为字符串或通过默认参数传递
err_msg = "配置文件 Users.csv 未找到"
Clock.schedule_once(lambda dt, msg=err_msg: self.dialog1(msg))
except modbus_tk.modbus.ModbusError as e:
# 修复:使用默认参数传递异常信息
err_msg = f"Modbus协议错误: {e}\n从站地址: {e.slave}\n功能码: {e.function_code}"
Clock.schedule_once(lambda dt, msg=err_msg: self.dialog1(msg))
self.modbus_master = None
except socket.error as e:
# 修复:提前格式化错误信息
err_msg = f"网络连接失败: {e}"
Clock.schedule_once(lambda dt, msg=err_msg: self.dialog1(msg))
self.modbus_master = None
except Exception as e:
# 修复:捕获所有其他异常
err_msg = f"连接失败: {str(e)}"
Clock.schedule_once(lambda dt, msg=err_msg: self.dialog1(msg))
self.modbus_master = None
threading.Thread(target=_connect_in_background, daemon=True).start()
def edit_profile(self):
if self.edit_name.text == "" or self.edit_prn.text == "" or self.edit_email.text == "" or self.edit_number.text == "":
check = "Enter all required fields"
return self.dialog1(check)
else:
user_data = {
'User': self.edit_name.text,
'User_pass': self.edit_prn.text,
'Wifi_SSID': self.edit_email.text,
'Modbus_IP': self.edit_number.text,
'Modbus_Port': self.password.text,
'Reserve_branch': self.edit_branch.text,
'Reserve_semester': self.edit_semester.text
}
update_user_profile(user_data, self.profile_wifi_ssid.text)
self.verify(True)
self.root.current = "home"
def secure_profile(self):
self.profile_edit_screen.ids.edit_email.readonly = True
self.profile_edit_screen.ids.edit_prn.readonly = True
self.profile_edit_screen.ids.edit_no.readonly = True
self.profile_edit_screen.ids.edit_private_credential_butn.disabled = False
from user_data_manager import update_user_profile
if __name__ == '__main__':
app().run()