QLineEdit: displaying overlong text as a tooltip if hovering with mouse - c++

Under Windows, I've seen a nice feature: If I hover with the mouse over a short text field which contains overlong text not fitting completely into the field, a tooltip opens, displaying the complete contents of the text field.
Can someone point me to a code snippet which does this with QLineEdit?

I would create a custom class derived from QLineEdit like so:
#ifndef LINEEDIT_H
#define LINEEDIT_H
#include <QtGui>
class LineEdit : public QLineEdit
{
Q_OBJECT
public:
LineEdit();
public slots:
void changeTooltip(QString);
};
LineEdit::LineEdit()
{
connect(this, SIGNAL(textChanged(QString)), this, SLOT(changeTooltip(QString)));
}
void LineEdit::changeTooltip(QString tip)
{
QFont font = this->font();
QFontMetrics metrics(font);
int width = this->width();
if(metrics.width(tip) > width)
{
this->setToolTip(tip);
}
else
{
this->setToolTip("");
}
}
#include "moc_LineEdit.cpp"
#endif // LINEEDIT_H
Then just add it to whatever:
#include <QtGui>
#include "LineEdit.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
LineEdit edit;
edit.show();
return app.exec();
}

Here the improved function as mentioned in the comments above.
void LineEdit::changeTooltip(QString tip)
{
QFont font = this->font();
QFontMetrics metrics(font);
// get the (sum of the) left and right borders;
// note that Qt doesn't have methods
// to access those margin values directly
int lineMinWidth = minimumSizeHint().width();
int charMaxWidth = metrics.maxWidth();
int border = lineMinWidth - charMaxWidth;
int lineWidth = this->width();
int textWidth = metrics.width(tip);
if (textWidth > lineWidth - border)
this->setToolTip(tip);
else
this->setToolTip("");
}

You can try to change the tooltip each time the text is changed:
First, define a private slot to react the textChanged() signal from the QLineEdit:
(in the header file from the class where your QTextEdit belongs)
....
private slots:
void onTextChanged();
....
In the cpp file, then, connect the QLineEdit textChanged() signal to the slot you defined, and implement the behavior when the text changes:
// In constructor, or wherever you want to start tracking changes in the QLineEdit
connect(myLineEdit, SIGNAL(textChanged()), this, SLOT(onTextChanged()));
Finally, this is how the slot would look like:
void MainWindow::onTextChanged() {
myLineEdit->setTooltip(myLineEdit->text());
}
I'm supposing a class called MainWindow contains the QLineEdit.

Related

How can I transform QTabWidget coordinates to its child's coordinates?

