class double_pendulum_window(QW.QMainWindow): """ The main window which houses both options and plot canvas """ def __init__(self): super().__init__() # Create the main Widget and layout self._main = QW.QWidget() self.setWindowTitle('Double Pendulum Simulation') self.setCentralWidget(self._main) self.layout_main = QW.QHBoxLayout(self._main) # A shortcut to close the app. self.closer = QW.QShortcut(QG.QKeySequence('Ctrl+Q'), self, self.quit) self.create_options() self.create_plot_window() def create_options(self): # Create all the options. Both the necessary backend and frontend # Backend - here are all the parameters # Since QSlider only works for integers, we create a linspace vector # for each parameter and use the QSlider value as the index for the # linspace vector. self.param_names = ['r1', 'm1', 'm2', 'g'] self.param_min = [0.05, 0.1, 0.1, 1] self.param_max = [0.95, 10, 10, 100] self.param_start = [45, 9, 9, 18] self.param_intervals = [0.01, 0.1, 0.1, 0.5] self.param_values = [] self.current_values = [] self.param_nums = [ ((max_ - min_) / int_ + 1) for min_, max_, int_ in zip( self.param_min, self.param_max, self.param_intervals) ] self.param_nums = [np.round(i).astype(int) for i in self.param_nums] for min_, max_, nums, start in zip(self.param_min, self.param_max, self.param_nums, self.param_start): # Here we create the actual linspace vectors and add them to the # backend values = np.linspace(min_, max_, nums) self.param_values.append(values) self.current_values.append(values[start]) # Frontend self.param_labels = [] self.param_fields = [] self.param_value_labels = [] self.layout_options = QW.QVBoxLayout() self.button_restart = QW.QPushButton('Restart program', self) self.button_restart.clicked.connect(self.restart_plot) # Create each line in the parameter layout for i, (name, max_, start, values) in enumerate( zip(self.param_names, self.param_nums, self.param_start, self.param_values)): label = QW.QLabel(name, self) field = QW.QSlider(QC.Qt.Horizontal) field.setMinimum(0) field.setMaximum(max_ - 1) field.setValue(start) field.valueChanged.connect( lambda sv, i=i: self.update_param_value(sv, i)) value_label = QW.QLabel(f'{values[start]:.2f}') self.param_labels.append(label) self.param_fields.append(field) self.param_value_labels.append(value_label) # Add the parameters to the layout self.layout_parameters = QW.QGridLayout() for n in range(len(self.param_fields)): self.layout_parameters.addWidget(self.param_labels[n], n, 0) self.layout_parameters.addWidget(self.param_fields[n], n, 1) self.layout_parameters.addWidget(self.param_value_labels[n], n, 2) self.layout_options.addWidget(self.button_restart) self.layout_options.addLayout(self.layout_parameters) self.layout_main.addLayout(self.layout_options) def create_plot_window(self): # Creates the actual plot window and initializes the animation self.fig, self.ax, self.ax2 = dp.animation_window() self.canvas = FigureCanvas(self.fig) self.canvas.setFixedSize(600, 800) self.initialize_plot() self.tool = NavigationToolbar(self.canvas, self) self.addToolBar(self.tool) self.layout_main.addWidget(self.canvas) def update_param_value(self, slider_index, i): # updates the i'th parameter value new_value = self.param_values[i][slider_index] self.param_value_labels[i].setText(f'{new_value:.2f}') self.current_values[i] = new_value def restart_plot(self): # Clears the plotting window and makes way for a new animtion # Stop the animation self.canvas.close_event() # Delete the animation connection ID, figure and axes objects del self.cid del self.fig del self.ax del self.ax2 # Remove and delete the toolbar self.removeToolBar(self.tool) del self.tool # Delete the canvas self.layout_main.removeWidget(self.canvas) self.canvas.deleteLater() self.canvas = None # Create the new window self.create_plot_window() def initialize_plot(self): # Initialize the animation class r1, m1, m2, g = self.current_values r2 = 1 - r1 N = 3001 dt = 0.01 self.cid = self.canvas.mpl_connect( 'button_press_event', lambda event: dp._on_mouse(event, r1=r1, r2=r2, ax=self.ax, ax2=self.ax2, fig=self.fig, N=N, dt=dt, m1=m1, m2=m2, g=g)) def quit(self): sys.exit()
class MyWindow(QtWidgets.QMainWindow): def __init__(self, **kwargs): super(MyWindow, self).__init__() uic.loadUi('equation_visualizer_layout.ui', self) def func(x, y): return np.cos(np.sqrt(x ** 2 + y ** 2)) x = np.linspace(-5, 6, 40) y = np.linspace(-5, 6, 40) X, Y = np.meshgrid(x, y) Z = func(X, Y) self.fig = plt.figure() self.ax = plt.axes(projection='3d') self.ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='magma', edgecolor='none') self.ax.set_title('Surface Map Magma'); self.ax.view_init(50, 35) self.plotWidget = FigureCanvas(self.fig) self.lay = QtWidgets.QVBoxLayout(self.content_plot) print(self.content_plot) self.lay.setContentsMargins(0, 0, 0, 0) self.lay.addWidget(self.plotWidget) self.addToolBar(QtCore.Qt.BottomToolBarArea, NavigationToolbar(self.plotWidget, self)) ''' self.addToolBar(QtCore.Qt.BottomToolBarArea, NavigationToolbar(self.plotWidget, self)) self.button = QPushButton('pushButtonNEW', self) self.button.move(70,540) self.button.clicked.connect(self.onClick) self.button2 = QPushButton('pushButtonNEW2', self) self.button2.move(350,540) self.button2.clicked.connect(self.onClick) self.button3 = QPushButton('pushButtonNEW3', self) self.button3.move(550,540) self.button3.clicked.connect(self.onClick) ''' def create_wire_frame(self): def func(x, y): return np.cos(np.sqrt(x ** 2 + y ** 2)) x = np.linspace(-5, 6, 40) y = np.linspace(-5, 6, 40) X, Y = np.meshgrid(x, y) Z = func(X, Y) fig = plt.figure() ax = plt.axes(projection='3d') ax.plot_wireframe(X, Y, Z, color='black') ax.set_title('wireframe'); return fig @pyqtSlot() def onClick(self): self.plotWidget.deleteLater() self.__init__()
class DendrogramWidget(QWidget): channel_type = data.Input image_widget = None group_combobox = None def __init__(self, parent = None): super(QWidget, self).__init__(parent) self.setLayout(QVBoxLayout()) header_layout = QHBoxLayout() header_layout.addWidget(QLabel("Selection group:")) self.group_combobox = QComboBox() self.group_combobox.addItems(data.selectionGroupNames()) self.group_combobox.currentTextChanged.connect(self.update_dendrogram) header_layout.addWidget(self.group_combobox) self.layout().addLayout(header_layout) self.plot = QLabel("Select a group above...") self.layout().addWidget(self.plot) self.resize(600, 800) self.setWindowTitle("Dendrogram Example") self.show() def update_dendrogram(self): self.layout().removeWidget(self.plot) self.plot.deleteLater() group_name = self.group_combobox.currentText print("dendrogram: update_dendrogram %s"%(group_name)) plt.clf() fig = plt.gcf() fig.set_size_inches(8, 8) fig.set_dpi(120) X = np.array([]) labels = [] for channel in data.timeSeriesList(self.channel_type): if channel.name == "TotalBeam": continue labels += [channel.name] d = np.array([]) for s in data.selectionGroup(group_name).selections(): selection_data = channel.dataForSelection(s) d = np.insert(d, 0, selection_data) try: d = d/np.max(d) except: labels.pop() print('Error processing channel %s'%(channel.name)) continue if len(X) == 0: X = d else: X = np.column_stack((X, d)) try: linked = linkage(np.transpose(X), 'ward') dendrogram(linked, labels=labels) except RuntimeError as r: print(r) self.plot = QLabel('There was a problem processing the selected group.') self.layout().addWidget(self.plot) return fig.canvas.draw() self.plot = FigureCanvas(fig) self.plot.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.layout().addWidget(self.plot)
class DensityWidget(QWidget): xrange_plot = None yrange_plot = None def __init__(self, parent=None, width=5, height=4, dpi=100): super(QWidget, self).__init__(parent) self.setLayout(QVBoxLayout()) self.header_layout = QHBoxLayout() self.layout().addLayout(self.header_layout) self.header_layout.addWidget(QLabel("Selection group")) self.group_combobox = QComboBox(self) self.header_layout.addWidget(self.group_combobox) self.header_layout.addWidget(QLabel("Channel")) self.channel_combobox = QComboBox(self) self.header_layout.addWidget(self.channel_combobox) axes_button = QToolButton(self) axes_button.setText("Setup axes") axes_button.clicked.connect(self.setup_axes) self.header_layout.addWidget(axes_button) spacer = QWidget(self) spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) self.header_layout.addWidget(spacer) save_button = QToolButton(self) save_button.setText("Save") save_button.clicked.connect(self.save_figure) self.header_layout.addWidget(save_button) self.plot = QLabel("Select a group above...") self.layout().addWidget(self.plot) self.group_combobox.addItems(data.selectionGroupNames()) self.group_combobox.currentTextChanged.connect( lambda: self.update_plot()) self.channel_combobox.addItems(data.timeSeriesNames()) self.channel_combobox.currentTextChanged.connect( lambda: self.update_plot()) self.setWindowTitle("Density Plot") def update_plot(self, xrange=None, yrange=None): self.layout().removeWidget(self.plot) self.plot.deleteLater() group_name = self.group_combobox.currentText channel_name = self.channel_combobox.currentText print("density: update_plot %s %s" % (group_name, channel_name)) group = data.selectionGroup(group_name) channel = data.timeSeries(channel_name) x = np.empty(group.count) * np.nan err = np.empty(group.count) * np.nan for i, s in enumerate(group.selections()): r = data.result(s, channel) x[i] = r.value() err[i] = r.uncertaintyAs2SE() self.fig = plt.figure() self.fig.set_size_inches(8, 8) self.fig.set_dpi(120) ax = self.fig.add_subplot(111) kde = stats.gaussian_kde(x, bw_method=kde_bandwidth) ax.plot(x, np.zeros(x.shape), 'b|', ms=5) x_eval = np.linspace(0.8 * x.min(), 1.2 * x.max(), num=2000) ax.plot(x_eval, kde(x_eval), 'k-', label='kde') if xrange is None: self.xrange_plot = ax.get_xlim() else: print('xrange=%s' % (xrange)) ax.set_xlim(xrange) if yrange is None: self.yrange_plot = ax.get_ylim() else: print('yrange=%s' % (yrange)) ax.set_ylim(yrange) ax.set_xlabel(channel.name) self.fig.canvas.draw() self.plot = FigureCanvas(self.fig) self.plot.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.layout().addWidget(self.plot) def save_figure(self): fname = QFileDialog.getSaveFileName( self, "Save figure", QDir.homePath(), "Images (*.png, *.jpg, *.tif, *.pdf, *.svg") print("Trying to save to file %s" % (fname)) if fname: self.fig.savefig(fname) def setup_axes(self): d = QDialog(self) l = QVBoxLayout() fl = QFormLayout() d.setLayout(l) params = { 'X plot range': (self.xrange_plot, QLineEdit(), QLineEdit()), 'Y plot range': (self.yrange_plot, QLineEdit(), QLineEdit()) } for key in params: ll = QHBoxLayout() params[key][1].setText(params[key][0][0]) params[key][2].setText(params[key][0][1]) ll.addWidget(params[key][1]) ll.addWidget(params[key][2]) fl.addRow(key, ll) l.addLayout(fl) ok_button = QPushButton('Ok') cancel_button = QPushButton('Cancel') button_layout = QHBoxLayout() button_layout.addWidget(cancel_button) button_layout.addWidget(ok_button) l.addLayout(button_layout) ok_button.clicked.connect(lambda: d.accept()) cancel_button.clicked.connect(lambda: d.reject()) if (d.exec() == QDialog.Accepted): self.xrange_plot = (float(params['X plot range'][1].text), float(params['X plot range'][2].text)) self.yrange_plot = (float(params['Y plot range'][1].text), float(params['Y plot range'][2].text)) self.update_plot(xrange=self.xrange_plot, yrange=self.yrange_plot)
class UPbContourWidget(QWidget): xrange_mask = (0, 30) yrange_mask = (0, 3) zrange_mask = (5000, math.inf) xrange_plot = (0, 30) yrange_plot = (0, 3) marker_sep = 100 * 10**6 def __init__(self, parent=None, width=5, height=4, dpi=100): super(QWidget, self).__init__(parent) self.setLayout(QVBoxLayout()) self.header_layout = QHBoxLayout() self.layout().addLayout(self.header_layout) self.header_layout.addWidget(QLabel("Selection group")) self.group_combobox = QComboBox(self) self.header_layout.addWidget(self.group_combobox) self.tw_checkbox = QCheckBox(self) self.tw_checkbox.setText("Tera-Wasserburg?") self.header_layout.addWidget(self.tw_checkbox) axes_button = QToolButton(self) axes_button.setText("Setup axes") axes_button.clicked.connect(self.setup_axes) self.header_layout.addWidget(axes_button) spacer = QWidget(self) spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) self.header_layout.addWidget(spacer) save_button = QToolButton(self) save_button.setText("Save") save_button.clicked.connect(self.save_figure) self.header_layout.addWidget(save_button) self.plot = QLabel("Select a group above...") self.layout().addWidget(self.plot) self.group_combobox.addItems(data.selectionGroupNames()) self.group_combobox.currentTextChanged.connect(self.update_plot) self.tw_checkbox.toggled.connect(self.update_plot) self.setWindowTitle("U-Pb contour") def update_plot(self): self.layout().removeWidget(self.plot) self.plot.deleteLater() TW = self.tw_checkbox.checked group_name = self.group_combobox.currentText print("UPb_contour: update_plot %s %s" % (group_name, TW)) channel_data = {} channel_names = () if TW: channel_names = ('Final U238/Pb206', 'Final Pb207/Pb206', 'Zr96_CPS') else: channel_names = ('Final Pb207/U235', 'Final Pb206/U238', 'Zr96_CPS') for channel_name in channel_names: channel = data.timeSeries(channel_name) channel_data[channel_name] = np.array([]) for s in data.selectionGroup(group_name).selections(): selection_data = channel.dataForSelection(s) channel_data[channel_name] = np.insert( channel_data[channel_name], 0, selection_data) masks = [self.xrange_mask, self.yrange_mask, self.zrange_mask] for i, mask in enumerate(masks): if mask is None: masks[i] = (-math.inf, math.inf) df = pd.DataFrame({ 'x': channel_data[channel_names[0]], 'y': channel_data[channel_names[1]], 'z': channel_data[channel_names[2]] }) df = df.loc[(df['x'] > masks[0][0]) & (df['x'] < masks[0][1]) & (df['y'] > masks[1][0]) & (df['y'] < masks[1][1]) & (df['z'] > masks[2][0]) & (df['z'] < masks[2][1])] # Calculate concordia and markers t = np.array(range(1, 4001 * 10**6, 1 * 10**6)) tmarkers = np.array(range(100 * 10**6, 4001 * 10**6, self.marker_sep)) Xcon, Ycon = np.array([]), np.array([]) Xmarkers, Ymarkers = np.array([]), np.array([]) if TW: (Xcon, Ycon) = (1 / (np.exp(l238U * t) - 1), (1 / U85r) * (np.exp(l235U * t) - 1) / (np.exp(l238U * t) - 1)) (Xmarkers, Ymarkers) = (1 / (np.exp(l238U * tmarkers) - 1), (1 / U85r) * (np.exp(l235U * tmarkers) - 1) / (np.exp(l238U * tmarkers) - 1)) else: (Xcon, Ycon) = (np.exp(l235U * t) - 1, np.exp(l238U * t) - 1) (Xmarkers, Ymarkers) = (np.exp(l235U * tmarkers) - 1, np.exp(l238U * tmarkers) - 1) # Do joint plot sb.set_style('ticks') self.jointgrid = sb.jointplot(df['x'], df['y'], kind="kde", height=8, space=0, n_levels=50, shade_lowest=False, xlim=self.xrange_plot, ylim=self.yrange_plot) self.jointgrid.ax_joint.plot(Xcon, Ycon, zorder=1) self.jointgrid.ax_joint.scatter(Xmarkers, Ymarkers, s=50, zorder=2) if TW: self.jointgrid.set_axis_labels( r'${^{238}\rm{U}}/{^{206}\rm{Pb}}$', r'${^{207}\rm{Pb}}/{^{206}\rm{Pb}}$', fontsize=24) else: self.jointgrid.set_axis_labels(r'${^{207}\rm{Pb}}/{^{235}\rm{U}}$', r'${^{206}\rm{Pb}}/{^{238}\rm{U}}$', fontsize=24) x_scale = 9 * 0.78 / (self.xrange_plot[1] - self.xrange_plot[0]) y_scale = 7 * 0.80 / (self.yrange_plot[1] - self.yrange_plot[0]) for i, a in enumerate(tmarkers): if (a == 0 or i < 2): continue rotation = np.degrees( np.arctan2((Ymarkers[i] - Ymarkers[i - 1]) * y_scale, (Xmarkers[i] - Xmarkers[i - 1]) * x_scale)) offset = (0, 0) self.jointgrid.ax_joint.annotate("%i" % (a / 1e6) + " Ma", xy=(Xmarkers[i], Ymarkers[i]), xytext=offset, textcoords='offset points', rotation=rotation - 90, ha='right', va='center', rotation_mode='anchor', fontsize=14) self.plot = FigureCanvas(self.jointgrid.fig) self.plot.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.layout().addWidget(self.plot) def save_figure(self): fname = QFileDialog.getSaveFileName( self, "Save figure", QDir.homePath(), "Images (*.png, *.jpg, *.tif, *.pdf, *.svg") print("Trying to save to file %s" % (fname)) if fname: self.jointgrid.savefig(fname) def setup_axes(self): d = QDialog(self) l = QVBoxLayout() fl = QFormLayout() d.setLayout(l) params = { 'X mask range': (self.xrange_mask, QLineEdit(), QLineEdit()), 'Y mask range': (self.yrange_mask, QLineEdit(), QLineEdit()), 'Z mask range': (self.zrange_mask, QLineEdit(), QLineEdit()), 'X plot range': (self.xrange_plot, QLineEdit(), QLineEdit()), 'Y plot range': (self.yrange_plot, QLineEdit(), QLineEdit()) } for key in params: ll = QHBoxLayout() params[key][1].setText(params[key][0][0]) params[key][2].setText(params[key][0][1]) ll.addWidget(params[key][1]) ll.addWidget(params[key][2]) fl.addRow(key, ll) l.addLayout(fl) ok_button = QPushButton('Ok') cancel_button = QPushButton('Cancel') button_layout = QHBoxLayout() button_layout.addWidget(cancel_button) button_layout.addWidget(ok_button) l.addLayout(button_layout) ok_button.clicked.connect(lambda: d.accept()) cancel_button.clicked.connect(lambda: d.reject()) if (d.exec() == QDialog.Accepted): self.xrange_mask = (float(params['X mask range'][1].text), float(params['X mask range'][2].text)) self.yrange_mask = (float(params['Y mask range'][1].text), float(params['Y mask range'][2].text)) self.zrange_mask = (float(params['Z mask range'][1].text), float(params['Z mask range'][2].text)) self.xrange_plot = (float(params['X plot range'][1].text), float(params['X plot range'][2].text)) self.yrange_plot = (float(params['Y plot range'][1].text), float(params['Y plot range'][2].text)) self.update_plot()