Win32的tkinter(ctypes)小技巧

tkinter是python原装的UI库,ctypes是python中用于处理C语言的库,ctypes.windll提供了调用Windows DLL的方法,通过这个我们可以实现以下内容,先注意了,注意了,等会看对照表,我这只是个例子,对照表里有更多方法,谁不定就有你需要的呢?

文章目录

1.保留标题栏,删除最小化、最大化、关闭按钮

代码:

无注释版本:

库版本:

简短版本:

效果:

2.隐藏任务栏图标

代码:

无注释版本:

库版本:

简短版本:

效果:

3.禁止聚焦窗口焦点

代码:

无注释版本:

库版本:

简短版本:

效果:

原理

原理

Windows窗口样式对照表

结语

回来了

点赞!👍( ̄▽ ̄)👍


1.保留标题栏,删除最小化、最大化、关闭按钮

代码:

import tkinter as tk
import ctypes

# Windows API Values
GWL_STYLE = -16
WS_SYSMENU = 0x00080000
SWP_FRAMECHANGED = 0x0020
SWP_NOMOVE = 0x0002
SWP_NOSIZE = 0x0001

# Create Tkinter Window
root = tk.Tk()

# Update The Window (Create Hwnd)
root.update_idletasks()

# Get Hwnd
Hwnd = ctypes.windll.user32.GetParent(root.winfo_id())

# Get Window Style
style = ctypes.windll.user32.GetWindowLongPtrW(Hwnd, GWL_STYLE)

# Remove System Menu (ICO and Close...)
new_style = style & ~WS_SYSMENU

# Add New Style
result = ctypes.windll.user32.SetWindowLongPtrW(Hwnd, GWL_STYLE, new_style)
if result == 0: # If Error
    # Error
    error_code = ctypes.windll.kernel32.GetLastError()
    print(f"ERROR CODE: {error_code}")
else:
    # ReDraw
    ctypes.windll.user32.SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED)

#MainLoop
root.mainloop()

无注释版本:

import tkinter as tk
import ctypes

GWL_STYLE = -16
WS_SYSMENU = 0x00080000
SWP_FRAMECHANGED = 0x0020
SWP_NOMOVE = 0x0002
SWP_NOSIZE = 0x0001

root = tk.Tk()
root.update_idletasks()


Hwnd = ctypes.windll.user32.GetParent(root.winfo_id())
style = ctypes.windll.user32.GetWindowLongPtrW(Hwnd, GWL_STYLE)
new_style = style & ~WS_SYSMENU
result = ctypes.windll.user32.SetWindowLongPtrW(Hwnd, GWL_STYLE, new_style)
if result == 0:
    error_code = ctypes.windll.kernel32.GetLastError()
    print(f"ERROR CODE: {error_code}")
else:
    ctypes.windll.user32.SetWindowPos(Hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED)

root.mainloop()

库版本:

import tkinter as tk
import ctypes


root = tk.Tk()
root.update_idletasks()


def DisableSysMenu(root: tk.Tk):
    # import tkinter as tk
    # improt ctypes
    root.update_idletasks()
    Hwnd = ctypes.windll.user32.GetParent(root.winfo_id())
    style = ctypes.windll.user32.GetWindowLongPtrW(Hwnd, -16)
    new_style = style & ~0x00080000
    result = ctypes.windll.user32.SetWindowLongPtrW(Hwnd, -16, new_style)
    if result == 0:
        return ctypes.windll.kernel32.GetLastError()
    else:
        ctypes.windll.user32.SetWindowPos(Hwnd, 0, 0, 0, 0, 0, 0x0002 | 0x0001 | 0x0020)
        return 1

DisableSysMenu(root)

root.mainloop()

简短版本:

(Bro吃撑了写这么长,生怕被人看懂了)

import tkinter as tk
import ctypes


root = tk.Tk()
root.update_idletasks()


def DisableSysMenu(root: tk.Tk):
    # import tkinter as tk
    # improt ctypes
    root.update_idletasks()
    return ctypes.windll.user32.SetWindowPos(Hwnd, 0, 0, 0, 0, 0, 0x0002 | 0x0001 | 0x0020) if ctypes.windll.user32.SetWindowLongPtrW((Hwnd:=ctypes.windll.user32.GetParent(root.winfo_id())), -16, ctypes.windll.user32.GetWindowLongPtrW((Hwnd:=ctypes.windll.user32.GetParent(root.winfo_id())), -16) & ~0x00080000) else ctypes.windll.kernel32.GetLastError()

DisableSysMenu(root)

root.mainloop()

效果:


2.隐藏任务栏图标

代码:

import tkinter as tk
import ctypes

# Create Tkinter Window
root = tk.Tk()

# Update The Window (Create Hwnd)
root.update_idletasks()

# Get Hwnd
Hwnd = ctypes.windll.user32.GetParent(root.winfo_id())

# Remove Taskbar ICON
GWL_EXSTYLE = -20
WS_EX_APPWINDOW = 0x00040000
WS_EX_TOOLWINDOW = 0x00000080
style = ctypes.windll.user32.GetWindowLongW(Hwnd, GWL_EXSTYLE)
style = style & ~WS_EX_APPWINDOW
style = style | WS_EX_TOOLWINDOW
ctypes.windll.user32.SetWindowLongW(Hwnd, GWL_EXSTYLE, style)

# MainLoop
root.mainloop()

无注释版本:

import tkinter as tk
import ctypes

root = tk.Tk()
root.update_idletasks()

Hwnd = ctypes.windll.user32.GetParent(root.winfo_id())
GWL_EXSTYLE = -20
WS_EX_APPWINDOW = 0x00040000
WS_EX_TOOLWINDOW = 0x00000080
style = ctypes.windll.user32.GetWindowLongW(Hwnd, GWL_EXSTYLE)
style = style & ~WS_EX_APPWINDOW
style = style | WS_EX_TOOLWINDOW
ctypes.windll.user32.SetWindowLongW(Hwnd, GWL_EXSTYLE, style)

root.mainloop()

库版本:

import tkinter as tk
import ctypes

root = tk.Tk()

def RemoveTitleBarICON(root: tk.Tk):
    # import tkinter as tk
    # import ctypes
    root.update_idletasks()
    Hwnd = ctypes.windll.user32.GetParent(root.winfo_id())
    style = ctypes.windll.user32.GetWindowLongW(Hwnd, -20)
    style = style & ~0x00040000
    style = style | 0x00000080
    ctypes.windll.user32.SetWindowLongW(Hwnd, -20, style)

RemoveTitleBarICON(root)

root.mainloop()

简短版本:

import tkinter as tk
import ctypes

root = tk.Tk()

def RemoveTitleBarICON(root: tk.Tk):
    root.update_idletasks()
    ctypes.windll.user32.SetWindowLongW((Hwnd:=ctypes.windll.user32.GetParent(root.winfo_id())), -20, ctypes.windll.user32.GetWindowLongW(Hwnd, -20)& ~0x00040000| 0x00000080)

RemoveTitleBarICON(root)

root.mainloop()

效果:


3.禁止聚焦窗口焦点

注意一下这个代码也可以使窗口没有任务栏图标,而且如果你把这个窗口最小化了,不会隐藏,二十变成一个小正方体躲在任务栏徽标上方处

代码:

import tkinter as tk
import ctypes


# Create Tkinter Window
root = tk.Tk()

# Update The Window
root.update_idletasks()

# Window Values
WS_EX_NOACTIVATE = 0x08000000
GWL_EXSTYLE = -20

# Get Hwnd
Hwnd = ctypes.windll.user32.GetParent(root.winfo_id())
# Get Window Style
style = ctypes.windll.user32.GetWindowLongW(Hwnd, GWL_EXSTYLE)
# Add WS_EX_NOACTIVATE Style
new_style = style | WS_EX_NOACTIVATE
# NewWindowLongW
ctypes.windll.user32.SetWindowLongW(Hwnd, GWL_EXSTYLE, new_style)


# MainLoop
root.mainloop()

无注释版本:

import tkinter as tk
import ctypes


root = tk.Tk()
root.update_idletasks()

WS_EX_NOACTIVATE = 0x08000000
GWL_EXSTYLE = -20
Hwnd = ctypes.windll.user32.GetParent(root.winfo_id())
style = ctypes.windll.user32.GetWindowLongW(Hwnd, GWL_EXSTYLE)
new_style = style | WS_EX_NOACTIVATE
ctypes.windll.user32.SetWindowLongW(Hwnd, GWL_EXSTYLE, new_style)


root.mainloop()

库版本:

import tkinter as tk
import ctypes


root = tk.Tk()

def CanotFocus(root: tk.Tk):
    root.update_idletasks()
    Hwnd = ctypes.windll.user32.GetParent(root.winfo_id())
    style = ctypes.windll.user32.GetWindowLongW(Hwnd, -20)
    new_style = style | 0x08000000
    ctypes.windll.user32.SetWindowLongW(Hwnd, -20, new_style)

CanotFocus(root)

root.mainloop()

简短版本:

import tkinter as tk
import ctypes


root = tk.Tk()

def CanotFocus(root: tk.Tk):
    root.update_idletasks()
    ctypes.windll.user32.SetWindowLongW((Hwnd:=ctypes.windll.user32.GetParent(root.winfo_id())), -20, ctypes.windll.user32.GetWindowLongW(Hwnd, -20) | 0x08000000)

CanotFocus(root)

root.mainloop()

效果:

(你真的能看出来效果吗?)


原理

原理

Windows Window都有自己的样式(style),通过修改样式来达成我们的目的。

第三个例子可以当作一个模板,你可以把0x08000000改成任何Windows窗口扩展类型的样式,你可以在Microsoft 窗口样式 (Winuser.h) - Win32 app中查找到对照表,以下是这个链接中的对照表内容,这个表真的特别特别有用,你可以通过这个表将你的窗口像C和C++那样自定义而不用忍受单一默认的tkinter传统窗口。

Windows窗口样式对照表

