第四篇【传奇开心果系列】beeware的Toga开发移动应用示例:健身追踪应用

传奇开心果博文系列

  • 系列博文目录
    • beeware的Toga开发移动应用示例系列
  • 博文目录
    • 前言
    • 一、记录存储运动活动数据
    • 二、添加添加统计显示功能
    • 三、添加图表显示数据分析功能
    • 四、增加健身计划管理
    • 五、增加备份数据恢复数据功能
    • 六、初步整合代码示例
    • 七、增加登录验证功能
    • 八、完成最终整合的小项目示例代码

系列博文目录

beeware的Toga开发移动应用示例系列

博文目录

前言

健身追踪应用:使用 Beeware的toga集成库和sqlite3嵌入式数据库开发人员可以开发健康和健身应用程序,如健康追踪应用、锻炼计划应用等。通过与移动设备的传感器和健康数据接口进行交互,可以实现数据收集、分析和展示等功能。
在这里插入图片描述

一、记录存储运动活动数据

在这里插入图片描述下面是使用beeware的toga和SQLite3实现健身追踪应用的示例代码:


import toga

from toga.style import Pack

from toga.style.pack import COLUMN, ROW

import sqlite3

class FitnessApp(toga.App):

def startup(self):

self.db_conn = sqlite3.connect('fitness_tracker.db')

self.create_table()

self.main_window = toga.MainWindow(title=self.name)

self.main_window.content = self.build_main_box()

self.main_window.show()

def create_table(self):

cursor = self.db_conn.cursor()

cursor.execute('''

CREATE TABLE IF NOT EXISTS activities (

id INTEGER PRIMARY KEY AUTOINCREMENT,

activity_type TEXT,

time TEXT,

distance REAL,

calories REAL

)

''')

self.db_conn.commit()

def add_activity(self, widget):

activity_type = self.activity_type.value

time = self.time.value

distance = float(self.distance.value)

calories = float(self.calories.value)

cursor = self.db_conn.cursor()

cursor.execute('''

INSERT INTO activities (activity_type, time, distance, calories)

VALUES (?, ?, ?, ?)

''', (activity_type, time, distance, calories))

self.db_conn.commit()

self.activity_type.value = ''

self.time.value = ''

self.distance.value = ''

self.calories.value = ''

def build_main_box(self):

activity_label = toga.Label('运动类型:', style=Pack(padding=(0, 5)))

self.activity_type = toga.TextInput(style=Pack(flex=1))

time_label = toga.Label('时间:', style=Pack(padding=(0, 5)))

self.time = toga.TextInput(style=Pack(flex=1))

distance_label = toga.Label('距离:', style=Pack(padding=(0, 5)))

self.distance = toga.TextInput(style=Pack(flex=1))

calories_label = toga.Label('消耗卡路里:', style=Pack(padding=(0, 5)))

self.calories = toga.TextInput(style=Pack(flex=1))

add_button = toga.Button('添加运动', on_press=self.add_activity, style=Pack(padding=5))

activity_box = toga.Box(children=[activity_label, self.activity_type], style=Pack(direction=ROW, padding=5))

time_box = toga.Box(children=[time_label, self.time], style=Pack(direction=ROW, padding=5))

distance_box = toga.Box(children=[distance_label, self.distance], style=Pack(direction=ROW, padding=5))

calories_box = toga.Box(children=[calories_label, self.calories], style=Pack(direction=ROW, padding=5))

main_box = toga.Box(

children=[activity_box, time_box, distance_box, calories_box, add_button],

style=Pack(direction=COLUMN, padding=10)

)

return main_box

def main():

return FitnessApp('健身追踪应用', 'com.example.fitnessapp')

if __name__ == '__main__':

main().main_loop()

以上代码是一个简单的健身追踪应用示例,使用了beeware的Toga作为GUI库,sqlite3作为数据库存储运动记录。用户可以通过界面输入运动类型、时间、距离和消耗的卡路里,并点击"添加运动"按钮将数据保存到SQLite数据库中。
在这里插入图片描述

二、添加添加统计显示功能

要为健身追踪应用添加统计数据分析功能,你可以修改示例代码,添加相应的功能和界面元素。下面是一个简单的示例,展示如何计算每周、每月和每年的运动时长和消耗的卡路里,并在界面上显示统计信息:


import datetime

import calendar

class FitnessApp(toga.App):

# ...

def calculate_weekly_stats(self):

cursor = self.db_conn.cursor()

cursor.execute('''

SELECT SUM(time), SUM(calories)

FROM activities

WHERE time >= ? AND time <= ?

''', (self.get_start_of_week(), self.get_end_of_week()))

result = cursor.fetchone()

return result[0], result[1]

def calculate_monthly_stats(self):

cursor = self.db_conn.cursor()

cursor.execute('''

SELECT SUM(time), SUM(calories)

FROM activities

WHERE time >= ? AND time <= ?

''', (self.get_start_of_month(), self.get_end_of_month()))

result = cursor.fetchone()

return result[0], result[1]

def calculate_yearly_stats(self):

cursor = self.db_conn.cursor()

cursor.execute('''

SELECT SUM(time), SUM(calories)

FROM activities

WHERE time >= ? AND time <= ?

''', (self.get_start_of_year(), self.get_end_of_year()))

result = cursor.fetchone()

return result[0], result[1]

def get_start_of_week(self):

today = datetime.date.today()

start_of_week = today - datetime.timedelta(days=today.weekday())

