This commit is contained in:
Irony 2018-04-18 01:11:59 +08:00
commit b5122dc6f1
21 changed files with 634 additions and 77 deletions

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Python35</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 3.0</pydev_property>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/${PROJECT_DIR_NAME}</path>
</pydev_pathproperty>
</pydev_project>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 3.0</pydev_property>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/${PROJECT_DIR_NAME}</path>
</pydev_pathproperty>
</pydev_project>

View file

@ -24,9 +24,15 @@
- [1.17 下拉选择联动](下拉选择联动/)
- [1.18 人脸描点检测](人脸描点检测/)
- [1.19 腾讯视频热播列表](腾讯视频热播列表/)
- [1.20 exec()动态生成控件](partner_625781186/1.exec动态生成控件/)
- 1.20 exec()动态生成控件
- [1. 动态控件基础例子 - 动态生成按钮](partner_625781186/1.exec动态生成控件/dynamic_button)
- [2. 动态控件基础例子 - 动态生成菜单](partner_625781186/1.exec动态生成控件/dynamic_Menu)
- [3. 配合setting记录模型类型](partner_625781186/1.exec动态生成控件/)
- [1.21 仿QQ设置面板](仿QQ设置面板/)
- [1.22 Json生成QTreeWidget](Json生成QTreeWidget/)
- [1.23 嵌入外部窗口](嵌入外部窗口/)
- [1.24 必应壁纸](https://github.com/892768447/BingWallpaper)
### [2.QGraphicsView练习](QGraphicsView练习/)
- [2.1 世界地图](QGraphicsView练习/世界地图)

View file

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'D:\pyPro\dynamic_Menu\动态控件.ui'
#
# Created by: PyQt5 UI code generator 5.9.2
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(370, 403)
Dialog.setSizeGripEnabled(True)
self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
self.verticalLayout.setObjectName("verticalLayout")
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Dialog = QtWidgets.QDialog()
ui = Ui_Dialog()
ui.setupUi(Dialog)
Dialog.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Project SYSTEM "Project-5.1.dtd">
<!-- eric project file for project dynamic_Controls -->
<!-- Saved: 2018-04-11, 02:00:19 -->
<!-- Copyright (C) 2018 , -->
<Project version="5.1">
<Language>en_US</Language>
<Hash>b35a15d37a7cb52779310396c238b2e70ac76888</Hash>
<ProgLanguage mixed="0">Python3</ProgLanguage>
<ProjectType>PyQt5</ProjectType>
<Version>0.1</Version>
<Author></Author>
<Email></Email>
<Eol index="0"/>
<Sources>
<Source>Ui_动态控件.py</Source>
<Source>__init__.py</Source>
<Source>动态控件.py</Source>
</Sources>
<Forms>
<Form>动态控件.ui</Form>
</Forms>
<Translations/>
<Resources/>
<Interfaces/>
<Others/>
<Vcs>
<VcsType>None</VcsType>
</Vcs>
<FiletypeAssociations>
<FiletypeAssociation pattern="*.e4p" type="OTHERS"/>
<FiletypeAssociation pattern="*.idl" type="INTERFACES"/>
<FiletypeAssociation pattern="*.md" type="OTHERS"/>
<FiletypeAssociation pattern="*.py" type="SOURCES"/>
<FiletypeAssociation pattern="*.py3" type="SOURCES"/>
<FiletypeAssociation pattern="*.pyw" type="SOURCES"/>
<FiletypeAssociation pattern="*.pyw3" type="SOURCES"/>
<FiletypeAssociation pattern="*.qm" type="TRANSLATIONS"/>
<FiletypeAssociation pattern="*.qrc" type="RESOURCES"/>
<FiletypeAssociation pattern="*.rst" type="OTHERS"/>
<FiletypeAssociation pattern="*.ts" type="TRANSLATIONS"/>
<FiletypeAssociation pattern="*.txt" type="OTHERS"/>
<FiletypeAssociation pattern="*.ui" type="FORMS"/>
<FiletypeAssociation pattern="README" type="OTHERS"/>
<FiletypeAssociation pattern="README.*" type="OTHERS"/>
</FiletypeAssociations>
</Project>

View file

@ -0,0 +1,103 @@
# -*- coding: utf-8 -*-
"""
Module implementing Dialog.
"""
from PyQt5 import QtGui, QtWidgets, QtCore, QtWinExtras
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from functools import partial
from Ui_动态控件 import Ui_Dialog
class Dialog(QDialog, Ui_Dialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.setupUi(self)
self.dynamic1()
self.dynamic2()
# 法一
def dynamic1(self):
for i in range(5):
self.pushButton = QtWidgets.QPushButton(self)
self.pushButton.setText("pushButton%d"%i)
self.pushButton.setObjectName("pushButton%d"%i)
self.verticalLayout.addWidget(self.pushButton)
self.pushButton.setContextMenuPolicy(Qt.CustomContextMenu)
self.pushButton.customContextMenuRequested.connect(lambda:self.helpMenu(i))#右键请求,传入i实际上一直是4
self.pushButton.clicked.connect(self.pr)
# 法二
def dynamic2(self):
for i in range(5, 8):
txt="""
self.pushButton_{i} = QtWidgets.QPushButton(self);
self.pushButton_{i}.setText("pushButton{i}");
self.pushButton_{i}.setObjectName("pushButton{i}");
self.verticalLayout.addWidget(self.pushButton_{i});
self.pushButton_{i}.setContextMenuPolicy(Qt.CustomContextMenu)
self.pushButton_{i}.customContextMenuRequested.connect(partial(self.helpMenu,i))#右键请求,用lambda会报错,partial需要import
self.pushButton_{i}.clicked.connect(self.pr)
""".format(i=i)
exec(txt)
#只能法二可用的方式
self.pushButton_5.clicked.connect(self.pr2)
self.pushButton_6.clicked.connect(self.pr2)
self.pushButton_7.clicked.connect(self.pr2)
def helpMenu(self, *type):
'''帮助按钮的右键菜单'''
print(type)
popMenu =QMenu()
if type[0]==5 or self.sender().text()== 'pushButton0':
popMenu.addAction(u'关于',self.pr)
popMenu.addSeparator()
elif type[0]==6 or self.sender().text()== 'pushButton1':
popMenu.addAction(u'清理图片',self.pr)
popMenu.addSeparator()
elif type[0]==7 or self.sender().text()== 'pushButton2':
gitMenu=QMenu('同步到Git', popMenu)
self.pushAction=QAction(u'上传', self, triggered=lambda:self.pr)
gitMenu.addAction(self.pushAction)
self.pullAction=QAction(u'下载', self, triggered=lambda:self.pr)
gitMenu.addAction(self.pullAction)
gitMenu.addSeparator()
gitMenu.addAction(u'配置仓库',lambda:self.pr)
popMenu.addAction(gitMenu.menuAction())#!!
popMenu.exec_(QCursor.pos())#鼠标位置
def pr(self):
'''法一和法二都可用的调用
if self.sender().objectName=='XXX':
self.pr2()
'''
print(self.sender().text())
print(self.sender().objectName())
print(self.pushButton.text())
def pr2(self):
print(2)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ui = Dialog()
ui.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>370</width>
<height>403</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout"/>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'D:\pyPro\dynamic_button\动态控件.ui'
#
# Created by: PyQt5 UI code generator 5.9.2
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(370, 403)
Dialog.setSizeGripEnabled(True)
self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
self.verticalLayout.setObjectName("verticalLayout")
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Dialog = QtWidgets.QDialog()
ui = Ui_Dialog()
ui.setupUi(Dialog)
Dialog.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Project SYSTEM "Project-5.1.dtd">
<!-- eric project file for project dynamic_Controls -->
<!-- Saved: 2018-04-11, 02:00:19 -->
<!-- Copyright (C) 2018 , -->
<Project version="5.1">
<Language>en_US</Language>
<Hash>b35a15d37a7cb52779310396c238b2e70ac76888</Hash>
<ProgLanguage mixed="0">Python3</ProgLanguage>
<ProjectType>PyQt5</ProjectType>
<Version>0.1</Version>
<Author></Author>
<Email></Email>
<Eol index="0"/>
<Sources>
<Source>Ui_动态控件.py</Source>
<Source>__init__.py</Source>
<Source>动态控件.py</Source>
</Sources>
<Forms>
<Form>动态控件.ui</Form>
</Forms>
<Translations/>
<Resources/>
<Interfaces/>
<Others/>
<Vcs>
<VcsType>None</VcsType>
</Vcs>
<FiletypeAssociations>
<FiletypeAssociation pattern="*.e4p" type="OTHERS"/>
<FiletypeAssociation pattern="*.idl" type="INTERFACES"/>
<FiletypeAssociation pattern="*.md" type="OTHERS"/>
<FiletypeAssociation pattern="*.py" type="SOURCES"/>
<FiletypeAssociation pattern="*.py3" type="SOURCES"/>
<FiletypeAssociation pattern="*.pyw" type="SOURCES"/>
<FiletypeAssociation pattern="*.pyw3" type="SOURCES"/>
<FiletypeAssociation pattern="*.qm" type="TRANSLATIONS"/>
<FiletypeAssociation pattern="*.qrc" type="RESOURCES"/>
<FiletypeAssociation pattern="*.rst" type="OTHERS"/>
<FiletypeAssociation pattern="*.ts" type="TRANSLATIONS"/>
<FiletypeAssociation pattern="*.txt" type="OTHERS"/>
<FiletypeAssociation pattern="*.ui" type="FORMS"/>
<FiletypeAssociation pattern="README" type="OTHERS"/>
<FiletypeAssociation pattern="README.*" type="OTHERS"/>
</FiletypeAssociations>
</Project>

View file

@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
"""
Module implementing Dialog.
"""
from PyQt5 import QtGui, QtWidgets, QtCore, QtWinExtras
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from Ui_动态控件 import Ui_Dialog
class Dialog(QDialog, Ui_Dialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.setupUi(self)
self.dynamic1()
self.dynamic2()
# 法一
def dynamic1(self):
for i in range(5):
self.pushButton = QtWidgets.QPushButton(self)
self.pushButton.setText("pushButton%d"%i)
self.pushButton.setObjectName("pushButton%d"%i)
self.verticalLayout.addWidget(self.pushButton)
self.pushButton.clicked.connect(self.pr)
# 法二
def dynamic2(self):
for i in range(4):
txt="""
self.pushButton_{i} = QtWidgets.QPushButton(self);
self.pushButton_{i}.setText("pushButton{i}");
self.pushButton_{i}.setObjectName("pushButton{i}");
self.verticalLayout.addWidget(self.pushButton_{i});
self.pushButton_{i}.clicked.connect(self.pr)
""".format(i=i)
exec(txt)
#只能法二可用的方式
self.pushButton_1.clicked.connect(self.pr2)
self.pushButton_2.clicked.connect(self.pr2)
self.pushButton_3.clicked.connect(self.pr2)
def pr(self):
'''法一和法二都可用的调用
if self.sender().objectName=='XXX':
self.pr2()
'''
print(self.sender().text())
print(self.sender().objectName())
print(self.pushButton.text())
def pr2(self):
print(2)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ui = Dialog()
ui.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>370</width>
<height>403</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout"/>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,102 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
@resource:http://blog.csdn.net/zzwdkxx/article/details/39338429
@description: 自定义QEvent事件,上面网址为C++版本原理解释,此篇为python改编
@Created on 2018年3月22日
@email: 625781186@qq.com
'''
from PyQt5 import QtGui, QtWidgets, QtCore
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
MyEventType = QEvent.registerEventType(QEvent.User+100)
#//长官
class MyEvent (QEvent):
def __init__(self, *args, **kwargs):
super(MyEvent, self).__init__(*args, **kwargs)
print(MyEventType)
def QEvent(self,MyEventType):
pass
#//信使
class MySender(QCoreApplication):
def __init__(self, *args, **kwargs):
super(MySender, self).__init__(*args, **kwargs)
def notify(self, receiver, event):
if(event.type() == MyEventType):
print("MyEventType is coming!")
# //return true;
# /*这里不能return true,因为重写notify就是在事件被向下传递之前截住它
# 随便搞它搞完了还得给QCoreApplication::notify向下传递除非在mySender.notify
# 实现了事件向下传递的那一套。直接返回的话myArmy就收不到这个事件因为执行完这个
# mySender.notify的return true后事件传递被人为的在半截终止了
# 见Qt事件处理的五个层次http:#//blog.csdn.net/michealtx/article/details/6865891
# 下面的myArmy的安装的过滤器和它自己的event都不会收到这个事件更甭提最后干活
# 的myEventHandler了。所以在主函数中执行完mySender.sendEvent把myEvent
# 交给mySender.notify这个败家子儿后就执行mySender.exec进入其它事件的循环了。这就是
# 问题http:#//topic.csdn.net/u/20111012/19/78036d16-c163-40f9-a05c-3b7d6f4e9043.html
# 出现的原因。感谢1+1=2大牛非常感谢
# */
return QCoreApplication.notify(self,receiver, event)
#//军队
class MyArmy (QWidget):
def MyEventHandler(self,event):
print("The event is being handled!")
event.accept()
def event(self, event):
if(event.type() == MyEventType):
print("event() is dispathing MyEvent")
self.MyEventHandler(event) # //调用事件处理函数
if(event.isAccepted()):
print("The event has been handled!")
return True
return QObject. event(self,event)
#//监控者
class MyWatcher (QObject):
def eventFilter(self,watched, event):
if(event.type() == MyEventType):
print("I don't wanna filter MyEventType")
return False
return QObject.eventFilter(self,watched, event)
if __name__ == "__main__":
import sys
#//QCoreApplication a(argc, argv);
app = QtWidgets.QApplication(sys.argv)
mySender = MySender(sys.argv)
myArmy=MyArmy ()
myWatcher=MyWatcher ()
myArmy.installEventFilter(myWatcher) # //安装事件过滤器
myEvent=MyEvent (MyEventType)
mySender.sendEvent( myArmy, myEvent)
mySender.exec()

View file

@ -9,71 +9,59 @@ Created on 2017年12月11日
@file: HotKey
@description:
'''
import ctypes # @UnusedImport
import ctypes.wintypes
from datetime import datetime
import sys
from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout,\
QMessageBox, QTextBrowser, QPushButton
from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QTextBrowser, QPushButton
import keyboard
# 参考
# https://github.com/wujunwei/python-cookbook/blob/6e550d1a2b2b045cb07e56dd0198ccf01a2f3ea1/HotKey.py
# https://github.com/chenyijie4238215/notebook/blob/ba11fcc43cf8d623d1d1a722c261ddc20ad6b941/global_hotkey/GlobalHotKey.py
__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
__Copyright__ = "Copyright (c) 2017 Irony.\"[讽刺]"
__Version__ = "Version 1.0"
WM_HOTKEY = 0x0312
MOD_ALT = 0x0001
MOD_NONE = 0x000
MOD_CONTROL = 0x0002
MOD_SHIFT = 0x0004
MOD_WIN = 0x0008
Modifier = {
"None": MOD_NONE,
"Ctrl": MOD_CONTROL,
"Alt": MOD_ALT,
"Shift": MOD_SHIFT,
"Win": MOD_WIN
}
class Window(QWidget):
KeyIds = {}
def __init__(self, *args, **kwargs):
super(Window, self).__init__(*args, **kwargs)
layout = QVBoxLayout(self)
self.logView = QTextBrowser(self)
self.logView.append("点击右上角关闭按钮会隐藏窗口,通过热键Alt+S来显示")
self.logView.append("等待热键中")
layout.addWidget(QPushButton("退出整个程序", self, clicked=self.onQuit))
layout.addWidget(QPushButton(
"退出整个程序", self, clicked=self.onQuit))
layout.addWidget(self.logView)
def unregisterHotKey(self, kid):
ctypes.windll.user32.UnregisterHotKey(ctypes.c_int(self.winId()), kid)
keyboard.add_hotkey('alt+s', self.onShow, suppress=False) # 显示界面
keyboard.add_hotkey('ctrl+s', self.onHide, suppress=False) # 隐藏界面
keyboard.add_hotkey('shift+s', self.onQuit, suppress=False) # 退出程序
def registerHotKey(self, kid, modifier, key):
key = str(key).upper()
_modifier = Modifier.get(modifier, None)
if not _modifier:
return QMessageBox.critical(self, "错误", "modifier key {0}未找到".format(modifier))
success = ctypes.windll.user32.RegisterHotKey(
ctypes.c_int(self.winId()), kid, _modifier, ord(key))
if success:
self.KeyIds[kid] = modifier + "+" + key
self.logView.append("热键:{0}+{1}注册{2}".format(modifier, key, "成功"))
else:
self.logView.append("热键:{0}+{1}注册{2}".format(modifier, key, "失败"))
# 拦截系统的快捷键,suppress=True表示拦截,不传递到其它程序
keyboard.add_hotkey(
'win+s', lambda: self.logView.append('按下了win+s'), suppress=True)
keyboard.add_hotkey(
'win+r', lambda: self.logView.append('按下了win+r'), suppress=True)
# 这个东西千万不能拦截掉,要出问题滴
keyboard.add_hotkey(
'ctrl+alt+del', lambda: self.logView.append('😏😏我知道你按了任务管理器😏😏'))
# 这个函数类似while True由于这里有界面GUI的loop事件可以达到类似的效果
# keyboard.wait()#Block forever, like `while True`.==
def onShow(self):
"""显示"""
self.logView.append('按下alt+s')
self.show()
self.showNormal()
def onHide(self):
"""隐藏"""
self.logView.append('按下ctrl+s')
self.hide()
def onQuit(self):
# 退出程序
for kid in self.KeyIds:
self.unregisterHotKey(kid)
"""退出函数"""
keyboard.unhook_all_hotkeys() # 取消所有热键
QApplication.instance().quit()
def closeEvent(self, event):
@ -81,30 +69,9 @@ class Window(QWidget):
self.hide()
return event.ignore()
# 能监听热键,但是有个问题就是其它程序无法接受到事件
# 比如Ctrl+S,在记事本里随便输入内容按下Ctrl+S发现无法保存
def nativeEvent(self, eventType, message):
if eventType == "windows_generic_MSG" or eventType == "windows_dispatcher_MSG":
msg = ctypes.wintypes.MSG.from_address(message.__int__())
# 这段代码无法运行
# if ctypes.windll.user32.GetMessageA(ctypes.byref(msg), None, 0,
# 0) != 0:
if msg.message == WM_HOTKEY:
if msg.wParam == 1: # Alt+S
self.show()
self.logView.append("id:{0}, {1} at time:{2}".format(
msg.wParam, self.KeyIds.get(msg.wParam, None), datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
return True, 0
return super(Window, self).nativeEvent(eventType, message)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = Window()
w.show()
w.registerHotKey(1, "Alt", "S")
w.registerHotKey(2, "Ctrl", "S")
w.registerHotKey(3, "Shift", "S")
w.registerHotKey(4, "Win", "S")
w.registerHotKey(5, "Win", "Z")
sys.exit(app.exec_())

View file

@ -1,9 +1,8 @@
# PyQt全局热键 For Windows Test
能监听热键,但是有个问题就是其它程序无法接受到事件<br/>
比如Ctrl+S,在记事本里随便输入内容按下Ctrl+S发现无法保存<br/>
pip install keyboard
这里还有个比较好的例子[hotkey.py](https://github.com/yeejlan/py-stock-watcher/blob/87a7b7cfdeb01b44058fac6906c9cce5fd19cac0/modules/hotkey.py)
https://github.com/892768447/keyboard
# 截图
![截图](ScreenShot/1.png)
![截图](ScreenShot/1.gif)

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View file

@ -0,0 +1 @@
keyboard

View file

@ -0,0 +1,93 @@
from PyQt5.QtGui import QWindow
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QListWidget,\
QLabel
import win32con
import win32gui
class Window(QWidget):
def __init__(self, *args, **kwargs):
super(Window, self).__init__(*args, **kwargs)
self.resize(800, 600)
layout = QVBoxLayout(self)
self.myhwnd = int(self.winId()) # 自己的句柄
layout.addWidget(QPushButton('获取所有可用、可视窗口', self,
clicked=self._getWindowList, maximumHeight=30))
layout.addWidget(
QLabel('双击列表中的项目则进行嵌入目标窗口到下方\n格式为:句柄|父句柄|标题|类名', self, maximumHeight=30))
self.windowList = QListWidget(
self, itemDoubleClicked=self.onItemDoubleClicked, maximumHeight=200)
layout.addWidget(self.windowList)
def closeEvent(self, event):
"""窗口关闭"""
if self.layout().count() == 4:
self.restore()
super(Window, self).closeEvent(event)
def _getWindowList(self):
"""清空原来的列表"""
self.windowList.clear()
win32gui.EnumWindows(self._enumWindows, None)
def onItemDoubleClicked(self, item):
"""列表双击选择事件"""
# 先移除掉item
self.windowList.takeItem(self.windowList.indexFromItem(item).row())
hwnd, phwnd, _, _ = item.text().split('|')
# 开始嵌入
if self.layout().count() == 4:
# 如果数量等于4说明之前已经嵌入了一个窗口现在需要把它释放出来
self.restore()
hwnd, phwnd = int(hwnd), int(phwnd)
# 嵌入之前的属性
style = win32gui.GetWindowLong(hwnd, win32con.GWL_STYLE)
exstyle = win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE)
print('save', hwnd, style, exstyle)
widget = QWidget.createWindowContainer(QWindow.fromWinId(hwnd))
widget.hwnd = hwnd # 窗口句柄
widget.phwnd = phwnd # 父窗口句柄
widget.style = style # 窗口样式
widget.exstyle = exstyle # 窗口额外样式
self.layout().addWidget(widget)
def restore(self):
"""归还窗口"""
# 有bug归还后窗口没有了WS_VISIBLE样式不可见
widget = self.layout().itemAt(3).widget()
print('restore', widget.hwnd, widget.style, widget.exstyle)
win32gui.SetParent(widget.hwnd, widget.phwnd) # 让它返回它的父窗口
win32gui.SetWindowLong(
widget.hwnd, win32con.GWL_STYLE, widget.style | win32con.WS_VISIBLE) # 恢复样式
win32gui.SetWindowLong(
widget.hwnd, win32con.GWL_EXSTYLE, widget.exstyle) # 恢复样式
win32gui.ShowWindow(
widget.hwnd, win32con.SW_SHOW) # 显示窗口
widget.close()
self.layout().removeWidget(widget) # 从布局中移出
widget.deleteLater()
def _enumWindows(self, hwnd, _):
"""遍历回调函数"""
if hwnd == self.myhwnd:
return # 防止自己嵌入自己
if win32gui.IsWindow(hwnd) and win32gui.IsWindowVisible(hwnd) and win32gui.IsWindowEnabled(hwnd):
phwnd = win32gui.GetParent(hwnd)
title = win32gui.GetWindowText(hwnd)
name = win32gui.GetClassName(hwnd)
self.windowList.addItem(
'{0}|{1}|\t标题:{2}\t|\t类名:{3}'.format(hwnd, phwnd, title, name))
if __name__ == '__main__':
import sys
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,11 @@
# 内嵌外部窗口
### 原理思路:
- 1.使用SetParent函数设置外部窗口的parent为Qt的窗口
- 2.Qt使用QWidget.createWindowContainer(QWindow.fromWinId(窗口ID))生成QWidget
- 3.使用GetWindowLong得到原来窗口的样式属性style = win32gui.GetWindowLong(hwnd, win32con.GWL_STYLE)和exstyle = win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE)
- 4.这里还原窗口后不会显示用spy++发现没有了WS_VISIBLE样式未解决
截图
![1](ScreenShot/1.gif)

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 KiB