ImGUI 1.87 绘制D3D外部菜单

简介: ImGUI 它是与平台无关的C++轻量级跨平台图形界面库,没有任何第三方依赖,可以将ImGUI的源码直接加到项目中使用,该框架通常会配合特定的D3Dx9等图形开发工具包一起使用,ImGUI常用来实现进程内的菜单功能,而有些辅助开发作者也会使用该框架开发菜单页面,总体来说这是一个很不错的绘图库,如下将公开新版ImGUI如何实现绘制外部菜单的功能。

ImGUI 它是与平台无关的C++轻量级跨平台图形界面库,没有任何第三方依赖,可以将ImGUI的源码直接加到项目中使用,该框架通常会配合特定的D3Dx9等图形开发工具包一起使用,ImGUI常用来实现进程内的菜单功能,而有些辅助开发作者也会使用该框架开发菜单页面,总体来说这是一个很不错的绘图库,如下将公开新版ImGUI如何实现绘制外部菜单的功能。

ImGUI官方下载地址:https://github.com/ocornut/imgui/releases

在使用ImGUI页面之前需要先来实现一个简单的附着功能,即如何将一个窗体附着到另一个窗体之上,其实代码很简单,如下所示当用户输入进程PID时,会自动跟随窗体并附着在窗体顶部。

#include <Windows.h>
#include <iostream>

struct handle_data
{
    unsigned long process_id;
    HWND best_handle;
};

// By: LyShark
BOOL IsMainWindow(HWND handle)
{
    return GetWindow(handle, GW_OWNER) == (HWND)0 && IsWindowVisible(handle);
}

BOOL CALLBACK EnumWindowsCallback(HWND handle, LPARAM lParam)
{
    // By: LyShark
    handle_data& data = *(handle_data*)lParam;
    unsigned long process_id = 0;
    GetWindowThreadProcessId(handle, &process_id);
    if (data.process_id != process_id || !IsMainWindow(handle)) {
        return TRUE;
    }
    data.best_handle = handle;
    return FALSE;
}

// By: LyShark
HWND FindMainWindow(unsigned long process_id)
{
    handle_data data;
    data.process_id = process_id;
    data.best_handle = 0;
    EnumWindows(EnumWindowsCallback, (LPARAM)&data);
    return data.best_handle;
}

int main(int argc, char* argv[])
{
    DWORD pid = 28396;

    std::cout << "输入进程PID: " << std::endl;
    std::cin >> pid;

    // 获取屏幕宽和高
    int iWidth = ::GetSystemMetrics(SM_CXSCREEN);
    int iHeight = ::GetSystemMetrics(SM_CYSCREEN);

    // 根据PID寻找游戏窗口
    HWND hwnd = FindMainWindow(pid);

    while (1)
    {
        SetTimer(hwnd, 1, 150, NULL);

        // 实现透明必须设置WS_EX_LAYERED标志
        LONG lWinStyleEx = GetWindowLong(hwnd, GWL_EXSTYLE);
        lWinStyleEx = lWinStyleEx | WS_EX_LAYERED;

        SetWindowLong(hwnd, GWL_EXSTYLE, lWinStyleEx);
        SetLayeredWindowAttributes(hwnd, 0, RGB(40, 40, 40), LWA_ALPHA);

        // 去掉标题栏及边框
        LONG_PTR Style = GetWindowLongPtr(hwnd, GWL_STYLE);
        Style = Style & ~WS_CAPTION & ~WS_SYSMENU & ~WS_SIZEBOX;
        SetWindowLongPtr(hwnd, GWL_STYLE, Style);

        // 至顶层窗口 最大化
        SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, iWidth, iHeight, SWP_SHOWWINDOW);

        // 设置窗体可穿透鼠标
        SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_TRANSPARENT | WS_EX_LAYERED);

        // 绘图
        HDC hdc = ::GetDC(hwnd);
        HDC mdc = ::CreateCompatibleDC(hdc);

        // 创建画笔
        HPEN hpen = CreatePen(PS_SOLID, 10, RGB(0, 255, 0));
        // DC 选择画笔
        SelectObject(hdc, hpen);
        // (画笔)从初始点移动到 50,50
        MoveToEx(hdc, 100, 100, NULL);
        // (画笔)从初始点画线到 100,100
        LineTo(hdc, 1000, 1000);

        RECT rect = {0};

        rect.bottom = 10;
        rect.left = 20;
        rect.right = 20;
        rect.top = 15;

        DrawText(hdc, L"hello lyshark.com", strlen("hello lyshark.com"), &rect, DT_CALCRECT | DT_CENTER | DT_SINGLELINE);
    }

    return 0;
}

