I'm trying to Draw VTK ChartXY using QVTKRenderWindowInteractor because my program is designed to use PyQt.
Problem is, showing chart is good but, i can't interact with the chart for example, zoom, pan, hovering.
When i tested identical chart show task without QVTKRenderWindowInteractor, then interact event was perfect.
Here is my reference Code.
from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
from PyQt5.QtWidgets import QVBoxLayout, QWidget, QApplication, QPushButton
import matplotlib.pyplot as plt
import numpy as np
import vtk
import math
import sys
import os
app = QApplication(sys.argv)
widget = QVTKRenderWindowInteractor()
widget.Initialize()
widget.Start()
view = vtk.vtkContextView()
view.GetRenderer().SetBackground(1.0, 1.0, 1.0)
view.GetRenderWindow().SetSize(400, 300)
chart = vtk.vtkChartXY()
view.GetScene().AddItem(chart)
chart.SetShowLegend(True)
table = vtk.vtkTable()
arrX = vtk.vtkFloatArray()
arrX.SetName('X Axis')
arrC = vtk.vtkFloatArray()
arrC.SetName('Cosine')
arrS = vtk.vtkFloatArray()
arrS.SetName('Sine')
arrT = vtk.vtkFloatArray()
arrT.SetName('Sine-Cosine')
table.AddColumn(arrC)
table.AddColumn(arrS)
table.AddColumn(arrX)
table.AddColumn(arrT)
numPoints = 40
inc = 7.5 / (numPoints - 1)
table.SetNumberOfRows(numPoints)
for i in range(numPoints):
table.SetValue(i, 0, i * inc)
table.SetValue(i, 1, math.cos(i * inc))
table.SetValue(i, 2, math.sin(i * inc))
table.SetValue(i, 3, math.sin(i * inc) - math.cos(i * inc))
points = chart.AddPlot(vtk.vtkChart.POINTS)
points.SetInputData(table, 0, 1)
points.SetColor(0, 0, 0, 255)
points.SetWidth(1.0)
points.SetMarkerStyle(vtk.vtkPlotPoints.CROSS)
view.GetRenderWindow().SetMultiSamples(0)
view.GetInteractor().Initialize()
view.GetInteractor().Start()
widget.GetRenderWindow().AddRenderer(view.GetRenderer())
widget.setWindowTitle('Qt Widget')
widget.show()
exit(app.exec_())
This example show Pure VTK window and after closing VTK window show QtWidget window.
The Pure VTK window interact perfectly with my commant, QtWidget interact not at all.
Is anybody know how to make VTK chart interact well with PyQt system??
Thanks.
Here is my own answer.
VTKRenderWindowInteractor should get parameter manually iren, rw.
view = vtk.vtkContextView()
widget = VTKRenderWindowInteractor(iren=view.GetInteractor(), rw=view.GetRenderWindow())
then i could interact with my chart with Qt system well.
here is better and precise solution from VTK community.
You need just simply set RenderWindow.
https://discourse.vtk.org/t/problem-with-vtk-chart-interaction-with-pyqt/6561?u=hhebb
Related
I'm trying to use a combination of JupyterLab and the latest ipywidgets to have a basic graphic interface to explore some data. However, I cannot find a way to set up the figure height for the interactive plot
The notebook loads some file, then the user has the ability to pass some input using widgets (in the code below a text box). Finally, by pressing a button the graph is generated and passed to an Output widget.
This is the example code:
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import clear_output, display
import ipywidgets as widgets
data = pd.read_csv('data.csv')
def show_results(input):
if 'fig' in locals():
del fig
with out:
clear_output(True)
fig, ax = plt.subplots(1, 1)
ax.axis('equal')
data.loc[input.value].plot(cmap='inferno_r', column='probability', ax=ax, legend=True)
plt.show()
input = widgets.Text(placeholder='input field', description='Input:')
showMe = widgets.Button(
description='Show me the results',
tooltip='Show me',
)
out = widgets.Output(layout = {
'width': '100%',
'height': '900px',
'border': '1px solid black'
})
showMe.on_click(show_results)
display(input)
display(showMe)
display(out)
The problem is that the graph automatically resizes no matter how large is the Output widget, and no matter of the figsize parameter passed.
I would expect one of the two to control the output size particularly because there is no more the option to resize the graph manually.
As an additional info the HTML div dealing with the graph inside the Output widget is always 500px height no matter what I do
Found the solution after a lot of research. Currently there is an open issue on github discussing just how %matplotlib widget behaves differently compared to the rest of matplotlib. Here is the link to the discussion
Basically this backend takes advantage of jupyter widgets and passes the figure to an Output widget. In my case I was nesting this into another Output widget to capture the plot.
In this case neither the figsize attribute for the plot and the CSS layout attributes for the outer Output widget have an impact as the problem sits with this intermediate Output widget.
The good news is that the CSS layout attributes that we need to change are exposed. To modify them we can use:
fig.canvas.layout.width = '100%'
fig.canvas.layout.height = '900px'
The above solution did not work for me. Neither setting the plot dimension globally using rcParams still I managed to get it working setting the figure dimension inside the function using fig = plt.figure(figsize=(12,12)):
from ipywidgets import interactive
import matplotlib.pyplot as plt
import numpy as np
np.random.rand(33,20)
def f():
fig = plt.figure(figsize=(15,15)) # <-- Here the figure dimension is controlled
ax = fig.add_subplot()
ax.imshow(np.random.rand(33,20)/65536)
plt.show()
interactive_plot = interactive(f, r = (0,15),g = (0,15),b = (0,15))
interactive_plot
I am making a gui using PyQt4 and matplotlib. I found this very helpful question/answer on making a tabbed gui window that I am trying to use. Everything seems to work fine (with a couple of minor adjustments); however, whenever I go to close the main window I get a "Python quit unexpectedly" error along with a segmentation fault. While this doesn't affect the operation of the program it is rather annoying and I'd like to hunt down the problem.
Now, while trying to figure out what was going on I got down to the following MWE (or minimum broken example if you will)
import sys
import matplotlib
matplotlib.use('Qt4agg')
import matplotlib.pyplot as plt
import numpy as np
from PyQt4 import QtCore
from PyQt4 import QtGui as qt
if __name__ == "__main__":
fig = plt.figure()
x, y = np.random.randn(2, 40)
ax = fig.add_subplot(111)
ax.plot(x, y, 'o')
ax.hold(False)
app = qt.QApplication(sys.argv)
# ui = MplMultiTab(figures=[fig], labels=["yay"])
ui = qt.QMainWindow()
ui.main_frame = qt.QWidget()
vbox = qt.QVBoxLayout()
vbox.addWidget(fig.canvas)
ui.main_frame.setLayout(vbox)
ui.setCentralWidget(ui.main_frame)
ui.show()
sys.exit(app.exec_())
The problem seems to be adding the figure canvas to the window, as if I don't do this and instead make an empty gui (remove vbox.addWidget(fig.canvas)) everything is fine and there is no segfault.
Can anyone see what is going wrong or is it a bug in matplotlib or pyqt? Also interestingly, If I set up my gui similar to this answer then I don't have a segmentation fault but I can't really figure out what the difference between them is.
For everyone's information I am running this on python 3.5.2 using PyQt verion 4.11.4 with matplotlib version 1.5.3 on OSX 10.11.6.
As stated in the PyQt documentation:
For any GUI application using Qt, there is precisely one QApplication object, no matter whether the application has 0, 1, 2 or more windows at any given time.
I think what most likely happens here is that a QApplication is silently created by pyplot when generating the figure. This QApplication is used to manage the main event loop of the FigureManager, which is the GUI that is created by pyplot to show the figure onscreen.
So, since a QApplication has already been created with pyplot, I think an error should normally be raised when qt.QApplication(sys.argv) is called further down in the code, but somewhat Qt does not seem to see it and allow the creation of another QApplication. That is probably what is causing the clash when trying to close the application. I see 3 different options to solve this issue:
1 - Put the pyplot code inside your QApplication, so that pyplot sees it and uses it instead of constructing its own one:
import sys
import matplotlib
matplotlib.use('Qt4agg')
import matplotlib.pyplot as plt
import numpy as np
from PyQt4 import QtGui as qt
if __name__ == '__main__':
app = qt.QApplication(sys.argv)
fig, ax = plt.subplots()
x, y = np.random.randn(2, 40)
ax.plot(x, y, 'o')
ui = qt.QWidget()
vbox = qt.QVBoxLayout()
vbox.addWidget(fig.canvas)
ui.setLayout(vbox)
ui.show()
sys.exit(app.exec_())
2 - Use a pointer to the QApplication already constructed by pyplot instead of creating a new one:
import sys
import matplotlib
matplotlib.use('Qt4agg')
import matplotlib.pyplot as plt
import numpy as np
from PyQt4 import QtGui as qt
if __name__ == '__main__':
fig, ax = plt.subplots()
x, y = np.random.randn(2, 40)
ax.plot(x, y, 'o')
app = qt.QApplication.instance()
ui = qt.QWidget()
vbox = qt.QVBoxLayout()
vbox.addWidget(fig.canvas)
ui.setLayout(vbox)
ui.show()
sys.exit(app.exec_())
3 - As suggested in the matplotlib documentation, avoid to use the pyplot interface when embedding mpl figures in a Qt interface and use the Object Oriented API instead. In this case, your MWE could be rewritten as:
import sys
import matplotlib
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg
import numpy as np
from PyQt4 import QtGui as qt
if __name__ == '__main__':
app = qt.QApplication(sys.argv)
fig = matplotlib.figure.Figure()
canvas = FigureCanvasQTAgg(fig)
ax = fig.add_subplot(111)
x, y = np.random.randn(2, 40)
ax.plot(x, y, 'o')
ui = qt.QWidget()
vbox = qt.QVBoxLayout()
vbox.addWidget(canvas)
ui.setLayout(vbox)
ui.show()
sys.exit(app.exec_())
I am fooling around making a small program for some personal use with Tkinter, and it will contain a plot which needs to be resized if the window is resized. This works if I make the window smaller, however, if I increase the window size the plot stays the same size after it has reached some size (although it nicely sticks to the middle of the frame it is in). I tried both pack and grid, but I don't understand why it will resize any bigger ... Currently I set to original image to 21 by 13 inches (but I would like to know what prevents/prohibits it from resizing in the first place.
this is my code (I know its a bit bulky..)
from Tkinter import *
import ttk
import misc
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
root = Tk()
root.title("Plotting Customers Lines")
content = ttk.Frame(root, padding=(3,3,12,12))
plot = ttk.Frame(root, padding=(3,3,12,12))
toolbarframe = ttk.Frame(root)
subject = StringVar(content)
dropdown = ttk.Combobox(content, textvariable = subject, values = ['To', 'Do', 'Later'])
ok = ttk.Button(content, text='Plot Timeline')
f = plt.figure(figsize=(21,13))
a = f.add_subplot(111)
a.plot(range(100),range(100))
canvas = FigureCanvasTkAgg(f, plot)
canvas.show()
toolbar = NavigationToolbar2TkAgg(canvas, toolbarframe)
toolbar.update()
content.grid(column=1, row=0)
plot.grid(column=0,row=0)
toolbarframe.grid(column=0,row=1)
ok.grid(column=1, row=1)
dropdown.grid(column=1,row=0)
canvas.get_tk_widget().grid(column=0, row=0, sticky=(N,W,E,S))#pack(side=TOP, fill=BOTH, expand = True)#grid(column=0, row=0, sticky=(N,W,E,S))
root.columnconfigure(0, weight = 1)
root.columnconfigure(1, weight = 0)
root.rowconfigure(0, weight = 1)
root.rowconfigure(1, weight = 0)
plot.columnconfigure(0, weight = 1)
plot.rowconfigure(0, weight = 1)
root.mainloop()
I have seen many examples of integrating matplotlib with python2.6 and QT4 Designer using mplwidget and they work fine. I now need to integrate a pyplot with QT4 Designer but cannot find any examples. All of my attempts to integrate a pyplot graphic have ended in a seg fault. Can someone please direct me to a working example using Designer and pyplot?
Follow up:
Okay, so I tried your solution but I'm still having issues. Unfortunately the machine I use for this code is not hooked up to the internet, so below is a fat finger of the pertinent parts of the code I am using:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import MatplotlibWidget # This is the code snippet you gave in your answer
.
.
.
def drawMap(self) :
fig = plt.figure()
map = Basemap(projection='cyl', llcrnrlat = -90.0, urcrnrlat = 90.0, llcrnrlon = -180.0, urcrnrlon = 180.0, resolution = 'c')
map.drawcoastlines()
map.drawcountries()
plt.show()
def drawMap_Qt(self) :
self.ui.PlotWidget.figure = plt.figure() # This is the Qt widget promoted to MatplotlibWidget
map = Basemap(projection='cyl', llcrnrlat = -90.0, urcrnrlat = 90.0, llcrnrlon = -180.0, urcrnrlon = 180.0, resolution = 'c')
map.drawcoastlines()
map.drawcountries()
self.ui.PlotWidget.show()
The function drawMap() works fine. It creates a separate window and plots the map. The drawMap_Qt() function results in a segmentation fault with no other errors. The end goal is to plot a contour on top of the map. I can do this with the drawMap() function. Of course, I can't even get to the contour part with the drawMap_Qt() function. Any insights as to why it is seg faulting would be greatly appreciated.
If you're referring to the mplwidget from Python(x,y) and WinPython, I think it does use pyplot, but I had trouble putting it in my python install so I just used this class:
from PyQt4 import QtGui
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
class MatplotlibWidget(QtGui.QWidget):
def __init__(self, parent=None, *args, **kwargs):
super(MatplotlibWidget, self).__init__(parent)
self.figure = Figure(*args, **kwargs)
self.canvas = FigureCanvas(self.figure)
self.toolbar = NavigationToolbar(self.canvas, self)
layout = QtGui.QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
self.setLayout(layout)
See also How to embed matplotib in pyqt - for Dummies.
Then in Qt Designer you need to create a promoted class, with base class: QWidget, promoted class name: MatplotlibWidget, and header file: the python script containing the MatplotlibWidget class (without the .py). You can add things like ax = self.figure.add_subplot(111), line = ax.plt(...) within the class or by calling methods of the figure attribute of an instance of the class.
Edit:
So I was a bit wrong before, in general with embedded matplotlib widgets you need to use the object oriented methods and not the functions in pyplot. (This sort of explains what the difference is.) Using my snippet above as mymatplotlibwidget.py, try something like this. I don't have basemap installed, so this is somewhat of a guess, but from the examples you need to tell Basemap which axes to use.
import sys
from PyQt4 import QtGui
from mpl_toolkits.basemap import Basemap
from mymatplotlibwidget import MatplotlibWidget
app = QtGui.QApplication(sys.argv)
widget = MatplotlibWidget()
fig = widget.figure
ax = fig.add_subplot(111)
map = Basemap(..., ax=ax)
fig.canvas.draw()
widget.show()
app.exec_()
I've encountered this behaviour intermittently using the Matplotlib NavigationToolbar2Wx in a Matplotlib Figure canvas in a wx.Frame (or wx.Panel). If the Zoom Icon or Pan Icon are selected the icon disappears however a click in the vacant space still toggles the tool. The Icons for the Home, Backward step or Forward step all behave as expected.
Can anyone offer advice on 1. what causes it and 2. how to fix it?
Thanks to joaquin for posting the initial code slightly modified to include the toolbar.
(http://stackoverflow.com/questions/10737459/embedding-a-matplotlib-figure-inside-a-wxpython-panel)
I'm use python 2.6, wxPython 2.9.2.4 osx-carbon (classic) and Matplotlib 1.1.0
Thanks
The code below shows the problem:
#!/usr/bin/env python
# encoding: UTF-8
"""
wxPython and Matplotlib Canvas with Matplotlib Toolbar.py
"""
from numpy import arange, sin, pi
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wx import NavigationToolbar2Wx
from matplotlib.figure import Figure
import wx
class CanvasPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.figure = Figure()
self.axes = self.figure.add_subplot(111)
self.canvas = FigureCanvas(self, -1, self.figure)
# Add Matplotlib Toolbar
# Add the Matplotlib Navigation toolBar here
self.toolbar=NavigationToolbar2Wx(self.canvas)
self.toolbar.AddLabelTool(5,'',wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_TOOLBAR, (32,32)))
#self.Bind(wx.EVT_TOOL, self.NewTitle(), id=5)
self.toolbar.Realize()
# Add to Box Sizer
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.TOP | wx.GROW)
self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
self.SetSizer(self.sizer)
self.Fit()
def draw(self):
t = arange(0.0, 3.0, 0.01)
s = sin(2 * pi * t)
self.axes.plot(t, s)
if __name__ == "__main__":
app = wx.PySimpleApp()
fr = wx.Frame(None, title='test',size=(800,600))
panel = CanvasPanel(fr)
panel.draw()
fr.Show()
app.MainLoop()
I can't comment on the causes of this specific issue,
but I was experiencing some problems with non-Agg backend with wxpython 2.9 too (while the code worked ok in 2.8). Replacing the Toolbar with the Agg version fixed such problems for me; e.g.:
from matplotlib.backends.backend_wx import NavigationToolbar2Wx
==>
from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg
and adjusting the code accordingly:
self.toolbar=NavigationToolbar2Wx(self.canvas)
==>
self.toolbar = NavigationToolbar2WxAgg(self.canvas)
hth,
vbr