- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
如果窗口可见性发生变化,我会尝试获取一个事件。我发现有一个事件叫做“可见性”。操作系统是 Windows 64 位。所以我通过以下方式实现:
root.bind('<Visibility>', visibilityChanged)
但是无论上面是否有窗口,我总是得到“VisibilityUnobscured”状态。这个事件的正常行为是什么?我怎样才能实现这样的功能?
示例程序:
import tkinter as tk
class GUI:
def __init__(self, master):
self.master = master
master.title("Test GUI")
self.master.bind('<Visibility>', self.visibilityChanged)
self.label = tk.Label(master, text="GUI")
self.label.pack()
self.close_button = tk.Button(master, text="Close", command=master.quit)
self.close_button.pack()
def visibilityChanged(self, event):
if (str(event.type) == "Visibility"):
print(event.state)
root = tk.Tk()
my_gui = GUI(root)
root.mainloop()
最佳答案
What is the normal behaviour of this event?
docs 中对其进行了详细描述:X
服务器在任何窗口的可见性更改状态时生成 VisibilityNotify
事件。
How can I implement a feature like that?
这取决于您要实现自己的愿望多远,因为这不是一项微不足道的任务。因此,不要将该答案视为完整的解决方案,而应将其视为问题概述和一组建议。
Windows 操作系统
使用消息传递模型 - 系统通过消息与您的应用程序窗口通信,其中每条消息都是指定特定事件的数字代码。应用程序窗口有一个关联的窗口过程——一个处理(响应或忽略)所有发送的消息的函数。
最通用的解决方案是设置一个钩子(Hook)来捕获某些事件/消息,这可以通过 SetWindowsHookEx 来实现。或 pyHook .
主要问题是获取事件,因为Windows WM
没有VisibilityNotify
这样的消息。正如我在评论部分所说 - 我们可以依赖的一个选项是 z-order
(有可能检查窗口的可见性,只要这个窗口改变它在 z-order
中的位置)。
因此我们的目标消息是 WM_WINDOWPOSCHANGING或 WM_WINDOWPOSCHANGED .
一个简单的实现:
import ctypes
import ctypes.wintypes as wintypes
import tkinter as tk
class CWPRETSTRUCT(ctypes.Structure):
''' a class to represent CWPRETSTRUCT structure
https://msdn.microsoft.com/en-us/library/windows/desktop/ms644963(v=vs.85).aspx '''
_fields_ = [('lResult', wintypes.LPARAM),
('lParam', wintypes.LPARAM),
('wParam', wintypes.WPARAM),
('message', wintypes.UINT),
('hwnd', wintypes.HWND)]
class WINDOWPOS(ctypes.Structure):
''' a class to represent WINDOWPOS structure
https://msdn.microsoft.com/en-gb/library/windows/desktop/ms632612(v=vs.85).aspx '''
_fields_ = [('hwnd', wintypes.HWND),
('hwndInsertAfter', wintypes.HWND),
('x', wintypes.INT),
('y', wintypes.INT),
('cx', wintypes.INT),
('cy', wintypes.INT),
('flags', wintypes.UINT)]
class App(tk.Tk):
''' generic tk app with win api interaction '''
wm_windowposschanged = 71
wh_callwndprocret = 12
swp_noownerzorder = 512
set_hook = ctypes.windll.user32.SetWindowsHookExW
call_next_hook = ctypes.windll.user32.CallNextHookEx
un_hook = ctypes.windll.user32.UnhookWindowsHookEx
get_thread = ctypes.windll.kernel32.GetCurrentThreadId
get_error = ctypes.windll.kernel32.GetLastError
get_parent = ctypes.windll.user32.GetParent
wnd_ret_proc = ctypes.WINFUNCTYPE(ctypes.c_long, wintypes.INT, wintypes.WPARAM, wintypes.LPARAM)
def __init__(self):
''' generic __init__ '''
super().__init__()
self.minsize(350, 200)
self.hook = self.setup_hook()
self.protocol('WM_DELETE_WINDOW', self.on_closing)
def setup_hook(self):
''' setting up the hook '''
thread = self.get_thread()
hook = self.set_hook(self.wh_callwndprocret, self.call_wnd_ret_proc, wintypes.HINSTANCE(0), thread)
if not hook:
raise ctypes.WinError(self.get_error())
return hook
def on_closing(self):
''' releasing the hook '''
if self.hook:
self.un_hook(self.hook)
self.destroy()
@staticmethod
@wnd_ret_proc
def call_wnd_ret_proc(nCode, wParam, lParam):
''' an implementation of the CallWndRetProc callback
https://msdn.microsoft.com/en-us/library/windows/desktop/ms644976(v=vs.85).aspx'''
# get a message
msg = ctypes.cast(lParam, ctypes.POINTER(CWPRETSTRUCT)).contents
if msg.message == App.wm_windowposschanged and msg.hwnd == App.get_parent(app.winfo_id()):
# if message, which belongs to owner hwnd, is signaling that windows position is changed - check z-order
wnd_pos = ctypes.cast(msg.lParam, ctypes.POINTER(WINDOWPOS)).contents
print('z-order changed: %r' % ((wnd_pos.flags & App.swp_noownerzorder) != App.swp_noownerzorder))
return App.call_next_hook(None, nCode, wParam, lParam)
app = App()
app.mainloop()
如您所见,此实现具有与“损坏的”Visibility
事件类似的行为。
这个问题源于这样一个事实,即您只能捕获线程指定的消息,因此应用程序不知道堆栈中的变化。这只是我的假设,但我认为 Visibility
损坏的原因是相同的。
当然,我们可以为所有消息设置一个全局钩子(Hook),而不管线程,但这种方法需要 DLL 注入(inject),这肯定是另一回事。
确定窗口的遮挡不是问题,因为我们可以依赖 Graphical Device Interface .
逻辑很简单:
z-order
中较高的每个可见窗口)表示为一个矩形。如果最后的几何减法是:
return 'VisibilityFullyObscured'
return 'VisibilityPartiallyObscured'
return 'VisibilityUnobscured'
return 'VisibilityPartiallyObscured'
一个简单的实现(带有自调度循环):
import ctypes
import ctypes.wintypes as wintypes
import tkinter as tk
class App(tk.Tk):
''' generic tk app with win api interaction '''
enum_windows = ctypes.windll.user32.EnumWindows
is_window_visible = ctypes.windll.user32.IsWindowVisible
get_window_rect = ctypes.windll.user32.GetWindowRect
create_rect_rgn = ctypes.windll.gdi32.CreateRectRgn
combine_rgn = ctypes.windll.gdi32.CombineRgn
del_rgn = ctypes.windll.gdi32.DeleteObject
get_parent = ctypes.windll.user32.GetParent
enum_windows_proc = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM)
def __init__(self):
''' generic __init__ '''
super().__init__()
self.minsize(350, 200)
self.status_label = tk.Label(self)
self.status_label.pack()
self.after(100, self.continuous_check)
self.state = ''
def continuous_check(self):
''' continuous (self-scheduled) check '''
state = self.determine_obscuration()
if self.state != state:
# mimic the event - fire only when state changes
print(state)
self.status_label.config(text=state)
self.state = state
self.after(100, self.continuous_check)
def enumerate_higher_windows(self, self_hwnd):
''' enumerate window, which has a higher position in z-order '''
@self.enum_windows_proc
def enum_func(hwnd, lParam):
''' clojure-callback for enumeration '''
rect = wintypes.RECT()
if hwnd == lParam:
# stop enumeration if hwnd is equal to lParam (self_hwnd)
return False
else:
# continue enumeration
if self.is_window_visible(hwnd):
self.get_window_rect(hwnd, ctypes.byref(rect))
rgn = self.create_rect_rgn(rect.left, rect.top, rect.right, rect.bottom)
# append region
rgns.append(rgn)
return True
rgns = []
self.enum_windows(enum_func, self_hwnd)
return rgns
def determine_obscuration(self):
''' determine obscuration via CombineRgn '''
hwnd = self.get_parent(self.winfo_id())
results = {1: 'VisibilityFullyObscured', 2: 'VisibilityUnobscured', 3: 'VisibilityPartiallyObscured'}
rgns = self.enumerate_higher_windows(hwnd)
result = 2
if len(rgns):
rect = wintypes.RECT()
self.get_window_rect(hwnd, ctypes.byref(rect))
# region of tk-window
reference_rgn = self.create_rect_rgn(rect.left, rect.top, rect.right, rect.bottom)
# temp region for storing diff and xor rgn-results
rgn = self.create_rect_rgn(0, 0, 0, 0)
# iterate over stored results
for _ in range(len(rgns)):
_rgn = rgn if _ != 0 else reference_rgn
result = self.combine_rgn(rgn, _rgn, rgns[_], 4)
self.del_rgn(rgns[_])
if result != 2:
# if result isn't a single rectangle
# (NULLREGION - 'VisibilityFullyObscured' or COMPLEXREGION - 'VisibilityPartiallyObscured')
pass
elif self.combine_rgn(rgn, reference_rgn, rgn, 3) == 1:
# if result of XOR is NULLREGION - 'VisibilityUnobscured'
result = 2
else:
# 'VisibilityPartiallyObscured'
result = 3
# clear up regions to prevent memory leaking
self.del_rgn(rgn)
self.del_rgn(reference_rgn)
return results[result]
app = App()
app.mainloop()
不幸的是,这种方法远非可行的解决方案,但从某种角度来看它是可以调整的。
关于python - Tkinter 窗口事件 <Visibility>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48263043/
我正在处理一组标记为 160 个组的 173k 点。我想通过合并最接近的(到 9 或 10 个组)来减少组/集群的数量。我搜索过 sklearn 或类似的库,但没有成功。 我猜它只是通过 knn 聚类
我有一个扁平数字列表,这些数字逻辑上以 3 为一组,其中每个三元组是 (number, __ignored, flag[0 or 1]),例如: [7,56,1, 8,0,0, 2,0,0, 6,1,
我正在使用 pipenv 来管理我的包。我想编写一个 python 脚本来调用另一个使用不同虚拟环境(VE)的 python 脚本。 如何运行使用 VE1 的 python 脚本 1 并调用另一个 p
假设我有一个文件 script.py 位于 path = "foo/bar/script.py"。我正在寻找一种在 Python 中通过函数 execute_script() 从我的主要 Python
这听起来像是谜语或笑话,但实际上我还没有找到这个问题的答案。 问题到底是什么? 我想运行 2 个脚本。在第一个脚本中,我调用另一个脚本,但我希望它们继续并行,而不是在两个单独的线程中。主要是我不希望第
我有一个带有 python 2.5.5 的软件。我想发送一个命令,该命令将在 python 2.7.5 中启动一个脚本,然后继续执行该脚本。 我试过用 #!python2.7.5 和http://re
我在 python 命令行(使用 python 2.7)中,并尝试运行 Python 脚本。我的操作系统是 Windows 7。我已将我的目录设置为包含我所有脚本的文件夹,使用: os.chdir("
剧透:部分解决(见最后)。 以下是使用 Python 嵌入的代码示例: #include int main(int argc, char** argv) { Py_SetPythonHome
假设我有以下列表,对应于及时的股票价格: prices = [1, 3, 7, 10, 9, 8, 5, 3, 6, 8, 12, 9, 6, 10, 13, 8, 4, 11] 我想确定以下总体上最
所以我试图在选择某个单选按钮时更改此框架的背景。 我的框架位于一个类中,并且单选按钮的功能位于该类之外。 (这样我就可以在所有其他框架上调用它们。) 问题是每当我选择单选按钮时都会出现以下错误: co
我正在尝试将字符串与 python 中的正则表达式进行比较,如下所示, #!/usr/bin/env python3 import re str1 = "Expecting property name
考虑以下原型(prototype) Boost.Python 模块,该模块从单独的 C++ 头文件中引入类“D”。 /* file: a/b.cpp */ BOOST_PYTHON_MODULE(c)
如何编写一个程序来“识别函数调用的行号?” python 检查模块提供了定位行号的选项,但是, def di(): return inspect.currentframe().f_back.f_l
我已经使用 macports 安装了 Python 2.7,并且由于我的 $PATH 变量,这就是我输入 $ python 时得到的变量。然而,virtualenv 默认使用 Python 2.6,除
我只想问如何加快 python 上的 re.search 速度。 我有一个很长的字符串行,长度为 176861(即带有一些符号的字母数字字符),我使用此函数测试了该行以进行研究: def getExe
list1= [u'%app%%General%%Council%', u'%people%', u'%people%%Regional%%Council%%Mandate%', u'%ppp%%Ge
这个问题在这里已经有了答案: Is it Pythonic to use list comprehensions for just side effects? (7 个答案) 关闭 4 个月前。 告
我想用 Python 将两个列表组合成一个列表,方法如下: a = [1,1,1,2,2,2,3,3,3,3] b= ["Sun", "is", "bright", "June","and" ,"Ju
我正在运行带有最新 Boost 发行版 (1.55.0) 的 Mac OS X 10.8.4 (Darwin 12.4.0)。我正在按照说明 here构建包含在我的发行版中的教程 Boost-Pyth
学习 Python,我正在尝试制作一个没有任何第 3 方库的网络抓取工具,这样过程对我来说并没有简化,而且我知道我在做什么。我浏览了一些在线资源,但所有这些都让我对某些事情感到困惑。 html 看起来
我是一名优秀的程序员,十分优秀!