基于PyQt5+JSON实现小型管理系统(原创)

(一)管理系统功能展示

管理员界面(服务端)

8c32bc1078ae46e6b67b4d326bdd905d.png

aaec1ecfcd774e6fb51fda3c72d276ef.png

展示界面(客户端)

8cc8b6f2c02e4416a20a4b46a1c63989.png

e8ec9191ada7434d83c7ade276a1e237.png

(二)开发环境

处理器:Apple M1 Max (内存:64G)

系统版本:MacOS Sonoma 14.1.2 

开发环境:Anaconda(Python3.9) + MySql 8.0.29 + Pycharm + PyQt5

实现功能:对JSON数据的增删改查,包含一个简单的身份验证(密码输入不正确功能不能用),并且使用多线程通过socket服务端与客户端进行通讯

(三)主窗口代码详解(知识点!)

注:完整代码在整个文章的最后,并附有下载链接!

导入程序所需要的模块

import socket  # 导入 Socket 编程相关的模块
import sys  # 导入 sys 模块,提供对 Python 解释器的访问
import json  # 导入处理 JSON 数据的模块
import os  # 导入提供与操作系统交互的功能的模块
import threading  # 导入创建多线程的模块
from PyQt5.QtCore import Qt  # 导入 PyQt5 中的 Qt 模块
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QLineEdit, QPushButton, QDialog, QTableWidget, QTableWidgetItem  # 导入 PyQt5 中的 GUI 元素相关的模块

新增数据和修改数据的窗口

这里很容易犯错,我们的JSON文件是要用字典形式去存储,不要使用列表的方式,仅仅是符号上的区别但是差别很大!列表的符号是[ ]  而字典是{ } 

  • JSON 中的字典使用花括号 {} 来表示,形如 {"key1": "value1", "key2": "value2"}
  • JSON 中的列表使用方括号 [] 来表示,形如 ["item1", "item2", "item3"]

这里特别注意要用字典!!!

新增数据窗口

# 新增数据窗口
class AddDataDialog(QDialog):
    def __init__(self, parent=None):
        super(AddDataDialog, self).__init__(parent)

        # 设置对话框标题和大小
        self.setWindowTitle('新增数据')
        self.setGeometry(200, 200, 200, 258)

        # 创建标签显示 "新增数据"
        self.t_add = QLabel('新增数据', self)
        self.t_add.setGeometry(70, 20, 60, 16)

        # 创建文本框输入注册地址
        self.t_data1_input = QLineEdit(self)
        self.t_data1_input.setPlaceholderText('注册地址')
        self.t_data1_input.setGeometry(30, 50, 141, 31)

        # 创建文本框输入用户名
        self.t_data2_input = QLineEdit(self)
        self.t_data2_input.setPlaceholderText('用户名')
        self.t_data2_input.setGeometry(30, 90, 141, 31)

        # 创建文本框输入密码
        self.t_data3_input = QLineEdit(self)
        self.t_data3_input.setPlaceholderText('密码')
        self.t_data3_input.setGeometry(30, 130, 141, 31)

        # 创建按钮 "新增数据",点击按钮触发 accept() 方法
        self.t_Button = QPushButton('新增数据', self)
        self.t_Button.setGeometry(40, 170, 113, 41)
        self.t_Button.clicked.connect(self.accept)

    def get_data(self):
        # 获取输入的数据,以字典形式返回
        return {
            'address': self.t_data1_input.text(),
            'username': self.t_data2_input.text(),
            'password': self.t_data3_input.text()
        }
# 修改数据窗口
class UpDataDialog(QDialog):
    def __init__(self, parent=None):
        super(UpDataDialog, self).__init__(parent)

        self.setWindowTitle('修改数据')
        self.setGeometry(200, 200, 200, 258)

        self.t2_add = QLabel('修改数据', self)
        self.t2_add.setGeometry(70, 20, 60, 16)

        self.t2_data1_input = QLineEdit(self)
        self.t2_data1_input.setPlaceholderText('注册地址')
        self.t2_data1_input.setGeometry(30, 50, 141, 31)

        self.t2_data2_input = QLineEdit(self)
        self.t2_data2_input.setPlaceholderText('用户名')
        self.t2_data2_input.setGeometry(30, 90, 141, 31)

        self.t2_data3_input = QLineEdit(self)
        self.t2_data3_input.setPlaceholderText('密码')
        self.t2_data3_input.setGeometry(30, 130, 141, 31)

        self.t2_Button = QPushButton('修改数据', self)
        self.t2_Button.setGeometry(40, 170, 113, 41)
        self.t2_Button.clicked.connect(self.accept)
    def get_data(self):
        return {
            'address': self.t2_data1_input.text(),
            'username': self.t2_data2_input.text(),
            'password': self.t2_data3_input.text()
        }

详细解释上述代码(重点内容):

def __init__(self, parent=None):
    super(AddDataDialog, self).__init__(parent)

def __init__(self, parent=None):这是一个类的构造函数,它在类的实例被创建时被调用。__init__ 用于初始化对象的属性。self 表示类的实例本身。

super(AddDataDialog, self).__init__(parent):这一行调用了父类的构造函数。在这里,super() 是一个用于调用父类方法的内建函数。AddDataDialog 是当前类的名称,而 self 是当前类的实例。通过 super(AddDataDialog, self),你告诉 Python 在 AddDataDialog 类的父类中查找,并调用父类的构造函数 __init__

