4

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.

Image showing the 'Box' and the hover indicators of the plastique and Win7 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

Anti Earth
  • 4,671
  • 13
  • 52
  • 83
  • Silly question, but if you need a visibly clickable label, a QPushButton is exactly that. You can play with the button's style (raised/lowered, a few other things). Why not use that? – Mat Jan 22 '12 at 09:58
  • A reasonable question! The text of the Box must be rich text. If it's possible to set rich text on a button, then my cheecks will be read indeed! :| – Anti Earth Jan 22 '12 at 10:03
  • Plus, how do you set raised/lowered on a QPushButton (it doesn't have a QFrame... does it?) – Anti Earth Jan 23 '12 at 04:04

2 Answers2

6

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
  • 12,444
  • 4
  • 29
  • 65
  • When I pass `pushButton` to `label`'s constructor I mean I made `label` a child of the `pushButton`. And then yes, I made new layout for `pushButton` and added `label` to it – Lol4t0 Jan 22 '12 at 13:03
  • Could you please explain the lower piece of code in words? (I see you are subclassing QLabel, but what exactly are you having the program perform when mouseReleaseEvent is triggered?). Thanks! – Anti Earth Jan 22 '12 at 13:14
  • 1
    i override `mouseReleaseEvent` (as you did), then, you remeber, that the `parent` of my `label` is `QPushButton`, so I cast parent from `QObject` to `QPushButton` and call method `setDown(false)`, that makes button release – Lol4t0 Jan 22 '12 at 13:17
  • I've updated the OP with my implementation. Just having some signalling issues now, but I'll see if I can't solve them myself. Thanks for the help! – Anti Earth Jan 22 '12 at 13:38
  • 4
    Adding `label->setTextInteractionFlags(Qt::NoTextInteraction);` fixes the always pressed issue. –  Jan 22 '12 at 16:41
  • Marvelous. You two gentlemen are wizards. – Anti Earth Jan 23 '12 at 01:13
  • @Lol4t0, i have some improvements for your method here: http://stackoverflow.com/a/10860800/440168 – k06a Jun 02 '12 at 08:12
4

@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...

k06a
  • 17,755
  • 10
  • 70
  • 110
  • Very sensible if I were to re-use the widget. I'm using fixed positions however, so it doesn't really matter. Thanks though! – Anti Earth Jun 03 '12 at 03:24