如何绘制自定义控件然后传播事件?

时间:2013-12-24 07:34:13

标签: qt

例如,我尝试创建的一个小部件是QToolButtonQLineEdit的混合。

leaveEvent()出现时,它显示为QToolButton,左侧带有图标/文字&右侧的{-1}}菜单箭头。当(QToolButton.MenuButtonPopup出现时,按钮的左半部分(图标/文字)变为enterEvent(),而右半部分仍然是下拉菜单。

我一直在浏览谷歌搜索带来的任何论坛,虽然我松散地了解如何绘制它,我如何正确地连接事件?如果我绘制QLineEdit,我该如何让自定义窗口小部件的那部分行动并像QLineEdit一样回复? (Python首选,C ++没问题)

[QLineEdit将无效,而且我想创建的其他小部件肯定没有任何近似的等效内容。此外,我知道我可以用其他更简单的小部件构建一个小部件,然后弄乱样式表来实现类似的外观,但我知道上面有一个答案,因为Qt做到了,它会有更好的交叉 - 系统样式然后编写自定义样式表。]

1 个答案:

答案 0 :(得分:1)

理解这个问题的关键是认识到没有实际的小部件存在就没有行为。因此,仅仅使用样式机制来绘制给定的控件是不够的。您需要QWidget的实例(例如,QToolButtonQLineEdit)。一旦你拥有了它,你也可以像孩子一样拥有它,让Qt的事件传播处理适当的事件。这会引起很多麻烦。

如果您希望工具按钮的行为具有嵌入式行编辑,则最简单的方法是将行编辑实际作为工具按钮的子窗口小部件。您从QToolButton继承,并根据需要显示/隐藏行编辑子项。此继承还允许您利用控件窗口小部件中存在的密钥保护方法:initStyleOption。使用组合(has-a)而不是继承(is-a),你需要一个仅将QToolButton::initStyleOption方法公开给朋友类的适配器类。

从焦点角度来看,您的预期行为很成问题。只使用键盘时,该按钮应可编辑。仅仅使用鼠标进入/离开事件来暴露行编辑器 。合理的方法可能是按 Enter 返回进入编辑模式 - 是的,这是两个不同的键!

您还需要了解Qt的可访问性如何工作,并确保您的窗口小部件的行为正常 - 我根本没有解决过这个问题。总而言之,设计控件(而不仅仅是可视化小部件)并不是一项简单的任务,如果您真的需要“调整”标准控件而不是通过样式设置,您总是需要三思而后行。

下面的代码演示了一个相当小的实现,它仍然在焦点方面表现得相当合理。我没有在OS X 10.9上测试它,除了Qt 4.8.5和5.2,所以YMMV,但我希望它能适用于任何合理的平台风格。

最后的挑剔:我不太明白你的意思是“QComboBox无效”。您的实现本质上是一个混淆的组合框,与典型的HIG(人机界面指南)不兼容。你基本上是在制作一个基于工具按钮的组合框混蛋孩子,看起来很奇怪。它不是一个组合框,菜单与按钮的内容完全分开。有什么意义?

screenshot

#include <QApplication>
#include <QToolButton>
#include <QLineEdit>
#include <QGridLayout>
#include <QStyle>
#include <QStyleOptionButton>
#include <QMenu>
#include <QPushButton>
#include <QKeyEvent>
#include <QDebug>

class EditButton : public QToolButton {
    Q_OBJECT
    QLineEdit * m_edit;
    QRect m_textGeometry;
    Q_PROPERTY(QString text READ text WRITE setText NOTIFY newText USER true)
public:
    EditButton(QWidget * parent = 0) : QToolButton(parent),
        m_edit(new QLineEdit(this))
    {
        m_edit->hide();
        connect(m_edit, SIGNAL(textChanged(QString)), SIGNAL(newText(QString)));
        connect(m_edit, SIGNAL(editingFinished()), SLOT(hideEditor()));
        setFocusPolicy(Qt::StrongFocus);
    }
    QString text() const { return m_edit->text(); }
    void setText(const QString & text) {
        if (text == m_edit->text()) return;
        m_edit->setText(text);
        QToolButton::setText(text);
        emit newText(text);
    }
    Q_SIGNAL void newText(const QString &);
protected:
    void resizeEvent(QResizeEvent * ev) {
        QToolButton::resizeEvent(ev);
        setEditGeometry();
    }
    void keyPressEvent(QKeyEvent * ev) {
        if (ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return) {
            showEditor();
            return;
        }
        QToolButton::keyPressEvent(ev);
    }
    void enterEvent(QEvent * ev) {
        showEditor();
        QToolButton::enterEvent(ev);
    }
    void leaveEvent(QEvent * ev) {
        hideEditor();
        QToolButton::leaveEvent(ev);
    }
private:
    Q_SLOT void hideEditor() {
        QToolButton::setText(m_edit->text());
        setFocusProxy(0);
        m_edit->hide();
        update();
    }
    void showEditor() {
        setEditGeometry();
        m_edit->show();
        setFocusProxy(m_edit);
        setFocus();
    }
    void setEditGeometry() {
        QStyleOptionToolButton opt;
        initStyleOption(&opt);
        QRect r = style()->subControlRect(QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButton, this);
        m_edit->setGeometry(r);
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget window;
    QGridLayout * l = new QGridLayout(&window);
    EditButton * btn = new EditButton;
    btn->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
    QMenu * menu = new QMenu(btn);
    menu->addAction("Action!");
    btn->setMenu(menu);
    btn->setPopupMode(QToolButton::MenuButtonPopup);
    btn->setText("Foo Bar Baz");
    l->addWidget(btn);
    l->addWidget(new QPushButton("Focus Test Button"));
    window.show();
    return a.exec();
}

#include "main.moc"