当前位置: 首页 > news >正文

Python Pyside6 加Sqlite3 写一个 通用 进销存 系统 初型

 图:

 

说明:

进销存管理系统说明文档

功能模块

1. 首页

  • 显示关键业务数据
    • 商品总数
    • 供应商总数
    • 本月采购金额
    • 本月销售金额
  • 显示预警信息
    • 库存不足预警
    • 待付款采购单
    • 待收款销售单

2. 商品管理

  • 商品信息维护
    • 商品编码(唯一标识)
    • 商品名称
    • 规格型号
    • 单位
    • 分类
    • 进货价
    • 销售价
    • 库存数量
    • 预警数量
  • 支持批量导入导出
  • 支持搜索和筛选

3. 供应商管理

  • 供应商信息维护
    • 供应商名称(唯一标识)
    • 联系人
    • 联系电话
    • 地址
    • 账期
  • 支持批量导入导出
  • 支持搜索和筛选

4. 客户管理

  • 客户信息维护
    • 客户名称(唯一标识)
    • 联系人
    • 联系电话
    • 地址
    • 电子邮箱
    • 备注信息
  • 基本操作
    • 新建客户:添加新的客户信息
    • 编辑客户:修改现有客户信息
    • 删除客户:删除未使用的客户(已有销售订单的客户不能删除)
    • 搜索客户:按名称、联系人、电话等信息搜索
  • 数据导入导出
    • 支持从Excel导入客户数据
    • 支持导出客户数据到Excel
    • 导入时自动处理重复数据
  • 数据验证
    • 客户名称不能重复
    • 删除前检查销售订单关联
    • 导入数据格式验证

5. 采购管理

  • 采购单管理
    • 新建采购单
    • 编辑采购单
    • 删除采购单
    • 查看采购历史
  • 采购单信息
    • 采购单号(自动生成)
    • 供应商
    • 采购日期
    • 付款状态
    • 入库状态
    • 商品明细
      • 商品
      • 数量
      • 单价
      • 金额
  • 支持搜索和筛选

6. 销售管理

  • 销售单管理
    • 新建销售单
    • 编辑销售单
    • 删除销售单
    • 查看销售历史
  • 销售单信息
    • 销售单号(自动生成)
    • 客户信息
    • 销售日期
    • 收款状态
    • 出库状态
    • 商品明细
      • 商品
      • 数量
      • 单价
      • 金额
  • 支持搜索和筛选

7. 库存管理

  • 库存状态查看
    • 商品名称
    • 当前库存
    • 预警库存
    • 库存状态
  • 库存调整
    • 手动入库
    • 手动出库
    • 调整原因记录
  • 变动历史查询
    • 调整时间
    • 调整类型
    • 调整数量
    • 调整原因
  • 支持搜索和筛选

8. 系统备份

  • 数据备份功能
    • 创建备份:将当前数据库导出为备份文件
    • 自动生成包含时间戳的文件名
    • 可选择备份文件保存位置
  • 数据恢复功能
    • 从备份文件恢复数据
    • 恢复前自动创建当前数据的备份
    • 支持确认提示,防止误操作
  • 使用建议
    • 定期创建数据备份
    • 将备份文件保存在不同的存储设备上
    • 重要操作前先创建备份
    • 系统升级前创建备份

财务管理

财务管理模块提供了全面的财务操作和分析功能,包括收付款管理、财务统计、应收应付对账等功能。

收付款记录

  • 新建收款:记录客户的付款信息
    • 选择关联销售单号
    • 输入收款金额
    • 选择支付方式(现金/银行转账/支付宝/微信)
    • 添加备注信息
  • 新建付款:记录向供应商的付款信息
    • 选择关联采购单号
    • 输入付款金额
    • 选择支付方式
    • 添加备注信息
  • 搜索功能:支持按关键字搜索收付款记录

财务统计

  • 时间范围选择:
    • 本月
    • 上月
    • 最近7天
    • 最近30天
    • 全部
  • 统计指标:
    • 总收入
    • 总支出
    • 净收入

应收对账

  • 客户应收账款管理:
    • 按客户查看应收账款
    • 时间筛选(全部/本月/上月/本年/上年)
    • 显示销售总额、已收金额、应收金额
    • 详细的销售订单列表(含收款状态)
  • 导出功能:
    • 导出Excel格式的对账单
    • 支持打印对账单(含打印预览)

应付对账

  • 供应商应付账款管理:
    • 按供应商查看应付账款
    • 时间筛选(全部/本月/上月/本年/上年)
    • 显示采购总额、已付金额、应付金额
    • 详细的采购订单列表(含付款状态)
  • 导出功能:
    • 导出Excel格式的对账单
    • 支持打印对账单(含打印预览)

使用说明

  1. 收付款操作:
    • 点击"新建收款"或"新建付款"按钮
    • 选择关联单号,系统会自动填充相关金额
    • 填写实际收付款金额和支付方式
    • 可添加备注信息
    • 确认保存
  1. 财务统计:
    • 选择要查看的时间范围
    • 系统自动计算并显示收入、支出和净收入
  1. 应收对账:
    • 选择客户和时间范围
    • 查看应收账款汇总和明细
    • 可导出Excel或打印对账单
  1. 应付对账:
    • 选择供应商和时间范围
    • 查看应付账款汇总和明细
    • 可导出Excel或打印对账单

使用说明

1. 系统初始化

  1. 首次使用请先维护基础数据:
    • 添加商品信息
    • 添加供应商信息
    • 添加客户信息
  1. 设置商品库存预警值

2. 日常操作

  1. 采购入库
    • 新建采购单
    • 选择供应商
    • 添加商品明细
    • 设置入库状态
  1. 销售出库
    • 新建销售单
    • 选择客户
    • 添加商品明细
    • 设置出库状态
  1. 库存管理
    • 定期盘点库存
    • 及时处理库存预警
  1. 系统维护
    • 及时备份数据
    • 定期检查系统运行状态

3. 数据备份

  1. 定期备份
    • 进入"系统备份"页面
    • 点击"创建备份"按钮
    • 选择备份文件保存位置
    • 确认备份成功
  1. 数据恢复
    • 进入"系统备份"页面
    • 点击"恢复备份"按钮
    • 选择要恢复的备份文件
    • 确认恢复操作
    • 等待恢复完成

注意事项

  1. 数据安全
    • 定期备份数据库
    • 将备份文件保存在安全的位置
    • 定期检查备份文件的完整性
    • 重要操作前先创建备份
    • 定期备份数据库
    • 妥善保管系统账号密码
  1. 操作规范
    • 严格按照操作流程执行
    • 认真核对数据后再保存
  1. 异常处理
    • 遇到问题及时记录
    • 系统故障及时报修

