第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號安全,請及時(shí)綁定郵箱和手機(jī)立即綁定
已解決430363個(gè)問題,去搜搜看,總會(huì)有你想問的

在 QtreeWidget 中拖動(dòng) Qframe

在 QtreeWidget 中拖動(dòng) Qframe

陪伴而非守候 2023-07-27 14:12:32
例如Qt 的項(xiàng)目視圖使用內(nèi)部 application/x-qabstractitemmodeldatalist MIME 類型傳遞項(xiàng)目我怎樣才能獲得QAbstractItemView.model()包含多個(gè) QtWidgets 的 QFrame 。底線問題是:如何在 QTreeWidget 中移動(dòng)包含多個(gè) QtWidget 的 QFrame。請參閱下面的示例代碼: 按按鈕添加子級并嘗試將它們拖動(dòng)到其他子級或第一級樹層次結(jié)構(gòu)的父級之間from PyQt5.QtWidgets import (QTreeWidget, QTreeWidgetItem, QPushButton, QLabel, QDialog, QVBoxLayout, QApplication, QLineEdit)from PyQt5.QtWidgets import (QPushButton, QDialog, QTreeWidget,? ? ? ? ? ? ? ? ? ? ? ? ? ? ?QTreeWidgetItem, QVBoxLayout,? ? ? ? ? ? ? ? ? ? ? ? ? ? ?QHBoxLayout, QFrame, QLabel, QComboBox,? ? ? ? ? ? ? ? ? ? ? ? ? ? ?QApplication)class Ui_MainWindow(object):? ? def setupUi(self, MainWindow):? ? ? ? self.index=0? ? ? ? MainWindow.setObjectName("MainWindow")? ? ? ? MainWindow.resize(800, 600)? ? ? ? self.centralwidget = QtWidgets.QWidget(MainWindow)? ? ? ? self.centralwidget.setObjectName("centralwidget")? ? ? ? self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)? ? ? ? self.gridLayout.setObjectName("gridLayout")? ? ? ? self.treeWidget = QtWidgets.QTreeWidget(self.centralwidget)? ? ? ? self.treeWidget.setObjectName("treeWidget")? ? ? ? self.treeWidget.setFrameShape(QtWidgets.QFrame.StyledPanel)? ? ? ? self.treeWidget.setFrameShadow(QtWidgets.QFrame.Sunken)? ? ? ? self.treeWidget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)? ? ? ? self.treeWidget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)? ? ? ? self.treeWidget.setAutoScrollMargin(10)? ? ? ??? ? ? ? self.treeWidget.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
查看完整描述

1 回答

?
瀟瀟雨雨

TA貢獻(xiàn)1833條經(jīng)驗(yàn) 獲得超4個(gè)贊

前提

OP 想要實(shí)現(xiàn)的目標(biāo)并不容易。索引小部件與底層模型無關(guān)(它們不應(yīng)該?。?yàn)樗鼈儍H與項(xiàng)目視圖相關(guān)。由于拖放操作作用于 QMimeData 對象及其內(nèi)容(序列化為字節(jié)數(shù)據(jù)),因此沒有直接方法從放置事件訪問索引小部件。這意味著 d&d 操作僅作用于項(xiàng)目模型,而索引小部件將被完全忽略。

但是,這還不夠。
即使您可以獲得對索引小部件的字節(jié)引用,一旦替換或刪除索引小部件,這些小部件總是會(huì)被刪除:主要問題與以下setItemWidget()相同setIndexWidget()

如果將索引小部件 A 替換為索引小部件 B,則索引小部件 A 將被刪除。

源代碼執(zhí)行以下操作:

void QAbstractItemView::setIndexWidget(const QModelIndex &index, QWidget *widget)

{

? ? # ...

? ? if (QWidget *oldWidget = indexWidget(index)) {

? ? ? ? d->persistent.remove(oldWidget);

? ? ? ? d->removeEditor(oldWidget);

? ? ? ? oldWidget->removeEventFilter(this);

? ? ? ? oldWidget->deleteLater();

? ? }

? ? # ...


}

結(jié)果是,每當(dāng)設(shè)置索引小部件(或刪除索引)時(shí),相關(guān)索引小部件就會(huì)被刪除。從 PyQt 方面來看,我們無法控制這一點(diǎn),除非我們徹底實(shí)現(xiàn)相關(guān)項(xiàng)目視圖類(并且......祝你好運(yùn))。

關(guān)于樹模型的注意事項(xiàng)

Qt 有自己的方式來支持InternalMove樹模型的標(biāo)志。在下面的解釋中,我假設(shè)拖/放操作總是在SingleSelection為屬性設(shè)置的模式下發(fā)生selectionMode(),并且dragDropMode()設(shè)置為默認(rèn)值InternalMove。如果您想提供具有擴(kuò)展選擇功能的高級拖放模式的實(shí)現(xiàn),您必須找到自己的實(shí)現(xiàn)(可能通過研究 QAbstractItemView 和 QTreeView 的源代碼)。

[解決辦法] 解決方案

