|
| 1 | +from PyQt5.QtGui import QWindow |
| 2 | +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QListWidget,\ |
| 3 | + QLabel |
| 4 | +import win32con |
| 5 | +import win32gui |
| 6 | + |
| 7 | + |
| 8 | +class Window(QWidget): |
| 9 | + |
| 10 | + def __init__(self, *args, **kwargs): |
| 11 | + super(Window, self).__init__(*args, **kwargs) |
| 12 | + self.resize(800, 600) |
| 13 | + layout = QVBoxLayout(self) |
| 14 | + |
| 15 | + self.myhwnd = int(self.winId()) # 自己的句柄 |
| 16 | + |
| 17 | + layout.addWidget(QPushButton('获取所有可用、可视窗口', self, |
| 18 | + clicked=self._getWindowList, maximumHeight=30)) |
| 19 | + layout.addWidget( |
| 20 | + QLabel('双击列表中的项目则进行嵌入目标窗口到下方\n格式为:句柄|父句柄|标题|类名', self, maximumHeight=30)) |
| 21 | + self.windowList = QListWidget( |
| 22 | + self, itemDoubleClicked=self.onItemDoubleClicked, maximumHeight=200) |
| 23 | + layout.addWidget(self.windowList) |
| 24 | + |
| 25 | + def closeEvent(self, event): |
| 26 | + """窗口关闭""" |
| 27 | + if self.layout().count() == 4: |
| 28 | + self.restore() |
| 29 | + super(Window, self).closeEvent(event) |
| 30 | + |
| 31 | + def _getWindowList(self): |
| 32 | + """清空原来的列表""" |
| 33 | + self.windowList.clear() |
| 34 | + win32gui.EnumWindows(self._enumWindows, None) |
| 35 | + |
| 36 | + def onItemDoubleClicked(self, item): |
| 37 | + """列表双击选择事件""" |
| 38 | + # 先移除掉item |
| 39 | + self.windowList.takeItem(self.windowList.indexFromItem(item).row()) |
| 40 | + hwnd, phwnd, _, _ = item.text().split('|') |
| 41 | + # 开始嵌入 |
| 42 | + |
| 43 | + if self.layout().count() == 4: |
| 44 | + # 如果数量等于4说明之前已经嵌入了一个窗口,现在需要把它释放出来 |
| 45 | + self.restore() |
| 46 | + hwnd, phwnd = int(hwnd), int(phwnd) |
| 47 | + # 嵌入之前的属性 |
| 48 | + style = win32gui.GetWindowLong(hwnd, win32con.GWL_STYLE) |
| 49 | + exstyle = win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE) |
| 50 | + print('save', hwnd, style, exstyle) |
| 51 | + |
| 52 | + widget = QWidget.createWindowContainer(QWindow.fromWinId(hwnd)) |
| 53 | + widget.hwnd = hwnd # 窗口句柄 |
| 54 | + widget.phwnd = phwnd # 父窗口句柄 |
| 55 | + widget.style = style # 窗口样式 |
| 56 | + widget.exstyle = exstyle # 窗口额外样式 |
| 57 | + self.layout().addWidget(widget) |
| 58 | + |
| 59 | + def restore(self): |
| 60 | + """归还窗口""" |
| 61 | + # 有bug,归还后窗口没有了WS_VISIBLE样式,不可见 |
| 62 | + widget = self.layout().itemAt(3).widget() |
| 63 | + print('restore', widget.hwnd, widget.style, widget.exstyle) |
| 64 | + win32gui.SetParent(widget.hwnd, widget.phwnd) # 让它返回它的父窗口 |
| 65 | + win32gui.SetWindowLong( |
| 66 | + widget.hwnd, win32con.GWL_STYLE, widget.style | win32con.WS_VISIBLE) # 恢复样式 |
| 67 | + win32gui.SetWindowLong( |
| 68 | + widget.hwnd, win32con.GWL_EXSTYLE, widget.exstyle) # 恢复样式 |
| 69 | + win32gui.ShowWindow( |
| 70 | + widget.hwnd, win32con.SW_SHOW) # 显示窗口 |
| 71 | + widget.close() |
| 72 | + self.layout().removeWidget(widget) # 从布局中移出 |
| 73 | + widget.deleteLater() |
| 74 | + |
| 75 | + def _enumWindows(self, hwnd, _): |
| 76 | + """遍历回调函数""" |
| 77 | + if hwnd == self.myhwnd: |
| 78 | + return # 防止自己嵌入自己 |
| 79 | + if win32gui.IsWindow(hwnd) and win32gui.IsWindowVisible(hwnd) and win32gui.IsWindowEnabled(hwnd): |
| 80 | + phwnd = win32gui.GetParent(hwnd) |
| 81 | + title = win32gui.GetWindowText(hwnd) |
| 82 | + name = win32gui.GetClassName(hwnd) |
| 83 | + self.windowList.addItem( |
| 84 | + '{0}|{1}|\t标题:{2}\t|\t类名:{3}'.format(hwnd, phwnd, title, name)) |
| 85 | + |
| 86 | + |
| 87 | +if __name__ == '__main__': |
| 88 | + import sys |
| 89 | + from PyQt5.QtWidgets import QApplication |
| 90 | + app = QApplication(sys.argv) |
| 91 | + w = Window() |
| 92 | + w.show() |
| 93 | + sys.exit(app.exec_()) |
0 commit comments