代码:

主窗口 main.py

import sys
from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,QHBoxLayout, QTabWidget, QPushButton, QLabel)
from PySide6.QtCore import Qt
from database import Database
from products import ProductsTab
from dashboard import DashboardTab
from suppliers import SuppliersTab
from customers import CustomersTab
from purchase import PurchaseTab
from sales import SalesTab
from inventory import InventoryTab
from finance import FinanceTab
from backup import BackupWidgetclass MainWindow(QMainWindow):def __init__(self, db):super().__init__()self.db = dbself.init_ui()def init_ui(self):self.setWindowTitle('进销存管理系统')self.resize(1200, 800)# Create tab widgettab_widget = QTabWidget()tab_widget.addTab(ProductsTab(self.db), '商品管理')tab_widget.addTab(SuppliersTab(self.db), '供应商管理')tab_widget.addTab(CustomersTab(self.db), '客户管理')tab_widget.addTab(PurchaseTab(self.db), '采购管理')tab_widget.addTab(SalesTab(self.db), '销售管理')tab_widget.addTab(InventoryTab(self.db), '库存管理')tab_widget.addTab(FinanceTab(self.db), '财务管理')tab_widget.addTab(BackupWidget('inventory.db'), '系统备份')self.setCentralWidget(tab_widget)def main():app = QApplication(sys.argv)db = Database()  # Create database instancewindow = MainWindow(db)  # Pass database instance to MainWindowwindow.show()sys.exit(app.exec())if __name__ == '__main__':main() 

 

商品管理 products.py

