更新了sqlite视图的功能

This commit is contained in:
2025-11-28 22:29:47 +08:00
parent 8fcf3bcfe2
commit b32549c5df
7 changed files with 1856 additions and 9 deletions

View File

@@ -14,9 +14,91 @@ from PySide6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayo
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
QLineEdit, QGroupBox, QTextEdit, QStyledItemDelegate, QMenu)
from PySide6.QtCore import Qt, QSize
from PySide6.QtGui import QAction, QFontMetrics
class MultiLineDelegate(QStyledItemDelegate):
"""多行文本委托,支持自动调整行高"""
def __init__(self, parent=None):
super().__init__(parent)
self.min_height = 30 # 最小行高
self.max_height = 200 # 最大行高
def paint(self, painter, option, index):
"""自定义绘制,支持多行文本"""
# 保存原始选项
opt = option
# 获取文本内容
text = index.data(Qt.DisplayRole)
if text is None:
text = ""
# 设置文本换行
text = str(text)
# 计算文本高度
metrics = QFontMetrics(option.font)
rect = option.rect
# 计算需要的行数
lines = text.count('\n') + 1
line_height = metrics.lineSpacing()
text_height = lines * line_height + 10 # 添加一些边距
# 限制高度在最小和最大值之间
if text_height < self.min_height:
text_height = self.min_height
elif text_height > self.max_height:
text_height = self.max_height
# 调整绘制区域高度
opt.rect.setHeight(text_height)
# 调用父类绘制方法
super().paint(painter, opt, index)
def sizeHint(self, option, index):
"""返回建议的单元格大小"""
# 获取文本内容
text = index.data(Qt.DisplayRole)
if text is None:
text = ""
text = str(text)
# 计算文本尺寸
metrics = QFontMetrics(option.font)
# 计算行数
lines = text.count('\n') + 1
line_height = metrics.lineSpacing()
text_height = lines * line_height + 10 # 添加边距
# 计算文本宽度(考虑换行)
if '\n' in text:
# 多行文本,计算最长行的宽度
max_width = 0
for line in text.split('\n'):
line_width = metrics.horizontalAdvance(line) + 20
max_width = max(max_width, line_width)
else:
# 单行文本
max_width = metrics.horizontalAdvance(text) + 20
# 限制高度
if text_height < self.min_height:
text_height = self.min_height
elif text_height > self.max_height:
text_height = self.max_height
# 最小宽度设置为100像素
max_width = max(max_width, 100)
return QSize(max_width, text_height)
class SQLiteViewer(QMainWindow):
@@ -148,6 +230,24 @@ class SQLiteViewer(QMainWindow):
right_layout.addWidget(QLabel("表数据:"))
self.data_table = QTableWidget()
self.data_table.setAlternatingRowColors(True)
# 设置表格支持多行内容和可调整列宽
self.data_table.setItemDelegate(MultiLineDelegate(self.data_table))
self.data_table.setWordWrap(True) # 启用自动换行
self.data_table.setTextElideMode(Qt.ElideNone) # 不省略文本
# 设置列头支持拖拽调整大小
header = self.data_table.horizontalHeader()
header.setSectionsMovable(True) # 允许移动列
header.setStretchLastSection(False) # 不自动拉伸最后一列
# 设置行头自动调整高度
self.data_table.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
# 添加右键菜单支持
self.data_table.setContextMenuPolicy(Qt.CustomContextMenu)
self.data_table.customContextMenuRequested.connect(self.show_table_context_menu)
right_layout.addWidget(self.data_table)
splitter.addWidget(left_widget)
@@ -273,11 +373,37 @@ class SQLiteViewer(QMainWindow):
# 填充数据
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 "")
# 处理None值和格式化数据
if cell_data is None:
display_text = ""
elif isinstance(cell_data, (int, float)):
# 数字类型保持原样,但转换为字符串
display_text = str(cell_data)
else:
# 文本类型,保留原始格式,包括换行符
display_text = str(cell_data)
item = QTableWidgetItem(display_text)
item.setToolTip(display_text) # 添加悬停提示
self.data_table.setItem(row_idx, col_idx, item)
# 调整列宽
self.data_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
# 调整列宽 - 使用Interactive模式让用户可以手动调整
header = self.data_table.horizontalHeader()
header.setSectionResizeMode(QHeaderView.Interactive)
# 设置初始列宽为内容宽度,但有最大宽度限制
for col in range(len(column_names)):
# 计算该列内容的最大宽度
max_width = 0
for row in range(min(100, len(data))): # 只检查前100行避免性能问题
item = self.data_table.item(row, col)
if item and item.text():
text_width = self.data_table.fontMetrics().horizontalAdvance(item.text()) + 20
max_width = max(max_width, text_width)
# 设置列宽最小100像素最大400像素
column_width = min(max(max_width, 100), 400)
self.data_table.setColumnWidth(col, column_width)
logger.info(f"加载表 {table_name} 数据完成,共 {len(data)}")
self.status_bar.showMessage(f"{table_name}: {len(data)} 行数据")
@@ -363,11 +489,37 @@ class SQLiteViewer(QMainWindow):
# 填充筛选后的数据
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 "")
# 处理None值和格式化数据
if cell_data is None:
display_text = ""
elif isinstance(cell_data, (int, float)):
# 数字类型保持原样,但转换为字符串
display_text = str(cell_data)
else:
# 文本类型,保留原始格式,包括换行符
display_text = str(cell_data)
item = QTableWidgetItem(display_text)
item.setToolTip(display_text) # 添加悬停提示
self.data_table.setItem(row_idx, col_idx, item)
# 调整列宽
self.data_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
# 调整列宽 - 使用Interactive模式让用户可以手动调整
header = self.data_table.horizontalHeader()
header.setSectionResizeMode(QHeaderView.Interactive)
# 设置初始列宽为内容宽度,但有最大宽度限制
for col in range(len(column_names)):
# 计算该列内容的最大宽度
max_width = 0
for row in range(min(100, len(data))): # 只检查前100行避免性能问题
item = self.data_table.item(row, col)
if item and item.text():
text_width = self.data_table.fontMetrics().horizontalAdvance(item.text()) + 20
max_width = max(max_width, text_width)
# 设置列宽最小100像素最大400像素
column_width = min(max(max_width, 100), 400)
self.data_table.setColumnWidth(col, column_width)
# 启用清除筛选按钮
self.clear_filter_button.setEnabled(True)
@@ -413,6 +565,83 @@ class SQLiteViewer(QMainWindow):
else:
self.load_table_list()
def show_table_context_menu(self, position):
"""显示表格右键菜单"""
menu = QMenu()
# 添加菜单项
auto_resize_action = menu.addAction("自动调整列宽")
auto_resize_rows_action = menu.addAction("自动调整行高")
copy_action = menu.addAction("复制选中内容")
# 显示菜单
action = menu.exec(self.data_table.mapToGlobal(position))
if action == auto_resize_action:
self.auto_resize_columns()
elif action == auto_resize_rows_action:
self.auto_resize_rows()
elif action == copy_action:
self.copy_selected_content()
def auto_resize_columns(self):
"""自动调整所有列宽"""
logger.info("自动调整列宽")
# 遍历所有列
for col in range(self.data_table.columnCount()):
# 计算该列内容的最大宽度
max_width = 0
for row in range(min(100, self.data_table.rowCount())): # 只检查前100行
item = self.data_table.item(row, col)
if item and item.text():
text_width = self.data_table.fontMetrics().horizontalAdvance(item.text()) + 20
max_width = max(max_width, text_width)
# 设置列宽最小100像素最大500像素
column_width = min(max(max_width, 100), 500)
self.data_table.setColumnWidth(col, column_width)
self.status_bar.showMessage("已自动调整列宽")
def auto_resize_rows(self):
"""自动调整所有行高"""
logger.info("自动调整行高")
# 触发重新计算行高
self.data_table.resizeRowsToContents()
self.status_bar.showMessage("已自动调整行高")
def copy_selected_content(self):
"""复制选中的内容"""
selected_items = self.data_table.selectedItems()
if not selected_items:
return
# 按行列组织数据
rows = {}
for item in selected_items:
row = item.row()
col = item.column()
if row not in rows:
rows[row] = {}
rows[row][col] = item.text()
# 构建复制的文本
text_lines = []
for row in sorted(rows.keys()):
row_data = []
for col in sorted(rows[row].keys()):
row_data.append(rows[row][col])
text_lines.append('\t'.join(row_data))
# 复制到剪贴板
clipboard = QApplication.clipboard()
clipboard.setText('\n'.join(text_lines))
self.status_bar.showMessage(f"已复制 {len(selected_items)} 个单元格的内容")
def closeEvent(self, event):
"""关闭事件处理"""
logger.info("关闭应用程序")