亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

帶有瀏覽按鈕的自定義項目委托的 QTreeView

帶有瀏覽按鈕的自定義項目委托的 QTreeView

Cats萌萌 2022-07-26 15:59:32
使用 Qt5 框架(通過 Python 的 pyQt5),我需要創建一個帶有參數 - 值列的 QTreeView 小部件,其中某些行的值項必須具有內部“瀏覽”按鈕以打開文件瀏覽對話框并放置選定的文件到相應值的字段中。閱讀關于項目委托的 Qt 手冊,我整理了以下代碼:自定義 BrowseEdit 類(QLineEdit + Browse 操作)class BrowseEdit(QtWidgets.QLineEdit):    def __init__(self, contents='', filefilters=None,        btnicon=None, btnposition=None,        opendialogtitle=None, opendialogdir=None, parent=None):        super().__init__(contents, parent)        self.filefilters = filefilters or _('All files (*.*)')        self.btnicon = btnicon or 'folder-2.png'        self.btnposition = btnposition or QtWidgets.QLineEdit.TrailingPosition        self.opendialogtitle = opendialogtitle or _('Select file')        self.opendialogdir = opendialogdir or os.getcwd()        self.reset_action()    def _clear_actions(self):        for act_ in self.actions():            self.removeAction(act_)    def reset_action(self):        self._clear_actions()        self.btnaction = QtWidgets.QAction(QtGui.QIcon(f"{ICONFOLDER}/{self.btnicon}"), '')        self.btnaction.triggered.connect(self.on_btnaction)        self.addAction(self.btnaction, self.btnposition)        #self.show()    @QtCore.pyqtSlot()    def on_btnaction(self):        selected_path = QtWidgets.QFileDialog.getOpenFileName(self.window(), self.opendialogtitle, self.opendialogdir, self.filefilters)        if not selected_path[0]: return        selected_path = selected_path[0].replace('/', os.sep)        # THIS CAUSES ERROR ('self' GETS DELETED BEFORE THIS LINE!)        self.setText(selected_path)QTreeView 的自定義項目委托:class BrowseEditDelegate(QtWidgets.QStyledItemDelegate):    def __init__(self, model_indices=None, thisparent=None,                 **browse_edit_kwargs):        super().__init__(thisparent)        self.model_indices = model_indices        self.editor = BrowseEdit(**browse_edit_kwargs)          self.editor.setFrame(False)      
查看完整描述

1 回答

?
侃侃爾雅

TA貢獻1801條經驗 獲得超16個贊

每個代表不能只有一個唯一的編輯器,原因有兩個:

  1. 可能有更多的編輯器的活動實例(使用 打開openPersistentEditor),例如一個表,其中一列的每一行都有一個組合框。

  2. 每次編輯器將其數據提交給模型時,如果它不是持久編輯器,它就會被銷毀??紤]當一個 Qt 對象被分配給一個 Python 變量/屬性時,它實際上是一個指向由 Qt 創建的底層 C++ 對象的指針。這意味著雖然self.editor仍然作為 python 對象存在,但它指向一個在編輯器被委托關閉時實際刪除的對象。

正如函數名所說,createEditor() 創建一個編輯器,所以解決方法是每次createEditor()調用都創建一個新實例。

更新

但是,這里有一個重要問題:一旦您打開對話框,委托編輯器就會失去焦點。對于一個項目視圖,這與單擊另一個項目(更改焦點)相同,這將導致數據提交和編輯器破壞。

“簡單”的解決方案是在要打開對話框時阻止委托信號(最重要的closeEditor()是會調用),然后再解除阻止。destroyEditor()

class BrowseEdit(QtWidgets.QLineEdit):

    @QtCore.pyqtSlot()

    def on_btnaction(self):

        self.delegate.blockSignals(True)

        selected_path = QtWidgets.QFileDialog.getOpenFileName(self.window(), self.opendialogtitle, self.opendialogdir, self.filefilters)

        self.delegate.blockSignals(False)

        if not selected_path[0]: return

        selected_path = selected_path[0].replace('/', os.sep)

        # THIS CAUSES ERROR ('self' GETS DELETED BEFORE THIS LINE!)

        self.setText(selected_path)



class BrowseEditDelegate(QtWidgets.QStyledItemDelegate):

    # ...

    def createEditor(self, parent: QtWidgets.QWidget, option: QtWidgets.QStyleOptionViewItem,

                    index: QtCore.QModelIndex) -> QtWidgets.QWidget:

        try:

            if self.model_indices and index in self.model_indices:

                editor = BrowseEdit(parent=parent)

                editor.delegate = self

                return editor

            else:

                return super().createEditor(parent, option, index)

        except Exception as err:

            print(err)

            return None

也就是說,這是一個hack。雖然它有效,但不能保證它會在未來版本的 Qt 中,當可能引入其他信號或它們的行為發生變化時。


更好更優雅的解決方案是創建一個在單擊瀏覽按鈕時調用的信號,然后項目視圖(或其任何父項)將負責瀏覽,如果文件對話框結果有效則設置數據并再次開始編輯該字段:



class BrowseEditDelegate(QtWidgets.QStyledItemDelegate):

    browseRequested = QtCore.pyqtSignal(QtCore.QModelIndex)

    # ...

    def createEditor(self, parent: QtWidgets.QWidget, option: QtWidgets.QStyleOptionViewItem,

                    index: QtCore.QModelIndex) -> QtWidgets.QWidget:

        try:

            if self.model_indices and index in self.model_indices:

                editor = BrowseEdit(parent=parent)

                editor.btnaction.triggered.connect(

                    lambda: self.browseRequested.emit(index))

                return editor

            else:

                return super().createEditor(parent, option, index)

        except Exception as err:

            print(err)

            return None



class Window(QtWidgets.QWidget):

    def __init__(self):

        # ...

        delegate = BrowseEditDelegate(indices)

        self.tv_plugins_3party.setItemDelegate(delegate)

        delegate.browseRequested.connect(self.browseRequested)


    def browseRequested(self, index):

        selected_path = QtWidgets.QFileDialog.getOpenFileName(self.window(), 'Select file', index.data())

        if selected_path[0]:

            self.model_plugins_3party.setData(index, selected_path[0])

        self.tv_plugins_3party.edit(index)


查看完整回答
反對 回復 2022-07-26
  • 1 回答
  • 0 關注
  • 204 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號