from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QPushButton,QLabel, QLineEdit, QTableWidget, QTableWidgetItem,QMessageBox, QHeaderView, QFileDialog, QDialog,QFormLayout, QDoubleSpinBox, QSpinBox)
from PySide6.QtCore import Qt
import openpyxl
from datetime import datetime
import osclass ProductEditDialog(QDialog):def __init__(self, product=None, parent=None):super().__init__(parent)self.product = productself.init_ui()def init_ui(self):self.setWindowTitle('编辑商品' if self.product else '新增商品')layout = QFormLayout(self)# Create input fieldsself.code = QLineEdit(self.product[0] if self.product else '')self.name = QLineEdit(self.product[1] if self.product else '')self.specification = QLineEdit(self.product[2] if self.product else '')self.unit = QLineEdit(self.product[3] if self.product else '')self.category = QLineEdit(self.product[4] if self.product else '')self.cost_price = QDoubleSpinBox()self.cost_price.setMaximum(999999.99)self.cost_price.setDecimals(2)if self.product:self.cost_price.setValue(float(self.product[5]))self.selling_price = QDoubleSpinBox()self.selling_price.setMaximum(999999.99)self.selling_price.setDecimals(2)if self.product:self.selling_price.setValue(float(self.product[6]))self.stock_quantity = QSpinBox()self.stock_quantity.setMaximum(999999)if self.product:self.stock_quantity.setValue(int(self.product[7]))self.warning_quantity = QSpinBox()self.warning_quantity.setMaximum(999999)if self.product:self.warning_quantity.setValue(int(self.product[8]))# Add fields to layoutlayout.addRow('商品编码:', self.code)layout.addRow('商品名称:', self.name)layout.addRow('规格型号:', self.specification)layout.addRow('单位:', self.unit)layout.addRow('分类:', self.category)layout.addRow('成本价:', self.cost_price)layout.addRow('销售价:', self.selling_price)layout.addRow('库存数量:', self.stock_quantity)layout.addRow('库存预警值:', self.warning_quantity)# Add buttonsbuttons_layout = QHBoxLayout()save_btn = QPushButton('保存')save_btn.clicked.connect(self.accept)cancel_btn = QPushButton('取消')cancel_btn.clicked.connect(self.reject)buttons_layout.addWidget(save_btn)buttons_layout.addWidget(cancel_btn)layout.addRow(buttons_layout)def get_data(self):return [self.code.text(),self.name.text(),self.specification.text(),self.unit.text(),self.category.text(),str(self.cost_price.value()),str(self.selling_price.value()),str(self.stock_quantity.value()),str(self.warning_quantity.value())]class ProductsTab(QWidget):def __init__(self, db):super().__init__()self.db = dbself.init_ui()self.load_products()self.editing = Falsedef init_ui(self):layout = QVBoxLayout(self)# Create top toolbartoolbar = QHBoxLayout()# Add product buttonself.add_btn = QPushButton('添加商品')self.add_btn.clicked.connect(self.add_product)toolbar.addWidget(self.add_btn)# Edit product buttonself.edit_btn = QPushButton('编辑商品')self.edit_btn.clicked.connect(self.edit_product)toolbar.addWidget(self.edit_btn)# Delete product buttonself.delete_btn = QPushButton('删除商品')self.delete_btn.clicked.connect(self.delete_product)toolbar.addWidget(self.delete_btn)# Export buttonself.export_btn = QPushButton('导出Excel')self.export_btn.clicked.connect(self.export_to_excel)toolbar.addWidget(self.export_btn)# Import buttonself.import_btn = QPushButton('导入Excel')self.import_btn.clicked.connect(self.import_from_excel)toolbar.addWidget(self.import_btn)# Search boxself.search_input = QLineEdit()self.search_input.setPlaceholderText('搜索商品...')self.search_input.textChanged.connect(self.search_products)toolbar.addWidget(self.search_input)toolbar.addStretch()layout.addLayout(toolbar)# Create tableself.table = QTableWidget()self.table.setColumnCount(9)self.table.setHorizontalHeaderLabels(['商品编码', '商品名称', '规格型号', '单位', '分类','成本价', '销售价', '库存数量', '库存预警值'])header = self.table.horizontalHeader()header.setSectionResizeMode(QHeaderView.Stretch)# Enable selection of entire rowsself.table.setSelectionBehavior(QTableWidget.SelectRows)self.table.setSelectionMode(QTableWidget.SingleSelection)layout.addWidget(self.table)def export_to_excel(self):try:# Create a new workbook and select the active sheetwb = openpyxl.Workbook()ws = wb.activews.title = "商品列表"# Write headersheaders = ['商品编码', '商品名称', '规格型号', '单位', '分类','成本价', '销售价', '库存数量', '库存预警值']for col, header in enumerate(headers, 1):ws.cell(row=1, column=col, value=header)# Write datafor row in range(self.table.rowCount()):for col in range(self.table.columnCount()):item = self.table.item(row, col)value = item.text() if item else ''ws.cell(row=row+2, column=col+1, value=value)# Get save file namefile_name = f"商品列表_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"file_path, _ = QFileDialog.getSaveFileName(self, "导出Excel", file_name, "Excel Files (*.xlsx)")if file_path:wb.save(file_path)QMessageBox.information(self, "成功", "导出成功!")except Exception as e:QMessageBox.warning(self, "错误", f"导出失败: {str(e)}")def import_from_excel(self):try:# Get file namefile_path, _ = QFileDialog.getOpenFileName(self, "选择Excel文件", "", "Excel Files (*.xlsx)")if not file_path:return# Load workbookwb = openpyxl.load_workbook(file_path)ws = wb.active# Get all rowsrows = list(ws.rows)if len(rows) < 2:  # Check if file has data (header + at least one row)QMessageBox.warning(self, "错误", "Excel文件为空或格式不正确")return# Verify headersexpected_headers = ['商品编码', '商品名称', '规格型号', '单位', '分类','成本价', '销售价', '库存数量', '库存预警值']headers = [cell.value for cell in rows[0]]if headers != expected_headers:QMessageBox.warning(self, "错误", "Excel文件格式不正确,请使用导出的模板")return# Begin transactionconn = self.db.connect()cursor = conn.cursor()try:# Process each rowfor row in rows[1:]:  # Skip header rowvalues = [cell.value for cell in row]# Convert numeric valuesvalues[5] = float(values[5]) if values[5] else 0  # cost_pricevalues[6] = float(values[6]) if values[6] else 0  # selling_pricevalues[7] = int(values[7]) if values[7] else 0    # stock_quantityvalues[8] = int(values[8]) if values[8] else 0    # warning_quantity# Insert or update productquery = '''INSERT OR REPLACE INTO products (code, name, specification, unit, category,cost_price, selling_price, stock_quantity, warning_quantity)VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)'''cursor.execute(query, values)conn.commit()QMessageBox.information(self, "成功", "导入成功!")self.load_products()  # Refresh tableexcept Exception as e:conn.rollback()raise eexcept Exception as e:QMessageBox.warning(self, "错误", f"导入失败: {str(e)}")def load_products(self):self.table.setRowCount(0)query = '''SELECT code, name, specification, unit, category,cost_price, selling_price, stock_quantity, warning_quantityFROM products'''products = self.db.fetch_query(query)for row, product in enumerate(products):self.table.insertRow(row)for col, value in enumerate(product):item = QTableWidgetItem(str(value) if value is not None else '')self.table.setItem(row, col, item)def add_product(self):dialog = ProductEditDialog(parent=self)if dialog.exec_():values = dialog.get_data()try:# Check if product code existscursor = self.db.connect().cursor()cursor.execute("SELECT code FROM products WHERE code = ?", (values[0],))if cursor.fetchone():QMessageBox.warning(self, "错误", f"商品编码 '{values[0]}' 已存在")return# Insert new productquery = '''INSERT INTO products (code, name, specification, unit, category,cost_price, selling_price, stock_quantity, warning_quantity)VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)'''self.db.execute_query(query, tuple(values))self.load_products()except Exception as e:QMessageBox.warning(self, '错误', f'保存失败: {str(e)}')def edit_product(self):current_row = self.table.currentRow()if current_row < 0:QMessageBox.warning(self, "提示", "请先选择要编辑的商品")return# Get current product datavalues = []for col in range(self.table.columnCount()):item = self.table.item(current_row, col)values.append(item.text() if item else '')dialog = ProductEditDialog(values, self)if dialog.exec_():new_values = dialog.get_data()try:# Check if new code exists (if code was changed)if new_values[0] != values[0]:cursor = self.db.connect().cursor()cursor.execute("SELECT code FROM products WHERE code = ?", (new_values[0],))if cursor.fetchone():QMessageBox.warning(self, "错误", f"商品编码 '{new_values[0]}' 已存在")return# Update productquery = '''UPDATE products SET code = ?, name = ?, specification = ?, unit = ?, category = ?, cost_price = ?, selling_price = ?,stock_quantity = ?, warning_quantity = ?WHERE code = ?'''self.db.execute_query(query, tuple(new_values + [values[0]]))self.load_products()except Exception as e:QMessageBox.warning(self, '错误', f'保存失败: {str(e)}')def delete_product(self):current_row = self.table.currentRow()if current_row < 0:QMessageBox.warning(self, "提示", "请先选择要删除的商品")returnproduct_code = self.table.item(current_row, 0).text()product_name = self.table.item(current_row, 1).text()reply = QMessageBox.question(self, '确认删除',f'确定要删除商品 "{product_name}" 吗?',QMessageBox.Yes | QMessageBox.No)if reply == QMessageBox.Yes:try:# Check if product is referenced in other tablescursor = self.db.connect().cursor()# Check purchase orderscursor.execute("""SELECT COUNT(*) FROM purchase_order_details podJOIN products p ON pod.product_id = p.idWHERE p.code = ?""", (product_code,))if cursor.fetchone()[0] > 0:QMessageBox.warning(self, "错误", "该商品已存在采购记录,无法删除")return# Check sales orderscursor.execute("""SELECT COUNT(*) FROM sales_order_details sodJOIN products p ON sod.product_id = p.idWHERE p.code = ?""", (product_code,))if cursor.fetchone()[0] > 0:QMessageBox.warning(self, "错误", "该商品已存在销售记录,无法删除")return# Check inventory recordscursor.execute("""SELECT COUNT(*) FROM inventory_records irJOIN products p ON ir.product_id = p.idWHERE p.code = ?""", (product_code,))if cursor.fetchone()[0] > 0:QMessageBox.warning(self, "错误", "该商品已存在库存记录,无法删除")return# Delete the productself.db.execute_query("DELETE FROM products WHERE code = ?", (product_code,))self.load_products()QMessageBox.information(self, "成功", "商品已删除")except Exception as e:QMessageBox.warning(self, '错误', f'删除失败: {str(e)}')def search_products(self):search_text = self.search_input.text().lower()for row in range(self.table.rowCount()):match = Falsefor col in range(self.table.columnCount()):item = self.table.item(row, col)if item and search_text in item.text().lower():match = Truebreakself.table.setRowHidden(row, not match)def on_item_changed(self, item):row = item.row()try:# Get all values from the rowvalues = []for col in range(self.table.columnCount()):cell_item = self.table.item(row, col)values.append(cell_item.text() if cell_item else '')# Update or insert into databasequery = '''INSERT OR REPLACE INTO products (code, name, specification, unit, category,cost_price, selling_price, stock_quantity, warning_quantity)VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)'''# Convert numeric valuesvalues[5] = float(values[5]) if values[5] else 0  # cost_pricevalues[6] = float(values[6]) if values[6] else 0  # selling_pricevalues[7] = int(values[7]) if values[7] else 0    # stock_quantityvalues[8] = int(values[8]) if values[8] else 0    # warning_quantityself.db.execute_query(query, tuple(values))except Exception as e:QMessageBox.warning(self, '错误', f'保存失败: {str(e)}')self.load_products()  # Reload to revert changes 

 

