duilib开发(十二):使用自定义控件

一、新建一个窗口用来播放 yuv 文件

1、新建一个窗口,添加两个 Button,一个播放按钮,一个暂停按钮

<?xml version="1.0" encoding="UTF-8"?>
<Window size="960,720" mininfo="600,400" caption="0,0,0,32" sizebox="4,4,4,4">
  <Font id="0" shared="true" name="宋体" size="18" bold="false" underline="false" italic="false" />
  <Font id="1" shared="true" name="宋体" size="20" bold="false" underline="false" italic="false" />
  <Font id="2" shared="true" name="宋体" size="16" bold="false" underline="false" italic="false" />
  <Default shared="true" name="Button" value=" height=&quot;30&quot; width=&quot;100&quot; normalimage=&quot;file=&apos;common/button_normal.bmp&apos;&quot; hotimage=&quot;file=&apos;common/button_over.bmp&apos;&quot; pushedimage=&quot;file=&apos;common/button_down.bmp&apos;&quot; font=&quot;0&quot;" />

  <VerticalLayout bkcolor="#FFDFFDF0">  <!-- 整个窗口使用 HorizontalLayout 布局 -->
  	<VerticalLayout vscroll="true" hscroll="true">
      <Button name="btnPlay" text="播放" float="true" pos="330,600,430,630"/>
      <Button name="btnPause" text="暂停" float="true" pos="460,600,560,630"/>
  	</VerticalLayout>
  </VerticalLayout>
</Window>

2、把之前 d3d9 播放 yuv 的代码拿过来:https://blog.csdn.net/yp18792574062/article/details/108942384,这里有一个小改动就是指定了渲染区域的大小

RECT rect{ 0,0,960,544 };
d3d9_device_->Present(NULL, &rect, NULL, NULL);

3、在点击播放按钮的时候开始播放,点击暂停的时候开始暂停

void PlayerWnd::OnClick(DuiLib::TNotifyUI& msg)
{
    if (msg.pSender->GetName() == "btnPlay") {
        if (_running) {
            return;
        }
        _running = true;
        if (_work.joinable()) {
            _work.join();
        }
        std::thread work = std::thread([&]() {
            
            uint32_t width = 480;
            uint32_t height = 272;

            if (!_yuv_player)
            {
                _yuv_player = std::make_shared<D3D9Player>();
                _yuv_player->Init(width, height, I420);
                _yuv_player->SetWindow(_ownerWnd);

            }
            uint8_t* data = new uint8_t[width * height * 3 / 2];
            while (_running) {
                _fin.read((char*)data, width * height * 3 / 2);
                _yuv_player->Render(data);
                if (_fin.eof())
                {
                    _fin.clear();
                    _fin.seekg(0);
                }
                std::this_thread::sleep_for(std::chrono::milliseconds(40));
            }
            std::cout << "end" << std::endl;
            delete[] data;
        });
        work.detach();
    }
    else if (msg.pSender->GetName() == "btnPause")
    {
        _running = false;
    }
}

4、界面如下图所示,整个流程跑起来也都是正常的

5、我们使用自定义的控件封装 win32 的窗体,然后去做和上面相同的事情

my_window.h

#pragma once

#include "UIlib.h"

class CMyWindowUI : public DuiLib::CControlUI
{
public:
    CMyWindowUI();

    ~CMyWindowUI();

    virtual void SetInternVisible(bool bVisible = true) override;

    virtual void SetPos(RECT rc);

    BOOL Attach(HWND hWndNew);

    HWND Detach();

    HWND GetHwnd();

private:
    HWND _hWnd{};
};

my_window.cpp

#include "my_window.h"

CMyWindowUI::CMyWindowUI()
{

}

CMyWindowUI::~CMyWindowUI()
{

}

void CMyWindowUI::SetInternVisible(bool bVisible)
{
    __super::SetInternVisible(bVisible);
    ::ShowWindow(_hWnd, bVisible);
}

void CMyWindowUI::SetPos(RECT rc)
{
    __super::SetPos(rc);
    ::SetWindowPos(_hWnd, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOACTIVATE);
}

BOOL CMyWindowUI::Attach(HWND hWndNew)
{
    if (!::IsWindow(hWndNew))
    {
        return FALSE;
    }

    _hWnd = hWndNew;
    return TRUE;
}

HWND CMyWindowUI::Detach()
{
    HWND hWnd = _hWnd;
    _hWnd = NULL;
    return hWnd;
}

HWND CMyWindowUI::GetHwnd()
{
    return _hWnd;
}

在 xml 里面添加自定义的控件,暂时没有指定大小,后面指定

<VideoDisplay name="videoDisplay" />

新的类需要继承 DuiLib::IDialogBuilderCallback

class PlayerWnd : public DuiLib::CWindowWnd, public DuiLib::INotifyUI, public DuiLib::IDialogBuilderCallback

重写 CreateControl 函数