parent=None:这是构造函数的参数,表示一个可选的父对象。在 GUI 编程中,通常用于指定新创建的窗口或对话框的父窗口。如果没有提供父对象,就使用默认值 None

主窗口(整个程序的核心部分!)

class ManageApp(QMainWindow):
    def __init__(self):
        super(ManageApp, self).__init__()
        self.admin_password = "admin"  # 设置管理员密码
        self.logged_in = False
        self.init_ui()
        self.load_data()
        self.init_socket()  # 初始化 socket
        self.receive_data()  # 启动接收数据的线程
        print("Current Working Directory:", os.getcwd())

    def init_socket(self):
        self.server_address = ('127.0.0.1', 8080)
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.socket.bind(self.server_address)

    def receive_data(self):
        # 启动一个线程来接收数据
        thread = threading.Thread(target=self._receive_data_thread)
        thread.start()

    def _receive_data_thread(self):
        while True:
            try:
                data, address = self.socket.recvfrom(1024)  # 接收数据
                # 在这里处理接收到的数据,更新 UI
                data_str = data.decode('utf-8')
                self.update_ui_with_received_data(data_str)
            except Exception as e:
                print(f"Error receiving data: {e}")

    def update_ui_with_received_data(self, received_data):
        self.result_data_label.setText(received_data)

    def init_ui(self):
        self.setWindowTitle('用户管理系统')
        self.setFixedSize(655, 338)  # 设置窗口为固定大小

        # 数据展示区域
        self.data_label = QLabel('数据展示', self)
        self.data_label.setGeometry(30, 10, 60, 16)

        self.result_data_text = QTableWidget(self)
        self.result_data_text.setGeometry(30, 40, 320, 261)
        self.result_data_text.setColumnCount(3)
        self.result_data_text.setHorizontalHeaderLabels(['注册地址', '用户名', '密码'])
        self.result_data_text.horizontalHeader().setStretchLastSection(True)
        self.result_data_text.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        # 数据操作区域
        self.data_control = QLabel('数据操作区', self)
        self.data_control.setGeometry(360, 10, 71, 16)

        self.search_input = QLineEdit(self)
        self.search_input.setPlaceholderText('输入注册地址查询...')
        self.search_input.setGeometry(370, 70, 131, 31)

        self.search_button = QPushButton('查询数据', self)
        self.search_button.setGeometry(510, 67, 113, 41)
        self.search_button.clicked.connect(self.submit_score)

        self.del_input = QLineEdit(self)
        self.del_input.setPlaceholderText('输入注册地址删除...')
        self.del_input.setGeometry(370, 130, 131, 31)

        self.del_button = QPushButton('删除数据', self)
        self.del_button.setGeometry(510, 128, 113, 41)
        self.del_button.clicked.connect(self.submit_del)

        self.add_button = QPushButton('新增数据', self)
        self.add_button.setGeometry(365, 190, 123, 41)
        self.add_button.clicked.connect(self.show_add_dialog)

        self.updata_button = QPushButton('修改数据', self)
        self.updata_button.setGeometry(500, 190, 123, 41)
        self.updata_button.clicked.connect(self.show_updata_dialog)

        self.admin_button = QPushButton('管理员登录', self)
        self.admin_button.setGeometry(360, 255, 113, 41)
        self.admin_button.clicked.connect(self.admin_login)

        self.adminout_button = QPushButton('管理员退出', self)
        self.adminout_button.setGeometry(550, 0, 100, 30)
        self.adminout_button.clicked.connect(self.admin_logout)

        self.admin_input = QLineEdit(self)
        self.admin_input.setGeometry(490, 260, 131, 31)
        self.admin_input.setPlaceholderText('输入管理员密码...')
        self.admin_input.setEchoMode(QLineEdit.Password)

        self.result_data_label = QLabel('信息返回值', self)
        self.result_data_label.setGeometry(20, 310, 500, 15)
    # 登录逻辑
    def admin_login(self):
        admin_login_input = self.admin_input.text()
        if admin_login_input == self.admin_password:
            self.result_data_label.setText("管理员登录成功")
            self.logged_in = True
            self.admin_input.clear()
            self.load_data()
        else:
            self.result_data_label.setText("管理员密码错误")
    # 登出逻辑
    def admin_logout(self):
        self.result_data_label.setText("管理员已退出登录")
        self.logged_in = False
        # 清除输入框的文本
        self.admin_input.clear()
        # 清除表格内容
        self.result_data_text.setRowCount(0)

    # 新增数据
    def show_add_dialog(self):
        if not self.logged_in:
            self.result_data_label.setText("请先进行管理员登录")
            return

        add_dialog = AddDataDialog(self)
        result = add_dialog.exec_()

        if result == QDialog.Accepted:
            new_data = add_dialog.get_data()

            # 进行输入校验,确保三个输入框都不为空
            if all(value != '' for value in new_data.values()):
                # 读取原有数据
                with open('data.json', 'r') as file:
                    existing_data = json.load(file)

                # 添加新数据
                existing_data.append(new_data)

                # 将更新后的数据写回文件
                with open('data.json', 'w') as file:
                    json.dump(existing_data, file, indent=2)
                    self.result_data_label.setText(f'New Data: {new_data}')

                self.load_data()
            else:
                self.result_data_label.setText("请确保输入不为空")

    # 更新数据
    def show_updata_dialog(self):
        if not self.logged_in:
            self.result_data_label.setText("请先进行管理员登录")
            return

        updata_dialog = UpDataDialog(self)
        result = updata_dialog.exec_()

        if result == QDialog.Accepted:
            new_data = updata_dialog.get_data()
            address = new_data.get('address', '')

            # 进行输入校验,确保要修改的数据存在并且输入框都不为空
            if address != '' and all(value != '' for value in new_data.values()):
                # 读取原有数据
                with open('data.json', 'r') as file:
                    existing_data = json.load(file)

                # 查找要修改的数据
                for data in existing_data:
                    if data.get('address') == address:
                        # 修改数据
                        data.update(new_data)
                        break
                else:
                    self.result_data_label.setText("该数据不存在")
                    print("该数据不存在")

                # 将更新后的数据写回文件
                with open('data.json', 'w') as file:
                    json.dump(existing_data, file, indent=2)
                    self.result_data_label.setText(f'New Data: {new_data}')
                self.load_data()
            else:
                self.result_data_label.setText("请确保要修改的数据存在并且输入不为空")

    # 查询数据
    def submit_score(self):
        if not self.logged_in:
            self.result_data_label.setText("请先进行管理员登录")
            return
        try:
            search_address = self.search_input.text()
            if search_address != '':
                # 读取原有数据
                with open('data.json', 'r') as file:
                    existing_data = json.load(file)

                # 查找具有相应注册地址的数据
                search_result = [data for data in existing_data if data.get('address') == search_address]

                if not search_result:  # 如果 search_result 为空
                    self.result_data_label.setText("暂无查询结果")
                    return

                self.result_data_text.setRowCount(len(search_result))
                for row, data in enumerate(search_result):
                    self.result_data_text.setItem(row, 0, QTableWidgetItem(data.get('address')))
                    self.result_data_text.setItem(row, 1, QTableWidgetItem(str(data.get('username'))))
                    self.result_data_text.setItem(row, 2, QTableWidgetItem(data.get('password')))
            else:
                self.result_data_label.setText("暂无查询结果")
                self.load_data()
        except Exception as e:
            self.result_data_label.setText("出错啦")

    # 查询所有数据
    def load_data(self):
        if not self.logged_in:
            self.result_data_label.setText("请先进行管理员登录")
            return

        try:
            # 读取原有数据
            with open('data.json', 'r') as file:
                existing_data = json.load(file)

            self.result_data_text.setRowCount(len(existing_data))
            for row, data in enumerate(existing_data):
                self.result_data_text.setItem(row, 0, QTableWidgetItem(data.get('address')))
                self.result_data_text.setItem(row, 1, QTableWidgetItem(str(data.get('username'))))
                self.result_data_text.setItem(row, 2, QTableWidgetItem(data.get('password')))
        except FileNotFoundError:
            print("File 'data.json' not found.")
            self.result_data_label.setText("出错啦!")
        except Exception as e:
            self.result_data_label.setText("出错啦!")
            print('Error loading data:', e)

    # 删除数据
    def submit_del(self):
        if not self.logged_in:
            self.result_data_label.setText("请先进行管理员登录")
            return

        address_to_delete = self.del_input.text()
        # 读取原有数据
        with open('data.json', 'r') as file:
            existing_data = json.load(file)

        # 查找并删除对应地址的数据
        existing_data = [data for data in existing_data if data.get('address') != address_to_delete]

        # 将更新后的数据写回文件
        with open('data.json', 'w') as file:
            json.dump(existing_data, file, indent=2)
            self.result_data_label.setText(f'Data with address "{address_to_delete}" deleted.')

        self.load_data()