供应商管理 suppliers.py

from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QPushButton,QLabel, QLineEdit, QTableWidget, QTableWidgetItem,QMessageBox, QHeaderView, QFileDialog, QDialog,QFormLayout)
from PySide6.QtCore import Qt
import openpyxl
from datetime import datetimeclass SupplierEditDialog(QDialog):def __init__(self, supplier=None, parent=None):super().__init__(parent)self.supplier = supplierself.init_ui()def init_ui(self):self.setWindowTitle('编辑供应商' if self.supplier else '新增供应商')layout = QFormLayout(self)# Create input fieldsself.name = QLineEdit(self.supplier[0] if self.supplier else '')self.contact_person = QLineEdit(self.supplier[1] if self.supplier else '')self.phone = QLineEdit(self.supplier[2] if self.supplier else '')self.address = QLineEdit(self.supplier[3] if self.supplier else '')self.payment_terms = QLineEdit(self.supplier[4] if self.supplier else '')# Add fields to layoutlayout.addRow('供应商名称:', self.name)layout.addRow('联系人:', self.contact_person)layout.addRow('联系电话:', self.phone)layout.addRow('地址:', self.address)layout.addRow('账期:', self.payment_terms)# Add buttonsbuttons_layout = QHBoxLayout()save_btn = QPushButton('保存')save_btn.clicked.connect(self.accept)cancel_btn = QPushButton('取消')cancel_btn.clicked.connect(self.reject)buttons_layout.addWidget(save_btn)buttons_layout.addWidget(cancel_btn)layout.addRow(buttons_layout)def get_data(self):return [self.name.text(),self.contact_person.text(),self.phone.text(),self.address.text(),self.payment_terms.text()]class SuppliersTab(QWidget):def __init__(self, db):super().__init__()self.db = dbself.init_ui()self.load_suppliers()self.editing = Falsedef init_ui(self):layout = QVBoxLayout(self)# Create toolbartoolbar = QHBoxLayout()# Add supplier buttonself.add_btn = QPushButton('新建供应商')self.add_btn.clicked.connect(self.add_supplier)toolbar.addWidget(self.add_btn)# Edit supplier buttonself.edit_btn = QPushButton('编辑供应商')self.edit_btn.clicked.connect(self.edit_supplier)toolbar.addWidget(self.edit_btn)# Delete supplier buttonself.delete_btn = QPushButton('删除供应商')self.delete_btn.clicked.connect(self.delete_supplier)toolbar.addWidget(self.delete_btn)# Import/Export buttonsself.import_btn = QPushButton('导入')self.import_btn.clicked.connect(self.import_from_excel)toolbar.addWidget(self.import_btn)self.export_btn = QPushButton('导出')self.export_btn.clicked.connect(self.export_to_excel)toolbar.addWidget(self.export_btn)# Search boxself.search_input = QLineEdit()self.search_input.setPlaceholderText('搜索供应商...')self.search_input.textChanged.connect(self.search_suppliers)toolbar.addWidget(self.search_input)toolbar.addStretch()layout.addLayout(toolbar)# Create tableself.table = QTableWidget()self.table.setColumnCount(5)self.table.setHorizontalHeaderLabels(['供应商名称', '联系人', '联系电话', '地址', '账期'])header = self.table.horizontalHeader()header.setSectionResizeMode(QHeaderView.Stretch)# Enable selection of entire rowsself.table.setSelectionBehavior(QTableWidget.SelectRows)self.table.setSelectionMode(QTableWidget.SingleSelection)layout.addWidget(self.table)def add_supplier(self):dialog = SupplierEditDialog(parent=self)if dialog.exec_():values = dialog.get_data()try:# Check if supplier name existscursor = self.db.connect().cursor()cursor.execute("SELECT name FROM suppliers WHERE name = ?", (values[0],))if cursor.fetchone():QMessageBox.warning(self, "错误", f"供应商 '{values[0]}' 已存在")return# Insert new supplierquery = '''INSERT INTO suppliers (name, contact_person, phone, address, payment_terms)VALUES (?, ?, ?, ?, ?)'''self.db.execute_query(query, tuple(values))self.load_suppliers()except Exception as e:QMessageBox.warning(self, '错误', f'保存失败: {str(e)}')def edit_supplier(self):current_row = self.table.currentRow()if current_row < 0:QMessageBox.warning(self, "提示", "请先选择要编辑的供应商")return# Get current supplier datavalues = []for col in range(self.table.columnCount()):item = self.table.item(current_row, col)values.append(item.text() if item else '')dialog = SupplierEditDialog(values, self)if dialog.exec_():new_values = dialog.get_data()try:# Check if new name exists (if name was changed)if new_values[0] != values[0]:cursor = self.db.connect().cursor()cursor.execute("SELECT name FROM suppliers WHERE name = ?", (new_values[0],))if cursor.fetchone():QMessageBox.warning(self, "错误", f"供应商 '{new_values[0]}' 已存在")return# Update supplierquery = '''UPDATE suppliers SET name = ?, contact_person = ?, phone = ?, address = ?, payment_terms = ?WHERE name = ?'''self.db.execute_query(query, tuple(new_values + [values[0]]))self.load_suppliers()except Exception as e:QMessageBox.warning(self, '错误', f'保存失败: {str(e)}')def delete_supplier(self):current_row = self.table.currentRow()if current_row < 0:QMessageBox.warning(self, "提示", "请先选择要删除的供应商")returnsupplier_name = self.table.item(current_row, 0).text()reply = QMessageBox.question(self, '确认删除',f'确定要删除供应商 "{supplier_name}" 吗?',QMessageBox.Yes | QMessageBox.No)if reply == QMessageBox.Yes:try:# Check if supplier is referenced in purchase orderscursor = self.db.connect().cursor()cursor.execute("""SELECT COUNT(*) FROM purchase_orders poJOIN suppliers s ON po.supplier_id = s.idWHERE s.name = ?""", (supplier_name,))if cursor.fetchone()[0] > 0:QMessageBox.warning(self, "错误", "该供应商已有采购记录,无法删除")return# Delete the supplierself.db.execute_query("DELETE FROM suppliers WHERE name = ?", (supplier_name,))self.load_suppliers()QMessageBox.information(self, "成功", "供应商已删除")except Exception as e:QMessageBox.warning(self, '错误', f'删除失败: {str(e)}')def export_to_excel(self):try:# Create a new workbook and select the active sheetwb = openpyxl.Workbook()ws = wb.activews.title = "供应商列表"# Write headersheaders = ['供应商名称', '联系人', '联系电话', '地址', '账期']for col, header in enumerate(headers, 1):ws.cell(row=1, column=col, value=header)# Write datafor row in range(self.table.rowCount()):for col in range(self.table.columnCount()):item = self.table.item(row, col)value = item.text() if item else ''ws.cell(row=row+2, column=col+1, value=value)# Get save file namefile_name = f"供应商列表_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"file_path, _ = QFileDialog.getSaveFileName(self, "导出Excel", file_name, "Excel Files (*.xlsx)")if file_path:wb.save(file_path)QMessageBox.information(self, "成功", "导出成功!")except Exception as e:QMessageBox.warning(self, "错误", f"导出失败: {str(e)}")def import_from_excel(self):try:# Get file namefile_path, _ = QFileDialog.getOpenFileName(self, "选择Excel文件", "", "Excel Files (*.xlsx)")if not file_path:return# Load workbookwb = openpyxl.load_workbook(file_path)ws = wb.active# Get all rowsrows = list(ws.rows)if len(rows) < 2:  # Check if file has data (header + at least one row)QMessageBox.warning(self, "错误", "Excel文件为空或格式不正确")return# Verify headersexpected_headers = ['供应商名称', '联系人', '联系电话', '地址', '账期']headers = [cell.value for cell in rows[0]]if headers != expected_headers:QMessageBox.warning(self, "错误", "Excel文件格式不正确,请使用导出的模板")return# Begin transactionconn = self.db.connect()cursor = conn.cursor()try:# Process each rowduplicates = []new_suppliers = []for row in rows[1:]:  # Skip header rowvalues = [cell.value if cell.value is not None else '' for cell in row]supplier_name = values[0]# Check if supplier existscursor.execute("SELECT name FROM suppliers WHERE name = ?", (supplier_name,))if cursor.fetchone():duplicates.append(values)else:new_suppliers.append(values)# Handle duplicatesif duplicates:duplicate_names = "\n".join(d[0] for d in duplicates)reply = QMessageBox.question(self, "发现重复供应商",f"以下供应商已存在:\n{duplicate_names}\n\n是否更新这些供应商的信息?",QMessageBox.Yes | QMessageBox.No)if reply == QMessageBox.Yes:# Update existing suppliersfor values in duplicates:query = '''UPDATE suppliers SET contact_person = ?, phone = ?, address = ?, payment_terms = ?WHERE name = ?'''cursor.execute(query, (values[1], values[2], values[3], values[4], values[0]))# Insert new suppliersfor values in new_suppliers:query = '''INSERT INTO suppliers (name, contact_person, phone, address, payment_terms)VALUES (?, ?, ?, ?, ?)'''cursor.execute(query, values)conn.commit()# Show summarymsg = f"导入完成!\n新增供应商:{len(new_suppliers)}个"if duplicates:if reply == QMessageBox.Yes:msg += f"\n更新供应商:{len(duplicates)}个"else:msg += f"\n跳过重复供应商:{len(duplicates)}个"QMessageBox.information(self, "成功", msg)self.load_suppliers()  # Refresh tableexcept Exception as e:conn.rollback()raise eexcept Exception as e:QMessageBox.warning(self, "错误", f"导入失败: {str(e)}")def load_suppliers(self):self.editing = True  # Prevent triggering item change eventsself.table.setRowCount(0)query = '''SELECT name, contact_person, phone, address, payment_termsFROM suppliers'''suppliers = self.db.fetch_query(query)for row, supplier in enumerate(suppliers):self.table.insertRow(row)for col, value in enumerate(supplier):item = QTableWidgetItem(str(value) if value is not None else '')self.table.setItem(row, col, item)self.editing = False  # Re-enable item change eventsdef search_suppliers(self):search_text = self.search_input.text().lower()for row in range(self.table.rowCount()):# Only search in the supplier name column (column 0)item = self.table.item(row, 0)  # Get supplier name cellmatch = item and search_text in item.text().lower()self.table.setRowHidden(row, not match) 