return start_of_week.isoformat()

def get_end_of_week(self):

today = datetime.date.today()

end_of_week = today + datetime.timedelta(days=6 - today.weekday())

return end_of_week.isoformat()

def get_start_of_month(self):

today = datetime.date.today()

start_of_month = today.replace(day=1)

return start_of_month.isoformat()

def get_end_of_month(self):

today = datetime.date.today()

_, last_day = calendar.monthrange(today.year, today.month)

end_of_month = today.replace(day=last_day)

return end_of_month.isoformat()

def get_start_of_year(self):

today = datetime.date.today()

start_of_year = today.replace(month=1, day=1)

return start_of_year.isoformat()

def get_end_of_year(self):

today = datetime.date.today()

end_of_year = today.replace(month=12, day=31)

return end_of_year.isoformat()

def build_stats_box(self):

weekly_label = toga.Label('每周统计:', style=Pack(padding=(0, 5)))

weekly_time, weekly_calories = self.calculate_weekly_stats()

weekly_stats = toga.Label(f'运动时长: {weekly_time} 小时, 消耗卡路里: {weekly_calories}', style=Pack(padding=(0, 5)))

monthly_label = toga.Label('每月统计:', style=Pack(padding=(0, 5)))

monthly_time, monthly_calories = self.calculate_monthly_stats()

monthly_stats = toga.Label(f'运动时长: {monthly_time} 小时, 消耗卡路里: {monthly_calories}', style=Pack(padding=(0, 5)))

yearly_label = toga.Label('每年统计:', style=Pack(padding=(0, 5)))

yearly_time, yearly_calories = self.calculate_yearly_stats()

yearly_stats = toga.Label(f'运动时长: {yearly_time} 小时, 消耗卡路里: {yearly_calories}', style=Pack(padding=(0, 5)))

stats_box = toga.Box(

children=[weekly_label, weekly_stats, monthly_label, monthly_stats, yearly_label, yearly_stats],

style=Pack(direction=COLUMN, padding=10)

)

return stats_box

def build_main_box(self):

# ...

stats_box = self.build_stats_box()

main_box = toga.Box(

children=[activity_box, time_box, distance_box, calories_box, add_button, stats_box],

style=Pack(direction=COLUMN, padding=10)

)

return main_box

# ...

def main():

return FitnessApp('健身追踪应用', 'com.example.fitnessapp')

if __name__ == '__main__':

main().main_loop()

在上述示例中,我们添加了calculate_weekly_stats()calculate_monthly_stats()calculate_yearly_stats()方法来计算每周、每月和每年的运动时长和消耗的卡路里。然后,在build_stats_box()方法中,我们创建了显示统计信息的标签,并将其添加到界面中的stats_box中。最后,将stats_box添加到主界面的布局中。

这样,用户就可以在健身追踪应用中查看每周、每月和每年的运动统计信息了。
在这里插入图片描述

三、添加图表显示数据分析功能

要为健身追踪应用添加数据分析功能,你可以使用Python的数据分析库(如pandas和matplotlib)来处理和可视化运动数据。下面是一个示例代码,展示如何使用pandas和matplotlib对健身数据进行分析和绘图:


import pandas as pd

import matplotlib.pyplot as plt

class FitnessApp(toga.App):

# ...

def analyze_data(self):

cursor = self.db_conn.cursor()

cursor.execute('''

SELECT activity_type, SUM(time) as total_time, SUM(calories) as total_calories

FROM activities

GROUP BY activity_type

''')

result = cursor.fetchall()

data = pd.DataFrame(result, columns=['Activity Type', 'Total Time', 'Total Calories'])



# 绘制柱状图

data.plot(x='Activity Type', y='Total Time', kind='bar', legend=False)

plt.title('Total Time Spent on Each Activity')

plt.xlabel('Activity Type')

plt.ylabel('Total Time (hours)')

plt.show()

# 绘制饼图

data.plot(x='Activity Type', y='Total Calories', kind='pie', labels=data['Activity Type'], autopct='%1.1f%%')

plt.title('Total Calories Burned for Each Activity')

plt.ylabel('')

plt.show()

def build_main_box(self):

# ...

analyze_button = toga.Button('数据分析', on_press=self.analyze_data, style=Pack(padding=5))

# ...

main_box = toga.Box(

children=[activity_box, time_box, distance_box, calories_box, add_button, analyze_button, stats_box],

style=Pack(direction=COLUMN, padding=10)

)

return main_box

# ...

def main():

return FitnessApp('健身追踪应用', 'com.example.fitnessapp')

if __name__ == '__main__':

main().main_loop()

在上述示例中,我们添加了analyze_data()方法来从数据库中获取运动数据,并使用pandas创建一个DataFrame对象。然后,我们使用matplotlib库来绘制柱状图和饼图,展示每种运动类型的总时长和总消耗卡路里。

build_main_box()方法中,我们创建了一个"数据分析"按钮,并将其添加到主界面的布局中。当用户点击该按钮时,analyze_data()方法会被调用,进行数据分析和绘图操作。
在这里插入图片描述

四、增加健身计划管理

要为健身追踪应用添加健身计划管理功能,你可以修改示例代码,添加相应的功能和界面元素。下面是一个简单的示例,展示如何创建、查看和管理健身计划:


class FitnessApp(toga.App):

# ...

def create_plan(self, widget):

plan_name = self.plan_name.value

plan_description = self.plan_description.value

cursor = self.db_conn.cursor()