代码详解(划重点!!!)

    def init_socket(self):
        self.server_address = ('127.0.0.1', 8080)
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.socket.bind(self.server_address)

这个方法初始化一个UDP socket,以便后续进行网络通信。

self.server_address = ('127.0.0.1', 8080): 将服务器的IP地址和端口号设置为 ('127.0.0.1', 8080)。这个IP地址是本地回环地址,端口号是 8080。这是一个常见的设置,用于在本地进行测试。

self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM): 创建一个UDP socket。socket.AF_INET表示使用IPv4地址,socket.SOCK_DGRAM表示这是一个数据报式socket,即UDP socket。

self.socket.bind(self.server_address): 将创建的socket绑定到指定的服务器地址。这是为了确保socket监听指定的IP地址和端口号,以便接收从该地址发送过来的数据。

    def receive_data(self):
        # 启动一个线程来接收数据
        thread = threading.Thread(target=self._receive_data_thread)
        thread.start()

这个方法的主要目的是启动一个线程,用于在后台异步地接收数据。

thread = threading.Thread(target=self._receive_data_thread): 这行代码创建了一个线程对象 thread,并指定了该线程的目标函数(target)为 self._receive_data_thread。这里使用了多线程的概念,允许程序同时执行多个任务。

thread.start(): 这一行代码启动了线程。一旦调用了 start() 方法,线程就会执行 self._receive_data_thread 函数中的代码。

    def _receive_data_thread(self):
        while True:
            try:
                data, address = self.socket.recvfrom(1024)  # 接收数据
                # 在这里处理接收到的数据,更新 UI
                data_str = data.decode('utf-8')
                self.update_ui_with_received_data(data_str)
            except Exception as e:
                print(f"Error receiving data: {e}")

它通常被用作一个线程的目标函数,由 receive_data 方法中的线程调用。

