Issues with link switching via keyboard in QPlainTextEdit
-
I'm using a QPlainTextEdit widget to display read-only text to the user, programatically inserted into the widget via
.appendHtml()
. Included in the HTML are links enclosed in standard<a></a>
tags. Clicking them with the mouse works fine.I intend to provide full keyboard navigation capability to the user by allowing them to cycle through links with
Tab
and click them withEnter
. After some digging, I discovered theLinksAccessibleByKeyboard
text interaction flag, which - according to the documentation - can do exactly that.However, even after implementing
textWidget.setTextInteractionFlags(Qt.TextInteractionFlag.LinksAccessibleByKeyboard)
,Tab
still only changes focus between a dropdown on my QMainWindow's toolbar and the QPlainTextEdit rather than focusing on the links inside the widget. I tried changing/removing the other text interaction flags I used and hiding the toolbar usingtoolBar.hide()
but nothing's worked yet, so I'm not sure what the problem is. -
Hi,
Which version of PySide/PyQt are you using ?
On which OS ?
Can you provide a minimal runnable script that shows this behavior ? -
I'm on Arch Linux, using PyQt version 6.9.1-2 from the Extra repository. Here's a minimum working example:
import sys from PyQt6.QtCore import Qt from PyQt6.QtWidgets import QApplication, QMainWindow, QToolBar, QToolButton, QMenu, QPlainTextEdit from PyQt6.QtGui import QIcon, QAction class AppWindow(QMainWindow): def __init__(self): super().__init__() self.buffer = QPlainTextEdit() self.setCentralWidget(self.buffer) toggleIcon = QIcon.fromTheme('preferences-other') toggleSomething = QAction(QIcon(toggleIcon), 'Toggle Something', self) aboutIcon = QIcon.fromTheme('help-about') aboutPage = QAction(QIcon(aboutIcon), 'About the App', self) settingsButton = QToolButton() settingsButton.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup) settingsMenu = QMenu() settingsButton.setMenu(settingsMenu) settingsAction = QAction(QIcon(QIcon.fromTheme('preferences-other')), 'Settings', self) settingsButton.setDefaultAction(settingsAction) settingsMenu.addAction(toggleSomething) settingsMenu.addAction(aboutPage) self.toolBar = QToolBar() self.addToolBar(self.toolBar) self.toolBar.setMovable(False) self.toolBar.addWidget(settingsButton) self.addAction(toggleSomething) self.addAction(aboutPage) self.setContextMenuPolicy(Qt.ContextMenuPolicy.PreventContextMenu) self.buffer.setContextMenuPolicy(Qt.ContextMenuPolicy.PreventContextMenu) self.buffer.setTextInteractionFlags(Qt.TextInteractionFlag.LinksAccessibleByKeyboard) # This should make the links clickable via keyboard, but it's not doing that class QPlainTextEdit(QPlainTextEdit): def mousePressEvent(self, e): self.link = self.anchorAt(e.pos()) if self.link: print('Link clicked! In my app, this is a function call instead of a print statement.') def addLines(): lines = ['<a href="https://example.com">Example</a>', 'Another line', '<a href="https://example.com">Example</a>'] for line in lines: window.buffer.appendHtml(line) app = QApplication(sys.argv) window = AppWindow() addLines() window.show() app.exec()
-
From a quick look at Qt sources, it seems this flag requires keypad navigation so I currently don't know how exactly it is supposed to work in this case.
-
I'm using a QPlainTextEdit widget to display read-only text to the user, programatically inserted into the widget via
.appendHtml()
. Included in the HTML are links enclosed in standard<a></a>
tags. Clicking them with the mouse works fine.I intend to provide full keyboard navigation capability to the user by allowing them to cycle through links with
Tab
and click them withEnter
. After some digging, I discovered theLinksAccessibleByKeyboard
text interaction flag, which - according to the documentation - can do exactly that.However, even after implementing
textWidget.setTextInteractionFlags(Qt.TextInteractionFlag.LinksAccessibleByKeyboard)
,Tab
still only changes focus between a dropdown on my QMainWindow's toolbar and the QPlainTextEdit rather than focusing on the links inside the widget. I tried changing/removing the other text interaction flags I used and hiding the toolbar usingtoolBar.hide()
but nothing's worked yet, so I'm not sure what the problem is.@wayfarer said in Issues with link switching via keyboard in QPlainTextEdit:
I'm using a QPlainTextEdit widget to display read-only text to the user, programatically inserted into the widget via
.appendHtml()
. Included in the HTML are links enclosed in standard<a></a>
tags. Clicking them with the mouse works fine.Have you considered QTextBrowser? It has link handling APIs, and tab navigation between links works.
from PyQt6.QtWidgets import QApplication, QTextBrowser from PyQt6.QtCore import Qt app = QApplication([]) browser = QTextBrowser() browser.setOpenLinks(False) browser.anchorClicked.connect(lambda url: print(f"Anchor clicked: {url}")) browser.setTextInteractionFlags(Qt.TextInteractionFlag.LinksAccessibleByKeyboard | Qt.TextInteractionFlag.LinksAccessibleByMouse) for i in range(3): browser.append(f"<a href='link_{i}'>link {i}</a>") browser.show() app.exec()
-
@wayfarer said in Issues with link switching via keyboard in QPlainTextEdit:
I'm using a QPlainTextEdit widget to display read-only text to the user, programatically inserted into the widget via
.appendHtml()
. Included in the HTML are links enclosed in standard<a></a>
tags. Clicking them with the mouse works fine.Have you considered QTextBrowser? It has link handling APIs, and tab navigation between links works.
from PyQt6.QtWidgets import QApplication, QTextBrowser from PyQt6.QtCore import Qt app = QApplication([]) browser = QTextBrowser() browser.setOpenLinks(False) browser.anchorClicked.connect(lambda url: print(f"Anchor clicked: {url}")) browser.setTextInteractionFlags(Qt.TextInteractionFlag.LinksAccessibleByKeyboard | Qt.TextInteractionFlag.LinksAccessibleByMouse) for i in range(3): browser.append(f"<a href='link_{i}'>link {i}</a>") browser.show() app.exec()
@jeremy_k said in Issues with link switching via keyboard in QPlainTextEdit:
Have you considered QTextBrowser? It has link handling APIs, and tab navigation between links works.
I was hoping to avoid having to use a different widget since I've already built most of my app around the QPlainTextEdit.
Besides, I vastly prefer its scrolling behavior. QTextEdit and QTextBrowser both cut off the text when the user scrolls past it, leaving it partially visible at the top of the window. QPlainTextEdit doesn't. It's a nice visual feature that makes the app feel much smoother.
Switching to QTextBrowser seems like a viable solution, though I'll have to modify my existing code to work with it. I would prefer to keep the QPlainTextEdit scrolling behavior somehow if possible.
Is there any particular reason why that flag doesn't work with QPlainTextEdit?
-
@jeremy_k said in Issues with link switching via keyboard in QPlainTextEdit:
Have you considered QTextBrowser? It has link handling APIs, and tab navigation between links works.
I was hoping to avoid having to use a different widget since I've already built most of my app around the QPlainTextEdit.
Besides, I vastly prefer its scrolling behavior. QTextEdit and QTextBrowser both cut off the text when the user scrolls past it, leaving it partially visible at the top of the window. QPlainTextEdit doesn't. It's a nice visual feature that makes the app feel much smoother.
Switching to QTextBrowser seems like a viable solution, though I'll have to modify my existing code to work with it. I would prefer to keep the QPlainTextEdit scrolling behavior somehow if possible.
Is there any particular reason why that flag doesn't work with QPlainTextEdit?
@wayfarer said in Issues with link switching via keyboard in QPlainTextEdit:
@jeremy_k said in Issues with link switching via keyboard in QPlainTextEdit:
Have you considered QTextBrowser? It has link handling APIs, and tab navigation between links works.
I was hoping to avoid having to use a different widget since I've already built most of my app around the QPlainTextEdit.
Besides, I vastly prefer its scrolling behavior. QTextEdit and QTextBrowser both cut off the text when the user scrolls past it, leaving it partially visible at the top of the window. QPlainTextEdit doesn't. It's a nice visual feature that makes the app feel much smoother.
Switching to QTextBrowser seems like a viable solution, though I'll have to modify my existing code to work with it. I would prefer to keep the QPlainTextEdit scrolling behavior somehow if possible.
Is there any particular reason why that flag doesn't work with QPlainTextEdit?
The scrolling difference is due to QPlainTextEdit's override definition of QAbstractScrollArea::scrollContentsBy. The implementation is here.
My guess is that handling of links, including navigation between them, isn't seen as part of the functionality of the widget. QTextEdit also lacks navigation between links. The QPlainTextEdit::textInteractionFlags contains what may be a revealing error:
Specifies how the
label
should interact with user input if it displays text. -