cursor.execute('''

INSERT INTO plans (name, description)

VALUES (?, ?)

''', (plan_name, plan_description))

self.db_conn.commit()

self.plan_name.value = ''

self.plan_description.value = ''

def view_plans(self, widget):

cursor = self.db_conn.cursor()

cursor.execute('SELECT name, description FROM plans')

plans = cursor.fetchall()

plan_list = toga.Table(

headings=['计划名称', '计划描述'],

data=plans,

style=Pack(flex=1)

)

self.plan_window = toga.Window(title='健身计划列表')

self.plan_window.content = plan_list

self.plan_window.show()

def build_main_box(self):

# ...

plan_name_label = toga.Label('计划名称:', style=Pack(padding=(0, 5)))

self.plan_name = toga.TextInput(style=Pack(flex=1))

plan_description_label = toga.Label('计划描述:', style=Pack(padding=(0, 5)))

self.plan_description = toga.TextInput(style=Pack(flex=1))

create_plan_button = toga.Button('创建计划', on_press=self.create_plan, style=Pack(padding=5))

view_plans_button = toga.Button('查看计划', on_press=self.view_plans, style=Pack(padding=5))

plan_box = toga.Box(

children=[plan_name_label, self.plan_name, plan_description_label, self.plan_description],

style=Pack(direction=COLUMN, padding=10)

)

main_box = toga.Box(

children=[activity_box, time_box, distance_box, calories_box, add_button, plan_box,

create_plan_button, view_plans_button, stats_box],

style=Pack(direction=COLUMN, padding=10)

)

return main_box

# ...

def main():

return FitnessApp('健身追踪应用', 'com.example.fitnessapp')

if __name__ == '__main__':

main().main_loop()

在上述示例中,我们添加了create_plan()方法来创建健身计划,并将计划名称和描述保存到数据库中。我们还添加了view_plans()方法来查看已创建的健身计划,并在一个新的窗口中显示计划列表。

build_main_box()方法中,我们创建了输入计划名称和描述的文本框,以及"创建计划"和"查看计划"按钮。当用户点击"创建计划"按钮时,create_plan()方法会被调用,创建并保存健身计划。当用户点击"查看计划"按钮时,view_plans()方法会被调用,显示健身计划列表。
在这里插入图片描述

五、增加备份数据恢复数据功能

要为健身追踪应用添加备份和恢复数据功能,你可以使用文件操作来实现。下面是一个简单的示例代码,展示如何实现数据的备份和恢复功能:


import shutil

class FitnessApp(toga.App):

# ...

def backup_data(self, widget):

# 关闭数据库连接

self.db_conn.close()

# 备份数据库文件

shutil.copy2('fitness_tracker.db', 'backup/fitness_tracker_backup.db')

# 重新打开数据库连接

self.db_conn = sqlite3.connect('fitness_tracker.db')

# 提示备份成功

toga.dialog.info('备份成功', '数据已成功备份至 backup 文件夹。')

def restore_data(self, widget):

# 关闭数据库连接

self.db_conn.close()

# 恢复数据库文件

shutil.copy2('backup/fitness_tracker_backup.db', 'fitness_tracker.db')

# 重新打开数据库连接

self.db_conn = sqlite3.connect('fitness_tracker.db')

# 提示恢复成功

toga.dialog.info('恢复成功', '数据已成功恢复。')

def build_main_box(self):

# ...

backup_button = toga.Button('备份数据', on_press=self.backup_data, style=Pack(padding=5))

restore_button = toga.Button('恢复数据', on_press=self.restore_data, style=Pack(padding=5))

main_box = toga.Box(

children=[activity_box, time_box, distance_box, calories_box, add_button, plan_box,

create_plan_button, view_plans_button, stats_box, backup_button, restore_button],

style=Pack(direction=COLUMN, padding=10)

)

return main_box

# ...

def main():

return FitnessApp('健身追踪应用', 'com.example.fitnessapp')

if __name__ == '__main__':

main().main_loop()

在上述示例中,我们添加了backup_data()方法来备份数据库文件。该方法关闭数据库连接,将数据库文件复制到指定的备份文件夹中,然后重新打开数据库连接。

我们还添加了restore_data()方法来恢复数据库文件。该方法关闭数据库连接,将备份文件复制回原始的数据库文件位置,然后重新打开数据库连接。

build_main_box()方法中,我们创建了"备份数据"和"恢复数据"按钮,并将其添加到主界面的布局中。当用户点击"备份数据"按钮时,backup_data()方法会被调用,执行数据备份操作。当用户点击"恢复数据"按钮时,restore_data()方法会被调用,执行数据恢复操作。

请确保在进行数据备份和恢复操作时,谨慎处理用户数据,以免丢失或覆盖重要数据。
在这里插入图片描述

六、初步整合代码示例


import toga

from toga.style import Pack

from toga.style.pack import COLUMN, ROW

import sqlite3

import datetime

import calendar

import pandas as pd

import matplotlib.pyplot as plt

import shutil

class FitnessApp(toga.App):

def startup(self):

self.db_conn = sqlite3.connect('fitness_tracker.db')

self.create_table()

self.main_window = toga.MainWindow(title=self.name)

self.main_window.content = self.build_main_box()

self.main_window.show()

def create_table(self):

cursor = self.db_conn.cursor()

cursor.execute('''

CREATE TABLE IF NOT EXISTS activities (

id INTEGER PRIMARY KEY AUTOINCREMENT,

activity_type TEXT,

time TEXT,

distance REAL,

calories REAL

)

''')