while True:: 这是一个无限循环,表示线程会一直执行下去。

data, address = self.socket.recvfrom(1024): 在循环中,通过 self.socket.recvfrom(1024) 从网络接收数据。这个方法是阻塞的,它会一直等待,直到接收到数据为止。1024 是接收数据的缓冲区大小,可以根据实际情况调整。

data_str = data.decode('utf-8'): 将接收到的二进制数据解码为字符串,使用 UTF-8 编码。这是因为网络传输的数据通常是以字节形式存在,而解码为字符串后更容易处理和理解。

self.update_ui_with_received_data(data_str): 调用类中的另一个方法 update_ui_with_received_data,用于处理接收到的数据并更新用户界面(UI)。这个方法的实现没有在提供的代码片段中给出,但可以设想它负责将接收到的数据以某种方式呈现给用户。

except Exception as e:: 捕获可能出现的异常。如果在接收数据的过程中发生了错误,将错误信息打印到控制台,以便进行调试和错误处理。

    def update_ui_with_received_data(self, received_data):
        self.result_data_label.setText(received_data)

它的主要功能是将接收到的数据更新到用户界面上

self.result_data_label.setText(received_data): 用于更新界面上标签文本内容。self.result_data_label 是一个 UI 元素,通过 setText 方法将 received_data 中的数据显示在该标签上。

self.result_data_label 是一个用于显示数据的标签控件,那么这一行代码的效果是,每当有新的数据接收到时,就会更新这个标签上显示的文本内容为接收到的数据。

    def admin_login(self):
        admin_login_input = self.admin_input.text()
        if admin_login_input == self.admin_password:
            self.result_data_label.setText("管理员登录成功")
            self.logged_in = True
            self.admin_input.clear()
            self.load_data()
        else:
            self.result_data_label.setText("管理员密码错误")

这段代码主要是用来校验管理员登录的功能

我们在ManageApp这个类中的def _init_(self):方法中定义了

self.logged_in = False 默认的登录状态是False
self.admin_password = "admin"  设置了登录的密码(大家如果想深入去写登录的校验过程,可以将这一段进行单独的JSON文件进行查询,当查询的密码与文件中的密码一致时更改登录状态,并且把密码进行加密后存储,从文件中取密码的时候再进行解密)

只有在登录成功后,我们才对更新表格中的数据,通过调用self.load_data()来加载表格中的数据,并且通过self.admin_input.clear()方法,将输入框的密码清空,对密码进行保护!

    def admin_logout(self):
        self.result_data_label.setText("管理员已退出登录")
        self.logged_in = False
        # 清除输入框的文本
        self.admin_input.clear()
        # 清除表格内容
        self.result_data_text.setRowCount(0)

退出登录时,就将登录状态改为false,清空输入框,清空表格里的数据!

        if not self.logged_in:
            self.result_data_label.setText("请先进行管理员登录")
            return

通过在每个方法里面加入登录状态的校验,必须输入管理员密码进行登录后,才能使用每个功能,否则就提示用户,先登录后使用!

