How to draw a QStaticText with a mnemonic underline in Qt? - c++

For a custom widget, there are tabs which ban be accessed with the ALT + <C> shortcut where <C> can be any keyboard character key. In Qt, this is called a Mnemonic
For this shortcut, it is needed to have that letter underlined in the label.
I can see that QPainter::drawText has an argument for flags, which can be provided with Qt::TextShowMnemonic but I would like to have this while using QStaticText for performance purpose. QStaticText allows Rich-Text, however underline seems not supported, or I could not make it work.
#include <QApplication>
#include <QDebug>
#include <QStaticText>
#include <QPainter>
#include <QPaintEvent>
#include <QWidget>
class TestWidget: public QWidget
{
Q_OBJECT
public:
explicit TestWidget( QWidget* parent=nullptr):QWidget(parent){}
auto paintEvent(QPaintEvent *event) -> void override
{
QPainter p(this);
QStaticText staticText; // this is not how it should be used, but for the example...
staticText.setTextFormat(Qt::TextFormat::RichText);
staticText.setText("<u>F</u>ile"); //What happens with Underline?
p.drawStaticText(QPoint(50,50), staticText);
p.drawText(QRect(50, 80, 100, 100), Qt::TextShowMnemonic, "&File"); // Ok, this works, but no static-text
}
};
#include "main.moc"
auto main (int argn, char* args[])-> int
{
QApplication app(argn, args);
qDebug() << QT_VERSION_STR;
TestWidget w;
w.resize(200,200);
w.show();
return app.exec();
}
Results in:
The question is:
How to make underline, or &mnemonic to work with QStaticText ?
.

There seems to be a QT-BUG for this, almost 10 years old (it was created in 2012).
QStaticText doesn't support text-decoration css property.
Properties like font-weight, color, font-style do have an effect but the text-decoration does not. See the attached example program where is HTML string using a element to underline a part of the string. This doesn't seem to have any effect using .... Also when using just plain underline tags it doesn't work.
There is also a conflict in the documentation of QStaticText concerning this issue stating that "For extra convenience, it is possible to apply formatting to the text using the HTML subset supported by QTextDocument.". However in the next chapter of the documention is said that "QStaticText can only represent text, so only HTML tags which alter the layout or appearance of the text will be respected. Adding an image to the input HTML, for instance, will cause the image to be included as part of the layout, affecting the positions of the text glyphs, but it will not be displayed. The result will be an empty area the size of the image in the output. Similarly, using tables will cause the text to be laid out in table format, but the borders will not be drawn."
It seems that the HTML subset supported by QTextDocument is not entirely applicable to QStaticText formatting.

Related

Qt generate UI based on template

