QLineEdit in QTableWdiget cell not losing focus

时光怂恿深爱的人放手 提交于 2020-05-17 08:46:06

问题


I'm currently trying to make a graphical interface using PyQt5. Everything has been working fine until now (I have some experience in this after all), but I've been struggling with this problem for a few days now and I can't find a solution anywhere.

In my app I display a FigureCanvas from matplotlib (along with various other widgets) where I have a key event connected to a function which picks a point from a mouse click and saves this point in a QTableWidget. However, I want the user to be able to edit the y-value that was picked up using a validator, so I changed the second column from holding a QTableWidgetItem to a QLineEdit widget. Everything works except when I press enter after editing the QLineEdit widget in the table, the focus doesn't change as expected (I set the focus to go back to the canvas but the cell stays active with the cursor blinking).

In the same app I have another QLineEdit widget elsewhere in the layout which works as expected. When I press enter in this box, the function connected to its returnPressed signal sends the focus back to the canvas.

Can anyone please help me figure out what is going on?

Also, I don't understand why the app start up with focus being on the QLineEdit when I explicitly set focus to the canvas while creating the main frame. Do you know how to fix this?

I've stripped back my app to a minimal example pasted below and the behavior remains the same.

Just to summarize:

  1. I click on the canvas and press 'a' which call the function 'add_point'.
  2. I then click a point in the plot which creates a new row in the Widget and sets focus to the second column.
  3. I input whatever float value I want here and press Enter.
  4. The function 'print_val' is then called but the focus doesn't return to the canvas as expected. The cursor stays blinking in the table cell.

  5. When I edit the leftmost QLineEdit (outside the table) and hit Enter, the function 'func' is called and correctly sends the focus back to the canvas.

Why do the two instances of QLineEdit behave differently?

For reference, the output of the function 'print_val' gives me: TableWidget has focus? False Cell editor has focus? False

Thanks in advance!

import sys
from matplotlib.backends.backend_qt5agg import FigureCanvas, NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *



class GraphicInterface(QMainWindow):
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.setWindowTitle('Test')
        self._main = QWidget()
        self.setCentralWidget(self._main)

        # Create Toolbar and Menubar
        toolbar = QToolBar()
        toolbar.setMovable(False)
        toolbar_fontsize = QFont()
        toolbar_fontsize.setPointSize(14)

        quit_action = QAction("Close", self)
        quit_action.setFont(toolbar_fontsize)
        quit_action.setStatusTip("Quit the application")
        quit_action.triggered.connect(self.close)
        quit_action.setShortcut("ctrl+Q")
        toolbar.addAction(quit_action)


        # =============================================================
        # Start Layout:
        layout = QHBoxLayout(self._main)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)

        self.line_editor = QLineEdit("3")
        self.line_editor.setValidator(QIntValidator(1, 100))
        self.line_editor.returnPressed.connect(self.func)
        layout.addWidget(self.line_editor)

        # Create Table for Pixel Identifications:
        pixtab_layout = QVBoxLayout()
        label_pixtab_header = QLabel("Pixel Table\n")
        label_pixtab_header.setAlignment(Qt.AlignCenter)
        self.linetable = QTableWidget()
        self.linetable.verticalHeader().hide()
        self.linetable.setColumnCount(2)
        self.linetable.setHorizontalHeaderLabels(["Pixel", "Wavelength"])
        self.linetable.setColumnWidth(0, 80)
        self.linetable.setColumnWidth(1, 90)
        self.linetable.setFixedWidth(180)
        pixtab_layout.addWidget(label_pixtab_header)
        pixtab_layout.addWidget(self.linetable)
        layout.addLayout(pixtab_layout)

        # Create Figure Canvas:
        right_layout = QVBoxLayout()
        self.fig = Figure(figsize=(6, 6))
        self.canvas = FigureCanvas(self.fig)
        self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.canvas.updateGeometry()
        self.canvas.mpl_connect('key_press_event', self.on_key_press)
        self.canvas.setFocusPolicy(Qt.StrongFocus)
        self.canvas.setFocus()
        right_layout.addWidget(self.canvas)
        self.mpl_toolbar = NavigationToolbar(self.canvas, self)
        right_layout.addWidget(self.mpl_toolbar)
        layout.addLayout(right_layout)

        # Plot some random data:
        self.ax = self.fig.add_subplot(111)
        self.ax.plot([0, 1, 5, 10, 20], [-1, 2, -4, 5, -2])

    def on_key_press(self, e):
        if e.key == "a":
            self.add_point()

    def add_point(self):
        print("Select point...")
        point = self.fig.ginput(1)
        x0, _ = point[0]
        rowPosition = self.linetable.rowCount()
        self.linetable.insertRow(rowPosition)
        item = QTableWidgetItem("%.2f" % x0)
        item.setFlags(Qt.ItemIsEnabled)
        item.setBackground(QColor('lightgray'))
        self.linetable.setItem(rowPosition, 0, item)

        y_item = QLineEdit("")
        y_item.setValidator(QDoubleValidator())
        y_item.returnPressed.connect(self.print_val)
        self.linetable.setCellWidget(rowPosition, 1, y_item)
        self.linetable.cellWidget(rowPosition, 1).setFocus()

    def print_val(self):
        rowPosition = self.linetable.rowCount()
        editor = self.linetable.cellWidget(rowPosition-1, 1)
        y_in = float(editor.text())
        print(" Table value: %.2f" % y_in)
        # and set focus back to canvas:
        self.canvas.setFocus()
        # Check which widget has focus:
        focus_widget = self.focusWidget()
        print(focus_widget.__class__)
        print("TableWidget has focus?  %r" % self.linetable.hasFocus())
        print("Cell editor has focus?  %r" % editor.hasFocus())

    def func(self):
        # dome some stuff
        print(self.line_editor.text())
        # and set focus back to canvas:
        self.canvas.setFocus()