cursor.execute('''

CREATE TABLE IF NOT EXISTS plans (

id INTEGER PRIMARY KEY AUTOINCREMENT,

name TEXT,

description TEXT

)

''')

self.db_conn.commit()

def add_activity(self, widget):

activity_type = self.activity_type.value

time = self.time.value

distance = float(self.distance.value)

calories = float(self.calories.value)

cursor = self.db_conn.cursor()

cursor.execute('''

INSERT INTO activities (activity_type, time, distance, calories)

VALUES (?, ?, ?, ?)

''', (activity_type, time, distance, calories))

self.db_conn.commit()

self.activity_type.value = ''

self.time.value = ''

self.distance.value = ''

self.calories.value = ''

def calculate_weekly_stats(self):

cursor = self.db_conn.cursor()

cursor.execute('''

SELECT SUM(time), SUM(calories)

FROM activities

WHERE time >= ? AND time <= ?

''', (self.get_start_of_week(), self.get_end_of_week()))

result = cursor.fetchone()

return result[0], result[1]

def calculate_monthly_stats(self):

cursor = self.db_conn.cursor()

cursor.execute('''

SELECT SUM(time), SUM(calories)

FROM activities

WHERE time >= ? AND time <= ?

''', (self.get_start_of_month(), self.get_end_of_month()))

result = cursor.fetchone()

return result[0], result[1]

def calculate_yearly_stats(self):

cursor = self.db_conn.cursor()

cursor.execute('''

SELECT SUM(time), SUM(calories)

FROM activities

WHERE time >= ? AND time <= ?

''', (self.get_start_of_year(), self.get_end_of_year()))

result = cursor.fetchone()

return result[0], result[1]

def get_start_of_week(self):

today = datetime.date.today()

start_of_week = today - datetime.timedelta(days=today.weekday())

return start_of_week.isoformat()

def get_end_of_week(self):

today = datetime.date.today()

end_of_week = today + datetime.timedelta(days=6 - today.weekday())

return end_of_week.isoformat()

def get_start_of_month(self):

today = datetime.date.today()

start_of_month = today.replace(day=1)

return start_of_month.isoformat()

def get_end_of_month(self):

today = datetime.date.today()

_, last_day = calendar.monthrange(today.year, today.month)

end_of_month = today.replace(day=last_day)

return end_of_month.isoformat()

def get_start_of_year(self):

today = datetime.date.today()

start_of_year = today.replace(month=1, day=1)

return start_of_year.isoformat()

def get_end_of_year(self):

today = datetime.date.today()

end_of_year = today.replace(month=12, day=31)

return end_of_year.isoformat()

def analyze_data(self, widget):

cursor = self.db_conn.cursor()

cursor.execute('''

SELECT activity_type, SUM(time) as total_time, SUM(calories) as total_calories

FROM activities

GROUP BY activity_type

''')

result = cursor.fetchall()

data = pd.DataFrame(result, columns=['Activity Type', 'Total Time', 'Total Calories'])



# 绘制柱状图

data.plot(x='Activity Type', y='Total Time', kind='bar', legend=False)

plt.title('Total Time Spent on Each Activity')

plt.xlabel('Activity Type')

plt.ylabel('Total Time (hours)')

plt.show()

# 绘制饼图

data.plot(x='Activity Type', y='Total Calories', kind='pie', labels=data['Activity Type'], autopct='%1.1f%%')

plt.title('Total Calories Burned for Each Activity')

plt.ylabel('')

plt.show()

def create_plan(self, widget):

plan_name = self.plan_name.value

plan_description = self.plan_description.value

cursor = self.db_conn.cursor()

cursor.execute('''

INSERT INTO plans (name, description)

VALUES (?, ?)

''', (plan_name, plan_description))

self.db_conn.commit()

self.plan_name.value = ''

self.plan_description.value = ''

def view_plans(self, widget):

cursor = self.db_conn.cursor()

cursor.execute('SELECT name, description FROM plans')

plans = cursor.fetchall()

plan_list = toga.Table(

headings=['计划名称', '计划描述'],

data=plans,

style=Pack(flex=1)

)

self.plan_window = toga.Window(title='健身计划列表')

self.plan_window.content = plan_list

self.plan_window.show()

def backup_data(self, widget):

self.db_conn.close()

shutil.copy2('fitness_tracker.db', 'backup/fitness_tracker_backup.db')

self.db_conn = sqlite3.connect('fitness_tracker.db')

toga.dialog.info('备份成功', '数据已成功备份至 backup 文件夹。')

def restore_data(self, widget):

self.db_conn.close()

shutil.copy2('backup/fitness_tracker_backup.db', 'fitness_tracker.db')

self.db_conn = sqlite3.connect('fitness_tracker.db')

toga.dialog.info('恢复成功', '数据已成功恢复。')

def build_main_box(self):

activity_label = toga.Label('运动类型:', style=Pack(padding=(0, 5)))

self.activity_type = toga.TextInput(style=Pack(flex=1))

time_label = toga.Label('时间:', style=Pack(padding=(0, 5)))

self.time = toga.TextInput(style=Pack(flex=1))

distance_label = toga.Label('距离:', style=Pack(padding=(0, 5)))

self.distance = toga.TextInput(style=Pack(flex=1))

calories_label = toga.Label('消耗卡路里:', style=Pack(padding=(0, 5)))

self.calories = toga.TextInput(style=Pack(flex=1))