# 读取原有数据
                with open('data.json', 'r') as file:
                    existing_data = json.load(file)
  1. with open('data.json', 'r') as file: 打开文件 'data.json',并使用 with 语句,这样可以确保在读取完数据后正确关闭文件。'r' 表示以只读模式打开文件。

  2. existing_data = json.load(file): 使用json 模块中的 load 函数,将打开的文件对象 file 中的 JSON 数据加载到 existing_data 变量中。这样,'data.json' 文件中的原有数据就被读取到了一个 Python 数据结构中。

 # 将更新后的数据写回文件
                with open('data.json', 'w') as file:
                    json.dump(existing_data, file, indent=2)
                    self.result_data_label.setText(f'New Data: {new_data}')
  1. with open('data.json', 'w') as file:: 打开文件 'data.json' 以进行写入操作,使用 with 语句确保在写入完成后正确关闭文件。'w' 参数表示以写入模式打开文件。

  2. json.dump(existing_data, file, indent=2): 使用  json 模块中的 dump 函数,将 existing_data 中的数据写入到文件对象 file 中。 indent=2 是为了让写入的 JSON 数据更加可读。这一步实际上是将原有的数据覆盖掉,用更新后的数据进行替换。

  3. self.result_data_label.setText(f'New Data: {new_data}'): 更新用户界面上的标签 self.result_data_label 中的文本内容,以显示新的数据。

  4. # 查找具有相应注册地址的数据
                    search_result = [data for data in existing_data if data.get('address') == search_address]
    
                    if not search_result:  # 如果 search_result 为空
                        self.result_data_label.setText("暂无查询结果")
                        return
    
                    self.result_data_text.setRowCount(len(search_result))
                    for row, data in enumerate(search_result):
                        self.result_data_text.setItem(row, 0, QTableWidgetItem(data.get('address')))
                        self.result_data_text.setItem(row, 1, QTableWidgetItem(str(data.get('username'))))
                        self.result_data_text.setItem(row, 2, QTableWidgetItem(data.get('password')))

    这段代码是查找具有特定注册地址的数据,并将查询结果在用户界面上显示

  5. search_result = [data for data in existing_data if data.get('address') == search_address]: 使用列表推导式,从 existing_data 中筛选出所有具有与 search_address 相匹配注册地址的数据,并将结果存储在 search_result 列表中。

  6. if not search_result:: 如果 search_result 列表为空,即没有找到匹配的数据,则执行下面的代码块。

  7. return: 结束函数的执行,因为没有查询结果,后续的显示操作也就没有必要了。

  8. self.result_data_text.setRowCount(len(search_result)): 设置用户界面上的表格 self.result_data_text 的行数为查询结果的长度,以确保表格有足够的行数来显示所有查询结果。

  9. 使用循环遍历 search_result,并将每条数据的地址、用户名和密码分别设置到表格的相应位置:

  10. for row, data in enumerate(search_result):
        self.result_data_text.setItem(row, 0, QTableWidgetItem(data.get('address')))
        self.result_data_text.setItem(row, 1, QTableWidgetItem(str(data.get('username'))))
        self.result_data_text.setItem(row, 2, QTableWidgetItem(data.get('password')))
    

     self.result_data_text 是一个表格控件,setItem 方法用于设置表格中的每个单元格的内容。行号 row 表示表格中的行索引,列号 0、1、2 分别表示地址、用户名和密码所在的列。

  11. if __name__ == '__main__':
        app = QApplication(sys.argv)
        score_app = ManageApp()
        score_app.show()
        sys.exit(app.exec_())

  12. if __name__ == '__main__':: 这是一个条件语句,用于检查脚本是否被直接执行而不是被导入为一个模块。当脚本被直接执行时,__name__ 的值会是 '__main__'

  13. app = QApplication(sys.argv): 创建一个Qt应用程序实例。QApplication 是Qt库提供的用于管理应用程序的类,sys.argv 是命令行参数的列表,用于传递一些启动参数给应用程序。

  14. score_app = ManageApp(): 创建一个 ManageApp 类的实例

  15. score_app.show(): 显示主窗口或应用程序的主要界面。

  16. sys.exit(app.exec_()): 启动应用程序的主事件循环。app.exec_() 开始 Qt 的事件循环,并阻塞程序直到窗口被关闭。sys.exit 确保在退出应用程序时返回正确的退出码。

  17. (四)主窗口完整代码:

  18. (可直接复制运行!)

  19. import socket
    import sys
    import json
    import os
    import threading
    from PyQt5.QtCore import Qt
    from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QLineEdit, QPushButton, QDialog, QTableWidget, QTableWidgetItem
    
    
    class AddDataDialog(QDialog):
        def __init__(self, parent=None):
            super(AddDataDialog, self).__init__(parent)
    
            self.setWindowTitle('新增数据')
            self.setGeometry(200, 200, 200, 258)
    
            self.t_add = QLabel('新增数据', self)
            self.t_add.setGeometry(70, 20, 60, 16)
    
            self.t_data1_input = QLineEdit(self)
            self.t_data1_input.setPlaceholderText('注册地址')
            self.t_data1_input.setGeometry(30, 50, 141, 31)
    
            self.t_data2_input = QLineEdit(self)
            self.t_data2_input.setPlaceholderText('用户名')
            self.t_data2_input.setGeometry(30, 90, 141, 31)
    
            self.t_data3_input = QLineEdit(self)
            self.t_data3_input.setPlaceholderText('密码')
            self.t_data3_input.setGeometry(30, 130, 141, 31)
    
            self.t_Button = QPushButton('新增数据', self)
            self.t_Button.setGeometry(40, 170, 113, 41)
            self.t_Button.clicked.connect(self.accept)
    
    
        def get_data(self):
            return {
                'address': self.t_data1_input.text(),
                'username': self.t_data2_input.text(),
                'password': self.t_data3_input.text()
            }
    
    class UpDataDialog(QDialog):
        def __init__(self, parent=None):
            super(UpDataDialog, self).__init__(parent)
    
            self.setWindowTitle('修改数据')
            self.setGeometry(200, 200, 200, 258)
    
            self.t2_add = QLabel('修改数据', self)
            self.t2_add.setGeometry(70, 20, 60, 16)
    
            self.t2_data1_input = QLineEdit(self)
            self.t2_data1_input.setPlaceholderText('注册地址')
            self.t2_data1_input.setGeometry(30, 50, 141, 31)
    
            self.t2_data2_input = QLineEdit(self)
            self.t2_data2_input.setPlaceholderText('用户名')
            self.t2_data2_input.setGeometry(30, 90, 141, 31)
    
            self.t2_data3_input = QLineEdit(self)
            self.t2_data3_input.setPlaceholderText('密码')
            self.t2_data3_input.setGeometry(30, 130, 141, 31)
    
            self.t2_Button = QPushButton('修改数据', self)
            self.t2_Button.setGeometry(40, 170, 113, 41)
            self.t2_Button.clicked.connect(self.accept)
        def get_data(self):
            return {
                'address': self.t2_data1_input.text(),
                'username': self.t2_data2_input.text(),
                'password': self.t2_data3_input.text()
            }
    
    class ManageApp(QMainWindow):
        def __init__(self):
            super(ManageApp, self).__init__()
            self.admin_password = "admin"  # 设置管理员密码
            self.logged_in = False
            self.init_ui()
            self.load_data()
            self.init_socket()  # 初始化 socket
            self.receive_data()  # 启动接收数据的线程
            print("Current Working Directory:", os.getcwd())
    
        def init_socket(self):
            self.server_address = ('127.0.0.1', 8080)
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self.socket.bind(self.server_address)
    
        def receive_data(self):
            # 启动一个线程来接收数据
            thread = threading.Thread(target=self._receive_data_thread)
            thread.start()
    
        def _receive_data_thread(self):
            while True:
                try:
                    data, address = self.socket.recvfrom(1024)  # 接收数据
                    # 在这里处理接收到的数据,更新 UI
                    data_str = data.decode('utf-8')
                    self.update_ui_with_received_data(data_str)
                except Exception as e:
                    print(f"Error receiving data: {e}")
    
        def update_ui_with_received_data(self, received_data):
            self.result_data_label.setText(received_data)
    
        def init_ui(self):
            self.setWindowTitle('用户管理系统')
            self.setFixedSize(655, 338)  # 设置窗口为固定大小
    
            # 数据展示区域
            self.data_label = QLabel('数据展示', self)
            self.data_label.setGeometry(30, 10, 60, 16)
    
            self.result_data_text = QTableWidget(self)
            self.result_data_text.setGeometry(30, 40, 320, 261)
            self.result_data_text.setColumnCount(3)
            self.result_data_text.setHorizontalHeaderLabels(['注册地址', '用户名', '密码'])
            self.result_data_text.horizontalHeader().setStretchLastSection(True)
            self.result_data_text.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
    
            # 数据操作区域
            self.data_control = QLabel('数据操作区', self)
            self.data_control.setGeometry(360, 10, 71, 16)
    
            self.search_input = QLineEdit(self)
            self.search_input.setPlaceholderText('输入注册地址查询...')
            self.search_input.setGeometry(370, 70, 131, 31)
    
            self.search_button = QPushButton('查询数据', self)
            self.search_button.setGeometry(510, 67, 113, 41)
            self.search_button.clicked.connect(self.submit_score)
    
            self.del_input = QLineEdit(self)
            self.del_input.setPlaceholderText('输入注册地址删除...')
            self.del_input.setGeometry(370, 130, 131, 31)
    
            self.del_button = QPushButton('删除数据', self)
            self.del_button.setGeometry(510, 128, 113, 41)
            self.del_button.clicked.connect(self.submit_del)
    
            self.add_button = QPushButton('新增数据', self)
            self.add_button.setGeometry(365, 190, 123, 41)
            self.add_button.clicked.connect(self.show_add_dialog)
    
            self.updata_button = QPushButton('修改数据', self)
            self.updata_button.setGeometry(500, 190, 123, 41)
            self.updata_button.clicked.connect(self.show_updata_dialog)
    
            self.admin_button = QPushButton('管理员登录', self)
            self.admin_button.setGeometry(360, 255, 113, 41)
            self.admin_button.clicked.connect(self.admin_login)
    
            self.adminout_button = QPushButton('管理员退出', self)
            self.adminout_button.setGeometry(550, 0, 100, 30)
            self.adminout_button.clicked.connect(self.admin_logout)
    
            self.admin_input = QLineEdit(self)
            self.admin_input.setGeometry(490, 260, 131, 31)
            self.admin_input.setPlaceholderText('输入管理员密码...')
            self.admin_input.setEchoMode(QLineEdit.Password)
    
            self.result_data_label = QLabel('信息返回值', self)
            self.result_data_label.setGeometry(20, 310, 500, 15)
        # 登录逻辑
        def admin_login(self):
            admin_login_input = self.admin_input.text()
            if admin_login_input == self.admin_password:
                self.result_data_label.setText("管理员登录成功")
                self.logged_in = True
                self.admin_input.clear()
                self.load_data()
            else:
                self.result_data_label.setText("管理员密码错误")
        # 登出逻辑
        def admin_logout(self):
            self.result_data_label.setText("管理员已退出登录")
            self.logged_in = False
            # 清除输入框的文本
            self.admin_input.clear()
            # 清除表格内容
            self.result_data_text.setRowCount(0)
    
        # 新增数据
        def show_add_dialog(self):
            if not self.logged_in:
                self.result_data_label.setText("请先进行管理员登录")
                return
    
            add_dialog = AddDataDialog(self)
            result = add_dialog.exec_()
    
            if result == QDialog.Accepted:
                new_data = add_dialog.get_data()
    
                # 进行输入校验,确保三个输入框都不为空
                if all(value != '' for value in new_data.values()):
                    # 读取原有数据
                    with open('data.json', 'r') as file:
                        existing_data = json.load(file)
    
                    # 添加新数据
                    existing_data.append(new_data)
    
                    # 将更新后的数据写回文件
                    with open('data.json', 'w') as file:
                        json.dump(existing_data, file, indent=2)
                        self.result_data_label.setText(f'New Data: {new_data}')
    
                    self.load_data()
                else:
                    self.result_data_label.setText("请确保输入不为空")
    
        # 更新数据
        def show_updata_dialog(self):
            if not self.logged_in:
                self.result_data_label.setText("请先进行管理员登录")
                return
    
            updata_dialog = UpDataDialog(self)
            result = updata_dialog.exec_()
    
            if result == QDialog.Accepted:
                new_data = updata_dialog.get_data()
                address = new_data.get('address', '')
    
                # 进行输入校验,确保要修改的数据存在并且输入框都不为空
                if address != '' and all(value != '' for value in new_data.values()):
                    # 读取原有数据
                    with open('data.json', 'r') as file:
                        existing_data = json.load(file)
    
                    # 查找要修改的数据
                    for data in existing_data:
                        if data.get('address') == address:
                            # 修改数据
                            data.update(new_data)
                            break
                    else:
                        self.result_data_label.setText("该数据不存在")
                        print("该数据不存在")
    
                    # 将更新后的数据写回文件
                    with open('data.json', 'w') as file:
                        json.dump(existing_data, file, indent=2)
                        self.result_data_label.setText(f'New Data: {new_data}')
                    self.load_data()
                else:
                    self.result_data_label.setText("请确保要修改的数据存在并且输入不为空")
    
        # 查询数据
        def submit_score(self):
            if not self.logged_in:
                self.result_data_label.setText("请先进行管理员登录")
                return
            try:
                search_address = self.search_input.text()
                if search_address != '':
                    # 读取原有数据
                    with open('data.json', 'r') as file:
                        existing_data = json.load(file)
    
                    # 查找具有相应注册地址的数据
                    search_result = [data for data in existing_data if data.get('address') == search_address]
    
                    if not search_result:  # 如果 search_result 为空
                        self.result_data_label.setText("暂无查询结果")
                        return
    
                    self.result_data_text.setRowCount(len(search_result))
                    for row, data in enumerate(search_result):
                        self.result_data_text.setItem(row, 0, QTableWidgetItem(data.get('address')))
                        self.result_data_text.setItem(row, 1, QTableWidgetItem(str(data.get('username'))))
                        self.result_data_text.setItem(row, 2, QTableWidgetItem(data.get('password')))
                else:
                    self.result_data_label.setText("暂无查询结果")
                    self.load_data()
            except Exception as e:
                self.result_data_label.setText("出错啦")
    
        # 查询所有数据
        def load_data(self):
            if not self.logged_in:
                self.result_data_label.setText("请先进行管理员登录")
                return
    
            try:
                # 读取原有数据
                with open('data.json', 'r') as file:
                    existing_data = json.load(file)
    
                self.result_data_text.setRowCount(len(existing_data))
                for row, data in enumerate(existing_data):
                    self.result_data_text.setItem(row, 0, QTableWidgetItem(data.get('address')))
                    self.result_data_text.setItem(row, 1, QTableWidgetItem(str(data.get('username'))))
                    self.result_data_text.setItem(row, 2, QTableWidgetItem(data.get('password')))
            except FileNotFoundError:
                print("File 'data.json' not found.")
                self.result_data_label.setText("出错啦!")
            except Exception as e:
                self.result_data_label.setText("出错啦!")
                print('Error loading data:', e)
    
        # 删除数据
        def submit_del(self):
            if not self.logged_in:
                self.result_data_label.setText("请先进行管理员登录")
                return
    
            address_to_delete = self.del_input.text()
            # 读取原有数据
            with open('data.json', 'r') as file:
                existing_data = json.load(file)
    
            # 查找并删除对应地址的数据
            existing_data = [data for data in existing_data if data.get('address') != address_to_delete]
    
            # 将更新后的数据写回文件
            with open('data.json', 'w') as file:
                json.dump(existing_data, file, indent=2)
                self.result_data_label.setText(f'Data with address "{address_to_delete}" deleted.')
    
            self.load_data()
    
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        score_app = ManageApp()
        score_app.show()
        sys.exit(app.exec_())
    

    (五)客户端程序

