更新了sqlite视图的功能
This commit is contained in:
@@ -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("关闭应用程序")
|
||||
|
||||
Reference in New Issue
Block a user