Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2e83a8a

Browse files
authored
Merge pull request #9934 from fariza/toolmanager-qt
ENH: MEP22 implementation for QT backend
2 parents 3e37306 + 943c40d commit 2e83a8a

File tree

3 files changed

+194
-6
lines changed

3 files changed

+194
-6
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Added support for QT in new ToolManager
2+
=======================================
3+
4+
Now it is possible to use the ToolManager with Qt5
5+
For example
6+
7+
import matplotlib
8+
9+
matplotlib.use('QT5AGG')
10+
matplotlib.rcParams['toolbar'] = 'toolmanager'
11+
import matplotlib.pyplot as plt
12+
13+
plt.plot([1,2,3])
14+
plt.show()
15+
16+
17+
Treat the new Tool classes experimental for now, the API will likely change and perhaps the rcParam as well
18+
19+
The main example `examples/user_interfaces/toolmanager_sgskip.py` shows more
20+
details, just adjust the header to use QT instead of GTK3

examples/user_interfaces/toolmanager_sgskip.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616

1717
from __future__ import print_function
1818
import matplotlib
19+
# Change to the desired backend
1920
matplotlib.use('GTK3Cairo')
21+
# matplotlib.use('TkAgg')
22+
# matplotlib.use('QT5Agg')
2023
matplotlib.rcParams['toolbar'] = 'toolmanager'
2124
import matplotlib.pyplot as plt
2225
from matplotlib.backend_tools import ToolBase, ToolToggleBase

lib/matplotlib/backends/backend_qt5.py

Lines changed: 171 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
from matplotlib._pylab_helpers import Gcf
1616
from matplotlib.backend_bases import (
1717
_Backend, FigureCanvasBase, FigureManagerBase, NavigationToolbar2,
18-
TimerBase, cursors)
18+
TimerBase, cursors, ToolContainerBase, StatusbarBase)
1919
import matplotlib.backends.qt_editor.figureoptions as figureoptions
2020
from matplotlib.backends.qt_editor.formsubplottool import UiSubplotTool
2121
from matplotlib.figure import Figure
22+
from matplotlib.backend_managers import ToolManager
23+
from matplotlib import backend_tools
2224

2325
from .qt_compat import (
2426
QtCore, QtGui, QtWidgets, _getSaveFileName, is_pyqt5, __version__, QT_API)
@@ -583,14 +585,23 @@ def __init__(self, canvas, num):
583585

584586
self.window._destroying = False
585587

586-
# add text label to status bar
587-
self.statusbar_label = QtWidgets.QLabel()
588-
self.window.statusBar().addWidget(self.statusbar_label)
589-
588+
self.toolmanager = self._get_toolmanager()
590589
self.toolbar = self._get_toolbar(self.canvas, self.window)
590+
self.statusbar = None
591+
592+
if self.toolmanager:
593+
backend_tools.add_tools_to_manager(self.toolmanager)
594+
if self.toolbar:
595+
backend_tools.add_tools_to_container(self.toolbar)
596+
self.statusbar = StatusbarQt(self.window, self.toolmanager)
597+
591598
if self.toolbar is not None:
592599
self.window.addToolBar(self.toolbar)
593-
self.toolbar.message.connect(self.statusbar_label.setText)
600+
if not self.toolmanager:
601+
# add text label to status bar
602+
statusbar_label = QtWidgets.QLabel()
603+
self.window.statusBar().addWidget(statusbar_label)
604+
self.toolbar.message.connect(statusbar_label.setText)
594605
tbs_height = self.toolbar.sizeHint().height()
595606
else:
596607
tbs_height = 0
@@ -639,10 +650,19 @@ def _get_toolbar(self, canvas, parent):
639650
# attrs are set
640651
if matplotlib.rcParams['toolbar'] == 'toolbar2':
641652
toolbar = NavigationToolbar2QT(canvas, parent, False)
653+
elif matplotlib.rcParams['toolbar'] == 'toolmanager':
654+
toolbar = ToolbarQt(self.toolmanager, self.window)
642655
else:
643656
toolbar = None
644657
return toolbar
645658

659+
def _get_toolmanager(self):
660+
if matplotlib.rcParams['toolbar'] == 'toolmanager':
661+
toolmanager = ToolManager(self.canvas.figure)
662+
else:
663+
toolmanager = None
664+
return toolmanager
665+
646666
def resize(self, width, height):
647667
'set the canvas size in pixels'
648668
self.window.resize(width, height + self._status_and_tool_height)
@@ -912,6 +932,151 @@ def _reset(self):
912932
self._widgets[attr].setValue(value)
913933

914934

