1390 lines
58 KiB
Python
1390 lines
58 KiB
Python
|
||
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 csv
|
||
|
||
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
|
||
|
||
from modbus_tk.modbus_tcp import TcpMaster
|
||
import modbus_tk.defines as cst
|
||
import socket
|
||
|
||
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")
|
||
|
||
|
||
|
||
def get_books():
|
||
pass
|
||
|
||
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)
|
||
|
||
|
||
class SignUpScreen(Screen):
|
||
signup = ObjectProperty(None)
|
||
class ForgetPassScreen(Screen):
|
||
forget_Password = ObjectProperty(None)
|
||
class OtpScreen(Screen):
|
||
otp = ObjectProperty(None)
|
||
class ResetPassScreen(Screen):
|
||
reset_pass = ObjectProperty(None)
|
||
class SearchBookScreen(Screen):
|
||
search_book = ObjectProperty(None)
|
||
class NotificationScreen(Screen):
|
||
notifications = ObjectProperty(None)
|
||
class RecommendScreen(Screen):
|
||
recommendation = ObjectProperty(None)
|
||
class BorrowBookScreen(Screen):
|
||
borrow_book = ObjectProperty(None)
|
||
class ReturnBookScreen(Screen):
|
||
return_book = ObjectProperty(None)
|
||
class RenewBookScreen(Screen):
|
||
renew_book = ObjectProperty(None)
|
||
|
||
|
||
|
||
|
||
# Window.size = (dp(360), dp(680))
|
||
|
||
class app(MDApp):
|
||
wifi_status_text = StringProperty("")
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.signup_sem = None
|
||
self.signup_branch = None
|
||
self.user_name = None
|
||
self.book_pos_name = None
|
||
self.book_pos = None
|
||
self.rec_label = 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.profile_edit_screen = None
|
||
self.edit_email = None
|
||
self.edit_prn = None
|
||
self.edit_name = None
|
||
self.edit_number = None
|
||
self.edit_branch = None
|
||
self.profile_semester = None
|
||
self.profile_branch = None
|
||
self.profile_number = None
|
||
self.profile_prn = None
|
||
self.profile_email = None
|
||
self.profile_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 = None # 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 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"))
|
||
|
||
screen_manager.add_widget(SignUpScreen(name="signup"))
|
||
screen_manager.add_widget(ForgetPassScreen(name="forgot_pass"))
|
||
screen_manager.add_widget(OtpScreen(name="otp"))
|
||
screen_manager.add_widget(ResetPassScreen(name="reset_pass"))
|
||
screen_manager.add_widget(NotificationScreen(name="notification"))
|
||
screen_manager.add_widget(SearchBookScreen(name="searchBook"))
|
||
screen_manager.add_widget(RecommendScreen(name="recommend"))
|
||
screen_manager.add_widget(BorrowBookScreen(name="borrow_book"))
|
||
screen_manager.add_widget(ReturnBookScreen(name="return_book"))
|
||
screen_manager.add_widget(RenewBookScreen(name="renew_book"))
|
||
|
||
|
||
Clock.schedule_once(self.update_wifi_status, 2)
|
||
|
||
|
||
# Clock.schedule_interval(self.update_register_display, 1)
|
||
|
||
return screen_manager
|
||
|
||
#############################################ALL INPUT TEXT############################################################
|
||
def on_start(self):
|
||
|
||
# 从根窗口中获取名为"login"的屏幕(登录界面)
|
||
login_screen = self.root.get_screen("login")
|
||
# 通过界面ID获取登录界面中的用户名输入框组件
|
||
# self.username = login_screen.ids.username
|
||
# 通过界面ID获取登录界面中的密码输入框组件
|
||
# self.password = login_screen.ids.password
|
||
|
||
# 从根窗口中获取名为"home"的屏幕(主界面)
|
||
home_screen = self.root.get_screen("home")
|
||
# 通过界面ID获取主界面中的用户信息组件
|
||
self.user = home_screen.ids.user
|
||
# 通过界面ID获取主界面中的书架位置名称组件
|
||
self.book_pos_name = home_screen.ids.book_pos_name
|
||
# 通过界面ID获取主界面中的书架位置组件
|
||
self.book_pos = home_screen.ids.book_pos
|
||
|
||
# 从根窗口中获取名为"forgot_pass"的屏幕(忘记密码界面)
|
||
forgot_pass_screen = self.root.get_screen("forgot_pass")
|
||
# 通过界面ID获取忘记密码界面中的手机号输入框组件
|
||
self.mobile_no = forgot_pass_screen.ids.forgotPass_no
|
||
|
||
# 从根窗口中获取名为"reset_pass"的屏幕(重置密码界面)
|
||
reset_pass_screen = self.root.get_screen("reset_pass")
|
||
# 通过界面ID获取重置密码界面中的新密码输入框组件
|
||
self.new_pass = reset_pass_screen.ids.new_pass
|
||
# 通过界面ID获取重置密码界面中的确认新密码输入框组件
|
||
self.new_pass1 = reset_pass_screen.ids.new_pass1
|
||
|
||
# 从根窗口中获取名为"otp"的屏幕(验证码界面)
|
||
otp_screen = self.root.get_screen("otp")
|
||
# 通过界面ID获取验证码界面中的"验证"按钮组件
|
||
self.otp_button = otp_screen.ids.otp_button
|
||
# 通过界面ID获取验证码界面中的"返回"按钮组件
|
||
self.back_button = otp_screen.ids.back_button
|
||
# 通过界面ID获取验证码界面中的第一个数字输入框组件
|
||
self.d1 = otp_screen.ids.d1
|
||
# 通过界面ID获取验证码界面中的第二个数字输入框组件
|
||
self.d2 = otp_screen.ids.d2
|
||
# 通过界面ID获取验证码界面中的第三个数字输入框组件
|
||
self.d3 = otp_screen.ids.d3
|
||
# 通过界面ID获取验证码界面中的第四个数字输入框组件
|
||
self.d4 = otp_screen.ids.d4
|
||
|
||
# 从根窗口中获取名为"signup"的屏幕(注册界面)
|
||
# signup_screen = self.root.get_screen("signup")
|
||
# 通过界面ID获取注册界面中的用户名输入框组件
|
||
# self.user_name = signup_screen.ids.signup_name
|
||
# 通过界面ID获取注册界面中的PRN(可能是学号/身份标识)输入框组件
|
||
# self.user_prn = signup_screen.ids.signup_prn
|
||
# 通过界面ID获取注册界面中的邮箱输入框组件
|
||
# self.user_email = signup_screen.ids.signup_email
|
||
# 通过界面ID获取注册界面中的手机号输入框组件
|
||
# self.user_no = signup_screen.ids.signup_no
|
||
# 通过界面ID获取注册界面中的密码输入框组件
|
||
# self.user_pass = signup_screen.ids.signup_pass
|
||
# 通过界面ID获取注册界面中的所属部门/专业选择组件
|
||
# self.signup_branch = signup_screen.ids.signup_branch
|
||
# 通过界面ID获取注册界面中的年级/学期选择组件
|
||
# self.signup_sem = signup_screen.ids.signup_sem
|
||
|
||
# 从根窗口中获取名为"profile"的屏幕(个人资料界面)
|
||
profile_screen = self.root.get_screen("profile")
|
||
# 通过界面ID获取个人资料界面中的姓名展示组件
|
||
self.profile_name = profile_screen.ids.profile_name
|
||
# 通过界面ID获取个人资料界面中的邮箱展示组件
|
||
self.profile_email = profile_screen.ids.profile_email
|
||
# 通过界面ID获取个人资料界面中的PRN(身份标识)展示组件
|
||
self.profile_prn = profile_screen.ids.profile_prn
|
||
# 通过界面ID获取个人资料界面中的手机号展示组件
|
||
self.profile_number = profile_screen.ids.profile_number
|
||
# 通过界面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_number = 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
|
||
|
||
# 从根窗口中获取名为"borrow_book"的屏幕(借书界面)
|
||
borrow_screen = self.root.get_screen("borrow_book")
|
||
# 通过界面ID获取借书界面中的ISBN(图书编号)输入框组件
|
||
self.isbn = borrow_screen.ids.isbn
|
||
|
||
# 从根窗口中获取名为"return_book"的屏幕(还书界面)
|
||
return_book_screen = self.root.get_screen("return_book")
|
||
# 通过界面ID获取还书界面中的图片与标签网格布局组件
|
||
self.image_label_grid = return_book_screen.ids.image_label_grid
|
||
|
||
# 从根窗口中获取名为"renew_book"的屏幕(图书续借界面)
|
||
renew_book_screen = self.root.get_screen("renew_book")
|
||
# 通过界面ID获取续借界面中的网格布局组件
|
||
self.renew_grid = renew_book_screen.ids.renew_grid
|
||
|
||
# 从根窗口中获取名为"history"的屏幕(借阅历史界面)
|
||
history_screen = self.root.get_screen("history")
|
||
# 通过界面ID获取借阅历史界面中的网格布局组件
|
||
self.history_grid = history_screen.ids.history_grid
|
||
|
||
# 从根窗口中获取名为"searchBook"的屏幕(图书搜索界面)
|
||
search_book_screen = self.root.get_screen("searchBook")
|
||
# 通过界面ID获取图书搜索界面中的搜索输入框组件
|
||
self.search_field = search_book_screen.ids.search_field
|
||
# 通过界面ID获取图书搜索界面中的搜索结果网格布局组件
|
||
self.search_grid = search_book_screen.ids.search_grid
|
||
|
||
# 从根窗口中获取名为"recommend"的屏幕(图书推荐界面)
|
||
self.recommend_screen = self.root.get_screen("recommend")
|
||
# 通过界面ID获取推荐界面中的图书输入框组件
|
||
self.book_input = self.recommend_screen.ids.book_input
|
||
# 通过界面ID获取推荐界面中的推荐结果网格布局组件
|
||
self.rec_grid = self.recommend_screen.ids.rec_grid
|
||
# 通过界面ID获取推荐界面中的推荐信息标签组件
|
||
self.rec_label = self.recommend_screen.ids.rec_label
|
||
|
||
self.root.bind(current=self.on_screen_changed)
|
||
self.register_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
|
||
|
||
def read_modbus_register(self):
|
||
"""读取寄存器值(复用原逻辑)"""
|
||
if not self.modbus_master:
|
||
try:
|
||
self.modbus_master = TcpMaster(self.modbus_ip or '192.168.1.1', self.modbus_port or 502)
|
||
self.modbus_master.set_timeout(5.0)
|
||
except Exception as e:
|
||
print(f"Modbus连接失败: {e}")
|
||
return "连接失败"
|
||
|
||
try:
|
||
result = self.modbus_master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 1)
|
||
return str(result[0]) # 返回寄存器值
|
||
except Exception as e:
|
||
print(f"读取失败: {e}")
|
||
return "读取失败"
|
||
|
||
# def update_register_display(self, dt):
|
||
# """更新原标签的显示内容"""
|
||
# # 仅在目标屏幕时更新
|
||
# if self.root.current != "real_time_curve":
|
||
# return
|
||
# # 读取值并更新到原标签
|
||
# value = self.read_modbus_register()
|
||
# real_time_screen = self.root.get_screen("real_time_curve")
|
||
# real_time_screen.ids.register_label.text = f"寄存器 0: {value}" # 直接更新原标签
|
||
|
||
def write_modbus_register(self, value):
|
||
"""写入寄存器值"""
|
||
if not self.modbus_master:
|
||
pass
|
||
try:
|
||
self.modbus_master.execute(1, cst.WRITE_SINGLE_REGISTER, 0, value)
|
||
return "修改成功"
|
||
except Exception as e:
|
||
return f"修改失败: {e}"
|
||
|
||
def modify_register(self, input_text):
|
||
"""处理修改请求,修改后更新原标签"""
|
||
if not input_text:
|
||
self.show_dialog("错误", "请输入值")
|
||
return
|
||
try:
|
||
value = int(input_text)
|
||
result = self.write_modbus_register(value)
|
||
self.show_dialog("结果", result)
|
||
# 若成功,立即刷新原标签显示
|
||
if "成功" in result:
|
||
self.update_register_display(0)
|
||
except ValueError:
|
||
self.show_dialog("错误", "请输入有效整数")
|
||
|
||
def show_dialog(self, title, message):
|
||
self.dialog2(title, message)
|
||
#############################################@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):
|
||
"""清空界面中所有输入框和文本组件的内容"""
|
||
# 清空图书搜索框内容
|
||
self.search_field.text = ""
|
||
# 清空图书推荐输入框内容
|
||
self.book_input.text = ""
|
||
# 清空登录界面的用户名输入框内容
|
||
# self.username.text = ""
|
||
# 清空登录界面的密码输入框内容
|
||
# self.password.text = ""
|
||
# 清空忘记密码界面的手机号输入框内容
|
||
self.mobile_no.text = ""
|
||
# 清空验证码界面的第一个输入框内容
|
||
self.d1.text = ""
|
||
# 清空验证码界面的第二个输入框内容
|
||
self.d2.text = ""
|
||
# 清空验证码界面的第三个输入框内容
|
||
self.d3.text = ""
|
||
# 清空验证码界面的第四个输入框内容
|
||
self.d4.text = ""
|
||
# 清空书架位置名称文本内容
|
||
self.book_pos_name.text = ""
|
||
# 重置书架位置说明文本为示例内容
|
||
self.book_pos.text = "测试内容示例"
|
||
# 再次清空手机号输入框(可能是为了确保完全清空)
|
||
self.mobile_no.text = ""
|
||
# 清空借书界面的ISBN输入框内容
|
||
self.isbn.text = ''
|
||
|
||
def on_press_back_arrow(self):
|
||
# self.username.text = ""
|
||
# self.password.text = ""
|
||
self.mobile_no.text = ""
|
||
self.d1.text = ""
|
||
self.d2.text = ""
|
||
self.d3.text = ""
|
||
self.d4.text = ""
|
||
|
||
def focus_text_field(self, icon_button, focus, page):
|
||
if focus:
|
||
icon_button.icon_color = self.theme_cls.primary_color
|
||
elif page == "password":
|
||
icon_button.icon_color = 162 / 255, 169 / 255, 188 / 255, 1
|
||
elif page == "sign_pass":
|
||
icon_button.icon_color = 217 / 255, 217 / 255, 217 / 255, 1
|
||
else:
|
||
pass
|
||
|
||
def otp_button_fun(self, obj):
|
||
if obj == "True":
|
||
self.otp_button.text = "Reset Password"
|
||
elif obj == "1":
|
||
self.otp_button.text = "Edit Credentials"
|
||
elif obj == "2":
|
||
self.otp_button.text = "Verify OTP"
|
||
else:
|
||
self.otp_button.text = "Sign Up"
|
||
|
||
def otp_back_arrow_fun(self):
|
||
if self.otp_button.text == "Sign Up":
|
||
self.root.current = 'signup'
|
||
elif self.otp_button.text == "Reset Password":
|
||
self.root.current = 'reset_pass'
|
||
elif self.otp_button.text == "Verify OTP":
|
||
self.root.current = 'borrow_book'
|
||
|
||
############################################@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 read_modbus_register(self):
|
||
"""读取Modbus寄存器值并更新界面显示"""
|
||
if not self.modbus_master:
|
||
pass
|
||
|
||
try:
|
||
# 读取寄存器(1号从机,保持寄存器,地址0,长度1)
|
||
result = self.modbus_master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 1)
|
||
# 结果是元组,取第一个值
|
||
return str(result[0])
|
||
except Exception as e:
|
||
pass
|
||
|
||
def update_register_display(self, dt):
|
||
"""定时更新寄存器值显示"""
|
||
register_value = self.read_modbus_register()
|
||
# 获取显示标签并更新文本
|
||
real_time_screen = self.root.get_screen("real_time_curve") # 根据实际屏幕名称调整
|
||
if hasattr(real_time_screen.ids, 'register_label'):
|
||
real_time_screen.ids.register_label.text = f"寄存器 0: {register_value}"
|
||
|
||
###################################LoginPageWork-Start#################################################
|
||
|
||
def update_wifi_status(self, dt):
|
||
self.check_wifi()
|
||
Clock.schedule_once(self.update_wifi_status, 1) # 5秒刷新一次
|
||
|
||
def check_wifi(self):
|
||
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 # 这里直接返回,不再继续取SSID等信息
|
||
|
||
wifi_info = wifi_manager.getConnectionInfo()
|
||
if wifi_info is None:
|
||
self.wifi_status_text = 'WiFi 信息不可用'
|
||
return
|
||
|
||
ssid = wifi_info.getSSID()
|
||
if ssid:
|
||
ssid_display = ssid.strip('"')
|
||
else:
|
||
ssid_display = "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_id = wifi_info.getSSID().strip('"').lower()
|
||
else:
|
||
wifi_id = "zhizhan-2" # 非 Android 用模拟 WiFi
|
||
except Exception as e:
|
||
self.dialog1(f"获取WiFi信息失败:{e}")
|
||
return
|
||
|
||
try:
|
||
with open("data/Users.csv", "r", encoding="utf-8") as file:
|
||
csv_reader = csv.reader(file)
|
||
for line in csv_reader:
|
||
|
||
if len(line) < 8:
|
||
continue
|
||
|
||
# 假设SSID存储在第8列(索引7)
|
||
csv_ssid = line[7].strip().lower()
|
||
|
||
# 更精确匹配SSID,且忽略大小写
|
||
if wifi_id == csv_ssid or wifi_id in map(str.lower, line):
|
||
name, prn, email, number, password, branch, semester,csv_ssid = line[:8]
|
||
|
||
self.root.current = "home"
|
||
self.user.text = f"[b]Hey! {name}[/b]"
|
||
self.profile_name.text = self.edit_name.text = name
|
||
self.profile_email.text = self.edit_email.text = email
|
||
self.profile_prn.text = self.edit_prn.text = prn
|
||
self.profile_number.text = self.edit_number.text = number
|
||
self.profile_semester.text = self.edit_semester.text = semester
|
||
self.profile_branch.text = self.edit_branch.text = branch
|
||
|
||
self.connect_modbus()
|
||
self.dialog1(f"欢迎你,{name}!\n认证成功!")
|
||
|
||
return
|
||
except Exception as e:
|
||
self.dialog1(f"读取用户信息失败:{e}")
|
||
return
|
||
|
||
self.dialog1("认证失败,请检查网络或确保手机权限打开定位和连接到目标WiFi")
|
||
# 添加Modbus连接函数
|
||
def connect_modbus(self):
|
||
try:
|
||
# 从CSV文件读取Modbus配置
|
||
modbus_ip = None
|
||
modbus_port = 502 # 默认端口
|
||
|
||
# 获取当前连接的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" # 非Android环境模拟WiFi
|
||
|
||
# 读取CSV文件查找匹配的Modbus配置
|
||
with open("data/Users.csv", "r", encoding="utf-8") as file:
|
||
csv_reader = csv.reader(file)
|
||
headers = next(csv_reader) # 获取表头
|
||
|
||
for line in csv_reader:
|
||
if len(line) < 10: # 确保有足够的字段
|
||
continue
|
||
|
||
# 匹配当前连接的WiFi SSID
|
||
csv_ssid = line[7].strip().lower()
|
||
if current_wifi_id == csv_ssid or current_wifi_id in map(str.lower, line):
|
||
modbus_ip = line[8]
|
||
modbus_port = int(line[9]) if line[9] else 502
|
||
break # 找到匹配项则退出循环
|
||
|
||
if not modbus_ip:
|
||
# self.dialog1("未找到与当前WiFi匹配的Modbus配置")
|
||
return
|
||
|
||
# 断开现有连接(如果存在)
|
||
if hasattr(self, 'modbus_master') and self.modbus_master:
|
||
self.modbus_master.close()
|
||
|
||
# 创建新连接
|
||
self.modbus_master = TcpMaster(host=modbus_ip, port=modbus_port)
|
||
self.modbus_master.set_timeout(5.0) # 设置超时时间
|
||
|
||
# 测试连接(读取一个保持寄存器)
|
||
self.modbus_master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 1)
|
||
print(f"Modbus连接成功\nIP: {modbus_ip}\n端口: {modbus_port}")
|
||
|
||
except FileNotFoundError:
|
||
self.dialog1("配置文件user.csv未找到")
|
||
except socket.error as e:
|
||
self.dialog1(f"Modbus连接失败: 网络错误\n{e}")
|
||
self.modbus_master = None
|
||
except Exception as e:
|
||
self.dialog1(f"Modbus连接失败: {e}")
|
||
self.modbus_master = None
|
||
|
||
|
||
###############################################SIGNUP Page Functions#####################################################
|
||
|
||
# signup Verification function "check_signup"
|
||
def check_signup(self, name, prn, email, num, password, branch, sem, 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['Student_Whatsapp_no'] == self.mobile_no.text:
|
||
row['Student_pass'] = self.new_pass.text
|
||
break
|
||
|
||
try:
|
||
with open('data/Users.csv', "w", newline='', encoding="utf-8") as file:
|
||
headers = ["Student_Name", "Student_PRN", "Student_Email", "Student_Whatsapp_no", "Student_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['Student_Whatsapp_no'] == self.profile_number.text:
|
||
row['Student_Name'] = self.edit_name.text
|
||
row['Student_PRN'] = self.edit_prn.text
|
||
row['Student_Email'] = self.edit_email.text
|
||
row['Student_Whatsapp_no'] = self.edit_number.text
|
||
row['Student_pass'] = self.password.text
|
||
row['Branch'] = self.edit_branch.text
|
||
row['Semester'] = self.edit_semester.text
|
||
break
|
||
|
||
with open('data/Users.csv', newline="", mode="w", encoding="utf-8") as file:
|
||
header = ["Student_Name", "Student_PRN", "Student_Email", "Student_Whatsapp_no", "Student_pass",
|
||
"Branch", "Semester"]
|
||
csv_writer = csv.DictWriter(file, fieldnames=header)
|
||
csv_writer.writeheader()
|
||
csv_writer.writerows(rows)
|
||
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
|
||
|
||
|
||
#############################################OTP SEND & VERIFY PAGE#####################################################
|
||
|
||
def send_otp(self, number):
|
||
self.a = random.randint(1000, 9999)
|
||
if len(str(number)) == 10:
|
||
number1 = "+91" + str(number)
|
||
message = f"""Your verification code is {self.a}. It expires in 10 minutes. Don’t share this code with anyone."""
|
||
pywhatkit.sendwhatmsg_instantly(number1, message, wait_time=15, tab_close=True, close_time=3)
|
||
elif len(str(number)) != 10:
|
||
check = "Please enter a 10 digit number."
|
||
return self.dialog1(check)
|
||
else:
|
||
pass
|
||
|
||
def verify_otp(self, d1, d2, d3, d4, otp_button):
|
||
entered_otp = str(d1) + str(d2) + str(d3) + str(d4)
|
||
if entered_otp == str(self.a):
|
||
if otp_button == "Reset Password":
|
||
self.root.current = "reset_pass"
|
||
check = "Reset your password "
|
||
return self.dialog1(check)
|
||
|
||
elif otp_button == "Edit Credentials":
|
||
self.root.current = "profile_edit"
|
||
check = "Edit your credentials "
|
||
self.profile_edit_screen.ids.edit_email.readonly = False
|
||
self.profile_edit_screen.ids.edit_prn.readonly = False
|
||
self.profile_edit_screen.ids.edit_no.readonly = False
|
||
self.profile_edit_screen.ids.edit_private_credential_butn.disabled = True
|
||
return self.dialog1(check)
|
||
|
||
elif otp_button == "Verify OTP":
|
||
self.root.current = "home"
|
||
self.add_book()
|
||
|
||
else:
|
||
data = [self.user_name.text, self.user_prn.text, self.user_email.text, self.user_no.text,
|
||
self.user_pass.text, self.signup_branch.text, self.signup_sem.text]
|
||
|
||
with open('data/Users.csv', encoding="utf-8") as file:
|
||
csv_writer = csv.writer(file, delimiter=",")
|
||
csv_writer.writerow(data)
|
||
|
||
self.root.current = "home"
|
||
check = "SignUP Successful" + "\nHello ! " + self.user_name.text
|
||
self.user.text = "H e y !" + self.user_name.text
|
||
self.verify(True)
|
||
return self.dialog1(check)
|
||
|
||
elif entered_otp != str(self.a):
|
||
check = "Please enter a correct verification code."
|
||
self.d1.text = ""
|
||
self.d2.text = ""
|
||
self.d3.text = ""
|
||
self.d4.text = ""
|
||
return self.dialog1(check)
|
||
|
||
else:
|
||
check = "Sorry! Try again later."
|
||
self.root.current = "login"
|
||
self.d1.text = ""
|
||
self.d2.text = ""
|
||
self.d3.text = ""
|
||
self.d4.text = ""
|
||
return self.dialog1(check)
|
||
|
||
#######################################################Borrow BOok######################################################
|
||
|
||
def get_book(self, isbn):
|
||
self.isbn.text = isbn
|
||
if not self.isbn.text:
|
||
check = "Please enter a valid ISBN."
|
||
return self.dialog1(check)
|
||
elif not len(self.isbn.text) == 13:
|
||
check = "Please enter a valid ISBN."
|
||
return self.dialog1(check)
|
||
else:
|
||
with open('data/Books.csv', mode="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["ISBN"] == self.isbn.text:
|
||
if row['Availability'] == "0":
|
||
check = "Your book is unavailable. "
|
||
return self.dialog1(check)
|
||
else:
|
||
self.otp_button_fun('2')
|
||
self.root.current = "otp"
|
||
self.send_otp(self.profile_number.text, )
|
||
|
||
def add_book(self):
|
||
with open('data/Books.csv', mode="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['ISBN'] == self.isbn.text:
|
||
try:
|
||
self.book_name = row['Name']
|
||
a = row['Availability']
|
||
row['Availability'] = str(int(a) - 1)
|
||
except KeyError:
|
||
pass
|
||
|
||
with open('data/Books.csv', newline="", mode="w", encoding="utf-8") as file:
|
||
headers = ["Name", "Author", "Publication Year", "Publisher", "ISBN",
|
||
"Availability"]
|
||
csv_writer = csv.DictWriter(file, fieldnames=headers)
|
||
csv_writer.writeheader()
|
||
csv_writer.writerows(rows)
|
||
|
||
self.date = datetime.now()
|
||
self.Due_date = self.date + timedelta(days=10)
|
||
|
||
details = [self.profile_prn.text, self.profile_number.text, self.book_name, self.isbn.text, self.date,
|
||
self.Due_date, ]
|
||
transaction_details = details + ["Not Returned"]
|
||
borrow_details = details + ["Not Renewed"]
|
||
|
||
files = ['data/borrowed_books.csv', 'data/all_book_transactions.csv']
|
||
for file1 in files:
|
||
with open(file1, "a", encoding="utf-8", newline='') as file:
|
||
csv_writer = csv.writer(file, delimiter=",")
|
||
if file1 == 'data/borrowed_books.csv':
|
||
csv_writer.writerow(borrow_details)
|
||
elif file1 == 'data/all_book_transactions.csv':
|
||
csv_writer.writerow(transaction_details)
|
||
|
||
check = "OTP Verified! \nBook Borrowed Successfully!"
|
||
return self.dialog1(check)
|
||
|
||
def fetch_borrowed_book(self):
|
||
with open('data/borrowed_books.csv', mode="r", encoding="utf-8") as file:
|
||
csv_reader = csv.DictReader(file, delimiter=",")
|
||
rows = [row for row in csv_reader]
|
||
self.books.clear()
|
||
for row in rows:
|
||
if row['User'] == self.profile_prn.text:
|
||
book = {'source': 'assets/sample_book_display.jpg', 'btn_text': 'Return Book', 'Book_name': row['Book_Name'],
|
||
'time': row['Due_Date'], 'Renew_status': row['Renew_status']}
|
||
self.books += [book]
|
||
print(self.books)
|
||
self.items = self.books
|
||
|
||
def fetch_renewable_books(self):
|
||
with open('data/borrowed_books.csv', mode="r", encoding="utf-8") as file:
|
||
csv_reader = csv.DictReader(file, delimiter=",")
|
||
rows = [row for row in csv_reader]
|
||
self.renewable_books.clear()
|
||
for row in rows:
|
||
a = datetime.strptime(row['Due_Date'], "%Y-%m-%d %H:%M:%S.%f")
|
||
a = a.strftime("%d-%m-%Y")
|
||
if row['User'] == self.profile_prn.text and row[
|
||
'Renew_status'] == "Not Renewed" and a == datetime.now().strftime("%d-%m-%Y"):
|
||
renew_book = {'source': 'assets/sample_book_display.jpg', 'btn_text': 'Renew Book', 'Book_name': row['Book_Name'],
|
||
'time': row['Due_Date'], 'Renew_status': row['Renew_status']}
|
||
self.renewable_books += [renew_book]
|
||
print(self.renewable_books)
|
||
self.renew = self.renewable_books
|
||
|
||
def return_book(self, obj):
|
||
if obj == 'Return':
|
||
self.fetch_borrowed_book()
|
||
self.image_label_grid.clear_widgets()
|
||
book_list = self.items
|
||
elif obj == 'Renew':
|
||
self.fetch_renewable_books()
|
||
self.renew_grid.clear_widgets()
|
||
book_list = self.renew
|
||
|
||
for item in book_list:
|
||
box = MDBoxLayout(orientation='vertical', size_hint_y=None, height="125dp")
|
||
anchor = AnchorLayout(anchor_x="center", anchor_y="center")
|
||
grid = GridLayout(cols=2, spacing=dp(20), size_hint_x=None, size_hint_y=None, size=[dp(320), dp(200)],
|
||
padding=dp(0))
|
||
|
||
img = Image(source=item['source'],
|
||
size_hint_x=None,
|
||
size_hint_y=None,
|
||
width=dp(100),
|
||
height=dp(125),
|
||
allow_stretch=True)
|
||
label = MDLabel(
|
||
text=item['Book_name'],
|
||
font_name="BPoppins",
|
||
size_hint_x=None,
|
||
size_hint_y=None,
|
||
bold=True,
|
||
size=[dp(215), dp(70)],
|
||
padding=dp(0),
|
||
on_touch_down=lambda instance, touch: self.change_cursor(True),
|
||
on_touch_up=lambda instance, touch: self.change_cursor(False),
|
||
)
|
||
date11 = item['time']
|
||
date22 = "Due Date is "
|
||
for i in range(10):
|
||
date22 += date11[i]
|
||
|
||
label1 = MDLabel(
|
||
text=date22,
|
||
font_name="BPoppins",
|
||
size_hint_x=None,
|
||
size_hint_y=None,
|
||
size=[dp(215), dp(40)],
|
||
padding=dp(0),
|
||
on_touch_down=lambda instance, touch: self.change_cursor(True),
|
||
on_touch_up=lambda instance, touch: self.change_cursor(False),
|
||
)
|
||
|
||
if item['btn_text'] == "Return Book":
|
||
btn = Button(
|
||
text=item['btn_text'],
|
||
font_name="BPoppins",
|
||
size_hint=(None, None),
|
||
size=(dp(200), dp(30)),
|
||
background_color=(0, 0, 1, 1),
|
||
on_touch_down=lambda instance, touch: self.change_cursor(True),
|
||
on_touch_up=lambda instance, touch: self.change_cursor(False),
|
||
on_release=lambda instance, src=item['source'], txt=item['btn_text'], name=item['Book_name'],
|
||
time=item['time']: self.remove_book(src, txt, name, time),
|
||
)
|
||
elif item['btn_text'] == "Renew Book":
|
||
btn = Button(
|
||
text=item['btn_text'],
|
||
font_name="BPoppins",
|
||
size_hint=(None, None),
|
||
size=(dp(200), dp(30)),
|
||
background_color=(0, 0, 1, 1),
|
||
on_touch_down=lambda instance, touch: self.change_cursor(True),
|
||
on_touch_up=lambda instance, touch: self.change_cursor(False),
|
||
on_release=lambda instance, src=item['source'], txt=item['btn_text'], name=item['Book_name'],
|
||
time=item['time']: self.renew_book(src, txt, name, time),
|
||
)
|
||
|
||
vertical_box = BoxLayout(orientation='vertical', size_hint_y=None, height=label.height + btn.height)
|
||
vertical_box.add_widget(label)
|
||
vertical_box.add_widget(label1)
|
||
vertical_box.add_widget(btn)
|
||
|
||
grid.add_widget(img)
|
||
grid.add_widget(vertical_box)
|
||
anchor.add_widget(grid)
|
||
box.add_widget(anchor)
|
||
if btn.text == "Return Book":
|
||
self.image_label_grid.add_widget(box)
|
||
elif btn.text == "Renew Book":
|
||
self.renew_grid.add_widget(box)
|
||
|
||
def remove_book(self, source, text, name, time):
|
||
with open('data/borrowed_books.csv', mode="r", encoding="utf-8") as file:
|
||
csv_reader = csv.reader(file, delimiter=",")
|
||
header = next(csv_reader)
|
||
rows = list(csv_reader)
|
||
|
||
new_rows = [row for row in rows if row[0] != self.profile_prn.text or row[2] != name or row[5] != time]
|
||
|
||
with open('data/borrowed_books.csv', mode="w", newline="", encoding="utf-8") as file:
|
||
csv_writer = csv.writer(file, delimiter=",")
|
||
csv_writer.writerow(header)
|
||
csv_writer.writerows(new_rows)
|
||
|
||
with open('data/all_book_transactions.csv', mode="r", encoding="utf-8") as file:
|
||
csv_reader = csv.DictReader(file, delimiter=",")
|
||
rows = [row for row in csv_reader]
|
||
|
||
return_date = datetime.now()
|
||
return_date = return_date.strftime("%d/%m/%Y")
|
||
|
||
for row in rows:
|
||
if row['User'] == self.profile_prn.text and row['Book_Name'] == name and row['Due_Date'] == time:
|
||
row["Return_Date"] = return_date
|
||
|
||
with open('data/all_book_transactions.csv', mode="w", newline="", encoding="utf-8") as file:
|
||
headers = ['User', 'User_No', 'Book_Name', 'ISBN', 'Date', 'Due_Date', 'Return_Date', ]
|
||
csv_writer = csv.DictWriter(file, fieldnames=headers)
|
||
csv_writer.writeheader()
|
||
csv_writer.writerows(rows)
|
||
|
||
with open('data/Books.csv', mode="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['Name'] == name:
|
||
try:
|
||
a = row['Availability']
|
||
row['Availability'] = str(int(a) + 1)
|
||
except KeyError:
|
||
pass
|
||
|
||
with open('data/Books.csv', newline="", mode="w", encoding="utf-8") as file:
|
||
headers = ["Name", "Author", "Publication Year", "Publisher", "ISBN",
|
||
"Availability"]
|
||
csv_writer = csv.DictWriter(file, fieldnames=headers)
|
||
csv_writer.writeheader()
|
||
csv_writer.writerows(rows)
|
||
|
||
self.items = [item for item in self.items if
|
||
not (item['source'] == source and item['btn_text'] == text and item['Book_name'] == name)]
|
||
|
||
check = "Book Returned Successfully!"
|
||
self.dialog1(check)
|
||
self.return_book("Return")
|
||
|
||
def fetch_book_history(self):
|
||
with open('data/all_book_transactions.csv', mode="r", encoding="utf-8") as file:
|
||
csv_reader = csv.DictReader(file, delimiter=",")
|
||
rows = [row for row in csv_reader]
|
||
self.books_history.clear()
|
||
for row in rows:
|
||
if row['User'] == self.profile_prn.text and row['Return_Date'] != 'Not Returned':
|
||
book = {'source': 'assets/sample_book_display.jpg', 'text': 'Rate Book', 'Book_name': row['Book_Name'],
|
||
'time': row['Date'], 'Return_Date': row['Return_Date']}
|
||
self.books_history += [book]
|
||
self.history = self.books_history
|
||
|
||
def book_history(self):
|
||
self.fetch_book_history()
|
||
self.history_grid.clear_widgets()
|
||
|
||
for item in self.history:
|
||
box = MDBoxLayout(orientation='vertical', size_hint_y=None, height="125.001dp")
|
||
anchor = AnchorLayout(anchor_x="center", anchor_y="center")
|
||
grid = GridLayout(cols=2, spacing=dp(20), size_hint_x=None, size_hint_y=None, size=[dp(320), dp(200)],
|
||
padding=dp(0))
|
||
|
||
img = Image(source=item['source'], size_hint_x=None, size_hint_y=None, width=dp(100), height=dp(125),
|
||
allow_stretch=True)
|
||
label = MDLabel(
|
||
text=item['Book_name'],
|
||
font_name="BPoppins",
|
||
size_hint_x=None,
|
||
size_hint_y=None,
|
||
bold=True,
|
||
size=[dp(215), dp(60)],
|
||
padding=dp(0),
|
||
on_touch_down=lambda instance, touch: self.change_cursor(True),
|
||
on_touch_up=lambda instance, touch: self.change_cursor(False),
|
||
)
|
||
|
||
B_date = datetime.strptime(item['time'], '%Y-%m-%d %H:%M:%S.%f')
|
||
B_date = B_date.strftime("%d-%m-%Y")
|
||
|
||
formated_B = ""
|
||
for i in range(10):
|
||
formated_B += B_date[i]
|
||
|
||
R_date = datetime.strptime(item['Return_Date'], '%d/%m/%Y')
|
||
R_date = R_date.strftime("%d-%m-%Y")
|
||
formated_R = ""
|
||
for i in range(10):
|
||
formated_R += R_date[i]
|
||
|
||
date22 = f"""Borrow Date: {formated_B}\nReturn Date: {formated_R}"""
|
||
|
||
label1 = MDLabel(
|
||
text=date22,
|
||
font_name="BPoppins",
|
||
size_hint_x=None,
|
||
size_hint_y=None,
|
||
size=[dp(215), dp(50)],
|
||
padding=dp(0),
|
||
on_touch_down=lambda instance, touch: self.change_cursor(True),
|
||
on_touch_up=lambda instance, touch: self.change_cursor(False),
|
||
)
|
||
|
||
btn = Button(
|
||
text=item['text'],
|
||
font_name="BPoppins",
|
||
size_hint=(None, None),
|
||
size=(dp(200), dp(30)),
|
||
background_color=(0, 0, 1, 1),
|
||
on_touch_down=lambda instance, touch: self.change_cursor(True),
|
||
on_touch_up=lambda instance, touch: self.change_cursor(False),
|
||
)
|
||
vertical_box = BoxLayout(orientation='vertical', size_hint_y=None, height=label.height + btn.height)
|
||
vertical_box.add_widget(label)
|
||
vertical_box.add_widget(label1)
|
||
vertical_box.add_widget(btn)
|
||
|
||
grid.add_widget(img)
|
||
grid.add_widget(vertical_box)
|
||
anchor.add_widget(grid)
|
||
box.add_widget(anchor)
|
||
self.history_grid.add_widget(box)
|
||
|
||
def data_load(self):
|
||
self.search_field.bind(text=self.search_books)
|
||
self.search_grid.clear_widgets()
|
||
self.data_tables = MDDataTable(
|
||
pos_hint={'center_x': 0.5, 'center_y': 0.4},
|
||
size_hint=(0.95, 0.5),
|
||
use_pagination=True,
|
||
padding=dp(0),
|
||
rows_num=7,
|
||
pagination_menu_pos='auto',
|
||
background_color_selected_cell=(0.8, 0.9, 1, 1),
|
||
column_data=[
|
||
("Book Name", dp(80)),
|
||
("Author", dp(50)),
|
||
("Publication Year", dp(30)),
|
||
("Publisher", dp(40)),
|
||
("ISBN", dp(30)),
|
||
("Availability", dp(30)),
|
||
],
|
||
row_data=get_books()
|
||
)
|
||
self.search_grid.add_widget(self.data_tables)
|
||
|
||
def search_books(self, instance, value):
|
||
all_data = get_books()
|
||
filtered_data = [row for row in all_data if (
|
||
value.lower() in row[0].lower() or value.lower() in row[1].lower() or value.lower() in row[4])]
|
||
self.data_tables.row_data = filtered_data
|
||
|
||
def get_recommendations(self):
|
||
book_title = self.book_input.text
|
||
recommendations = self.rec_book(book_title)
|
||
if isinstance(recommendations, str):
|
||
self.rec_label.text = recommendations
|
||
self.rec_grid.clear_widgets()
|
||
else:
|
||
self.display_rec_table(recommendations)
|
||
|
||
@staticmethod
|
||
def rec_book(book_title):
|
||
book_title_lower = book_title.lower()
|
||
if book_title_lower not in df["Name_lower"].values:
|
||
return f"{book_title} not in database"
|
||
index = df[df['Name_lower'] == book_title_lower].index[0]
|
||
|
||
distance, indices = knn.kneighbors(s_matrix[index].reshape(1, -1))
|
||
rec_title = df.iloc[indices.flatten()]["Name"].tolist()
|
||
aval = df.iloc[indices.flatten()]["Availability"].tolist()
|
||
isbn = df.iloc[indices.flatten()]["ISBN"].tolist()
|
||
|
||
recommendation = list(zip(rec_title, isbn, aval))
|
||
|
||
return recommendation
|
||
|
||
def display_rec_table(self, recommendations):
|
||
self.rec_grid.clear_widgets()
|
||
data_tables = MDDataTable(
|
||
pos_hint={'center_x': 0.5, 'center_y': 0.2},
|
||
size_hint=(0.95, 0.2),
|
||
use_pagination=True,
|
||
padding=dp(0),
|
||
rows_num=6,
|
||
pagination_menu_pos='auto',
|
||
background_color_selected_cell=(0.8, 0.9, 1, 1),
|
||
column_data=[
|
||
("Book Name", dp(80)),
|
||
("ISBN", dp(30)),
|
||
("Availability", dp(30)),
|
||
],
|
||
row_data=[(title, isbn, availability) for title, isbn, availability in recommendations]
|
||
)
|
||
self.rec_grid.add_widget(data_tables)
|
||
|
||
def where_is_book(self):
|
||
with open('data/book_positions.csv', 'r') as csvfile:
|
||
data = csv.reader(csvfile, delimiter=',')
|
||
rows = list(data)
|
||
a = self.book_pos_name.text.lower()
|
||
for row in rows:
|
||
if row[0].lower() == a:
|
||
pos = str(row[1])
|
||
self.book_pos.text = f"{a.upper()} Book is kept at {row[1]} location. Please Go and Borrow it!!\n({row[1]} means {pos[0]} th row, Left/Right side(L/R), {pos[2]} th shelf, {pos[3]} th shelf step )"
|
||
break
|
||
else:
|
||
self.book_pos.text = "Please check the Spell!"
|
||
|
||
|
||
if __name__ == '__main__':
|
||
app().run()
|
||
|
||
|
||
|