How to place an icon onto a QLineEdit? - qt

There is a Search field with the magnification-lens and a greyed out "search" keyword at the top right corner of stackoverflow.com web site:
I wonder if it is possible to achieve a same appearance with QLineEdit. If so then how?

QLineEdit* _lineEdit = new QLineEdit();
_lineEdit->setClearButtonEnabled(true);
_lineEdit->addAction(":/resources/search.ico", QLineEdit::LeadingPosition);
_lineEdit->setPlaceHolderText("Search...");
extracted from: http://saurabhg.com/programming/search-box-using-qlineedit/

Simple Way for Dummies
Add a QLineEdit, and set it frameless by QLineEdit::setFrame
Add a QLabel with background color in white (by stylesheet) and a icon
Combine the line edit and the label with a layout, set spacing to 0
Set placeholder text with QLineEdit::setPlaceholderText
Result
Advanced Way
Check this thread: "Can QLineEdit do this?"
And the related python code: http://bazaar.launchpad.net/~henning-schroeder/%2Bjunk/qtwidgets/annotate/head:/qtwidgets/lineedit.py
Or
"How to do - inside in QLineEdit insert the button.[pyqt4]"
Basically customized a QLineEdit by painting a widget(label, button or even combobox) onto it. Then reset the margin, cursor, padding and the paint event. No magics!

Here is an alternate simple way:
Set the placeholderText to "🔍" and the font Family to Seqoe UI Symbol or other font that can be found on your target systems that include the U+1F50D LEFT-POINTING MAGNIFYING GLASS glyph.

Here's a way to achieve this with stylesheets only:
QLineEdit {
background: #f3f3f3;
background-image: url(:Images/search.svg); /* actual size, e.g. 16x16 */
background-repeat: no-repeat;
background-position: left;
color: #252424;
font-family: SegoeUI;
font-size: 12px;
padding: 2 2 2 20; /* left padding (last number) must be more than the icon's width */
}
Here's the result:
It's still not perfect. You don't have much influence over the icon's position.

To have a result like this:
You can subclass the QLineEdit.
So your header should look something like this:
#ifndef LINEEDITICON_H
#define LINEEDITICON_H
#include <QLineEdit>
#include <QIcon>
class LineEditIcon : public QLineEdit
{
Q_OBJECT
public:
LineEditIcon(const QIcon icon, QWidget *parent = Q_NULLPTR);
~LineEditIcon();
void setIcon(QIcon icon);
protected:
virtual void paintEvent(QPaintEvent *event);
private:
QIcon m_icon;
};
#endif // LINEEDITICON_H
And your source file look like:
#include "lineediticon.h"
#include <QPainter>
LineEditIcon::LineEditIcon(const QIcon icon, QWidget *parent)
: QLineEdit(parent)
{
setIcon(icon);
}
LineEditIcon::~LineEditIcon()
{
}
void LineEditIcon::setIcon(QIcon icon)
{
m_icon = icon;
if (m_icon.isNull())
setTextMargins(1, 1, 1, 1);
else
setTextMargins(20, 1, 1, 1);
}
void LineEditIcon::paintEvent(QPaintEvent * event)
{
QLineEdit::paintEvent(event);
if (!m_icon.isNull()) {
QPainter painter(this);
QPixmap pxm = m_icon.pixmap(height() - 6, height() - 6);
int x = 2, cx = pxm.width();
painter.drawPixmap(x, 3, pxm);
painter.setPen(QColor("lightgrey"));
painter.drawLine(cx + 2, 3, cx + 2, height() - 4);
}
}
Edit: A possible solution is to use a custom plugin to use it directly in QtDesigner.

QT5 addAction
```
const QIcon passwordIcon(":/new/icons/res/passwd.png");
ui->password->setClearButtonEnabled(true);
ui->password->addAction(passwordIcon, QLineEdit::LeadingPosition);
```

