理发店收银管理系统 python、sqlite3、pyqt5

news2024/11/16 19:42:59

给姐姐家店写的一个,功能比较简单,结合gpt
功能包含:次卡和充值卡,可以查剩余次数、以及查找消费记录
···后期会把sqlite3转到mysql,换成springboot的一个项目

1.使用技术:

Python、sqlite3、PyQt5

2.页面

在这里插入图片描述

3.sqlite3

一共三张数据表:

  • user_card : 次卡数据
  • user_store : 储蓄卡
  • consumption: 消费记录表
    消费记录为了省事,包含了所有的记录,

4.代码(主要部分)

import re
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, \
    QTableWidget, QTableWidgetItem, QHeaderView, QMessageBox, QDialog, QFormLayout, QInputDialog, QMainWindow
from PyQt5.QtGui import QColor, QPalette, QImage, QPixmap, QBrush, QPainter
from PyQt5.QtCore import Qt, QDate, QDateTime
import sqlite3


class BackgroundWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        # self.image_path = "image/background.png"
        self.image_path = "image/panda.png"

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)

        # 绘制背景图片
        background_image = QImage(self.image_path)
        background_image = background_image.scaled(self.size(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
        painter.drawImage(0, 0, background_image)

        # 绘制半透明遮罩层
        opacity = 0.6
        overlay_color = QColor(0, 0, 0, int(255 * opacity))
        painter.setCompositionMode(QPainter.CompositionMode_DestinationOver)
        painter.fillRect(self.rect(), overlay_color)

        super().paintEvent(event)


class ConsumeCard(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("理发店收银系统")
        self.resize(1200, 800)

        # 创建数据库连接
        self.connection = sqlite3.connect("db/barbershop.db")
        self.cursor = self.connection.cursor()

        # 创建界面控件
        self.id_label = QLabel("ID:")
        self.id_input = QLineEdit()
        self.name_label = QLabel("姓名:")
        self.name_input = QLineEdit()
        self.tel_label = QLabel("电话:")
        self.tel_input = QLineEdit()
        self.remain_label = QLabel("次数:")
        self.remain_input = QLineEdit()
        self.remark_label = QLabel("备注:")
        self.remark_input = QLineEdit()
        # self.discount_label = QLabel("折扣:")
        # self.discount_input = QLineEdit()

        self.create_user_button = QPushButton("新建用户")
        self.search_label = QLabel("查询(ID或电话):")
        self.search_input = QLineEdit()
        self.search_button = QPushButton("查询")
        self.clear_button = QPushButton("清除")
        self.recharge_button = QPushButton("充值")
        self.consume_button = QPushButton("消费")
        # self.record_button = QPushButton("消费记录")
        self.consumption_record_button = QPushButton("消费记录")

        self.user_table = QTableWidget()

        # 设置布局
        form_layout = QFormLayout()
        # form_layout.addRow(self.id_label, self.id_input)
        form_layout.addRow(self.name_label, self.name_input)
        form_layout.addRow(self.tel_label, self.tel_input)
        form_layout.addRow(self.remain_label, self.remain_input)
        form_layout.addRow(self.remark_label, self.remark_input)
        # form_layout.addRow(self.discount_label, self.discount_input)

        button_layout = QHBoxLayout()
        button_layout.addWidget(self.create_user_button)
        button_layout.addWidget(self.search_label)
        button_layout.addWidget(self.search_input)
        button_layout.addWidget(self.search_button)
        button_layout.addWidget(self.clear_button)
        button_layout.addWidget(self.recharge_button)
        button_layout.addWidget(self.consume_button)
        button_layout.addWidget(self.consumption_record_button)

        main_layout = QVBoxLayout()
        main_layout.addLayout(form_layout)
        main_layout.addLayout(button_layout)
        main_layout.addWidget(self.user_table)

        self.setLayout(main_layout)

        # 绑定信号与槽函数
        self.create_user_button.clicked.connect(self.create_user)
        self.search_button.clicked.connect(self.search_user)
        self.clear_button.clicked.connect(self.clear)
        self.recharge_button.clicked.connect(self.recharge)
        self.consume_button.clicked.connect(self.consume)
        # self.record_button.clicked.connect(self.show_consumption_records)
        self.consumption_record_button.clicked.connect(self.show_consumption_record)


        # 初始化用户表格
        self.init_user_table()

        # 当前选中的用户信息
        self.current_user = None

        background_widget = BackgroundWidget()
        background_widget.setLayout(main_layout)
        self.setCentralWidget(background_widget)

        self.setAttribute(Qt.WA_TranslucentBackground)  # 设置窗口背景透明

        # 设置样式
        self.setStyleSheet(
            
        )

    def set_background_image(self, image_path):
        background_image = QImage(image_path)
        background_image = background_image.scaled(self.size(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
        palette = QPalette()
        palette.setBrush(QPalette.Background, QBrush(background_image))
        self.setPalette(palette)

    def init_user_table(self):
        self.user_table.setColumnCount(5)
        self.user_table.setHorizontalHeaderLabels(["ID", "姓名", "电话", "剩余次数", "备注"])
        self.user_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.user_table.setSelectionBehavior(QTableWidget.SelectRows)
        self.user_table.setEditTriggers(QTableWidget.NoEditTriggers)
        self.user_table.cellClicked.connect(self.show_user_info)

        self.update_user_table()

    def update_user_table(self):
        self.user_table.clearContents()
        self.cursor.execute("SELECT id, name, tel, remain, remark FROM user_card")
        users = self.cursor.fetchall()
        self.user_table.setRowCount(len(users))
        for i, user in enumerate(users):
            for j in range(len(user)):
                item = QTableWidgetItem(str(user[j]))
                item.setTextAlignment(Qt.AlignCenter)
                self.user_table.setItem(i, j, item)

    def show_user_info(self, row):
        self.id_input.setText(self.user_table.item(row, 0).text())
        self.name_input.setText(self.user_table.item(row, 1).text())
        self.tel_input.setText(self.user_table.item(row, 2).text())
        self.remain_input.setText(self.user_table.item(row, 3).text())
        self.remark_input.setText(self.user_table.item(row, 4).text())
        # self.discount_input.setText(self.user_table.item(row, 5).text())

        # 存储选中的用户信息
        self.current_user = {
            'id': self.user_table.item(row, 0).text(),
            'name': self.user_table.item(row, 1).text(),
            'tel': self.user_table.item(row, 2).text(),
            'remain': self.user_table.item(row, 3).text(),
            'remark': self.user_table.item(row, 4).text(),
            # 'discount': self.user_table.item(row, 5).text()
        }

    def create_user(self):
        name = self.name_input.text()
        tel = self.tel_input.text()
        remain = self.remain_input.text()
        remark = self.remark_input.text()
        # discount = self.discount_input.text()

        if not name or not tel or not remain:
            QMessageBox.warning(self, "错误", "请填写完整用户信息")
            return

        try:
            remain = int(remain)
            # discount = float(discount)
        except ValueError:
            QMessageBox.warning(self, "错误", "次数必须是数字")
            return

        # 验证手机号格式
        if not self.validate_phone_number(tel):
            QMessageBox.warning(self, "错误", "手机号格式错误")
            return

        # 检查是否已经存在该手机号的用户
        self.cursor.execute("SELECT id FROM user_card WHERE tel = ?", (tel,))
        existing_user = self.cursor.fetchone()
        if existing_user:
            QMessageBox.warning(self, "错误", "已存在该用户手机号")
            return


        current_datetime = QDateTime.currentDateTime()
        formatted_datetime = current_datetime.toString(Qt.ISODate)
        formatted_datetime = formatted_datetime[:10] + " " + formatted_datetime[11:]
        self.cursor.execute("INSERT INTO consumption_record (name, tel, consumption, balance, consumption_time, remark) "
                            "VALUES (?, ?, ?, ?, ?, ?)",
                            (name, tel, remain, remain, formatted_datetime, remark))
        self.cursor.execute("INSERT INTO user_card (name, tel, remain, remark) "
                            "VALUES (?, ?, ?, ?)",
                            (name, tel, remain, remark))
        self.connection.commit()
        self.update_user_table()
        self.clear()

        QMessageBox.information(self, "成功", "用户创建成功")

    # 验证手机号
    def validate_phone_number(self, phone_number):
        pattern = r"^1[3-9]\d{9}$"
        return re.match(pattern, phone_number) is not None

    def search_user(self):
        keyword = self.search_input.text()
        self.cursor.execute(
            "SELECT id, name, tel, remain, remark  FROM user_card WHERE id LIKE ? OR name like ? OR tel LIKE ?",
            (f"%{keyword}%", f"%{keyword}%", f"%{keyword}%"))
        users = self.cursor.fetchall()
        if len(users) == 0:
            QMessageBox.warning(self, "错误", "未找到用户")
            return

        self.user_table.clearContents()
        self.user_table.setRowCount(len(users))
        for i, user in enumerate(users):
            for j in range(len(user)):
                item = QTableWidgetItem(str(user[j]))
                item.setTextAlignment(Qt.AlignCenter)
                self.user_table.setItem(i, j, item)

    def clear(self):
        self.id_input.clear()
        self.name_input.clear()
        self.tel_input.clear()
        self.remain_input.clear()
        self.remark_input.clear()
        # self.discount_input.clear()
        self.user_table.clearSelection()

        # 重置当前选中的用户信息
        self.current_user = None

    def recharge(self):
        if self.current_user is None:
            QMessageBox.warning(self, "错误", "请先选择用户")
            return

        id = self.current_user['id']
        name = self.current_user['name']
        tel = self.current_user['tel']
        remain = self.current_user['remain']
        remark = self.current_user['remark']

        # amount, ok = QInputDialog.getDouble(self, "充值", "请输入充值次数:", decimals=2)
        amount, ok = QInputDialog.getInt(self, "充值", "请输入充值次数:")
        if ok and amount > 0:
            self.cursor.execute("UPDATE user_card SET remain = remain + ? WHERE id = ?", (amount, id))
            try:
                remain = int(remain)
                amount = int(amount)
            except ValueError:
                QMessageBox.warning(self, "错误", "余额和折扣必须是数字")
                return
            current_datetime = QDateTime.currentDateTime()
            formatted_datetime = current_datetime.toString(Qt.ISODate)
            formatted_datetime = formatted_datetime[:10] + " " + formatted_datetime[11:]
            self.cursor.execute("INSERT INTO consumption_record (name, tel, consumption, balance, consumption_time, remark) "
                                "VALUES (?, ?, ?, ?, ?, ?)",
                                (name, tel, amount, remain + amount, formatted_datetime, remark))
            self.connection.commit()
            self.update_user_table()
            self.clear()
            QMessageBox.information(self, "成功", "充值成功")

        # 重置当前选中的用户信息
        self.current_user = None

    def consume(self):
        if self.current_user is None:
            QMessageBox.warning(self, "错误", "请先选择用户")
            return

        id = self.current_user['id']
        name = self.current_user['name']
        tel = self.current_user['tel']
        remain = self.current_user['remain']
        remark = self.current_user['remark']

        # amount, ok = QInputDialog.getDouble(self, "消费", "请输入消费金额:", decimals=2)
        amount, ok = QInputDialog.getInt(self, "消费", "请输入消费金额:")
        if ok and amount > 0:
            # self.cursor.execute("SELECT discount FROM user_card WHERE id = ?", (id,))
            # discount = self.cursor.fetchone()[0]

            try:
                remain = int(remain)
                # discount = float(discount)
                amount = int(amount)
            except ValueError:
                QMessageBox.warning(self, "错误", "余额和折扣必须是数字")
                return

            # amount *= discount
            if remain < amount:
                QMessageBox.warning(self, "错误", "余额不足")
                return

            self.cursor.execute("UPDATE user_card SET remain = remain - ? WHERE id = ?", (amount, id))
            current_datetime = QDateTime.currentDateTime()
            formatted_datetime = current_datetime.toString(Qt.ISODate)
            formatted_datetime = formatted_datetime[:10] + " " + formatted_datetime[11:]
            self.cursor.execute(
                "INSERT INTO consumption_record (name, tel, consumption, balance, consumption_time, remark) "
                "VALUES (?, ?, ?, ?, ?, ?)",
                (name, tel, -amount, remain - amount, formatted_datetime, remark))
            self.connection.commit()
            self.update_user_table()
            self.clear()
            QMessageBox.information(self, "成功", "消费成功")

        # 重置当前选中的用户信息
        self.current_user = None


    def show_consumption_record(self):
        if self.current_user is None:
            QMessageBox.warning(self, "错误", "请先选择用户")
            return

        tel = self.current_user['tel']
        self.cursor.execute(
            "SELECT id, name, tel, consumption, balance, consumption_time, remark FROM consumption_record WHERE tel = ?",
            (tel,))
        records = self.cursor.fetchall()

        dialog = QDialog(self)
        dialog.setWindowTitle("消费记录")
        dialog.resize(1400, 600)

        table = QTableWidget(dialog)
        table.setColumnCount(6)
        table.setHorizontalHeaderLabels(["姓名", "电话", "消费", "剩余次数", "消费时间", "备注"])
        table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        table.setSelectionBehavior(QTableWidget.SelectRows)
        table.setEditTriggers(QTableWidget.NoEditTriggers)
        table.setRowCount(len(records))

        for i, record in enumerate(records):
            for j in range(1, len(record)):
                item = QTableWidgetItem(str(record[j]))
                item.setTextAlignment(Qt.AlignCenter)
                table.setItem(i, j - 1, item)

        main_layout = QVBoxLayout()
        main_layout.addWidget(table)
        dialog.setLayout(main_layout)

        dialog.exec_()

    # def get_consumption_records(self, phone_number):
    #     self.cursor.execute("SELECT * FROM consumption_record WHERE tel=?",
    #                         (phone_number,))
    #     return self.cursor.fetchall()

    def closeEvent(self, event):
        self.connection.close()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    cashier = ConsumeCard()
    cashier.show()
    sys.exit(app.exec_())

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

快手可灵视频生成大模型全方位测评

快手视频生成大模型“可灵”&#xff08;Kling&#xff09;&#xff0c;是全球首个真正用户可用的视频生成大模型&#xff0c;自面世以来&#xff0c;凭借其无与伦比的视频生成效果&#xff0c;在全球范围内赢得了用户的热烈追捧与高度评价。截至目前&#xff0c;申请体验其内测…

如何使用C#自制一个Windows安装包

原文链接&#xff1a;https://www.cnblogs.com/zhaotianff/p/17387496.html 以前都在用InstallShield制作安装包&#xff0c;基本需求是能满足的&#xff0c;但也有一些缺点&#xff1a; 1、界面不能完全定制 2、不能直接调用代码里的功能 平常使用一些其它软件&#xff0c;…

ETL数据集成丨将PostgreSQL数据库数据实时同步至PostgreSQL

前言 我们在进行数据集成、实时数据同步中&#xff0c;经常会出现在同一个数据库中做数据同步和复制、实时分析和报告、负载均衡和高可用性等场景&#xff0c;这次我们以PostgreSQL为例&#xff0c;通过ETLCloud工具&#xff0c;进行同数据库中数据实时同步的步骤应该如何设置…

Anconda 快速常用命令简洁版

目的&#xff1a;简单清楚的使用基本的conda 命令 可能需求 查看项目中的虚拟环境及依赖是否满足需求操作新环境来满足项目或者论文的实现 Anconda 常用命令 conda 查看基础命令1. 进入Anaconda 环境2. 查看版本3.查看有哪些虚拟环境4.激活虚拟环境5. 进入虚拟环境查看6. 退出…

shopee虾皮 java后端 一面面经 整体感觉不难

面试总结&#xff1a;总体不难&#xff0c;算法题脑抽了只过了一半&#xff0c;面试官点出了问题说时间到了&#xff0c;反问一点点&#xff0c;感觉五五开&#xff0c;许愿一个二面 1.Java中的锁机制&#xff0c;什么是可重入锁 Java中的机制主要包括 synchronized关键字 Loc…

微信小程序之计算器

在日常生活中&#xff0c;计算器是人们广泛使用的工具&#xff0c;可以帮助我们快速且方便地计算金额、成本、利润等。下面将会讲解如何开发一个“计算器”微信小程序。 一、开发思路 1、界面和功能 “计算器”微信小程序的页面效果如图所示 在计算器中可以进行整数和小数的…

NET8部署Kestrel服务HTTPS深入解读TLS协议之Certificate证书

Certificate证书 Certificate称为数字证书。数字证书是一种证明身份的电子凭证&#xff0c;它包含一个公钥和一些身份信息&#xff0c;用于验证数字签名和加密通信。数字证书在网络通信、电子签名、认证授权等场景中都有广泛应用。其特征如下&#xff1a; 由权威机构颁发&…

Minos 多主机分布式 docker-compose 集群部署

参考 docker-compose搭建多主机分布式minio - 会bk的鱼 - 博客园 (cnblogs.com) Minio 是个基于 Golang 编写的开源对象存储套件&#xff0c;虽然轻量&#xff0c;却拥有着不错的性能 中文地址&#xff1a;MinIO | 用于AI的S3 & Kubernetes原生对象存储 官网地址&#xf…

数字看板:跨行业需求下的创新与升级

在当今这个数据驱动的时代&#xff0c;数字看板作为信息展示与决策支持的重要工具&#xff0c;正逐步渗透到各行各业之中。从智慧城市到智能制造&#xff0c;从金融分析到医疗健康&#xff0c;数字看板以其直观、动态、高效的特点&#xff0c;成为了连接数据与决策者的桥梁。本…

C# 将字符串数组以树型结构化

例如字符串数组&#xff1a; string[] arr { "1","3-4-5-6-7", "2","3-4","3-4-5","3-4-5-6", "3", "6", "4", "6-1", "6-2", "5", "6-1-1&…

c++如何理解多态与虚函数

目录 **前言****1. 何为多态**1.1 **编译时多态**1.1.1 函数重载1.1.2 模板 **1.2 运行时多态****1.2.1 虚函数****1.2.2 为什么要用父类指针去调用子类函数** **2. 注意****2.1 基类的析构函数应写为虚函数****2.2 构造函数不能设为虚函数** **本文参考** 前言 在学习 c 的虚…

Tableau入门|数据可视化与仪表盘搭建

原视频链接&#xff08;up:戴戴戴师兄&#xff09;&#xff0c;文章为笔者的自学笔记&#xff0c;用于复习回顾&#xff0c;原视频下方有原up整理的笔记&#xff0c;更加直观便捷。因为视频中间涉及的细节较多&#xff0c;建议一边操作&#xff0c;一边学习。 整体介绍 可视化…

生成式AI:对话系统(Chat)与自主代理(Agent)的和谐共舞

生成式AI&#xff1a;对话与行动的和谐共舞 我们正站在一个令人激动的时代门槛上——生成式AI技术飞速发展&#xff0c;带来了无限的可能性。一个关键问题浮现&#xff1a;AI的未来是对话系统&#xff08;Chat&#xff09;的天下&#xff0c;还是自主代理&#xff08;Agent&am…

非凸T0算法,如何获取超额收益?

什么是非凸 T0 算法&#xff1f; 非凸 T0 算法基于投资者持有的股票持仓&#xff0c;利用机器学习等技术&#xff0c;短周期预测&#xff0c;全自动操作&#xff0c;抓取行情波动价差&#xff0c;增厚产品收益。通过开仓金额限制、持仓时长控制等&#xff0c;把控盈亏风险&…

【Ant Design Pro】快速上手

初始化 初始化脚手架&#xff1a;快速开始 官方默认使用 umi4&#xff0c;这里文档还没有及时更新&#xff08;不能像文档一样选择 umi 的版本&#xff09;&#xff0c;之后我选择 simple。 然后安装依赖。 在 package.json 中&#xff1a; "start": "cross-e…

java-数据结构与算法-02-数据结构-05-栈

文章目录 1. 栈1. 概述2. 链表实现3. 数组实现4. 应用 2. 习题E01. 有效的括号-Leetcode 20E02. 后缀表达式求值-Leetcode 120E03. 中缀表达式转后缀E04. 双栈模拟队列-Leetcode 232E05. 单队列模拟栈-Leetcode 225 1. 栈 1. 概述 计算机科学中&#xff0c;stack 是一种线性的…

学习Numpy的奇思妙想

学习Numpy的奇思妙想 本文主要想记录一下&#xff0c;学习 numpy 过程中的偶然的灵感&#xff0c;并记录一下知识框架。 推荐资源&#xff1a;https://numpy.org/doc/stable/user/absolute_beginners.html &#x1f4a1;灵感 为什么 numpy 数组的 shape 和 pytorch 是 tensor 是…

WordPress 后台开发技巧:向文章发布页右侧添加自定义菜单项

案例图片 这个案例向你介绍了如何在文章发布页的右侧边栏增加一个新的自定义菜单项。具体用它实现什么功能&#xff0c;就看你的需要了。 代码 function add_custom_menu_item() { add_meta_box(custom_menu_item, 这里是菜单项名称, display_custom_menu_item, post, side, …

昇思MindSpore学习入门-高阶自动微分

mindspore.ops模块提供的grad和value_and_grad接口可以生成网络模型的梯度。grad计算网络梯度&#xff0c;value_and_grad同时计算网络的正向输出和梯度。本文主要介绍如何使用grad接口的主要功能&#xff0c;包括一阶、二阶求导&#xff0c;单独对输入或网络权重求导&#xff…

代码随想录算法训练营Day 63| 图论 part03 | 417.太平洋大西洋水流问题、827.最大人工岛、127. 单词接龙

代码随想录算法训练营Day 63| 图论 part03 | 417.太平洋大西洋水流问题、827.最大人工岛、127. 单词接龙 文章目录 代码随想录算法训练营Day 63| 图论 part03 | 417.太平洋大西洋水流问题、827.最大人工岛、127. 单词接龙17.太平洋大西洋水流问题一、DFS二、BFS三、本题总结 82…