Python matplotlib graph data persists with iPython kernal - python

I have an application that allows the user to make some vertical line plots among other data. Since I need to use multiple graphs (on multiple pages of a ttk.notebook), I created a class that handles all the gui elements needed to achieve my goals. When I launched the class as its own application things were fine. Once I added the class to my larger gui I encountered an error: the lines created by the user persist as long as the iPython kernal remains open.
I have two questions:
What is causing the new lines to persist on my graph?
Will the issue persist once I turn the larger gui into a standalone executable?
Edit: I've added some code as suggested by j_4321. Its not the exact code but I feel I have the important bits listed. I'm having some trouble making my own executable so I'm still working on that.
Edit 2: The Sequel
Thanks to j_4321 for being patient. I Have updated the code. The error occurs when the classes are in different files (maybe only). This may be a solution to my problem but being that I am new to Python I'd still like to understand what the deal is.
sample.py:
from tkinter import Tk
from tkinter.ttk import Frame, Notebook
from Sample1 import ContainsPlot
class MainUserInterface(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.grid()
self.parent = parent
self.initUI()
def initUI(self):
plotsTab = Notebook(self, name ='controlTab') #create notebook in the GUI
plotsTab.grid(column = 1, row =0, columnspan = 5, rowspan = 14)
plotsFrame = Frame(plotsTab, name = "plotsFrame")
plotsFrame.grid(column = 1, row = 0, rowspan = 14)
plotsTab.add(plotsFrame, text = "ContainsPlot class lives here")
plotFrameElements = ContainsPlot(plotsFrame)
plotFrameElements.grid(column =0, row = 0)
def main():
root = Tk()
app = MainUserInterface(root)
root.mainloop()
if __name__ == '__main__':
main()
Sample1.py:
from tkinter.ttk import Frame
import matplotlib
matplotlib.use("TkAgg")
#from matplotlib import cbook
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg,\
NavigationToolbar2TkAgg
from matplotlib.figure import Figure
class ContainsPlot(Frame, object):
TestFig = Figure(figsize = (5,5), dpi = 100, tight_layout = True)
TestPlotA = TestFig.add_subplot(111)
dist = range(0,10)
dataListA = range(0,10)
TestPlotA.plot(dist, dataListA)
def __init__(self, parent):
Frame.__init__(self, parent)
self.initUI()
def initUI(self):
TestPlot = FigureCanvasTkAgg(self.TestFig, self)
TestPlot.get_tk_widget().grid(column = 2, row = 0, columnspan = 3,\
rowspan = 4)
TestFrame = Frame(self)
TestFrame.grid(column = 2, row =6, columnspan = 3)
nuToolbar = PlotToolbar(TestPlot, TestFrame, self)
nuToolbar.update()
class PlotToolbar(NavigationToolbar2TkAgg, object):
toolitems = [t for t in NavigationToolbar2TkAgg.toolitems if
t[0] in ('Home', 'Pan', 'Zoom', 'Save')]
toolitems.append(('Add Vert Line', 'Add Line', 'move', 'AddLine'))
def __init__(self, plotCanvas, frame, parent):
self.parent = parent
self.fig = plotCanvas.figure
for foo in self.fig.axes:
self.ax = foo
NavigationToolbar2TkAgg.__init__(self, plotCanvas, frame)
def AddLine(self):
yMin, yMax = self.ax.get_ylim()
self.parent.TestPlotA.axvline(x = 3, ymin = yMin, ymax = yMax, color = 'm')
self.fig.canvas.draw()
To reproduce the bug, click the "add line" button (the rightmost) and close the Gui Window. Launch the program again and the line persists. I am using spyder to run my program. Thanks!

Related

New signal connects to old slot instead of separate slot

I am trying to tag x-spans of a data trace and populate a table with tagNames, starting x value, and the ending x value. I am using a dictionary of 'highlight' objects to keep track of the x-spans in case they need to be edited (increased or decreased) later. The dictionary maps the x Start value to the highlight object, as the x start values are expected to be unique (there is no overlap of x-spans for the tagging).
In order to do this, I am emitting a signal when the user beings to edit a cell on the table. The function that the first signal connects to emits another signal (ideally for whether the xStart is changed vs. the xEnd, but I have only implemented the xStart thus far), which actually changes the appearance of the span to match the edit.
I asked a similar question a few weeks ago but wasn't able to get an answer. The old question is here: PyQT5 slot parameters not updating after first call. In response to the tips given there, I wrote the following example:
import sys
from PyQt5.QtWidgets import *
from PyQt5 import QtCore
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.widgets as mwidgets
from functools import partial
class Window(QMainWindow):
def __init__(self, parent = None):
super(Window, self).__init__(parent)
self.resize(1600, 800)
self.MyUI()
def MyUI(self):
canvas = Canvas(self, width=14, height=12, dpi=100)
canvas.move(0,0)
#use this object in the dictionary to hold onto all the spans.
class highlight:
def __init__(self, tag, xStart, xEnd, highlightObj):
self.tag = tag
self.xStart = xStart
self.xEnd = xEnd
self.highlightObj = highlightObj
class Canvas(FigureCanvas):
def __init__(self, parent, width = 14, height = 12, dpi = 100):
Plot = Figure(figsize=(width, height), dpi=dpi)
self.Axes = Plot.add_subplot(111)
self.Axes.set_position([0.05, 0.58, 0.66, 0.55])
self.rowCount = 0
super().__init__(Plot)
self.setParent(parent)
##add all relevant lines to plot
self.Axes.plot([0,1,2,3,4], [3, 4, 5, 6, 7])
self.Axes.set_xlabel('Frame', fontsize = 10)
self.Axes.grid()
self.Axes.set_aspect(1)
Plot.canvas.draw()
self.highlights = {} #empty dictionary to store all the tags.
##define a table to hold the values postselection
self.taggingTable = QTableWidget(self)
self.taggingTable.setColumnCount(3)
self.taggingTable.setRowCount(100)
self.taggingTable.setGeometry(QRect(1005,85, 330, 310))
self.taggingTable.setHorizontalHeaderLabels(['Behavior','Start Frame', 'End Frame'])
Canvas.span = mwidgets.SpanSelector(self.Axes, self.onHighlight, "horizontal",
interactive = True, useblit=True, props=dict(alpha=0.5, facecolor="blue"),)
self.draw_idle()
self.taggingTable.selectionModel().selectionChanged.connect(self.onCellSelect)
self.draw_idle()
##highlighting adds a highlight item to the directory.
def onHighlight(self, xStart, xEnd):
tagName = "No Tag"
self.taggingTable.setItem(self.rowCount, 0, QTableWidgetItem(tagName))
self.taggingTable.setItem(self.rowCount, 1, QTableWidgetItem(str(int(xStart))))
self.taggingTable.setItem(self.rowCount, 2, QTableWidgetItem(str(int(xEnd))))
self.rowCount = self.rowCount + 1
highlightObj = self.Axes.axvspan(xStart, xEnd, color = 'blue', alpha = 0.5)
self.highlights[int(xStart)] = highlight(tagName, xStart, xEnd, highlightObj)
self.draw_idle()
def xStartChanged(self, xStart, rowVal):
if self.inCounter == 0:
print("xStart in slot: ", xStart)
xEnd = self.highlights[xStart].xEnd
xStartNew = int(self.taggingTable.item(rowVal, 1).text())
self.highlights[xStart].highlightObj.remove() #remove old from the plot
del self.highlights[xStart] #remove old from directory
highlightObj = self.Axes.axvspan(xStartNew, xEnd, color = 'blue', alpha = 0.5) #add new to plot
self.highlights[xStartNew] = highlight("No tagName", xStartNew, xEnd, highlightObj) #add new to directory
self.taggingTable.clearSelection() #deselect value from table
self.draw_idle()
self.inCounter = self.inCounter + 1
def onCellSelect(self):
index = self.taggingTable.selectedIndexes()
if len(index) != 0:
rowVal = index[0].row()
if not (self.taggingTable.item(rowVal, 1) is None):
xStart = int(self.taggingTable.item(rowVal, 1).text())
print("--------------")
print("xStart in signal: ", xStart)
self.inCounter = 0
self.taggingTable.itemChanged.connect(lambda: self.xStartChanged(xStart, rowVal))
app = QApplication(sys.argv)
window = Window()
window.show()
app.exec()
A test I run is when I highlight two traces:
and then I successfully change a first trace:
However, when I attempt to edit the second trace, the program crashes:
To debug, I tried to check what the signals were emitting and receiving. it produces the following output:
--------------
xStart in signal: 0
xStart in slot: 0 ##First slot call gets correct signal
--------------
xStart in signal: 3
xStart in slot: 0 ## Second slot gets the first signal instead of the second
Traceback (most recent call last):
File "//Volumes/path/file.py", line 105, in <lambda>
self.taggingTable.itemChanged.connect(lambda: self.xStartChanged(xStart, rowVal))
File "//Volumes/path/file.py", line 86, in xStartChanged
xEnd = self.highlights[xStart].xEnd
KeyError: 0
zsh: abort python Volumes/path file.py
I tried to use information online on unique connections but I am not sure how to implement them. Thank you in advance for any help.
It seems that what you need is table-widget signal that emits an item and its old text value whenever a change is made to a specific column. Unfortunately, the itemChanged signal isn't really suitable, because it doesn't indicate what changed, and it doesn't supply the previous value. So, to work around this limitation, one solution would be to subclass QTableWidget / QTableWidgetItem and emit a custom signal with the required parameters. This will completely side-step the issue with multiple signal-slot connections.
The implementation of the subclasses is quite simple:
class TableWidgetItem(QTableWidgetItem):
def setData(self, role, value):
oldval = self.text()
super().setData(role, value)
if role == Qt.EditRole and self.text() != oldval:
table = self.tableWidget()
if table is not None:
table.itemTextChanged.emit(self, oldval)
class TableWidget(QTableWidget):
itemTextChanged = pyqtSignal(TableWidgetItem, str)
Below is basic a demo based on your example that shows how to use them. (Note that I have made no attempt to handle xEnd as well, as that would go beyond the scope of the immediate issue).
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.widgets as mwidgets
class Window(QMainWindow):
def __init__(self, parent = None):
super(Window, self).__init__(parent)
self.resize(1600, 800)
self.MyUI()
def MyUI(self):
canvas = Canvas(self, width=14, height=12, dpi=100)
canvas.move(0,0)
# CUSTOM SUBCLASSES
class TableWidgetItem(QTableWidgetItem):
def setData(self, role, value):
oldval = self.text()
super().setData(role, value)
if role == Qt.EditRole and self.text() != oldval:
table = self.tableWidget()
if table is not None:
table.itemTextChanged.emit(self, oldval)
class TableWidget(QTableWidget):
itemTextChanged = pyqtSignal(TableWidgetItem, str)
#use this object in the dictionary to hold onto all the spans.
class highlight:
def __init__(self, tag, xStart, xEnd, highlightObj):
self.tag = tag
self.xStart = xStart
self.xEnd = xEnd
self.highlightObj = highlightObj
class Canvas(FigureCanvas):
def __init__(self, parent, width = 14, height = 12, dpi = 100):
Plot = Figure(figsize=(width, height), dpi=dpi)
self.Axes = Plot.add_subplot(111)
self.Axes.set_position([0.05, 0.58, 0.66, 0.55])
self.rowCount = 0
super().__init__(Plot)
self.setParent(parent)
##add all relevant lines to plot
self.Axes.plot([0,1,2,3,4], [3, 4, 5, 6, 7])
self.Axes.set_xlabel('Frame', fontsize = 10)
self.Axes.grid()
self.Axes.set_aspect(1)
Plot.canvas.draw()
self.highlights = {} #empty dictionary to store all the tags.
##define a table to hold the values postselection
# USE CUSTOM TABLE SUBCLASS
self.taggingTable = TableWidget(self)
self.taggingTable.setColumnCount(3)
self.taggingTable.setRowCount(100)
self.taggingTable.setGeometry(QRect(1005,85, 330, 310))
self.taggingTable.setHorizontalHeaderLabels(['Behavior','Start Frame', 'End Frame'])
# CONNECT TO CUSTOM SIGNAL
self.taggingTable.itemTextChanged.connect(self.xStartChanged)
Canvas.span = mwidgets.SpanSelector(self.Axes, self.onHighlight, "horizontal",
interactive = True, useblit=True, props=dict(alpha=0.5, facecolor="blue"),)
self.draw_idle()
##highlighting adds a highlight item to the directory.
def onHighlight(self, xStart, xEnd):
tagName = "No Tag"
self.taggingTable.setItem(self.rowCount, 0, QTableWidgetItem(tagName))
# USE CUSTOM ITEM SUBCLASS
self.taggingTable.setItem(self.rowCount, 1, TableWidgetItem(str(int(xStart))))
self.taggingTable.setItem(self.rowCount, 2, QTableWidgetItem(str(int(xEnd))))
self.rowCount = self.rowCount + 1
highlightObj = self.Axes.axvspan(xStart, xEnd, color = 'blue', alpha = 0.5)
self.highlights[int(xStart)] = highlight(tagName, xStart, xEnd, highlightObj)
self.draw_idle()
def xStartChanged(self, item, oldVal):
try:
# VALIDATE NEW VALUES
xStart = int(oldVal)
xStartNew = int(item.text())
except ValueError:
pass
else:
print("xStart in slot: ", xStart)
xEnd = self.highlights[xStart].xEnd
self.highlights[xStart].highlightObj.remove() #remove old from the plot
del self.highlights[xStart] #remove old from directory
highlightObj = self.Axes.axvspan(xStartNew, xEnd, color = 'blue', alpha = 0.5) #add new to plot
self.highlights[xStartNew] = highlight("No tagName", xStartNew, xEnd, highlightObj) #add new to directory
self.taggingTable.clearSelection() #deselect value from table
self.draw_idle()
app = QApplication(sys.argv)
window = Window()
window.show()
app.exec()

How to remove Matplotlib colorbar upon subsequent replots?

I'm trying to do something pretty straight forward in Matplotlib, but hitting a ton of hurdles; after googling for hours, I'm thinking this takes some very specific magic. I just want my GUI to start on a blank screen and then import a file that needs a colorbar - if the file is good, a colorbar is added. If the file is added again (or a different one), the colorbar is removed and a new one is plotted. If the file is bad, we reset to initial conditions (no colorbars or plot). I really hope the below code makes sense for what I'm getting at here, thank you so much for your help and time in advance:
import sys
import os
import random
import matplotlib
matplotlib.use('Qt5Agg')
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QGridLayout, QFileDialog, QPushButton
import numpy as np
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
class MyMplCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
MyMplCanvas.fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = MyMplCanvas.fig.add_subplot(111)
self.compute_initial_figure()
FigureCanvas.__init__(self, MyMplCanvas.fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
def compute_initial_figure(self):
pass
class MyDynamicMplCanvas(MyMplCanvas):
def __init__(self, *args, **kwargs):
MyMplCanvas.__init__(self, *args, **kwargs)
def compute_initial_figure(self):
C=np.random.rand(500).reshape((20,25))
S=np.random.rand(500).reshape((20,25))
dc = self.function(S,C)
im = self.axes.imshow(dc, alpha = 0)
MyMplCanvas.fig.patch.set_facecolor('k')
self.axes.patch.set_facecolor('k')
# don't want to show the cb initially (imagine GUI starts on a blank screen)
#cb = MyMplCanvas.fig.colorbar(im, ax=self.axes,
#orientation='vertical')#,use_gridspec=True)??
def update_figure(self):
# need something like the below to delete subsequent colorbars if they exist:
#if MyMplCanvas.fig.axes[1] is not None:
# MyMplCanvas.fig.delaxes(MyMplCanvas.fig.axes[1]) #remove colorbar
self.axes.cla()
if P1.df: #datafile is good
MyMplCanvas.fig.patch.set_facecolor('w')
self.axes.patch.set_facecolor('w')
C=np.random.rand(500).reshape((20,25))
S=np.random.rand(500).reshape((20,25))
dc = self.function(S,C)
im = self.axes.imshow(dc)
cb = MyMplCanvas.fig.colorbar(im, ax=self.axes,
orientation='vertical')#,use_gridspec=True)??
else: #datafile was bad, or they pressed cancel
C=np.random.rand(500).reshape((20,25))
S=np.random.rand(500).reshape((20,25))
dc = self.function(S,C)
im = self.axes.imshow(dc, alpha = 0)
MyMplCanvas.fig.patch.set_facecolor('k')
self.axes.patch.set_facecolor('k')
self.show()
self.draw()
def function(self,s,c):
return s*2+c
class P1(QtWidgets.QWidget):
df = False
def __init__(self, parent=None):
super(P1, self).__init__(parent)
layout = QGridLayout(self)
self.button_browse1 = QPushButton('Browse Good', self)
self.button_browse1.clicked.connect(self.browseFile1)
layout.addWidget(self.button_browse1, 1, 1, 1, 1)
self.button_browse1.show()
self.button_browse2 = QPushButton('Browse Bad', self)
self.button_browse2.clicked.connect(self.browseFile2)
layout.addWidget(self.button_browse2, 2, 1, 1, 1)
self.button_browse2.show()
self.dc = MyDynamicMplCanvas(self, width=5, height=4, dpi=100)
layout.addWidget(self.dc, 3, 1, 1, 1)
def browseFile1(self):
P1.df = True
self.dc.update_figure()
def browseFile2(self):
P1.df = False
self.dc.update_figure()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.stack = QtWidgets.QStackedWidget(self)
P1f = P1(self)
self.stack.addWidget(P1f)
self.setCentralWidget(self.stack)
if __name__ == '__main__':
qApp = QtWidgets.QApplication(sys.argv)
aw = MainWindow()
aw.show()
sys.exit(qApp.exec_())
Okay, so for anyone else stumbling on this, the trick is to first define your colorbar the very first time you plot (initial figure) via the top Class its in, i.e., MyMplCanvas.cbar = MyMplCanvas.fig.colorbar(im). Its very important to define it up front even if you didn't want to even use it yet.
Then at the top of the update plot function, simply call MyMplCanvas.cbar.remove().

How to refresh wx.Panel correctly?

I am creating a image viewer using wxPython. I want to view multiple images individually, so I wrote a code below (partially).
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
class CanvasPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.figs = []
self.axes = []
self.canvases = []
self.panelsizers = []
def draw(self, data):
"""data is 2D numpy array"""
fig = Figure()
self.figs.append(fig)
ax = fig.add_subplot(111)
ax.imshow(data, interpolation='none')
self.axes.append(ax)
canvas = FigureCanvas(self, wx.ID_ANY, fig)
self.canvases.append(canvas)
panelsizer = wx.BoxSizer()
panelsizer.Add(canvas, 1, wx.GROW)
self.panelsizers.append(panelsizer)
self.SetSizer(panelsizer)
This works almost perfectly except a slight problem.
When I ran the code and open a image, the window looks like below.
enter image description here
This window consists of three wx.Panels and the center one is CanvasPanel. You can see that the size of CanvasPanel is a bit small even though proportion=1 and style=wx.GROW. Moreover, when I resize this window by dragging the corner of window, it looks like below.
enter image description here
The size of CanvasPanel changes correctly! Why? And how can I revise my code to fit the CanvasPanel in the viewer without resizing.

matplotlib animation in wxpython panel

I'm struggling on an application combining wxPython and matplotlib.
I want to embed an animated matplotlib object in an wxPanel. The Data should be added on runtime.
My Module Code:
(i cant get the correct formatting, see http://pastebin.com/PU5QFEzG)
'''
a panel to display a given set of data in a wxframe as a heatmap, using pcolor
from the matplotlib
#author: me
'''
import wx
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas #todo: OW 26.10.15 needed?
class plotPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.figure = plt.Figure()
self.subplot = self.figure.add_subplot(111)
plt.title('test')
self.canvas = FigureCanvas(self, -1, self.figure) #ToDo: OW 26.10.15 Verstehen
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
self.SetSizer(self.sizer)
self.Fit()
self.dataSet = []
self.animator = animation.FuncAnimation(self.figure,self.anim, interval=1000)
def anim(self, a):
if(len(self.dataSet) == 0):
return 0
i = a % len(self.dataSet)
obj = self.subplot.pcolor(self.dataSet[i], cmap='RdBu')
return obj
def add_data(self, data):
self.dataSet.append(data)
#
# Code for a standalone test run
#
class TestFrame(wx.Frame):
def __init__(self,parent,title):
wx.Frame.__init__(self,parent,title=title,size=(1000,1000))
self.statusbar = self.CreateStatusBar()
self.statusbar.SetStatusText('Status Bar')
if __name__ == '__main__':
from numpy.random import rand #todo: OW 26.10.15 remove
app = wx.App(redirect=False)
frame = TestFrame(None, 'Debug Frame')
panel = plotPanel(frame)
frame.Show()
C = rand(10,10)
panel.add_data(C)
C = rand(10,10)
panel.add_data(C)
C = rand(10,10)
panel.add_data(C)
app.MainLoop()
Im now struggeling on adding more Details to the Graph, eg a colorbar or a title.
If I add self.subplot.title = 'test' in the anim_Fkt, i get "'str' object has no attribute 'get_animated'". If i try plt.title('test'), it has no effect.
Whats the correct way to add a title or a colorbar or a legend?
To add the features to an embedded matplotlib graph, you have to use object-oriented matplotlib API. A matplotlib graph consists of a figure (a matplotlib.figure.Figure instance), which has one or several axes (matplotlib.axes.Axes instances) in it. Each of them, in their turn, contains Drawables (lines, images, scatter plots etc).
A title can be added (or modified) to the Figure or to each Axes with setter methods, such as Figure.suptitle or Axes.set_title (or, in your case, self.figure.suptitle() or self.subplot.set_title).
The colorbar is a little bit trickier, as it needs a data object (a mappable) to be created: we can create it only in the anim function. Also, wo do not want to create many colorbar instances; once created, we only need to update it. Achieving that is easy: instantiate self.colorbar with None in constructor, and then check it against None in animation function: if it is None, then create colorbar, if it is not, then update it:
class plotPanel(wx.Panel):
def __init__(self, parent):
...
self.colorbar = None
def anim(self, a):
...
self.subplot.set_title("Frame %i" % a)
if self.colorbar is None:
self.colorbar = self.figure.colorbar(obj)
else:
self.colorbar.update_normal(obj)
self.colorbar.update_normal(obj)
return obj

Starting a new event loop for chaco-traits popup window from pyside?

I am trying to make a numpy image slider using chaco which is launched from PySide. Ive tried using matplotlib but it lacked the speed for updating so Ive been trying chaco instead. The script below starts by initializing a Pyside dialog, from here I am trying to open a chaco/traits window which is used to display different views of numpy arrays using a slider to scroll through the arrays like a movie. The script works but I get a warning message:
QCoreApplication::exec: The event loop is already running
I think I probably need to start the chaco window using its own thread or something but am not sure how to go about it. Any help much would be appreciated, the script is below:
import numpy as np
import sys
from PySide.QtCore import *
from PySide.QtGui import *
app = QApplication(sys.argv)
from traits.api import *
from traitsui.api import *
from enable.api import *
from enable.component_editor import *
from chaco.api import *
from chaco.tools.api import *
from chaco import *
class chacoWindow(HasTraits):
#Chaco popup window with slider
plot = Instance(HPlotContainer)
#Slider max value is set using Frame
Frame = Int
high = 100
traits_view = View(Group(
Item('plot',
editor=ComponentEditor(),
show_label=False),
Item( 'Frame',
editor = RangeEditor(
high_name = 'high',
format = '%d',
is_float = False))),
width=1000, height=600, resizable=True, title="Track viewer")
def __init__(self, dataDict, arg1):
self.high = arg1 - 1
self.dataDict = dataDict
self.data1 = self.dataDict['Movie1']
self.data2 = self.dataDict['Movie2']
#Left plot
self.plotdata1 = ArrayPlotData(image1 = self.data1['0'])
plotLeft = Plot(self.plotdata1)
plotLeft.img_plot("image1")
#Right plot
self.plotdata2 = ArrayPlotData(image2 = self.data2['0'])
plotRight = Plot(self.plotdata2)
plotRight.img_plot("image2")
#Add some zoom/pan tools
plotLeft.tools.append(PanTool(plotLeft))
plotLeft.tools.append(BetterZoom(plotLeft, zoom_factor=0.8, zoom_to_mouse = True))
plotLeft.tools.append(DragZoom(plotLeft, drag_button="right"))
plotRight.tools.append(PanTool(plotRight))
plotRight.tools.append(BetterZoom(plotRight))
plotRight.tools.append(DragZoom(plotRight, drag_button="right"))
#Axes shared
plotLeft.range2d = plotRight.range2d
container = HPlotContainer(plotLeft, plotRight)
self.plot = container
def _Frame_changed(self):
#Change frame when slider changes
self.plotdata1.set_data("image1", self.data1[str(self.Frame)])
self.plotdata2.set_data("image2", self.data2[str(self.Frame)])
class Form(QDialog):
#Basic form dialog using Pyside
def __init__(self, dataDict, parent=None):
super(Form, self).__init__(parent)
self.dataDict = dataDict
self.totalFrames = len(dataDict['Movie1'])
formatButton1 = QPushButton("Format 1")
buttonLayout = QHBoxLayout()
buttonLayout.addStretch()
buttonLayout.addWidget(formatButton1)
#Add VBox space for extra widgets
layout = QVBoxLayout()
layout.addLayout(buttonLayout)
self.setLayout(layout)
self.connect(formatButton1, SIGNAL("clicked()"), self.setChaco)
self.setWindowTitle('Master form')
def setChaco(self):
#Create chaco window instance
scatter = chacoWindow(dataDict, self.totalFrames)
scatter.configure_traits(self)
if __name__ == "__main__":
#make some demo data
ar0 = np.zeros((25,25))
ar0[10][10] = 1
ar1 = np.zeros((25,25))
ar1[12][12] = 1
#Dictionary of images
dataDict = {'Movie1': {'0': ar0, '1':ar1, '2':ar0},
'Movie2': {'0': ar1, '1':ar0, '2':ar1}}
form = Form(dataDict)
form.show()
app.exec_()
I managed to find the solution. Changing this function solves the problem:
def setChaco(self):
#Create chaco window instance
scatter = chacoWindow(dataDict, self.totalFrames)
#Change this line: scatter.configure_traits(self)
scatter.edit_traits()
This appears to let the PySide event loop handle everything.

Categories