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
英文地址,請注明出處:http://en.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通信協議優勢顯著,具體表現如下: 跨平臺兼容:支持多種操作系統和硬件平臺,實現無縫數據交換。高安全性:內置加密、身份驗證和授權機制,確保數據傳輸安全。高效數據交換:采用二進制編碼和優化的傳輸協議,提高通信效率。復雜數據建模:…