增加了AI分析产品的功能
This commit is contained in:
431
product/sqlite_viewer.py
Normal file
431
product/sqlite_viewer.py
Normal file
@@ -0,0 +1,431 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
SQLite数据库查看器 - 基于PySide6
|
||||
功能:打开product目录下的product.db的sqlite文件,显示表和数据的界面
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import sqlite3
|
||||
from loguru import logger
|
||||
|
||||
from PySide6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout,
|
||||
QWidget, QPushButton, QTableWidget, QTableWidgetItem,
|
||||
QListWidget, QListWidgetItem, QSplitter, QFileDialog,
|
||||
QLabel, QStatusBar, QMessageBox, QHeaderView, QComboBox,
|
||||
QLineEdit, QGroupBox)
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtGui import QAction
|
||||
|
||||
|
||||
class SQLiteViewer(QMainWindow):
|
||||
"""SQLite数据库查看器主窗口"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
logger.info("初始化SQLite数据库查看器")
|
||||
self.db_connection = None
|
||||
self.current_table = None
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
"""初始化用户界面"""
|
||||
logger.info("设置主窗口界面")
|
||||
self.setWindowTitle("SQLite数据库查看器")
|
||||
self.setGeometry(100, 100, 1200, 800)
|
||||
|
||||
# 创建中央部件
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
# 创建主布局
|
||||
main_layout = QVBoxLayout(central_widget)
|
||||
|
||||
# 创建顶部按钮布局
|
||||
self.create_top_buttons(main_layout)
|
||||
|
||||
# 创建筛选控件区域
|
||||
self.create_filter_section(main_layout)
|
||||
|
||||
# 创建分割器(左侧表列表,右侧数据表格)
|
||||
self.create_splitter(main_layout)
|
||||
|
||||
# 创建状态栏
|
||||
self.create_status_bar()
|
||||
|
||||
# 创建菜单栏
|
||||
self.create_menubar()
|
||||
|
||||
logger.info("界面初始化完成")
|
||||
|
||||
def create_top_buttons(self, layout):
|
||||
"""创建顶部按钮布局"""
|
||||
logger.info("创建顶部按钮")
|
||||
button_layout = QHBoxLayout()
|
||||
|
||||
# 打开数据库按钮
|
||||
self.open_button = QPushButton("打开SQLite数据库")
|
||||
self.open_button.clicked.connect(self.open_database)
|
||||
button_layout.addWidget(self.open_button)
|
||||
|
||||
# 刷新按钮
|
||||
self.refresh_button = QPushButton("刷新")
|
||||
self.refresh_button.clicked.connect(self.refresh_data)
|
||||
self.refresh_button.setEnabled(False)
|
||||
button_layout.addWidget(self.refresh_button)
|
||||
|
||||
# 数据库路径显示
|
||||
self.db_path_label = QLabel("未打开数据库")
|
||||
button_layout.addWidget(self.db_path_label)
|
||||
|
||||
button_layout.addStretch()
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
def create_filter_section(self, layout):
|
||||
"""创建筛选控件区域"""
|
||||
logger.info("创建筛选控件区域")
|
||||
|
||||
# 创建筛选分组框
|
||||
filter_group = QGroupBox("数据筛选")
|
||||
filter_layout = QHBoxLayout(filter_group)
|
||||
|
||||
# 字段选择标签
|
||||
filter_layout.addWidget(QLabel("筛选字段:"))
|
||||
|
||||
# 字段选择下拉框
|
||||
self.field_combo = QComboBox()
|
||||
self.field_combo.setMinimumWidth(150)
|
||||
filter_layout.addWidget(self.field_combo)
|
||||
|
||||
# 筛选条件标签
|
||||
filter_layout.addWidget(QLabel("筛选条件:"))
|
||||
|
||||
# 筛选条件输入框
|
||||
self.filter_input = QLineEdit()
|
||||
self.filter_input.setPlaceholderText("输入筛选条件,如:name='test' 或 created_at>'2024-01-01'")
|
||||
self.filter_input.setMinimumWidth(300)
|
||||
filter_layout.addWidget(self.filter_input)
|
||||
|
||||
# 筛选按钮
|
||||
self.filter_button = QPushButton("筛选")
|
||||
self.filter_button.clicked.connect(self.apply_filter)
|
||||
filter_layout.addWidget(self.filter_button)
|
||||
|
||||
# 清除筛选按钮
|
||||
self.clear_filter_button = QPushButton("清除筛选")
|
||||
self.clear_filter_button.clicked.connect(self.clear_filter)
|
||||
self.clear_filter_button.setEnabled(False)
|
||||
filter_layout.addWidget(self.clear_filter_button)
|
||||
|
||||
filter_layout.addStretch()
|
||||
|
||||
# 初始状态下禁用筛选控件
|
||||
self.field_combo.setEnabled(False)
|
||||
self.filter_input.setEnabled(False)
|
||||
self.filter_button.setEnabled(False)
|
||||
|
||||
layout.addWidget(filter_group)
|
||||
|
||||
def create_splitter(self, layout):
|
||||
"""创建分割器界面"""
|
||||
logger.info("创建分割器界面")
|
||||
splitter = QSplitter(Qt.Horizontal)
|
||||
|
||||
# 左侧:表列表
|
||||
left_widget = QWidget()
|
||||
left_layout = QVBoxLayout(left_widget)
|
||||
|
||||
left_layout.addWidget(QLabel("数据库表列表:"))
|
||||
self.table_list = QListWidget()
|
||||
self.table_list.itemClicked.connect(self.on_table_selected)
|
||||
left_layout.addWidget(self.table_list)
|
||||
|
||||
# 右侧:数据表格
|
||||
right_widget = QWidget()
|
||||
right_layout = QVBoxLayout(right_widget)
|
||||
|
||||
right_layout.addWidget(QLabel("表数据:"))
|
||||
self.data_table = QTableWidget()
|
||||
self.data_table.setAlternatingRowColors(True)
|
||||
right_layout.addWidget(self.data_table)
|
||||
|
||||
splitter.addWidget(left_widget)
|
||||
splitter.addWidget(right_widget)
|
||||
splitter.setSizes([300, 900])
|
||||
|
||||
layout.addWidget(splitter)
|
||||
|
||||
def create_status_bar(self):
|
||||
"""创建状态栏"""
|
||||
logger.info("创建状态栏")
|
||||
self.status_bar = QStatusBar()
|
||||
self.setStatusBar(self.status_bar)
|
||||
self.status_bar.showMessage("就绪")
|
||||
|
||||
def create_menubar(self):
|
||||
"""创建菜单栏"""
|
||||
logger.info("创建菜单栏")
|
||||
menubar = self.menuBar()
|
||||
|
||||
# 文件菜单
|
||||
file_menu = menubar.addMenu("文件")
|
||||
|
||||
open_action = QAction("打开数据库", self)
|
||||
open_action.triggered.connect(self.open_database)
|
||||
file_menu.addAction(open_action)
|
||||
|
||||
exit_action = QAction("退出", self)
|
||||
exit_action.triggered.connect(self.close)
|
||||
file_menu.addAction(exit_action)
|
||||
|
||||
def open_database(self):
|
||||
"""打开SQLite数据库文件"""
|
||||
logger.info("打开数据库文件对话框")
|
||||
|
||||
# 默认打开product目录下的product.db
|
||||
default_path = os.path.join('product', 'product.db')
|
||||
if os.path.exists(default_path):
|
||||
file_path, _ = QFileDialog.getOpenFileName(
|
||||
self, "打开SQLite数据库", default_path, "SQLite数据库文件 (*.db *.sqlite *.sqlite3)"
|
||||
)
|
||||
else:
|
||||
file_path, _ = QFileDialog.getOpenFileName(
|
||||
self, "打开SQLite数据库", "", "SQLite数据库文件 (*.db *.sqlite *.sqlite3)"
|
||||
)
|
||||
|
||||
if file_path:
|
||||
logger.info(f"打开数据库文件: {file_path}")
|
||||
self.connect_to_database(file_path)
|
||||
|
||||
def connect_to_database(self, file_path):
|
||||
"""连接到指定的SQLite数据库"""
|
||||
try:
|
||||
if self.db_connection:
|
||||
self.db_connection.close()
|
||||
|
||||
self.db_connection = sqlite3.connect(file_path)
|
||||
logger.info("数据库连接成功")
|
||||
|
||||
self.db_path_label.setText(f"数据库: {os.path.basename(file_path)}")
|
||||
self.status_bar.showMessage(f"已连接到数据库: {os.path.basename(file_path)}")
|
||||
self.refresh_button.setEnabled(True)
|
||||
|
||||
# 加载表列表
|
||||
self.load_table_list()
|
||||
|
||||
except sqlite3.Error as e:
|
||||
logger.error(f"数据库连接失败: {e}")
|
||||
QMessageBox.critical(self, "错误", f"无法打开数据库: {e}")
|
||||
|
||||
def load_table_list(self):
|
||||
"""加载数据库表列表"""
|
||||
if not self.db_connection:
|
||||
return
|
||||
|
||||
try:
|
||||
cursor = self.db_connection.cursor()
|
||||
cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
|
||||
tables = cursor.fetchall()
|
||||
|
||||
self.table_list.clear()
|
||||
for table in tables:
|
||||
item = QListWidgetItem(table[0])
|
||||
self.table_list.addItem(item)
|
||||
|
||||
logger.info(f"加载了 {len(tables)} 个表")
|
||||
self.status_bar.showMessage(f"已加载 {len(tables)} 个表")
|
||||
|
||||
except sqlite3.Error as e:
|
||||
logger.error(f"加载表列表失败: {e}")
|
||||
QMessageBox.critical(self, "错误", f"加载表列表失败: {e}")
|
||||
|
||||
def on_table_selected(self, item):
|
||||
"""当表被选中时加载表数据和字段列表"""
|
||||
table_name = item.text()
|
||||
logger.info(f"选中表: {table_name}")
|
||||
self.current_table = table_name
|
||||
self.load_table_data(table_name)
|
||||
self.update_field_combo(table_name)
|
||||
|
||||
def load_table_data(self, table_name):
|
||||
"""加载指定表的数据"""
|
||||
if not self.db_connection:
|
||||
return
|
||||
|
||||
try:
|
||||
cursor = self.db_connection.cursor()
|
||||
|
||||
# 获取表结构
|
||||
cursor.execute(f"PRAGMA table_info({table_name})")
|
||||
columns = cursor.fetchall()
|
||||
column_names = [col[1] for col in columns]
|
||||
|
||||
# 获取数据
|
||||
cursor.execute(f"SELECT * FROM {table_name}")
|
||||
data = cursor.fetchall()
|
||||
|
||||
# 设置表格
|
||||
self.data_table.setRowCount(len(data))
|
||||
self.data_table.setColumnCount(len(column_names))
|
||||
self.data_table.setHorizontalHeaderLabels(column_names)
|
||||
|
||||
# 填充数据
|
||||
for row_idx, row_data in enumerate(data):
|
||||
for col_idx, cell_data in enumerate(row_data):
|
||||
item = QTableWidgetItem(str(cell_data) if cell_data is not None else "")
|
||||
self.data_table.setItem(row_idx, col_idx, item)
|
||||
|
||||
# 调整列宽
|
||||
self.data_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
|
||||
|
||||
logger.info(f"加载表 {table_name} 数据完成,共 {len(data)} 行")
|
||||
self.status_bar.showMessage(f"表 {table_name}: {len(data)} 行数据")
|
||||
|
||||
except sqlite3.Error as e:
|
||||
logger.error(f"加载表数据失败: {e}")
|
||||
QMessageBox.critical(self, "错误", f"加载表数据失败: {e}")
|
||||
|
||||
def update_field_combo(self, table_name):
|
||||
"""更新字段选择下拉框"""
|
||||
if not self.db_connection:
|
||||
return
|
||||
|
||||
try:
|
||||
cursor = self.db_connection.cursor()
|
||||
cursor.execute(f"PRAGMA table_info({table_name})")
|
||||
columns = cursor.fetchall()
|
||||
|
||||
# 清空当前字段列表
|
||||
self.field_combo.clear()
|
||||
|
||||
# 添加所有字段到下拉框
|
||||
for column in columns:
|
||||
field_name = column[1] # 字段名在第二个位置
|
||||
self.field_combo.addItem(field_name)
|
||||
|
||||
# 启用筛选控件
|
||||
self.field_combo.setEnabled(True)
|
||||
self.filter_input.setEnabled(True)
|
||||
self.filter_button.setEnabled(True)
|
||||
|
||||
logger.info(f"更新字段下拉框: {table_name}, 共 {len(columns)} 个字段")
|
||||
|
||||
except sqlite3.Error as e:
|
||||
logger.error(f"获取表字段信息失败: {e}")
|
||||
QMessageBox.warning(self, "错误", f"获取表字段信息失败: {e}")
|
||||
|
||||
def apply_filter(self):
|
||||
"""应用筛选条件"""
|
||||
if not self.db_connection or not self.current_table:
|
||||
return
|
||||
|
||||
selected_field = self.field_combo.currentText()
|
||||
filter_condition = self.filter_input.text().strip()
|
||||
|
||||
if not selected_field or not filter_condition:
|
||||
QMessageBox.warning(self, "警告", "请选择筛选字段并输入筛选条件")
|
||||
return
|
||||
|
||||
try:
|
||||
cursor = self.db_connection.cursor()
|
||||
|
||||
# 构建SQL查询语句
|
||||
query = f"SELECT * FROM {self.current_table} WHERE {selected_field} LIKE ?"
|
||||
|
||||
# 处理筛选条件(支持模糊匹配)
|
||||
filter_value = f"%{filter_condition}%"
|
||||
|
||||
# 执行查询
|
||||
cursor.execute(query, (filter_value,))
|
||||
data = cursor.fetchall()
|
||||
|
||||
# 获取表结构
|
||||
cursor.execute(f"PRAGMA table_info({self.current_table})")
|
||||
columns = cursor.fetchall()
|
||||
column_names = [col[1] for col in columns]
|
||||
|
||||
# 更新表格显示
|
||||
self.data_table.setRowCount(len(data))
|
||||
self.data_table.setColumnCount(len(column_names))
|
||||
self.data_table.setHorizontalHeaderLabels(column_names)
|
||||
|
||||
# 填充筛选后的数据
|
||||
for row_idx, row_data in enumerate(data):
|
||||
for col_idx, cell_data in enumerate(row_data):
|
||||
item = QTableWidgetItem(str(cell_data) if cell_data is not None else "")
|
||||
self.data_table.setItem(row_idx, col_idx, item)
|
||||
|
||||
# 调整列宽
|
||||
self.data_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
|
||||
|
||||
# 启用清除筛选按钮
|
||||
self.clear_filter_button.setEnabled(True)
|
||||
|
||||
logger.info(f"应用筛选条件: {selected_field} LIKE '%{filter_condition}%', 匹配到 {len(data)} 行数据")
|
||||
self.status_bar.showMessage(f"筛选结果: {len(data)} 行数据 (条件: {selected_field} 包含 '{filter_condition}')")
|
||||
|
||||
except sqlite3.Error as e:
|
||||
logger.error(f"筛选数据失败: {e}")
|
||||
QMessageBox.critical(self, "错误", f"筛选数据失败: {e}")
|
||||
|
||||
def clear_filter(self):
|
||||
"""清除筛选条件,显示所有数据"""
|
||||
if not self.current_table:
|
||||
return
|
||||
|
||||
try:
|
||||
# 重新加载完整数据
|
||||
self.load_table_data(self.current_table)
|
||||
|
||||
# 清空筛选条件
|
||||
self.filter_input.clear()
|
||||
|
||||
# 禁用清除筛选按钮
|
||||
self.clear_filter_button.setEnabled(False)
|
||||
|
||||
logger.info("清除筛选条件,显示所有数据")
|
||||
self.status_bar.showMessage("已清除筛选条件,显示所有数据")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"清除筛选失败: {e}")
|
||||
QMessageBox.critical(self, "错误", f"清除筛选失败: {e}")
|
||||
|
||||
def refresh_data(self):
|
||||
"""刷新当前数据"""
|
||||
logger.info("刷新数据")
|
||||
if self.current_table:
|
||||
self.load_table_data(self.current_table)
|
||||
else:
|
||||
self.load_table_list()
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""关闭事件处理"""
|
||||
logger.info("关闭应用程序")
|
||||
if self.db_connection:
|
||||
self.db_connection.close()
|
||||
event.accept()
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
logger.info("启动SQLite数据库查看器")
|
||||
|
||||
# 配置日志
|
||||
logger.add("sqlite_viewer.log", rotation="10 MB", level="INFO")
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
# 设置应用程序信息
|
||||
app.setApplicationName("SQLite数据库查看器")
|
||||
app.setApplicationVersion("1.0.0")
|
||||
|
||||
viewer = SQLiteViewer()
|
||||
viewer.show()
|
||||
|
||||
logger.info("应用程序启动完成")
|
||||
sys.exit(app.exec())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user