New signal connects to old slot instead of separate slot - python

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()

Related

AttributeError: 'Figure' object has no property 'polar'

I'm having troubles to plot a spyder graph, I'm making it on pyside with matplotlib
Here is my class(widget in a widget):
# IMPORT PYSIDE CORE
# ///////////////////////////////////////////////////////////////
from pyside_core import *
# IMPORT MODULES
# ///////////////////////////////////////////////////////////////
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.pyplot as plt
import pandas as pd
from math import pi
# IMPORT DICTS
# ///////////////////////////////////////////////////////////////
from gui.core.dicts import util_lists
# PY SPYDER WIDGET
# ///////////////////////////////////////////////////////////////
class PySpyderWidget(QWidget):
def __init__(
self,
language,
parent=None,
bg_two="#343b48",
dark_three="#21252d",
axis_color="#f5f6f9",
color_title="#dce1ec",
line_color="#3f6fd1"
):
super().__init__(parent)
self.language = language
self.spyder_chart = _CustomSpyder(
self,
language=self.language,
bg_two=bg_two,
dark_three=dark_three,
axis_color=axis_color,
color_title=color_title,
line_color=line_color
)
self.principal_layout = QVBoxLayout(self)
self.principal_layout.addWidget(self.spyder_chart)
class _CustomSpyder(FigureCanvas):
def __init__(
self,
parent,
bg_two,
dark_three,
axis_color,
color_title,
line_color
):
fig1, self.ax = plt.subplots(111, polar=True, dpi=100)
super().__init__(fig1)
# COLORS
self.bg_two = bg_two
self.dark_three = dark_three
self.axis_color = axis_color
self.color_title = color_title
self.line_color = line_color
self._parent = parent
self._data = None
self.setParent(parent)
def set_data(self, data):
if self._data is not None:
self._data = pd.concat([self._data, data], axis=0)
else:
self._data = data
def set_chart(self, players, opts):
angles = [n / float(len(opts)) * 2 * pi for n in range(len(opts))]
angles += angles[:1]
if self._data is not None:
column_index_name = self._data.columns[0]
tmp_df = self._data[self._data[column_index_name].isin(players)]
tmp_df = tmp_df.loc[:, opts]
tmp_df = tmp_df.set_index(column_index_name)
# EXTRACT VALUES FOR PLAYER 1
values1 = tmp_df.iloc[0].tolist()
values1 += values1[:1]
# EXTRACT VALUES FOR PLAYER 2
values2 = tmp_df.iloc[1].tolist()
values2 += values2[:1]
plt.xticks(angles[:-1], opts)
# FILL FOR PLAYER 1
self.ax.plot(angles, values1)
self.ax.fill(angles, values1, 'teal', alpha=0.1)
# FILL FOR PLAYER 2
self.ax.plot(angles, values2)
self.ax.fill(angles, values2, 'red', alpha=0.1)
plt.draw()
else:
self.ax.clear()
and the error is like the title
AttributeError: 'Figure' object has no property 'polar'
in line
fig1, self.ax = plt.subplots(111, polar=True, dpi=100)
I guess the formatting on that line is not correct or maybe I should do it differently.
Shouldn't you pass the "polar" keyword in subplot_kw ? See here : https://matplotlib.org/3.5.0/api/_as_gen/matplotlib.pyplot.subplots.html
As for the error in itself, nor subplots nor figure have a "polar" argument, that's what the error message is telling you (you can see in the link above that extra keyword arguments passed to pyplot.subplots get passed to the pyplot.figure call).

Matplotlib set axis limits works in __init__ but not on button click?

