吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1158|回复: 1
收起左侧

[Python 原创] 【NiceGUI】修改窗口标题

  [复制链接]
PythonPan 发表于 2026-4-6 12:29

对于网页模式,修改窗口标题很简单,最简单的方式莫过于直接运行JavaScript代码来修改:

from nicegui import ui

def index():
    title = ui.input(
        '窗口标题',
        value='NiceGUI程序'
    )
    ui.button(
        '修改', 
        on_click=lambda:ui.run_javascript(
            f'document.title = "{title.value}"'
        )
    )

ui.run(
    root=index,
    native=True
)

对于窗口模式,则可以使用app.native.main_window.set_title方法来修改窗口标题:

from nicegui import ui,app

def index():
    title = ui.input(
        '窗口标题',
        value='NiceGUI程序'
    )
    ui.button(
        '修改', 
        on_click=lambda:app.native.main_window.set_title(
            title.value
        )
    )

ui.run(
    root=index,
    native=True
)

不过,如果不想针对不同的显示模式使用不同的方法,则可以使用ui.page_title方法同时修改窗口模式、网页模式的窗口标题:

from nicegui import ui

def index():
    title = ui.input(
        '窗口标题',
        value='NiceGUI程序'
    )
    ui.button(
        '修改', 
        on_click=lambda:ui.page_title(
            title.value
        )
    )

ui.run(
    root=index,
    native=True
)

2026_27_1.png

以下为扩展内容:

就在本文在其他平台发出后不久,一位不愿透露姓名的热心粉丝向笔者提问,如何修改其他窗口的标题。笔者简单搜索了一下Python提供相关功能的库,发现pywin32库做到了Windows系统下简单易用,便想直接回答。但是,只是告诉粉丝库名,不给现成代码,也不做解释说明,会显得笔者有些“敷衍”。于是,便有了本章。

代码涉及的操作需要安装pywin32库,因此需要通过pip命令或uv命令安装:

pip install pywin32
uv add pywin32

pywin32库提供了win32gui模块,可以完成所需操作。思路如下:

  • 使用win32gui.EnumWindows方法获取所有窗口的句柄和标题。
  • 用窗口对应的句柄,通过win32gui.SetWindowText方法修改该窗口的标题。

但在实现时,会遇到以下问题:

  • win32gui.EnumWindows方法获取的窗口包括不可见窗口和标题为空的可见窗口,实际上这些窗口基本上不是在Windows系统按win + tab键或者alt + tab键看到的窗口。
  • 部分窗口标题通过win32gui.SetWindowText方法修改之后,再次获取依然没有生效。

笔者简单研究之后,找到了解决方法和原因:

  • 可以使用win32gui.IsWindowVisible方法判断窗口是否可见。使用win32gui.GetWindowText方法获取可见窗口的标题,判断是否为空。
  • 部分窗口有其他函数自动更新窗口标题,通过程序修改之后,只是在其他函数未执行前看上去修改了,一旦重新获取或者窗口刷新,窗口标题会被自动更新,因此参会看上去“没有生效”。

基于上面的思路,示例如下:

from nicegui import ui
import win32gui

current_hwnd = None
def refresh_windows_title():
    windows_dict = []
    def get_windows_dict(hwnd,extra):
        if win32gui.IsWindowVisible(hwnd) and (win32gui.GetWindowText(hwnd) != ''):
            windows_dict.append(
                {
                    'hwnd':hwnd,
                    'title':win32gui.GetWindowText(
                        hwnd
                    )
                }
            )
    win32gui.EnumWindows(get_windows_dict,None)
    return windows_dict