I'm looking to generate a set of labels and buttons based on some kind of template, but I don't know how to do the template part.
I'll be using a tab widget which I already have set up, and in one the tabs, I want to have a two labels, a custom button, and a textbox. It'll be repeated around 40-50 times (dependent on a given value at startup) and have spacing as needed.
Once I have a template, I foresee calling it in a loop and setting the appropriate displayed text(Label_1, Label_2, etc) and connect statements where needed.
As I said, I don't know how to template parts of the UI so they can be placed in a kind of auto-generation.
I had thought of making one group, copying the xml, and somehow adding it but that doesn't seem to be a proper way. A little new to Qt.
This is roughly the layout I want to repeat. It has two labels, a lineedit, and one pushbutton.
There's no "good" way to do this in QtDesigner/QtCreator. At best you could copy/paste the set of controls 50 times and then in C++ code hide the ones you don't need. But I wouldn't recommend this.
Instead, just create the controls (labels/button/text box), and a layout to hold them, in C++ code, inside a loop which iterates however many times you need at runtime. Insert the controls layout into the tab widget page layout which you have set up in designer mode. It is not difficult, and will actually be more efficient than what QtDesigner produces since that tends to generate more code than you typically need in the first place.
As a starting point, you could look at the C++ code which is generated by the Qt UI Compiler (UIC) tool for your current design (it takes the XML from designer and turns it into C++ code). You can find this in the build folder for your project, typically named something like ui_ClassName.h, probably in a ui subfolder of the build tree.
UPDATE:
Another, possibly better, way to do this is to create the "template" QWidget class/form, which is going to be used multiple times, as a separate object. The "template" design could be created/maintained using QtCreator/Designer (or just directly in C++). The (possible) advantage here is that as the app requirements evolve, the template widget can be expanded with additional functionality or even re-used in other parts of the UI.
For example, I'd assume the text editor and button in the given mockup image will actually need to do something (eg. edit data and submit it). So some basic functionality can be built into the "template" widget, for example to emit a signal with the text contents of the line editor when the button is pressed.
I put together a quick example. I'm creating the simple MainWindow in pure C++ to simplify/shorten the example code. The "template" I'm calling an Editor. The Editor class and UI form I initially created with the QtCreator wizard (New -> Qt Designer Form Class). I then added the label/control widgets in designer mode. And in C++, a textEdited(const QString &text) signal in the header, and in the Editor() constructor a lambda connection to emit that signal when the button is pressed.
The Editor class code is straight out of the QtCreator wizard except for two edits I'm highlighting below. The designer form has two relevant controls: a QLineEdit (lineEdit) and a QPushButton (pushButton). I'll link to the full files below.
Editor.h
// in the Editor class declarations:
signals:
void textEdited(const QString &text) const;
Editor.cpp
// in the constructor, after ui->setupUi(this);
connect(ui->pushButton, &QPushButton::clicked, this, [this]() {
emit textEdited(ui->lineEdit->text());
});
Test harness, including MainWindow subclass and main()
#include <QApplication>
#include <QMainWindow>
#include <QTabWidget>
#include <QBoxLayout>
#include <QMessageBox>
#include "Editor.h"
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow() : QMainWindow()
{
// set up a tab widget as the window central widget
QTabWidget *tabWidget = new QTabWidget(this);
setCentralWidget(tabWidget);
// the first/only page will contain all the editors in a vertical layout
QWidget *editorsPage = new QWidget(this);
editorsPage->setLayout(new QVBoxLayout());
// add the editors container page to tab widget
tabWidget->addTab(editorsPage, tr("Editors page"));
// Now create a number of editor widgets using our Editor class "template"
int layoutItems = 5; // number of editors needed, could be dynamic
for (int i=0; i < layoutItems; ++i) {
// Create an Editor instance with the tab page as parent
Editor *editor = new Editor(editorsPage);
// Add the editor widget to the tab page layout
editorsPage->layout()->addWidget(editor);
// A simple connection with the editor signal, as way of example.
connect(editor, &Editor::textEdited, this, [this](const QString &text) {
// just show a message box with the editor text
QMessageBox::information(this, tr("Text Edited"), text, QMessageBox::Ok);
});
}
}
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
MainWindow mainWindow;
mainWindow.show();
return app.exec();
}
#include "main.moc"
Links The full Editor code:
Editor.h
Editor.cpp
Editor.ui
The XML in Qt Creator is for UIC in QMake to generator code for you.
For example, QMake translates your mainwindow.ui to ui_mainwindow.h, and within you will find void setupUi(QMainWindow *MainWindow) with the actual code that creates and places the widgets.
Look at this code, the docs, and create and place the widgets yourself by code.
For example, adding 5 checkboxes to a groupbox by code:
QVBoxLayout *l = new QVBoxLayout(this);
ui->groupBox_4->setLayout(l);
for(int i=0; i<5; i++){
QCheckBox *c = new QCheckBox(this);
l->addWidget(c);
}

Set text for statusbar in QtDesigner?

By default, if I create a new Form in QtDesigner, of type "Main Window", I get three elements in there: centralwidget, menubar - which is not visible in Preview (Ctrl-R), unless actual menu entries are added; and statusbar.
The problem is - by default, the statusbar is the same background color as the rest, and so, when I do a Preview, I cannot really perceive whether the statusbar is there or not:
Basically, the only thing I can see is the "sizeGrip", which is not always easy to see - so I would like to have a text/message shown in the statusbar as well.
Now, I know that the API for QStatusBar Class has .showMessage used to show text in a statusbar - however, I cannot find any similar field in QtDesigner?!
So - is it possible to set a default/placeholder text in the statusbar in QtDesigner - and if so, how?
No, you cannot.
In QtDesigner, you can only set properties of a widget (see Q_PROPERTY), not invoke methods. The properties are listed in the Properties section of the documentation, and QStatusBar only has sizeGripEnabled (and the inherited properties from QWidget)
But what is the actual problem? You cannot clearly make out the status bar in the preview? The preview is supposed to help in checking singal/slots and layout constraints, not as a full functioning application.
There are also things that will influence the actual look&feel in the final application that cannot be checked in designer preview, like dynamic stylesheets, or custom styles.
if you want to check how your window looks like with text in the statusbar, you will need to make a mock-application that does just this: show some text
#include <QtCore>
#include <QtWidgets>
#include "ui_mainwindow.h"
int main(int argc, char **argv) {
QApplication app(argc, argv);
Ui::MainWindow ui;
QMainWindow wnd;
ui.setupUi(&wnd);
wnd.show();
ui.statusbar->showMessage("Hello World!");
return app.exec();
}

