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

在 Qt4 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 <QtGui>
  2. #include "burning.h"
  3. Burning::Burning(QWidget *parent)
  4. : QFrame(parent) {
  5. initUI();
  6. }
  7. void Burning::initUI() {
  8. const int MAX_VALUE = 750;
  9. cur_width = 0;
  10. slider = new QSlider(Qt::Horizontal , this);
  11. slider->setMaximum(MAX_VALUE);
  12. slider->setGeometry(50, 50, 130, 30);
  13. connect(slider, SIGNAL(valueChanged(int)),
  14. this, SLOT(valueChanged(int)));
  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, SIGNAL(valueChanged(int)),
  2. this, SLOT(valueChanged(int)));

当我们移动滑块时,将执行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. Q_OBJECT
  6. public:
  7. Widget(QWidget *parent = 0);
  8. protected:
  9. void paintEvent(QPaintEvent *e);
  10. void drawWidget(QPainter &qp);
  11. private:
  12. QWidget *m_parent;
  13. Burning *burn;
  14. };

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

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

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

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. const int DISTANCE = 19;
  17. const int LINE_WIDTH = 5;
  18. const int DIVISIONS = 10;
  19. const float FULL_CAPACITY = 700;
  20. const float MAX_CAPACITY = 750;
  21. QString num[] = { "75", "150", "225", "300", "375", "450",
  22. "525", "600", "675" };
  23. int asize = sizeof(num)/sizeof(num[1]);
  24. QColor redColor(255, 175, 175);
  25. QColor yellowColor(255, 255, 184);
  26. int width = size().width();
  27. Burning *burn = (Burning *) m_parent;
  28. int cur_width = burn->getCurrentWidth();
  29. int step = (int) qRound(width / DIVISIONS);
  30. int till = (int) ((width / MAX_CAPACITY) * cur_width);
  31. int full = (int) ((width / MAX_CAPACITY) * FULL_CAPACITY);
  32. if (cur_width >= FULL_CAPACITY) {
  33. qp.setPen(yellowColor);
  34. qp.setBrush(yellowColor);
  35. qp.drawRect(0, 0, full, 30);
  36. qp.setPen(redColor);
  37. qp.setBrush(redColor);
  38. qp.drawRect(full, 0, till-full, PANEL_HEIGHT);
  39. } else if (till > 0) {
  40. qp.setPen(yellowColor);
  41. qp.setBrush(yellowColor);
  42. qp.drawRect(0, 0, till, PANEL_HEIGHT);
  43. }
  44. QColor grayColor(90, 80, 60);
  45. qp.setPen(grayColor);
  46. for (int i=1; i <=asize; i++) {
  47. qp.drawLine(i*step, 0, i*step, LINE_WIDTH);
  48. QFont newFont = font();
  49. newFont.setPointSize(7);
  50. setFont(newFont);
  51. QFontMetrics metrics(font());
  52. int w = metrics.width(num[i-1]);
  53. qp.drawText(i*step-w/2, DISTANCE, num[i-1]);
  54. }
  55. }

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

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

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

  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是介质的最大容量。

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

这是主文件。

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

图:刻录小部件

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