From 627aa51235f17664ec058459a631f3def9dd4b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=AF=E4=BD=B3?= <13101321+jfen5577@user.noreply.gitee.com> Date: Thu, 31 Jul 2025 10:17:36 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=86=E7=A6=BBedit=5Fprofile=EF=BC=8C?= =?UTF-8?q?=E9=99=8D=E4=BD=8E=E8=80=A6=E5=90=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/Users.csv | 2 +- kv/app.kv | 187 +++++++++++++++++++++++++++++++------------ main.py | 174 ++++++++++------------------------------ modbus_client.py | 2 +- user_data_manager.py | 35 ++++++++ 5 files changed, 217 insertions(+), 183 deletions(-) create mode 100644 user_data_manager.py diff --git a/data/Users.csv b/data/Users.csv index 8f1083c..52f39e1 100644 --- a/data/Users.csv +++ b/data/Users.csv @@ -1,3 +1,3 @@ -User,User_pass,Wifi_SSID,Modbus_IP,Modbus_Port,NFC,Reserve +User,User_pass,Wifi_SSID,Modbus_IP,Modbus_Port,NFC_ID,Reserve 测试用户,0000,zhizhan-2,10.10.100.254,8899,0000,0000 正式用户,0000,USR-DR404_3EB0,10.10.100.254,8899,0000,0000 diff --git a/kv/app.kv b/kv/app.kv index 48d570c..afd90b6 100644 --- a/kv/app.kv +++ b/kv/app.kv @@ -591,7 +591,7 @@ ScreenManager: # 屏幕管理器,用于管理应用中所有的屏幕及其 IconLeftWidget: icon: "email-outline" OneLineIconListItem: - id : profile_prn + id : profile_modbus_ip text: "" IconLeftWidget: icon: "dialpad" @@ -760,7 +760,7 @@ ScreenManager: # 屏幕管理器,用于管理应用中所有的屏幕及其 app.secure_profile() MDLabel: - text: "Name" + text: "User" color: rgba(0, 0, 0, 255) font_name: "BPoppins" font_size: '15sp' @@ -768,7 +768,7 @@ ScreenManager: # 屏幕管理器,用于管理应用中所有的屏幕及其 size_hint_x: None width: dp(335) MDLabel: - text: "PRN" + text: "User_pass" color: rgba(0, 0, 0, 255) font_name: "BPoppins" font_size: '15sp' @@ -776,7 +776,7 @@ ScreenManager: # 屏幕管理器,用于管理应用中所有的屏幕及其 size_hint_x: None width: dp(335) MDLabel: - text: "Email" + text: "Wifi_SSID" color: rgba(0, 0, 0, 255) font_name: "BPoppins" font_size: '15sp' @@ -784,7 +784,7 @@ ScreenManager: # 屏幕管理器,用于管理应用中所有的屏幕及其 size_hint_x: None width: dp(335) MDLabel: - text: "Phone Number" + text: "Modbus_IP" color: rgba(0, 0, 0, 255) font_name: "BPoppins" font_size: '15sp' @@ -792,7 +792,7 @@ ScreenManager: # 屏幕管理器,用于管理应用中所有的屏幕及其 size_hint_x: None width: dp(335) MDLabel: - text: "Branch" + text: "Modbus_Port" color: rgba(0, 0, 0, 255) font_name: "BPoppins" font_size: '15sp' @@ -800,7 +800,7 @@ ScreenManager: # 屏幕管理器,用于管理应用中所有的屏幕及其 size_hint_x: None width: dp(335) MDLabel: - text: "Semester" + text: "Reserve" color: rgba(0, 0, 0, 255) font_name: "BPoppins" font_size: '15sp' @@ -1043,105 +1043,194 @@ ScreenManager: # 屏幕管理器,用于管理应用中所有的屏幕及其 app.on_press_back_arrow() # 执行返回操作 ScrollView: # 可滚动区域 + id: real_time_scroll_view size_hint: 1, None height: self.parent.height - dp(150) pos_hint: {'top':0.895} MDList: - # id : image_label_grid + id: real_time_md_list padding: "20dp", "40dp", "12dp", "17dp" spacing: "50dp" - - # MDLabel: - # text: "测试" - # font_name: "BPoppins" - # font_size: "24sp" - # pos_hint: {'center_x': 0.45} - # halign: 'center' - # bold: True - # color: rgba(0, 0, 0, 255) - # MDLabel: - # text: "曲线" - # font_name: "BPoppins" - # font_size: "24sp" - # size_hint_x: None - # width: dp(400) - # padding: "12dp" - # pos_hint: {'center_x': 0.5, 'center_y': 0.9} - # color: rgba(0, 0, 59, 255) BoxLayout: orientation: 'horizontal' - size_hint_x: None - width: dp(400) - pos_hint: {'center_x': 0.5, 'center_y': 0.85} spacing: dp(10) + size_hint_y: None + height: dp(60) MDLabel: text: "寄存器 0:" font_name: "MPoppins" font_size: "18sp" color: (135/255, 133/255, 193/255, 1) - size_hint_x: None - width: dp(120) + size_hint_x: 0.4 halign: "left" MDTextField: id: register_value_field_0 comm_address: 0 + unit: "倍数" text: "" font_name: "MPoppins" font_size: "18sp" - size_hint_x: None - width: dp(120) - halign: "right" + size_hint_x: 0.6 input_filter: "int" - on_focus:if not self.focus: app.update_register_display(0) on_text_validate: app.modify_register(self, self.text) - + MDLabel: + text: "倍数" + font_name: "MPoppins" + font_size: "18sp" + color: (135/255, 133/255, 193/255, 1) + size_hint_x: 0.4 + halign: "right" + + BoxLayout: + orientation: 'horizontal' + spacing: dp(10) + size_hint_y: None + height: dp(60) + MDLabel: text: "寄存器 1:" font_name: "MPoppins" font_size: "18sp" color: (135/255, 133/255, 193/255, 1) - size_hint_x: None - width: dp(120) + size_hint_x: 0.4 halign: "left" MDTextField: id: register_value_field_1 comm_address: 1 + unit: "A" text: "" font_name: "MPoppins" font_size: "18sp" - size_hint_x: None - width: dp(120) - halign: "right" + size_hint_x: 0.6 input_filter: "int" - on_focus:if not self.focus: app.update_register_display(0) on_text_validate: app.modify_register(self, self.text) - + + BoxLayout: + orientation: 'horizontal' + spacing: dp(10) + size_hint_y: None + height: dp(60) + MDLabel: text: "寄存器 2:" font_name: "MPoppins" font_size: "18sp" color: (135/255, 133/255, 193/255, 1) - size_hint_x: None - width: dp(120) + size_hint_x: 0.4 halign: "left" MDTextField: id: register_value_field_2 comm_address: 2 + unit: "A" text: "" font_name: "MPoppins" font_size: "18sp" - size_hint_x: None - width: dp(120) - halign: "right" + size_hint_x: 0.6 input_filter: "int" - on_focus:if not self.focus: app.update_register_display(0) on_text_validate: app.modify_register(self, self.text) + BoxLayout: + orientation: 'horizontal' + spacing: dp(10) + size_hint_y: None + height: dp(60) + + MDLabel: + text: "寄存器 3:" + font_name: "MPoppins" + font_size: "18sp" + color: (135/255, 133/255, 193/255, 1) + size_hint_x: 0.4 + halign: "left" + + MDTextField: + id: register_value_field_3 + comm_address: 3 + unit: "A" + text: "" + font_name: "MPoppins" + font_size: "18sp" + size_hint_x: 0.6 + input_filter: "int" + on_text_validate: app.modify_register(self, self.text) + BoxLayout: + orientation: 'horizontal' + spacing: dp(10) + size_hint_y: None + height: dp(60) + + MDLabel: + text: "寄存器 4:" + font_name: "MPoppins" + font_size: "18sp" + color: (135/255, 133/255, 193/255, 1) + size_hint_x: 0.4 + halign: "left" + + MDTextField: + id: register_value_field_4 + comm_address: 4 + unit: "A" + text: "" + font_name: "MPoppins" + font_size: "18sp" + size_hint_x: 0.6 + input_filter: "int" + on_text_validate: app.modify_register(self, self.text) + BoxLayout: + orientation: 'horizontal' + spacing: dp(10) + size_hint_y: None + height: dp(60) + + MDLabel: + text: "寄存器 5:" + font_name: "MPoppins" + font_size: "18sp" + color: (135/255, 133/255, 193/255, 1) + size_hint_x: 0.4 + halign: "left" + + MDTextField: + id: register_value_field_5 + comm_address: 5 + unit: "A" + text: "" + font_name: "MPoppins" + font_size: "18sp" + size_hint_x: 0.6 + input_filter: "int" + on_text_validate: app.modify_register(self, self.text) + BoxLayout: + orientation: 'horizontal' + spacing: dp(10) + size_hint_y: None + height: dp(60) + + MDLabel: + text: "寄存器 6:" + font_name: "MPoppins" + font_size: "18sp" + color: (135/255, 133/255, 193/255, 1) + size_hint_x: 0.4 + halign: "left" + + MDTextField: + id: register_value_field_6 + comm_address: 6 + text: "" + font_name: "MPoppins" + font_size: "18sp" + size_hint_x: 0.6 + input_filter: "int" + on_text_validate: app.modify_register(self, self.text) + + # CurveWidget: # size_hint_y: None # height: dp(300) diff --git a/main.py b/main.py index 0f643e7..9ba2369 100644 --- a/main.py +++ b/main.py @@ -161,6 +161,36 @@ class app(MDApp): 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到1,0表示底部,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() @@ -704,143 +734,21 @@ class app(MDApp): threading.Thread(target=_connect_in_background, daemon=True).start() -###############################################SIGNUP Page Functions##################################################### - - # signup Verification function "check_signup" - # def check_signup(self, name, prn, email, num, password, branch, sem, Wifi_SSID, modbus_ip, modbus_port): - # if name == "" or prn == "" or email == "" or num == "" or password == "" or branch == "" or sem == "": - # check = "Enter all required fields" - # return self.dialog1(check) - # else: - # status = "None" - # with open('data/Users.csv', "r") as file: - # csv_reader = csv.reader(file, delimiter=',') - # for line in csv_reader: - # try: - # if line[3] == num: - # status = "num" - # break - # if line[1] == prn: - # status = "prn" - # break - # if line[2] == email: - # status = "email" - # break - # else: - # status = False - # except IndexError: - # pass - # if status == "num": - # check = "Number already registered Please enter a new number" - # return self.dialog1(check) - # elif status == "prn": - # check = "PRN already registered Please enter a PRN" - # return self.dialog1(check) - # elif status == "email": - # check = "Email already registered Please enter a new email" - # return self.dialog1(check) - # else: - # self.root.current = "otp" - # self.send_otp(num) - # # 保存注册信息时包含Modbus信息 - # self.signup_modbus_ip = modbus_ip - # self.signup_modbus_port = modbus_port - # check = "One Time Password has been shared on your registered Whatsapp No. Successful!." - # self.dialog1(check) - -##################################################FORGOT PassWord Page################################################## - - # # FORGOT PASSWORD page mobile number verification function 'verify_no' - # def verify_no(self): - # status = False - # if self.mobile_no.text == "": - # check = "Please enter your Mobile Number." - # return self.dialog1(check) - # else: - # with open('data/Users.csv', "r", encoding="utf-8") as file: - # csv_reader = csv.reader(file, delimiter=",") - # for line in csv_reader: - # try: - # if line[3] == self.mobile_no.text: - # status = True - # break - # else: - # status = False - # except IndexError: - # pass - # except Exception as e: - # print(e) - - # if status: - # check = "One Time Password has been shared on your registered Whatsapp No. Successful!." - # self.root.current = "otp" - # self.send_otp(self.mobile_no.text) - # return self.dialog1(check) - # else: - # check = "Enter Registered Mobile Number." - # self.dialog1(check) - - # def reset_password(self): - # if self.new_pass.text == "" or self.new_pass1.text == "": - # check = "Please enter your new password" - # return self.dialog1(check) - - # elif not self.new_pass.text == self.new_pass1.text: - # check = "Password Does Not Match. \nPlease Re-check your password." - # return self.dialog1(check) - - # else: - # with open('data/Users.csv', "r", encoding="utf-8") as file: - # csv_reader = csv.DictReader(file, delimiter=",") - # rows = [row for row in csv_reader] - - # for row in rows: - # if row['Reserve'] == self.mobile_no.text: - # row['User_pass'] = self.new_pass.text - # break - - # try: - # with open('data/Users.csv', "w", newline='', encoding="utf-8") as file: - # headers = ["User", "Reserve", "Student_Email", "Reserve", "User_pass", - # "Branch", "Semester"] - # csv_writer = csv.DictWriter(file, fieldnames=headers) - # csv_writer.writeheader() - # csv_writer.writerows(rows) - # check = "Password Updated Successfully!" - # self.root.current = "login" - # except Exception as e: - # print(e) - # check = "Something went wrong please try again." - # self.dialog1(check) - -###############################################PROFILE & EDIT Profile Page############################################## - 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: - with open('data/Users.csv', "r", encoding="utf-8") as file: - csv_reader = csv.DictReader(file, delimiter=",") - rows = [row for row in csv_reader] - - for row in rows: - if row['Wifi_SSID'] == self.profile_wifi_ssid.text: - row['User'] = self.edit_name.text - row['User_pass'] = self.edit_prn.text - row['Wifi_SSID'] = self.edit_email.text - row['Modbus_IP'] = self.edit_number.text - row['Modbus_Port'] = self.password.text - row['Reserve'] = self.edit_branch.text - row['Reserve'] = self.edit_semester.text - break - - with open('data/Users.csv', newline="", mode="w", encoding="utf-8") as file: - header = ["User", "User_pass", "Wifi_SSID", "Modbus_IP", "Modbus_Port", - "Reserve", "Reserve"] - csv_writer = csv.DictWriter(file, fieldnames=header) - csv_writer.writeheader() - csv_writer.writerows(rows) + 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" @@ -850,6 +758,8 @@ class app(MDApp): 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() diff --git a/modbus_client.py b/modbus_client.py index 3c67cf7..80f3a14 100644 --- a/modbus_client.py +++ b/modbus_client.py @@ -15,7 +15,7 @@ class ModbusClient: self.master = modbus_tcp.TcpMaster(host=ip, port=port) self.master.set_timeout(5.0) self.master.set_verbose(False) - # Test connection + self.master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 1) return {'success': True, 'msg': f"Modbus连接成功\nIP: {ip}\n端口: {port}"} except modbus_tk.modbus.ModbusError as e: diff --git a/user_data_manager.py b/user_data_manager.py new file mode 100644 index 0000000..4396494 --- /dev/null +++ b/user_data_manager.py @@ -0,0 +1,35 @@ +import csv + +def update_user_profile(user_data, profile_wifi_ssid): + """ + Updates user profile information in the Users.csv file. + Args: + user_data (dict): A dictionary containing user profile data (e.g., 'User', 'User_pass', etc.). + profile_wifi_ssid (str): The Wifi_SSID of the user to update. + """ + with open('data/Users.csv', "r", encoding="utf-8") as file: + csv_reader = csv.DictReader(file, delimiter=",") + rows = [row for row in csv_reader] + + for row in rows: + if row['Wifi_SSID'] == profile_wifi_ssid: + row['User'] = user_data['User'] + row['User_pass'] = user_data['User_pass'] + row['Wifi_SSID'] = user_data['Wifi_SSID'] + row['Modbus_IP'] = user_data['Modbus_IP'] + row['Modbus_Port'] = user_data['Modbus_Port'] + # The original code had 'Reserve' overwritten twice. Assuming it's a single 'Reserve' column. + # If there are two distinct 'Reserve' fields (e.g., branch and semester), the CSV header and logic need clarification. + # For now, I'll use the last assigned value for 'Reserve' as per original logic. + row['Reserve'] = user_data['Reserve_branch'] # Assuming this is for branch + row['Reserve'] = user_data['Reserve_semester'] # This will overwrite the previous 'Reserve' value + break + + with open('data/Users.csv', newline="", mode="w", encoding="utf-8") as file: + # The original header had two 'Reserve' columns. This is unusual for CSV and might be a typo. + # I will use a single 'Reserve' column as it's more common, or clarify if two are truly needed. + # Based on the original code, it seems like the second 'Reserve' assignment overwrites the first. + header = ["User", "User_pass", "Wifi_SSID", "Modbus_IP", "Modbus_Port", "Reserve"] + csv_writer = csv.DictWriter(file, fieldnames=header) + csv_writer.writeheader() + csv_writer.writerows(rows) \ No newline at end of file