网站建设哪家好 北京,window优化大师,成功营销网站,我们不是做网站的生产数据管理系统说明 系统概述
这是一个基于PyQt5和pyodbc开发的生产数据管理系统#xff0c;主要用于管理生产过程中的物料绑定和查询操作。系统提供了上料绑定和下料查询功能#xff0c;支持与SQL Server数据库交互#xff0c;实现数据的插入、查询、更新和删除操作。界…生产数据管理系统说明 系统概述
这是一个基于PyQt5和pyodbc开发的生产数据管理系统主要用于管理生产过程中的物料绑定和查询操作。系统提供了上料绑定和下料查询功能支持与SQL Server数据库交互实现数据的插入、查询、更新和删除操作。界面采用标签页设计操作流程清晰反馈明确适合生产环境使用。
主要功能模块
系统包含以下核心模块 数据库管理模块DatabaseManager 负责与SQL Server数据库建立连接和交互实现数据插入、查询、更新和删除操作提供表和列的存在性检查功能记录数据库操作日志 上料绑定模块MaterialBindingWidget 常温上料绑定高温上料插管座绑定高温上料拔管座绑定支持扫码信息和批次信息录入数据查询和结果展示右键删除记录功能 下料查询模块QueryWidget 扫码查询下料记录结果以表格形式展示按时间由近到远排序无结果时显示友好提示 日志管理模块Logger 全局日志记录带时间戳的日志消息日志自动清理功能日志显示在主界面底部
界面设计特点 标签页布局 四个主要功能标签页常温上料绑定、高温上料插管座绑定、高温上料拔管座绑定、下料查询界面整洁功能分区明确 按钮设计 绿色提交按钮#4CAF50表示正向操作黄色按钮表示中间状态红色按钮表示错误状态按钮悬停和按下状态有视觉反馈 结果展示 表格形式展示查询结果列宽自动适应内容支持右键删除记录 操作反馈 提交操作后显示明确的状态提示操作过程中按钮状态变化日志区域实时记录操作信息
数据库操作流程 数据插入流程 先更新原有数据状态再插入新数据提交事务记录操作日志 数据查询流程 构建查询条件执行SQL查询按时间降序排列结果填充到表格中展示 数据删除流程 右键选择记录确认删除操作执行删除SQL提交事务重新查询刷新结果
颜色说明 绿色#4CAF50 用于表示成功状态提交按钮背景色成功提示文字颜色 黄色#FFC107 用于表示执行中状态中间状态按钮背景色 红色#F44336 用于表示错误状态失败提示文字和按钮颜色 绿色#008000 用于标题文字表示正常状态
特殊功能说明 状态提示按钮 提交操作时按钮状态变化初始 - 执行中 - 成功/失败视觉反馈清晰操作状态一目了然 自动清空日志 当日志行数超过10行且滚动条在底部时自动清空保留最新的日志记录 扫码规则检查 自动检查扫码信息长度长度小于10时提示不符合规则 日期范围查询 默认为最近7天的数据支持自定义日期范围查询
使用说明 数据库配置 在MainWindow类中修改db_config字典中的服务器地址、数据库名称、用户名和密码 上料绑定 输入扫码信息和批次信息点击提交按钮绑定数据按钮状态显示操作结果可在查询区域查询历史记录 下料查询 输入扫码信息点击查询按钮结果显示在表格中按时间由近到远排列 日志查看 主界面底部显示系统操作日志包含时间戳和操作详情
技术要点 数据库连接管理 使用pyodbc连接SQL Server自动重连机制事务管理确保数据一致性 界面交互 使用信号与槽机制表格组件的上下文菜单输入框回车提交功能 代码结构 面向对象设计模块职责明确日志系统独立封装界面组件继承复用
这个生产数据管理系统适合在生产环境中使用提供了完整的物料绑定和查询功能界面友好操作简单同时具备完善的日志记录和错误处理机制。
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, loggerNone):self.server serverself.database databaseself.username usernameself.password passwordself.connection Noneself.logger loggerself.db_log_signal LogSignal()def get_connection_string(self):获取数据库连接字符串return (fDRIVER{{ODBC Driver 17 for SQL Server}};fSERVER{self.server};fDATABASE{self.database};fUID{self.username};fPWD{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, paramsNone):执行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 fSQL执行错误: {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):更新指定条码的状态为1query fUPDATE {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 fINSERT 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, barcodeNone, start_dateNone, end_dateNone):从指定表查询数据结果按时间由近到远排序params []query fSELECT barcode, batch, TIME FROM {table_name} WHERE 11if 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 fDELETE 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, loggerNone, parentNone, table_nameT_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, loggerNone, parentNone):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],loggerself.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,loggerself.logger,table_nameT_IN,title常温上料绑定)self.tab_widget.addTab(self.normal_material_tab, 常温上料绑定)# 2. 高温上料插管座绑定self.hight_temp_insert_tab MaterialBindingWidget(self.db_manager,loggerself.logger,table_nameT_HIGHTIN,title高温上料插管座绑定)self.tab_widget.addTab(self.hight_temp_insert_tab, 高温上料插管座绑定)# 3. 高温上料拔管座绑定self.hight_temp_remove_tab MaterialBindingWidget(self.db_manager,loggerself.logger,table_nameT_HIGHTOUT,title高温上料拔管座绑定)self.tab_widget.addTab(self.hight_temp_remove_tab, 高温上料拔管座绑定)# 4. 下料查询self.query_tab QueryWidget(self.db_manager,loggerself.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_())