I am using showCritical() for displaying an error in my add-on. I would like to pass an absolute URL like the URL of the GitHub issues page as the help argument. But it is always interpreted as a relative URL to the Anki docs site, for example.
I understand that there may be concerns that Anki opens arbitrary URLs but:
An add-on can probably even open the browser itself, right?
It would be possible to restrict the domain name part to a number of domains like the Anki forums, GitHub, …
As it turned out, passing a customBtn argument to showInfo() does not help because you cannot connect a signal to the buttons.
You have to roll your own version. The code below shows how to format a Python exception and display it in the message box. Clicking the help button will open the external help URL in the user’s default browser:
import html
import traceback
from aqt import mw, qconnect
from aqt.qt import (QDialog, Qt, QMessageBox, # type: ignore[attr-defined]
QDesktopServices, QUrl) # type: ignore[attr-defined]
from anki.utils import no_bundled_libs
class MyDialog(QDialog):
def __init__(self):
super().__init__()
# Dialog design goes here.
def accept(self) -> None:
try:
raise TypeError('I cannot type!')
except Exception as e:
self._show_exception(e)
def _show_exception(self, e: Exception):
ftb = list(traceback.format_tb(e.__traceback__))
msgs = (
_('An error occurred!'),
_('Clicking the help button will open a web page explaining how to report a bug.'),
_('Please include the following information in your bug report:'),
_('<hr />'),
_('Exception type:') + ' ' + html.escape(type(e).__name__),
_('Exception message:') + ' ' + html.escape(str(e)),
_('Traceback:'),
)
msg = '<br />'.join(msgs) + '<br />'.join(ftb)
print(msg)
parent = mw.app.activeWindow() or mw
msg_box = QMessageBox(parent)
msg_box.setIcon(QMessageBox.Icon.Critical)
msg_box.setTextFormat(Qt.TextFormat.MarkdownText)
msg_box.setText(msg)
msg_box.setWindowTitle(_('Chess Opening Trainer'))
# This is the reason, whey the customBtn argument for `showInfo()`
# cannot be used. You can only call setDefault() or use
# qconnect() with the return value of addButton() which is a
# pointer to QPushButton.
ok_button = msg_box.addButton(QMessageBox.StandardButton.Ok)
ok_button.setDefault(True)
def open_link(link: str):
with no_bundled_libs():
QDesktopServices.openUrl(QUrl(link))
help_button = msg_box.addButton(QMessageBox.StandardButton.Help)
link = _('https://www.guido-flohr.net/practice-chess-openings-with-anki/#report-bugs')
help_button.clicked = qconnect(
help_button.clicked,
lambda: open_link(link),
)
help_button.setAutoDefault(False)
return msg_box.exec()