I'm trying to generate a right-click menu on a QTabWidget (lists) containing only QListWidgets. I get a menu below where I click the distance of the tab bar's height, which is expected because the context menu is applied to the QTabWidget.
void onCustomContextMenuRequested(const QPoint& pos) {
QListWidgetItem * item = ((QListWidget*)(lists->currentWidget()))->itemAt(pos);
if (item) showContextMenu(item, QListWidget(lists->currentWidget()).viewport()->mapToGlobal(pos));
}
void showContextMenu(QListWidgetItem* item, const QPoint& globalPos) {
QMenu menu;
menu.addAction(item->text());
menu.exec(globalPos);
}
I can get the menu to appear at the mouse, while still referring to an item about 100px beneath it, by changing
QListWidget(lists->currentWidget()).viewport()->mapToGlobal(pos));
to
QListWidget(lists->currentWidget()).viewport()->mapToParent(mapToGlobal(pos)));
But I can't get the menu to refer to the item I am clicking on. I have tried transforming to and from parent coordinates to no effect.
QPoint pos_temp = ((QListWidget*)(lists->currentWidget()))->viewport()->mapFromParent(pos);
if (item) showContextMenu(item, QListWidget(lists->currentWidget()).viewport()->mapToGlobal(pos_temp));
I have also tried to and from global coordinate, and combinations of global and parent, to undesirable effect.
So how can I get the right click menu to refer to the item I am clicking on?
The position sent by the customContextMenuRequested signal is with respect to the widget where the connection is established, and in this case I am assuming that it is the main widget so when using itemAt() of QListWidget it throws inadequate values since this method waits for the position with respect to the viewport(). The approach in these cases is to convert that local position to a global one and then map that global to a local position of the final widget.
In the next part I show an example.
#include <QApplication>
#include <QTabWidget>
#include <QListWidget>
#include <QVBoxLayout>
#include <QMenu>
class Widget: public QWidget{
Q_OBJECT
QTabWidget *lists;
public:
Widget(QWidget *parent=Q_NULLPTR):QWidget(parent){
lists = new QTabWidget;
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, &Widget::customContextMenuRequested, this, &Widget::onCustomContextMenuRequested);
auto layout = new QVBoxLayout(this);
layout->addWidget(lists);
for(int i=0; i<4; i++){
auto list = new QListWidget;
lists->addTab(list, QString("tab-%1").arg(i));
for(int j=0; j<10; j++){
list->addItem(QString("item %1-%2").arg(i).arg(j));
}
}
}
private slots:
void onCustomContextMenuRequested(const QPoint& pos){
QPoint globalPos = mapToGlobal(pos);
QListWidget *list = static_cast<QListWidget *>(lists->currentWidget());
if(list){
QPoint p = list->viewport()->mapFromGlobal(globalPos);
QListWidgetItem *item = list->itemAt(p);
if(item)
showContextMenu(item, globalPos);
}
}
void showContextMenu(QListWidgetItem* item, const QPoint& globalPos) {
QMenu menu;
menu.addAction(item->text());
menu.exec(globalPos);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
#include "main.moc"
In the following link is the complete example.

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:

QTableWidget's cell widget not displaying correctly when last row deleted

I have a QTableWidget that has QPushButtons as cell widgets. I use these push buttons as a means to delete a row from the table. I'm having a problem where the QPushButtons can be accidentally shifted from being centered in their cell. The image below shows what is happening.
This occurs in the specific case where the user has 1) made selected a cell in the last row of the table, 2) that cell contains a delete button, and 3) the vertical scroll bar is moved such that the last row is partially visible.
Below is a very minimal example that will create this problem. The user needs to use the scrollbar and scroll up by one tick, then press the last delete button.
mainwindow.h:
#pragma once
#include <QMainWindow>
class QSignalMapper;
class QTableWidget;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void DeletePressed(int row);
private:
QSignalMapper* signalMapper_;
QTableWidget* table_;
};
mainwindow.cpp:
#include "mainwindow.h"
#include <QGridLayout>
#include <QSignalMapper>
#include <QTableWidget>
#include <QPushButton>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
signalMapper_(new QSignalMapper(this)),
table_(new QTableWidget())
{
setCentralWidget( new QWidget );
this->setFixedSize(200,300);
QGridLayout *layout = new QGridLayout(centralWidget());
layout->addWidget(table_);
table_->setRowCount(0);
table_->setColumnCount(1);
// if you comment out the following line then the problem is not present
table_->setSelectionMode(QAbstractItemView::SingleSelection);
for (int i = 0; i < 20; i++) {
table_->insertRow(i);
QPushButton *button = new QPushButton("Delete");
connect(button, SIGNAL(clicked(bool)), signalMapper_, SLOT(map()));
signalMapper_->setMapping(button, i);
table_->setCellWidget(i, 0, button);
}
connect(signalMapper_, SIGNAL(mapped(int)), this, SLOT(DeletePressed(int)));
table_->setCurrentCell(19,0);
}
MainWindow::~MainWindow()
{
}
void MainWindow::DeletePressed(int row)
{
table_->clearSelection(); // <- added clear selection before remove row
table_->removeRow(row);
}
main.cpp:
#include <QApplication>
#include "mainwindow.h"
int main( int argc, char* argv[] )
{
QApplication app(argc, argv);
MainWindow wnd;
wnd.show();
return app.exec();
}
I think the problem stems from the QTableWidget trying to keep the selected cell visible when a new selection is made. I have tried both of the answers posted here but neither solved my problem.
If anyone could tell me how to solve this problem I would appreciate the help.
I had a similar problem a long time ago with a QtreeWidget and I solved it by callig protected method updateGeometries().
If you did not specialize QTableView, you can't simply call this function (as it is protected), so here's a trick to simply do it:
class MyQTableView : public QTableView
{
public:
inline void publicUpdateGeometries() { updateGeometries(); }
};
void MainWindow::DeletePressed(int row)
{
table_->clearSelection(); // <- added clear selection before remove row
table_->removeRow(row);
// call the protected method QTableView::updateGeometries()
((MyQTableView*) table_)->publicUpdateGeometries();
}
Hope this will solve your problem.
Try ui->tableWidget->clearSelection();
This happen only when scroll bar is added.to solve this you need to setvrticalscrollbarpolicy to scroll bar off then delete the row and add the scroll as needed by setverticalbarpolicy.
It will solve your problem.
I did the same way.

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.

QT4.8 - Implement highlight to QLineEdit

I am looking for a way to implement a highlighter for the QLineEdit widget.
I am using a QLineEdit to store a a path variable in my application, and I would to highlight environment variables.
Something like this:
${MY_ENVVAR}/foo/bar/myfile
Practically I would to have something like the QHightligher class.
Subclass QSyntaxHighliger
Write your own highlightBlock() method
Detect in your string this specific text which must be colored (you can do this for example by regular expression QRegExp) and use setFormat() method to paint string from x to x+n with some color
Helpful link: http://qt-project.org/doc/qt-4.8/qsyntaxhighlighter.html#highlightBlock
I never used highliter with QLineEdit before, so it can be impossible. But we can simply attach highliter to the QTextEdit. So we should create lineEdit from textEdit, there are many examples in web how to do that.
For example (I use code given by hyde with small additions.)
TextEdit.h
#ifndef TEXTEDIT_H
#define TEXTEDIT_H
#include <QTextEdit>
#include <QCompleter>
#include <QTextEdit>
#include <QKeyEvent>
#include <QStyleOption>
#include <QApplication>
class TextEdit : public QTextEdit
{
Q_OBJECT
public:
explicit TextEdit(QWidget *parent = 0)
{
setTabChangesFocus(true);
setWordWrapMode(QTextOption::NoWrap);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
setFixedHeight(sizeHint().height());
}
void keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
event->ignore();
else
QTextEdit::keyPressEvent(event);
}
QSize sizeHint() const
{
QFontMetrics fm(font());
int h = qMax(fm.height(), 14) + 4;
int w = fm.width(QLatin1Char('x')) * 17 + 4;
QStyleOptionFrameV2 opt;
opt.initFrom(this);
return (style()->sizeFromContents(QStyle::CT_LineEdit, &opt, QSize(w, h).
expandedTo(QApplication::globalStrut()), this));
}
};
#endif // TEXTEDIT_H
Usage (in the main.cpp)
#include "textedit.h"
//...
TextEdit *t = new TextEdit;
t->show();
new Highlighter(t->document());
Highlighter constructor as example
Highlighter::Highlighter(QTextDocument *parent)
: QSyntaxHighlighter(parent)
{
}