QPainter与Graphics View 架构的区别

PyQt5 提供了两种绘图方法。一种是使用 QPainter 类在 QWidget 类提供的画布上画图,可以 绘制点、线、圆等各种基本形状,从而组成自己需要的图形。所有界面组件都是 QWidget 的子类, 界面上的按钮、编辑框等各种组件的界面效果都是使用 QPainter 绘制出来的。用户从 QWidget 继 承定义自己的界面组件时,要绘制组件的界面效果也是使用这种方法。QPainter 绘制的图形如同 位图,是不可交互操作的图形。 PyQt5 另外提供一种基于 Graphics View 架构的绘图方法,这种方法使用 QGraphicsView、 QGraphicsScene 和各种 QGraphicsItem 图形项绘图,在一个场景中可以绘制大量图件,且每个图件 是可选择、可交互的,如同矢量图编辑软件那样可以操作每个图件。Graphics View 架构为用户绘制 复杂的组件化图形提供了便利。

关于QPainter跟Graphics View的基础知识请参考如下连接:

https://blog.csdn.net/zhengyanan815/article/details/127119421

https://blog.csdn.net/qq_40732350/article/details/90116319

需求说明

在主窗口中,存在一个按钮(或者label),点击该按钮,会弹出一个圆形菜单,如下图红框所示,我点击主窗口中的工具箱按钮,会弹出一个按钮菜单,跟悬浮桌面球的效果类似,虽然最终实现了,但是还存在两个小bug,这个后面会提到。当然使用QPainter也可以实现,这里主要是为了学习QGraphicsView

实现思路

  1. 新建一个主窗口,里面放置一个自定义的label(不想自定义也可以直接使用按钮,不过按钮也要重写,因为涉及到鼠标事件)

  1. 自定义一个QWidget继承自QGraphicsView,主要用来加载弹出菜单按钮,该自定义widget包含弹出动作,每次弹出时,会获取工具集按钮的全局坐标(相对于整个屏幕的),然后设置该widget的中心为工具集的中心坐标,最后做弹出动作,弹出菜单使用的是并行动画类:QParallelAnimationGroup()

  1. 当鼠标离开自定义widget区域时,菜单收起来,这里没有做收起动作,感兴趣的可以自己做一下,跟展开动作相反即可。

说明一下,为什么不做鼠标悬浮进入工具集区域,就弹出按钮,因为QGraphicsView设置为透明属性后,存在鼠标穿透问题,弹出菜单将会反复切换。

存在的问题:

存在两个问题如下:

  1. QGraphicsView想要设置为背景透明,组件不透明,就必须设置透明背景区域不响应鼠标事件,这时,如果鼠标从透明区域出去,菜单将不会收起,如下图所示。

#设置为无边框模式,背景透明,组件不透明
self.view.setStyleSheet("background: transparent;border:0px")
self.view.setWindowFlags(Qt.FramelessWindowHint)
self.view.setAttribute(Qt.WA_TranslucentBackground) # 透明区域不响应鼠标事件

2. QGraphicsView组件中加载的按钮,样式设置为原型边框时,四角的白色区域仍然显示,不知道如何去除,正在的QWidget就不存在这个问题,大家看上面第一张图,那个大的原型按钮,就没有这个问题。

真对上面的两个问题,看到此篇文章的大佬,如果有解决办法,还请留言,感激不尽。

代码部分:

    • 主窗口QDsigner设计的代码

mainWin.py

