【发布时间】:2021-02-16 09:09:32
【问题描述】:
基本上,每次选中复选框时,我都会尝试用一组新图标重绘我的NavigationToolbar。我将NavigationToolbar2QT 子类化为Toolbar_route,它在应用启动时运行良好,但更新无法正常运行。
编辑:
我还是想不通,所以我做了一个小应用程序来说明它,这是我的结构:
????myApp
┣ ????resources
┃ ┣ ????images_dark
┃ ┃ ┗ ????matplotlib-dark-images
┃ ┃ ┃ ┣ ????<...>.png
┃ ┗ ????GUI_preferences.ini
┣ ????custom_mpl_toolbar.py
┣ ????myApp_GUI_pref.py
┣ ????myApp_main.py
┗ ????save_restore.py
我猜问题是一样的,我没有正确摆脱 window_plot_update 中的旧画布和工具栏。这是myApp_main.py:
import os
import random
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.pyplot import rcParams, style
from PyQt5 import QtCore
from PyQt5 import QtWidgets as qtw
from PyQt5.QtWidgets import QMainWindow, QSizePolicy, QWidget
# cwd modules
from custom_mpl_toolbar import MyMplToolbar
from myApp_GUI_pref import Ui_Form
from save_restore import guirestore, guisave
class NewWindow(QMainWindow):
"""MainWindow factory"""
def __init__(self):
super().__init__()
global window_list
window_list = [] # Allow multiple MainWindow instances
# ? Preferences on program start
BASEDIR = os.path.dirname(__file__)
GUI_preferences_path = os.path.join(BASEDIR, 'resources/GUI_preferences.ini')
self.GUI_preferences = QtCore.QSettings(GUI_preferences_path, QtCore.QSettings.IniFormat)
self.add_new_window()
def add_new_window(self):
"""Create now MainWindow instance"""
window = MainWindow(self, self.GUI_preferences)
window_list.append(window)
window.show()
class MainWindow(QMainWindow):
def __init__(self, window, GUI):
super().__init__()
self.window = window
self.GUI_preferences = GUI
self.instances_route_canvas = []
self.instances_route_toolbar = []
self.centralwidget = qtw.QWidget()
self.centralwidget.setMaximumSize(QtCore.QSize(833, 559))
self.centralwidget.setLayoutDirection(QtCore.Qt.LeftToRight)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout = qtw.QVBoxLayout(self.centralwidget)
self.setCentralWidget(self.centralwidget)
self.menubar = qtw.QMenuBar()
self.menubar.setGeometry(QtCore.QRect(0, 0, 819, 21))
self.menubar.setObjectName("menubar")
self.menuFile = qtw.QMenu(self.menubar)
self.menuFile.setObjectName("File")
self.setMenuBar(self.menubar)
menu_new = self.menubar.addMenu("File")
self.actionNew_Window = qtw.QAction()
menu_new.addAction('New window', self.window.add_new_window)
self.actionEdit = qtw.QAction()
menu_new.addAction('Preferences', self.show_preferences)
self.mainEdits()
def mainEdits(self):
"""MainWindow GUI changes"""
# ? window exit
qtw.QAction("Quit", self).triggered.connect(self.closeEvent)
#? Instantiate and restore theme preferences
self.pref_screen = Preferences(self.GUI_preferences)
#? Embedded matplotlib in Route tab
self.route_canvas = PlotCanvas_route(self.GUI_preferences)
darkMode = bool(int(self.GUI_preferences.value('cb_dark')))
self.route_toolbar = Toolbar_route(
self.route_canvas, None, coordinates=True, darkMode=darkMode
) # Do not set parent on this first widget - Prevents toolbar theme update
self.verticalLayout.addWidget(self.route_canvas)
self.verticalLayout.addWidget(self.route_toolbar)
# ? Keep track of NavToolbar and Canvas
self.instances_route_canvas.append(self.route_canvas)
self.instances_route_toolbar.append(self.route_toolbar)
def show_preferences(self):
"""Shows the Preferences widget"""
guirestore(self.pref_screen, self.GUI_preferences)
self.pref_screen.setWindowModality(QtCore.Qt.ApplicationModal)
self.pref_screen.setFocus(True)
self.pref_screen.show()
def closeEvent(self, event):
"""Close current window"""
close = qtw.QMessageBox(qtw.QMessageBox.Question, 'Exit', 'Exit application?', parent=self)
close_reject = close.addButton('No', qtw.QMessageBox.NoRole)
close_accept = close.addButton('Yes', qtw.QMessageBox.AcceptRole)
close.exec() # Necessary for property-based API
if close.clickedButton() == close_accept:
event.accept()
else:
event.ignore()
class Toolbar_route(MyMplToolbar):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class PlotCanvas_route(FigureCanvas):
"""Route input data graph"""
def __init__(self, GUI, dpi=100):
# TODO param font lost on new window and updates
self.GUI_preferences = GUI
self.route_fig = Figure(dpi=dpi)
FigureCanvas.__init__(self, self.route_fig)
FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
self.plot()
def plot(self):
"""Clear and plot chart"""
self.route_fig.clear() # clear wrong format on graph init
self.dark_mode_set = bool(int(self.GUI_preferences.value('cb_dark')))
if self.dark_mode_set:
self.route_fig.patch.set_facecolor(
(0.09803921569, 0.13725490196, 0.17647058824)
) # light grey
rcParams['axes.facecolor'] = (0.19607843137, 0.25490196078, 0.29411764706) # dark grey
rcParams['text.color'] = rcParams['axes.labelcolor'] = rcParams[
'axes.edgecolor'] = rcParams['xtick.color'] = rcParams['ytick.color'] = 'white'
rcParams['savefig.facecolor'] = (0.19607843137, 0.25490196078, 0.29411764706)
else:
style.use('default')
self.route_fig.patch.set_facecolor('white')
rcParams['savefig.dpi'] = 300
rcParams['font.family'] = 'Euclid'
data = [random.random() for i in range(50)]
self.ax = self.figure.add_subplot(111)
self.line0, = self.ax.plot(data, 'r-', linewidth=0.5)
self.ax.set_title('Very nice graph')
# Line color has to be set during/after axis plot
if self.dark_mode_set: self.line0.set_color("white")
try:
watermarkText = Preferences.watermarkText
except:
watermarkText = str((self.GUI_preferences.value('watermark')))
self.route_fig.text(
0.5, 0.5, watermarkText, fontsize=20, color='gray', ha='center', va='center', alpha=0.5
)
self.draw()
class Preferences(QWidget, Ui_Form):
"""Preferences widget screen"""
def __init__(self, GUI):
super().__init__()
self.setupUi(self)
self.GUI_preferences = GUI
try:
guirestore(self, self.GUI_preferences)
except: # Create new empty file if none found
guisave(self, self.GUI_preferences)
self.watermarkText = self.watermark.text()
self.pushButton.clicked.connect(self.hide)
self.cb_dark.stateChanged.connect(self.cb_dark_check)
self.cb_watermark.stateChanged.connect(self.cb_watermark_check)
self.cb_dark_check() # Restores dark mode on start
def cb_dark_check(self):
"""Define stylesheet based on saved settings"""
# * Save checkbox status after signal
guisave(self, self.GUI_preferences)
self.dark_mode_set = bool(int(self.GUI_preferences.value('cb_dark')))
# * Apply corresponding style
self.window_plot_update()
try:
import qdarkstyle
if not self.dark_mode_set:
app.setStyleSheet("") # Default style
else:
app.setStyleSheet(qdarkstyle.load_stylesheet())
# * Update icons, toolbar and canvas
except:
qtw.QMessageBox.critical(self, "Error", "Could not set all stylesheet settings.")
# ! Currently not updating multiple graphs without app restart
def cb_watermark_check(self):
"""Define watermark based on checkbox state and line text"""
if self.cb_watermark.isChecked():
self.watermarkText = self.watermark.text()
else:
self.watermarkText = ""
# Replot with set watermarkText
for window in window_list:
window.route_canvas.plot()
guisave(self, self.GUI_preferences)
def window_plot_update(self):
"""Update icons and replot"""
for window in window_list:
window.route_toolbar = Toolbar_route(
window.route_canvas, None, coordinates=True, darkMode=self.dark_mode_set
)
window.route_canvas = PlotCanvas_route(window.GUI_preferences)
window.instances_route_canvas.append(window.route_canvas)
window.instances_route_toolbar.append(window.route_toolbar)
# ? Hide or delete previous
if len(window.instances_route_toolbar) > 1:
a = window.instances_route_toolbar[-1]
a.setVisible(False)
a = window.instances_route_canvas[-1]
a.setVisible(False)
if __name__ == "__main__":
import sys
app = qtw.QApplication(sys.argv)
w = NewWindow() # Instantiate window factory class
# ? Exit with Ctrl + C
timer = QtCore.QTimer()
timer.timeout.connect(lambda: None)
timer.start(100)
sys.exit(app.exec_())
如果需要,除了图标之外的其余文件都在this gist 中。
【问题讨论】:
-
恐怕重现这个的最小代码相当广泛,如果有帮助的话,我会尽可能地减少它
标签: python-3.x matplotlib pyqt pyqt5