客户管理 customers.py

from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QPushButton,QLabel, QLineEdit, QTableWidget, QTableWidgetItem,QMessageBox, QHeaderView, QFileDialog, QDialog,QFormLayout)
from PySide6.QtCore import Qt
import openpyxl
from datetime import datetimeclass SupplierEditDialog(QDialog):def __init__(self, supplier=None, parent=None):super().__init__(parent)self.supplier = supplierself.init_ui()def init_ui(self):self.setWindowTitle('编辑供应商' if self.supplier else '新增供应商')layout = QFormLayout(self)# Create input fieldsself.name = QLineEdit(self.supplier[0] if self.supplier else '')self.contact_person = QLineEdit(self.supplier[1] if self.supplier else '')self.phone = QLineEdit(self.supplier[2] if self.supplier else '')self.address = QLineEdit(self.supplier[3] if self.supplier else '')self.payment_terms = QLineEdit(self.supplier[4] if self.supplier else '')# Add fields to layoutlayout.addRow('供应商名称:', self.name)layout.addRow('联系人:', self.contact_person)layout.addRow('联系电话:', self.phone)layout.addRow('地址:', self.address)layout.addRow('账期:', self.payment_terms)# Add buttonsbuttons_layout = QHBoxLayout()save_btn = QPushButton('保存')save_btn.clicked.connect(self.accept)cancel_btn = QPushButton('取消')cancel_btn.clicked.connect(self.reject)buttons_layout.addWidget(save_btn)buttons_layout.addWidget(cancel_btn)layout.addRow(buttons_layout)def get_data(self):return [self.name.text(),self.contact_person.text(),self.phone.text(),self.address.text(),self.payment_terms.text()]class SuppliersTab(QWidget):def __init__(self, db):super().__init__()self.db = dbself.init_ui()self.load_suppliers()self.editing = Falsedef init_ui(self):layout = QVBoxLayout(self)# Create toolbartoolbar = QHBoxLayout()# Add supplier buttonself.add_btn = QPushButton('新建供应商')self.add_btn.clicked.connect(self.add_supplier)toolbar.addWidget(self.add_btn)# Edit supplier buttonself.edit_btn = QPushButton('编辑供应商')self.edit_btn.clicked.connect(self.edit_supplier)toolbar.addWidget(self.edit_btn)# Delete supplier buttonself.delete_btn = QPushButton('删除供应商')self.delete_btn.clicked.connect(self.delete_supplier)toolbar.addWidget(self.delete_btn)# Import/Export buttonsself.import_btn = QPushButton('导入')self.import_btn.clicked.connect(self.import_from_excel)toolbar.addWidget(self.import_btn)self.export_btn = QPushButton('导出')self.export_btn.clicked.connect(self.export_to_excel)

