pyqt 简单条码系统

生产数据管理系统说明

在这里插入图片描述

系统概述

这是一个基于PyQt5和pyodbc开发的生产数据管理系统,主要用于管理生产过程中的物料绑定和查询操作。系统提供了上料绑定和下料查询功能,支持与SQL Server数据库交互,实现数据的插入、查询、更新和删除操作。界面采用标签页设计,操作流程清晰,反馈明确,适合生产环境使用。

主要功能模块

系统包含以下核心模块:

  1. 数据库管理模块(DatabaseManager)

    • 负责与SQL Server数据库建立连接和交互
    • 实现数据插入、查询、更新和删除操作
    • 提供表和列的存在性检查功能
    • 记录数据库操作日志
  2. 上料绑定模块(MaterialBindingWidget)

    • 常温上料绑定
    • 高温上料插管座绑定
    • 高温上料拔管座绑定
    • 支持扫码信息和批次信息录入
    • 数据查询和结果展示
    • 右键删除记录功能
  3. 下料查询模块(QueryWidget)

    • 扫码查询下料记录
    • 结果以表格形式展示
    • 按时间由近到远排序
    • 无结果时显示友好提示
  4. 日志管理模块(Logger)

    • 全局日志记录
    • 带时间戳的日志消息
    • 日志自动清理功能
    • 日志显示在主界面底部