绘制效果图:

image.png

接着我们使用Imgui绘制一个动态菜单,首先下载imgui并打开项目中的examples目录,找到example_win32_directx9打开后自己配置好dx9SDK开发工具包。

image.png

代码直接调用,并附加到Counter-Strike Source游戏窗体之上即可,这段代码也很简单。

#include "imgui.h"
#include "imgui_impl_dx9.h"
#include "imgui_impl_win32.h"

#include <d3d9.h>
#include <tchar.h>
#include <iostream>

#pragma execution_character_set("utf-8")

// 全局变量
// lyshark.com
static HWND hwnd;
static HWND GameHwnd;
static RECT WindowRectangle;
static int WindowWide, WindowHeight;

static LPDIRECT3D9 g_pD3D = NULL;
static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
static D3DPRESENT_PARAMETERS g_d3dpp = {};

// 单选框设置状态
bool show_another_window = false;

// imgui 回调函数
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// By: LyShark
bool CreateDeviceD3D(HWND hWnd)
{
    if ((g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL)
    {
        return false;
    }

    ZeroMemory(&g_d3dpp, sizeof(g_d3dpp));
    g_d3dpp.Windowed = TRUE;
    g_d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    g_d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
    g_d3dpp.EnableAutoDepthStencil = TRUE;
    g_d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
    g_d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;

    if (g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &g_d3dpp, &g_pd3dDevice) < 0)
    {
        return false;
    }
    return true;
}

void CleanupDeviceD3D()
{
    if (g_pd3dDevice)
    {
        g_pd3dDevice->Release();
        g_pd3dDevice = NULL;
    }

    if (g_pD3D)
    {
        g_pD3D->Release();
        g_pD3D = NULL;
    }
}

void ResetDevice()
{
    ImGui_ImplDX9_InvalidateDeviceObjects();
    HRESULT hr = g_pd3dDevice->Reset(&g_d3dpp);
    if (hr == D3DERR_INVALIDCALL)
    {
        IM_ASSERT(0);
    }
    ImGui_ImplDX9_CreateDeviceObjects();
}

LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
    {
        return true;
    }

    switch (msg)
    {
    case WM_SIZE:
        if (g_pd3dDevice != NULL && wParam != SIZE_MINIMIZED)
        {
            g_d3dpp.BackBufferWidth = LOWORD(lParam);
            g_d3dpp.BackBufferHeight = HIWORD(lParam);
            ResetDevice();
        }
        return 0;

    case WM_SYSCOMMAND:
        if ((wParam & 0xfff0) == SC_KEYMENU)
        {
            return 0;
        }
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProc(hWnd, msg, wParam, lParam);
}

// 绘制主方法
// www.cnblogs.com/lyshark
void DrawImGUI()
{
    // 启动IMGUI自绘
    ImGui_ImplDX9_NewFrame();
    ImGui_ImplWin32_NewFrame();
    ImGui::NewFrame();

    static float f = 0.0f;
    static int counter = 0;
    static char sz[256] = { 0 };

    ImGui::Begin("LyShark 辅助GUI主菜单");
    ImGui::Text("这是一段测试字符串");
    ImGui::Checkbox("弹出子窗口", &show_another_window);
    ImGui::SliderFloat("浮点条", &f, 0.0f, 1.0f);

    ImGui::InputText("输入内容", sz, 256, 0, 0, 0);

    if (ImGui::Button("点我触发"))
        counter++;
    ImGui::SameLine();
    ImGui::Text("触发次数 = %d", counter);

    ImGui::Text("当前FPS: %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
    ImGui::End();

    if (show_another_window)
    {
        ImGui::Begin("我是子窗体", &show_another_window);
        ImGui::Text(" 您好,LyShark !");
        if (ImGui::Button("关闭窗体"))
            show_another_window = false;
        ImGui::End();
    }
    ImGui::EndFrame();
}

// 自身窗口循环事件
void WindowMessageLoop()
{
    bool done = false;
    while (!done)
    {
        // 每次都将窗体置顶并跟随游戏窗体移动
        GetWindowRect(GameHwnd, &WindowRectangle);
        WindowWide = (WindowRectangle.right) - (WindowRectangle.left);
        WindowHeight = (WindowRectangle.bottom) - (WindowRectangle.top);
        DWORD dwStyle = GetWindowLong(GameHwnd, GWL_STYLE);
        if (dwStyle & WS_BORDER)
        {
            WindowRectangle.top += 23;
            WindowHeight -= 23;
        }

        // 跟随窗口移动
        MoveWindow(hwnd, WindowRectangle.left + 8, WindowRectangle.top + 8, WindowWide - 11, WindowHeight - 11, true);

        // 开始消息循环
        MSG msg;
        while (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            if (msg.message == WM_QUIT)
            {
                done = true;
            }
        }

        if (done)
        {
            break;
        }

        // 开始绘制
        DrawImGUI();

        g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, FALSE);
        g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
        g_pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
        g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 1.0f, 0);

        if (g_pd3dDevice->BeginScene() >= 0)
        {
            ImGui::Render();
            ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
            g_pd3dDevice->EndScene();
        }

        HRESULT result = g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
        if (result == D3DERR_DEVICELOST && g_pd3dDevice->TestCooperativeLevel() == D3DERR_DEVICENOTRESET)
        {
            ResetDevice();
        }
    }
}