相关文章:

Python Pyside6 加Sqlite3 写一个 通用 进销存 系统 初型

图: 说明: 进销存管理系统说明文档 功能模块 1. 首页 显示关键业务数据商品总数供应商总数本月采购金额本月销售金额显示预警信息库存不足预警待付款采购单待收款销售单2. 商品管理 商品信息维护商品编码(唯一标识)商品名称规格型号单位分类进货价销售价库存数量预警…...

Java工程结构:服务器规约(JVM 碰到 OOM 场景时输出 dump 信息、设置tomcat的 JVM 的内存参数、了解服务平均耗时)

文章目录 I 调用远程操作必须有超时设置。II 推荐了解每个服务大致的平均耗时JVM 的 Xms 和 Xmx 设置一样大小的内存容量让 JVM 碰到 OOM 场景时输出 dump 信息调大服务器所支持的最大文件句柄数(File Descriptor,简写为 fd)高并发服务器建议调小 TCP 协议的 time_wait 超时…...

Spring经典面试题

在Spring的面试中&#xff0c;经常会被问到一些经典的问题&#xff0c;这些问题涵盖了Spring的基本概念、核心特性、工作原理以及在实际项目中的应用。以下是一些Spring面试中最经典的题目&#xff1a; 一、Spring概述 什么是Spring框架&#xff1f;Spring框架有哪些主要模块&…...

以太网实战AD采集上传上位机——FPGA学习笔记27

一、设计目标 使用FPGA实现AD模块驱动采集模拟电压&#xff0c;通过以太网上传到电脑上位机。 二、框架设计 数据位宽转换模块&#xff08;ad_10bit_to_16bit&#xff09;&#xff1a;为了方便数据传输&#xff0c;数据位宽转换模块实现了将十位的 AD 数据转换成十六位&#…...

数据结构与算法整理复习(一):数据结构概念与线性表

目录 第一章&#xff1a;绪论 1.1 数据结构的基本概念 1.2 算法与算法评价 第二章&#xff1a;线性表 2.1 线性表的定义和基本操作 2.2 线性表的顺序表示&#xff08;顺序表&#xff09; 应用题 2.3 线性表的链式表达&#xff08;链表&#xff09; 2.3.1 单链表 2.3.2…...

虚幻商城 Fab 免费资产自动化入库

文章目录 一、背景二、实现效果展示三、实现自动化入库一、背景 上一次写了个这篇文章 虚幻商城 Quixel 免费资产一键入库,根据这个构想,便决定将范围扩大,使 Fab 商城的所有的免费资产自动化入库,是所有!所有! 上一篇文章是根据下图这部分资产一键入库: 而这篇文章则…...

TCP Window Full是怎么来的

wireshark查看包时&#xff0c;会看到TCP Window Full&#xff0c;总结下它的特点&#xff1a; 1. Sender会显示 TCP Window Full 2. “Sender已发出&#xff0c;但&#xff0c;Receiver尚未ack的字节”&#xff0c;即Sender的 bytes in flights 3. Sender的 bytes in fligh…...

高效建站指南:通过Portainer快速搭建自己的在线网站

文章目录 前言1. 安装Portainer1.1 访问Portainer Web界面 2. 使用Portainer创建Nginx容器3. 将Web静态站点实现公网访问4. 配置Web站点公网访问地址4.1公网访问Web站点 5. 固定Web静态站点公网地址6. 固定公网地址访问Web静态站点 前言 Portainer是一个开源的Docker轻量级可视…...

“UniApp的音频播放——点击视频进入空白+解决视频播放器切换视频时一直加载的问题”——video.js、video-js.css

今天&#xff0c;又解决了一个单子“UniApp的音频播放——点击视频进入空白解决视频播放器切换视频时一直加载的问题” 一、问题描述 在开发一个基于 video.js 的视频播放器时&#xff0c;用户通过上下滑动切换视频时&#xff0c;视频一直处于加载状态&#xff0c;无法正常播放…...

如何让openhands始终输出中文?

在本地创建一个文件./user_prompt.j2 添加一行 Always respond in 中文你可以直接&#xff1a; echo "Always respond in 中文" > ./user_prompt.j2使用官方文档的docker命令启动容器时-v挂载一个文件/app/openhands/agenthub/codeact_agent/prompts/user_promp…...

CSS 溢出问题及解决方案:实用案例与技巧

在网页开发中&#xff0c;CSS 的布局和样式起着至关重要的作用&#xff0c;但经常会遇到一个棘手的问题——溢出问题。溢出是指元素内的内容超出了其设定的容器大小&#xff0c;这不仅会影响页面的美观&#xff0c;还可能干扰用户体验。本文将详细探讨 CSS 溢出问题的案例&…...

vue3使用音频audio标签

文章目录 一、背景二、页面三、标签介绍四、代码五、代码说明场景1&#xff1a;针对加载固定格式的比如MP3文件&#xff0c;可直接使用\<audio>标签场景2&#xff1a;针对播放告警内容&#xff0c;比如中文或者英文词条情况 一、背景 项目使用vue3&#xff0c;需求针对告…...

【useCallback Hook】在多次渲染中缓存组件中的函数,避免重复创建函数

文章目录 什么是 useCallback&#xff1f;基本语法 为什么需要 useCallback&#xff1f;示例1. 避免子组件重复创建函数2. 作为 useEffect 的依赖项 注意事项总结 在 React 开发中&#xff0c;性能优化是一个重要的主题。随着应用规模的增长&#xff0c;组件的重新渲染可能会变…...

Vue2+OpenLayers添加缩放、滑块缩放、拾取坐标、鹰眼、全屏控件(提供Gitee源码)

目录 一、案例截图 二、安装OpenLayers库 三、代码实现 四、Gitee源码 一、案例截图 二、安装OpenLayers库 npm install ol 三、代码实现 废话不多说&#xff0c;直接给完整代码&#xff0c;替换成自己的KEY即可运行&#xff1a; <template><div><div i…...

feign调用跳过HTTPS的SSL证书校验配置详解

一、问题抛出 如果不配置跳过SSL证书校验&#xff0c;当Feign客户端尝试连接到一个使用自签名证书的服务器时&#xff0c;可能会抛出类似以下的异常&#xff1a; javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building faile…...

spring @EnableAspectJAutoProxy @Aspect的使用和源码流程

目录 测试代码EnableAspectJAutoProxyAspectJAutoProxyRegistrarAnnotationAwareAspectJAutoProxyCreatororg.springframework.context.support.AbstractApplicationContext#registerBeanPostProcessors 实例化AnnotationAwareAspectJAutoProxyCreator bean "a"的代理…...

项目实战--网页五子棋(游戏大厅)(3)