def index():
    table = ui.table(
        columns=[
            {
                'label': '句柄', 
                'field': 'hwnd'
            },
            {
                'label': '标题', 
                'field': 'title',
                'align': 'left'
            },
            {
                'label': '操作',
                'name': 'action', 
                'align': 'center'
            },
        ],
        rows=[],
        pagination={
            'rowsPerPage':7,
            'sortedBy':'hwnd',
        }
    )
    def get_hwnd_title(args):
        global current_hwnd
        current_hwnd = args[0]
        current_title.value = args[1]

    with table.add_slot('body-cell-action'):
        with table.cell('action'):
            ui.button('修改').props('flat').on(
            'click',
            js_handler='() => emit(props.row.hwnd,props.row.title)',
            handler=lambda e: get_hwnd_title(e.args),
        )
    with ui.row():
        current_title = ui.input('修改窗口的标题')
        ui.button(
            '修改',
            on_click=lambda:win32gui.SetWindowText(
                current_hwnd,
                current_title.value
            )
        )
    ui.button(
        '刷新可见窗口句柄(不含空白标题)',
        on_click=lambda:table.props.update(
            {
                'rows':refresh_windows_title()
            }
        )
    )

ui.run(
    root=index,
    native=True
)

2026_27_2.png

免费评分

参与人数 1吾爱币 +7 热心值 +1 收起 理由
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

289 发表于 2026-4-6 15:44
本帖最后由 289 于 2026-4-6 15:47 编辑

自己用DELPHI 13 纯 API 写了一款软件  --  超强标题修改工具
直接拖放鼠标到外部窗口上 即可修改!
UPX压缩了下 288KB
完成时间:2026年4月6日15:47:43

[Delphi] 纯文本查看 复制代码
program PerfectCrosshairChanger;

{*******************************************************}
{                                                       }
{       纯 WinAPI 增强型跨进程标题修改器 (零警告版)     }
{                                                            }
{                                                       }
{*******************************************************}

uses
  Winapi.Windows,
  Winapi.Messages;

// 彻底关闭平台相关警告和过时警告(针对此单元)
{$WARN SYMBOL_PLATFORM OFF}
{$WARN SYMBOL_DEPRECATED OFF}

const
  ID_EDIT_BOX = 101;
  MY_IDC_CROSS = 32515;

var
  msg: Winapi.Windows.TMsg;
  hggg: Winapi.Windows.HWND;      // 主窗口句柄 (符合 hggg 要求)
  hOverlay: Winapi.Windows.HWND;  // 透明覆盖层
  hEdit: Winapi.Windows.HWND;     // 用户输入框
  wc: Winapi.Windows.TWndClass;
  isDragging: System.Boolean = False;
  curPt: Winapi.Windows.TPoint;

// --- 作者主页:https://www.zhihu.com/people/union-29 ---

function OverlayProc(hWnd: Winapi.Windows.HWND; Msg: System.Cardinal; wParam: Winapi.Windows.WPARAM; lParam: Winapi.Windows.LPARAM): Winapi.Windows.LRESULT; stdcall;
var
  ps: Winapi.Windows.TPaintStruct;
  hdc: Winapi.Windows.HDC;
  rect: Winapi.Windows.TRect;
  memDC: Winapi.Windows.HDC;
  memBitmap, oldBitmap: Winapi.Windows.HBITMAP;
