Qt QFont selection of a monospace font doesn't work - c++

i'm trying to make a qt widget that shows a table of qlabels displaying hex numbers.
i pass the numbers to the labels as qstrings ready to be printed and the labels work properly but the font type is the system default (a sans serif) that has different letter sizes, so the numbers containing "A-F" digits are no more aligned with the other numbers...
i initially create the font with the function:
static const QFont getMonospaceFont(){
QFont monospaceFont("monospace"); // tried both with and without capitalized initial M
monospaceFont.setStyleHint(QFont::TypeWriter);
return monospaceFont;
}
and create a custom QLabel class that has this constructor:
monoLabel(QWidget *parent = 0, Qt::WindowFlags f = 0) : QLabel(parent, f) {
setTextFormat(Qt::RichText);
setFont(getMonospaceFont());
}
but it doesn't work, so i add to the main file
QApplication app(argn, argv);
app.setFont(monoLabel::getMonospaceFont(), "monoLabel");
and again the font remains unchanged..
i searched the net for font-setting problems with QLabels but i seem to be the only one who doesn't get them to work properly..
what am i doing wrong??

You probably want a Monospace style hint, not Typewriter. The following works for me on OS X under Qt 4 and 5.
Setting QLabel to rich text is unnecessary for your application.
Note that QFontInfo::fixedPitch() is not the same as QFont::fixedPitch(). The latter lets you know whether you requested a fixed pitch font. The former indicated whether you actually got a fixed pitch font.
// https://github.com/KubaO/stackoverflown/tree/master/questions/label-font-18896933
// This project is compatible with Qt 4 and Qt 5
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
#include <QtWidgets>
#endif
bool isFixedPitch(const QFont &font) {
const QFontInfo fi(font);
qDebug() << fi.family() << fi.fixedPitch();
return fi.fixedPitch();
}
QFont getMonospaceFont() {
QFont font("monospace");
if (isFixedPitch(font)) return font;
font.setStyleHint(QFont::Monospace);
if (isFixedPitch(font)) return font;
font.setStyleHint(QFont::TypeWriter);
if (isFixedPitch(font)) return font;
font.setFamily("courier");
if (isFixedPitch(font)) return font;
return font;
}
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QString text("0123456789ABCDEF");
QWidget w;
QVBoxLayout layout(&w);
QLabel label1(text), label2(text);
label1.setFont(getMonospaceFont());
layout.addWidget(&label1);
layout.addWidget(&label2);
w.show();
return a.exec();
}

Related

Render QWidget which is larger than 32767 px