# -*- coding: utf-8 -*-# Form implementation generated from reading ui file 'mainWin.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.from PyQt5 import QtCore, QtGui, QtWidgetsclass Ui_MainWindow(object):def setupUi(self, MainWindow):MainWindow.setObjectName("MainWindow")MainWindow.resize(505, 346)self.centralwidget = QtWidgets.QWidget(MainWindow)self.centralwidget.setObjectName("centralwidget")self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)self.verticalLayout.setObjectName("verticalLayout")spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)self.verticalLayout.addItem(spacerItem)self.horizontalLayout_2 = QtWidgets.QHBoxLayout()self.horizontalLayout_2.setObjectName("horizontalLayout_2")spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)self.horizontalLayout_2.addItem(spacerItem1)self.pushButton = QtWidgets.QPushButton(self.centralwidget)self.pushButton.setMinimumSize(QtCore.QSize(100, 100))self.pushButton.setStyleSheet("border-color: rgb(255, 255, 255);\n"
"font: 15pt \"MS Shell Dlg 2\";\n"
"background-color: rgb(255, 255, 255);")icon = QtGui.QIcon()icon.addPixmap(QtGui.QPixmap("tool.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)self.pushButton.setIcon(icon)self.pushButton.setIconSize(QtCore.QSize(64, 64))self.pushButton.setObjectName("pushButton")self.horizontalLayout_2.addWidget(self.pushButton)spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)self.horizontalLayout_2.addItem(spacerItem2)self.verticalLayout.addLayout(self.horizontalLayout_2)spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)self.verticalLayout.addItem(spacerItem3)self.horizontalLayout = QtWidgets.QHBoxLayout()self.horizontalLayout.setObjectName("horizontalLayout")self.label = MyQLabel(self.centralwidget)self.label.setText("")self.label.setAlignment(QtCore.Qt.AlignCenter)self.label.setObjectName("label")self.horizontalLayout.addWidget(self.label)self.verticalLayout.addLayout(self.horizontalLayout)spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)self.verticalLayout.addItem(spacerItem4)MainWindow.setCentralWidget(self.centralwidget)self.statusbar = QtWidgets.QStatusBar(MainWindow)self.statusbar.setObjectName("statusbar")MainWindow.setStatusBar(self.statusbar)self.retranslateUi(MainWindow)QtCore.QMetaObject.connectSlotsByName(MainWindow)def retranslateUi(self, MainWindow):_translate = QtCore.QCoreApplication.translateMainWindow.setWindowTitle(_translate("MainWindow", "GraphicsView Demo"))
from myLabel import MyQLabel

2. 自定义label组件

myLabel.py

from PyQt5.QtWidgets import QLabel
from PyQt5.QtCore import pyqtSignal, QPoint, Qtclass MyQLabel(QLabel):enterSignal = pyqtSignal(tuple)def __init__(self, parent=None):super(MyQLabel, self).__init__(parent)# 鼠标左键点击触发def mousePressEvent(self, event):if (event.button() == Qt.LeftButton):# label在屏幕的绝对位置global_coordinates = self.mapToGlobal(QPoint(0, 0)).x(), self.mapToGlobal(QPoint(0, 0)).y()# 获取label组件的中心绝对坐标(全屏幕范围内)label_central = global_coordinates[0] + self.width() // 2, global_coordinates[1] + self.height() // 2# print(label_central)self.enterSignal.emit(label_central)# 鼠标离开labeldef leaveEvent(self, event):# self.setAttribute(Qt.WA_TransparentForMouseEvents, True)pass

3. 主程序入口以及自定义QGraphicsView

这两个写到了一起:runMainWin.py

import sys
import math
from PyQt5.QtCore import Qt, QPropertyAnimation, QParallelAnimationGroup, QPointF, QEasingCurve, QSize
from PyQt5.QtGui import QGuiApplication, QPixmap, QIcon
from PyQt5.QtWidgets import QMainWindow, QApplication, QGraphicsView, QGraphicsScene, QPushButtonfrom mainWin import Ui_MainWindow# 自定义GraphicsView的子类,并实现按钮的添加
class MyGraphics(QGraphicsView):def __init__(self, parent=None):super(MyGraphics, self).__init__(parent)self.__initView()self.resize(166, 156)def __initView(self):self.scene = QGraphicsScene()self.scene.setSceneRect(0, 0, 150, 150)self.ref_x0 = self.scene.width() / 2    # 场景的中心x坐标self.ref_y0 = self.scene.height() / 2   # 场景的中新y坐标self.btnProxyList = []btn1 = QPushButton(QIcon(QPixmap("openConfigFolder.png")), None)btn2 = QPushButton(QIcon(QPixmap("newFile.png")), None)btn2.setToolTip('New case file')btn_size = 40btn1.resize(btn_size, btn_size)btn2.resize(btn_size, btn_size)btn1_proxy = self.scene.addWidget(btn1)btn1_proxy.setPos(self.ref_x0 - btn1_proxy.rect().width()/2, self.ref_y0 - btn1_proxy.rect().height()/2)self.btnProxyList.append(btn1_proxy)btn2_proxy = self.scene.addWidget(btn2)btn2_proxy.setPos(self.ref_x0 - btn2_proxy.rect().width()/2, self.ref_y0 - btn2_proxy.rect().height()/2)self.btnProxyList.append(btn2_proxy)btn3 = QPushButton(QIcon("uploadWeight.png"), None)btn4 = QPushButton(QIcon("coffee.png"), None)btn3.resize(btn_size, btn_size)btn4.resize(btn_size, btn_size)btn3_proxy = self.scene.addWidget(btn3)btn3_proxy.setPos(self.ref_x0 - btn3_proxy.rect().width() / 2, self.ref_y0 - btn3_proxy.rect().height() / 2)self.btnProxyList.append(btn3_proxy)btn4_proxy = self.scene.addWidget(btn4)btn4_proxy.setPos(self.ref_x0 - btn4_proxy.rect().width() / 2, self.ref_y0 - btn4_proxy.rect().height() / 2)self.btnProxyList.append(btn4_proxy)btn5 = QPushButton(QIcon("punch.png"), None)btn6 = QPushButton(QIcon("role.png"), None)btn5.resize(btn_size, btn_size)btn6.resize(btn_size, btn_size)btn5_proxy = self.scene.addWidget(btn5)btn5_proxy.setPos(self.ref_x0 - btn5_proxy.rect().width() / 2, self.ref_y0 - btn5_proxy.rect().height() / 2)self.btnProxyList.append(btn5_proxy)btn6_proxy = self.scene.addWidget(btn6)btn6_proxy.setPos(self.ref_x0 - btn6_proxy.rect().width() / 2, self.ref_y0 - btn6_proxy.rect().height() / 2)self.btnProxyList.append(btn6_proxy)for x in range(6):eval(f"btn{x+1}").setIconSize(QSize(30,30))eval(f"btn{x+1}").setStyleSheet("background: #ffffff;border:2px solid #001721;border-radius: 20px;")self.setScene(self.scene)# 鼠标离开widget时执行的动作def leaveEvent(self, event):if self.isVisible():self.hide()pass# 按钮动画,每次弹出时,都要调用一次def btnAnimate(self):R = 60     # 动画时,圆周的半径item_number = len(self.items())angle = [x*round(float(360/item_number), 2) for x in range(item_number)]animateTime = 200       # 动画持续时间self.group = QParallelAnimationGroup(self)num = 0for item in self.btnProxyList:animation = QPropertyAnimation(item, b'pos')animation.setStartValue(QPointF(self.ref_x0 - item.rect().width() / 2,self.ref_y0 - item.rect().height() / 2))animation.setEndValue(QPointF(self.ref_x0 + R * math.cos(angle[num] * math.pi / 180) - item.rect().width() / 2,self.ref_y0 - R * math.sin(angle[num] * math.pi / 180) - item.rect().height() / 2))animation.setDuration(animateTime)animation.setEasingCurve(QEasingCurve.InCubic)self.group.addAnimation(animation)num += 1self.group.start()self.update()class MainWin(QMainWindow, Ui_MainWindow):def __init__(self):super(MainWin, self).__init__()self.setupUi(self)self.resize(730, 500)self.__initUI()self.handle()def __initUI(self):self.label.setFixedHeight(30)self.label.setFixedWidth(30)pix = QPixmap("tool.png")pix.scaled(self.label.size(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)        # 让图片填充满QLabel# pix.scaled(self.label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)self.label.setPixmap(pix)self.label.setScaledContents(True)self.pushButton.setStyleSheet("background: #ffffff;border:3px solid #FF0000;border-radius:50px;")self.view = MyGraphics()def handle(self):self.label.enterSignal.connect(self.showPopMenu)def showPopMenu(self, labelPos):# print(self.view.style().pixelMetric(QStyle.PM_TitleBarHeight))      # 获取标题栏高度self.view.setStyleSheet("background: transparent;border:0px")self.view.setWindowFlags(Qt.FramelessWindowHint)self.view.setAttribute(Qt.WA_TranslucentBackground)self.view.move(int(labelPos[0] - self.view.width() / 2), int(labelPos[1] - self.view.height()/2))self.view.show()self.view.btnAnimate()self.view.setMouseTracking(True)if __name__ == '__main__':QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)app = QApplication(sys.argv)main_win = MainWin()main_win.show()sys.exit(app.exec_())

资源链接

使用的资源以及代码打包放在一个文件夹,想学习的直接下载即可使用,CSDN有自动调整积分机制,如果不能下载,请联系我,我单独发给你们。

https://download.csdn.net/download/weixin_43054437/87361094

pyqt5中QGraphicsView弹出菜单相关推荐

  1. 用Dreamware制作弹出菜单

    弹出菜单在网页制作中是一种非常常用的菜单方式,这种菜单不但制作简单,而且效果也很好. 效果说明 本实例所要完成的效果是当鼠标移动到文字上时,就会弹出一个漂亮的菜单,如下图所示. 弹出菜单在网页制作中是 ...

  2. python tkinter 下拉框_python中tkinter入门之Menu创建顶级菜单、下拉菜单和弹出菜单。...

    创建顶级菜单 Menu 组件通常被用于实现应用程序上的各种菜单.使用add_command可以添加菜单内容.创建后通过root.config()添加到窗口上. from tkinter import ...

  3. python getmenu不到菜单句柄,从弹出菜单win32 api Python中选择项目

    我在 Windows 7 64位机器上使用python 2.7(32位).我正在使用win32 Api自动执行一些Windows任务,我也是python和win32 api的新手.我看到了类似的问题, ...

  4. java鼠标右击出现选择窗口_java菜单代码 java中鼠标右击弹出菜单怎样实现

    帮忙给一个java菜单栏例子的源代码 给你个小例子,已经添加注释了.自己运行下看看效果,满意的话记得结贴子. import java.awt.BorderLayout; import java.awt ...

  5. android 弹出网格菜单,在android中的recyclerView中显示弹出按钮的确...

    as per my above comment可以使用Popup Menu Android Popup Menu displays the menu below the anchor text if ...

  6. 怎样在Delphi中屏蔽Flash控件的右键弹出菜单

    关于如何屏蔽Flash控件的右键菜单的问题有很多人问过,也有很多人回答,基本上都是说拦截 Application消息,或者继承一个新的控件,重载MouseDown消息,第一种解决办法,好象是简单 些, ...

  7. 机器性能这么好,为什么点击右键弹出菜单还这么慢?

    为什么80%的码农都做不了架构师?>>>    我想很多人估计遇到过这样的问题,一开始估计会怀疑是系统问题或者是有什么硬件瓶颈. 其实不然, 在与一位同事交流后发现,其实问题的原因很 ...

  8. 教你如何创建类似QQ的android弹出菜单

    热心推荐: Android  ListView两种长按弹出菜单方式 popupwindow实现弹出菜单效果 Android人才招聘--最新招聘 大家可能看到 android 的自带的系统菜单比较难看, ...

  9. DirectUI界面编程(六)实现右键弹出菜单

    本节向大家介绍一下右键弹出菜单是如何实现的.效果如下,在窗口中点击鼠标右键弹出菜单,点击菜单项能够响应菜单点击事件. 使用Duilib库实现的弹出菜单,实际上也是一个Windows窗口,因此我们需要创 ...

最新文章

  1. java基础面试题:抽象类中是否可以有静态的main方法?
  2. python 读取txt
  3. Embedding技术在房产推荐中的应用
  4. go语言快速刷《程序员面试金典》(3)
  5. python两列相乘_如何将pandas中具有不同索引的两列相乘?
  6. CC版本添加 LUCI
  7. 雪城大学信息安全讲义 3.2 Set-UID 程序的漏洞
  8. Codeforces.888G.Xor-MST(Borůvka算法求MST 贪心 Trie)
  9. 关于SQL Server自动备份无法删除过期的备份文件奇怪现象
  10. 递归系列——数组和对象的相关递归
  11. UDS/OBD DTC(诊断故障码)格式解析
  12. 关闭445端口等危险端口,应对勒索病毒
  13. 如何下载可爱随意字体KISS ME
  14. 详解linux下的串口通讯开发
  15. 关于spark-evn.sh的配置(里面有worker的设置)
  16. excel表格横向纵向变换_Excel 的初级逻辑
  17. bios密码解锁软件_你知道自己的电脑还有这么一个BIOS程序吗
  18. CSDN如何搜索自己的博客;使用Google搜索自己的博客
  19. 解决png图片在IE6下的透明问题
  20. 阿博茨科技:不仅是工具,基于用户体验的CRM再选择

热门文章

  1. 圆、椭圆和三角形的代码画法
  2. 《15天玩转WPF》—— 直线、矩形、椭圆的各种画法
  3. 【Nordic蓝牙模块justwork模式配置】
  4. css画钟表_利用css+原生js制作简单的钟表
  5. hdu 4745 区间dp
  6. 开源的大文件上传组件NeatUpload™
  7. 【Tableau 图表大全21】之箱型图(盒须图)
  8. MiniUI Api 方法
  9. 有哪些公司为外贸独立站提供收款服务?
  10. TeamTalk源码分析(二) —— 服务器端的程序的编译与部署