if __name__ == '__main__':
    # Launch App:
    qapp = QApplication(sys.argv)
    app = GraphicInterface()
    app.show()
    qapp.exec_()

回答1:


If you want to validate the information of a QTableWidget it is not necessary to insert a QLineEdit but use the one created by the delegate and set a QValidator there:

class ValidatorDelegate(QtWidgets.QStyledItemDelegate):
    def createEditor(self, parent, option, index):
        editor = super(ValidatorDelegate, self).createEditor(parent, option, index)
        if isinstance(editor, QtWidgets.QLineEdit):
            editor.setValidator(QtGui.QDoubleValidator(editor))
        return editor

And then open the editor with the edit method of the view, to change the focus you can use the delegate's closeEditor signal:

import sys
from matplotlib.backends.backend_qt5agg import (
    FigureCanvas,
    NavigationToolbar2QT as NavigationToolbar,
)
from matplotlib.figure import Figure


from PyQt5 import QtCore, QtGui, QtWidgets


class ValidatorDelegate(QtWidgets.QStyledItemDelegate):
    def createEditor(self, parent, option, index):
        editor = super(ValidatorDelegate, self).createEditor(parent, option, index)
        if isinstance(editor, QtWidgets.QLineEdit):
            editor.setValidator(QtGui.QDoubleValidator(editor))
        return editor


class GraphicInterface(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(GraphicInterface, self).__init__(parent)
        self.setWindowTitle("Test")
        self._main = QtWidgets.QWidget()
        self.setCentralWidget(self._main)

        # Create Toolbar and Menubar
        toolbar = QtWidgets.QToolBar(movable=False)
        toolbar_fontsize = QtGui.QFont()
        toolbar_fontsize.setPointSize(14)

        quit_action = QtWidgets.QAction("Close", self)
        quit_action.setFont(toolbar_fontsize)
        quit_action.setStatusTip("Quit the application")
        quit_action.triggered.connect(self.close)
        quit_action.setShortcut("ctrl+Q")
        toolbar.addAction(quit_action)

        self.line_editor = QtWidgets.QLineEdit("3")
        self.line_editor.setValidator(QtGui.QIntValidator(1, 100))

        label_pixtab_header = QtWidgets.QLabel(
            "Pixel Table\n", alignment=QtCore.Qt.AlignCenter
        )

        self.linetable = QtWidgets.QTableWidget()
        self.linetable.verticalHeader().hide()
        self.linetable.setColumnCount(2)
        self.linetable.setHorizontalHeaderLabels(["Pixel", "Wavelength"])
        self.linetable.setColumnWidth(0, 80)
        self.linetable.setColumnWidth(1, 90)
        self.linetable.setFixedWidth(180)

        delegate = ValidatorDelegate(self.linetable)
        self.linetable.setItemDelegate(delegate)

        layout = QtWidgets.QHBoxLayout(self._main)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(self.line_editor)

        pixtab_layout = QtWidgets.QVBoxLayout()
        pixtab_layout.addWidget(label_pixtab_header)
        pixtab_layout.addWidget(self.linetable)
        layout.addLayout(pixtab_layout)

        # Create Figure Canvas:
        right_layout = QtWidgets.QVBoxLayout()
        self.fig = Figure(figsize=(6, 6))
        self.canvas = FigureCanvas(self.fig)
        self.canvas.setSizePolicy(
            QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding
        )
        self.canvas.updateGeometry()
        self.canvas.mpl_connect("key_press_event", self.on_key_press)
        self.canvas.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.canvas.setFocus()
        right_layout.addWidget(self.canvas)
        self.mpl_toolbar = NavigationToolbar(self.canvas, self)
        right_layout.addWidget(self.mpl_toolbar)
        layout.addLayout(right_layout)

        delegate.closeEditor.connect(self.canvas.setFocus)

        # Plot some random data:
        self.ax = self.fig.add_subplot(111)
        self.ax.plot([0, 1, 5, 10, 20], [-1, 2, -4, 5, -2])

    def on_key_press(self, e):
        if e.key == "a":
            self.add_point()

    def add_point(self):
        print("Select point...")
        point = self.fig.ginput(1)
        if not point:
            return
        x0, _ = point[0]

        row = self.linetable.rowCount()
        self.linetable.insertRow(row)

        item = QtWidgets.QTableWidgetItem("%.2f" % x0)
        item.setFlags(QtCore.Qt.ItemIsEnabled)
        item.setBackground(QtGui.QColor("lightgray"))
        self.linetable.setItem(row, 0, item)

        index = self.linetable.model().index(row, 1)

        self.linetable.edit(index)


if __name__ == "__main__":
    # Launch App:
    qapp = QtWidgets.QApplication(sys.argv)
    app = GraphicInterface()
    app.show()
    qapp.exec_()


来源:https://stackoverflow.com/questions/61427950/qlineedit-in-qtablewdiget-cell-not-losing-focus

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!