import json
import sys
import socket
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtWidgets import QApplication, QDialog, QLabel, QLineEdit, QPushButton, QVBoxLayout, QMainWindow, \
    QTableWidget, QTableWidgetItem, QMessageBox

# --------数据展示类-----------
class UserDisplay(QMainWindow):
    def __init__(self):
        super(UserDisplay, self).__init__()
        self.setWindowTitle('用户端数据展示')
        self.setFixedSize(390, 400)
        self.init_ui()
    # ------------------------------------------------------
    # ---------数据展示界面ui----------
    def init_ui(self):
        self.data_label = QLabel('数据展示', self)
        self.data_label.setGeometry(30, 10, 60, 16)

        self.result_data_text = QTableWidget(self)
        self.result_data_text.setGeometry(30, 40, 320, 261)
        self.result_data_text.setColumnCount(3)
        self.result_data_text.setHorizontalHeaderLabels(['注册地址', '用户名', '密码'])
        self.result_data_text.horizontalHeader().setStretchLastSection(True)
        self.result_data_text.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.load_data_button = QPushButton('加载数据', self)
        self.load_data_button.setGeometry(30, 310, 150, 30)
        self.load_data_button.clicked.connect(self.load_data)

        self.logout_button = QPushButton('LOGOUT', self)
        self.logout_button.setGeometry(170, 310, 100, 30)
        self.logout_button.clicked.connect(self.logout)

        self.result_data_label = QLabel('系统操作提示', self)
        self.result_data_label.setGeometry(20, 370, 200, 20)
    # -------------------------------------------------------------
    # -----------数据加载-----------
    def load_data(self):
        try:
            # 读取原有数据
            with open('data.json', 'r') as file:
                existing_data = json.load(file)

            self.result_data_text.setRowCount(len(existing_data))
            for row, data in enumerate(existing_data):
                self.result_data_text.setItem(row, 0, QTableWidgetItem(data.get('address')))
                self.result_data_text.setItem(row, 1, QTableWidgetItem(str(data.get('username'))))
                self.result_data_text.setItem(row, 2, QTableWidgetItem(data.get('password')))
            self.result_data_label.setText("JSON数据加载完成!")
        except FileNotFoundError:
            print("File 'data.json' not found.")
            self.result_data_label.setText("出错啦!")
        except Exception as e:
            self.result_data_label.setText("出错啦!")
            print('Error loading data:', e)

    def logout(self):
        # 执行退出时的清理工作
        # 例如:关闭当前界面,显示登录界面
        self.close()
        login_and_display.show()
        # 在登录成功后发送UDP消息到服务器
        send_udp_message("User logged out... ")