add_button = toga.Button('添加运动', on_press=self.add_activity, style=Pack(padding=5))

analyze_button = toga.Button('数据分析', on_press=self.analyze_data, style=Pack(padding=5))

plan_name_label = toga.Label('计划名称:', style=Pack(padding=(0, 5)))

self.plan_name = toga.TextInput(style=Pack(flex=1))

plan_description_label = toga.Label('计划描述:', style=Pack(padding=(0, 5)))

self.plan_description = toga.TextInput(style=Pack(flex=1))

create_plan_button = toga.Button('创建计划', on_press=self.create_plan, style=Pack(padding=5))

view_plans_button = toga.Button('查看计划', on_press=self.view_plans, style=Pack(padding=5))

main_box = toga.Box(

children=[

activity_label, self.activity_type, time_label, self.time,

distance_label, self.distance, calories_label, self.calories,

add_button, analyze_button,

plan_name_label, self.plan_name, plan_description_label, self.plan_description,

create_plan_button, view_plans_button

],

style=Pack(direction=COLUMN, padding=10)

)

return main_box

def main():

return FitnessApp('健身追踪应用', 'com.example.fitnessapp')

if __name__ == '__main__':

main().main_loop()

上述代码是一个比较完整的健身追踪应用示例,包括记录运动数据、统计分析、健身计划管理以及数据备份和恢复功能。你可以根据需要进行修改和扩展,以满足实际需求。
在这里插入图片描述

七、增加登录验证功能

要为健身追踪应用添加登录验证功能,你可以使用用户名和密码进行身份验证。下面是一个示例代码,展示如何实现登录验证功能:


import toga

from toga.style import Pack

from toga.style.pack import COLUMN, ROW

import sqlite3

class FitnessApp(toga.App):

def startup(self):

self.db_conn = sqlite3.connect('fitness_tracker.db')

self.create_table()

self.login_window = toga.MainWindow(title='登录')

self.login_window.content = self.build_login_box()

self.login_window.show()

def create_table(self):

cursor = self.db_conn.cursor()

cursor.execute('''

CREATE TABLE IF NOT EXISTS users (

id INTEGER PRIMARY KEY AUTOINCREMENT,

username TEXT UNIQUE,

password TEXT

)

''')

self.db_conn.commit()

def build_login_box(self):

username_label = toga.Label('用户名:', style=Pack(padding=(0, 5)))

self.username_input = toga.TextInput(style=Pack(flex=1))

password_label = toga.Label('密码:', style=Pack(padding=(0, 5)))

self.password_input = toga.PasswordInput(style=Pack(flex=1))

login_button = toga.Button('登录', on_press=self.login, style=Pack(padding=5))

login_box = toga.Box(

children=[

username_label, self.username_input,

password_label, self.password_input,

login_button

],

style=Pack(direction=COLUMN, padding=10)

)

return login_box

def login(self, widget):

username = self.username_input.value

password = self.password_input.value

cursor = self.db_conn.cursor()

cursor.execute('SELECT * FROM users WHERE username=? AND password=?', (username, password))

user = cursor.fetchone()

if user is not None:

self.username_input.value = ''

self.password_input.value = ''

self.login_window.close()

self.show_main_window()

else:

toga.dialog.info('登录失败', '用户名或密码错误。')

def show_main_window(self):

self.main_window = toga.MainWindow(title=self.name)

self.main_window.content = self.build_main_box()

self.main_window.show()

def build_main_box(self):

# ... 剩余的代码和界面元素

def main():

return FitnessApp('健身追踪应用', 'com.example.fitnessapp')

if __name__ == '__main__':

main().main_loop()

在上述代码中,我们添加了一个users表来存储用户的用户名和密码。在build_login_box()方法中,我们创建了用户名和密码输入框以及登录按钮,并在login()方法中实现了登录验证逻辑。当用户点击登录按钮时,会检查输入的用户名和密码是否与数据库中的用户匹配。如果匹配成功,就关闭登录窗口并显示主窗口;如果匹配失败,则显示登录失败的提示。

startup()方法中,我们首先创建了用户表,并显示登录窗口。只有在成功登录后,才会显示主窗口。

请注意,这只是一个简单的示例,你可能需要根据实际需求进一步优化代码和添加更多的安全性措施,例如使用哈希算法对密码进行加密存储。同时,建议在生产环境中使用专业的身份验证解决方案,确保应用的安全性。
在这里插入图片描述

八、完成最终整合的小项目示例代码


import toga

from toga.style import Pack

from toga.style.pack import COLUMN, ROW

import sqlite3

import datetime

import calendar

import pandas as pd

import matplotlib.pyplot as plt

import shutil

class FitnessApp(toga.App):

def startup(self):

self.db_conn = sqlite3.connect('fitness_tracker.db')

self.create_table()

self.login_window = toga.MainWindow(title='登录')

self.login_window.content = self.build_login_box()

self.login_window.show()

def create_table(self):

cursor = self.db_conn.cursor()

cursor.execute('''

CREATE TABLE IF NOT EXISTS users (

id INTEGER PRIMARY KEY AUTOINCREMENT,

username TEXT UNIQUE,

password TEXT

)

''')

cursor.execute('''

CREATE TABLE IF NOT EXISTS activities (

id INTEGER PRIMARY KEY AUTOINCREMENT,

user_id INTEGER,

activity_type TEXT,

time TEXT,

distance REAL,

calories REAL

)

''')