int main(int argc, char *argv[])
{
    // 注册窗体类
    WNDCLASSEX wc;

    // 附加到指定窗体上
    wc.cbClsExtra = NULL;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.cbWndExtra = NULL;
    wc.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0, 0, 0));
    wc.hCursor = LoadCursor(0, IDC_ARROW);
    wc.hIcon = LoadIcon(0, IDI_APPLICATION);
    wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpfnWndProc = (WNDPROC)WndProc;
    wc.lpszClassName = L" ";
    wc.lpszMenuName = L" ";
    wc.style = CS_VREDRAW | CS_HREDRAW;

    RegisterClassEx(&wc);

    // 得到窗口句柄
    GameHwnd = FindWindowA(NULL, "Counter-Strike Source");
    GetWindowRect(GameHwnd, &WindowRectangle);
    WindowWide = WindowRectangle.right - WindowRectangle.left;
    WindowHeight = WindowRectangle.bottom - WindowRectangle.top;

    // 创建窗体
    hwnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TOOLWINDOW, L" ", L" ", WS_POPUP, 1, 1, WindowWide, WindowHeight, 0, 0, wc.hInstance, 0);

    // 显示窗口
    SetLayeredWindowAttributes(hwnd, 0, RGB(0, 0, 0), LWA_ALPHA);
    SetLayeredWindowAttributes(hwnd, 0, RGB(0, 0, 0), LWA_COLORKEY);
    ShowWindow(hwnd, SW_SHOW);

    // 初始化D3D
    if (!CreateDeviceD3D(hwnd))
    {
        CleanupDeviceD3D();
        UnregisterClass(wc.lpszClassName, wc.hInstance);
        return 0;
    }

    // 更新窗体
    UpdateWindow(hwnd);

    // 初始化ImGUI
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO(); (void)io;
    io.Fonts->AddFontFromFileTTF("c:/windows/fonts/simhei.ttf", 13.0f, NULL, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());

    ImGui::StyleColorsDark();
    ImGui_ImplWin32_Init(hwnd);
    ImGui_ImplDX9_Init(g_pd3dDevice);

    // 开始执行绘制循环事件
    WindowMessageLoop();

    ImGui_ImplDX9_Shutdown();
    ImGui_ImplWin32_Shutdown();
    ImGui::DestroyContext();

    CleanupDeviceD3D();
    DestroyWindow(hwnd);
    UnregisterClass(wc.lpszClassName, wc.hInstance);
    return 0;
}

绘制效果如下:

image.png

另外,Imgui也支持绘制到整个屏幕上,也可以当作全局GUI界面来使用。

#include "imgui.h"
#include "imgui_impl_dx9.h"
#include "imgui_impl_win32.h"

#include <d3d9.h>
#include <tchar.h>
#include <iostream>

#pragma execution_character_set("utf-8")

// 全局变量
static HWND hwnd;
static HWND GameHwnd;
static RECT WindowRectangle;
static int WindowWide, WindowHeight;

static LPDIRECT3D9 g_pD3D = NULL;
static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
static D3DPRESENT_PARAMETERS g_d3dpp = {};

// 单选框设置状态
bool show_another_window = false;

// imgui 回调函数
// By: LyShark
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

bool CreateDeviceD3D(HWND hWnd)
{
    if ((g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL)
    {
        return false;
    }

    ZeroMemory(&g_d3dpp, sizeof(g_d3dpp));
    g_d3dpp.Windowed = TRUE;
    g_d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    g_d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
    g_d3dpp.EnableAutoDepthStencil = TRUE;
    g_d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
    g_d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;

    if (g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &g_d3dpp, &g_pd3dDevice) < 0)
    {
        return false;
    }
    return true;
}