# ----------登录界面类-------------
class LoginAndDisplay(QDialog):
    login_successful = pyqtSignal()

    def __init__(self, parent=None):
        super(LoginAndDisplay, self).__init__(parent)

        self.logged_in = False
        self.user_username = "user"  # 设置用户账号
        self.user_password = "pass"  # 设置用户密码

        self.setWindowTitle('登录')
        self.setGeometry(200, 200, 300, 150)

        self.label_username = QLabel('账号:', self)
        self.input_username = QLineEdit(self)

        self.label_password = QLabel('密码:', self)
        self.input_password = QLineEdit(self)
        self.input_password.setEchoMode(QLineEdit.Password)

        self.btn_login = QPushButton('登录', self)
        self.btn_login.clicked.connect(self.login)

        # -------账号密码免输入--------
        self.input_username.setText(self.user_username)
        self.input_password.setText(self.user_password)

        layout = QVBoxLayout(self)
        layout.addWidget(self.label_username)
        layout.addWidget(self.input_username)
        layout.addWidget(self.label_password)
        layout.addWidget(self.input_password)
        layout.addWidget(self.btn_login)
    # ------------------------------------------------------
    # ------------登录功能-------------
    def login(self):
        username_input = self.input_username.text()
        password_input = self.input_password.text()
        if username_input == self.user_username and password_input == self.user_password:
            self.logged_in = True
            self.login_successful.emit()  # 发出登录成功的信号
        else:
            QMessageBox.warning(self, "登录失败", "账号或密码错误!", QMessageBox.Ok)