From QT 5.2 onwards you can do something like this. You should use pointers if you want to make it more flexible:
QIcon *ico;
ico = new QIcon(":/images/search.ico");
searchEdit->addAction(*ico, QLineEdit::LeadingPosition);

Related

Drawing rectangle line-by-line doesn't have the same result as drawing it directly

I'm trying to customize the borders of the cells in a (custom) QTableView. To do that, I found this:
class MyDelegate : public QItemDelegate {
public:
MyDelegate( QObject *parent ) : QItemDelegate( parent ) { }
void paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const {
QItemDelegate::paint( painter, option, index );
if( /* some condition */ ) {
painter->setPen( Qt::red );
painter->drawRect( option.rect );
}
}
}
This works well. I added this in my delegate - which already has a custom paint method for other things and it does exactly what I want:
Neat.
Things start to go rogue when I want to decide what borders I actually draw. To do this, each item has a BorderOption property, which describes what borders should be draw. And my grid isn't 1px large anymore.
So what I decided to try out was the following:
painter->drawLine(option.rect.topLeft(), option.rect.topRight());
painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
Which should have the same result, right? Right? Well... no.
It seems like two 1px-width lines are being drawn, one for each cell.
That could make sense, if the cells' rectangles didn't overlap. But we've seen before it is not the case, as the first example worked and produced rectangles of width 1px.
Am I doing something wrong?
Thanks
I think there is a misconception on the meaning of option.rect. It is really the rectangle of the item of the table. There is still a portion, a one pixel wide line, that does not belong to the option.rect of the item, but to the QTreeView itself.
This becomes more visible if you restrict the drawing to the allowed item portion by setting setClipRect and by making the lines distinguishable using different colors.
Try to experiment with the width of the drawn line and by enabling and disabling the clip rect. Maybe you need to draw outside of the allowed option.rec, but be aware that you have to consider also different drawings of QTreeView on different platforms, where the separating lines might be 0px wide or wider than just one pixel.
#pragma once
#include <QStyledItemDelegate>
#include <QItemSelectionModel>
#include <QPainter>
#include <QDebug>
class MyDelegate : public QStyledItemDelegate {
public:
MyDelegate(QObject* parent=nullptr) : QStyledItemDelegate(parent) { }
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
QStyledItemDelegate::paint(painter, option, index);
if (true) {
painter->save();
auto color=QColor::fromHsv((30*index.row() + 30 * index.column()) % 255, 255, 127);
painter->setPen(QPen(color, 1, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin));
painter->setClipRect(option.rect); // Rectangle of the item
painter->drawLine(option.rect.topLeft(), option.rect.topRight());
painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
painter->restore();
}
}
};
Did you tried to set thickness of the pen manually?
painter->setPen(QPen(QColor(255, 0, 0), 3, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin));
painter->drawLine(option.rect.topLeft(), option.rect.topRight());
painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
Here, I set the thickness to the 3px for example!

QTabWidget how to hide pane only?

