Open CASCADE+Qt:实现简单的窗口程序

简介

这篇小的博文来说明如何搭建基于 OCCT+Qt 的简单的窗口程序(没有其它多余配置,简单实用)。环境配置这里就不讲了,可以参考官网文档以及网上其它教程,这里默认你已经具有了 OCCT 以及 Qt 的开发环境。本文中介绍一种常用方式来实现基于 OCCT+Qt 的简单的窗口程序,另一种实现方式下期介绍。

方式一

首先创建一个继承自QWidget的类:OCCWidget,配置其构造函数(都是固定配置,没有什么好讲):

 

OCCWidget::OCCWidget(QWidget *parent)
    : QWidget{parent}
{
    // 直接绘制在屏幕上
    this->setAttribute(Qt::WA_PaintOnScreen);
    // 创建连接显示设备
    Handle(Aspect_DisplayConnection) m_Aspect_DisplayConnect = new Aspect_DisplayConnection();
    // 创建3D接口定义图形驱动
    Handle(OpenGl_GraphicDriver) driver = new OpenGl_GraphicDriver(m_Aspect_DisplayConnect);
    // 创建3D查看器对象,并指定图形驱动
    m_viewer = new V3d_Viewer(driver);
    // 创建交互上下文对象,关联到3D查看器
    m_context = new AIS_InteractiveContext(m_viewer);
    // 创建视图,并关联到3D查看器
    m_view = m_viewer->CreateView();
    // 获取窗口句柄并创建WNT_Window
    WId window_handle = (WId) winId();
    Handle(WNT_Window) wind = new WNT_Window((Aspect_Handle) window_handle);
    // 设置视图窗口
    m_view->SetWindow(wind);
    if (!wind->IsMapped()) wind->Map();
}

此外,还需要重写父类中的三个虚函数paintEnginepaintEventresizeEvent

QPaintEngine* OCCWidget::paintEngine() const
{
    return nullptr;
}
void OCCWidget::paintEvent( QPaintEvent* /*theEvent*/ )
{
    m_view->Redraw();
}
void OCCWidget::resizeEvent( QResizeEvent* /*theEvent*/ )
{
    if( !m_view.IsNull() )
    {
        m_view->MustBeResized();
    }
}

配置好OCCWidget类后,在MainWindow中实例化类OCCWidget

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    // 创建一个名为m_occWidget的OCCWidget实例
    m_occWidget = new OCCWidget(this);
    // 将OCCWidget设置为主窗口的中央部件
    this->setCentralWidget(m_occWidget);
}

做好上述配置后,在主函数中实例化MainWindow,并显示基于 OCCT+Qt 的窗口程序:

int main(int argc, char *argv[]){
    QApplication app(argc, argv);
    MainWindow mainWindow;
    mainWindow.show();
    mainWindow.resize(800,600);
    return app.exec();
}

运行程序,便可以显示基本的窗口,默认窗口背景为灰色:

避坑处

避坑的地方有两个,其一是在main函数中:

mainWindow.show();
mainWindow.resize(800,600);

上述代码不能交换顺序,需要首先显示出窗口,然后再调用resize函数。该函数会调用我们上文中重写的虚函数使得 OCCT 窗口铺满 Qt 中,否则窗口会出现如下状况:

可以看出来,OCCT 窗口在左下角,没有铺满 Qt 窗口。

第二个需要注意的地方是,MainWindow构造函数中,绑定了 ui:

ui->setupUi(this);

我们打开setupUi函数:

    void setupUi(QMainWindow *MainWindow)
    {
        if (MainWindow->objectName().isEmpty())
            MainWindow->setObjectName("MainWindow");
        MainWindow->resize(640, 450);
        centralwidget = new QWidget(MainWindow);
        centralwidget->setObjectName("centralwidget");
        MainWindow->setCentralWidget(centralwidget);

        retranslateUi(MainWindow);

        QMetaObject::connectSlotsByName(MainWindow);
    } // setupUi

发现其中有调用resize函数,并且传入了窗口的宽和高分别为 640 和 450,与我们在主函数中调用的 resize 函数传入的宽和高分别为 800 和 600 并不同,由于前者在构造函数中首先被调用,因此,最终显示主函数中 resize 设置的窗口大小。