I try to render a widget whose size is 3000x50000.
#include <QApplication>
#include <QPainter>
class widget: public QWidget{
protected:
void paintEvent(QPaintEvent *event) override{
QPainter painter(this);
painter.setPen(QPen(Qt::black, 12, Qt::DashDotLine, Qt::RoundCap));
painter.drawLine(QPoint(0,0),rect().bottomRight());
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
widget wid;
wid.setGeometry(0,0,3000,500000);
QPixmap pixmap(3000,50000);
wid.render(&pixmap);
pixmap.save("PATH_TO_IMAGE");
exit(EXIT_SUCCESS);
return a.exec();
}
but in output I have this image result after 32767px image was not rendered.
Maybe you have any idea how to fix it?
The limitation comes from the internal raster paint engine (see qoutlinemapper_p.h: const int QT_RASTER_COORD_LIMIT = 32767;). Upgrade to Qt6 and use a 64bit build - then this limit is updated to 8388607 (2^23-1) pixels.
The better solution which I make is to render the widget to QImage step by step (step should be lower the 32767px) and then copy bits of QImage(usingQImage::bits()) into one buffer(QVector). And after all these steps you can create one QImage(using QImage(&buffer.first())

Why QGraphicsView is not shown

I have the following codes in my Qt project with the following main:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
The class Widget is a QWidget object with the following constructor:
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
m_Scene = new QGraphicsScene(this);
QGraphicsLinearLayout* layout = new
QGraphicsLinearLayout(Qt::Orientation::Vertical);
for(int i = 0; i < 10; i++)
{
std::string name = "m_" + std::to_string(i);
GraphicsTextItem* item = new GraphicsTextItem(nullptr, QString(name.c_str()));
layout->addItem(item);
}
QGraphicsWidget* list = new QGraphicsWidget;
list->setPos(0,0);
list->setLayout(layout);
m_Scene->addItem(list);
QGraphicsView* view = new QGraphicsView(this);
view->setScene(m_Scene);
// Why one of these lines must be uncommented?
//m_Scene->setSceneRect(0, 0, 1920, 768);
//QVBoxLayout *ttopLayout = new QVBoxLayout;
//ttopLayout->addWidget(view);
//setLayout(ttopLayout);
}
GraphicsTextItem is just a QGraphicsWidget for displaying text:
class GraphicsTextItem : public QGraphicsWidget
{
public:
QString m_Name;
QColor m_Color;
public:
GraphicsTextItem(QGraphicsItem * parent = nullptr, const QString& name = QString());
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
{
Q_UNUSED(option)
Q_UNUSED(widget)
QFont font("Times", 10);
painter->setFont(font);
painter->setPen(m_Color);
painter->drawText(0, 0, m_Name);
}
};
My question is that why my scene is not shown. I must either define a SceneRect or define a layout on my widget?
I made an even shorter MCVE for demonstration:
#include <QtWidgets>
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
QWidget qWinMain;
qWinMain.resize(320, 240);
QFrame qFrm(&qWinMain);
qFrm.setFrameStyle(QFrame::Box | QFrame::Raised);
qFrm.setLineWidth(0);
qFrm.setMidLineWidth(1);
qWinMain.show();
return app.exec();
}
compiled and started in cygwin64. This is how it looks:
There is a main window (with window manager decoration).
There is a child QFrame.
The child QFrame is "pressed" into the upper left corner.
How comes?
What QWidget does ensure: Child widgets are rendered (in front) when QWidget is rendered.
What QWidget is not (directly) responsible for: Layouting child widgets.
For this, a layout manager has to be plugged in:
#include <QtWidgets>
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
QWidget qWinMain;
qWinMain.resize(320, 240);
QVBoxLayout qVBox(&qWinMain);
QFrame qFrm(&qWinMain);
qFrm.setFrameStyle(QFrame::Box | QFrame::Raised);
qFrm.setLineWidth(0);
qFrm.setMidLineWidth(1);
qVBox.addWidget(&qFrm);
qWinMain.show();
return app.exec();
}
compiled and started again in cygwin64. This is how it looks:
Now, the QFrame qFrm is filling the QWidget qWinMain nicely. Resize events received in qWinMain will be forwarded to the layout manager qVBox which will re-layout the children of qWinMain (i.e. qFrm) again.
I strongly believe OP's GraphicsView is just not visible because it has no minimal size requirement. (It's just to small to be visible.)
Hence, adding a layout manager ensures that the GraphicsView fills the parent widget client area. Resizing the contents of GraphicsView (by m_Scene->setSceneRect(0, 0, 1920, 768);) is yet another option to fix this, albeit the worse one.
Finally, the link to Qt Doc.: Layout Management.
Layout Management
The Qt layout system provides a simple and powerful way of automatically arranging child widgets within a widget to ensure that they make good use of the available space.
Introduction
Qt includes a set of layout management classes that are used to describe how widgets are laid out in an application's user interface. These layouts automatically position and resize widgets when the amount of space available for them changes, ensuring that they are consistently arranged and that the user interface as a whole remains usable.
All QWidget subclasses can use layouts to manage their children. The QWidget::setLayout() function applies a layout to a widget. When a layout is set on a widget in this way, it takes charge of the following tasks:
Positioning of child widgets
Sensible default sizes for windows
Sensible minimum sizes for windows
Resize handling
Automatic updates when contents change:
Font size, text or other contents of child widgets
Hiding or showing a child widget
Removal of child widgets

How do I change qt label text to look like window title text