I have added a QToolButton as corner widget in QTabWidget which is checkable. I want to hide all tabs (panes only) when the tool button is unchecked. I tried to connect button's signal clicked(bool) with all tab's setVisible(bool) slot not working but. I also connected tabwidget's setvisible to the signal but complete widget became invisible(it was a silly trial). Is there any way to make only pane invisible and tab bar will not disappear ?
Edit: Code (ui have a tabwidget and two tabs namely tab and tab_2)
ui->setupUi(this);
QToolButton * b = new QToolButton;
b->setCheckable(true);
b->setChecked(true);
b->setAutoRaise(true);
b->setText("Hide Tabs");
ui->tabWidget->setCornerWidget(b);
connect(b,SIGNAL(clicked()),ui->tab,SLOT(hide()));
connect(b,SIGNAL(clicked()),ui->tab_2,SLOT(hide()));
Use qFindChild to find the QTabBar within the QTabWidget:
QTabBar *tabBar = qFindChild<QTabBar *>(ui->tabWidget);
tabBar->hide();
For Qt5:
QTabBar *tabBar = ui->tabWidget->findChild<QTabBar *>();
tabBar->hide();
so I understand it like this, you want to hide the TabBar and let the tab visible. Or at least that's what I get from your question
Well if that the case all you have to do it's this:
connect(ui->pushButton,SIGNAL(clicked()),ui->tabWidget->tabBar(),SLOT(hide()));
I hope this was helpful, even do the questions in a little old, I though it may help new viewers.
Here is my take on this. I've created a class that inherits QTabWidget. What I do is; set the "maximum vertical size of QTabWidget" to its tabBars height to hide the panels.
It is a hacky solution and I had to add some extra lines to deal with quirks.
file: hidabletabwidget.h
#ifndef HIDABLETABWIDGET_H
#define HIDABLETABWIDGET_H
#include <QTabWidget>
#include <QAction>
class HidableTabWidget : public QTabWidget
{
Q_OBJECT
public:
explicit HidableTabWidget(QWidget *parent = 0);
QAction hideAction;
private slots:
void onHideAction(bool checked);
void onTabBarClicked();
};
#endif // HIDABLETABWIDGET_H
file: hidablewidget.cpp
#include "hidabletabwidget.h"
#include <QTabBar>
#include <QToolButton>
HidableTabWidget::HidableTabWidget(QWidget *parent) :
QTabWidget(parent),
hideAction("▾", this)
{
hideAction.setCheckable(true);
hideAction.setToolTip("Hide Panels");
QToolButton* hideButton = new QToolButton();
hideButton->setDefaultAction(&hideAction);
hideButton->setAutoRaise(true);
this->setCornerWidget(hideButton);
connect(&hideAction, SIGNAL(toggled(bool)), this, SLOT(onHideAction(bool)));
connect(this, SIGNAL(tabBarClicked(int)), this, SLOT(onTabBarClicked()));
}
void HidableTabWidget::onHideAction(bool checked)
{
if (checked)
{
this->setMaximumHeight(this->tabBar()->height());
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
}
else
{
this->setMaximumHeight(QWIDGETSIZE_MAX); // by default widgets can expand to a maximum sized defined by this macro
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}
}
void HidableTabWidget::onTabBarClicked()
{
hideAction.setChecked(false);
}
To use this, you can simply "promote" your QTabWidget to "HidableTabWidget" using qt designer.
And here is how it looks on my system:
You usually want to remove the Tab from the QTabWidget:
void QTabWidget::removeTab ( int index )
The Tab removed will not be deleted and can be reinserted!
So you would connect your QToolButton b to a slot which simply removes the Tabs like this:
connect( b, SIGNAL(clicked()), this, SLOT(hideTabs() );
..
void Foobar::hideTabs( void )
{
for( int i = 0; i < ui->tabWidget->count(); ++i )
ui->tabWidget->removeTab(i);
}
I can not comment due to my low "reputation" so far. If I could I'd just add a comment to Anatoli's answer: the goal is to hide "page area", not "tab bar". So if we imply they always use QStackedWidget for that then the answer should be more like:
auto * tab_pane = qFindChild<QStackedWidget *>(ui->tabWidget);
tab_pane->hide();
or for Qt5:
auto * tab_pane = ui->tabWidget->findChild<QStackedWidget *>();
tab_pane->hide();

Change QWizard buttons size

I am trying to do something that looks easy, but I cannot make it working. I want to make buttons bigger in my QWizard. Here is the code :
#include "wizard.h"
#include "ui_wizard.h"
#include "QAbstractButton"
Wizard::Wizard(QWidget *parent) :
QWizard(parent),
ui(new Ui::Wizard)
{
ui->setupUi(this);
QRect rect = this->button(QWizard::NextButton)->geometry();
this->button(QWizard::NextButton)->setGeometry(rect.x(), rect.y(), rect.width(), 40);
rect = this->button(QWizard::CancelButton)->geometry();
this->button(QWizard::CancelButton)->setGeometry(rect.x(), rect.y(), rect.width(), 40);
rect = this->button(QWizard::BackButton)->geometry();
this->button(QWizard::BackButton)->setGeometry(rect.x(), rect.y(), rect.width(), 40);
}
Wizard::~Wizard()
{
delete ui;
}
This code does nothing. Is it possible to change the geometry of the buttons? Or it is forbidden?
Thank you
Better is to customize user interface using QSS (Qt Style Sheet). You can read your qss file and setup stylesheet for the whole application using QApplication::setStyleSheet().
Also you can setup qss programmatically (not the best practics).
setStyleSheet("QAbstractButton { height: 50px }");
What sets height for all buttons on the widget.
In the worst case you can try this:
button(QWizard::CancelButton)->setStyleSheet("height: 50px");

Subclassing QLabel to show native 'Mouse Hover Button indicator'

I have a QLabel with a 'StyledPanel, raised' frame.
It is clickable, by subclassing QLabel;
class InteractiveLabel(QtGui.QLabel):
def __init__(self, parent):
QtGui.QLabel.__init__(self, parent)
def mouseReleaseEvent(self, event):
self.emit(QtCore.SIGNAL('clicked()'))
However, a general opinion is that this 'Box' is not easily recognised as clickable.
In an effort toward usability, I'd like the 'Box' to show it is clickable when the mouse is hovered over it.
Obviously a reaction to a mouse hover is easily achieved by connecting the mouseHoverEvent.
However, the 'button indicator' must be natively inherited, since my Qt application allows the User to change the style (out of Windows XP, Windows 7, plastique, motif, cde).
This image shows the particular widget (bottom right corner) and the mouseHover aesthetics I desire in two different styles.
When a mouse is hovered over 'Box', I'd like it to respond like the combobox has in the top, middle.
(The 'response' is aesthetically native and occurs with all Qt buttons, except in 'CDE' and 'motif'styles.).
Is there a way to implement this with PyQt4?
(I suppose non-native solutions would involve QGradient and checking the native style, but that's yucky.)
UPDATE:
lol4t0's idea of a QLabel over a QPushButton.
Here's my pythonic implementation with signals working properly and all the appropriate button aesthetics.
The RichTextBox is the widget you would embed into the program.
from PyQt4 import QtCore, QtGui
class RichTextButton(QtGui.QPushButton):
def __init__(self, parent=None):
QtGui.QPushButton.__init__(self, parent)
self.UnitText = QtGui.QLabel(self)
self.UnitText.setTextInteractionFlags(QtCore.Qt.NoTextInteraction)
self.UnitText.setAlignment(QtCore.Qt.AlignCenter)
self.UnitText.setMouseTracking(False)
self.setLayout(QtGui.QVBoxLayout())
self.layout().setMargin(0)
self.layout().addWidget(self.UnitText)
Thanks!
Specs:
- python 2.7.2
- Windows 7
- PyQt4
Main idea
You can add QLabelabove QPushButton (make QLabel child of QPushButton) and show rich text in label, while clicks and decorations can be processed with QPushButton
Experiment
Well, I am a C++ programmer, but there is nothing complicated, I hope, you understand the code
Implementing main idea:
QLabel * label = new QLabel(pushButton);
label->setText("<strong>sss</strong>");
label->setAlignment(Qt::AlignCenter);
label->setMouseTracking(false);
pushButton->setLayout(new QVBoxLayout(pushButton));
pushButton->layout()->setMargin(0);
pushButton->layout()->addWidget(label);
And this almost works! The only one silly bug (or my global misunderstanding) is that when you press button with mouse and then release it, it remains pressed.
- So, it seems we need to reimplement mouseReleaseEvent in our label to fix always pressed issue:
I'm pretty sure, there is a bit more elegant solution, but I'm too lazy to find it now, so, I made following:
class TransperentLabel: public QLabel
{
public:
TransperentLabel(QWidget* parent):QLabel(parent) {}
protected:
void mouseReleaseEvent(QMouseEvent *ev)
{
/*
QApplication::sendEvent(parent(), ev); -- does not help :(
*/
static_cast<QPushButton*>(parent())->setDown(false);
static_cast<QPushButton*>(parent())->click(); //fixing click signal issues
}
};
As #Roku said, to fix that issue, we have to add
label->setTextInteractionFlags(Qt::NoTextInteraction);
#Lol4t0, i have some improvements for your method...
This is my header file:
#ifndef QLABELEDPUSHBUTTON_H
#define QLABELEDPUSHBUTTON_H
#include <QPushButton>
class QLabel;
class QLabeledPushButton : public QPushButton
{
Q_OBJECT
QLabel * m_label;
public:
QLabeledPushButton(QWidget * parent = 0);
QString text() const;
void setText(const QString & text);
protected:
void resizeEvent(QResizeEvent * event);
};
#endif // QLABELEDPUSHBUTTON_H
And there is my cpp file:
#include <QLabel>
#include <QVBoxLayout>
#include <QResizeEvent>
#include "QLabeledPushButton.h"
QLabeledPushButton::QLabeledPushButton(QWidget * parent)
: QPushButton(parent)
, m_label(new QLabel(this))
{
m_label->setWordWrap(true);
m_label->setMouseTracking(false);
m_label->setAlignment(Qt::AlignCenter);
m_label->setTextInteractionFlags(Qt::NoTextInteraction);
m_label->setGeometry(QRect(4, 4, width()-8, height()-8));
}
QString QLabeledPushButton::text() const
{
return m_label->text();
}
void QLabeledPushButton::setText(const QString & text)
{
m_label->setText(text);
}
void QLabeledPushButton::resizeEvent(QResizeEvent * event)
{
if (width()-8 < m_label->sizeHint().width())
setMinimumWidth(event->oldSize().width());
if (height()-8 < m_label->sizeHint().height())
setMinimumHeight(event->oldSize().height());
m_label->setGeometry(QRect(4, 4, width()-8, height()-8));
}
So text on QLabel is always visible. QPushButton can't be too small to hide part of text. I think this way is more comfortable to use...

Can QMessageBox::about adjust size to title length?

I wanted to create a simple About dialog, but noticed that the QMessageBox::about does not adjust its size to the length of the title (which is usually longer due to the larger font ...at least in my desktop environment), only to the content. Is there a way to make sure that the dialog is made big enough to show all of the title as well? I could of course add white space to the aboutText, but I am hoping for a less hackish solution.
Example:
QString titleText("Some title which is slightly longer");
QString aboutText("Short about text");
QMessageBox::about(this,titleText,aboutText);
Currently the above code only gives me "Some ..." as the title string. I have built the program in Eclipse on Ubuntu with Qt 4.7.
Use "setStyleSheet()" function of "QMessageBox". Here is an example.
background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #787878, stop: 0.5 #404040, stop: 0.6 #303030, stop: 0.8 #252525, stop: 1 #151515);
border: 2px solid #05b8cc;
border-radius: 8px;
color: white;
min-width: 300px;
min-height: 80px;
It will also affect the children of the "QMessageBox" whose stylesheets can be reverted by iterating through them. To access the children use "findChildren(QWidget)".
I believe QMessageBox does adjust size to fit the window title, but for some reason it doesn't work right on my system also, not sure if it's a bug or a feature, this is done in the qmessagabox.cpp QMessageBoxPrivate::updateSize() method.
Another thing I've noticed is that you're using an instance of the QMessageBox class to call about() method, which is static and you can execute it by using just the class name: QMessageBox::about(..).
What you could do to adjust the window size is creating your own subclass of the QMessageBox and adjusting min width of the window in the showEvent method, see the example below for details:
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);
}
}
};
here's how you can call it:
QString titleText("Some title which is slightly longer");
QString aboutText("Short about text");
MyMessageBox::about(titleText, aboutText);
hope this helps, regards

Resources