Пользовательский виджет редактора элементов делегата, отображаемый не в том месте - PullRequest
1 голос
/ 24 сентября 2019

Я пытаюсь использовать пользовательский делегат для вызова пользовательских редакторов даты и времени для использования внутри qtreeview.Все, что я прочитал, говорит о том, что для того, чтобы виджет редактора отображался в правильном месте, поверх текущего индекса, вы повторно реализуете метод updateEditorGeometry и используете editor.setGeometry (option.rect) для установки позиции.

Я не могу понять это, хотя.Вот что у меня так далеко.При двойном щелчке по столбцу 1 в левом верхнем углу экрана отображается редактор.

from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtCore import Qt
import datetime


class NspDateEdit(QtWidgets.QWidget):

    editingFinished = QtCore.pyqtSignal()

    def __init__(self, parent=None):
        super(NspDateEdit, self).__init__(parent)
        # self.resize(137, 25)
        # self.setMaximumSize(QtCore.QSize(120, 25))

        self.date_edit = QtWidgets.QDateEdit()
        self.date_edit.setCalendarPopup(True)
        self.check_box = QtWidgets.QCheckBox()
        self.check_box.setText("")

        self.main_layout = QtWidgets.QHBoxLayout()
        self.main_layout.addWidget(self.date_edit)
        self.main_layout.addWidget(self.check_box)
        self.main_layout.setContentsMargins(2, 0, 0, 0)
        self.setLayout(self.main_layout)

        self.check_box.stateChanged.connect(self._on_state_change)
        self.date_edit.editingFinished.connect(self._on_editingFinished)

        self.setDate(None)

    def _on_state_change(self):
        state = self.checkState()
        if state:
            current_date = self.date_edit.date()
            current_date_py = current_date.toPyDate()
            current_date_str = current_date_py.strftime('%Y-%m-%d')
            if current_date_str == '2000-01-01':
                new_date = datetime.datetime.now().date()
            else:
                new_date = current_date_py
            self.setDate(new_date)
        else:
            self.setDate(None)
        self.editingFinished.emit()

    def _on_editingFinished(self):
        self.editingFinished.emit()

    def checkState(self):
        state = self.check_box.checkState()
        if state == Qt.Checked:
            return True
        else:
            return False

    def setDate(self, val):
        self.date_edit.setEnabled(True)
        if val is None:
            self.check_box.setCheckState(Qt.Unchecked)
            self.date_edit.setEnabled(False)
        else:
            self.date_edit.setDate(val)
            self.check_box.setCheckState(Qt.Checked)

    def date(self):
        if self.checkState():
            return self.date_edit.date().toPyDate()
        else:
            return None


class NspAbstractItemDelegate(QtWidgets.QStyledItemDelegate):

    def __init__(self):
        super(NspAbstractItemDelegate, self).__init__()

    def createEditor(self, parent, option, index):
        editor = NspDateEdit()
        editor.setWindowFlags(QtCore.Qt.Popup)
        return editor

    def setEditorData(self, editor, index):
        data = index.data()
        formatted = datetime.datetime.strptime(data, '%m/%d/%y').date()
        editor.setDate(formatted)

    def setModelData(self, editor, model, index):
        data = editor.date()
        txt = data.strftime('%m/%d/%y')
        model.setData(index, txt)

    def updateEditorGeometry(self, editor, option, index):
        editor.setGeometry(option.rect)


class testTreeview(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(testTreeview, self).__init__(parent)
        self.mainTree = QtWidgets.QTreeView()

        self.testButton = QtWidgets.QPushButton()

        self.lo = QtWidgets.QVBoxLayout()
        self.lo.addWidget(self.mainTree)
        self.lo.addWidget(self.testButton)
        self.setLayout(self.lo)

        self.model = QtGui.QStandardItemModel()
        self.mainTree.setModel(self.model)

        self.populate()

        self.testButton.clicked.connect(self._on_clicked_button)
        self.mainTree.setItemDelegateForColumn(0, NspAbstractItemDelegate())

    def _on_clicked_button(self):
        self.mainTree.edit(self.mainTree.currentIndex())  #, QtWidgets.QAbstractItemView.EditTrigger(), None)

    def populate(self):

        row = [QtGui.QStandardItem('05/12/15'), QtGui.QStandardItem('07/13/18'), ]
        row2 = [QtGui.QStandardItem('12/21/21'), QtGui.QStandardItem('11/05/17'), ]
        all_rows = list(row)
        all_rows.extend(row2)
        for item in all_rows:
            item.setEditable(True)
        root = self.model.invisibleRootItem()
        root.appendRow(row)
        root.appendRow(row2)


if __name__ == "__main__":
    from PyQt5 import QtCore, QtGui, QtWidgets

    app = QtWidgets.QApplication([])
    volume = testTreeview()
    volume.show()
    app.exec_()

1 Ответ

1 голос
/ 24 сентября 2019

Когда вы устанавливаете флаг Qt :: Popup, позиция виджета должна быть абсолютной, то есть относительно экрана, но option.rect является QRect относительно окна просмотра, поэтому решение состоит в том, чтобы преобразовать этот относительныйпуть к абсолюту с использованием mapToGlobal, но для этого вы должны передать родительский объект в редактор

def createEditor(self, parent, option, index):
    editor = NspDateEdit(<b>parent</b>)
    editor.setWindowFlags(QtCore.Qt.Popup)
    return editor

# ...

def updateEditorGeometry(self, editor, option, index):
    <b>r = QtCore.QRect(option.rect)
    if editor.windowFlags() & QtCore.Qt.Popup and editor.parent() is not None:
        r.setTopLeft(editor.parent().mapToGlobal(r.topLeft()))
    editor.setGeometry(r)</b>
...