原文: http://zetcode.com/gui/qt5/customwidget/

在 Qt5 C++ 编程教程的这一部分中,我们将创建一个自定义小部件。

大多数工具箱通常仅提供最常用的小部件,例如按钮,文本小部件或滑块。 没有工具包可以提供所有可能的小部件。 程序员必须自己创建此类小部件。 他们使用工具箱提供的绘图工具来完成此任务。 有两种可能:程序员可以修改或增强现有的小部件,或者可以从头开始创建自定义小部件。

刻录小部件

在下一个示例中,我们创建一个自定义的刻录小部件。 可以在 Nero 或 K3B 之类的应用中看到此小部件。 该小部件将从头开始创建。

burning.h

  1. #pragma once
  2. #include <QWidget>
  3. #include <QSlider>
  4. #include <QFrame>
  5. #include "widget.h"
  6. class Burning : public QFrame {
  7. Q_OBJECT
  8. public:
  9. Burning(QWidget *parent = 0);
  10. int getCurrentWidth();
  11. public slots:
  12. void valueChanged(int);
  13. private:
  14. QSlider *slider;
  15. Widget *widget;
  16. int cur_width;
  17. void initUI();
  18. };

这是示例主窗口的头文件。

  1. public:
  2. Burning(QWidget *parent = 0);
  3. int getCurrentWidth();

getCurrentWidth()方法将用于确定滑块值。

  1. private:
  2. QSlider *slider;
  3. Widget *widget;
  4. int cur_width;
  5. void initUI();

窗口的工作区上将有两个小部件:内置滑块小部件和自定义小部件。 cur_width变量将保存滑块中的当前值。 绘制自定义窗口小部件时使用此值。

burning.cpp

  1. #include <QVBoxLayout>
  2. #include <QHBoxLayout>
  3. #include "burning.h"
  4. Burning::Burning(QWidget *parent)
  5. : QFrame(parent) {
  6. initUI();
  7. }
  8. void Burning::initUI() {
  9. const int MAX_VALUE = 750;
  10. cur_width = 0;
  11. slider = new QSlider(Qt::Horizontal , this);
  12. slider->setMaximum(MAX_VALUE);
  13. slider->setGeometry(50, 50, 130, 30);
  14. connect(slider, &QSlider::valueChanged, this, &Burning::valueChanged);
  15. QVBoxLayout *vbox = new QVBoxLayout(this);
  16. QHBoxLayout *hbox = new QHBoxLayout();
  17. vbox->addStretch(1);
  18. widget = new Widget(this);
  19. hbox->addWidget(widget, 0);
  20. vbox->addLayout(hbox);
  21. setLayout(vbox);
  22. }
  23. void Burning::valueChanged(int val) {
  24. cur_width = val;
  25. widget->repaint();
  26. }
  27. int Burning::getCurrentWidth() {
  28. return cur_width;
  29. }

在这里,我们构建示例的主窗口。

  1. connect(slider, &QSlider::valueChanged, this, &Burning::valueChanged);

当我们移动滑块时,将执行valueChanged()槽。

  1. void Burning::valueChanged(int val) {
  2. cur_width = val;
  3. widget->repaint();
  4. }

更改滑块的值时,我们将存储新值并重新绘制自定义窗口小部件。

widget.h

  1. #pragma once
  2. #include <QFrame>
  3. class Burning;
  4. class Widget : public QFrame {
  5. public:
  6. Widget(QWidget *parent = 0);
  7. protected:
  8. void paintEvent(QPaintEvent *e);
  9. void drawWidget(QPainter &qp);
  10. private:
  11. QWidget *m_parent;
  12. Burning *burn;
  13. static const int DISTANCE = 19;
  14. static const int LINE_WIDTH = 5;
  15. static const int DIVISIONS = 10;
  16. static const float FULL_CAPACITY = 700;
  17. static const float MAX_CAPACITY = 750;
  18. };

这是自定义刻录窗口小部件的头文件。

  1. private:
  2. QWidget *m_parent;
  3. Burning *burn;

我们存储一个指向父窗口小部件的指针。 我们通过该指针获得cur_width

  1. const int DISTANCE = 19;
  2. const int LINE_WIDTH = 5;
  3. const int DIVISIONS = 10;
  4. const float FULL_CAPACITY = 700;
  5. const float MAX_CAPACITY = 750;

这些是重要的常数。 DISTANCE是比例尺上的数字与其父边界顶部之间的距离。 LINE_WIDTH是垂直线的宽度。 DIVISIONS是秤的数量。 FULL_CAPACITY是媒体的容量。 达到目标后,就会发生过度刻录。 这通过红色可视化。 MAX_CAPACITY是介质的最大容量。