935+
class ToolbarQt(ToolContainerBase, QtWidgets.QToolBar):
936+
def __init__(self, toolmanager, parent):
937+
ToolContainerBase.__init__(self, toolmanager)
938+
QtWidgets.QToolBar.__init__(self, parent)
939+
self._toolitems = {}
940+
self._groups = {}
941+
self._last = None
942+
943+
@property
944+
def _icon_extension(self):
945+
if is_pyqt5():
946+
return '_large.png'
947+
return '.png'
948+
949+
def add_toolitem(
950+
self, name, group, position, image_file, description, toggle):
951+
952+
button = QtWidgets.QToolButton(self)
953+
button.setIcon(self._icon(image_file))
954+
button.setText(name)
955+
if description:
956+
button.setToolTip(description)
957+
958+
def handler():
959+
self.trigger_tool(name)
960+
if toggle:
961+
button.setCheckable(True)
962+
button.toggled.connect(handler)
963+
else:
964+
button.clicked.connect(handler)
965+
966+
self._last = button
967+
self._toolitems.setdefault(name, [])
968+
self._add_to_group(group, name, button, position)
969+
self._toolitems[name].append((button, handler))
970+
971+
def _add_to_group(self, group, name, button, position):
972+
gr = self._groups.get(group, [])
973+
if not gr:
974+
sep = self.addSeparator()
975+
gr.append(sep)
976+
before = gr[position]
977+
widget = self.insertWidget(before, button)
978+
gr.insert(position, widget)
979+
self._groups[group] = gr
980+
981+
def _icon(self, name):
982+
pm = QtGui.QPixmap(name)
983+
if hasattr(pm, 'setDevicePixelRatio'):
984+
pm.setDevicePixelRatio(self.toolmanager.canvas._dpi_ratio)
985+
return QtGui.QIcon(pm)
986+
987+
def toggle_toolitem(self, name, toggled):
988+
if name not in self._toolitems:
989+
return
990+
for button, handler in self._toolitems[name]:
991+
button.toggled.disconnect(handler)
992+
button.setChecked(toggled)
993+
button.toggled.connect(handler)
994+
995+
def remove_toolitem(self, name):
996+
for button, handler in self._toolitems[name]:
997+
button.setParent(None)
998+
del self._toolitems[name]
999+
1000+
1001+
class StatusbarQt(StatusbarBase, QtWidgets.QLabel):
1002+
def __init__(self, window, *args, **kwargs):
1003+
StatusbarBase.__init__(self, *args, **kwargs)
1004+
QtWidgets.QLabel.__init__(self)
1005+
window.statusBar().addWidget(self)
1006+
1007+
def set_message(self, s):
1008+
self.setText(s)
1009+
1010+
1011+
class ConfigureSubplotsQt(backend_tools.ConfigureSubplotsBase):
1012+
def trigger(self, *args):
1013+
image = os.path.join(matplotlib.rcParams['datapath'],
1014+
'images', 'matplotlib.png')
1015+
parent = self.canvas.manager.window
1016+
dia = SubplotToolQt(self.figure, parent)
1017+
dia.setWindowIcon(QtGui.QIcon(image))
1018+
dia.exec_()
1019+
1020+
1021+
class SaveFigureQt(backend_tools.SaveFigureBase):
1022+
def trigger(self, *args):
1023+
filetypes = self.canvas.get_supported_filetypes_grouped()
1024+
sorted_filetypes = sorted(six.iteritems(filetypes))
1025+
default_filetype = self.canvas.get_default_filetype()
1026+
1027+
startpath = os.path.expanduser(
1028+
matplotlib.rcParams['savefig.directory'])
1029+
start = os.path.join(startpath, self.canvas.get_default_filename())
1030+
filters = []
1031+
selectedFilter = None
1032+
for name, exts in sorted_filetypes:
1033+
exts_list = " ".join(['*.%s' % ext for ext in exts])
1034+
filter = '%s (%s)' % (name, exts_list)
1035+
if default_filetype in exts:
1036+
selectedFilter = filter
1037+
filters.append(filter)
1038+
filters = ';;'.join(filters)
1039+
1040+
parent = self.canvas.manager.window
1041+
fname, filter = _getSaveFileName(parent,
1042+
"Choose a filename to save to",
1043+
start, filters, selectedFilter)
1044+
if fname:
1045+
# Save dir for next time, unless empty str (i.e., use cwd).
1046+
if startpath != "":
1047+
matplotlib.rcParams['savefig.directory'] = (
1048+
os.path.dirname(six.text_type(fname)))
1049+
try:
1050+
self.canvas.figure.savefig(six.text_type(fname))
1051+
except Exception as e:
1052+
QtWidgets.QMessageBox.critical(
1053+
self, "Error saving file", six.text_type(e),
1054+
QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.NoButton)
1055+
1056+
1057+
class SetCursorQt(backend_tools.SetCursorBase):
1058+
def set_cursor(self, cursor):
1059+
self.canvas.setCursor(cursord[cursor])
1060+
1061+
1062+
class RubberbandQt(backend_tools.RubberbandBase):
1063+
def draw_rubberband(self, x0, y0, x1, y1):
1064+
height = self.canvas.figure.bbox.height
1065+
y1 = height - y1
1066+
y0 = height - y0
1067+
rect = [int(val) for val in (x0, y0, x1 - x0, y1 - y0)]
1068+
self.canvas.drawRectangle(rect)
1069+
1070+
def remove_rubberband(self):
1071+
self.canvas.drawRectangle(None)
1072+
1073+
1074+
backend_tools.ToolSaveFigure = SaveFigureQt
1075+
backend_tools.ToolConfigureSubplots = ConfigureSubplotsQt
1076+
backend_tools.ToolSetCursor = SetCursorQt
1077+
backend_tools.ToolRubberband = RubberbandQt
1078+
1079+
9151080
def error_msg_qt(msg, parent=None):
9161081
if not isinstance(msg, six.string_types):
9171082
msg = ','.join(map(str, msg))

0 commit comments

Comments
 (0)