In my program I am displaying a dialog window to provide information to the user. I am using the QT Label element to do this and I would like to make the text look more "organic" or carved in, like the title text of a window than simple black on gray. Does anyone have any suggestions? I am using QT Creator and coding in c++ for Linux.
here is an image of what I am trying to acomplish
I prepared an MCVE to demonstrate the options I suggested in my comments.
Changing style of QLabel is rather simple and straight forward.
Using the QStylePainter doesn't quite match what I actually expected. I left in the sample code what I got so far. (May be, somebody could leave a helpful hint.) I will investigate in this topic later and edit this answer as soon as I got some satisfying progress on this topic.
The sample code:
#include <QtWidgets>
class TitleBar: public QWidget {
private:
QStyleOptionTitleBar _option;
//QString _text;
public:
explicit TitleBar(
const QString &text = QString(), QWidget *pQParent = nullptr);
protected:
virtual QSize sizeHint() const;
virtual void resizeEvent(QResizeEvent *pQEvent);
virtual void paintEvent(QPaintEvent *pQEvent);
};
int main(int argc, char **argv)
{
qDebug() << "Qt Version: " << QT_VERSION_STR;
// main application
#undef qApp // undef macro qApp out of the way
QApplication qApp(argc, argv);
// setup of GUI
QWidget qWin;
QVBoxLayout qBox;
// a boring label
QLabel qLbl1(QString::fromUtf8("Rather boring"));
qLbl1.setAlignment(Qt::AlignCenter);
qBox.addWidget(&qLbl1);
// a label with frame
QLabel qLbl2(QString::fromUtf8("Label with Frame"));
qLbl2.setFrameStyle(QLabel::Panel | QLabel::Raised);
qLbl2.setLineWidth(2);
qLbl2.setAlignment(Qt::AlignCenter);
qBox.addWidget(&qLbl2);
// a label with rich-text
QLabel qLbl3(
QString::fromUtf8(
"<body bgcolor='#28f'>" // doesn't have the effect
"<font size='+2' color='#f8e' face='Old English Text MT'>"
"Label <i>with</i> <b>Rich-Text</b>"
"</font>"
"</body>")); // background is set by style-sheet instead
qLbl3.setTextFormat(Qt::RichText); // might be auto-detected...
qLbl3.setStyleSheet("QLabel { background-color: #28f; }");
qLbl3.setAlignment(Qt::AlignCenter);
qLbl3.show();
qBox.addWidget(&qLbl3);
// a home-brew title bar
TitleBar qTitle("A Home-Brew Title Bar");
qBox.addWidget(&qTitle);
// finish setup of GUI
qWin.setLayout(&qBox);
qWin.show();
// run application
return qApp.exec();
}
TitleBar::TitleBar(const QString &text, QWidget *pQParent):
QWidget(pQParent)
{
_option.initFrom(this);
_option.titleBarFlags = Qt::Window;
_option.text = text;
}
QSize TitleBar::sizeHint() const
{
#if 0 // does not provide the expected result
return _option.rect.size();
#elif 0 // does not provide the expected result
return style()->proxy()->subControlRect(QStyle::CC_TitleBar,
&_option, QStyle::SC_TitleBarLabel).size();
#else
return QSize(0,
style()->proxy()->pixelMetric(QStyle::PM_TitleBarHeight,
&_option, this));
#endif // 0
}
void TitleBar::resizeEvent(QResizeEvent *pQEvent)
{
_option.rect = QRect(_option.rect.topLeft(), pQEvent->size());
}
void TitleBar::paintEvent(QPaintEvent *pQEvent)
{
QPainter qPainter(this);
style()->proxy()->drawComplexControl(QStyle::CC_TitleBar,
&_option, &qPainter, this);
}
I compiled and tested it in VS2013 on Windows 10 (64 bit). Below a snapshot:

QMessageBox is not displaying whole title