begin
  case Msg of
    Winapi.Messages.WM_ERASEBKGND: Result := 1;
    Winapi.Messages.WM_PAINT:
    begin
      hdc := Winapi.Windows.BeginPaint(hWnd, ps);
      Winapi.Windows.GetClientRect(hWnd, rect);

      // 双倍缓冲:解决闪烁与残影的核心
      memDC := Winapi.Windows.CreateCompatibleDC(hdc);
      memBitmap := Winapi.Windows.CreateCompatibleBitmap(hdc, rect.Right, rect.Bottom);
      oldBitmap := Winapi.Windows.SelectObject(memDC, memBitmap);

      // 填充透明底色 (黑色在 Layered 模式下变为透明)
      Winapi.Windows.FillRect(memDC, rect, Winapi.Windows.GetStockObject(BLACK_BRUSH));

      if isDragging then
      begin
        Winapi.Windows.SelectObject(memDC, Winapi.Windows.GetStockObject(DC_PEN));
        Winapi.Windows.SetDCPenColor(memDC, $0000FF); // 红色
        // 绘制十字准星
        Winapi.Windows.MoveToEx(memDC, curPt.X - 30, curPt.Y, nil);
        Winapi.Windows.LineTo(memDC, curPt.X + 30, curPt.Y);
        Winapi.Windows.MoveToEx(memDC, curPt.X, curPt.Y - 30, nil);
        Winapi.Windows.LineTo(memDC, curPt.X, curPt.Y + 30);
      end;

      Winapi.Windows.BitBlt(hdc, 0, 0, rect.Right, rect.Bottom, memDC, 0, 0, SRCCOPY);
      Winapi.Windows.SelectObject(memDC, oldBitmap);
      Winapi.Windows.DeleteObject(memBitmap);
      Winapi.Windows.DeleteDC(memDC);
      Winapi.Windows.EndPaint(hWnd, ps);
      Result := 0;
    end;
  else
    Result := Winapi.Windows.DefWindowProc(hWnd, Msg, wParam, lParam);
  end;
end;

function MainProc(hWnd: Winapi.Windows.HWND; Msg: System.Cardinal; wParam: Winapi.Windows.WPARAM; lParam: Winapi.Windows.LPARAM): Winapi.Windows.LRESULT; stdcall;
var
  rect: Winapi.Windows.TRect;
  targetHwnd: Winapi.Windows.HWND;
  userText: array[0..512] of WideChar;
  ps: Winapi.Windows.TPaintStruct;
  resVal: Winapi.Windows.DWORD_PTR; // 匹配 SendMessageTimeoutW
begin
  case Msg of
    Winapi.Messages.WM_CREATE:
    begin
      hEdit := Winapi.Windows.CreateWindowEx(WS_EX_CLIENTEDGE, 'EDIT', 'zhihu.com/people/union-29',
        WS_CHILD or WS_VISIBLE or ES_AUTOHSCROLL, 20, 50, 340, 25, hWnd, ID_EDIT_BOX, System.MainInstance, nil);

      // 居中逻辑
      Winapi.Windows.GetWindowRect(hWnd, rect);
      Winapi.Windows.SetWindowPos(hWnd, 0,
        (Winapi.Windows.GetSystemMetrics(SM_CXSCREEN) - (rect.Right - rect.Left)) div 2,
        (Winapi.Windows.GetSystemMetrics(SM_CYSCREEN) - (rect.Bottom - rect.Top)) div 2,
        0, 0, SWP_NOSIZE or SWP_NOZORDER);
      Result := 0;
    end;

    Winapi.Messages.WM_LBUTTONDOWN:
    begin
      isDragging := True;
      Winapi.Windows.SetCapture(hWnd);
      Winapi.Windows.ShowWindow(hOverlay, SW_SHOW);
      Result := 0;
    end;

    Winapi.Messages.WM_MOUSEMOVE:
    begin
      if isDragging then
      begin
        Winapi.Windows.GetCursorPos(curPt);
        // 实时刷新透明层
        Winapi.Windows.InvalidateRect(hOverlay, nil, False);
      end;
      Result := 0;
    end;

    Winapi.Messages.WM_LBUTTONUP:
    begin
      if isDragging then
      begin
        isDragging := False;
        Winapi.Windows.ReleaseCapture;
        Winapi.Windows.ShowWindow(hOverlay, SW_HIDE);

        Winapi.Windows.GetCursorPos(curPt);
        targetHwnd := Winapi.Windows.WindowFromPoint(curPt);

        if (targetHwnd <> 0) then
        begin
          // 获取顶层父窗口,确保修改的是主标题而非局部控件
          targetHwnd := Winapi.Windows.GetAncestor(targetHwnd, GA_ROOT);

          if (targetHwnd <> hggg) then
          begin
            Winapi.Windows.GetWindowText(hEdit, @userText[0], 512);

            // --- 零警告调用:显式使用 SendMessageTimeoutW ---
            // 作者主页:https://www.zhihu.com/people/union-29
            resVal := 0;
            Winapi.Windows.SendMessageTimeoutW(
              targetHwnd,
              Winapi.Messages.WM_SETTEXT,
              0,
              Winapi.Windows.LPARAM(@userText[0]),
              SMTO_ABORTIFHUNG or SMTO_NORMAL,
              1000,
              @resVal  // 传递指针地址,适配标准 WinAPI 定义
            );

            // 刷新目标窗口框架
            Winapi.Windows.SetWindowPos(targetHwnd, 0, 0, 0, 0, 0,
              SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER or SWP_FRAMECHANGED);
          end;
        end;
      end;
      Result := 0;
    end;

    Winapi.Messages.WM_PAINT:
    begin
      Winapi.Windows.BeginPaint(hWnd, ps);
      Winapi.Windows.GetClientRect(hWnd, rect);
      Winapi.Windows.FillRect(ps.hdc, rect, Winapi.Windows.GetSysColorBrush(COLOR_BTNFACE));
      Winapi.Windows.TextOut(ps.hdc, 20, 20, '请在下方输入标题并按住窗口拖动', 16);
      Winapi.Windows.EndPaint(hWnd, ps);
      Result := 0;
    end;

    Winapi.Messages.WM_DESTROY:
    begin
      Winapi.Windows.PostQuitMessage(0);
      Result := 0;
    end;
  else
    Result := Winapi.Windows.DefWindowProc(hWnd, Msg, wParam, lParam);
  end;
