我正在制作一个Python程序,使用
LVM_SETITEMPOSITION
从winapi
移动桌面图标,但我对commctrl.LVM_SETITEMPOSITION
有问题,它给了我一个错误'int' object is not callable
。这是我的代码:
import win32gui
import commctrl
from time import sleep
from ctypes import wintypes
hd = wintypes.HWND
hd = win32gui.FindWindow("Progman", None)
hd = win32gui.FindWindowEx(hd, 0, "SHELLDLL_DefView", None)
hd = win32gui.FindWindowEx(hd, 0, "SysListView32", None)
i = 0
while i < 1000:
commctrl.LVM_SETITEMPOSITION(hd, 0, i, i)
i+100
sleep(1)
正确的方法(使用COM)比我采取的捷径(仍在答案的末尾)更痛苦。
资源:
[SO]:如何在C上使用winapi移动桌面图标? (@IInspectable 的回答)
MSdoc(很多)。提及:
PyWin32文档:
我从第 1st URL 开始“翻译”代码。一切都很顺利,获得了 IFolderView,它没有被 PyWin32 的 COM 扩展包裹。我尝试通过 [SO]:在 Python 中实现 COM 接口(@SimonMourier 的答案)来解决这个问题,但没有成功。
所以,我只能选择自己添加 IFolderView(部分)支持。 PR 是 [GitHub]:mhammond/pywin32 - IFolderView COM 客户端(合并到240220上的main)。
在本地构建shell.pyd(包含上述更改)后,我能够使用以下脚本获得相同的行为(工作正常)。
code01.py:
#!/usr/bin/env python
import msvcrt
import sys
import time
import pythoncom
import win32com.client as wcomcli
from win32com.shell import shell, shellcon
SWC_DESKTOP = 0x08
SWFO_NEEDDISPATCH = 0x01
IID_IFolderView = "{CDE725B0-CCC9-4519-917E-325D72FAB4CE}"
IID_IShellView = "{000214E3-0000-0000-C000-000000000046}"
def main(*argv):
CLSID_ShellWindows = "{9BA05972-F6A8-11CF-A442-00A0C90A8F39}"
shell_windows = wcomcli.Dispatch(CLSID_ShellWindows)
hwnd = 0
dispatch = shell_windows.FindWindowSW(
wcomcli.VARIANT(pythoncom.VT_I4, shellcon.CSIDL_DESKTOP),
wcomcli.VARIANT(pythoncom.VT_EMPTY, None),
SWC_DESKTOP, hwnd, SWFO_NEEDDISPATCH,
)
service_provider = dispatch._oleobj_.QueryInterface(pythoncom.IID_IServiceProvider)
browser = service_provider.QueryService(shell.SID_STopLevelBrowser, shell.IID_IShellBrowser)
shell_view = browser.QueryActiveShellView()
print(shell_view.GetCurrentInfo())
folder_view = shell_view.QueryInterface(IID_IFolderView)
#print(folder_view.QueryInterface(shell.IID_IShellView))
items_len = folder_view.ItemCount(shellcon.SVGIO_ALLVIEW)
print(f"ItemCount: {items_len}")
for i in range(items_len):
item = folder_view.Item(i)
print(f"Item {i:2d}\n {item}")
print(f"Spacings: {folder_view.GetSpacing(300, 300)}, {folder_view.GetDefaultSpacing()}")
item = b"\x1fx@\xf0_d\x81P\x1b\x10\x9f\x08\x00\xaa\x00/\x95N" # "Recycle Bin" equivalent (in my case)
#item = 0 # May also be the index (still "Recicle Bin" equivalent)
#item = 1
print(f"Item {item} position: {folder_view.GetItemPosition(item)}")
for i in range(0, 1080, 16):
print(i)
folder_view.SelectAndPositionItem(item, (i, i), shellcon.SVSI_POSITIONITEM)
time.sleep(0.5)
if msvcrt.kbhit():
break
if __name__ == "__main__":
print(
"Python {:s} {:03d}bit on {:s}\n".format(
" ".join(elem.strip() for elem in sys.version.split("\n")),
64 if sys.maxsize > 0x100000000 else 32,
sys.platform,
)
)
rc = main(*sys.argv[1:])
print("\nDone.\n")
sys.exit(rc)
输出:
[cfati@CFATI-W10PC064:e:\Work\Dev\StackExchange\StackOverflow\q071905594]> "c:\Work\Dev\VEnvs\py_pc064_03.10_test0\Scripts\python.exe" ./code01.py Python 3.10.11 (tags/v3.10.11:7d4cc5a, Apr 5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)] 064bit on win32 (1, 1075839524) ItemCount: 8 Item 0 b'\x1fx@\xf0_d\x81P\x1b\x10\x9f\x08\x00\xaa\x00/\x95N' Item 1 b':\x00\r\x08\x00\x00NX\x95\xa4 \x00ADOBEA~2.LNK\x00\x00T\x00\t\x00\x04\x00\xef\xbemV\xa2\xbcRX\xe1h.\x00\x00\x00\xd8\x96\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00k\xc7(\x00A\x00d\x00o\x00b\x00e\x00 \x00A\x00c\x00r\x00o\x00b\x00a\x00t\x00.\x00l\x00n\x00k\x00\x00\x00\x1c\x00' Item 2 b':\x00\xae\x00\x00\x00\x87O\x96I&\x00desktop.ini\x00H\x00\t\x00\x04\x00\xef\xbe\x87O\xdcITX.m.\x00\x00\x00{\xb2\x03\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x86)\xbf\x00d\x00e\x00s\x00k\x00t\x00o\x00p\x00.\x00i\x00n\x00i\x00\x00\x00\x1a\x00' Item 3 b':\x00\xe4\x08\x00\x00TXtm \x00MICROS~1.LNK\x00\x00V\x00\t\x00\x04\x00\xef\xbesQ\xd1=TXtm.\x00\x00\x00s\xb8\x04\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00h\xf0Q\x00M\x00i\x00c\x00r\x00o\x00s\x00o\x00f\x00t\x00 \x00E\x00d\x00g\x00e\x00.\x00l\x00n\x00k\x00\x00\x00\x1c\x00' Item 4 b'2\x00\x19\x04\x00\x00wSiN \x00CONNEC~1.LNK\x00\x00\x86\x00\t\x00\x04\x00\xef\xbewSiNRX\xe1h.\x00\x00\x00\xa7\x8d\x00\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00X\xe5>\x00C\x00o\x00n\x00n\x00e\x00c\x00t\x00i\x00o\x00n\x00 \x00t\x00o\x00 \x00Q\x00C\x00_\x00A\x00I\x00C\x001\x000\x000\x00_\x006\x004\x00.\x001\x008\x007\x00.\x002\x001\x003\x00.\x001\x009\x004\x00.\x00l\x00n\x00k\x00\x00\x00\x1c\x00' Item 5 b'2\x00\x1a\x01\x00\x00\xafR+\xae&\x00desktop.ini\x00H\x00\t\x00\x04\x00\xef\xbe\xaeR\xeb\x01TX.m.\x00\x00\x00\xd9k\x01\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95\xa35\x00d\x00e\x00s\x00k\x00t\x00o\x00p\x00.\x00i\x00n\x00i\x00\x00\x00\x1a\x00' Item 6 b'2\x00\x9c\x04\x00\x00mV\x94\xb0 \x00TOTALC~1.LNK\x00\x00f\x00\t\x00\x04\x00\xef\xbemV\x94\xb0RX\xe1h.\x00\x00\x00\x9bo\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001\xbf\x01\x01T\x00o\x00t\x00a\x00l\x00 \x00C\x00o\x00m\x00m\x00a\x00n\x00d\x00e\x00r\x00 \x006\x004\x00 \x00b\x00i\x00t\x00.\x00l\x00n\x00k\x00\x00\x00\x1c\x00' Item 7 b'2\x00\x8e\x04\x00\x00mV\x94\xb0 \x00TOTALC~2.LNK\x00\x00X\x00\t\x00\x04\x00\xef\xbemV\x94\xb0RX\xe1h.\x00\x00\x00\x9co\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x001\xbf\x01\x01T\x00o\x00t\x00a\x00l\x00 \x00C\x00o\x00m\x00m\x00a\x00n\x00d\x00e\x00r\x00.\x00l\x00n\x00k\x00\x00\x00\x1c\x00' Spacings: (75, 101), (96, 16) Item b'\x1fx@\xf0_d\x81P\x1b\x10\x9f\x08\x00\xaa\x00/\x95N' position: (13, 2) 0 16 32 48 Done.
注释:
我不知道Win如何将屏幕分成图块,所以我不知道如何使图标在任何迭代中移动(但这超出了问题范围,并且在旧方法中是类似的)。我没有任何特殊原因将 X / Y 坐标增加 16
项目可以通过其数据来指定(在底层 WinAPI 实现中使用)。但由于该数据(上面输出中的乱码字符串)对用户来说毫无意义,因此我添加了使用其索引的功能。如果您想按名称使用它,请检查以下代码(code02.py,来自 [GitHub] 的“窃取”:mhammond/pywin32 - (main) com/win32comext/shell/test/testShellFolder.py):
#!/usr/bin/env python
import sys
from win32com.shell import shell, shellcon
def main(*argv):
sf = shell.SHGetDesktopFolder()
print("Shell Folder is", sf, dir(sf))
for i, e in enumerate(sf):
print(f"Item: {i}\n Name: {sf.GetDisplayNameOf(e, shellcon.SHGDN_NORMAL)}\n Data: {e}")
# And get the enumerator manually
objs = sf.EnumObjects(0, shellcon.SHCONTF_FOLDERS | shellcon.SHCONTF_NONFOLDERS | shellcon.SHCONTF_INCLUDEHIDDEN)
print(objs, dir(objs))
if __name__ == "__main__":
print(
"Python {:s} {:03d}bit on {:s}\n".format(
" ".join(elem.strip() for elem in sys.version.split("\n")),
64 if sys.maxsize > 0x100000000 else 32,
sys.platform,
)
)
rc = main(*sys.argv[1:])
print("\nDone.\n")
sys.exit(rc)
检查[SO]:如何使用 python 和 win32print 更改打印队列中作业的用户名(@CristiFati 的答案)(最后)以了解如何继续执行上述补丁
没有在新的Win 11版本上尝试它,因为我没有它
code00.py:
#!/usr/bin/env python
import msvcrt
import sys
import time
import commctrl as cc
import win32gui as wgui
def main(*argv):
search_criteria = (
(0, "Progman", None),
(0, "SHELLDLL_DefView", None),
(0, "SysListView32", None),
)
wnd = 0
for crit in search_criteria:
wnd = wgui.FindWindowEx(wnd, *crit)
if wnd == 0:
print("Could not find child matching criteria: {:}".format(crit))
return
idx = 0
for i in range(0, 1000, 16):
lparam = (i << 16) | i
print("{:d} - 0x{:08X}".format(i, lparam))
wgui.SendMessage(wnd, cc.LVM_SETITEMPOSITION, idx, lparam)
time.sleep(0.5)
if msvcrt.kbhit():
break
if __name__ == "__main__":
print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
64 if sys.maxsize > 0x100000000 else 32, sys.platform))
rc = main(*sys.argv[1:])
print("\nDone.")
sys.exit(rc)