这里我是故意而为之,如果两者传入参数相同,窗口同样会出现如下状况:

猜测原因是因为,当两者设置相同参数后,Qt机制中默认第二次调用 resize,并不会去调用上文中OCCWidget类中重写的三个虚函数,导致 OCCT 窗口并没有铺满 Qt 窗口。

 

 

 

方式二

方式一中,OCCWidget构造函数中通过获取当前Qt窗口句柄,并创建WNT_Window,从而将OCCT窗口绑定在当前Qt窗口中。

OCCWidget::OCCWidget(QWidget *parent)
    : QWidget{parent}
{
    ...
    // 获取窗口句柄并创建WNT_Window
    WId window_handle = (WId) winId();
    Handle(WNT_Window) wind = new WNT_Window((Aspect_Handle) window_handle);
    // 设置视图窗口
    m_view->SetWindow(wind);
    if (!wind->IsMapped()) wind->Map();
}

尽管OCCT源文件中有该类对应的源文件以及头文件,但我们在最新版本的OCCT手册中却找不到该类。查看OCCT-7.0.0版本手册,可以看到,WNT_Window继承自Aspect_Window

从7.1.0版本开始,WNT_Window貌似被取消了:

虽然在源文件中,仍然可以找到该类对应的源文件,但可能处于某种原因,OCCT官方并不推荐原来的方式。

那么我们其实可以从Aspect_Window继承一个新类,Aspect_Window是一个抽象类,参考7.8.0版本的帮助文档,要实现以下其纯虚函数:

virtual void  Map () const =0
virtual void  Unmap () const =0
virtual Aspect_TypeOfResize  DoResize () const =0
virtual Standard_Boolean  DoMapping () const =0
virtual Standard_Boolean  IsMapped () const =0
virtual Quantity_Ratio  Ratio () const =0
virtual void  Position (Standard_Integer &X1, Standard_Integer &Y1, Standard_Integer &X2, Standard_Integer &Y2) const =0
virtual void  Size (Standard_Integer &Width, Standard_Integer &Height) const =0
virtual Aspect_Drawable  NativeHandle () const =0
virtual Aspect_Drawable  NativeParentHandle () const =0
virtual Aspect_FBConfig  NativeFBConfig () const =0

虚函数的实现参考了WNT_Window源文件以及开源程序

