0

I am trying to create a GUI app that lets me plot any variable against any other variable. This is my first GUI app, so perhaps I'm biting more than I can chew. The values of these variables are in arrays, which I have grouped into collections -- that is , I use collections.nametuple to group the raw values, the calculated magnitude and phase, the peak index, and a normalized values (please forgive me if I'm not using the correct terminology), for example.

What I would like to do is to have all these variables (or elements of the various collections(?) as choices in two ComboBoxes, xdata and ydata, so that I can plot any variable against any other variable. Is this even possible to do? This is the MainWindow of the app I'm trying to create: enter image description here

I have the selection of the file, and the parsing of the data working, and for example, say I created these two collections from the data in the file:

type_aData = collections.namedtuple('type_aData',
                                        'A_RX '
                                        'A_RXlogmag '
                                        'A_RXmag'
                                        'A_RXph'
                                        'A_pkMagInd'
                                        'A_RXlogmag_N'
                                        'A_RXmag_N'
                                        'A_RX_N'
                                        'A_RXph_N')



type_distance = collections.namedtuple('type_distance',
                                     'A'
                                     'B')

each A_x is an array, and so are distanceA and distanceB. I'd like to do something like:

self.xdata.addItem(type_distance)
self.ydata.addItem(type_adata)

so that the all the distance variables and aData variables are exposed in the x and y data ComboBoxes, but it complains that the arguments cannot be of type 'type'. If I instead use 'type_distance', for example, in quotes, then all I get is the text in the ComboBox and not the actual variables (never mind that I still don't know how to grab the value selected from the ComboBox to do something with it --that's the next hurdle!).

In short, what I'd like to ultimately be able to do is select the variables I want to plot from the x and y data ComboBoxes so that I can use them in a function that would do something like:

self.data_line=self.graphWidget.plot(self.distance.A, self.aData.A_RX)

or

self.data_line=self.graphWidget.plot(self.distance.A, self.aData.A_RXph),

for example.

If anyone can point me to examples of simple GUI apps that do something similar to what I'm trying to do here , that would be great. I"m not married to doing it this way ---I just don't know any better. I get the feeling I'm complicating this way more than it needs to be.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
jrive
  • 218
  • 2
  • 3
  • 14
  • What do you *intend* `namedtuple` to do? It creates a class, i.e. a `type`. You probably want to add *instances* of those classes, not the classes themselves. – juanpa.arrivillaga Feb 02 '21 at 04:33
  • "The values of these variables are in arrays, which I have grouped into collections -- that is , I use collections.nametuple to group the raw values" Where do you do that? – juanpa.arrivillaga Feb 02 '21 at 04:34
  • @juanpa Gracias por responder. Each of the entries in the collections.namedtuple are arrayes. A_RX1, A_RXlogmax, etc. Same for the distance, distanceA and distanceB are arrays. – jrive Feb 02 '21 at 12:48
  • I just wanted to group them in "structures" so that I could just "pass" the structures between functions instead of each individual array. Can you explain what is meant by " instances of those classes with an example? Not being an object oriented programmer, I still don't fully understand classes, methods, etc... – jrive Feb 02 '21 at 12:50

1 Answers1

1

I'm not sure if this is what you have in mind, but here is a small example to illustrate how you can add the fields of a namedtuple to a QComboBox and use the currently selected fields in these combo boxes to plot the corresponding data in a matplotlib figure:

from PyQt5 import QtWidgets
from collections import namedtuple
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np


type_distance = namedtuple('type_aData', ['A_RX', 'A_RXlogmag', 'A_RXmag'])
type_aData = namedtuple('type_aData', ['A', 'B'])


class Widget(QtWidgets.QWidget):

    def __init__(self, parent=None):
        super().__init__(parent)

        # setup matplotlib figure
        self.figure = Figure()
        self.ax = self.figure.add_subplot(111)
        self.canvas = FigureCanvas(self.figure)

        # setup rest of ui
        self.combo_x = QtWidgets.QComboBox(self)
        self.combo_y = QtWidgets.QComboBox(self)
        hlayout = QtWidgets.QHBoxLayout()
        hlayout.addWidget(self.combo_x)
        hlayout.addWidget(self.combo_y)

        vlayout = QtWidgets.QVBoxLayout(self)
        vlayout.addWidget(self.canvas)
        vlayout.addLayout(hlayout)

        # generate some random data
        self.distance, self.data = self.generate_data()

        # add field names of data to comboboxes
        self.combo_x.addItems(self.distance._fields)
        self.combo_y.addItems(self.data._fields)

        # connect signals to slot to automatically update plot when selected fields have changed
        self.combo_x.currentTextChanged.connect(self.plot)
        self.combo_y.currentTextChanged.connect(self.plot)
        self.plot()

    def plot(self):
        # extract field names and corresponding data that should be plotted
        xlabel = self.combo_x.currentText()
        ylabel = self.combo_y.currentText()

        x_data = getattr(self.distance, xlabel)
        y_data = getattr(self.data, ylabel)

        # update plot with newly selected data
        self.ax.clear()
        self.ax.plot(x_data, y_data, 'ro')
        self.ax.set_xlabel(xlabel)
        self.ax.set_ylabel(ylabel)
        self.figure.tight_layout()
        self.canvas.draw()

    def generate_data(self):
        # generate some random data and return data as type_distance and type_aData objects
        npoints = 300
        x_data = np.random.normal(size=npoints)
        y_data_a = np.random.normal(size=npoints)
        y_data_b = np.sin(x_data)

        distance = type_distance(x_data, np.log(np.abs(x_data)), np.abs(x_data))
        data = type_aData(y_data_a, y_data_b)
        return distance, data


if __name__ == "__main__":
    app = QtWidgets.QApplication([])
    win = Widget()
    win.show()
    app.exec()

screen shot

Heike
  • 24,102
  • 2
  • 31
  • 45
  • I think this is exactly what I was looking for. Let me try to understand what you did. Thank you! – jrive Feb 02 '21 at 12:55
  • I'm running into the first snag. In my case, the ydata is the aggregate of multiple namedtuples (assume, for example that there is another type bData, similar to aData that is also added to the y-ComboBox; How would I implement the line y_data = getattr(self.data, ylabel) ? – jrive Feb 02 '21 at 14:56
  • @jrive Do you want to change the entries in the combobox for the y-data depending on the selectioin of the x-data? For example, if you select `self.distance.A`, would you then only want to see the fields of `self.aData` in the combobox for the y-data? – Heike Feb 03 '21 at 07:54
  • yes, that would be very useful. Appreciate the help! – jrive Feb 03 '21 at 18:21