cursor.execute('''

CREATE TABLE IF NOT EXISTS plans (

id INTEGER PRIMARY KEY AUTOINCREMENT,

user_id INTEGER,

name TEXT,

description TEXT

)

''')

self.db_conn.commit()

def build_login_box(self):

username_label = toga.Label('用户名:', style=Pack(padding=(0, 5)))

self.username_input = toga.TextInput(style=Pack(flex=1))

password_label = toga.Label('密码:', style=Pack(padding=(0, 5)))

self.password_input = toga.PasswordInput(style=Pack(flex=1))

login_button = toga.Button('登录', on_press=self.login, style=Pack(padding=5))

login_box = toga.Box(

children=[

username_label, self.username_input,

password_label, self.password_input,

login_button

],

style=Pack(direction=COLUMN, padding=10)

)

return login_box

def login(self, widget):

username = self.username_input.value

password = self.password_input.value

cursor = self.db_conn.cursor()

cursor.execute('SELECT * FROM users WHERE username=? AND password=?', (username, password))

user = cursor.fetchone()

if user is not None:

self.username_input.value = ''

self.password_input.value = ''

self.login_window.close()

self.show_main_window(user[0]) # 传递用户ID

else:

toga.dialog.info('登录失败', '用户名或密码错误。')

def show_main_window(self, user_id):

self.user_id = user_id

self.main_window = toga.MainWindow(title=self.name)

self.main_window.content = self.build_main_box()

self.main_window.show()

def add_activity(self, widget):

activity_type = self.activity_type.value

time = self.time.value

distance = float(self.distance.value)

calories = float(self.calories.value)

cursor = self.db_conn.cursor()

cursor.execute('''

INSERT INTO activities (user_id, activity_type, time, distance, calories)

VALUES (?, ?, ?, ?, ?)

''', (self.user_id, activity_type, time, distance, calories))

self.db_conn.commit()

self.activity_type.value = ''

self.time.value = ''

self.distance.value = ''

self.calories.value = ''

def calculate_weekly_stats(self):

cursor = self.db_conn.cursor()

cursor.execute('''

SELECT SUM(time), SUM(calories)

FROM activities

WHERE user_id=? AND time >= ? AND time <= ?

''', (self.user_id, self.get_start_of_week(), self.get_end_of_week()))

result = cursor.fetchone()

return result[0], result[1]

def calculate_monthly_stats(self):

cursor = self.db_conn.cursor()

cursor.execute('''

SELECT SUM(time), SUM(calories)

FROM activities

WHERE user_id=? AND time >= ? AND time <= ?

''', (self.user_id, self.get_start_of_month(), self.get_end_of_month()))

result = cursor.fetchone()

return result[0], result[1]

def calculate_yearly_stats(self):

cursor = self.db_conn.cursor()

cursor.execute('''

SELECT SUM(time), SUM(calories)

FROM activities

WHERE user_id=? AND time >= ? AND time <= ?

''', (self.user_id, self.get_start_of_year(), self.get_end_of_year()))

result = cursor.fetchone()

return result[0], result[1]

def get_start_of_week(self):

today = datetime.date.today()

start_of_week = today - datetime.timedelta(days=today.weekday())

return start_of_week.isoformat()

def get_end_of_week(self):

today = datetime.date.today()

end_of_week = today + datetime.timedelta(days=6 - today.weekday())

return end_of_week.isoformat()

def get_start_of_month(self):

today = datetime.date.today()

start_of_month = today.replace(day=1)

return start_of_month.isoformat()

def get_end_of_month(self):

today = datetime.date.today()

_, last_day = calendar.monthrange(today.year, today.month)

end_of_month = today.replace(day=last_day)

return end_of_month.isoformat()

def get_start_of_year(self):

today = datetime.date.today()

start_of_year = today.replace(month=1, day=1)

return start_of_year.isoformat()

def get_end_of_year(self):

today = datetime.date.today()

end_of_year = today.replace(month=12, day=31)

return end_of_year.isoformat()

def analyze_data(self, widget):

cursor = self.db_conn.cursor()

cursor.execute('''

SELECT activity_type, SUM(time) as total_time, SUM(calories) as total_calories

FROM activities

WHERE user_id=?

GROUP BY activity_type

''', (self.user_id,))

result = cursor.fetchall()

data = pd.DataFrame(result, columns=['Activity Type', 'Total Time', 'Total Calories'])



# 绘制柱状图

data.plot(x='Activity Type', y='Total Time', kind='bar', legend=False)

plt.title('Total Time Spent on Each Activity')

plt.xlabel('Activity Type')

plt.ylabel('Total Time (hours)')

plt.show()

# 绘制饼图

data.plot(x='Activity Type', y='Total Calories', kind='pie', labels=data['Activity Type'], autopct='%1.1f%%')

plt.title('Total Calories Burned for Each Activity')

plt.ylabel('')

plt.show()

def create_plan(self, widget):

plan_name = self.plan_name.value

plan_description = self.plan_description.value

cursor = self.db_conn.cursor()

cursor.execute('''

INSERT INTO plans (user_id, name, description)

VALUES (?, ?, ?)

''', (self.user_id, plan_name, plan_description))

self.db_conn.commit()

self.plan_name.value = ''

self.plan_description.value = ''

def view_plans(self, widget):

cursor = self.db_conn.cursor()

cursor.execute('SELECT name, description FROM plans WHERE user_id=?', (self.user_id,))

plans = cursor.fetchall()

