我的需求和实现代码如下:
''' 脚本修改 uvproj 和 uvopt 会导致文件格式变化,执行此函数打开工程再关闭,可恢复原本格式 '''
def open_close_uvproj(path):
print(f'\n{path}')
for file in os.listdir(path):
if file.endswith('.uvproj') or file.endswith('.uvprojx'):
proc = subprocess.Popen(fr'D:\Program\Keil\UV4\UV4.exe {os.path.join(path, file)}')
time.sleep(10)
proc.terminate()
现在能够打开指定的 Keil 工程,,也能在延时 10s 后自动关闭工程,,但工程文件没有恢复格式。。
猜测是因为只有通过点 Keil 右上角的叉号关闭,,Keil 才能写工程文件,,用 terminate() 和 kill() 杀死进程时,,Keil 不会保存工程文件
请问 Popen 有什么方法可以实现模拟点应用窗口右上角叉号关闭应用的方法吗??
我知道 Popen 有个 send_signal() 方法,,是不是通过该方法发送个特定信号就行了??请大神指点,,谢谢。。
1
NessajCN 2023-11-07 09:36:45 +08:00
你应该直接问怎么修改 uvproj 和 uvopt 才能保证文件格式不变,而不是自己想个这么奇葩的方法来问怎么实现
建议直接把你修改文件的脚本贴上来 |
2
clemente 2023-11-07 09:49:09 +08:00
获取进程号 subprocess.Popen 然后强制 kill
|
3
XIVN1987 OP @NessajCN
uvproj 和 uvopt 文件都是 XML 格式的。。python 似乎没有库能够修改 xml 文件同时保持格式完全不变。。 比如下面这个提问,,十多年了也没有简单的解决方案。。 https://stackoverflow.com/questions/6539891/python-library-for-editing-xml-preserving-formatting-and-comments |
4
XIVN1987 OP 之所以想要让 keil 恢复格式,,是因为每次修改 uvproj 和 uvopt 后提交代码,,都会显示整个文件都完全改变了,,但实际上我只修改了其中几个字符而已。。
|
5
ysc3839 2023-11-07 10:01:00 +08:00 via Android
FindWindow 找到对应窗口,然后发送 WM_CLOSE 消息
|
6
henix 2023-11-07 10:19:39 +08:00 2
可能最好的办法是用 Python 调用 Keil 的命令行工具,实现你要的操作。因为 subprocess.Popen 主要是针对命令行程序。但我对 Keil 不了解,不知道能否纯用命令行实现。
如果你要自动化地打开一个 GUI 程序再关闭,可以在 Win32 的层面使用 Win32 API 来自动化。 大致的原理是: 1. 使用 FindWindow 或其他方法得到窗口句柄 2. 向该窗口发送 WM_CLOSE 消息 参考: https://learn.microsoft.com/zh-cn/windows/win32/learnwin32/closing-the-window https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-findwindowa https://stackoverflow.com/questions/5402158/using-sendmessage-to-send-wm-close-to-another-process 但是以上是 C++ 层面的 API ,如果用 Python 又该怎么做呢?可以使用 pywinauto 。代码大概如下: ```py import time from pywinauto.application import Application app = Application().start("???.exe") time.sleep(10) app.kill(soft=True) ``` 另一种方法是找到顶层窗口之后 close: ```py import time from pywinauto.application import Application from pywinauto.controls.hwndwrapper import HwndWrapper app = Application().start("???.exe") time.sleep(10) root: HwndWrapper = app.top_window() root.set_focus() root.close() ``` |
7
NessajCN 2023-11-07 10:23:13 +08:00
@XIVN1987 你这需求甚至不需要用到任何库呀,简单的一个 re 正则替换不就完了
with open("/path/to/uvproj", "r") as fp: lines = fp.readlines() with open("/path/to/uvproj"", "w") as fp: for line in lines: fp.write(re.sub(r'<regexp>', '<string>', line)) |
8
XIVN1987 OP |
9
NessajCN 2023-11-07 10:43:03 +08:00
@XIVN1987 文件就是文件,只要不是二进制的你这么替换肯定没问题,你正则替换别去动其他字符,文件结构怎么会变?你用这个方法改了试试,不好用你找我
|
10
geelaw 2023-11-07 11:07:14 +08:00 via iPhone
抽象层级错误的原因,点关闭按钮是图形用户界面的概念(更具体来说是 user32 ),结束进程是进程( kernel32 )的概念,进程不一定创建窗口,自然不可能期待操作进程的代码能够处理用户界面。
正确的方法,根据 .NET 里的 Process.CloseMainWindow: 1. 获得目标窗口的句柄 2. 判断目标窗口是否是禁用状态( GetWindowLong ),如果是,则不可关闭 3. 否则,用 PostMessage (不等待回复)或者 SendMessage (等待回复)发送 WM_CLOSE 到目标窗口 但我个人的意见是不需要第二步,因为它不能保证第三步操作的时候窗口依然处于非禁用状态。另外第三步,如果目标窗口是对话框则无效,此时正确的操作是 WM_COMMAND ;第三步也可以考虑 WM_SYSCOMMAND 和 SC_CLOSE 。 最后,如果目标程序提供 COM (例如 Office 系列),则应该优先采用 COM 操作。 P.S. 如果有人搜索“禁用窗口”并看到了这条回复,有必要提示改变窗口禁用/启用状态不可以用 SetWindowLong ,而是要用 EnableWindow 。 |