I am trying to implement a QComboBox, which holds QIcon and QString, like this:
QComboBox.addItem(icon, label);
I want the icons to be visible in the drop-down list, but not in the toolbar. Only the string should be visible after item selection.
Is there an easy way to do this?
To solve this problem, it is enough to override the paintEvent method, taking the default implementation from the sources. Before drawing control QStyle::CE_ComboBoxLabel, it is necessary to set an invalid value of the QStyleOptionComboBox.currentIcon
This works well if the combobox is not editable, otherwise there is an empty space on the left side intended for drawing the icon. Looking at the source, I found out that the combobox changes the geometry of the QLineEdit. If the current element has an icon then geometrically QLineEdit will shift to the right. In order to prevent this in the same paintEvent, it is necessary to force the geometry of QLineEdit without taking into account the icon.
The following code takes this into account and works well in both modes:
class ComboBox : public QComboBox {
public:
ComboBox(QWidget *parent = nullptr)
: QComboBox(parent) { }
protected:
void paintEvent(QPaintEvent *) override {
QStylePainter painter(this);
painter.setPen(palette().color(QPalette::Text));
// draw the combobox frame, focusrect and selected etc.
QStyleOptionComboBox opt;
initStyleOption(&opt);
painter.drawComplexControl(QStyle::CC_ComboBox, opt);
// draw the icon and text
opt.currentIcon = QIcon();
if (auto le = lineEdit()) {
le->setGeometry(style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this));
} else {
painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
}
}
};
The best way is to set a delegate and to draw the items yourself.
Then you can choose when or not to draw the icon ( decorationRole ), you can choose not to draw the icon for the index which is the current index.
I could find a quick example on how to use a delegate on combobox:
http://programmingexamples.net/wiki/Qt/Delegates/ComboBoxDelegate
But I am afraid it is not the most easist of the ways.
Good luck!
Related
How can I control the color of its pixmap in disabled label state?
For some bizarre reasons I need to have exactly the same look of the pixmap in active and disabled state (displayed logo).
The pixmap I put on a QLabel with label->setPixmap(pm) is always shown in a different color than the active state when the label is in disabled state.
I struggled with the stylesheet and tried QFrame:disabled{background-color: rgba(..., ..., ..., 255);} but the part of the label which is covered with the pixmap is always a mix with another color, which seems to come from Qt's controlling for the disabled state.
EDIT: It seems, Qt always mixes the pixmap color and the background color in disabled state. But Qt does not mix the colors in active state; then the pixmap color stays opaque. I need to switch off this mixing behavior of the disabled state.
A (not so complicated) way to achieve that, is to draw the pixmap by yourself. Instead of subclassing QLabel and override paintEvent, you can install an event filter in your label and listen for QPaintEvent's only.
Have the filter:
class Filter : public QObject
{
Q_OBJECT
public:
Filter(): QObject(nullptr) {}
bool eventFilter(QObject *watched, QEvent *event);
};
In its eventFilter method, always return false, but when you draw the pixmap:
#include <QPaintEvent>
#include <QPainter>
#include <QStyle>
bool Filter::eventFilter(QObject *watched, QEvent *event)
{
if(event->type() == QEvent::Paint)
{
QLabel * label = dynamic_cast<QLabel*>(watched);
QPainter painter(label);
QPixmap pixmap = label->pixmap()->scaled(label->size());
label->style()->drawItemPixmap(&painter, label->rect(), Qt::AlignHCenter | Qt::AlignVCenter, pixmap);
return true;
}
return false;
}
Instantiate and install the filter, something like:
ui->setupUi(this);
Filter * filter = new Filter();
ui->label->installEventFilter(filter);
/* don't forget to call:
delete filter;
somewhere later */
In my example code, I scaled the pixmap to fit the label size and centered it both horizontally and vertically, but you can adjust all this, according to your needs.
Moreover, the same filter can be installed to more than one label, since the logic works fine for them all. More on event filtering here.
I can get a border to display on my QLabels just fine:
But when I try to display a pixmap in them, the border goes away:
I set the frame properties in the constructor of my QLabel subclass:
ObjectSlot::ObjectSlot(int index) {
setIndex(index);
setFrameShape(QFrame::StyledPanel);
setFrameShadow(QFrame::Raised);
setLineWidth(3);
setMidLineWidth(3);
setAlignment(Qt::AlignCenter);
return;
}
The pixmap is set in the paintEvent:
void ObjectSlot::paintEvent(QPaintEvent* event) {
QPixmap* image = new QPixmap("://images/Box.png");
setPixmap(image->scaled(width(),height(),Qt::KeepAspectRatio));
QLabel::paintEvent(event);
}
Why does the border go away? Why is life so cruel?
As doc said:
Setting the pixmap clears any previous content. The buddy shortcut, if
any, is disabled.
So it seems that it is impossible, but I found next solution, you shouldn't setPixmap(), you need just drawPixmap() when all correct label was painted:
void ObjectSlot::paintEvent(QPaintEvent *e)
{
QLabel::paintEvent(e);
//label painted
QPainter p(this);
QPixmap image("G:/2/qt.png");
p.drawPixmap(QPoint(1,1),image.scaled(100,100,Qt::KeepAspectRatio));
}
Result:
Not the best solution because you should adapt it to your purposes, but currently better than nothing.
I have grid of QFrames in QGridLayout and a popup menu with some actions, which are targeted on the cell where mouse right click happens. On the implementation of ContextMenuEvent I get clicked QPoint using common event->pos() but how I get access to my correct cell object by that point? Or is there some better alternative solution path for this purpose?
void X::contextMenuEvent(QContextMenuEvent* event)
{ // QPoint target = event->pos();
// TODO: m_gridLayout-> ...
// myDerivedCell->setSomething();
}
There are a bunch of solutions here. The simplest is to go through your widgets, calling bool QWidget::underMouse () const. My favorite is this:
frame_i->setContextMenuPolicy(Qt::CustomContextMenu);
connect(frame_i, SIGNAL(customContextMenuRequested(QPoint))
, SLOT(onContextMenu(QPoint)));
...
void X::onContextMenu(const QPoint &pos)
{
QFrame *w = qobject_cast < QFrame * >(sender());
...
}
I have a QGraphicsView window on my widget and have just put in an event for mouse wheel which zooms in on the image.
However as soon as i zoom in scroll bars are displayed and the scroll functionality on the mouse wheel overrides the zoom function i have.
i was wondering if there is any way that i can remove scrolling all together and add a drag to move option or maybe a CTRL and mouse wheel to zoom and mouse wheel alone would control scrolling
here is my zoom function (Which im aware isnt perfect) but if anyone could shed some light on that it would be a bonus
cheers in advance
void Test::wheelEvent(QWheelEvent *event)
{
if(event->delta() > 0)
{
ui->graphicsView->scale(2,2);
}
else
{
ui->graphicsView->scale(0.5,0.5);
}
}
You reimplemented wheelEvent for QWidget/QMainWindow that contains your QGraphicsView, however, wheelEvent of QGraphicsView remains intact.
You can derive from QGraphicsView, reimplement wheelEvent for derived class and use derive class instead of QGraphicsView - this way you won't even need wheelEvent in your QWidget/QMainWindow, and you can customize reimplemented wheelEvent to do what you want. Something like that:
Header file:
class myQGraphicsView : public QGraphicsView
{
public:
myQGraphicsView(QWidget * parent = nullptr);
myQGraphicsView(QGraphicsScene * scene, QWidget * parent = nullptr);
protected:
virtual void wheelEvent(QWheelEvent * event);
};
Source file:
myQGraphicsView::myQGraphicsView(QWidget * parent)
: QGraphicsView(parent) {}
myQGraphicsView::myQGraphicsView(QGraphicsScene * scene, QWidget * parent)
: QGraphicsView(scene, parent) {}
void myQGraphicsView::wheelEvent(QWheelEvent * event)
{
// your functionality, for example:
// if ctrl pressed, use original functionality
if (event->modifiers() & Qt::ControlModifier)
{
QGraphicsView::wheelEvent(event);
}
// otherwise, do yours
else
{
if (event->delta() > 0)
{
scale(2, 2);
}
else
{
scale(0.5, 0.5);
}
}
}
Scrolling can be disabled with the following code:
ui->graphicsView->verticalScrollBar()->blockSignals(true);
ui->graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->graphicsView->horizontalScrollBar()->blockSignals(true);
ui->graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
I think your question has a bit simpler answer.. To disable scroll bars just set scroll bar policy (QGraphicsView is just QScrollView), so step 1)
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
that will disable scroll bars..
step 2) (if you want to keep it simple)
QGraphicsView * pView; // pointer to your graphics view
pView->setInteractive(true);
pView->setDragMode(QGraphicsView::ScrollHandDrag);
thats the fastest way to get results you want
I am trying to display a square image and have an X in the top right corner (half in the image half outside) to close the image. I dont know of a layout manager that will allow me to do that. How do I implement this?
+---------O <- close button
| |
| |
+---------+
There will be a lot to implement here. I've accomplished this in the following way:
Step 1. Subclass QLabel to make it possible to capture mouse clicks. In the header declare signals Clicked and Pressed, and override the proper mouse events.
LabelButton::LabelButton(QWidget *parent) : QLabel(parent)
{
}
void LabelButton::mouseReleaseEvent(QMouseEvent *event){
emit Clicked();
event->accept();
}
void LabelButton::mousePressEvent(QMouseEvent *event){
emit Pressed();
event->accept();
}
Step 2. Add a LabelButton called xbutton containing an circular 'x' image to your widget at the location that you desire.
Example (This would be in your setupUi function):
...
xbutton = new LabelButton(MainWidget);
xbutton->setObjectName(QString::fromUtf8("xbutton"));
xbutton->setGeometry(QRect(0, 0, 31, 31));
xbutton->setPixmap(QPixmap(QString::fromUtf8(":/xbutton.gif")));
xbutton->setAlignment(Qt::AlignCenter);
...
Step 3. Create your widget. Set its background to transparent, and make sure its size includes room for the 'x' close button. Note: Setting your background to transparent means your widget will have to contain some child widget that accepts input from the user.
Example:
mywidget::mywidget(QWidget *parent): QWidget(parent){
setupUi(this);
moving=false; // notice that you must declare this bool for Step 4.
offset=QPoint(0,0); // Also a QPoint for Step 4
#if defined(Q_WS_MAC) //These values worked for me with the Mac OS 10.5 SDK
this->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | Qt::Window);
QPalette pal = this->palette();
pal.setColor(this->backgroundRole(), Qt::transparent);
this->setPalette(pal);
#elif defined(Q_WS_WIN)//These values worked for me on Windows XP/Vista/7
this->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |Qt::FramelessWindowHint | Qt::Window);
setStyleSheet("background:transparent;");
setAttribute(Qt::WA_TranslucentBackground);
#endif
connect(xbutton,SIGNAL(Clicked()),this,SLOT(hide()));
}
Now you have the functionality that you originally desired. When you click the xbutton, the window will close. But you will not have normal move functionality until you implement that.
Step 4. Implement move functionality to your widget.
/*
FUNCTION:mousePressEvent
used to help move the widget since there is no title bar, sets the initial offset of the mouse
*/
void mywidget::mousePressEvent(QMouseEvent *event){
if((event->button() == Qt::LeftButton)) {
moving = true;
offset = event->globalPos() - this->pos();
}
}
/*
FUNCTION:mouseReleaseEvent
used to help move the widget since there is no title bar, releases the "moving" attribute
*/
void mywidget::mouseReleaseEvent(QMouseEvent *event){
if(event->button() == Qt::LeftButton) {
moving = false;
}
}
/*
FUNCTION:mouseMoveEvent
used to help move the widget since there is no title bar
*/
void mywidget::mouseMoveEvent(QMouseEvent *event){
if(moving){
QPoint global = event->globalPos();
this->setGeometry(global.x()-offset.x(),global.y()-offset.y(),this->width(),this->height());
}
}
I found this way to be of most use to me, because I needed lots of functionality out of my customized slick-looking widget.
I really enjoy creative user interfaces and I hope that yours looks really sleek when you get it finished!