plan_list = toga.Table(

headings=['计划名称', '计划描述'],

data=plans,

style=Pack(flex=1)

)

self.plan_window = toga.Window(title='健身计划列表')

self.plan_window.content = plan_list

self.plan_window.show()

def backup_data(self, widget):

self.db_conn.close()

shutil.copy2('fitness_tracker.db', 'backup/fitness_tracker_backup.db')

self.db_conn = sqlite3.connect('fitness_tracker.db')

toga.dialog.info('备份成功', '数据已成功备份至 backup 文件夹。')

def restore_data(self, widget):

self.db_conn.close()

shutil.copy2('backup/fitness_tracker_backup.db', 'fitness_tracker.db')

self.db_conn = sqlite3.connect('fitness_tracker.db')

toga.dialog.info('恢复成功', '数据已成功恢复。')

def build_main_box(self):

activity_label = toga.Label('运动类型:', style=Pack(padding=(0, 5)))

self.activity_type = toga.TextInput(style=Pack(flex=1))

time_label = toga.Label('时间:', style=Pack(padding=(0, 5)))

self.time = toga.TextInput(style=Pack(flex=1))

distance_label = toga.Label('距离:', style=Pack(padding=(0, 5)))

self.distance = toga.TextInput(style=Pack(flex=1))

calories_label = toga.Label('消耗卡路里:', style=Pack(padding=(0, 5)))

self.calories = toga.TextInput(style=Pack(flex=1))

add_button = toga.Button('添加运动', on_press=self.add_activity, style=Pack(padding=5))

analyze_button = toga.Button('数据分析', on_press=self.analyze_data, style=Pack(padding=5))

plan_name_label = toga.Label('计划名称:', style=Pack(padding=(0, 5)))

self.plan_name = toga.TextInput(style=Pack(flex=1))

plan_description_label = toga.Label('计划描述:', style=Pack(padding=(0, 5)))

self.plan_description = toga.TextInput(style=Pack(flex=1))

create_plan_button = toga.Button('创建计划', on_press=self.create_plan, style=Pack(padding=5))

view_plans_button = toga.Button('查看计划', on_press=self.view_plans, style=Pack(padding=5))

backup_button = toga.Button('备份数据', on_press=self.backup_data, style=Pack(padding=5))

restore_button = toga.Button('恢复数据', on_press=self.restore_data, style=Pack(padding=5))

main_box = toga.Box(

children=[

activity_label, self.activity_type, time_label, self.time,

distance_label, self.distance, calories_label, self.calories,

add_button, analyze_button,

plan_name_label, self.plan_name, plan_description_label, self.plan_description,

create_plan_button, view_plans_button,

backup_button, restore_button

],

style=Pack(direction=COLUMN, padding=10)

)

return main_box

def main():

return FitnessApp('健身追踪应用', 'com.example.fitnessapp')

if __name__ == '__main__':

main().main_loop()

在这里插入图片描述
上述代码是一个完整的健身追踪应用示例,包括登录验证、记录运动数据、统计分析、健身计划管理以及数据备份和恢复功能。你可以根据需要进行修改和扩展,以满足实际需求。请注意,在实际应用中,你可能需要进一步优化代码和界面设计,以提供更好的用户体验和功能。同时,建议在生产环境中使用专业的身份验证解决方案,确保应用的安全性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/351422.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

利用STM32CubeMX和Keil模拟器,3天入门FreeRTOS(5.1) —— 队列集

前言 &#xff08;1&#xff09;FreeRTOS是我一天过完的&#xff0c;由此回忆并且记录一下。个人认为&#xff0c;如果只是入门&#xff0c;利用STM32CubeMX是一个非常好的选择。学习完本系列课程之后&#xff0c;再去学习网上的一些其他课程也许会简单很多。 &#xff08;2&am…

通过css隐藏popover的效果:即hover显示或隐藏另一个元素

场景一&#xff1a;隐藏旁边的兄弟元素 在原生的微信小程序上实现下图hover后出现提示的效果&#xff0c;如果是PC端就可以直接使用el-popover&#xff0c;但是小程序&#xff0c;我没有看到适合的组件。 样式代码<van-field value"{{ username }}" clearable pl…

数据结构与算法——队列

概述 计算机科学中&#xff0c;queue 是以顺序的方式维护的一组数据集合&#xff0c;在一端添加数据&#xff0c;从另一端移除数据。添加的一端称为尾&#xff0c;移除的一端称为头。 功能 插入offer(value : E) : boolean  取值并移除poll() : E  取值peek() : E  判断…

RCC——使用HSE/HSI配置时钟

RCC 文章目录 前言一、背景二、 2.1 2.2 总结 前言 前期疑问&#xff1a;1、RCC是什么意思。 2、最终配好的72M是系统时钟吗&#xff1f; 3、一共有哪些时钟 本文目标&#xff1a;将PLL时钟配置成72M 疑问解答&#xff1a;最终配好的时钟是PLL时钟。可以看一下时钟图就知道…

面向对象编程(进阶)(上)

文章目录 一. 关键字&#xff1a;this1.1 this是什么&#xff1f;1.2 什么时候使用this1.2.1 实例方法或构造器中使用当前对象的成员1.2.2 同一个类中构造器互相调用 1.3 练习 二. 面向对象特征二&#xff1a;继承(Inheritance)2.1 继承的概述2.1.1 生活中的继承2.1.2 Java中的…

LeetCode刷题---二叉树的最大深度