void CleanupDeviceD3D()
{
    if (g_pd3dDevice)
    {
        g_pd3dDevice->Release();
        g_pd3dDevice = NULL;
    }

    if (g_pD3D)
    {
        g_pD3D->Release();
        g_pD3D = NULL;
    }
}
// https://www.cnblogs.com/lyshark
void ResetDevice()
{
    ImGui_ImplDX9_InvalidateDeviceObjects();
    HRESULT hr = g_pd3dDevice->Reset(&g_d3dpp);
    if (hr == D3DERR_INVALIDCALL)
    {
        IM_ASSERT(0);
    }
    ImGui_ImplDX9_CreateDeviceObjects();
}

LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
    {
        return true;
    }

    switch (msg)
    {
    case WM_SIZE:
        if (g_pd3dDevice != NULL && wParam != SIZE_MINIMIZED)
        {
            g_d3dpp.BackBufferWidth = LOWORD(lParam);
            g_d3dpp.BackBufferHeight = HIWORD(lParam);
            ResetDevice();
        }
        return 0;

    case WM_SYSCOMMAND:
        if ((wParam & 0xfff0) == SC_KEYMENU)
        {
            return 0;
        }
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProc(hWnd, msg, wParam, lParam);
}

// 绘制主方法
// lyshark.com
void DrawImGUI()
{
    // 启动IMGUI自绘
    ImGui_ImplDX9_NewFrame();
    ImGui_ImplWin32_NewFrame();
    ImGui::NewFrame();

    static float f = 0.0f;
    static int counter = 0;
    static char sz[256] = { 0 };

    ImGui::Begin("LyShark 辅助GUI主菜单");
    ImGui::Text("这是一段测试字符串");
    ImGui::Checkbox("弹出子窗口", &show_another_window);
    ImGui::SliderFloat("浮点条", &f, 0.0f, 1.0f);

    ImGui::InputText("输入内容", sz, 256, 0, 0, 0);

    if (ImGui::Button("点我触发"))
        counter++;
    ImGui::SameLine();
    ImGui::Text("触发次数 = %d", counter);

    ImGui::Text("当前FPS: %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
    ImGui::End();

    if (show_another_window)
    {
        ImGui::Begin("我是子窗体", &show_another_window);
        ImGui::Text(" 您好,LyShark !");
        if (ImGui::Button("关闭窗体"))
            show_another_window = false;
        ImGui::End();
    }
    ImGui::EndFrame();
}

// 自身窗口循环事件
void WindowMessageLoop()
{
    bool done = false;
    while (!done)
    {
        // 每次都将窗体置顶并跟随游戏窗体移动
        GetWindowRect(GameHwnd, &WindowRectangle);
        WindowWide = (WindowRectangle.right) - (WindowRectangle.left);
        WindowHeight = (WindowRectangle.bottom) - (WindowRectangle.top);
        DWORD dwStyle = GetWindowLong(GameHwnd, GWL_STYLE);
        if (dwStyle & WS_BORDER)
        {
            WindowRectangle.top += 23;
            WindowHeight -= 23;
        }

        // 跟随窗口移动
        MoveWindow(hwnd, WindowRectangle.left + 8, WindowRectangle.top + 8, WindowWide - 11, WindowHeight - 11, true);

        // 开始消息循环
        MSG msg;
        while (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            if (msg.message == WM_QUIT)
            {
                done = true;
            }
        }

        if (done)
        {
            break;
        }

        // 开始绘制
        DrawImGUI();

        g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, FALSE);
        g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
        g_pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
        g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 1.0f, 0);

        if (g_pd3dDevice->BeginScene() >= 0)
        {
            ImGui::Render();
            ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
            g_pd3dDevice->EndScene();
        }

        HRESULT result = g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
        if (result == D3DERR_DEVICELOST && g_pd3dDevice->TestCooperativeLevel() == D3DERR_DEVICENOTRESET)
        {
            ResetDevice();
        }
    }
}