# -------------UDP通讯设置----------------
def send_udp_message(message):
    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
        server_address = ('127.0.0.1', 8080)  # 服务器地址和端口
        s.sendto(message.encode('utf-8'), server_address)


if __name__ == '__main__':
    app = QApplication(sys.argv)

    login_and_display = LoginAndDisplay()
    user_display = UserDisplay()

    def show_user_display():
        login_and_display.close()
        user_display.show()
        # 在登录成功后发送UDP消息到服务器
        send_udp_message("User logged in... ")

    login_and_display.login_successful.connect(show_user_display)
    login_and_display.exec_()

    sys.exit(app.exec_())
  1. UserDisplay 类

    • 该类继承自 QMainWindow,用于展示用户数据。
    • init_ui 方法初始化用户界面,包括数据展示的表格、加载数据的按钮、注销按钮等。
    • load_data 方法用于从 'data.json' 文件中加载数据,并在表格中展示。
    • logout 方法用于执行用户注销的操作,关闭当前界面,并显示登录界面。
  2. LoginAndDisplay 类

    • 该类继承自 QDialog,用于实现登录功能。
    • login_successful 是一个自定义的信号,当登录成功时发出。
    • login 方法用于验证用户输入的用户名和密码,如果匹配成功则发出登录成功的信号。
  3. UDP通讯设置

    • send_udp_message 函数用于通过UDP发送消息到指定的服务器地址和端口。
  4. if __name__ == '__main__': 部分

    • 创建了一个 QApplication 实例。
    • 创建了 LoginAndDisplayUserDisplay 的对象。
    • 通过连接 login_successful 信号和 show_user_display 槽函数,实现了在登录成功后关闭登录界面,显示用户数据展示界面,并发送UDP消息到服务器的功能。
    • 最后通过 sys.exit(app.exec_()) 启动了应用程序的主事件循环。

客户端可以复制多个!!!可以同时运行!!!在主函数里面已经开启了多线程,不会阻塞主线程的正常使用!

(六)JSON文件:

[
  {
    "address": "rdi@pf.io",
    "username": "\u79b9\u5b97",
    "password": "pwd446"
  },
  {
    "address": "r2ulhht8@hrtnfsic.io",
    "username": "\u6a0a\u5b50",
    "password": "pwd304"
  },
  {
    "address": "evhqx@orv.io",
    "username": "\u6743\u677e\u5357",
    "password": "pwd176"
  },
  {
    "address": "qtq4qybp4j@swtfdk.im",
    "username": "\u59ec\u8fdc",
    "password": "pwd340"
  },
  {
    "address": "oiax41g6j@n4mi.im",
    "username": "\u8a00\u671d",
    "password": "pwd030"
  },
  {
    "address": "vslp7@vc.me",
    "username": "\u53f2\u987a",
    "password": "pwd983"
  },
  {
    "address": "yf8ydn@hfdcq.im",
    "username": "\u51b7\u5a23\u806a",
    "password": "pwd501"
  },
  {
    "address": "5dj3qrym6@g3.cn",
    "username": "\u9ea6\u632f",
    "password": "pwd512"
  },
  {
    "address": "f2w3i0@dn2.cc",
    "username": "\u5b8b\u671d",
    "password": "pwd311"
  },
  {
    "address": "uctg9@kpjg.cn",
    "username": "123",
    "password": "123"
  }
]

本期的PyQt的程序案例就分享到这了!后期会更新更多PyQt的程序!感谢支持!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RMB Player

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值