OCCT_Window::OCCT_Window(QWidget* Widget, const Quantity_NameOfColor theBackColor)
    : Aspect_Window(),
    m_Widget(Widget),
    m_dpiScale(Widget->devicePixelRatioF())
{
    SetBackground (theBackColor);
    m_XLeft   = qRound(m_dpiScale*m_Widget->rect().left());
    m_YTop    = qRound(m_dpiScale*m_Widget->rect().top());
    m_XRight  = qRound(m_dpiScale*m_Widget->rect().right());
    m_YBottom = qRound(m_dpiScale*m_Widget->rect().bottom());
}
//! Opens the window <me>.
void OCCT_Window::Map() const{
    m_Widget->show();
    m_Widget->update();
}
//! Closes the window <me>.
void OCCT_Window:: Unmap() const{
    m_Widget->hide();
    m_Widget->update();
}
//! Apply the resizing to the window <me>.
Aspect_TypeOfResize OCCT_Window::DoResize(){
    int                 aMask = 0;
    Aspect_TypeOfResize aMode = Aspect_TOR_UNKNOWN;

    if ( !m_Widget->isMinimized() )
    {
        if ( Abs ( m_dpiScale*m_Widget->rect().left()   - m_XLeft   ) > 2 ) aMask |= 1;
        if ( Abs ( m_dpiScale*m_Widget->rect().right()  - m_XRight  ) > 2 ) aMask |= 2;
        if ( Abs ( m_dpiScale*m_Widget->rect().top()    - m_YTop    ) > 2 ) aMask |= 4;
        if ( Abs ( m_dpiScale*m_Widget->rect().bottom() - m_YBottom ) > 2 ) aMask |= 8;

        switch ( aMask )
        {
        case 0:
            aMode = Aspect_TOR_NO_BORDER;
            break;
        case 1:
            aMode = Aspect_TOR_LEFT_BORDER;
            break;
        case 2:
            aMode = Aspect_TOR_RIGHT_BORDER;
            break;
        case 4:
            aMode = Aspect_TOR_TOP_BORDER;
            break;
        case 5:
            aMode = Aspect_TOR_LEFT_AND_TOP_BORDER;
            break;
        case 6:
            aMode = Aspect_TOR_TOP_AND_RIGHT_BORDER;
            break;
        case 8:
            aMode = Aspect_TOR_BOTTOM_BORDER;
            break;
        case 9:
            aMode = Aspect_TOR_BOTTOM_AND_LEFT_BORDER;
            break;
        case 10:
            aMode = Aspect_TOR_RIGHT_AND_BOTTOM_BORDER;
            break;
        default:
            break;
        }  // end switch

        *( ( Standard_Integer* )&m_XLeft  ) = qRound(m_dpiScale*m_Widget->rect().left());
        *( ( Standard_Integer* )&m_XRight ) = qRound(m_dpiScale*m_Widget->rect().right());
        *( ( Standard_Integer* )&m_YTop   ) = qRound(m_dpiScale*m_Widget->rect().top());
        *( ( Standard_Integer* )&m_YBottom) = qRound(m_dpiScale*m_Widget->rect().bottom());
    }

    return aMode;
}
//! Apply the mapping change to the window <me>.
//! and returns TRUE if the window is mapped at screen.
Standard_Boolean OCCT_Window::DoMapping() const{
    return Standard_True;
};
//! Returns True if the window <me> is opened
//! and False if the window is closed.
Standard_Boolean OCCT_Window::IsMapped() const{
    return !(m_Widget->isMinimized() || m_Widget->isHidden());
}
//! Returns The Window RATIO equal to the physical
//! WIDTH/HEIGHT dimensions
Standard_Real OCCT_Window::Ratio() const{
    QRect aRect = m_Widget->rect();
    return Standard_Real( aRect.right() - aRect.left() ) / Standard_Real( aRect.bottom() - aRect.top() );
}
void OCCT_Window::Position (Standard_Integer& X1, Standard_Integer& Y1, Standard_Integer& X2, Standard_Integer& Y2) const {
    X1 = qRound(m_dpiScale*m_Widget->rect().left());
    X2 = qRound(m_dpiScale*m_Widget->rect().right());
    Y1 = qRound(m_dpiScale*m_Widget->rect().top());
    Y2 = qRound(m_dpiScale*m_Widget->rect().bottom());
}
//! Returns The Window SIZE in PIXEL
void OCCT_Window::Size (Standard_Integer& Width, Standard_Integer& Height) const{
    QRect aRect = m_Widget->rect();
    Width  = m_dpiScale*aRect.width();
    Height = m_dpiScale*aRect.height();
}


//! Returns native Window handle (HWND on Windows, Window with Xlib, and so on)
Aspect_Drawable OCCT_Window::NativeHandle() const{
    return (Aspect_Drawable)m_Widget->winId();
}
//! Returns parent of native Window handle (HWND on Windows, Window with Xlib, and so on)
Aspect_Drawable OCCT_Window::NativeParentHandle() const{
    QWidget* parentWidget = m_Widget->parentWidget();
    if (parentWidget) {
        return (Aspect_Drawable)parentWidget->winId();
    }else{
        return 0;
    }
}
//! Returns native Window FB config (GLXFBConfig on Xlib)
Aspect_FBConfig OCCT_Window::NativeFBConfig() const{
    return nullptr;
}

因此,OCCWidget构造函数相应部分修改为:

OCCWidget::OCCWidget(QWidget *parent)
    : QWidget{parent}
{
    ...
    // 获取窗口句柄并创建OCCT_Window
    OCCT_Window* wind = new OCCT_Window(this);
    // 设置视图窗口
    m_view->SetWindow(wind);
    if (!wind->IsMapped()) wind->Map();
}

程序执行结果为:

 

posted @   unicornsir  阅读(29)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
历史上的今天:
2023-01-16 c# ?的用法
点击右上角即可分享
微信分享提示