简体   繁体   中英

How to make a cursor in a PyQt5 label

I have a PyQt5 window that has a label with text in it.

The text that appears there is from a python string variable. The string is built by the user clicking on on-screen pushbuttons and then the string is outputted to the window in that label.

As of now I have a backspace button which deletes the last character in the string, but I would also like the user to be able to click on a spot in front of a character in the label, and then be able to delete that character.

So, I would like to know how to do two things.

  1. how to get the character location in the string based on the user click
  2. I've seen some examples for this, but I'd like to also show a cursor in that spot once the user has clicked there.

I would like to do this with a label widget - not with a text input field.

Anyone have any ideas?

Making a QLabel work like that is hard (QLabel is more complex than it looks).
It is possible to show the cursor after clicking, and that's achieved by setting the textInteractionFlags property:

self.label.setTextInteractionFlags(
    QtCore.Qt.TextSelectableByMouse | QtCore.Qt.TextSelectableByKeyboard)

The first flag is to allow handle of mouse events, while the second allows displaying the cursor as soon as the label has focus (for instance, after clicking it).
Unfortunately, this doesn't allow you to get the cursor position (nor to change it); there are ways (using QFontMetrics and QTextDocument), but you need a complex implementation in order to make it really reliable.

The solution is to use a QLineEdit and override the keyPressEvent , which is the function that is always called on a widget whenever a key press happens (and it has input focus). Considering that number input seems still required, just ensure that the event.key() corresponds to a Qt.Key enum for numbers, and in that case, call the base implementation.
You can even make it look exactly like a QLabel by properly setting its stylesheet.

class CommandLineEdit(QtWidgets.QLineEdit):
    allowedKeys = (
        QtCore.Qt.Key_0, 
        QtCore.Qt.Key_1, 
        QtCore.Qt.Key_2, 
        QtCore.Qt.Key_3, 
        QtCore.Qt.Key_4, 
        QtCore.Qt.Key_5, 
        QtCore.Qt.Key_6, 
        QtCore.Qt.Key_7, 
        QtCore.Qt.Key_8, 
        QtCore.Qt.Key_9, 
    )
    def __init__(self):
        super().__init__()
        self.setStyleSheet('''
            CommandLineEdit {
                border: none;
                background: transparent;
            }
        ''')

    def keyPressEvent(self, event):
        if event.key() in self.allowedKeys:
            super().keyPressEvent(event)

Then, if you want to set the text programmatically, also based on the cursor, here's a basic usage:

from functools import partial

class CommandTest(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        layout = QtWidgets.QVBoxLayout(self)
        self.commandLineEdit = CommandLineEdit()
        layout.addWidget(self.commandLineEdit)
        keys = (
            'QWERTYUIOP', 
            'ASDFGHJKL', 
            'ZXCVBNM'
        )
        backspaceButton = QtWidgets.QToolButton(text='<-')
        enterButton = QtWidgets.QToolButton(text='Enter')
        self.shiftButton = QtWidgets.QToolButton(text='Shift', checkable=True)
        for row, letters in enumerate(keys):
            rowLayout = QtWidgets.QHBoxLayout()
            rowLayout.addStretch()
            layout.addLayout(rowLayout)
            for letter in letters:
                btn = QtWidgets.QToolButton(text=letter)
                rowLayout.addWidget(btn)
                btn.clicked.connect(partial(self.typeLetter, letter))
            rowLayout.addStretch()
            if row == 0:
                rowLayout.addWidget(backspaceButton)
            elif row == 1:
                rowLayout.addWidget(enterButton)
            else:
                rowLayout.addWidget(self.shiftButton)
        spaceLayout = QtWidgets.QHBoxLayout()
        layout.addLayout(spaceLayout)
        spaceLayout.addStretch()
        spaceButton = QtWidgets.QToolButton(minimumWidth=200)
        spaceLayout.addWidget(spaceButton)
        spaceLayout.addStretch()

        backspaceButton.clicked.connect(self.commandLineEdit.backspace)
        spaceButton.clicked.connect(lambda: self.typeLetter(' '))

    def typeLetter(self, letter):
        text = self.commandLineEdit.text()
        pos = self.commandLineEdit.cursorPosition()
        if not self.shiftButton.isChecked():
            letter = letter.lower()
        self.commandLineEdit.setText(text[:pos] + letter + text[pos:])

import sys
app = QtWidgets.QApplication(sys.argv)
w = CommandTest()
w.show()
sys.exit(app.exec_())

As you see, you can call backspace() in order to clear the last character (or the selection), and in the typeLetter function there are all the remaining features you required: getting/setting the text and the cursor position.
For anything else, just study the full documentation .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM