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/QListView/ImageView.py b/QListView/ImageView.py new file mode 100644 index 0000000..2f3aa01 --- /dev/null +++ b/QListView/ImageView.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Created on 2021/4/15 +@author: Irony +@site: https://github.com/PyQt5 +@email: 892768447@qq.com +@file: ImageView +@description: +""" +import os + +from PyQt5.QtCore import QPointF, Qt, QRectF, QSizeF +from PyQt5.QtGui import QStandardItem, QStandardItemModel, QPainter, QColor, QImage, QPixmap +from PyQt5.QtWidgets import QListView, QGraphicsView, QGraphicsPixmapItem, QGraphicsScene + +ScrollPixel = 40 + + +class BigImageView(QGraphicsView): + """图片查看控件""" + + def __init__(self, *args, **kwargs): + image = kwargs.pop('image', None) + background = kwargs.pop('background', None) + super(BigImageView, self).__init__(*args, **kwargs) + self.setCursor(Qt.OpenHandCursor) + self.setBackground(background) + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.setRenderHints(QPainter.Antialiasing | QPainter.HighQualityAntialiasing | + QPainter.SmoothPixmapTransform) + self.setCacheMode(self.CacheBackground) + self.setViewportUpdateMode(self.SmartViewportUpdate) + self._item = QGraphicsPixmapItem() # 放置图像 + self._item.setFlags(QGraphicsPixmapItem.ItemIsFocusable | + QGraphicsPixmapItem.ItemIsMovable) + self._scene = QGraphicsScene(self) # 场景 + self.setScene(self._scene) + self._scene.addItem(self._item) + rect = QApplication.instance().desktop().availableGeometry() + self.resize(int(rect.width() * 2 / 3), int(rect.height() * 2 / 3)) + + self.pixmap = None + self._delta = 0.1 # 缩放 + self.setPixmap(image) + + def setBackground(self, color): + """设置背景颜色 + :param color: 背景颜色 + :type color: QColor or str or GlobalColor + """ + if isinstance(color, QColor): + self.setBackgroundBrush(color) + elif isinstance(color, (str, Qt.GlobalColor)): + color = QColor(color) + if color.isValid(): + self.setBackgroundBrush(color) + + def setPixmap(self, pixmap, fitIn=True): + """加载图片 + :param pixmap: 图片或者图片路径 + :param fitIn: 是否适应 + :type pixmap: QPixmap or QImage or str + :type fitIn: bool + """ + if isinstance(pixmap, QPixmap): + self.pixmap = pixmap + elif isinstance(pixmap, QImage): + self.pixmap = QPixmap.fromImage(pixmap) + elif isinstance(pixmap, str) and os.path.isfile(pixmap): + self.pixmap = QPixmap(pixmap) + else: + return + self._item.setPixmap(self.pixmap) + self._item.update() + self.setSceneDims() + if fitIn: + self.fitInView(QRectF(self._item.pos(), QSizeF( + self.pixmap.size())), Qt.KeepAspectRatio) + self.update() + + def setSceneDims(self): + if not self.pixmap: + return + self.setSceneRect(QRectF(QPointF(0, 0), QPointF(self.pixmap.width(), self.pixmap.height()))) + + def fitInView(self, rect, flags=Qt.IgnoreAspectRatio): + """剧中适应 + :param rect: 矩形范围 + :param flags: + :return: + """ + if not self.scene() or rect.isNull(): + return + unity = self.transform().mapRect(QRectF(0, 0, 1, 1)) + self.scale(1 / unity.width(), 1 / unity.height()) + viewRect = self.viewport().rect() + sceneRect = self.transform().mapRect(rect) + x_ratio = viewRect.width() / sceneRect.width() + y_ratio = viewRect.height() / sceneRect.height() + if flags == Qt.KeepAspectRatio: + x_ratio = y_ratio = min(x_ratio, y_ratio) + elif flags == Qt.KeepAspectRatioByExpanding: + x_ratio = y_ratio = max(x_ratio, y_ratio) + self.scale(x_ratio, y_ratio) + self.centerOn(rect.center()) + + def wheelEvent(self, event): + if event.angleDelta().y() > 0: + self.zoomIn() + else: + self.zoomOut() + + def zoomIn(self): + """放大""" + self.zoom(1 + self._delta) + + def zoomOut(self): + """缩小""" + self.zoom(1 - self._delta) + + def zoom(self, factor): + """缩放 + :param factor: 缩放的比例因子 + """ + _factor = self.transform().scale( + factor, factor).mapRect(QRectF(0, 0, 1, 1)).width() + if _factor < 0.07 or _factor > 100: + # 防止过大过小 + return + self.scale(factor, factor) + + +class ImageView(QListView): + + def __init__(self, *args, **kwargs): + super(ImageView, self).__init__(*args, **kwargs) + self.setFrameShape(self.NoFrame) + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.setEditTriggers(self.NoEditTriggers) + self.setDropIndicatorShown(True) + self.setDragDropMode(self.DragDrop) + self.setDefaultDropAction(Qt.IgnoreAction) + self.setSelectionMode(self.ExtendedSelection) + self.setVerticalScrollMode(self.ScrollPerPixel) + self.setHorizontalScrollMode(self.ScrollPerPixel) + self.setFlow(self.LeftToRight) + self.setWrapping(True) + self.setResizeMode(self.Adjust) + self.setSpacing(6) + self.setViewMode(self.IconMode) + self.setWordWrap(True) + self.setSelectionRectVisible(True) + self.setContextMenuPolicy(Qt.CustomContextMenu) + # 解决拖动到顶部或者底部自动滚动 + self.setAutoScrollMargin(150) + self.verticalScrollBar().setSingleStep(ScrollPixel) + # 设置model + self.dmodel = QStandardItemModel(self) + self.setModel(self.dmodel) + + # 大图控件 + self.bigView = BigImageView(background='#323232') + + def addItem(self, image): + if isinstance(image, str): + image = QPixmap(image) + # 添加一个item + item = QStandardItem() + # 记录原始图片 + item.setData(image, Qt.UserRole + 1) # 用于双击的时候取出来 + # 缩放成小图并显示 + item.setData(image.scaled(60, 60, Qt.IgnoreAspectRatio, Qt.SmoothTransformation), Qt.DecorationRole) + # 添加item到界面中 + self.dmodel.appendRow(item) + + def count(self): + return self.dmodel.rowCount() + + def setCurrentRow(self, row): + self.setCurrentIndex(self.dmodel.index(row, 0)) + + def currentRow(self): + return self.currentIndex().row() + + def updateGeometries(self): + # 一次滑动20px + super(ImageView, self).updateGeometries() + self.verticalScrollBar().setSingleStep(ScrollPixel) + + def closeEvent(self, event): + # 关闭预览窗口 + self.bigView.close() + super(ImageView, self).closeEvent(event) + + def wheelEvent(self, event): + # 修复滑动bug + if self.flow() == QListView.LeftToRight: + bar = self.horizontalScrollBar() + value = ScrollPixel if event.angleDelta().y() < 0 else (0 - ScrollPixel) + bar.setSliderPosition(bar.value() + value) + else: + super(ImageView, self).wheelEvent(event) + + def mouseDoubleClickEvent(self, event): + # 列表双击,如果有item则进入item处理流程,否则调用打开图片功能 + index = self.indexAt(event.pos()) + if index and index.isValid(): + item = self.dmodel.itemFromIndex(index) + if item: + # 取出原图用来新窗口显示 + image = item.data(Qt.UserRole + 1) + self.bigView.setPixmap(image) + self.bigView.show() + return + super(ImageView, self).mouseDoubleClickEvent(event) + + +if __name__ == '__main__': + import sys + import cgitb + + cgitb.enable(format='text') + from PyQt5.QtWidgets import QApplication + + app = QApplication(sys.argv) + w = ImageView() + w.show() + + # 添加模拟图片 + for i in range(3): + for name in os.listdir('ScreenShot'): + w.addItem(os.path.join('ScreenShot', name)) + sys.exit(app.exec_()) diff --git a/QWebEngineView/GetRequestInfo.py b/QWebEngineView/GetRequestInfo.py new file mode 100644 index 0000000..91b5105 --- /dev/null +++ b/QWebEngineView/GetRequestInfo.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Created on 2019年9月24日 +@author: Irony +@site: https://pyqt5.com https://github.com/892768447 +@email: 892768447@qq.com +@file: QWebEngineView.BlockAds +@description: 拦截请求 +""" +from PyQt5.QtCore import QUrl +from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest +from PyQt5.QtWebEngineCore import QWebEngineUrlSchemeHandler, QWebEngineUrlScheme, \ + QWebEngineUrlRequestInterceptor +from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile + +__Author__ = 'Irony' +__Copyright__ = 'Copyright (c) 2019' +__Version__ = 'Version 1.0' + + +class UrlSchemeHandler(QWebEngineUrlSchemeHandler): + AttrType = QNetworkRequest.User + 1 + + def __init__(self, *args, **kwargs): + super(UrlSchemeHandler, self).__init__(*args, **kwargs) + self._manager = QNetworkAccessManager(self) + self._manager.finished.connect(self.onFinished) + + def requestStarted(self, request): + # 拦截 + # request.fail(QWebEngineUrlRequestJob.RequestDenied) + # print('initiator:', request.initiator()) + print('requestMethod:', request.requestMethod()) + print('requestHeaders:', request.requestHeaders()) + url = request.requestUrl() + if url.scheme().startswith('myurl'): + url.setScheme(url.scheme().replace('myurl', 'http')) + print('requestUrl:', url) + + # 构造真实请求 + req = QNetworkRequest(url) + req.setAttribute(self.AttrType, request) # 记录 + for headerName, headerValue in request.requestHeaders().items(): + req.setRawHeader(headerName, headerValue) + method = request.requestMethod() + + # TODO: 这里需要把浏览器内部的cookie获取出来重新设置 + if method == b'GET': + self._manager.get(req) + # TODO: 这里貌似没法得到POST的数据,ajax的请求貌似也有问题 + elif method == b'POST': + self._manager.post(req) + + def onFinished(self, reply): + req = reply.request() # 获取请求 + o_req = req.attribute(self.AttrType, None) + if o_req: + # Notice: 这里可以对数据做修改再返回 + # TODO: 可能还存在 QNetworkAccessManager 与浏览器之间的 cookie 同步问题 + o_req.reply(req.header(QNetworkRequest.ContentTypeHeader) or b'text/html', reply) + o_req.destroyed.connect(reply.deleteLater) + + +# 把所有请求重定向到myurl +class RequestInterceptor(QWebEngineUrlRequestInterceptor): + + def interceptRequest(self, info): + url = info.requestUrl() + if url.scheme() == 'http': + # 重定向 + url.setScheme('myurl') + info.redirect(url) + elif url.scheme() == 'https': + # 重定向 + url.setScheme('myurls') + info.redirect(url) + + +class Window(QWebEngineView): + + def __init__(self, *args, **kwargs): + super(Window, self).__init__(*args, **kwargs) + self.resize(800, 600) + profile = QWebEngineProfile.defaultProfile() + + # 首先获取默认的url协议 + o_http = QWebEngineUrlScheme.schemeByName(b'http') + o_https = QWebEngineUrlScheme.schemeByName(b'https') + print('scheme:', o_http, o_https) + + # 这里需要修改增加本地文件和跨域支持 + CorsEnabled = 0x80 # 5.14才增加 + o_http.setFlags( + o_http.flags() | QWebEngineUrlScheme.SecureScheme | QWebEngineUrlScheme.LocalScheme | QWebEngineUrlScheme.LocalAccessAllowed | CorsEnabled) + o_https.setFlags( + o_https.flags() | QWebEngineUrlScheme.SecureScheme | QWebEngineUrlScheme.LocalScheme | QWebEngineUrlScheme.LocalAccessAllowed | CorsEnabled) + + # 安装url拦截器和自定义url协议处理 + de = QWebEngineProfile.defaultProfile() # @UndefinedVariable + de.setRequestInterceptor(RequestInterceptor(self)) + self.urlSchemeHandler = UrlSchemeHandler(self) + de.installUrlSchemeHandler(b'myurl', self.urlSchemeHandler) # for http + de.installUrlSchemeHandler(b'myurls', self.urlSchemeHandler) # for https + + +if __name__ == '__main__': + import sys + import os + import webbrowser + import cgitb + + cgitb.enable(format='text') + from PyQt5.QtWidgets import QApplication + + app = QApplication(sys.argv) + # 开启F12 控制台功能,需要单独通过浏览器打开这个页面 + # 这里可以做个保护, 发布软件,启动时把这个环境变量删掉。防止他人通过环境变量开启 + os.environ['QTWEBENGINE_REMOTE_DEBUGGING'] = '9966' + # 打开调试页面 + webbrowser.open_new_tab('http://127.0.0.1:9966') + + w = Window() + w.show() + w.load(QUrl('https://pyqt.site')) + sys.exit(app.exec_()) 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..30e16ab 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # 各种各样的PyQt测试和例子 -[![Blog](https://img.shields.io/badge/blog-pyqt5-green.svg)](https://pyqt5.com) +[![Blog](https://img.shields.io/badge/blog-pyqt-green.svg)](https://pyqt.site) [![codebeat badge](https://codebeat.co/badges/d23d0dc8-aef3-43d2-96aa-e3215b2c9861)](https://codebeat.co/projects/github-com-pyqt5-pyqt-master) +[![Badge](https://img.shields.io/badge/link-996.icu-%23FF4D5B.svg?style=flat-square)](https://996.icu/#/zh_CN) +[![LICENSE](https://img.shields.io/badge/license-Anti%20996-blue.svg?style=flat-square)](https://github.com/996icu/996.ICU/blob/master/LICENSE) https://pyqt.site 论坛是专门针对PyQt5学习和提升开设的网站,分享大家平时学习中记录的笔记和例子,以及对遇到的问题进行收集整理。 @@ -253,6 +255,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_())