tcarson4344 | 2020-05-25 18:03:30 UTC | #1
I am working on a Qdialog with an embedded Matplotlib plot. I have a RMB context menu popup but I want it to show the menu title. I can set the title (line 69) but I am not sure how to make it display.
import sys
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtWidgets import QDialog, QApplication, QVBoxLayout, QLabel
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.pyplot as plt
from matplotlib.widgets import SpanSelector
import numpy as np
import random
class FBPlot(QDialog):
def __init__(self, parent=None):
super(FBPlot, self).__init__(parent)
# a figure instance to plot on
self.figure = plt.figure()
# this is the Canvas Widget that displays the `figure`
# it takes the `figure` instance as a parameter to __init__
self.canvas = FigureCanvas(self.figure)
ax = self.figure.add_subplot(111)
# Capture button press
self.canvas.mpl_connect('button_press_event', self.on_press)
self.canvas.mpl_connect('pick_event', self.pick)
# set the layout
layout = QVBoxLayout()
layout.addWidget(self.canvas)
self.setLayout(layout)
# self.ax.grid(True)
self.canvas.draw()
self.span = SpanSelector(ax,
self.spanzoom,
'horizontal',
useblit=True,
rectprops=dict(alpha=0.5, facecolor='red'),
button=1)
def on_press(self, event):
print(event)
if event.button == 3:
canvasSize = event.canvas.geometry()
Qpoint_click = event.canvas.mapToGlobal(
QtCore.QPoint(event.x, canvasSize.height()-event.y))
if event.inaxes != None:
self.fbMenu = QtWidgets.QMenu()
self.fbMenu.addSection("Front/Back")
self.fbMenu.addSeparator()
self.fbMenu.addAction("Select All Curves")
self.fbMenu.addAction("Remove All Curves")
self.fbMenu.addSeparator()
self.fbMenu.addAction("Legend")
self.fbMenu.addAction("Cursor Legend")
self.fbMenu.move(Qpoint_click)
self.fbMenu.show()
else:
# Y Axis Front Area
self.ylim_menu = QtWidgets.QMenu('&Limits')
self.ylim_menu.addAction("Fixed")
self.ylim_menu.addAction("Free")
self.ylim_menu.addAction("Optimized")
# y axis main menu
self.yMenu = QtWidgets.QMenu('&Y Axis')
self.yMenu.setTitle('sdfj')
self.yMenu.addMenu(self.ylim_menu)
self.yMenu.addSeparator()
self.yMenu.addAction("Visable")
self.yMenu.addAction("Options...")
self.yMenu.move(Qpoint_click)
self.yMenu.show()
# Y Axis Back Area
print(self.yMenu.title())
# X Axis Area
# Title Area
else:
pass
def pick(self, event):
print(event)
def spanzoom(self, xmin, xmax):
# indmin, indmax = np.searchsorted(x, (xmin, xmax))
# indmax = min(len(x) - 1, indmax)
# thisx = x[indmin:indmax]
# thisy = y[indmin:indmax]
# fb1.set_data(thisx, thisy)
self.ax.set_xlim(xmin, xmax)
#ax1.set_ylim(thisy.min(), thisy.max())
self.canvas.draw()
# def plot(self, xdata, ydata):
# self.ax.plot(xdata, ydata)
if __name__ == '__main__':
app = QApplication(sys.argv)
main = FBPlot()
# generate data
# np.random.seed(19680801)
# x = np.arange(0.0, 5.0, 0.01)
# y = np.sin(2*np.pi*x) + 0.5*np.random.randn(len(x))
# fb1, = main.plot(x, y, Label='data')
main.show()
sys.exit(app.exec_())
Luca | 2020-05-25 14:27:41 UTC | #2
I think the title that you are setting is the one that would be visible if you add that QMenu to a QMenuBar, I'm not sure that internally, it is implemented to be shown as you would.
martin | 2020-05-25 18:29:23 UTC | #3
Yep, unfortunately as @Luca says the menu title is used when displaying the menu on the menubar -- i.e. it's the File, Edit, etc. title.
The menu does provide a menu.addSection(<str>)
method which might be what you're looking for? But on macOS/Windows themes it is no different to a separator. The only way I can find to actually show it is using the Qt Fusion theme, e.g.
PyQt6 Crash Course — a new tutorial in your Inbox every day
Beginner-focused crash course explaining the basics with hands-on examples.
app = QApplication(sys.argv)
app.setStyle("Fusion")
Then using the following
self.fbMenu.addSection("A menu part")
...will give the following results in the menu...
The Fusion theme is a cross-platform toolkit, so your app will look the same on all platforms. But it won't look "native" on any.
One other option you have is to add a null QAction
and disable it, e.g.
a = self.fbMenu.addAction("A menu part")
a.setDisabled(True)
This will show up how I suspect you're expecting it to...
...but that's potentially confusing for users if it looks otherwise like a disabled entry. I've not found a way to reliably theme individual menu items (e.g. so you could highlight this dummy entry in a different colour).
tcarson4344 | 2020-05-25 18:46:29 UTC | #4
thanks,
I ended up adding a label Widget. took more lines of code that I wanted but looks ok I think I need to make it bold and/or color background to change the appearance.
It looks like this:
My Code (in case someone wants it):
import sys
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtWidgets import QDialog, QApplication, QVBoxLayout, QLabel, QWidgetAction, QSizePolicy
from PyQt5.QtGui import QFont
from PyQt5.QtCore import Qt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.pyplot as plt
from matplotlib.widgets import SpanSelector
import numpy as np
import random
class FBPlot(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
# a figure instance to plot on
self.figure = plt.figure()
# this is the Canvas Widget that displays the `figure`
# it takes the `figure` instance as a parameter to __init__
self.canvas = FigureCanvas(self.figure)
ax = self.figure.add_subplot(111)
# Capture button press
self.canvas.mpl_connect('button_press_event', self.on_press)
self.canvas.mpl_connect('pick_event', self.pick)
# set the layout
layout = QVBoxLayout()
layout.addWidget(self.canvas)
self.setLayout(layout)
# self.ax.grid(True)
self.canvas.draw()
self.span = SpanSelector(ax,
self.spanzoom,
'horizontal',
useblit=True,
rectprops=dict(alpha=0.5, facecolor='red'),
button=1)
def on_press(self, event):
print(event)
if event.button == 3:
canvasSize = event.canvas.geometry()
Qpoint_click = event.canvas.mapToGlobal(
QtCore.QPoint(event.x, canvasSize.height()-event.y))
if event.inaxes != None:
self.fbMenu = QtWidgets.QMenu()
#Add a title to context menu
fbtl = QLabel('Front/Back')
fbtl.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
fbtl.setAlignment(Qt.AlignCenter)
fbtlAction = QWidgetAction(fbtl)
fbtlAction.setDefaultWidget(fbtl)
self.fbMenu.addAction(fbtlAction)
# Add rest of menu items
self.fbMenu.addSeparator()
self.fbMenu.addAction("Select All Curves")
self.fbMenu.addAction("Remove All Curves")
self.fbMenu.addSeparator()
self.fbMenu.addAction("Legend")
self.fbMenu.addAction("Cursor Legend")
self.fbMenu.move(Qpoint_click)
self.fbMenu.show()
else:
# Y Axis Front Area
self.ylim_menu = QtWidgets.QMenu('&Limits')
self.ylim_menu.addAction("Fixed")
self.ylim_menu.addAction("Free")
self.ylim_menu.addAction("Optimized")
# y axis main menu
self.yMenu = QtWidgets.QMenu('&Y Axis')
self.yMenu.setTitle('sdfj')
self.yMenu.addMenu(self.ylim_menu)
self.yMenu.addSeparator()
self.yMenu.addAction("Visable")
self.yMenu.addAction("Options...")
self.yMenu.move(Qpoint_click)
self.yMenu.show()
# Y Axis Back Area
print(self.yMenu.title())
# X Axis Area
# Title Area
else:
pass
def pick(self, event):
print(event)
def spanzoom(self, xmin, xmax):
# indmin, indmax = np.searchsorted(x, (xmin, xmax))
# indmax = min(len(x) - 1, indmax)
# thisx = x[indmin:indmax]
# thisy = y[indmin:indmax]
# fb1.set_data(thisx, thisy)
self.ax.set_xlim(xmin, xmax)
#ax1.set_ylim(thisy.min(), thisy.max())
self.canvas.draw()
# def plot(self, xdata, ydata):
# self.ax.plot(xdata, ydata)
if __name__ == '__main__':
app = QApplication(sys.argv)
main = FBPlot()
# generate data
# np.random.seed(19680801)
# x = np.arange(0.0, 5.0, 0.01)
# y = np.sin(2*np.pi*x) + 0.5*np.random.randn(len(x))
# fb1, = main.plot(x, y, Label='data')
main.show()
sys.exit(app.exec_())
martin | 2020-05-25 18:46:25 UTC | #5
Nice solution @tcarson4344 hadn't occurred to me to use a widget for this. Thanks for posting! :+1:
cippall | 2021-08-04 07:37:05 UTC | #6
Thanks for posting your solution. Really helped.
Just a note that the QWidgetAction needs as a parent the menu not the label itself. To avoid my app to crash i had to modify these lines:
fbtlAction = QWidgetAction(self.fbMenu)
Create GUI Applications with Python & Qt5 by Martin Fitzpatrick — (PyQt5 Edition) The hands-on guide to making apps with Python — Over 10,000 copies sold!