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.
Packaging Python Applications with PyInstaller by Martin Fitzpatrick — This step-by-step guide walks you through packaging your own Python applications from simple examples to complete installers and signed executables.
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
PyQt/PySide 1:1 Coaching with Martin Fitzpatrick — Save yourself time and frustration. Get one on one help with your Python GUI projects. Working together with you I'll identify issues and suggest fixes, from bugs and usability to architecture and maintainability.
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:
Purchasing Power Parity
Developers in [[ country ]] get [[ discount.discount_pc ]]% OFF on all books & courses with code [[ discount.coupon_code ]] fbtlAction = QWidgetAction(self.fbMenu)