DuiLib::CControlUI* CreateControl(LPCTSTR name) override;

 将 builder.Create 第三个参数改成 this

DuiLib::CControlUI* pRoot = builder.Create(_T("player.xml"), (UINT)0, this, &_paintManager);

在 cpp 里面实现 CreateControl 函数

DuiLib::CControlUI* PlayerWnd::CreateControl(LPCTSTR pstrClass)
{
    if (_tcscmp(pstrClass, _T("MyWindow")) == 0) {
        CMyWindowUI* wndui = new CMyWindowUI();
        HWND wnd = CreateWindow(_T("STATIC"), _T(""),
            WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
            0, 0, 0, 0, _paintManager.GetPaintWindow(), (HMENU)0, NULL, NULL);
        wndui->Attach(wnd);
        return wndui;
    }
    return NULL;
}

在点击播放按钮的时候获取这个控件的 HWND 并给到 player

HWND hwnd = _renderWnd->GetHwnd();
_renderWnd->SetPos({ 0,0,960,544 });
::ShowWindow(hwnd, true);
_yuv_player->SetWindow(hwnd);

以上只是实现了一个很简单的自定义控件,更多操作后面用到了慢慢补充 

全部代码

player.h

#pragma once

#include "UIlib.h"

#include <thread>
#include <fstream>

#include "my_window.h"

class D3D9Player;

class PlayerWnd : public DuiLib::CWindowWnd, public DuiLib::INotifyUI, public DuiLib::IDialogBuilderCallback
{
public:
    PlayerWnd();
    ~PlayerWnd();

    void Init();
    bool CreateDUIWindow();
    void ShowWindow();

    LPCTSTR GetWindowClassName() const override;
    void Notify(DuiLib::TNotifyUI& msg) override;
    LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) override;
    DuiLib::CControlUI* CreateControl(LPCTSTR pstrClass) override;

    LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam);

    void OnClick(DuiLib::TNotifyUI& msg);
    void OnValueChange(DuiLib::TNotifyUI& msg);

    void InitWindow();

private:
    HINSTANCE _hInstance;
    DuiLib::CPaintManagerUI _paintManager{};
    HWND _ownerWnd{};

    bool _running{ false };
    std::thread _work{};
    std::ifstream _fin{};
    std::shared_ptr<D3D9Player> _yuv_player{};

    CMyWindowUI* _renderWnd{};
};

player.cpp

#include "player.h"

#include <iostream>
#include <chrono>

#include "d3d9_player.h"

#include "my_window.h"
#include "resource.h"

PlayerWnd::PlayerWnd()
{
    
}
PlayerWnd::~PlayerWnd()
{
    std::cout << "~PlayerWnd()" << std::endl;
    _running = false;
    if (_work.joinable())
    {
        _work.join();
    }
	if (_fin)
	{
		_fin.close();
	}
}

void PlayerWnd::Init()
{
    SetProcessDPIAware();
    _hInstance = GetModuleHandle(0);
    DuiLib::CPaintManagerUI::SetInstance(_hInstance);
    DuiLib::CPaintManagerUI::SetResourcePath(DuiLib::CPaintManagerUI::GetInstancePath() + _T("resources"));
	std::string filename = DuiLib::CPaintManagerUI::GetInstancePath() + "resources\\480x272_yuv420p.yuv";
	_fin.open(filename, std::ios::in | std::ios::binary);
}
bool PlayerWnd::CreateDUIWindow()
{
    _ownerWnd = Create(NULL, _T("Player"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
    if (!_ownerWnd)
    {
        std::cout << "create dui window failed" << std::endl;
        return false;
    }
    return true;
}
void PlayerWnd::ShowWindow()
{
    ShowModal();
}

LPCTSTR PlayerWnd::GetWindowClassName() const
{
    return _T("DUIPlayerFrame");
}
void PlayerWnd::Notify(DuiLib::TNotifyUI& msg)
{
    if (msg.sType == _T("click"))
    {
        OnClick(msg);
    }
    else if (msg.sType == _T("valuechanged"))
    {
        OnValueChange(msg);
    }
}
LRESULT PlayerWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LRESULT lRes = 0;
    switch (uMsg) {
    case WM_CREATE:
        lRes = OnCreate(uMsg, wParam, lParam);
        break;
    case WM_CLOSE:
        lRes = OnClose(uMsg, wParam, lParam);
        break;
    default:
        break;
    }
    if (_paintManager.MessageHandler(uMsg, wParam, lParam, lRes))
    {
        return lRes;
    }

    return __super::HandleMessage(uMsg, wParam, lParam);
}

LRESULT PlayerWnd::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    _paintManager.Init(m_hWnd);
    DuiLib::CDialogBuilder builder;
    DuiLib::CControlUI* pRoot = builder.Create(_T("player.xml"), (UINT)0, this, &_paintManager);
    _paintManager.AttachDialog(pRoot);
    _paintManager.AddNotifier(this);
    InitWindow();

    return 0;
}
LRESULT PlayerWnd::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	_running = false;
	if (_work.joinable())
	{
		_work.join();
	}
    return 0;
}