widget.cpp

  1. #include <QtGui>
  2. #include "widget.h"
  3. #include "burning.h"
  4. const int PANEL_HEIGHT = 30;
  5. Widget::Widget(QWidget *parent)
  6. : QFrame(parent) {
  7. m_parent = parent;
  8. setMinimumHeight(PANEL_HEIGHT);
  9. }
  10. void Widget::paintEvent(QPaintEvent *e) {
  11. QPainter qp(this);
  12. drawWidget(qp);
  13. QFrame::paintEvent(e);
  14. }
  15. void Widget::drawWidget(QPainter &qp) {
  16. QString num[] = { "75", "150", "225", "300", "375", "450",
  17. "525", "600", "675" };
  18. int asize = sizeof(num)/sizeof(num[1]);
  19. QColor redColor(255, 175, 175);
  20. QColor yellowColor(255, 255, 184);
  21. int width = size().width();
  22. Burning *burn = (Burning *) m_parent;
  23. int cur_width = burn->getCurrentWidth();
  24. int step = (int) qRound((double)width / DIVISIONS);
  25. int till = (int) ((width / MAX_CAPACITY) * cur_width);
  26. int full = (int) ((width / MAX_CAPACITY) * FULL_CAPACITY);
  27. if (cur_width >= FULL_CAPACITY) {
  28. qp.setPen(yellowColor);
  29. qp.setBrush(yellowColor);
  30. qp.drawRect(0, 0, full, 30);
  31. qp.setPen(redColor);
  32. qp.setBrush(redColor);
  33. qp.drawRect(full, 0, till-full, PANEL_HEIGHT);
  34. } else if (till > 0) {
  35. qp.setPen(yellowColor);
  36. qp.setBrush(yellowColor);
  37. qp.drawRect(0, 0, till, PANEL_HEIGHT);
  38. }
  39. QColor grayColor(90, 80, 60);
  40. qp.setPen(grayColor);
  41. for (int i=1; i <=asize; i++) {
  42. qp.drawLine(i*step, 0, i*step, LINE_WIDTH);
  43. QFont newFont = font();
  44. newFont.setPointSize(7);
  45. setFont(newFont);
  46. QFontMetrics metrics(font());
  47. int w = metrics.width(num[i-1]);
  48. qp.drawText(i*step-w/2, DISTANCE, num[i-1]);
  49. }
  50. }

在这里,我们绘制自定义窗口小部件。 我们绘制矩形,垂直线和数字。

  1. void Widget::paintEvent(QPaintEvent *e) {
  2. QPainter qp(this);
  3. drawWidget(qp);
  4. QFrame::paintEvent(e);
  5. }

自定义窗口小部件的图形委托给drawWidget()方法。

  1. QString num[] = { "75", "150", "225", "300", "375", "450",
  2. "525", "600", "675" };

我们使用这些数字来构建刻录小部件的比例。

  1. int width = size().width();

我们得到小部件的宽度。 自定义窗口小部件的宽度是动态的。 用户可以调整大小。

  1. Burning *burn = (Burning *) m_parent;
  2. int cur_width = burn->getCurrentWidth();

我们得到cur_width值。

  1. int till = (int) ((width / MAX_CAPACITY) * cur_width);
  2. int full = (int) ((width / MAX_CAPACITY) * FULL_CAPACITY);

我们使用width变量在比例尺值和自定义小部件的度量之间进行转换。

  1. qp.setPen(redColor);
  2. qp.setBrush(redColor);
  3. qp.drawRect(full, 0, till-full, PANEL_HEIGHT);

这三行画出红色矩形,表示过度燃烧。

  1. qp.drawLine(i*step, 0, i*step, LINE_WIDTH);

在这里,我们画出小的垂直线。

  1. QFontMetrics metrics(font());
  2. int w = metrics.width(num[i-1]);
  3. qp.drawText(i*step-w/2, DISTANCE, num[i-1]);

在这里,我们绘制刻度的数字。 为了精确定位数字,我们必须获得字符串的宽度。

main.cpp

  1. #include <QApplication>
  2. #include "burning.h"
  3. int main(int argc, char *argv[]) {
  4. QApplication app(argc, argv);
  5. Burning window;
  6. window.resize(370, 200);
  7. window.setWindowTitle("The Burning widget");
  8. window.show();
  9. return app.exec();
  10. }

这是主文件。

Qt5 中的自定义小部件 - 图1

图:刻录小部件

在 Qt5 教程的这一部分中,我们创建了一个自定义的刻录小部件。