不過,有一個(gè)黑客。
唯一被deleteLater()調(diào)用的小部件是使用 集設(shè)置的實(shí)際小部件setIndexWidget(),而不是其子部件。
因此,在這些情況下,要添加對索引小部件拖放的支持,唯一簡單的解決方案是始終添加帶有容器父小部件的索引小部件,并在替換/刪除索引小部件之前從容器中刪除實(shí)際小部件,然后創(chuàng)建在新索引/項(xiàng)目上使用setIndexWidget()(或)之前,實(shí)際小部件的新容器setItemWidget(),可能使用遞歸函數(shù)來確保保留子引用。

這確保了實(shí)際顯示的(先前的)索引小部件不會(huì)刪除,因?yàn)橹挥兴娜萜鲿?huì)被刪除,從而允許我們?yōu)榱硪粋€(gè)索引設(shè)置該小部件。

幸運(yùn)的是,QTreeWidget 可以更輕松地訪問這些項(xiàng)目,因?yàn)檫@些項(xiàng)目是實(shí)際且持久的對象,即使在移動(dòng)后也可以對其進(jìn)行跟蹤(與 QTreeView 中的 QModelIndex 發(fā)生的情況不同)。

在下面的示例中(使用評論中提供的信息進(jìn)行更新),我正在創(chuàng)建頂級項(xiàng)目并僅允許在第一級上放置。這是一個(gè)基本示例,您可能想要添加一些功能:例如,如果項(xiàng)目組合未設(shè)置為“重復(fù)”,則防止掉落,甚至創(chuàng)建一個(gè)已設(shè)置為“重復(fù)”的新父項(xiàng)目并手動(dòng)添加子項(xiàng)目。

class WidgetDragTree(QtWidgets.QTreeWidget):

? ? def __init__(self, *args, **kwargs):

? ? ? ? super().__init__(*args, **kwargs)

? ? ? ? self.header().hide()

? ? ? ? self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)

? ? ? ? self.setDragEnabled(True)

? ? ? ? self.setDefaultDropAction(QtCore.Qt.MoveAction)


? ? def addFrame(self):

? ? ? ? item = QtWidgets.QTreeWidgetItem()

? ? ? ? self.addTopLevelItem(item)

? ? ? ? item.setExpanded(True)


? ? ? ? # create the "virtual" container; use 0 contents margins for the layout?

? ? ? ? # to avoid unnecessary padding around the widget

? ? ? ? container = QtWidgets.QWidget(self)

? ? ? ? layout = QtWidgets.QHBoxLayout(container)

? ? ? ? layout.setContentsMargins(0, 0, 0, 0)


? ? ? ? # the *actual* widget that we want to add

? ? ? ? widget = QtWidgets.QFrame()

? ? ? ? layout.addWidget(widget)

? ? ? ? frameLayout = QtWidgets.QHBoxLayout(widget)


? ? ? ? widget.label = QtWidgets.QLabel('#{}'.format(self.topLevelItemCount()))

? ? ? ? frameLayout.addWidget(widget.label)

? ? ? ? combo = QtWidgets.QComboBox()

? ? ? ? frameLayout.addWidget(combo)

? ? ? ? combo.addItems(['Select process', 'CC', 'VV', 'Repeat'])


? ? ? ? # add a spacer at the end to keep widgets at their minimum required size

? ? ? ? frameLayout.addStretch()


? ? ? ? # the widget has to be added AT THE END, otherwise its sizeHint won't be?

? ? ? ? # correctly considered for the index

? ? ? ? self.setItemWidget(item, 0, container)


? ? def delFrame(self):

? ? ? ? for index in self.selectedIndexes():

? ? ? ? ? ? item = self.itemFromIndex(index)

? ? ? ? ? ? if item.parent():

? ? ? ? ? ? ? ? item.parent().takeChild(item)

? ? ? ? ? ? else:

? ? ? ? ? ? ? ? self.takeTopLevelItem(index.row())


? ? def updateLabels(self, parent=None):

? ? ? ? if parent is None:

? ? ? ? ? ? parent = self.rootIndex()

? ? ? ? for row in range(self.model().rowCount(parent)):

? ? ? ? ? ? index = self.model().index(row, 0, parent)

? ? ? ? ? ? container = self.indexWidget(index)

? ? ? ? ? ? if container and container.layout():

? ? ? ? ? ? ? ? widget = container.layout().itemAt(0).widget()

? ? ? ? ? ? ? ? try:

? ? ? ? ? ? ? ? ? ? widget.label.setText('#{}'.format(row + 1))

? ? ? ? ? ? ? ? except Exception as e:

? ? ? ? ? ? ? ? ? ? print(e)

? ? ? ? ? ? # if the index has children, call updateLabels recursively

? ? ? ? ? ? if self.model().rowCount(index):

? ? ? ? ? ? ? ? self.updateLabels(index)


? ? def dragMoveEvent(self, event):

? ? ? ? super().dragMoveEvent(event)

? ? ? ? if self.dropIndicatorPosition() == self.OnViewport:

? ? ? ? ? ? # do not accept drop on the viewport

? ? ? ? ? ? event.ignore()