我们的游戏大厅界面主要需要包含两个功能&#xff0c;一是显示用户信息&#xff0c;二是匹配游戏按钮 1. 页面实现 hall.html <!DOCTYPE html> <html lang"ch"> <head><meta charset"UTF-8"><meta name"viewport"…...

『 实战项目 』Cloud Backup System - 云备份

文章目录 云备份项目服务端功能服务端功能模块划分客户端功能客户端模块划分 项目条件Jsoncpp第三方库Bundle第三方库httplib第三方库Request类Response类Server类Client类搭建简单服务器搭建简单客户端 服务端工具类实现 - 文件实用工具类服务器配置信息模块实现- 系统配置信息…...

【机器学习实战入门】使用OpenCV和Keras的驾驶员疲劳检测系统

嗜睡驾驶者警报系统 防止司机疲劳驾驶警报系统 中级 Python 项目 - 司机疲劳检测系统 疲劳检测是一种安全技术&#xff0c;能够预防因司机在驾驶过程中入睡而造成的事故。 本中级 Python 项目的目标是建立一个疲劳检测系统&#xff0c;该系统将检测到一个人的眼睛闭合了一段时…...

使用 spring boot 2.5.6 版本时缺少 jvm 配置项

2.5.6我正在使用带有版本和springfox-boot-starter版本的Spring Boot 项目3.0.0。我的项目还包括一个WebSecurityConfig扩展WebSecurityConfigurerAdapter并实现WebMvcConfigurer的类。但是&#xff0c;我面临的问题是指标在端点jvm_memory_usage_after_gc_percent中不可见/act…...

【强化学习】Soft Actor-Critic (SAC) 算法

&#x1f4e2;本篇文章是博主强化学习&#xff08;RL&#xff09;领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对相关等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅…...

2024年博客之星主题创作|Android 开发:前沿技术、跨领域融合与就业技能展望

目录 引言 一、推动 Android 应用创新的核心力量 1.1 人工智能与机器学习的崛起 1.2 增强现实&#xff08;AR&#xff09;与虚拟现实&#xff08;VR&#xff09;的应用扩展 1.3 5G技术的推动 1.4 跨平台开发技术的成熟 1.4.1 React Native 1.4.2 Flutter 1.4.3 Taro …...

Spring Boot--@PathVariable、@RequestParam、@RequestBody

目录 声明&#xff01;&#xff01; 什么是RESTful&#xff1f; RESTful 的基本原则 无状态性&#xff08;Stateless&#xff09; 统一接口&#xff08;Uniform Interface&#xff09; 分层系统&#xff08;Layered System&#xff09; 缓存&#xff08;Cacheable&#…...

网站HTTP改成HTTPS

您不仅需要知道如何将HTTP转换为HTTPS&#xff0c;还必须在不妨碍您的网站自成立以来建立的任何搜索排名权限的情况下进行切换。 为什么应该从HTTP转换为HTTPS&#xff1f; 与非安全HTTP于不同&#xff0c;安全域使用SSL&#xff08;安全套接字层&#xff09;服务器上的加密代…...

Spring Boot + Netty + WebSocket 实现消息推送

1、关于Netty Netty 是一个利用 Java 的高级网络的能力&#xff0c;隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架。 2、Maven依赖 <dependencies><!-- https://mvnrepository.com/artifact/io.netty/netty-all --><dependency><gr…...

AI之HardWare:英伟达NvidiaGPU性价比排名(消费级/专业级/中高端企业级)以及据传英伟达Nvidia2025年将推出RTX 5090/5080、华为2025年推出910C/910D

AI之HardWare&#xff1a;英伟达NvidiaGPU性价比排名(消费级/专业级/中高端企业级)以及据传英伟达Nvidia2025年将推出RTX 5090/5080、华为2025年推出910C/910D 目录 英伟达NvidiaGPU性能排名(消费级/专业级/中高端企业级) NVIDIA中消费级和专业级 GPU NVIDIA中高端企业GPU …...

ESP32云开发二( http + led + lcd)

文章目录 前言先上效果图platformio.iniwokwi.tomldiagram.json源代码编译编译成功上传云端完结撒花⭐⭐⭐⭐⭐ 前言 阅读此篇前建议先看 此片熟悉下wokwi https://blog.csdn.net/qq_20330595/article/details/144289986 先上效果图 Column 1Column 2 platformio.ini wokwi…...

JavaScript语言的软件工程

JavaScript语言的软件工程 引言 在当今软件开发的浪潮中&#xff0c;JavaScript已不仅仅是一个简单的前端脚本语言。它的位置已经升华为全栈开发的重要语言之一&#xff0c;借助Node.js等技术&#xff0c;JavaScript不仅可以用于浏览器环境&#xff0c;还可以在后端服务器中运…...

【Qt】04-Lambda表达式

前言一、概念引入二、使用方法2.1 基本用法代码示例2.2 捕获外部变量2.3 参数列表 三、完整代码mywidget.cppsecondwidget.cppmywidget.hsecondwidget.h 总结 前言 一、概念引入 Lambda表达式&#xff08;Lambda Expressions&#xff09;是C11标准引入的一种匿名函数对象&…...

Golang 生态学习

1. Go 语言基础 在深入 Go 语言的生态之前&#xff0c;首先需要掌握 Go 语言本身的核心特性。 • Go 语言官方文档&#xff1a;https://golang.org/doc/ Go 官方文档是学习语言基础和标准库的首选资源。 • 学习内容&#xff1a; • 基础语法&#xff1a;数据类型、控制流…...

Arcgis Pro安装完成后启动失败的解决办法

场景 之前安装的Arcgis Pro 今天突然不能使用了&#xff0c;之前是可以使用的&#xff0c;自从系统更新了以后就出现了这个问题。 环境描述 Arcgis Pro 3.0 Windows 10 问题描述 打开Arcgis Pro&#xff0c;页面也不弹出来&#xff0c;打开任务管理器可以看到进程创建之后&…...

支持向量机SVM的应用案例

支持向量机&#xff08;Support Vector Machine,SVM&#xff09;是一种强大的监督学习算法&#xff0c;广泛应用于分类和回归任务。 基本原理 SVM的主要目标是周到一个最优的超平面&#xff0c;该超平面能够将不同类别的数据点尽可能分开&#xff0c;并且使离该超平面最近的数…...

Linux中的Iptables介绍

文章目录 iptables1. 概述2. **工作原理**3. 数据包处理流程与规则匹配顺序4. 常用的匹配条件5. 动作类型6. 基本命令7. 高级功能 iptables 1. 概述 Iptables 是一个用于配置 Linux 内核防火墙的用户空间工具。它能够对进出服务器的网络数据包进行过滤、修改和转发等操作&…...

