如何为QWidget与QOpenGLWidget子类实现统一接口?
目标
对多个绘图库做基准测试并收集诊断信息,计划给每个基准测试实现统一接口,之后在主窗口中实例化不同类。
挑战
部分基准测试基于QWidget,另一部分基于QOpenGLWidget,即便功能逻辑一致,调用时也得动态转换,操作繁琐。
已尝试方案
一开始想过用虚拟多重继承,但没法正常工作,也不确定这是不是正确方向。也考虑过组合或适配器模式,但需要在每个基准测试里重写QWidget的resizeEvent这类函数,虽然可以复制代码或者用非成员函数,但想找更优雅的实现方式。
现有代码示例
QWidgets扩展类
class BenchmarkAddin : virtual public QWidget { public: BenchmarkAddin() { connect(&timer_, SIGNAL(timeout()), this, SLOT(update())); } double get_fps() { // use frames_ and time to estimate fps } void count_frame() { ++frames_; } void set_parameters(int param1_) { param1_; } protected: void resizeEvent(QResizeEvent* event) override { init(); } virtual void init() = 0; int param1_; private: int frames_; QTimer timer_; };
光栅基准测试类
class RasterBenchmark : public BenchmarkAddin { protected: void init() override { // create buffers } void paintEvent(QPaintEvent* event) override { // do drawing using param1_ count_frame(); } };
OpenGL基准测试类
class OpenGLBenchmark : virtual public QOpenGLWidget, public BenchmarkAddin { protected: void paintGL() override { // do GL drawing using param1_ count_frame(); } };
主窗口示例用法(无法正常运行)
BenchmarkAddin *widget; if (benchmark == "raster") { widget = new RasterBenchmark(this); } else { widget = new OpenGLBenchmark(this); } widget->set_parameters(100); // ... std::cout << widget->get_fps() << std::endl;
问题在于QOpenGLWidget没有对QWidget使用虚拟继承,同时Qt对象元系统也存在兼容性问题,导致上述代码无法运行。
核心问题
如何实现一个能同时适配QWidget和QOpenGLWidget子类的统一接口?
针对Qt中QWidget和QOpenGLWidget的继承关系限制,推荐以下两种优雅的实现方式:
方法一:纯虚接口+适配器类
定义一个不依赖Qt控件的纯虚接口,把基准测试的核心逻辑(FPS统计、参数设置等)抽离出来,然后分别为QWidget和QOpenGLWidget实现适配器类,再让具体的基准测试类继承对应的适配器。
步骤1:定义纯虚接口
class IBenchmark { public: virtual ~IBenchmark() = default; virtual double get_fps() = 0; virtual void set_parameters(int param1) = 0; virtual void count_frame() = 0; virtual void init() = 0; };
步骤2:实现QWidget基准适配器
把原BenchmarkAddin的通用逻辑移到这个适配器里,继承QWidget和IBenchmark:
class WidgetBenchmarkAdapter : public QWidget, public IBenchmark { Q_OBJECT public: WidgetBenchmarkAdapter(QWidget* parent = nullptr) : QWidget(parent) { connect(&timer_, SIGNAL(timeout()), this, SLOT(update())); start_time_ = QDateTime::currentMSecsSinceEpoch(); } double get_fps() override { qint64 current_time = QDateTime::currentMSecsSinceEpoch(); double elapsed = (current_time - start_time_) / 1000.0; if (elapsed <= 0) return 0; return frames_ / elapsed; } void count_frame() override { ++frames_; } void set_parameters(int param1) override { param1_ = param1; } protected: void resizeEvent(QResizeEvent* event) override { QWidget::resizeEvent(event); init(); } int param1_; private: int frames_ = 0; QTimer timer_; qint64 start_time_; };
步骤3:实现OpenGL基准适配器
针对QOpenGLWidget实现适配器,同样继承QOpenGLWidget和IBenchmark:
class OpenGLBenchmarkAdapter : public QOpenGLWidget, public IBenchmark { Q_OBJECT public: OpenGLBenchmarkAdapter(QWidget* parent = nullptr) : QOpenGLWidget(parent) { connect(&timer_, SIGNAL(timeout()), this, SLOT(update())); start_time_ = QDateTime::currentMSecsSinceEpoch(); } double get_fps() override { qint64 current_time = QDateTime::currentMSecsSinceEpoch(); double elapsed = (current_time - start_time_) / 1000.0; if (elapsed <= 0) return 0; return frames_ / elapsed; } void count_frame() override { ++frames_; } void set_parameters(int param1) override { param1_ = param1; } protected: void resizeGL(int w, int h) override { QOpenGLWidget::resizeGL(w, h); init(); } int param1_; private: int frames_ = 0; QTimer timer_; qint64 start_time_; };
步骤4:实现具体基准测试类
- 光栅基准测试:
class RasterBenchmark : public WidgetBenchmarkAdapter { Q_OBJECT protected: void init() override { // 创建缓冲区等初始化逻辑 } void paintEvent(QPaintEvent* event) override { // 使用param1_进行光栅绘图 count_frame(); } };
- OpenGL基准测试:
class OpenGLBenchmark : public OpenGLBenchmarkAdapter { Q_OBJECT protected: void init() override { // OpenGL上下文初始化、着色器加载等逻辑 } void paintGL() override { // 使用param1_进行OpenGL绘图 count_frame(); } };
主窗口调用
现在可以通过IBenchmark接口统一操作,同时如果需要把控件添加到窗口,只需动态转换为QWidget(因为QOpenGLWidget本身也是QWidget的子类):
IBenchmark* benchmark; QWidget* widget; if (benchmark_type == "raster") { auto* raster = new RasterBenchmark(this); benchmark = raster; widget = raster; } else { auto* gl = new OpenGLBenchmark(this); benchmark = gl; widget = gl; } benchmark->set_parameters(100); widget->show(); // 添加到窗口布局或直接显示 // ... std::cout << benchmark->get_fps() << std::endl;
方法二:组合模式(基准逻辑与控件分离)
把基准测试的核心逻辑(FPS统计、参数管理)封装成一个独立的类,然后在QWidget和QOpenGLWidget的子类中组合这个类,通过转发调用实现统一接口。
步骤1:封装基准核心逻辑
class BenchmarkCore { public: BenchmarkCore() { start_time_ = QDateTime::currentMSecsSinceEpoch(); } double get_fps() { qint64 current_time = QDateTime::currentMSecsSinceEpoch(); double elapsed = (current_time - start_time_) / 1000.0; if (elapsed <= 0) return 0; return frames_ / elapsed; } void count_frame() { ++frames_; } void set_parameters(int param1) { param1_ = param1; } int get_param1() const { return param1_; } private: int frames_ = 0; int param1_ = 0; qint64 start_time_; };
步骤2:定义统一接口(可选,用于约束)
class IBenchmarkWidget { public: virtual ~IBenchmarkWidget() = default; virtual double get_fps() = 0; virtual void set_parameters(int param1) = 0; virtual QWidget* get_widget() = 0; };
步骤3:实现具体基准测试类
- 光栅基准测试:
class RasterBenchmark : public QWidget, public IBenchmarkWidget { Q_OBJECT public: RasterBenchmark(QWidget* parent = nullptr) : QWidget(parent) { connect(&timer_, SIGNAL(timeout()), this, SLOT(update())); } double get_fps() override { return core_.get_fps(); } void set_parameters(int param1) override { core_.set_parameters(param1); } QWidget* get_widget() override { return this; } protected: void resizeEvent(QResizeEvent* event) override { QWidget::resizeEvent(event); init(); } void paintEvent(QPaintEvent* event) override { // 使用core_.get_param1()进行光栅绘图 core_.count_frame(); } private: void init() { // 创建缓冲区等初始化逻辑 } BenchmarkCore core_; QTimer timer_; };
- OpenGL基准测试:
class OpenGLBenchmark : public QOpenGLWidget, public IBenchmarkWidget { Q_OBJECT public: OpenGLBenchmark(QWidget* parent = nullptr) : QOpenGLWidget(parent) { connect(&timer_, SIGNAL(timeout()), this, SLOT(update())); } double get_fps() override { return core_.get_fps(); } void set_parameters(int param1) override { core_.set_parameters(param1); } QWidget* get_widget() override { return this; } protected: void resizeGL(int w, int h) override { QOpenGLWidget::resizeGL(w, h); init(); } void paintGL() override { // 使用core_.get_param1()进行OpenGL绘图 core_.count_frame(); } private: void init() { // OpenGL上下文初始化逻辑 } BenchmarkCore core_; QTimer timer_; };
主窗口调用
IBenchmarkWidget* benchmark; if (benchmark_type == "raster") { benchmark = new RasterBenchmark(this); } else { benchmark = new OpenGLBenchmark(this); } benchmark->set_parameters(100); benchmark->get_widget()->show(); // 添加到窗口 // ... std::cout << benchmark->get_fps() << std::endl;
方案对比
- 方法一:通过适配器类复用通用控件逻辑,代码复用率高,适合需要重写多个控件事件(如
resizeEvent、paintEvent)的场景,接口清晰。 - 方法二:核心逻辑与控件完全解耦,更灵活,适合后续扩展其他非Qt控件的基准测试,维护成本低。
内容的提问来源于stack exchange,提问作者Chris