常量名称常量值说明
WS_BORDER0x00800000L窗口具有细线边框
WS_CAPTION0x00C00000L窗口具有标题栏(包括 WS_BORDER 样式)。
WS_CHILD0x40000000L窗口是子窗口。 具有此样式的窗口不能有菜单栏。 此样式不能与 WS_POPUP 样式一起使用。
WS_CHILDWINDOW0x40000000L与 WS_CHILD 样式相同。
WS_CLIPCHILDREN0x02000000L在父窗口内进行绘图时,不包括子窗口所占用的区域。 创建父窗口时使用此样式。
WS_CLIPSIBLINGS0x04000000L相对于彼此剪裁子窗口;也就是说,当特定子窗口收到 WM_PAINT 消息时,WS_CLIPSIBLINGS 样式会将所有其他重叠的子窗口剪裁出要更新的子窗口的区域。 如果 未指定 WS_CLIPSIBLINGS 并且子窗口重叠,则在子窗口的工作区内绘图时,有可能在相邻子窗口的工作区内绘图。
WS_DISABLED0x08000000L窗口最初处于禁用状态。 禁用的窗口无法接收用户的输入。 若要在创建窗口后更改此值,请使用 EnableWindow 函数。
WS_DLGFRAME0x00400000L窗口的边框样式通常与对话框相同。 具有此样式的窗口不能有标题栏。
WS_GROUP0x00020000L窗口是一组控件中的第一个控件。 该组包含此第一个控件及其之后定义的所有控件,直到下一个具有 WS_GROUP 样式的控件。 每个组中的第一个控件通常具有 WS_TABSTOP 样式,以便用户可以从组移动到组。 随后,用户可以使用方向键将键盘焦点从组中的一个控件切换为组中的下一个控件。
您可以打开和关闭此样式以更改对话框导航。 若要在创建窗口后更改此样式,请使用 SetWindowLong 函数。
WS_HSCROLL0x00100000L窗口具有水平滚动条。
WS_ICONIC0x20000000L窗口最初是最小化的。 与 WS_MINIMIZE 样式相同。
WS_MAXIMIZE0x01000000L窗口最初是最大化的。
WS_MAXIMIZEBOX0x00010000L窗口具有最大化按钮。 不能与 WS_EX_CONTEXTHELP 样式组合。 还必须指定 WS_SYSMENU 样式。
WS_MINIMIZE0x20000000L窗口最初是最小化的。 与 WS_ICONIC 样式相同。
WS_MINIMIZEBOX0x00020000L窗口具有最小化按钮。 不能与 WS_EX_CONTEXTHELP 样式组合。 还必须指定 WS_SYSMENU 样式。
WS_OVERLAPPED0x00000000L窗口是重叠的窗口。 重叠的窗口带有标题栏和边框。 与 WS_TILED 样式相同。
WS_OVERLAPPEDWINDOW(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)窗口是重叠的窗口。 与 WS_TILEDWINDOW 样式相同。
WS_POPUP0x80000000L窗口是弹出窗口。 此样式不能与 WS_CHILD 样式一起使用。
WS_POPUPWINDOW(WS_POPUP | WS_BORDER | WS_SYSMENU)窗口是弹出窗口。 必须组合 WS_CAPTION 和 WS_POPUPWINDOW 样式以使窗口菜单可见。
WS_SIZEBOX0x00040000L窗口具有大小调整边框。 与 WS_THICKFRAME 样式相同。
WS_SYSMENU0x00080000L该窗口的标题栏上有一个窗口菜单。 还必须指定 WS_CAPTION 样式。
WS_TABSTOP0x00010000L窗口是一个控件,当用户按下 Tab 键时,该控件可以接收键盘焦点。 按下 Tab 键可将键盘焦点更改为具有 WS_TABSTOP 样式的下一个控件。
您可以打开和关闭此样式以更改对话框导航。 若要在创建窗口后更改此样式,请使用 SetWindowLong 函数。 要使用户创建的窗口和无模式对话框能够使用制表位,请更改消息循环以调用 IsDialogMessage 函数。
WS_THICKFRAME0x00040000L窗口具有大小调整边框。 与 WS_SIZEBOX 样式相同。
WS_TILED0x00000000L窗口是重叠的窗口。 重叠的窗口带有标题栏和边框。 与 WS_OVERLAPPED 样式相同。
WS_TILEDWINDOW(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)窗口是重叠的窗口。 与 WS_OVERLAPPEDWINDOW 样式相同。
WS_VISIBLE0x10000000L窗口最初可见。
可以使用 ShowWindow 或 SetWindowPos 函数打开和关闭此样式。
WS_VSCROLL0x00200000L窗口具有垂直滚动条。

结语

回来了

最近一直在写代码,没怎么看优快云了,现在终于腾出来点时间看看优快云了。这篇算是小逝牛刀了一下。文章还有诸多不足,静候各位指点。不知道诸位喜欢不喜欢这种罗列代码的方法,虽然使文章变得不太方便查看,但是我在网上找优快云文章时是很需要的,如果各位喜欢这种方法,随时欢迎催更~

点赞!👍( ̄▽ ̄)👍

点个赞吧点个赞吧点个赞吧!关注一下这个没到20个粉丝(至少25/2/8 2:00:00是)的人呗!到时候你就是老粉了QWQ,有缘再见!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值