int main(int argc, char *argv[])
{
    // 注册窗体类
    WNDCLASSEX wc;

    // 附加到整个屏幕上
    wc.cbClsExtra = NULL;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.cbWndExtra = NULL;
    wc.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0, 0, 0));
    wc.hCursor = LoadCursor(0, IDC_ARROW);
    wc.hIcon = LoadIcon(0, IDI_APPLICATION);
    wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpfnWndProc = (WNDPROC)WndProc;
    wc.lpszClassName = L" ";
    wc.lpszMenuName = L" ";
    wc.style = CS_VREDRAW | CS_HREDRAW;
    ::RegisterClassEx(&wc);

    // 屏幕宽度和高度
    WindowWide = GetSystemMetrics(SM_CXSCREEN);
    WindowHeight = GetSystemMetrics(SM_CYSCREEN);

    // 创建窗体
    HWND hwnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TOOLWINDOW, L" ", L" ", WS_POPUP, 1, 1, WindowWide, WindowHeight, 0, 0, wc.hInstance, 0);

    // 显示窗口
    SetLayeredWindowAttributes(hwnd, 0, 1.0f, LWA_ALPHA);
    SetLayeredWindowAttributes(hwnd, 0, RGB(0, 0, 0), LWA_COLORKEY);
    ShowWindow(hwnd, SW_SHOW);

    // 初始化D3D
    if (!CreateDeviceD3D(hwnd))
    {
        CleanupDeviceD3D();
        UnregisterClass(wc.lpszClassName, wc.hInstance);
        return 0;
    }

    // 更新窗体
    UpdateWindow(hwnd);

    // 初始化ImGUI
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO(); (void)io;
    io.Fonts->AddFontFromFileTTF("c:/windows/fonts/simhei.ttf", 13.0f, NULL, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());

    ImGui::StyleColorsDark();
    ImGui_ImplWin32_Init(hwnd);
    ImGui_ImplDX9_Init(g_pd3dDevice);

    // 开始执行绘制循环事件
    WindowMessageLoop();

    ImGui_ImplDX9_Shutdown();
    ImGui_ImplWin32_Shutdown();
    ImGui::DestroyContext();

    CleanupDeviceD3D();
    DestroyWindow(hwnd);
    UnregisterClass(wc.lpszClassName, wc.hInstance);
    return 0;
}

绘制效果如下:

image.png

目录
相关文章
|
4月前
|
定位技术 iOS开发
在地图页面,自动布局控件开始是隐藏或在屏幕外需要正常显示时再为正常的显示状态的,需要在显示之前加入
在地图页面,自动布局控件开始是隐藏或在屏幕外需要正常显示时再为正常的显示状态的,需要在显示之前加入
42 0
|
C++ 计算机视觉 Python
Qt+C++自定义标题栏最大最小化关闭堆叠切换美化
这篇博客针对<<Qt+C++自定义标题栏最大最小化关闭堆叠切换美化>>编写代码,代码整洁,规则,易读。 学习与应用推荐首选。
106 0
|
前端开发 Python
tkinter模块高级操作(一)—— 透明按钮、透明文本框、自定义按钮及自定义文本框
tkinter模块高级操作(一)—— 透明按钮、透明文本框、自定义按钮及自定义文本框
691 0
C#利用委托实现命令按钮跨窗体控制
C#利用委托实现命令按钮跨窗体控制
117 0
C#编程学习19:mdi窗体中子窗体不能重复打开的三种实现方式
C#编程学习19:mdi窗体中子窗体不能重复打开的三种实现方式
C#编程学习19:mdi窗体中子窗体不能重复打开的三种实现方式
|
安全 Java 数据库
页面控制菜单的显示或隐藏|学习笔记
快速学习页面控制菜单的显示或隐藏
143 0
页面控制菜单的显示或隐藏|学习笔记
|
搜索推荐
PyQt5 技巧篇-参数控制窗体右上角只显示关闭按钮实例演示
PyQt5 技巧篇-参数控制窗体右上角只显示关闭按钮实例演示
557 0
PyQt5 技巧篇-参数控制窗体右上角只显示关闭按钮实例演示
PyQt5 技术篇-调用颜色对话框(QColorDialog)获取颜色,调色板的调用。
PyQt5 技术篇-调用颜色对话框(QColorDialog)获取颜色,调色板的调用。
395 0
PyQt5 技术篇-调用颜色对话框(QColorDialog)获取颜色,调色板的调用。
chart(WinForm)放大多次后,直接回到放大前的状态
chart(WinForm)放大多次后,直接回到放大前的状态
185 0
|
程序员 开发工具 C语言
Qt编写自定义控件47-面板区域控件
一、前言 在很多web网页上,经常可以看到一个设备对应一个面板,或者某种同等类型的信息全部放在一个面板上,该面板还可以拖来拖去的,这个控件首次用在智能访客管理平台中,比如身份证信息一个面板,访客信息一个面板,被访人信息一个面板,这样相当于分类展示了,还提供了对应的标题栏有文字显示,这个控件的使用场景也是非常多,还有个子标题可以设置,拓展了报警闪烁的接口。
1218 0