14天学习微服务-->第2天:Spring Cloud深入与实践

第2天&#xff1a;Spring Cloud深入与实践 一、Spring Cloud核心组件深入 在微服务架构中&#xff0c;Spring Cloud 提供了一系列核心组件来支持服务的注册与发现、配置管理、负载均衡等功能。今天我们将深入学习其中的三个关键组件&#xff1a;Eureka/Nacos&#xff08;服务…...

使用 Box2D 库开发愤怒的小鸟游戏

使用 Box2D 库开发愤怒的小鸟游戏 Box2D 是一个开源的 2D 物理引擎&#xff0c;广泛应用于游戏开发中&#xff0c;特别是在模拟物体的运动、碰撞、重力等方面。在本文中&#xff0c;我们将利用 Box2D 库开发一个简化版的 愤怒的小鸟 游戏。我们将一步步展示如何实现物理引擎的…...

C# ComboBox 控件属性

ComboBox 的基本属性 在C#中&#xff0c;ComboBox控件具有多种属性&#xff0c;这些属性可以帮助开发者更好地控制和管理控件的各个方面。以下是一些基本的ComboBox属性及其功能&#xff1a; 公共属性 AccessibilityObject&#xff1a;获取分配给该控件的AccessibleObject。 Ac…...

《keras 3 内卷神经网络》

keras 3 内卷神经网络 作者&#xff1a;Aritra Roy Gosthipaty 创建日期&#xff1a;2021/07/25 最后修改时间&#xff1a;2021/07/25 描述&#xff1a;深入研究特定于位置和通道无关的“内卷”内核。 &#xff08;i&#xff09; 此示例使用 Keras 3 在 Colab 中查看 GitHub …...

Linux:文件描述符fd、系统调用open

目录 一、文件基础认识 二、C语言操作文件的接口 1.> 和 >> 2.理解“当前路径” 三、相关系统调用 1.open 2.文件描述符 3.一切皆文件 4.再次理解重定向 一、文件基础认识 文件 内容 属性。换句话说&#xff0c;如果在电脑上新建了一个空白文档&#xff0…...

ToDesk设置临时密码和安全密码都可以当做连接密码使用

ToDesk 在各领域办公都已经是非常常见了 为了安全 ToDesk 设置了连接密码&#xff0c;想连接 需要输入远程码和连接密码 我们刚打开 系统默认给我们用的是临时密码&#xff0c;安全性确实很强 和定时Tokey一样&#xff0c;固定时间切换。 但是 如果我们要经常连接这个电脑&a…...

C#防止重复提交

C#防止重复提交 文章目录 C#防止重复提交前言防止重复提交的思路Web API 防止重复提交代码实现代码讲解使用方法 MVC防止重复提交总结 前言 当用户在前端进行提交数据时&#xff0c;如果网络出现卡顿和前端没有给出响应的话顾客通常都会狂点提交按钮&#xff0c;这样就很容易导…...

递归算法学习v2.2

46. 全排列 class Solution {List<List<Integer>> ret;List<Integer> path;boolean[] check;public List<List<Integer>> permute(int[] nums) {ret new ArrayList<>();path new ArrayList<>();check new boolean[nums.length…...

unity插件Excel转换Proto插件-ExcelToProtobufferTool

unity插件Excel转换Proto插件-ExcelToProtobufferTool **ExcelToProtobufTool 插件文档****1. 插件概述****2. 默认配置类&#xff1a;DefaultIProtoPathConfig****属性说明** **3. 自定义配置类****定义规则****示例代码** **4. 使用方式****4.1 默认路径****4.2 自定义路径**…...

manim(manimgl)安装教学-win11(2024-08)

manim 目前的两种版本&#xff1a;★★ 稍微捋一捋【项目中的 readme.md 十分重要】 manimgl 是 Grant Sanderson&#xff08;YouTube频道 3Blue1Brown的作者&#xff09;等人开发。 现在为 manimgl&#xff0c;在维护中。 manimCE 是2020年后的 manim 分支 manim community e…...

【语言处理和机器学习】概述篇(基础小白入门篇)

前言 自学笔记&#xff0c;分享给语言学/语言教育学方向的&#xff0c;但对语言数据处理感兴趣但是尚未入门&#xff0c;却需要在论文中用到的小伙伴&#xff0c;欢迎大佬们补充或绕道。ps&#xff1a;本文不涉及公式讲解&#xff08;文科生小白友好体质&#xff09;&#xff…...

脚本工具:PYTHON

Python 是一种高级编程语言&#xff0c;以其简洁清晰的语法和强大的功能被广泛应用于各种领域&#xff0c;包括自动化脚本编写、数据分析、机器学习、Web开发等。以下是一些关于使用 Python 编写脚本工具的基本介绍、常用库以及一些实用技巧总结。 这里写目录标题 基础知识安装…...

一文讲解Redis常见使用方式

1. 单机模式部署 适用场景&#xff1a; • 开发和测试环境&#xff0c;或者对高可用性要求不高的小型项目。 部署步骤&#xff1a; 1. 拉取 Redis 镜像&#xff1a; docker pull redis:latest 2. 运行 Redis 容器&#xff1a; docker run -d --name redis-single -p 637…...

Gin 源码概览 - 路由

本文基于gin 1.1 源码解读 https://github.com/gin-gonic/gin/archive/refs/tags/v1.1.zip 1. 注册路由 我们先来看一段gin代码&#xff0c;来看看最终得到的一颗路由树长啥样 func TestGinDocExp(t *testing.T) {engine : gin.Default()engine.GET("/api/user", f…...

【计算机网络】传输层协议TCP与UDP

传输层 传输层位于OSI七层网络模型的第四层&#xff0c;主要负责端到端通信&#xff0c;可靠性保障&#xff08;TCP&#xff09;&#xff0c;流量控制(TCP)&#xff0c;拥塞控制(TCP)&#xff0c;数据分段与分组&#xff0c;多路复用与解复用等&#xff0c;通过TCP与UDP协议实现…...

iOS UIScrollView的一个特性

1如果UIScrollView 的contentSize.height > scrollView.bounds.size.height - scrollView.contentInset.top - scrollView.contentInset.bottom &#xff0c; 则scrollView就可以滚动&#xff0c;否则无法滚动 并且最大的滚动范围就是 contentSize.height - &#xff08; s…...

Docker 实现MySQL 主从复制

一、拉取镜像 docker pull mysql:5.7相关命令&#xff1a; 查看镜像&#xff1a;docker images 二、启动镜像 启动mysql01、02容器&#xff1a; docker run -d -p 3310:3306 -v /root/mysql/node-1/config:/etc/mysql/ -v /root/mysql/node-1/data:/var/lib/mysql -e MYS…...