界面设计特点
  1. 标签页布局

    • 四个主要功能标签页:常温上料绑定、高温上料插管座绑定、高温上料拔管座绑定、下料查询
    • 界面整洁,功能分区明确
  2. 按钮设计

    • 绿色提交按钮(#4CAF50)表示正向操作
    • 黄色按钮表示中间状态
    • 红色按钮表示错误状态
    • 按钮悬停和按下状态有视觉反馈
  3. 结果展示

    • 表格形式展示查询结果
    • 列宽自动适应内容
    • 支持右键删除记录
  4. 操作反馈

    • 提交操作后显示明确的状态提示
    • 操作过程中按钮状态变化
    • 日志区域实时记录操作信息
数据库操作流程
  1. 数据插入流程

    • 先更新原有数据状态
    • 再插入新数据
    • 提交事务
    • 记录操作日志
  2. 数据查询流程

    • 构建查询条件
    • 执行SQL查询
    • 按时间降序排列结果
    • 填充到表格中展示
  3. 数据删除流程

    • 右键选择记录
    • 确认删除操作
    • 执行删除SQL
    • 提交事务
    • 重新查询刷新结果
颜色说明
  1. 绿色(#4CAF50)

    • 用于表示成功状态
    • 提交按钮背景色
    • 成功提示文字颜色
  2. 黄色(#FFC107)

    • 用于表示执行中状态
    • 中间状态按钮背景色
  3. 红色(#F44336)

    • 用于表示错误状态
    • 失败提示文字和按钮颜色
  4. 绿色(#008000)

    • 用于标题文字
    • 表示正常状态
特殊功能说明
  1. 状态提示按钮

    • 提交操作时按钮状态变化:初始 -> 执行中 -> 成功/失败
    • 视觉反馈清晰,操作状态一目了然
  2. 自动清空日志

    • 当日志行数超过10行且滚动条在底部时自动清空
    • 保留最新的日志记录
  3. 扫码规则检查

    • 自动检查扫码信息长度
    • 长度小于10时提示不符合规则
  4. 日期范围查询

    • 默认为最近7天的数据
    • 支持自定义日期范围查询
使用说明
  1. 数据库配置

    • 在MainWindow类中修改db_config字典中的服务器地址、数据库名称、用户名和密码
  2. 上料绑定

    • 输入扫码信息和批次信息
    • 点击"提交"按钮绑定数据
    • 按钮状态显示操作结果
    • 可在查询区域查询历史记录
  3. 下料查询

    • 输入扫码信息
    • 点击"查询"按钮
    • 结果显示在表格中,按时间由近到远排列
  4. 日志查看

    • 主界面底部显示系统操作日志
    • 包含时间戳和操作详情
技术要点
  1. 数据库连接管理

    • 使用pyodbc连接SQL Server
    • 自动重连机制
    • 事务管理确保数据一致性
  2. 界面交互

    • 使用信号与槽机制
    • 表格组件的上下文菜单
    • 输入框回车提交功能
  3. 代码结构

    • 面向对象设计,模块职责明确
    • 日志系统独立封装
    • 界面组件继承复用

这个生产数据管理系统适合在生产环境中使用,提供了完整的物料绑定和查询功能,界面友好,操作简单,同时具备完善的日志记录和错误处理机制。

import sys
import traceback
import pyodbc
from PyQt5.QtWidgets import (QApplication, QMainWindow, QTabWidget, QWidget, QVBoxLayout,QHBoxLayout, QLabel, QLineEdit, QPushButton, QTextEdit,QMessageBox, QGroupBox, QTableWidget, QTableWidgetItem,QHeaderView, QMenu, QAction, QInputDialog, QDateEdit)
from PyQt5.QtCore import Qt, pyqtSignal, QObject, QDate, QDateTime
from PyQt5.QtGui import QFont, QColor, QPalette
import datetimeclass Logger(QObject):"""全局日志管理器,通过信号传递日志消息"""log_message = pyqtSignal(str)def log(self, message):"""发送带时间戳的日志消息到信号"""timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]  # 生成时间戳,精确到毫秒timestamped_message = f"[{timestamp}] {message}"self.log_message.emit(timestamped_message)class LogSignal(QObject):"""数据库操作日志信号类"""log_message = pyqtSignal(str)class DatabaseManager:"""数据库管理类,负责与SQL Server交互"""def __init__(self, server, database, username, password, logger=None):self.server = serverself.database = databaseself.username = usernameself.password = passwordself.connection = Noneself.logger = loggerself.db_log_signal = LogSignal()def get_connection_string(self):"""获取数据库连接字符串"""return (f'DRIVER={{ODBC Driver 17 for SQL Server}};'f'SERVER={self.server};'f'DATABASE={self.database};'f'UID={self.username};'f'PWD={self.password}')def connect(self):"""连接到SQL Server数据库"""try:connection_string = self.get_connection_string()self.log_message(f"[数据库] 尝试连接: {connection_string}")self.connection = pyodbc.connect(connection_string)self.log_message("[数据库] 连接成功")return Trueexcept Exception as e:error_msg = f"数据库连接错误: {str(e)}"self.log_message(f"[错误] {error_msg}")return Falsedef disconnect(self):"""断开数据库连接"""if self.connection:try:self.connection.close()self.log_message("[数据库] 连接已断开")except Exception as e:self.log_message(f"[错误] 断开数据库连接时出错: {str(e)}")finally:self.connection = Nonedef execute_query(self, query, params=None):"""执行SQL查询"""if not self.connection:if not self.connect():raise Exception("无法建立数据库连接")try:cursor = self.connection.cursor()query_str = queryif params:query_str = f"{query} (参数: {params})"self.log_message(f"[SQL] 执行查询: {query_str}")cursor.execute(query, params)return cursorexcept Exception as e:error_msg = f"SQL执行错误: {str(e)}"self.log_message(f"[错误] {error_msg}")raisedef commit(self):"""提交事务"""if self.connection:try:self.connection.commit()self.log_message("[数据库] 事务已提交")except Exception as e:error_msg = f"提交事务时出错: {str(e)}"self.log_message(f"[错误] {error_msg}")raisedef update_data(self, table_name, barcode):"""更新指定条码的状态为1"""query = f"UPDATE {table_name} SET state = 1 WHERE barcode = ?"try:cursor = self.execute_query(query, (barcode,))if cursor:affected_rows = cursor.rowcount  # 获取受影响的行数self.commit()self.log_message(f"[成功] 已更新 {table_name} 表中 {affected_rows} 条记录的状态")return Truereturn Falseexcept Exception as e:error_msg = f"更新数据时出错: {str(e)}"self.log_message(f"[错误] {error_msg}")raisedef insert_data(self, table_name, barcode, batch):"""插入数据到指定表,插入前先更新原有数据状态"""try:# 先更新原有数据状态self.update_data(table_name, barcode)# 再插入新数据query = f"INSERT INTO {table_name} (barcode, batch, STATE) VALUES (?, ?, 0)"cursor = self.execute_query(query, (barcode, batch))if cursor:self.commit()self.log_message(f"[成功] 数据已成功插入到 {table_name} 表")return Truereturn Falseexcept Exception as e:error_msg = f"插入数据时出错: {str(e)}"self.log_message(f"[错误] {error_msg}")raisedef query_data(self, table_name, barcode=None, start_date=None, end_date=None):"""从指定表查询数据,结果按时间由近到远排序"""params = []query = f"SELECT barcode, batch, TIME FROM {table_name} WHERE 1=1"if barcode:query += " AND barcode = ?"params.append(barcode)if start_date:query += " AND TIME >= ?"params.append(start_date)if end_date:query += " AND TIME <= ?"params.append(end_date)query += " ORDER BY TIME DESC"  # 按时间降序排列,最新时间在前try:cursor = self.execute_query(query, params)if cursor:results = []for row_num, row in enumerate(cursor.fetchall(), 1):  # 从1开始编号results.append({'id': row_num,  # 本地自增ID,用于显示序号'barcode': row.barcode,'batch': row.batch,'TIME': row.TIME})return resultsreturn []except Exception as e:error_msg = f"查询数据时出错: {str(e)}"self.log_message(f"[错误] {error_msg}")raisedef delete_data(self, table_name, barcode, time):"""从指定表删除数据,使用barcode和TIME作为唯一标识"""query = f"DELETE FROM {table_name} WHERE barcode = ? AND TIME = ?"try:cursor = self.execute_query(query, (barcode, time))if cursor:self.commit()self.log_message(f"[成功] 已从 {table_name} 表删除条码为 '{barcode}' 时间为 '{time}' 的记录")return Truereturn Falseexcept Exception as e:error_msg = f"删除数据时出错: {str(e)}"self.log_message(f"[错误] {error_msg}")raisedef check_table_exists(self, table_name):"""检查指定表是否存在"""try:query = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = ?"cursor = self.execute_query(query, (table_name,))result = cursor.fetchone()return result[0] > 0except Exception as e:error_msg = f"检查表格存在性时出错: {str(e)}"self.log_message(f"[错误] {error_msg}")return Falsedef check_column_exists(self, table_name, column_name):"""检查指定列是否存在于表中"""try:query = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ? AND COLUMN_NAME = ?"cursor = self.execute_query(query, (table_name, column_name))result = cursor.fetchone()return result[0] > 0except Exception as e:error_msg = f"检查列存在性时出错: {str(e)}"self.log_message(f"[错误] {error_msg}")return Falsedef log_message(self, message):"""记录日志消息(优先使用全局日志管理器)"""if self.logger:self.logger.log(message)if self.db_log_signal:self.db_log_signal.log_message.emit(message)class MaterialBindingWidget(QWidget):"""上料绑定模块基类"""def __init__(self, db_manager, logger=None, parent=None, table_name="T_IN", title="常温上料绑定"):super().__init__(parent)self.db_manager = db_managerself.table_name = table_nameself.title = titleself.logger = loggerself.init_ui()def init_ui(self):# 创建主布局main_layout = QVBoxLayout(self)# 创建标题title_label = QLabel(self.title)title_font = QFont("SimHei", 28, QFont.Bold)title_label.setFont(title_font)title_label.setAlignment(Qt.AlignCenter)# 设置标题颜色为绿色palette = QPalette()palette.setColor(QPalette.WindowText, QColor(0, 128, 0))title_label.setPalette(palette)main_layout.addWidget(title_label)# 创建输入区域input_group = QGroupBox("数据录入")input_layout = QVBoxLayout(input_group)# 扫码信息barcode_layout = QHBoxLayout()barcode_label = QLabel("扫码信息:")barcode_label.setFixedWidth(160)barcode_font = QFont("SimHei", 20)barcode_label.setFont(barcode_font)self.barcode_edit = QLineEdit()self.barcode_edit.setPlaceholderText("请输入扫码信息")self.barcode_edit.setFont(barcode_font)# 连接回车键信号到提交方法self.barcode_edit.returnPressed.connect(self.on_submit)barcode_layout.addWidget(barcode_label)barcode_layout.addWidget(self.barcode_edit)input_layout.addLayout(barcode_layout)# 批次信息batch_layout = QHBoxLayout()batch_label = QLabel("批次信息:")batch_label.setFixedWidth(160)batch_label.setFont(barcode_font)self.batch_edit = QLineEdit()self.batch_edit.setPlaceholderText("请输入批次信息")self.batch_edit.setFont(barcode_font)batch_layout.addWidget(batch_label)batch_layout.addWidget(self.batch_edit)input_layout.addLayout(batch_layout)# 按钮区域button_layout = QHBoxLayout()self.submit_button = QPushButton("提交")self.clear_button = QPushButton("清空")self.result_button = QPushButton("结果显示")button_font = QFont("SimHei", 20)self.submit_button.setFont(button_font)self.clear_button.setFont(button_font)self.result_button.setFont(button_font)# 添加绿色按钮样式button_style = """QPushButton {background-color: #4CAF50;color: white;border-radius: 5px;padding: 8px;}QPushButton:hover {background-color: #45a049;}QPushButton:pressed {background-color: #3e8e41;}"""button_styleNormal = """QPushButton {background-color: #FFC107;color: white;border-radius: 5px;padding: 8px;}QPushButton:hover {background-color: #FFC107;}QPushButton:pressed {background-color: #3e8e41;}"""self.submit_button.setStyleSheet(button_style)self.clear_button.setStyleSheet(button_style)self.result_button.setStyleSheet(button_styleNormal)button_layout.addWidget(self.submit_button)button_layout.addWidget(self.clear_button)button_layout.addWidget(self.result_button)input_layout.addLayout(button_layout)main_layout.addWidget(input_group)# 创建查询区域query_group = QGroupBox("数据查询")query_layout = QVBoxLayout(query_group)# 查询条件区域query_cond_layout = QHBoxLayout()# 条码查询query_barcode_label = QLabel("条码:")query_barcode_label.setFont(barcode_font)self.query_barcode_edit = QLineEdit()self.query_barcode_edit.setPlaceholderText("输入条码查询")self.query_barcode_edit.setFont(barcode_font)# 日期范围查询date_label = QLabel("日期范围:")date_label.setFont(barcode_font)self.start_date_edit = QDateEdit()self.start_date_edit.setCalendarPopup(True)self.start_date_edit.setDate(QDate.currentDate().addDays(-7))  # 默认显示一周内的数据self.start_date_edit.setFont(barcode_font)date_to_label = QLabel("至")date_to_label.setFont(barcode_font)self.end_date_edit = QDateEdit()self.end_date_edit.setCalendarPopup(True)self.end_date_edit.setDate(QDate.currentDate())self.end_date_edit.setFont(barcode_font)# 查询按钮(宽度加倍)self.query_button = QPushButton("查询")self.query_button.setFont(barcode_font)self.query_button.setStyleSheet(button_style)self.query_button.setMinimumWidth(200)  # 查询按钮宽度加倍query_cond_layout.addWidget(query_barcode_label)query_cond_layout.addWidget(self.query_barcode_edit)query_cond_layout.addWidget(date_label)query_cond_layout.addWidget(self.start_date_edit)query_cond_layout.addWidget(date_to_label)query_cond_layout.addWidget(self.end_date_edit)query_cond_layout.addWidget(self.query_button)query_layout.addLayout(query_cond_layout)# 查询结果表格self.result_table = QTableWidget()self.result_table.setColumnCount(4)self.result_table.setHorizontalHeaderLabels(["序号", "条码", "批次", "时间"])self.result_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)self.result_table.setContextMenuPolicy(Qt.CustomContextMenu)self.result_table.customContextMenuRequested.connect(self.show_context_menu)query_layout.addWidget(self.result_table)main_layout.addWidget(query_group)# 连接信号和槽self.submit_button.clicked.connect(self.on_submit)self.clear_button.clicked.connect(self.on_clear)self.query_button.clicked.connect(self.on_query)def log_message(self, message):"""记录日志消息(使用全局日志管理器)"""try:if self.logger:self.logger.log(message)else:print(f"[未设置日志管理器] {message}")except Exception as e:print(f"日志记录失败: {str(e)}")print(message)def on_submit(self):button_stylePass = """QPushButton {background-color: #4CAF50;color: white;border-radius: 5px;padding: 8px;}"""button_styleIng = """QPushButton {background-color: #FFC107;color: white;border-radius: 5px;padding: 8px;}"""button_styleErr = """QPushButton {background-color: #F44336;color: white;border-radius: 5px;padding: 8px;}"""   self.result_button.setText("执行中")self.result_button.setStyleSheet(button_styleIng)"""提交数据到数据库"""barcode = self.barcode_edit.text().strip()batch = self.batch_edit.text().strip()# 开始判断条码是否符合编码规则if len(barcode) < 10:self.log_message(f"[警告] {barcode} 扫码不符合规则!")self.result_button.setText("FAIL")self.result_button.setStyleSheet(button_styleErr)returnif not barcode or not batch:self.log_message("[警告] 扫码信息和批次信息不能为空!")returnself.log_message(f"[操作] 点击提交按钮,条码: {barcode}, 批次: {batch}")try:success = self.db_manager.insert_data(self.table_name, barcode, batch)if success:self.log_message(f"[成功] 条码绑定批次成功 {batch}, {barcode}")self.on_clear()self.result_button.setText("PASS")self.result_button.setStyleSheet(button_stylePass)else:self.log_message(f"[失败] 数据上传失败,请检查数据库连接!")self.result_button.setText("FAIL")self.result_button.setStyleSheet(button_styleErr)self.on_clear()except Exception as e:self.result_button.setText("FAIL")self.result_button.setStyleSheet(button_styleErr)error_details = traceback.format_exc()error_msg = f"提交数据时发生错误: {str(e)}"QMessageBox.critical(self, "错误", error_msg)self.log_message(f"[错误] {error_msg}")self.log_message(f"[详细] {error_details}")self.on_clear()def on_clear(self):"""清空输入框"""self.barcode_edit.clear()self.barcode_edit.setFocus()def on_query(self):"""查询数据"""self.log_message("[操作] 点击查询按钮")barcode = self.query_barcode_edit.text().strip()start_date = self.start_date_edit.date().toString("yyyy-MM-dd")end_date = self.end_date_edit.date().toString("yyyy-MM-dd") + " 23:59:59"  # 包含当天全部时间try:results = self.db_manager.query_data(self.table_name, barcode, start_date, end_date)# 清空表格self.result_table.setRowCount(0)# 填充表格for row_num, row_data in enumerate(results):self.result_table.insertRow(row_num)self.result_table.setItem(row_num, 0, QTableWidgetItem(str(row_data['id'])))self.result_table.setItem(row_num, 1, QTableWidgetItem(row_data['barcode']))self.result_table.setItem(row_num, 2, QTableWidgetItem(row_data['batch']))# 将datetime对象转换为字符串,精确到毫秒三位time_str = row_data['TIME'].strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]self.result_table.setItem(row_num, 3, QTableWidgetItem(time_str))# 显示查询结果数量result_count = len(results)self.log_message(f"[查询] 在 {self.table_name} 表中找到 {result_count} 条记录")except Exception as e:error_details = traceback.format_exc()error_msg = f"查询数据时发生错误: {str(e)}"QMessageBox.critical(self, "错误", error_msg)self.log_message(f"[错误] {error_msg}")self.log_message(f"[详细] {error_details}")def show_context_menu(self, position):"""显示右键菜单"""selected_row = self.result_table.rowAt(position.y())if selected_row >= 0:context_menu = QMenu(self)delete_action = QAction("删除记录", self)context_menu.addAction(delete_action)action = context_menu.exec_(self.result_table.mapToGlobal(position))if action == delete_action:self.delete_selected_record(selected_row)def delete_selected_record(self, row):"""删除选中的记录"""barcode = self.result_table.item(row, 1).text()record_time = self.result_table.item(row, 3).text()reply = QMessageBox.question(self, "确认删除",f"确定要删除条码为 '{barcode}' 的记录吗?",QMessageBox.Yes | QMessageBox.No, QMessageBox.No)if reply == QMessageBox.Yes:self.log_message(f"[操作] 确认删除条码为 '{barcode}' 的记录")try:success = self.db_manager.delete_data(self.table_name, barcode, record_time)if success:self.log_message(f"[成功] 已删除 {self.table_name} 表中条码为 '{barcode}' 的记录")# 删除成功后重新查询self.on_query()else:self.log_message(f"[失败] 删除记录失败,请检查数据库连接!")except Exception as e:error_details = traceback.format_exc()error_msg = f"删除数据时发生错误: {str(e)}"QMessageBox.critical(self, "错误", error_msg)self.log_message(f"[错误] {error_msg}")self.log_message(f"[详细] {error_details}")class QueryWidget(QWidget):"""下料查询模块,结果显示在表格中"""def __init__(self, db_manager, logger=None, parent=None):super().__init__(parent)self.db_manager = db_managerself.logger = loggerself.init_ui()def init_ui(self):# 创建主布局main_layout = QVBoxLayout(self)# 创建标题title_label = QLabel("下料查询")title_font = QFont("SimHei", 28, QFont.Bold)title_label.setFont(title_font)title_label.setAlignment(Qt.AlignCenter)# 设置标题颜色为绿色palette = QPalette()palette.setColor(QPalette.WindowText, QColor(0, 128, 0))title_label.setPalette(palette)main_layout.addWidget(title_label)# 创建查询区域query_group = QGroupBox("查询")query_layout = QHBoxLayout(query_group)# 扫码信息barcode_label = QLabel("扫码查询:")barcode_label.setFixedWidth(160)barcode_font = QFont("SimHei", 20)barcode_label.setFont(barcode_font)self.barcode_edit = QLineEdit()self.barcode_edit.setPlaceholderText("请输入扫码信息")self.barcode_edit.setFont(barcode_font)self.query_button = QPushButton("查询")self.query_button.setFont(barcode_font)# 添加绿色按钮样式button_style = """QPushButton {background-color: #4CAF50;color: white;border-radius: 5px;padding: 8px;}QPushButton:hover {background-color: #45a049;}QPushButton:pressed {background-color: #3e8e41;}"""self.query_button.setStyleSheet(button_style)self.query_button.setMinimumWidth(200)  # 查询按钮宽度加倍query_layout.addWidget(barcode_label)query_layout.addWidget(self.barcode_edit)query_layout.addWidget(self.query_button)main_layout.addWidget(query_group)# 创建结果显示区域result_group = QGroupBox("查询结果")result_layout = QVBoxLayout(result_group)# 使用表格显示查询结果self.result_table = QTableWidget()self.result_table.setColumnCount(4)self.result_table.setHorizontalHeaderLabels(["序号", "条码", "批次", "时间"])self.result_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)result_layout.addWidget(self.result_table)main_layout.addWidget(result_group)# 连接信号和槽self.query_button.clicked.connect(self.on_query)self.barcode_edit.returnPressed.connect(self.on_query)def log_message(self, message):"""记录日志消息(使用全局日志管理器)"""try:if self.logger:self.logger.log(message)else:print(f"[未设置日志管理器] {message}")except Exception as e:print(f"日志记录失败: {str(e)}")print(message)def on_query(self):"""查询数据并清空输入框,结果显示在表格中"""self.log_message("[操作] 点击查询按钮")barcode = self.barcode_edit.text().strip()if not barcode:QMessageBox.warning(self, "警告", "扫码信息不能为空!")returntry:results = self.db_manager.query_data("T_OUT", barcode)  # 获取查询结果# 清空输入框self.barcode_edit.clear()# 清空表格self.result_table.setRowCount(0)if results:# 填充表格for row_num, row_data in enumerate(results):self.result_table.insertRow(row_num)self.result_table.setItem(row_num, 0, QTableWidgetItem(str(row_data['id'])))self.result_table.setItem(row_num, 1, QTableWidgetItem(row_data['barcode']))self.result_table.setItem(row_num, 2, QTableWidgetItem(row_data['batch']))# 将datetime对象转换为字符串,精确到毫秒三位time_str = row_data['TIME'].strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]self.result_table.setItem(row_num, 3, QTableWidgetItem(time_str))result_count = len(results)self.log_message(f"[查询] 在下料表中找到 {result_count} 条记录,按时间由近到远显示")else:self.result_table.setRowCount(1)self.result_table.setItem(0, 0, QTableWidgetItem("无结果"))self.result_table.setItem(0, 1, QTableWidgetItem(f"未找到条码为 '{barcode}' 的记录"))self.result_table.setSpan(0, 0, 1, 4)  # 合并单元格self.result_table.item(0, 0).setTextAlignment(Qt.AlignCenter)self.log_message(f"[查询] 未找到条码为 '{barcode}' 的下料记录")except Exception as e:error_details = traceback.format_exc()error_msg = f"查询数据时发生错误: {str(e)}"QMessageBox.critical(self, "错误", error_msg)self.log_message(f"[错误] {error_msg}")self.log_message(f"[详细] {error_details}")class MainWindow(QMainWindow):"""主窗口类"""def __init__(self):super().__init__()# 数据库配置self.db_config = {'server': 'LEGENDLI',  # 请根据实际情况修改服务器地址'database': 'testbase','username': 'sa','password': '1'}# 初始化全局日志管理器self.logger = Logger()# 初始化数据库管理器并传递日志管理器self.db_manager = DatabaseManager(self.db_config['server'],self.db_config['database'],self.db_config['username'],self.db_config['password'],logger=self.logger)# 连接日志信号到界面显示self.logger.log_message.connect(self.log_to_ui)# 检查数据库连接if not self.db_manager.connect():QMessageBox.critical(self, "数据库连接失败", "无法连接到SQL Server数据库,请检查配置!")self.init_ui()def init_ui(self):# 设置窗口标题和大小self.setWindowTitle("生产数据管理系统")self.setGeometry(100, 100, 1800, 900)# 创建中心部件central_widget = QWidget()self.setCentralWidget(central_widget)# 创建主布局main_layout = QVBoxLayout(central_widget)# 创建标签页控件self.tab_widget = QTabWidget()# 1. 常温上料绑定self.normal_material_tab = MaterialBindingWidget(self.db_manager,logger=self.logger,table_name="T_IN",title="常温上料绑定")self.tab_widget.addTab(self.normal_material_tab, "常温上料绑定")# 2. 高温上料插管座绑定self.hight_temp_insert_tab = MaterialBindingWidget(self.db_manager,logger=self.logger,table_name="T_HIGHTIN",title="高温上料插管座绑定")self.tab_widget.addTab(self.hight_temp_insert_tab, "高温上料插管座绑定")# 3. 高温上料拔管座绑定self.hight_temp_remove_tab = MaterialBindingWidget(self.db_manager,logger=self.logger,table_name="T_HIGHTOUT",title="高温上料拔管座绑定")self.tab_widget.addTab(self.hight_temp_remove_tab, "高温上料拔管座绑定")# 4. 下料查询self.query_tab = QueryWidget(self.db_manager,logger=self.logger)self.tab_widget.addTab(self.query_tab, "下料查询")# 将标签页控件添加到主布局main_layout.addWidget(self.tab_widget)# 创建日志区域try:log_group = QGroupBox("系统日志")log_title_font = QFont("SimHei", 20)log_group.setFont(log_title_font)log_layout = QVBoxLayout(log_group)self.log_text = QTextEdit()self.log_text.setReadOnly(True)self.log_text.setPlaceholderText("系统日志将显示在这里...")log_font = QFont("SimHei", 15)self.log_text.setFont(log_font)log_layout.addWidget(self.log_text)main_layout.addWidget(log_group)# 初始化日志self.log_to_ui("===== 系统已启动 =====")self.log_to_ui(f"数据库配置: 服务器={self.db_config['server']}, 数据库={self.db_config['database']}")self.log_to_ui("[系统] 按钮样式已设置为绿色,操作日志功能已启用")except Exception as e:print(f"创建日志区域失败: {str(e)}")QMessageBox.warning(self, "警告", f"创建日志区域失败: {str(e)}")# 设置全局字体global_font = QFont("SimHei", 20)QApplication.setFont(global_font)# 设置标题栏样式(Windows系统有效)self.setStyleSheet("""QMainWindow::title {font-size: 36px;  /* 标题字体大小翻倍,从18px增加到36px */color: #008000;   /* 标题颜色设置为绿色 */font-weight: bold;}""")def log_to_ui(self, message):"""将日志消息添加到日志区域,并实现自动清空功能"""try:if hasattr(self, 'log_text'):# 添加新日志self.log_text.append(message)# 检查滚动条是否在底部vertical_scrollbar = self.log_text.verticalScrollBar()is_at_bottom = vertical_scrollbar.value() == vertical_scrollbar.maximum()# 如果滚动条在底部且日志行数超过100行,清空日志if is_at_bottom and self.log_text.document().lineCount() > 10:self.log_text.clear()self.log_to_ui(message)  # 重新添加当前日志(包含时间戳)# 确保显示最新日志vertical_scrollbar.setValue(vertical_scrollbar.maximum())except Exception as e:print(f"记录日志失败: {str(e)}")print(message)def closeEvent(self, event):"""关闭窗口时断开数据库连接"""try:self.db_manager.disconnect()except Exception as e:print(f"关闭数据库连接失败: {str(e)}")event.accept()if __name__ == "__main__":# 创建应用实例app = QApplication(sys.argv)# 创建并显示主窗口try:window = MainWindow()window.show()except Exception as e:print(f"创建主窗口失败: {str(e)}")QMessageBox.critical(None, "严重错误", f"应用程序无法启动: {str(e)}")sys.exit(1)# 进入应用主循环sys.exit(app.exec_())

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

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

相关文章

【unitrix】 4.1 类型级加一操作(Add1.rs)

一、原码 这段代码实现了一个类型级的加一操作(Add1 trait)&#xff0c;用于在Rust的类型系统中进行数值加一运算。 //! 加一操作特质实现 / Increment operation trait implementation //! //! 说明&#xff1a; //! 1. Z0、P1,、N1 1&#xff0c;常规计算 //! 2. …

git工作中常用

1.管理本地文件 git init//初始化生成一个本地仓库 git add * //添加到暂存区 git commit–m “message” //提交到本地仓库 2.删除本地分支 git branch -d local_branch_name3.隐藏及解除隐藏 git stashgit stash pop4.远程新建分支&#xff0c;在本地签出时候怎么看到 …

Golang 中接口嵌套的详细说明和使用示例

在 Go 语言中&#xff0c;接口嵌套&#xff08;也称为接口组合&#xff09;是一种强大的特性&#xff0c;它允许你通过组合现有接口来创建新的接口。这种方式遵循了 Go 的组合优于继承的设计哲学。 接口嵌套的基本概念 接口嵌套是指在一个接口中嵌入其他接口&#xff0c;从而…

数智管理学(二十四)

第二章 数智化重塑管理的核心 第三节 动态资源配置与实时优化 在当今数智化浪潮的席卷下&#xff0c;企业管理面临着前所未有的变革与挑战。资源配置作为企业管理的核心环节之一&#xff0c;其方式和效率直接影响着企业的运营成本、生产效率和市场竞争力。传统的静态资源配置…

Redis 各版本差异及性能测试指标对比

Redis 各版本差异及性能测试指标对比 Redis 主要版本差异 Redis 2.x 系列 主要特性&#xff1a; 支持主从复制支持简单的持久化(RDB和AOF)发布/订阅功能事务支持 局限性&#xff1a; 单线程模型集群功能有限 Redis 3.x 系列 重大改进&#xff1a; 引入Redis Cluster(官方…

Python图形化秒表:使用Turtle打造精确计时工具

⏱️ 编程基础第一期《6-30》–简易计时器/秒表&#xff0c;这是一个使用Python的turtle和time模块实现的简易计时器/秒表程序&#xff0c;提供简洁的数字时间显示。 目录 &#x1f31f; 功能特点&#x1f680; 使用方法&#x1f9e9; 程序架构设计&#x1f4bb; 代码详解窗口和…

【轨物方案】轨物科技|LoRaWAN 赋能智能光伏清扫,解锁电站高效运维新时代

在大型集中式光伏电站的广袤土地上&#xff0c;清扫机器人的高效运行是保障发电效率的关键。然而&#xff0c;传统的无线通信方式在这些偏远、无4G/5G信号覆盖的区域&#xff0c;往往步履维艰。作为专注于工业物联网解决方案的轨物科技&#xff0c;我们深知这些痛点&#xff0c…

Python函数实战:从基础到高级应用

Python-函数 Python 中可以使用def关键字来定义函数。 函数定义规则&#xff1a; 函数代码块以 def 关键词开头&#xff0c;后接函数标识符名称和圆括号 ()。任何传入参数和自变量必须放在圆括号中间&#xff0c;圆括号之间可以用于定义参数。函数的第一行语句可以选择性地使…

Mac在局域网中突然很慢(包括SMB、NFS、SCP、SSH、Ping等场景均很慢)

今天 SMB 又突然好慢&#xff0c;大概只有 8-9 MB/s&#xff0c;而苹果 SMB 很容易突然很慢是出了名的。我就想装 NFS&#xff0c;但是 NFS 弄好之后还是很慢&#xff0c;我服了&#xff0c;我就检查了scp等场景&#xff0c;都很慢&#xff0c;但是互联网下载速度还是很快的。 …

UMAP:用于降维的均匀流形近似和投影实验

关键词&#xff1a; Uniform Manifold Approximation and Projection (UMAP)&#xff1a;均匀流形近似与投影 一、说明 对于降维&#xff0c;首先看数据集是否线性&#xff0c;如果是线性的用pca降维&#xff1b;如果是非线性数据&#xff0c;t-SNE或者UMAP&#xff0c;本文针…

【Datawhale组队学习202506】YOLO-Master task03 IOU总结

系列文章目录 task01 导学课程 task02 YOLO系列发展线 文章目录 系列文章目录前言1 功能分块1.1 骨干网络 Backbone1.2 颈部网络 Neck1.3 头部网络 Head1.3.1 边界框回归头1.3.2 分类头 2 关键概念3 典型算法3.1 NMS3.2 IoU 总结 前言 Datawhale是一个专注于AI与数据科学的开…

Spring IOC容器核心揭秘:BeanFactory创建、配置加载解析并注册为BeanDefinition

文章目录 一、为何这个阶段如此重要&#xff1f;二、整体流程全景图三、源码级深度解析1. BeanFactory的诞生源码入口&#xff1a;refresh()方法核心方法&#xff1a;obtainFreshBeanFactory()核心实现&#xff1a;refreshBeanFactory()BeanFactory实例化 2. ★ 核心&#xff1…

解锁n8n:开启工作流自动化的无限可能(5/6)

文章摘要&#xff1a;n8n 是一款开源低代码工作流自动化平台&#xff0c;通过可视化拖放节点创建复杂工作流&#xff0c;无需大量代码。具有强大集成能力、数据转换、错误处理等功能&#xff0c;适用于数据同步、客户关系管理、IT 自动化等场景。相比 Zapier、IFTTT 等工具&…

数据赋能(308)——合作共享——数据交流

概述 重要性如下&#xff1a; 信息准确性&#xff1a;数据交流原则确保在数据传递过程中信息的准确性&#xff0c;这是决策和业务活动的基础。决策支持&#xff1a;准确的数据交流为决策提供有力支持&#xff0c;帮助组织做出更明智的决策。业务效率&#xff1a;有效的数据交…

TCP流量控制与拥塞控制:核心机制与区别

一、TCP流量控制&#xff08;Flow Control&#xff09; 定义&#xff1a;通过调节发送方的发送速率&#xff0c;确保接收方能够及时处理数据&#xff0c;避免缓冲区溢出。 本质&#xff1a;解决发送方与接收方之间的"端到端"速率匹配问题。 1. 实现机制&#xff1a…

iOS多端兼容性调试:一次iOS性能优化分工具协作排查过程

在多技术栈混合开发日益普及的今天&#xff0c;iOS应用中越来越多地集成了WebView、Flutter、React Native甚至小程序模块。而这些模块带来的复杂性&#xff0c;不仅体现在UI适配&#xff0c;还包括数据同步、系统权限管理、线程调度等方面的问题。 本文记录的是我们在处理一个…

秋招Day14 - MySQL - 索引

索引为什么能够提高MySQL的查询效率&#xff1f; 索引可以理解为目录&#xff0c;通过索引可以快速定位数据&#xff0c;避免全表扫描 一般是B树结构&#xff0c;查找效率是O(log n)。 索引还能加速排序、分组、连接等操作。 create index idx_name on students(name); 能简…

第5天:LSTM预测火灾温度

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目标 复用LSTM模型实现火灾温度预测 具体实现 &#xff08;一&#xff09;环境 语言环境&#xff1a;Python 3.10 编 译 器: PyCharm 框 架: Pytorch &am…

目标检测之YOLOV11自定义数据使用OBB训练与验证

一、前置条件与环境准备 在开始训练前&#xff0c;确保已完成以下准备《目标检测之YOLOV11自定义数据预处理——从原始标注到YOLO-OBB格式转换与验证》&#xff1a; 数据目录结构&#xff1a; yolov11/ ├── datasets/ │ └── shrimp/ │ ├── images/ │ …

Labview教程进阶一(Labview与OPC UA设备通信)

1.Labview与OPC UA设备通信 OPC UA通信协议优势显著,具体表现如下: 跨平台兼容:支持多种操作系统和硬件平台,实现无缝数据交换。高安全性:内置加密、身份验证和授权机制,确保数据传输安全。高效数据交换:采用二进制编码和优化的传输协议,提高通信效率。复杂数据建模:…