diff --git a/Demo/README.md b/Demo/README.md index 31e582c..3bd9406 100644 --- a/Demo/README.md +++ b/Demo/README.md @@ -23,6 +23,7 @@ - [判断信号是否连接](#20判断信号是否连接) - [调用虚拟键盘](#21调用虚拟键盘) - [动态忙碌光标](#22动态忙碌光标) + - [屏幕变动监听](#23屏幕变动监听) ## 1、重启窗口Widget [运行 RestartWindow.py](RestartWindow.py) @@ -223,4 +224,11 @@ PyQt 结合 Opencv 进行人脸检测; 通过定时器不停的修改光标图片来实现动态效果 -![GifCursor](ScreenShot/GifCursor.gif) \ No newline at end of file +![GifCursor](ScreenShot/GifCursor.gif) + +## 23、屏幕变动监听 +[运行 ScreenNotify.py](ScreenNotify.py) + +通过定时器减少不同的变化信号,尽量保证只调用一次槽函数来获取信息 + +![ScreenNotify](ScreenShot/ScreenNotify.png) \ No newline at end of file diff --git a/Demo/ScreenNotify.py b/Demo/ScreenNotify.py new file mode 100644 index 0000000..33c29cb --- /dev/null +++ b/Demo/ScreenNotify.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Created on 2021/4/13 +@author: Irony +@site: https://github.com/PyQt5 +@email: 892768447@qq.com +@file: ScreenNotify +@description: 屏幕、分辨率、DPI变化通知 +""" +import sys + +from PyQt5.QtCore import QTimer, QRect +from PyQt5.QtWidgets import QApplication, QPlainTextEdit, qApp + + +class Window(QPlainTextEdit): + + def __init__(self, *args, **kwargs): + super(Window, self).__init__(*args, **kwargs) + self.appendPlainText('修改分辨率后查看') + # 记录最后一次的值(减少槽调用) + self.m_rect = QRect() + # 使用定时器来延迟触发最后一次变化 + self.m_timer = QTimer(self, timeout=self.onSolutionChanged) + self.m_timer.setSingleShot(True) # **重要** 保证多次信号尽量少的调用函数 + + # 主要是多屏幕->无屏幕->有屏幕 + qApp.primaryScreenChanged.connect(lambda _: self.m_timer.start(1000)) + # 其它信号最终基本上都会调用该信号 + qApp.primaryScreen().virtualGeometryChanged.connect(lambda _: self.m_timer.start(1000)) + # DPI变化 + qApp.primaryScreen().logicalDotsPerInchChanged.connect(lambda _: self.m_timer.start(1000)) + + def onSolutionChanged(self): + # 获取主屏幕 + screen = qApp.primaryScreen() + if self.m_rect == screen.availableVirtualGeometry(): + return + self.m_rect = screen.availableVirtualGeometry() + # 所有屏幕可用大小 + self.appendPlainText('\navailableVirtualGeometry: {0}'.format(str(screen.availableVirtualGeometry()))) + # 获取所有屏幕 + screens = qApp.screens() + for screen in screens: + self.appendPlainText( + 'screen: {0}, geometry({1}), availableGeometry({2}), logicalDotsPerInch({3}), ' + 'physicalDotsPerInch({4}), refreshRate({5})'.format( + screen.name(), screen.geometry(), screen.availableGeometry(), screen.logicalDotsPerInch(), + screen.physicalDotsPerInch(), screen.refreshRate())) + + +if __name__ == '__main__': + app = QApplication(sys.argv) + w = Window() + w.show() + sys.exit(app.exec_()) diff --git a/Demo/ScreenShot/ScreenNotify.png b/Demo/ScreenShot/ScreenNotify.png new file mode 100644 index 0000000..e95330c Binary files /dev/null and b/Demo/ScreenShot/ScreenNotify.png differ diff --git a/QtRemoteObjects/SyncUi/ClipboardMaster.py b/QtRemoteObjects/SyncUi/ClipboardMaster.py new file mode 100644 index 0000000..feefebb --- /dev/null +++ b/QtRemoteObjects/SyncUi/ClipboardMaster.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Created on 2020/7/31 +@author: Irony +@site: https://pyqt.site https://github.com/PyQt5 +@email: 892768447@qq.com +@file: ClipboardMaster +@description: +""" +from PyQt5.QtCore import QUrl, pyqtSlot, pyqtSignal, QLoggingCategory, QVariant, QMimeData +from PyQt5.QtRemoteObjects import QRemoteObjectHost +from PyQt5.QtWidgets import QTextBrowser + +__Author__ = 'Irony' +__Copyright__ = 'Copyright (c) 2019 Irony' +__Version__ = 1.0 + +import sys + + +class WindowMaster(QTextBrowser): + SignalUpdateMimeData = pyqtSignal( + bool, QVariant, # color + bool, QVariant, # html + bool, QVariant, # image + bool, QVariant, # text + bool, QVariant, # urls + ) + + def __init__(self, *args, **kwargs): + super(WindowMaster, self).__init__(*args, **kwargs) + # 监听剪切板 + # clipboard = QApplication.clipboard() + # clipboard.dataChanged.connect(self.on_data_changed) + # 开启节点 + host = QRemoteObjectHost(QUrl('tcp://0.0.0.0:' + sys.argv[1]), parent=self) + host.enableRemoting(self, 'WindowMaster') + self.append('开启节点完成') + + def on_data_changed(self): + # 服务端剪贴板变化后发送到客户端 + clipboard = QApplication.clipboard() + clipboard.blockSignals(True) + mime_data = clipboard.mimeData() + self.SignalUpdateMimeData.emit( + mime_data.hasColor(), mime_data.colorData(), + mime_data.hasHtml(), mime_data.html(), + mime_data.hasImage(), mime_data.imageData(), + mime_data.hasText(), mime_data.text(), + mime_data.hasUrls(), mime_data.urls(), + ) + clipboard.blockSignals(False) + + @pyqtSlot( + bool, QVariant, # color + bool, QVariant, # html + bool, QVariant, # image + bool, QVariant, # text + bool, QVariant, # urls + bool, QVariant # files + ) + def updateMimeData(self, + hasColor, color, + hasHtml, html, + hasImage, image, + hasText, text, + hasUrls, urls, + hasFiles, files, + ): + # 客户端剪切板同步到服务端 + self.append('收到客户端发送的剪贴板') + clipboard = QApplication.clipboard() + clipboard.blockSignals(True) + data = QMimeData() + if hasColor: + data.setColorData(color) + if hasHtml: + data.setHtml(html) + if hasImage: + data.setImageData(image) + if hasText: + data.setText(text) + # if hasUrls: + # data.setUrls(urls) + if hasFiles: + data.setData('') + clipboard.setMimeData(data) + clipboard.blockSignals(False) + + +if __name__ == '__main__': + import cgitb + + cgitb.enable(1, None, 5, '') + from PyQt5.QtWidgets import QApplication + + QLoggingCategory.setFilterRules('qt.remoteobjects.debug=true\n' + 'qt.remoteobjects.warning=true') + + app = QApplication(sys.argv) + w = WindowMaster() + w.show() + sys.exit(app.exec_()) diff --git a/QtRemoteObjects/SyncUi/ClipboardSlave.py b/QtRemoteObjects/SyncUi/ClipboardSlave.py new file mode 100644 index 0000000..bfe9b60 --- /dev/null +++ b/QtRemoteObjects/SyncUi/ClipboardSlave.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Created on 2020/7/31 +@author: Irony +@site: https://pyqt.site https://github.com/PyQt5 +@email: 892768447@qq.com +@file: ClipboardSlave +@description: +""" +from PyQt5.QtCore import QUrl, pyqtSignal, QVariant, QMimeData +from PyQt5.QtRemoteObjects import QRemoteObjectNode, QRemoteObjectReplica +from PyQt5.QtWidgets import QTextBrowser + +__Author__ = 'Irony' +__Copyright__ = 'Copyright (c) 2019 Irony' +__Version__ = 1.0 + + +class WindowSlave(QTextBrowser): + SignalUpdateMimeData = pyqtSignal( + bool, QVariant, # color + bool, QVariant, # html + bool, QVariant, # image + bool, QVariant, # text + bool, QVariant, # urls + bool, QVariant, # files + ) + + def __init__(self, *args, **kwargs): + super(WindowSlave, self).__init__(*args, **kwargs) + # 监听剪切板 + clipboard = QApplication.clipboard() + clipboard.dataChanged.connect(self.on_data_changed) + # 加入Master节点 + node = QRemoteObjectNode(parent=self) + node.connectToNode(QUrl('tcp://{}:{}'.format(sys.argv[1], sys.argv[2]))) + # 获取WindowMaster对象 + self.windowMaster = node.acquireDynamic('WindowMaster') + # 初始化成功后才能去绑定信号等 + self.windowMaster.initialized.connect(self.onInitialized) + # 状态改变 https://doc.qt.io/qt-5/qremoteobjectreplica.html#State-enum + self.windowMaster.stateChanged.connect(self.onStateChanged) + + def onStateChanged(self, newState, oldState): + if newState == QRemoteObjectReplica.Suspect: + self.append('连接丢失') + + def onInitialized(self): + self.SignalUpdateMimeData.connect(self.windowMaster.updateMimeData) + # self.windowMaster.SignalUpdateMimeData.connect(self.updateMimeData) + self.append('绑定信号槽完成') + + def on_data_changed(self): + # 客户端剪贴板变化后发送到远程 + print('on_data_changed') + clipboard = QApplication.clipboard() + clipboard.blockSignals(True) + mime_data = clipboard.mimeData() + files = mime_data.data('text/uri-list') + self.SignalUpdateMimeData.emit( + mime_data.hasColor(), mime_data.colorData(), + mime_data.hasHtml(), mime_data.html(), + mime_data.hasImage(), mime_data.imageData(), + mime_data.hasText(), mime_data.text(), + mime_data.hasUrls(), mime_data.urls(), + True if files else False, files, + ) + clipboard.blockSignals(False) + + def updateMimeData(self, + hasColor, color, + hasHtml, html, + hasImage, image, + hasText, text, + hasUrls, urls + ): + # 远程的剪贴板同步到客户端 + clipboard = QApplication.clipboard() + clipboard.blockSignals(True) + data = QMimeData() + if hasColor: + data.setColorData(color) + if hasHtml: + data.setHtml(html) + if hasImage: + data.setImageData(image) + if hasText: + data.setText(text) + if hasUrls: + data.setUrls(urls) + clipboard.setMimeData(data) + clipboard.blockSignals(False) + + +if __name__ == '__main__': + import sys + import cgitb + + cgitb.enable(1, None, 5, '') + from PyQt5.QtWidgets import QApplication + + app = QApplication(sys.argv) + w = WindowSlave() + w.show() + sys.exit(app.exec_()) diff --git a/README.md b/README.md index 6405705..e4ac617 100644 --- a/README.md +++ b/README.md @@ -253,6 +253,7 @@ https://pyqt.site 论坛是专门针对PyQt5学习和提升开设的网站,分 - [判断信号是否连接](Demo/IsSignalConnected.py) - [调用虚拟键盘](Demo/CallVirtualKeyboard.py) - [动态忙碌光标](Demo/GifCursor.py) + - [屏幕变动监听](Demo/ScreenNotify.py) # QQ群 diff --git a/Test/ColumnView.py b/Test/ColumnView.py new file mode 100644 index 0000000..5ce9859 --- /dev/null +++ b/Test/ColumnView.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Created on 2020/9/14 +@author: Irony +@site: https://pyqt.site https://github.com/PyQt5 +@email: 892768447@qq.com +@file: ColumnView +@description: +""" + +from PyQt5.QtWidgets import QComboBox, QFileSystemModel, QHBoxLayout, QSpacerItem, QSizePolicy + +__Author__ = 'Irony' +__Copyright__ = 'Copyright (c) 2020' +__Version__ = 'Version 1.0' + + +class PathComboBox(QComboBox): + + def __init__(self, *args, is_item=False, **kwargs): + super(PathComboBox, self).__init__(*args, **kwargs) + self.is_item = is_item + if not self.is_item: + self.setEditable(True) + layout = QHBoxLayout(self) + layout.setSpacing(0) + layout.setContentsMargins(0, 0, 0, 23) + layout.addItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)) + else: + self.f_model = QFileSystemModel(self) + self.f_model.setRootPath('') + self.setModel(self.f_model) + + def addWidget(self, widget): + self.layout().insertWidget(self.layout().count()-1, widget) + + +if __name__ == '__main__': + import sys + from PyQt5.QtWidgets import QApplication + + app = QApplication(sys.argv) + w = PathComboBox() + w.show() + w.addWidget(PathComboBox(w, is_item=True)) + sys.exit(app.exec_())