#include <QtGui>
int main(int argc, char** argv)
{
QApplication app(argc, argv);
// first
QMessageBox box(0);
box.setText("short text");
box.setWindowTitle("looooooooooooooooong text");
box.setMinimumSize(800, 0);
box.exec();
// second
//QMessageBox::warning(0, "looooooooooooooooong text", "short text");
return app.exec();
}
Both approaches produce this messagebox, where title is not displayed properly. I have tried to resize dialog widget by it doesn't help. How can I force QMessageBox to display whole title?
As a workaround I can add trailing spaces to title text but I think there should be better solution.
aminasya#aminasya-desktop:~/qt$ qmake --version
QMake version 2.01a
Using Qt version 4.8.6 in /usr/lib/x86_64-linux-gnu
I tried to create QMessageBox with constructor which qt help mentioned in qmessagebox.cpp but it didnt worked for me too.
For some reason QMessageBox adjust size to fit the window title doesn't work. However you can adjust MessageBox size by creating your own sublclass of QMessageBox.
Please see example below;
class MyMessageBox : public QMessageBox
{
public:
explicit MyMessageBox(QWidget *parent = 0) : QMessageBox(parent) { }
MyMessageBox(const QString &title, const QString &text, Icon icon,
int button0, int button1, int button2,
QWidget *parent = 0,
Qt::WindowFlags f = Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint) :
QMessageBox(title, text, icon, button0, button1, button2, parent, f) { }
static void about(QString title, QString text)
{
MyMessageBox aboutBox(title, text, QMessageBox::Information, 0, 0, 0, NULL);
aboutBox.setText(title);
aboutBox.setText(text);
QIcon icon = aboutBox.windowIcon();
QSize size = icon.actualSize(QSize(64, 64));
aboutBox.setIconPixmap(icon.pixmap(size));
aboutBox.exec();
}
void showEvent(QShowEvent *event)
{
QMessageBox::showEvent(event);
QWidget *textField = findChild<QWidget *>("qt_msgbox_label");
if (textField != NULL)
{
// getting what ever my system has set for the window title font
QFont font = QFont("Ubuntu Bold", 11);
// you might want to make it more generic by detecting the actuall font
// or using smth like this:
//QFont font = QApplication::font("QWorkspaceTitleBar");
QFontMetrics fm(font);
int width = qMax(fm.width(windowTitle()) + 50, textField->minimumWidth());
textField->setMinimumWidth(width);
}
}
};
It appears that QMessageBox, when launched with exec() checks the length of the body text and automatically adjusts the size, ignoring the fact that the title text may be longer. Though not ideal, you can change it just afterwards with a QTimer, as demonstrated here:
QMessageBox* box = new QMessageBox;
box->setText("short text");
box->setWindowTitle("looooooooooooooooong text");
QTimer* pTimer = new QTimer;
pTimer->setSingleShot(true);
QObject::connect(pTimer, &QTimer::timeout, [=](){
box->setMinimumWidth(400);
pTimer->deleteLater();
});
pTimer->start(0);
box->exec();
Since this occurs after the message box is launched, the change in size is visible.
The better solution would simply be to create your own MessageBox, derived from QDialog. After all, the QMessageBox class is just a convenience widget.
Since exec() and show() both override your minimum size based on the contents of the box's text, the simple solution is not to use exec() and to set the minimum size after the box has been shown. In any case, you should never use exec() in dialogs anyway.
Note: Window titles are non-portable. Your UI must still make sense without a window title. For example, on OS X the message box window titles are invisible.
#include <QApplication>
#include <QMessageBox>
#include <QDebug>
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QMessageBox box;
box.setText("short text");
box.setWindowTitle("looooooooooooooooong text");
box.show();
box.setMinimumSize(qMax(box.minimumWidth(), 800), box.minimumHeight());
return app.exec();
}
Since the message box width is adjusted to the width of the text, a simple answer seems to be to pad the text string with spaces:
QString title = "This is a looooooooooooooooooooooooong title";
QString txt = "Short Text";
QMessageBox box;
box.setWindowTitle(title);
int titleLen = title.length();
int txtLen = txt.length();
if (txtLen < titleLen)
{
int diff = titleLen - txtLen;
txt.resize(titleLen + (diff * 2),' '); // diff * 2 is extra padding;
}
box.setText(txt);
box.exec();
You can see I kludged the padding length because of variable width fonts. But hey, the kludge works for me.

Transpaprent QLabel

#include <QtGui>
class Label : public QLabel
{
public:
Label(QWidget *parent =0) :
QLabel(parent)
{
resize(100, 100);
setText("hello");
show();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Label l;
return a.exec();
}
This outputs a label 'hello' with a background. I'm using Qt4. I want to make the background of this label completely transparent. But setWindowOpacity changes the whole widget transparency. I want the content as it is, but only the background to be transparent.
I found this as simpler....
QWidget::setAttribute(Qt::WA_TranslucentBackground);
You can use stylesheets to set the color and the alpha value of the background:
setStyleSheet("background-color: rgba(0,0,0,0%)");
In PyQt:
lbl.setAttribute(Qt.WA_TranslucentBackground, True)
If you define a QColor with alpha of 0 you will get background with transparent color, so for example:
QColor bg_color(255, 0, 0, 0);
QPalette p(l.palette());
p.setColor(QPalette::BackgroundColor, bg_color);
l.setPalette(p);
Which should make label's background whatever color transparent.
In my case with Qt5, the following worked:
movieLabel->setAutoFillBackground(false);
Tested both with Qt 5.11.1 and Qt 5.8.0.