解题思路: 二叉树的最大深度是从根节点到最远叶子节点的最长路径上的节点数。 对于任意一个节点&#xff0c;其深度为其左子树深度和右子树深度的最大值加1。 最大高度是从根节点到最远叶子节点的最长路径的长度。 使用先序遍历的方法来找出二叉树的最大深度&#xff0c;即先访…

linuxshell日常脚本命令之if判断

shell脚本if中判断大于、小于、等于、不等于的符号 脚本有问题&#xff0c;有没有哪位大佬能帮忙检查一下&#xff1f; #!/bin/bash#run_num$(squeue | grep shifting | wc -l) run_numsqueue | grep shifting | wc -l #run_num$(squeue | grep shifting | wc -l 2>&1…

Qt环境搭建及基础

目录 Qt背景及环境搭建 ​编辑 基础语法 数据类型 内联函数 inline Lambda表达式 通过函数调用中加lambda匿名函数 参数捕获 Lambda和内联函数区别​编辑 函数指针 Lambda匿名函数小案例 通过结构体初始化&#xff0c;和指针初始化结构体 c类的引入 &#xff1a;&am…

python黑马模块

1、使用内置模块 # import通过.使用模块内部的全部功能 """ import time print("ff") time. sleep(5) print("as")# 使用from 导入某个功能 from time import sleep print("ff") sleep(5) print("as")# 使用 * 导入全部…

2024年,我与CSDN的邂逅之旅:一段燃烧激情、成就梦想的博客专家之路

文章目录 初入CSDN知识分享的本质什么有用学什么 成为博客专家的经历成为博客专家有什么好处实际好处隐形好处 今天获得了CSDN认证的博客专家&#xff0c;感觉很开心~分享一下这个始末。分享一下我和CSDN的5年邂逅。 初入CSDN 进入CSDN已经五年了&#xff0c;大一开始写博客&a…

Azure Private endpoint DNS 记录是如何解析的

Private endpoint 从本质上来说是Azure 服务在Azure 虚拟网络中安插的一张带私有地址的网卡。 举例来说如果Storage account在没有绑定private endpoint之前&#xff0c;查询Storage account的DNS记录会是如下情况&#xff1a; Seq Name …

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Swiper容器组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Swiper容器组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Swiper容器组件 滑块视图容器&#xff0c;提供子组件滑动轮播显示的能力。…

浪花 - 响应拦截器(强制登录)

1. 配置响应拦截器 import axios from axios;const myAxios axios.create({baseURL: http://localhost:8080/api/, });myAxios.defaults.withCredentials true;// 请求拦截器 myAxios.interceptors.request.use(function (config) {// Do something before request is sentc…

windows上使用anconda安装tensorrt环境

windows上使用anconda安装tensorrt环境 1 安装tensorrt1.1 下载最新的稳定的tensorrt 8.6.1(tensorrt对应的cuda、cudnn等版本是参考链接4)1.2 将tensorrt添加到环境变量1.3 安装tensorrt依赖1.4 安装Pycuda1.5 安装pytorch 2 测试2.1 测试TensorRT 样例(这个测试主要来源于参考…

03-Nacos-服务注册基于spring cloud实现

本项目基于spring boot 多模块 注意spring -boot、spring-cloud、spring-cloud-alibaba的版本兼容性 1.1、父级pom依赖 <properties><spring-boot.version>2.7.18</spring-boot.version><spring.cloud.version>2021.0.1</spring.cloud.version&g…

python函数式编程

函数式编程是一种编程范式&#xff0c;它强调使用纯函数、无副作用和不可变性。在Python中&#xff0c;可以使用高阶函数&#xff08;接受其他函数作为参数的函数&#xff09;和lambda表达式来实现函数式编程。 Python函数式编程主要包括以下内容&#xff1a; 头等函数&#…

RustDesk私有化部署,自建远程桌面搭建教程

以linux操作系统为例&#xff1a; 解压安装 # 使用wget进行下载1.1.8-2版本&#xff08;最新版本可以看上述发布地址&#xff09; wget https://github.com/rustdesk/rustdesk-server/releases/download/1.1.8-2/rustdesk-server-linux-amd64.zip # 使用unzip解压 unzip rust…

UBUNTU中NGINX的负载均衡和环境搭建

1.准备三台ubuntu版本的虚拟机 2.开始安装&#xff0c;下载&#xff0c;解压&#xff0c;以及编译nginx所需的环境依赖 这里需要注意我们创建了一个新的目录 /home/nginx,所以在编译中记得更改 然后再编译过程中我们会发现提示无法编译&#xff0c;原因是缺少c语言的插件&…

林浩然的哲学冒险乐园:尼采与超人哲学的诙谐解读与深度探索

林浩然的哲学冒险乐园&#xff1a;尼采与超人哲学的诙谐解读与深度探索 Lin Haoran’s Philosophical Adventureland: A Whimsical Exploration of Nietzsche and the Philosophy of the Superman 在一场思维的盛宴中&#xff0c;林浩然同学勇敢地踏入了尼采哲学的探险乐园&…

WPS复制时不能对多重选定区域使用此命令问题

今天在进行两个表格复制过程中频繁出现下面这个提示&#xff0c;就很烦&#xff0c;且不知道是什么原因。 后来在操作过程中发现了规律&#xff0c;所以记录一下。 问题复现&#xff1a; 1、这里鼠标随机点了一个方格 2、与此同时&#xff0c;我按着ctrl键选中第一列&…