end;

begin
  // DPI 自适应设置
  Winapi.Windows.SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);

  // 注册主类
  System.FillChar(wc, System.SizeOf(wc), 0);
  wc.lpfnWndProc   := @MainProc;
  wc.hInstance     := System.MainInstance;
  wc.lpszClassName := 'U29_MainClass';
  wc.hCursor       := Winapi.Windows.LoadCursor(0, IDC_ARROW);
  wc.hbrBackground := Winapi.Windows.COLOR_BTNFACE + 1;
  Winapi.Windows.RegisterClass(wc);

  // 注册 Overlay 类
  System.FillChar(wc, System.SizeOf(wc), 0);
  wc.lpfnWndProc   := @OverlayProc;
  wc.hInstance     := System.MainInstance;
  wc.lpszClassName := 'U29_OverlayClass';
  wc.hbrBackground := Winapi.Windows.GetStockObject(BLACK_BRUSH);
  Winapi.Windows.RegisterClass(wc);

  // 创建窗口
  hggg := Winapi.Windows.CreateWindowEx(WS_EX_TOPMOST, 'U29_MainClass', '超强标题修改工具',
    WS_OVERLAPPED or WS_CAPTION or WS_SYSMENU or WS_VISIBLE,
    CW_USEDEFAULT, CW_USEDEFAULT, 400, 160, 0, 0, System.MainInstance, nil);

  // 关键:创建透明全屏分层窗口
  hOverlay := Winapi.Windows.CreateWindowEx(
    WS_EX_TOPMOST or WS_EX_LAYERED or WS_EX_TRANSPARENT or WS_EX_NOACTIVATE,
    'U29_OverlayClass', nil, WS_POPUP,
    0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
    hggg, 0, System.MainInstance, nil);

  Winapi.Windows.SetLayeredWindowAttributes(hOverlay, 0, 0, LWA_COLORKEY);

  while Winapi.Windows.GetMessage(msg, 0, 0, 0) do
  begin
    Winapi.Windows.TranslateMessage(msg);
    Winapi.Windows.DispatchMessage(msg);
  end;
end.


您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-10-9 13:16

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表