mix setStyleSheet and setFont: wrong background

I merge several classes and functions into only two classes,
so it looks strange and ugly.
The problem that my class MyW at the end of constructor
set background to white, but it's child QLabel has background
from Page, not from MyW.
And question why?
I know that if I remove magic with fonts in MyW,
or call setStyleSheet at the begining like this:
setStyleSheet("border:none;background:#ffffff;color:#000000;");
I get the right result (white background),
but I can not understand why font make influence to background,
and why there is difference between set stylesheet in two steps,
instead of one?
#include <QApplication>
#include <QLabel>
class MyW : public QWidget {
public:
MyW(QWidget *parent) : QWidget(parent) {
setStyleSheet("border:none;");
setFont(QFont{"Arial", 42});
setStyleSheet(styleSheet() + "background:#ffffff;color:#000000;");
auto lbl = new QLabel{"AAAA", this};
lbl->ensurePolished();
}
};
class Page : public QWidget {
public:
Page(QWidget *parent) : QWidget{parent} {
setStyleSheet("background:#f0f4f7;");
auto item = new MyW{this};
}
};
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
Page p{nullptr};
p.resize(400, 800);
p.show();
return a.exec();
}
Update: I removed all not important parts, like layouts, QApplication::setFont and so on.
I am sure that reason is not in auto font = QApplication::font();, but
in QWidget::setFont call. You can check it, for example, by replacing font stuff with:
QFont font;
font.setPixelSize(42);
setFont(font);
The "magic" behind this is cache that is used inside Qt to deal with style sheets.
You can find hint in qtbase/src/widgets/styles/qstylesheetstyle.cpp, look at usage of
static QStyleSheetStyleCaches *styleSheetCaches = 0;
Qt does not use QWidget::styleSheet property directly,
it parses it and caches result.
There are several triggers for parsing(re-parsing) of QWidget::styleSheet:
Call of QWidget::ensurePolished (it is done automatically when your widget
becomes visible for the first time);
Call of setStyleSheet but only in the case if your widget called QWidget::ensurePolished at least once;
Call of QWidget::setFont or QWidget::setPalette (only if your widget does not have an empty styleSheet).
In your case your problem is a combination of (1-2) and (3):
after force caching of the parsed stylesheet via QWidget::setFont your widget is still not "polished",
so the next call of setStyleSheet does not update the cached style sheet that was created on setFont step,
so on the step with lbl->ensurePolished(); you actually have a style sheet with "border:none;" plus font, plus background of parent.
You can call this->ensurePoslished() before lbl->ensurePoslished() to fix this issue or as suggested by #William Miller use stylesheet to set font,
or place setFont after all calls of setStyleSheet
Per the docs on QApplication::setFont(),
Warning: Do not use this function in conjunction with Qt Style Sheets. The font of an application can be customized using the "font" style sheet property. To set a bold font for all QPushButtons, set the application styleSheet() as "QPushButton { font: bold }"
Since they explicitly warn against this I would expect there is an inheritance conflict when using style sheets in conjunction with the application level default font, so the line
QApplication::setFont(font);
and subsequently,
auto font = QApplication::font();
May not produce the expected behavior. Their alternative is set the application-level styleSheet() for your class, i.e.
setStyleSheet(" MyW { font-family: 'Garamond' }");
So it seems the short answer is that they don't support it.
The reason it makes a difference "between set stylesheet in two steps, instead of one" is not because you are setting the style sheet in two different steps but because you call QApplication::font() between the steps and as this is unsupported behavior, it is producing the unexpected result.

QTreeView no show when placed inside QDockWidget

