python - pyqt - 表格中的拖放行无法拖动单元格中的图像和小部件
问题描述
我使用了stackoverflow中的一个类,该类将拖放功能应用于表格行,但在拖放任何行时它不会保存从单元格中删除的图像我需要一些帮助来弄清楚如何修复代码以正确执行此操作!整个项目的代码与数据库:使用的数据库:https ://gofile.io/d/c0srQ0
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import (QApplication, QComboBox, QDialog,
QDialogButtonBox, QFormLayout, QGridLayout, QGroupBox, QHBoxLayout,
QLabel, QLineEdit, QMenu, QMenuBar, QPushButton, QSpinBox, QTextEdit,
QVBoxLayout, QStyledItemDelegate, QCheckBox, QTableWidgetItem, QAbstractItemView, QTableWidget)
import sqlite3
import random
import ast
#import qdarkstyle
class Main(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.cashed = []
self.setWindowTitle('Arabic Math Project')
self.setContentsMargins(20,20,20,20)
# create table:
#Options()
#self.setStyleSheet(qdarkstyle.load_stylesheet())
layout = QVBoxLayout()
self.searchBar = QtWidgets.QLineEdit()
self.searchBar.setAlignment(QtCore.Qt.AlignCenter)
self.searchBar.setPlaceholderText("search")
self.searchBar.textChanged.connect(self.startSearchThreading) #editingFinished()
#self.table = QtWidgets.QTableWidget()
self.table = TableWidgetDragRows()
conn = sqlite3.connect("math.db")
conn.text_factory = bytes
self.cur = conn.cursor()
data = self.cur.execute("select * from problems;").fetchall();conn.close()
self.dims = (lambda x: (len(x), len(x[0])))(data) #(rows number, col number)
[self.table.insertRow(i) for i in [i for i in range(self.dims[0])]]
[self.table.insertColumn(i) for i in [i for i in range(self.dims[1]+1)]]
#changing h-header names .
self.table.setHorizontalHeaderLabels(["Unique", "Image", "Test", "Year", "Lesson", "Page","Comment", "Options", "Options-advanced"])
for c in range(self.dims[1]):
for r in range(self.dims[0]):
if c!=1:self.table.setItem(r, c, QtWidgets.QTableWidgetItem(data[r][c].decode("utf-8")))
else:self.table.setCellWidget(r, c, self.getImage(data[r][c]))
for r in range(self.dims[0]):self.table.setCellWidget(r, self.dims[1], Options(ops=[data[r][self.dims[1]-1].decode("utf-8")], row=data[r]))
#table h & v auto - resizing .
header = self.table.horizontalHeader()
header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
header.setSectionResizeMode(2, QtWidgets.QHeaderView.Interactive)
header.setSectionResizeMode(7, QtWidgets.QHeaderView.Interactive)
headerv = self.table.verticalHeader()
headerv.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
#hide the options column cuz it doesn't concern user to see it.
self.table.setColumnHidden(7, True)
#layout setting up.
layout.addWidget(self.searchBar)
layout.addWidget(self.table)
self.setLayout(layout)
def getImage(self, image):
imageLabel = ScaledPixmapLabel()
pixmap = QtGui.QPixmap()
pixmap.loadFromData(image, "jpg")
imageLabel.setPixmap(pixmap)
return imageLabel
def startSearchThreading(self):
self.table.setRowCount(0)
self.update = updateTable(data= self.searchBar.text())
self.update.new_signal.connect(self.Search)
self.update.start()
def create(self):
self.createFormGroupBox()
return self.formGroupBox
@QtCore.pyqtSlot(int, int, bytes, bool, bytes)
def Search(self, r, c, d, newRowOrder, ops):
if newRowOrder:self.table.insertRow(self.table.rowCount()) # create new row.
if c!=1:self.table.setItem(r, c, QtWidgets.QTableWidgetItem(d.decode("utf-8")))
else:self.table.setCellWidget(r, c, self.getImage(d))
self.cashed.append(d)
if c ==self.dims[1]-1:
self.table.setCellWidget(r, self.dims[1], Options(ops=[ops.decode("utf-8")], row=self.cashed))
self.cashed = []
class updateTable(QtCore.QThread):
def __init__(self, parent=None, data=True):
super(QtCore.QThread, self).__init__()
self.data = data
new_signal = QtCore.pyqtSignal(int, int, bytes, bool, bytes)
def run(self):
currRow = 0
conn = sqlite3.connect("math.db")
conn.text_factory = bytes
self.cur = conn.cursor()
searched = self.cur.execute(" SELECT * FROM problems WHERE text LIKE ('%' || ? || '%') ", (self.data,)).fetchall()
dims = (lambda x: (len(x), len(x[0])))(searched) if searched!=[] else (0, 0)
for r in range(dims[0]):
for c in range(dims[1]):
self.new_signal.emit(r, c, searched[r][c], True if c==0 else False, searched[r][dims[1]-1])
class ScaledPixmapLabel(QtWidgets.QLabel):
def __init__(self):
super().__init__()
self.setScaledContents(True)
def paintEvent(self, event):
if self.pixmap():
pm = self.pixmap()
originalRatio = pm.width() / pm.height()
currentRatio = self.width() / self.height()
if originalRatio != currentRatio:
qp = QtGui.QPainter(self)
pm = self.pixmap().scaled(self.size(), QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
rect = QtCore.QRect(0, 0, pm.width(), pm.height())
rect.moveCenter(self.rect().center())
qp.drawPixmap(rect, pm)
return
super().paintEvent(event)
class Options(QtWidgets.QWidget):
def __init__(self, parent=None,row=[], ops=[]):
super(Options, self).__init__(parent)
self.i = random.randint(1, 100)
self.row = [j.decode("utf-8") if i!=1 else "image" for i,j in enumerate(row)]
self.formLayout = QFormLayout()
self.setStyleSheet(open("styles.css").read())
ops = ast.literal_eval(ops[0]) if ops!=[] else {}
for i,j in ops.items():
widget = {"lineedit": QLineEdit(), "combobox": QComboBox(), "spinbox": QSpinBox(), "checkbox": QCheckBox()}
title = j.get("title", "")
combos = j.get("combos", [])
if i == "lineedit":widget[i].setText(j.get("default", ""))
elif i == "combobox":widget[i].addItems(combos)
elif i == "checkbox":
widget[i].setCheckState((lambda x: {1:2, 2:2, 0:0}[x])(j.get("default", 0)))
widget[i].setText(j.get("text", ""))
elif i == "spinbox":
widget[i].setMaximum(j.get("max", 100))
widget[i].setMinimum(j.get("min", 0))
self.formLayout.addRow(QLabel(title), widget[i])
self.btn = QtWidgets.QPushButton("Submit")
self.btn.clicked.connect(self.do)
self.formLayout.addWidget(self.btn)
self.formLayout.setFormAlignment(QtCore.Qt.AlignVCenter)
self.setLayout(self.formLayout)
def do(self):
#[STOPED HERE]
heads, tails = [], []
for i in range(self.formLayout.count()):
w = self.formLayout.itemAt(i).widget()
if i%2==0:heads.append(w.text())
elif isinstance(w, QLineEdit):tails.append(w.text())
elif isinstance(w, QComboBox):tails.append(w.currentText())
elif isinstance(w, QSpinBox):tails.append(w.value())
elif isinstance(w, QCheckBox):tails.append(w.checkState())
values = {i:j for i,j in zip(heads, tails)}
print(self.row[:-1]+[values])
class TableWidgetDragRows(QTableWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setDragEnabled(True)
self.setAcceptDrops(True)
self.viewport().setAcceptDrops(True)
self.setDragDropOverwriteMode(False)
self.setDropIndicatorShown(True)
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
#self.setSelectionBehavior(QAbstractItemView.SelectRows)
self.setDragDropMode(QAbstractItemView.InternalMove)
def dropEvent(self, event: QDropEvent):
if not event.isAccepted() and event.source() == self:
drop_row = self.drop_on(event)
rows = sorted(set(item.row() for item in self.selectedItems()))
print(rows)
rows_to_move = [[QTableWidgetItem(self.item(row_index, column_index)) for column_index in range(self.columnCount())]
for row_index in rows]
for row_index in reversed(rows):
self.removeRow(row_index)
if row_index < drop_row:
drop_row -= 1
for row_index, data in enumerate(rows_to_move):
row_index += drop_row
self.insertRow(row_index)
for column_index, column_data in enumerate(data):
self.setItem(row_index, column_index, column_data)
event.accept()
for row_index in range(len(rows_to_move)):
self.item(drop_row + row_index, 0).setSelected(True)
self.item(drop_row + row_index, 1).setSelected(True)
super().dropEvent(event)
def drop_on(self, event):
index = self.indexAt(event.pos())
if not index.isValid():
return self.rowCount()
return index.row() + 1 if self.is_below(event.pos(), index) else index.row()
def is_below(self, pos, index):
rect = self.visualRect(index)
margin = 2
if pos.y() - rect.top() < margin:
return False
elif rect.bottom() - pos.y() < margin:
return True
# noinspection PyTypeChecker
return rect.contains(pos, True) and not (int(self.model().flags(index)) & Qt.ItemIsDropEnabled) and pos.y() >= rect.center().y()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
main = Main()
main.resize(600,600)
main.show()
app.exec_()
解决方案
您正在使用设置为单元格小部件的 QLabels 设置图像。设置单元格小部件只是表格 UI 使用的一个功能:它与底层模型完全无关,QTableWidgetItems 也对它们一无所知。因此,检查单元格是否设置了小部件并相应地移动该小部件完全取决于您。
半伪代码:
def dropEvent(self, event: QDropEvent):
if not event.isAccepted() and event.source() == self:
drop_row = self.drop_on(event)
rows = sorted(set(item.row() for item in self.selectedItems()))
print(rows)
rows_to_move = []
images = []
for row_index in rows:
items = []
for column_index in range(self.columnCount()):
items.append(QTableWidgetItem(
self.item(row_index, column_index)))
widget = self.cellWidget(row_index, column_index)
if isinstance(widget, ScaledPixmapLabel):
images.append((row_index, column_index, widget.pixmap()))
rows_to_move.append(items)
# ...
for row, column, pixmap in widgets:
self.setCellWidget(row + drop_row, column,
ScaledPixmapLabel(pixmap))
如您所见,我每次都重新创建了标签实例,这是因为当您设置单元格小部件时,表格将获得其所有权。虽然您可以重新设置小部件的父级,但结果可能是未定义的,因此最好基于像素图创建一个全新的实例。
提示:避免使用内联if/else
或for
循环:这样做绝对没有任何好处,唯一的结果是它会使您的代码的可读性大大降低
推荐阅读
- c - 在 C 中释放不是二元的树
- node.js - 如何使用 nodejs azure SDK 启动具有 azure VM 映像的新 VM?
- java - 迁移到java11后String的Concat速度降低
- database - Shop API 插件数据库问题 - Docker
- sql-server - 运行时错误:自动化错误,接口未知
- vba - 我需要一个宏来搜索和突出显示 word 文件中的单词和短语,以帮助我标记作业
- selenium-webdriver - 除了 JMeter 中的标签名称之外,更改自定义图形轴测量值
- php - PhpSpreadsheet 使用第一列值作为 db 列名从 .xls/.xlsx 在数据库中插入数据
- python - Django - 允许自定义用户编辑他们的个人资料
- python - 如何比较数据框中的两个列表列表?