? ? ? ? elif self.dropIndicatorPosition() == self.OnItem:

? ? ? ? ? ? # do not accept drop beyond the first level

? ? ? ? ? ? target = self.indexAt(event.pos())

? ? ? ? ? ? if target.parent().isValid():

? ? ? ? ? ? ? ? event.ignore()


? ? def getIndexes(self, indexList):

? ? ? ? # get indexes recursively using a set (to get unique indexes only)

? ? ? ? indexes = set(indexList)

? ? ? ? for index in indexList:

? ? ? ? ? ? childIndexes = []

? ? ? ? ? ? for row in range(self.model().rowCount(index)):

? ? ? ? ? ? ? ? childIndexes.append(self.model().index(row, 0, index))

? ? ? ? ? ? if childIndexes:

? ? ? ? ? ? ? ? indexes |= self.getIndexes(childIndexes)

? ? ? ? return indexes


? ? def dropEvent(self, event):

? ? ? ? widgets = []

? ? ? ? # remove the actual widget from the container layout and store it along?

? ? ? ? # with the tree item

? ? ? ? for index in self.getIndexes(self.selectedIndexes()):

? ? ? ? ? ? item = self.itemFromIndex(index)

? ? ? ? ? ? container = self.indexWidget(index)

? ? ? ? ? ? if container and container.layout():

? ? ? ? ? ? ? ? widget = container.layout().itemAt(0).widget()

? ? ? ? ? ? ? ? if widget:

? ? ? ? ? ? ? ? ? ? container.layout().removeWidget(widget)

? ? ? ? ? ? ? ? ? ? widgets.append((item, widget))


? ? ? ? super().dropEvent(event)


? ? ? ? # restore the widgets in a new container

? ? ? ? for item, widget in widgets:

? ? ? ? ? ? container = QtWidgets.QWidget(self)

? ? ? ? ? ? layout = QtWidgets.QHBoxLayout(container)

? ? ? ? ? ? layout.setContentsMargins(0, 0, 0, 0)

? ? ? ? ? ? layout.addWidget(widget)

? ? ? ? ? ? self.setItemWidget(item, 0, container)

? ? ? ? ? ? index = self.indexFromItem(item)

? ? ? ? ? ? if index.parent().isValid():

? ? ? ? ? ? ? ? self.expand(index.parent())


? ? ? ? # force the update of the item layouts

? ? ? ? self.updateGeometries()


? ? ? ? # update the widget labels

? ? ? ? self.updateLabels()



class Test(QtWidgets.QWidget):

? ? def __init__(self, parent=None):

? ? ? ? super().__init__(parent)

? ? ? ? layout = QtWidgets.QVBoxLayout(self)

? ? ? ? btnLayout = QtWidgets.QHBoxLayout()

? ? ? ? layout.addLayout(btnLayout)

? ? ? ? self.addBtn = QtWidgets.QPushButton('+')

? ? ? ? btnLayout.addWidget(self.addBtn)

? ? ? ? self.delBtn = QtWidgets.QPushButton('-')

? ? ? ? btnLayout.addWidget(self.delBtn)

? ? ? ? self.tree = WidgetDragTree()

? ? ? ? layout.addWidget(self.tree)


? ? ? ? self.addBtn.clicked.connect(self.tree.addFrame)

? ? ? ? self.delBtn.clicked.connect(self.tree.delFrame)

更新(Windows 修復(fù))

似乎存在一個(gè)可能的錯(cuò)誤,該錯(cuò)誤發(fā)生在 Windows 中(至少在 Qt 5.13 和 Windows 10 中):單擊某個(gè)項(xiàng)目然后單擊組合框后,樹小部件會(huì)收到一堆mouseMoveEvent觸發(fā)拖動(dòng)的信息。不幸的是,我無法進(jìn)行進(jìn)一步的測試,但這是一個(gè)可能的解決方法:


class WidgetDragTree(QtWidgets.QTreeWidget):

? ? # ...

? ? def mousePressEvent(self, event):

? ? ? ? # fix for [unknown] bug on windows where clicking on a combo child of an?

? ? ? ? # item widget also sends back some mouseMoveEvents

? ? ? ? item = self.itemAt(event.pos())

? ? ? ? if item and self.itemWidget(item, 0):

? ? ? ? ? ? # if the item has a widget, make a list of child combo boxes

? ? ? ? ? ? combos = self.itemWidget(item, 0).findChildren(QtWidgets.QComboBox)

? ? ? ? ? ? underMouseWidget = QtWidgets.QApplication.widgetAt(event.globalPos())

? ? ? ? ? ? if underMouseWidget in combos:

? ? ? ? ? ? ? ? return

? ? ? ? super().mousePressEvent(event)


查看完整回答
反對 回復(fù) 2023-07-27
  • 1 回答
  • 0 關(guān)注
  • 227 瀏覽
慕課專欄
更多

添加回答

舉報(bào)

0/150
提交
取消
微信客服

購課補(bǔ)貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動(dòng)學(xué)習(xí)伙伴

公眾號

掃描二維碼
關(guān)注慕課網(wǎng)微信公眾號