I want to show a file system using QTreeView on a QDockWidget. The tree will be dynamically changed, so I decided to use QTreeView instead of QTreeWidget.
Here is my code:
QFile file(":/default.txt");
file.open(QIODevice::ReadOnly);
TreeModel model(file.readAll());
file.close();
QTreeView w;
w.setModel(&model);
swatch1->setWidget(&w);
w.setEnabled(true);
addDockWidget(leftarea, swatch1);
swatch1 is of type QDockWidget. The above code is inside a function body of type (inherited from) MainWindow. The code runs smoothly, and the tree does not show up.
I also tried another way: putting QTreeView into a QVBoxLayout (using setWidget method), which in turn be put into a QDockWidget (using setLayout method). This 2nd code also runs smoothly, and the tree does not show up.
This code is copied from a working example on Qt Creator IDE, and I tested it working. The only difference is, in the original QTreeView example, the above code is placed inside the main() { ..... } function.
Does anyone has a working example, putting QTreeView into QDockWidget and working (the code actually shows the tree)? Thanks in advance.
I'm not quite sure what went wrong in the OP. However, I made a minimal complete sample to see whether there are pitfalls:
// standard C++ header:
#include <iostream>
#include <string>
// Qt header:
#include <QApplication>
#include <QDockWidget>
#include <QFileSystemModel>
#include <QMainWindow>
#include <QTreeView>
using namespace std;
int main(int argc, char **argv)
{
cout << QT_VERSION_STR << endl;
// main application
#undef qApp // undef macro qApp out of the way
QApplication qApp(argc, argv);
// setup GUI
QMainWindow qWin;
QDockWidget qDock;
qDock.setAllowedAreas(Qt::AllDockWidgetAreas);
QTreeView qTreeView;
QFileSystemModel qFSModel;
qTreeView.setModel(&qFSModel);
QString path = QDir::currentPath();
QModelIndex indexPath = qFSModel.index(path);
qTreeView.scrollTo(indexPath);
qDock.setWidget(&qTreeView);
qWin.addDockWidget(Qt::TopDockWidgetArea, &qDock);
qWin.show();
// run application
return qApp.exec();
}
Compiled and tested it with VS2013, Qt 5.6 on Windows 10 (64 bit):
As can be seen in the snapshot, the QTreeView is visible (docked and undocked). I checked that both re-act on mouse clicks - they did.
(I guess this is one of my most minimal Qt applications I ever wrote.)
Scheff,
Thank you very much for your answer. Sorry I may not be clear about what I am asking: the tree become visible when this code section is in the main() { ....} function:
QFile file(":/default.txt");
file.open(QIODevice::ReadOnly);
TreeModel model(file.readAll());
file.close();
QTreeView w;
w.setModel(&model);
w.show();
But the same code (almost same) does not working (program runs but the tree is not visible) when this section of code is in a class function inside MainWindow and QTreeView is added to a QDockWidget:
QFile file(":/default.txt");
file.open(QIODevice::ReadOnly);
TreeModel model(file.readAll());
file.close();
QTreeView w;
w.setModel(&model);
swatch1->setWidget(&w);
addDockWidget(leftarea, swatch1);
here, leftarea is a Qt:DockWidgetArea, and swatch1 is an object of type inherited from QDockWidget. when run this program, swatch (a QDockWidget) is visible, but not the tree. still struggling ...
The problem solved. The original code I wrote by itself is correct, but it is in an object method, and as soon as the execution leaves the object, the tree is destroyed.
So, it is a C++ variable scoping problem, not exactly a Qt problem. I have been using python for a while, and just switch back to C++.
Scheff, thank you for your posting confirmed to me that the Qt code is correct, and suggests to me that something else is wrong.

Qt 5- QTextEdit reverts to default font

I am working on a text editor using the QT library. I am subclassing QTextEdit for my main editor widget.
Here is my code:
editorwidget.hpp
#ifndef EDITORWIDGET_H_INCLUDED
#define EDITORWIDGET_H_INCLUDED
#include <QTextEdit>
#include <QFile>
class EditorWidget : public QTextEdit
{
Q_OBJECT
public:
EditorWidget(const QString& filename, QWidget* parent = 0);
~EditorWidget();
public slots:
void saveRequested();
//...
private:
QFile* editorFile;
};
#endif
editorwidget.cpp
#include "editorwidget.hpp"
EditorWidget::EditorWidget(const QString& filename, QWidget* parent)
: QTextEdit(parent)
{
this->setFontPointSize(getFontSize()); // this is in another file
this->setFontFamily(getFont()); // also in another file
// those two functions get the font and font size from the user's settings
this->editorFile = new QFile(filename);
}
EditorWidget::~EditorWidget()
{
if(this->editorFile->isOpen()) this->editorFile->close():
delete editorFile;
}
...
When the EditorWidget is created, the font shows up correctly. However, when I enter some text, and then delete it, the widget reverts to the default font.
I don't understand what's going on; I've searched Google and Stack Overflow but found nothing. Any help would be greatly appreciated. Thanks!
This thread might be helpful. The setFont...() functions set the format behind the edit cursor, but the default format is free from it. The QT Docs also explains this situation.
"...The current style, which is used to render the content of all standard Qt widgets, is free to choose to use the widget font, or in some cases, to ignore it (partially, or completely). In particular, certain styles like GTK style, Mac style, Windows XP, and Vista style, apply special modifications to the widget font to match the platform's native look and feel. Because of this, assigning properties to a widget's font is not guaranteed to change the appearance of the widget."
In your case, you may try setStyleSheet() instead.