void PlayerWnd::OnClick(DuiLib::TNotifyUI& msg)
{
    if (msg.pSender->GetName() == "btnPlay") {
        _renderWnd = static_cast<CMyWindowUI*>(_paintManager.FindControl(_T("videoDisplay")));
        if (_running) {
            return;
        }
        _running = true;
        if (_work.joinable()) {
            _work.join();
        }
        std::thread work = std::thread([&]() {
            
            uint32_t width = 480;
            uint32_t height = 272;

            if (!_yuv_player)
            {
                _yuv_player = std::make_shared<D3D9Player>();
                _yuv_player->Init(width, height, I420);
                HWND hwnd = _renderWnd->GetHwnd();
                _renderWnd->SetPos({ 0,0,960,544 });
                ::ShowWindow(hwnd, true);
                _yuv_player->SetWindow(hwnd);

            }
            uint8_t* data = new uint8_t[width * height * 3 / 2];
            while (_running) {
                _fin.read((char*)data, width * height * 3 / 2);
                _yuv_player->Render(data);
                if (_fin.eof())
                {
                    _fin.clear();
                    _fin.seekg(0);
                }
                std::this_thread::sleep_for(std::chrono::milliseconds(40));
            }
            delete[] data;
        });
        work.detach();
    }
    else if (msg.pSender->GetName() == "btnPause")
    {
        _running = false;
    }
}
void PlayerWnd::OnValueChange(DuiLib::TNotifyUI& msg)
{

}

void PlayerWnd::InitWindow()
{
    SetIcon(IDI_ICON1);
}

DuiLib::CControlUI* PlayerWnd::CreateControl(LPCTSTR pstrClass)
{
    if (_tcscmp(pstrClass, _T("MyWindow")) == 0) {
        CMyWindowUI* wndui = new CMyWindowUI();
        HWND wnd = CreateWindow(_T("STATIC"), _T(""),
            WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
            0, 0, 0, 0, _paintManager.GetPaintWindow(), (HMENU)0, NULL, NULL);
        wndui->Attach(wnd);
        return wndui;
    }
    return NULL;
}

二、参考资料

1、duilib 自定义控件播放 opencv 视频:https://my.oschina.net/u/3949534/blog/3107549

2、duilib 创建自定义控件:https://www.itdaan.com/blog/2017/05/04/3dff944303ab.html

3、duilib 创建自定义控件过程:https://blog.csdn.net/zhuhongshu/article/details/45362751

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Duilib是一个基于C++的开源UI库,它提供了丰富的控件和功能,可以用于快速开发Windows桌面应用程序。在Duilib中,你可以自定义控件来满足特定的需求。 下面是一个示例,演示如何在Duilib中创建一个自定义控件: ```cpp // 自定义控件的头文件 CustomControl.h #pragma once #include "UIlib.h" class CCustomControl : public DuiLib::CControlUI { public: CCustomControl(); virtual ~CCustomControl(); LPCTSTR GetClass() const; LPVOID GetInterface(LPCTSTR pstrName); void DoEvent(DuiLib::TEventUI& event); void PaintStatusImage(HDC hDC); protected: bool m_bMouseHover; bool m_bMousePressed; }; // 自定义控件的实现文件 CustomControl.cpp #include "CustomControl.h" CCustomControl::CCustomControl() : m_bMouseHover(false) , m_bMousePressed(false) { } CCustomControl::~CCustomControl() { } LPCTSTR CCustomControl::GetClass() const { return _T("CustomControl"); } LPVOID CCustomControl::GetInterface(LPCTSTR pstrName) { if (_tcscmp(pstrName, _T("CustomControl")) == 0) return static_cast<CCustomControl*>(this); return CControlUI::GetInterface(pstrName); } void CCustomControl::DoEvent(DuiLib::TEventUI& event) { if (event.Type == DuiLib::UIEVENT_MOUSEENTER) { m_bMouseHover = true; Invalidate(); } else if (event.Type == DuiLib::UIEVENT_MOUSELEAVE) { m_bMouseHover = false; Invalidate(); } else if (event.Type == DuiLib::UIEVENT_BUTTONDOWN) { m_bMousePressed = true; Invalidate(); } else if (event.Type == DuiLib::UIEVENT_BUTTONUP) { m_bMousePressed = false; Invalidate(); } CControlUI::DoEvent(event); } void CCustomControl::PaintStatusImage(HDC hDC) { if (m_bMousePressed) { // 绘制按下状态的控件外观 } else if (m_bMouseHover) { // 绘制鼠标悬停状态的控件外观 } else { // 绘制正常状态的控件外观 } } ``` 在上面的示例中,我们创建了一个名为`CCustomControl`的自定义控件类,继承自`DuiLib::CControlUI`。在这个类中,我们重写了一些方法来处理控件的事件和绘制外观。你可以根据自己的需求来实现这些方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值