I'm writing a medium-sized application to review some data. The structure is that plots will be held in a QTabWidget interface, with a plot control widget to adjust x limits (and later, there may be more features in the control widget). I have included a minimum reproducible example below.
Currently, I pass the axis of a figure to my control widget, and within that widget change the x limits after clicking a button. I have verified that the axis is being passed to the widget (with a print statement). I can programmatically set the axis limits (see line 91: self.Fig_ax.set_xlim(5, 20000) ) in the widget __init__ function and it works, but in the button click function, that same syntax does not do anything. However, with print statements, I verified that the axis is still being passed to the button click function.
I am very confused as to why the set_xlims method works in __init__ but not upon button press. Use: Run the code, enter a number in the X Min and X Max fields, click the Apply X Limits button. For the sake of the example, I hardcoded the button click axis shift to have defined limits rather than use what is entered into the fields, but those fields do get printed to the console for debugging purposes.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created
"""
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
## import matplotlib and animation
import functools
import random as rd
import numpy as np
from numpy import array, sin, pi, arange
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import pandas as pd
## import threading
import time
from matplotlib.backends.qt_compat import QtCore, QtWidgets
from matplotlib.backends.backend_qt5agg import (FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
## New:
from PyQt5.QtWebEngineWidgets import *
######################################################8
class AppWindow(QMainWindow):
def __init__(self):
super().__init__()
self.title = 'DDBRT'
self.setWindowTitle(self.title)
self.DDBRT_Widget = DDBRT(self) # Call the DDBRT
self.setCentralWidget(self.DDBRT_Widget) # set it as the central widget in the window
self.show()
####
####
''' End AppWindow '''
# August 27 2019 Start building a custom QWidget that can be put into the tool in multiple instances to adjust xlims.
# This may also serve as a templatge for other custom widgets that can go in
class XLimControlWidget(QWidget):
def __init__(self, parent, **kwargs):
super(QWidget, self).__init__(parent)
# set layout:
self.XLCWLayout = QVBoxLayout(self)
# Insert X Min Box Label
self.XMinSelectLbl = QLabel('Set X Min:')
self.XLCWLayout.addWidget(self.XMinSelectLbl)
# Insert X Min Entry Field
self.XMinEntryField = QLineEdit('X Min')
self.XLCWLayout.addWidget(self.XMinEntryField)
# Insert X Max Box Label
self.XMaxSelectLbl = QLabel('Set X Min:')
self.XLCWLayout.addWidget(self.XMaxSelectLbl)
# Insert X Max Box Entry Field
self.XMaxEntryField = QLineEdit('X Max')
self.XLCWLayout.addWidget(self.XMaxEntryField)
# Insert Set Button
self.SetXLimsBtn = QPushButton('Apply X Limits')
self.XLCWLayout.addWidget(self.SetXLimsBtn)
# Adjust layout so this widget is compact:
self.XLCWLayout.setSpacing(0)
self.XLCWLayout.setContentsMargins(0, 0, 0, 0)
# Note, that doesn't actually work and it still looks ugly
# That's annoying, but not worth figuring out how to fix right now.
# Need to focus on programming the behavior.
# Try out the kwargs pass to make sure passing something works.
for key, value in kwargs.items():
print('%s = %s' %(key, value))
####
self.Fig_ax = kwargs['Fig_ax_Key']
print('self.Fig_ax = %s of type %s' %(self.Fig_ax, type(self.Fig_ax)))
# Try the fig ax set xlim, which does work but doesn't.
self.Fig_ax.set_xlim(5, 20000)
self.SetXLimsBtn.clicked.connect(self.SetXLimsBtnClkd)
####
def SetXLimsBtnClkd(self): # Define what happens when the button is clicked.
self.xmin = float(self.XMinEntryField.text())
print('X Min will be ', self.xmin, ' of type ', type(self.xmin))
self.xmax = float(self.XMaxEntryField.text())
print('X Max will be ', self.xmax, ' of type ', type(self.xmax))
print('self.Fig_ax = %s of type %s' %(self.Fig_ax, type(self.Fig_ax)))
self.Fig_ax.set_xlim(20, 45)
# End desired goal:
# self.Fig_ax.set_xlim(self.xmin, self.xmax)
####
####
class DDBRT(QWidget):
def __init__(self, parent):
super(QWidget, self).__init__(parent)
#%% Set up multithreading
self.threadpool = QThreadPool() # Set up QThreadPool for multithreading so the GIL doesn't freeze the GUI
print("Multithreading with maximum %d theads" % self.threadpool.maxThreadCount())
#%% Layout:
## Set layout
self.MainLayout = QGridLayout(self)
## Create and embed CheckBox Placeholder
self.ChkBx_Placeholder = QCheckBox('ChkBxPlcholdr1');
self.MainLayout.addWidget(self.ChkBx_Placeholder, 3, 0)
## Create and embed tab container to hold plots, etc.
# Initialize tab container
self.TabsContainer = QTabWidget()
# Initialize tabs
self.tab0 = QWidget()
# Add tabs
self.TabsContainer.addTab(self.tab0, "Tab 0")
# Populate 0th tab
self.tab0.layout = QGridLayout(self)
self.pushButton0 = QPushButton("PyQt5 button")
self.tab0.layout.addWidget(self.pushButton0)
self.tab0.setLayout(self.tab0.layout)
# Add TabsContainer to widget
self.MainLayout.addWidget(self.TabsContainer, 3, 1) # self.MainLayout.addWidget(self.TabsContainer, 2, 2) # Works just fine too, but I can worry about layout finessing later because it's not that difficult, important, or urgent right now
self.setLayout(self.MainLayout)
#%% Plot XLs (accelerations)
XL_t = np.arange(0, 200000, 1)
XL_X = np.sin(XL_t/20000)
XL_Y = np.sin(XL_t/2000)
XL_Z = np.sin(XL_t/200)
self.tab8 = QWidget()
self.TabsContainer.addTab(self.tab8, "Tab 8: Acceleration mpl subplots")
self.tab8.layout = QHBoxLayout(self)
self.XL_Fig = Figure()
self.XL_X_ax = self.XL_Fig.add_subplot(3, 1, 1)
self.XL_X_ax.plot(XL_t, XL_X)
self.XL_X_ax.set_title('Acceleration X')
# self.XL_X_ax.grid(True)
self.XL_X_ax.set_xlabel('Time (s)')
self.XL_X_ax.set_ylabel('Acceleration')
#
self.XL_Y_ax = self.XL_Fig.add_subplot(3, 1, 2, sharex=self.XL_X_ax)
self.XL_Y_ax.plot(XL_t, XL_Y)
self.XL_Y_ax.set_title('Acceleration Y')
# self.XL_Y.grid(True)
self.XL_Y_ax.set_xlabel('Time (s)')
self.XL_Y_ax.set_ylabel('Acceleration')
#
self.XL_Z_ax = self.XL_Fig.add_subplot(3, 1, 3, sharex=self.XL_X_ax)
self.XL_Z_ax.plot(XL_t, XL_Z)
self.XL_Z_ax.set_title('Acceleration Z')
# self.XL_Z.grid(True)
self.XL_Z_ax.set_xlabel('Time (s)')
self.XL_Z_ax.set_ylabel('Acceleration')
#
self.XL_Canvas = FigureCanvas(self.XL_Fig)
self.XL_Canvas.print_figure('test')
# # Create an XLPlot container widget and add the canvas and navigation bar to it
self.XL_PlotContainer = QWidget()
self.XL_PlotContainer.layout = QVBoxLayout(self)
self.XL_PlotContainer.layout.addWidget(self.XL_Canvas)
self.XLMPLToolbar = NavigationToolbar(self.XL_Canvas, self)
self.XL_PlotContainer.layout.addWidget(self.XLMPLToolbar, 3)
self.XL_PlotContainer.setLayout(self.XL_PlotContainer.layout) # Looks redundant but it's needed to display the widgets
# add XLPlotContainer Widget to tab
self.tab8.layout.addWidget(self.XL_PlotContainer, 1)
self.tab8.setLayout(self.tab8.layout)
# add XLCWidget to tab
self.kwargs = {"Fig_ax_Key": self.XL_X_ax}
self.XLXLCW = XLimControlWidget(self, **self.kwargs)
self.tab8.layout.addWidget(self.XLXLCW)
####
####
#%%
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = AppWindow()
sys.exit(app.exec_())
I expect the button click to change the axes on all subplots (since I linked the subplots when I set them up), but the x limits do not change at all. The button click function does run, as shown by the print statements.
In case anyone else finds this question later, here is the solution that worked: I passed the entire figure to my control widget, used a = self.Fig.get_axes() to get a list of my axes, and a[0].set_xlim(20, 200) ; self.Fig.canvas.draw_idle() to update the figure with the button click.

matplotlib and pyqt5 - refresh with button handle

I am a newbie in Python programming, I have started recently.
I am also discovering OOP, I was mostly programming embedded software based on ASM and C languages.
I have a small project where I need to read a file, get the data and "clean" them. These data are plot on a figure in Cartesian and in Polar coordinate. So far I achieved to grab examples of code here and there and to make the interface with pyQt5 and integrate both matplotlib canvases on them.
I have created a button called 'clean' when pressed it creates an event which is working, which clean the data but so far I didn't achieve to refresh the canvasses with the cleaned data.
I think that I got lost with the classes, functions and variables in the OOP of the pyQt integration.
Can somebody help me to see the main structure of the program and to pooint out my issue?
I also know that my python code is not that 'clean' and that I am not using the best of it and I verbose quite a lot... This come from C background.
See below code, I have removed the data treatment from the file. I need to plot the following list of list called LDTvalue[][], I have two graph on one plot which is the list of LDTvalue[0] and LDTvalue[6]:
I figure out that I cannot change any widget when calling the button even clicked. The window title change but not the widgets label for example. So this is where I have a strong suspicion of messing up with the classes and objects functions.
### UPDATE, I have copied the complete code, a bit dirty, but was a first try :) ###
import sys
import matplotlib
matplotlib.use("Qt5Agg")
from PyQt5 import QtCore
from PyQt5.QtWidgets import *
from numpy import arange, sin, cos, pi, radians
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
LDTheader = {'Company': 'xxx','Type_ind': 0,'Symmetry': 0, 'Mc': 0, 'Dc': 0 , 'Ng': 0, 'Dg': 0}
LDTline = list()
LDTCplane = list()
LDTCvalue = list()
LDTvalue = list()
LDTvalue2 = list()
LDTtemplist = list()
LDTfile = open('01.ldt')
for line in LDTfile :
LDTline.append(line.strip())
LDTheader['Company'] = LDTline[0];
LDTheader['Type_ind'] = LDTline[1];
LDTheader['Symmetry'] = LDTline[2];
LDTheader['Mc'] = int(LDTline[3]);
LDTheader['Dc'] = float(LDTline[4]);
LDTheader['Ng'] = int(LDTline[5]);
LDTheader['Dg'] = float(LDTline[6]);
print("Company name:", LDTheader['Company'])
print("Type indicator:", LDTheader['Type_ind'])
print("Symmetry:", LDTheader['Symmetry'])
print("Mc:", LDTheader['Mc'])
print("Dc", LDTheader['Dc'])
print("Ng:", LDTheader['Ng'])
print("Dg:", LDTheader['Dg'])
if LDTheader['Type_ind'] == '1' :
print('1 - point source with symmetry about the vertical axis')
elif LDTheader['Type_ind'] == '2' :
print('2 - linear luminaire')
elif LDTheader['Type_ind'] == '3' :
print('3 - point source with any other symmetry')
else: print('Error in source type indicator!')
if LDTheader['Symmetry'] == '0' :
print('0 - no symmetry')
elif LDTheader['Symmetry'] == '1' :
print('1 - symmetry about the vertical axis')
elif LDTheader['Symmetry'] == '2' :
print('2- symmetry to plane C0-C180')
elif LDTheader['Symmetry'] == '3' :
print('3- symmetry to plane C90-C270')
elif LDTheader['Symmetry'] == '4' :
print('4- symmetry to plane C0-C180 and to plane C90-C270')
else: print('Error in symmetry paramater!')
for i in range(42,42+LDTheader['Mc']) :
LDTCplane.append(LDTline[i])
for i in range(42+LDTheader['Mc'],42+LDTheader['Mc']+LDTheader['Ng']) :
LDTCvalue.append(float(LDTline[i]))
#Separate all the values of the different Cplane in a list of a list called LDTvalue
#print(int(LDTheader['Mc']/int(LDTheader['Symmetry']))+1)
for j in range(0,int(LDTheader['Mc']/int(LDTheader['Symmetry']))+1):
if LDTheader['Symmetry'] == '4':
for i in range(1,LDTheader['Ng']):
LDTtemplist.append(float(LDTline[42+LDTheader['Ng']*(j+1)+LDTheader['Mc']+(LDTheader['Ng']-i)]))
for i in range(0,LDTheader['Ng']-1):
LDTtemplist.append(float(LDTline[42+LDTheader['Ng']*(j+1)+LDTheader['Mc']+(i)]))
LDTvalue.append(list(LDTtemplist))
del LDTtemplist
LDTtemplist = list()
if LDTheader['Symmetry'] == '4':
for i in range(1,LDTheader['Ng']):
LDTtemplist.append(float(LDTline[42+LDTheader['Ng']+LDTheader['Mc']+(LDTheader['Ng']-i)]))
for i in range(0,LDTheader['Ng']-1):
LDTtemplist.append(float(LDTline[42+LDTheader['Ng']+LDTheader['Mc']+(i)]))
LDTvalue2 = LDTtemplist
del LDTtemplist
LDTtemplist = list()
class MyPDiagram(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111,projection='polar')
# We want the axes cleared every time plot() is called
#self.axes.hold(False)
t = arange(-180,180,LDTheader['Dg'])
t = radians(t)
self.axes.plot(t, LDTvalue[0], color='r', linewidth=1)
self.axes.plot(t, LDTvalue[6], color='b', linewidth=1)
self.axes.set_theta_zero_location("S")
self.axes.grid(True)
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QSizePolicy.Expanding,
QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
class MyCDiagram(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
# We want the axes cleared every time plot() is called
#self.axes.hold(False)
x = arange(-180,180,LDTheader['Dg'])
self.axes.plot(x, LDTvalue[0], color='r', linewidth=1)
self.axes.plot(x, LDTvalue[6], color='b', linewidth=1)
self.axes.grid(True)
#
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QSizePolicy.Expanding,
QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
buttonOK = QPushButton('OK')
buttonClean = QPushButton('Clean')
pd_label = QLabel('Polar Diagram')
pd = MyPDiagram(QWidget(self), width=5, height=4, dpi=100)
cd_label = QLabel('Carthesian Diagram')
cd = MyCDiagram(QWidget(self), width=5, height=4, dpi=100)
buttonClean.clicked.connect(self.handleButtonClean)
grid = QGridLayout()
grid.setSpacing(10)
grid.addWidget(buttonOK, 1, 0)
grid.addWidget(buttonClean, 1, 1)
grid.addWidget(pd_label, 2, 0)
grid.addWidget(pd, 3, 0, 5, 1)
grid.addWidget(cd_label, 2, 1)
grid.addWidget(cd, 3, 1, 5, 1)
grid.update()
self.setLayout(grid)
self.setGeometry(300, 300, 1000, 600)
self.setWindowTitle('W&D LDTcleaner')
def handleButtonClean(self):
#1. Clean the value above 90 degrees
for j in range(0,int(LDTheader['Mc']/int(LDTheader['Symmetry']))+1):
for i in range(180,LDTheader['Ng']-1):
LDTvalue[j][i] = 0
#2. Clean when center value is not the max
for j in range(0,int(LDTheader['Mc']/int(LDTheader['Symmetry']))+1):
for i in range(0,LDTheader['Ng']-1):
if LDTvalue[j][0] < max(LDTvalue[j]):
LDTvalue[j][0] = max(LDTvalue[j])
self.initUI() #-----> This is where I am blocked. I tried to recall initUI()
print('Cleaned!')
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MainWindow()
ex.show()
sys.exit(app.exec_())

Python matplotlib graph data persists with iPython kernal

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!

Embedding a Matplotlib plot into PyQt

I have been trying unsuccessfully to embed a matplotlib into PyQt for the past few days and before you say anything, I have been to every site possible to help me.
When I run the code it produces a 1 by 1 graph but doesn't actually graph anything
from __future__ import unicode_literals
import sys
import os
import random
from matplotlib.backends import qt4_compat
use_pyside = qt4_compat.QT_API == qt4_compat.QT_API_PYSIDE
if use_pyside:
from PySide import QtGui, QtCore
else:
from PyQt4 import QtGui, QtCore
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import numpy as np
import scipy.io
import os
import pandas as pd
progname = os.path.basename(sys.argv[0])
progversion = "0.1"
class MyMplCanvas(FigureCanvas):
"""Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.)."""
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
# We want the axes cleared every time plot() is called
self.axes.hold(False)
self.compute_initial_figure()
#
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
def compute_initial_figure(self):
pass
class MyStaticMplCanvas(MyMplCanvas):
def extract_data(self, name):
#setting up lists
Stim_trig = []
Stim_order = []
Sch_wav = []
data = scipy.io.loadmat(name)
for k,v in data.items():
#Sends Sch_wav data to a list
if "Sch_wav" in k:
for d in (((v[0])[0])[4]):
Sch_wav.append(d[0])
#Sends StimTrig to a list
if k=="StimTrig":
for g in (((v[0])[0])[4]):
Stim_trig.append(g[0])
Stim_trig.append(Stim_trig[-1]+1)
#Sends Stim order to a list
for w in (((v[0])[0])[5]):
Stim_order.append(w[0])
superdata = []
#Prepares grouping stimuli and trigger
for i in range(len(Stim_trig)-1):
fire = []
for p in Sch_wav:
if p > Stim_trig[i] and p < Stim_trig[i+1]:
fire.append(p - Stim_trig[i])
superdata.append([Stim_order[i],fire])
#sorts all the data
superdata.sort()
alladdedup = [[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[62]]
count = 0
for d in superdata:
if d[0] == (alladdedup[count])[0]:
for j in d[1]:
((alladdedup)[count]).append(j)
else:
count += 1
#places time stamps of triggers in lists for each trigger
for l in alladdedup:
l.pop(0)
l.sort()
#removes title and sorts data
ffmsb = []
#finds number of firings for each milisecond bin
for v in alladdedup:
fmsb = []
for b in range(100):
msbc = b/100
msb = []
for t in v:
if t > msbc and t < msbc + 0.01:
msb.append(t)
fmsb.append(len(msb))
ffmsb.append(fmsb)
#returns list of stimuli firings per milisecond bin
return ffmsb
#End of Sorting Code
#Start of Graphing Code
def stimuli_graph(self):
#Set file to use and the stimuli wanted. In this case stimuli 1 is being used
filename = ("654508_rec02_all.mat"[1])
self.extract_data(filename)
#Creates parameters for index
numberlist = []
for i in range(100):
numberlist.append(i/100)
#Adjusts the y-axis max according to the y-axis of a graphed stimuli e.g. If plotted graph has y-axis of 10
# then it'll be adjusted by 1.3. So it'll change to 13
x = 0
for i in filename:
if i > x:
x = i*1.3
#Dataframes the data from extract_data
c = pd.Series(filename, index = numberlist)
neurons = pd.DataFrame((c))
#This is where the data gets graphed. I know this is where the problem occurs, I just don't know where
#This code is a mess and for that I am sorry
times = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
ax = neurons.plot(
kind = 'bar',
title = 'Number of neurons firing for stimuli '+str(1),
legend = False,
xlim = (0, 100),
ylim = (0, x),
width = 1,
position = 0,
edgecolor = 'white',
xticks = (np.arange(min(times), max(times), 10)) #Makes the x-axis label in increments of 0.1 instead of 0.01
)
ax.set_xlabel('Time (s)')
ax.set_ylabel('Neurons Firing')
t = arange(0, 100, 1)
self.axes.plot(t, neurons)
class ApplicationWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.setWindowTitle("application main window")
self.file_menu = QtGui.QMenu('&File', self)
self.file_menu.addAction('&Quit', self.fileQuit,
QtCore.Qt.CTRL + QtCore.Qt.Key_Q)
self.menuBar().addMenu(self.file_menu)
self.help_menu = QtGui.QMenu('&Help', self)
self.menuBar().addSeparator()
self.menuBar().addMenu(self.help_menu)
self.main_widget = QtGui.QWidget(self)
l = QtGui.QVBoxLayout(self.main_widget)
sc = MyStaticMplCanvas(self.main_widget, width=5, height=4, dpi=100)
l.addWidget(sc)
self.main_widget.setFocus()
self.setCentralWidget(self.main_widget)
self.statusBar().showMessage("All hail matplotlib!", 2000)
def fileQuit(self):
self.close()
def closeEvent(self, ce):
self.fileQuit()
qApp = QtGui.QApplication(sys.argv)
aw = ApplicationWindow()
aw.setWindowTitle("%s" % progname)
aw.show()
sys.exit(qApp.exec_())
#qApp.exec_()
The data used in the graph is extracted from an external file

Categories