Is it possible to create a chaco plot with latex text? For example, if we wanted latex symbols in the title of this exampe:
from traits.api import HasTraits, Instance
from traitsui.api import View, Item
from chaco.api import Plot, ArrayPlotData
from enable.component_editor import ComponentEditor
from numpy import linspace, sin
class LinePlot(HasTraits):
plot = Instance(Plot)
traits_view = View(
Item('plot',editor=ComponentEditor(), show_label=False),
width=500, height=500, resizable=True, title="Chaco Plot")
def __init__(self):
super(LinePlot, self).__init__()
x = linspace(-14, 14, 100)
y = sin(x) * x**3
plotdata = ArrayPlotData(x=x, y=y)
plot = Plot(plotdata)
plot.plot(("x", "y"), type="line", color="blue")
plot.title = "sin(x) * x^3"
self.plot = plot
if __name__ == "__main__":
LinePlot().configure_traits()
I tried replacing title with $sin(x)^3$ to no avail, and wondered if this was possible? Screenshot below:
No, it is not (it is a matplotlib feature). But you could try to use unicode symbols for easy cases.
Related
I would like to use matplotlib in my code but without FigureCanvasKivyAgg. I use kivy and I tried this but the plot it is not visible. This is my code:
import matplotlib
import matplotlib.pyplot as plt
x = [1,2,3,4]
y = [5,10,12,9]
plt.plot(x,y)
class ClassForPlot(Screen):
def __init__(self, **kw):
super().__init__(**kw)
self._app = App.get_running_app()
plt.show()
plt.show() doesn't work in init?
So I have a very basic plot layout described below (with x and y values changed for brevity):
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
import numpy as np
figure = Figure()
axes = figure.gca()
axes.set_title(‘My Plot’)
x=np.linspace(1,10)
y=np.linspace(1,10)
y1=np.linspace(11,20)
axes.plot(x,y,’-k’,label=‘first one’)
axes.plot(x,y1,’-b’,label=‘second one’)
axes.legend()
axes.grid(True)
And I have designed a GUI in QT designer that has a GraphicsView (named graphicsView_Plot) that I would like to put this graph into and I would like to know how I would go about putting this graph into the GraphicsView. Barring starting over and using the QT based graphing ability I don’t really know how (if possible) to put a matplotlib plot into this graphics view. I know it would be a super simple thing if I can convert it into a QGraphicsItem as well, so either directly putting it into the GraphicsView or converting it to a QGraphicsItem would work for me.
You have to use a canvas that is a QWidget that renders the matplotlib instructions, and then add it to the scene using addWidget() method (or through a QGraphicsProxyWidget):
import sys
from PyQt5 import QtWidgets
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import numpy as np
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
scene = QtWidgets.QGraphicsScene()
view = QtWidgets.QGraphicsView(scene)
figure = Figure()
axes = figure.gca()
axes.set_title("My Plot")
x = np.linspace(1, 10)
y = np.linspace(1, 10)
y1 = np.linspace(11, 20)
axes.plot(x, y, "-k", label="first one")
axes.plot(x, y1, "-b", label="second one")
axes.legend()
axes.grid(True)
canvas = FigureCanvas(figure)
proxy_widget = scene.addWidget(canvas)
# or
# proxy_widget = QtWidgets.QGraphicsProxyWidget()
# proxy_widget.setWidget(canvas)
# scene.addItem(proxy_widget)
view.resize(640, 480)
view.show()
sys.exit(app.exec_())
With creating simple bar plot with chaco strange behavior is occured: half of plot is filled with line_color, another one with fill_color and the last one is not drawn at all (view screenshot). It is expected that data from 0 to 7000 for x and from 0 to 150 for y should be displayed and filled with line_color. With using small values it works fine (for example using 50 instead of 150 for y values). Is there an explanation of such behavior? How it can be solved?
Code below demonstrates the problem:
from enable.api import ComponentEditor
from traits.api import Instance, HasStrictTraits
from traitsui.api import View, UItem
from chaco.api import Plot, ArrayPlotData
class TestPlot(HasStrictTraits):
plot = Instance(Plot)
traits_view = View(UItem('plot', editor=ComponentEditor()),
width=1000, height=800, resizable=True,)
def __init__(self, **kw):
super(TestPlot, self).__init__(**kw)
plot_data = ArrayPlotData(x=list(xrange(0, 7000)), y=[150] * 7000)
self.plot = Plot(plot_data)
self.plot.plot(('x', 'y'), type='bar', line_color="gray", fill_color="lightgray")
self.plot.index_mapper.range.set(low=0 - 150, high=8000 + 50)
self.plot.value_mapper.range.set(low=0 - 50, high=100 + 100)
test = TestPlot()
if __name__ == "__main__":
test.configure_traits()
Is it possible to make chaco plot automatically show full output and not hiding the parts of ticks and labels? E.g. this is the output of standard example:
from chaco.api import ArrayPlotData, Plot
from enable.component_editor import ComponentEditor
from traits.api import HasTraits, Instance
from traitsui.api import View, Item
class MyPlot(HasTraits):
plot = Instance(Plot)
traits_view = View(Item('plot', editor = ComponentEditor(), show_label = False),
width = 500, height = 500, resizable = True)
def __init__(self, x, y, *args, **kw):
super(MyPlot, self).__init__(*args, **kw)
plotdata = ArrayPlotData(x=x,y=y)
plot = Plot(plotdata)
plot.plot(("x","y"), type = "line", color = "blue")
self.plot = plot
import numpy as np
x = np.linspace(-300,300,10000)
y = np.sin(x)*x**3
lineplot = MyPlot(x,y)
lineplot.configure_traits()
As you see the part of tick labels are hidden.. the only thing I can do is to manually adjust left padding of the plot. But this becomes extremely incovinient when you plot different data and different scales or fonts with the plot in application. Is it possible somehow to make padding automatically adjusted to include ALL related info?
UPD.: I've found ensure_labels_bounded property for the axis, but seems it has no effect.
Chaco does not support advanced layout features like these. If you use Chaco, you should use it for its speed, not for nice graphs or features. That being said, here's a version as close as I could get. It requires you to re-size the window with the mouse at least once for the padding correction to take place. Maybe you can find a way to refresh the window without having to manually resize it, I didn't have any luck with that. Anyways hope that gets you on the right track.
from chaco.api import ArrayPlotData, Plot
from enable.component_editor import ComponentEditor
from traits.api import HasTraits, Instance
from traitsui.api import View, Item
class MyPlot(HasTraits):
plot = Instance(Plot)
traits_view = View(Item('plot', editor = ComponentEditor(), show_label = False),
width = 500, height = 500, resizable = True)
def __init__(self, x, y, *args, **kw):
super(MyPlot, self).__init__(*args, **kw)
plotdata = ArrayPlotData(x=x,y=y)
plot = Plot(plotdata, padding=25)
plot.plot(("x","y"), type = "line", color = "blue", name='abc')
self.plot = plot
# watch for changes to the bounding boxes of the tick labels
self.plot.underlays[2].on_trait_change(self._update_size, '_tick_label_bounding_boxes')
self.plot.underlays[3].on_trait_change(self._update_size, '_tick_label_bounding_boxes')
def _update_size(self):
if len(self.plot.underlays[2]._tick_label_bounding_boxes) > 0:
self.plot.padding_bottom = int(np.amax(np.array(self.plot.underlays[2]._tick_label_bounding_boxes),0)[1]+8+4)
if len(self.plot.underlays[3]._tick_label_bounding_boxes) > 0:
self.plot.padding_left = int(np.amax(np.array(self.plot.underlays[3]._tick_label_bounding_boxes),0)[0]+8+4)
import numpy as np
x = np.linspace(-300,300,10000)
y = np.sin(x)*x**3
lineplot = MyPlot(x,y)
lineplot.configure_traits()
I have a matplotlib graph which I want repeated in two separate windows, under PyQt4. I've tried adding the widget to the layout of both, but then the widget vanishes from the first one. Is there any way to do this except creating two identical graphs and keeping them in sync?
The problem is that you can't add the same qt widget to two differents parents widgets because in the process of adding a widget Qt also make a reparent process which does what you see:
... the widget vanishes from the first one[window]...
So the solution is to make two canvas that share the same figure.
Here is an example code, this will show you two main windows each with two canvas and the four plots will be syncronized:
import sys
from PyQt4 import QtGui
import numpy as np
import numpy.random as rd
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
class ApplicationWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.main_widget = QtGui.QWidget(self)
vbl = QtGui.QVBoxLayout(self.main_widget)
self._fig = Figure()
self.ax = self._fig.add_subplot(111)
#note the same fig for two canvas
self.fc1 = FigureCanvas(self._fig) #canvas #1
self.fc2 = FigureCanvas(self._fig) #canvas #1
self.but = QtGui.QPushButton(self.main_widget)
self.but.setText("Update") #for testing the sync
vbl.addWidget(self.fc1)
vbl.addWidget(self.fc2)
vbl.addWidget(self.but)
self.setCentralWidget(self.main_widget)
#property
def fig(self):
return self._fig
#fig.setter
def fig(self, value):
self._fig = value
#keep the same fig in both canvas
self.fc1.figure = value
self.fc2.figure = value
def redraw_plot(self):
self.fc1.draw()
self.fc2.draw()
qApp = QtGui.QApplication(sys.argv)
aw1 = ApplicationWindow() #window #1
aw2 = ApplicationWindow() #window #2
aw1.fig = aw2.fig #THE SAME FIG FOR THE TWO WINDOWS!
def update_plot():
'''Just a random plot for test the sync!'''
#note that the update is only in the first window
ax = aw1.fig.gca()
ax.clear()
ax.plot(range(10),rd.random(10))
#calls to redraw the canvas
aw1.redraw_plot()
aw2.redraw_plot()
#just for testing the update
aw1.but.clicked.connect(update_plot)
aw2.but.clicked.connect(update_plot)
aw1.show()
aw2.show()
sys.exit(qApp.exec_())
While it's not a perfect solution, matplotlib has a built-in way to keep the limits, ticks, etc of two separate plots in sync.
E.g.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 4 * np.pi, 100)
y = np.cos(x)
figures = [plt.figure() for _ in range(3)]
ax1 = figures[0].add_subplot(111)
axes = [ax1] + [fig.add_subplot(111, sharex=ax1, sharey=ax1) for fig in figures[1:]]
for ax in axes:
ax.plot(x, y, 'go-')
ax1.set_xlabel('test')
plt.show()
Notice that all 3 plots will stay in-sync as you zoom, pan, etc.
There's probably a better way of doing it though.