1

I am writing a routine where I can append a set of datafiles. The first part of the code imports a single datafile and displays it using matplotlib. I can then use a slider to define a specific x range (i.e. to exclude noisy or irrelevant parts). The second part of the code involves an edit routine where I edit the range of all the datafiles in the specific folder based on this newly set x range (i.e. removing all of the rows).

The first part of the code is not a problem for me and results in the following interface. My problem is with the second part. I want the Edit button to close the figure and continue the rest of the code (the datafile editing part). This is necessary because I need the slider values to define the new datarange. enter image description here My initial thought was to place part 2 of the code after plt.show(). The Edit button would simply close the figure and the rest of the code would continue. An example of my code without using my actual data:

### Part 1 ###

import os
import sys
import six
import tkinter as tk
from tkinter import Tk
from tkinter import filedialog
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.gridspec as gridspec
from matplotlib.widgets import Slider, Button


class MinMaxSlider(Slider):
    def __init__(self, ax, label, valmin, valmax, **kwargs):

        self.valinit2 = kwargs.pop("valinit2", valmax)
        self.val2 = self.valinit2
        Slider.__init__(self,ax, label, valmin, valmax, **kwargs)
        self.poly.xy = np.array([[self.valinit,0],[self.valinit,1],
                        [self.valinit2,1],[self.valinit2,0]])
        self.vline.set_visible(False)
    def set_val(self, val):

        if np.abs(val-self.val) < np.abs(val-self.val2):
            self.val = val
        else:
            self.val2 = val
        self.poly.xy = np.array([[self.val,0],[self.val,1],
                                 [self.val2,1],[self.val2,0]])
        self.valtext.set_text(self.valfmt % self.val +"\n"+self.valfmt % self.val2)
        if self.drawon:
            self.ax.figure.canvas.draw_idle()
        if not self.eventson:
            return
        for cid, func in six.iteritems(self.observers):
            func(self.val,self.val2)

def update(mini,maxi):
    ax.set_xlim(mini,maxi)

def edit(event):
    plt.close() 

def find_nearest(array,value):
    return (np.abs(array-value)).argmin()

## Part 1 ##

plt.ion()

x = np.array(range(100))
y_low = 10*np.array(range(100))
y_high = 10*np.array(range(100))

font = {'family' : 'Calibri',
        'weight' : 'normal',
        'size'   : 14}
mpl.rc('font', **font)
axcolor = 'lightgoldenrodyellow'

fig,(ax, sliderax) = plt.subplots(figsize=(12,8), 
                                  nrows=2,gridspec_kw={"height_ratios":[1,0.05]})
fig.subplots_adjust(hspace=0.5)

ln1 = ax.plot(x,y_low, color = 'dodgerblue', label = 'Low')
ax.set_xlabel('x', fontsize=24)
ax.xaxis.labelpad = 15
ax.set_ylabel('y', fontsize=24, color = 'dodgerblue')
ax.yaxis.labelpad = 15
for tl in ax.get_yticklabels():
    tl.set_color('dodgerblue')

ax1 = ax.twinx()
ln2 = ax1.plot(x,y_high, color = 'darkred', label = 'High')
ax1.set_ylabel('y', fontsize=24, color = 'darkred')
for tl in ax1.get_yticklabels():
    tl.set_color('darkred')

lns = ln1+ln2
labs = [l.get_label() for l in lns]
ax1.legend(lns, labs, loc=0)

slider = MinMaxSlider(sliderax,'x(min/max)',x.min(),x.max(),
                      valinit=x.min(),valinit2=x.max())

slider.on_changed(update)
update(x.min(),x.max())

resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Edit', color=axcolor, hovercolor='0.975')
button.on_clicked(edit)

plt.show()

## Part 2 ##

idx_low = find_nearest(x, slider.val)
idx_high = find_nearest(x, slider.val2)
print(idx_low)
print(idx_high)         

However the idx_low and idx_high are already calculated and printed while the figure is open, and thus based on the initial values of the slider. I want them to be calculated after clicking the Edit button and closing the figure so that they are based on the slider values set by the user. How can I achieve this? Do I need to edit the Edit event function? Thank you for your help.

STJ
  • 113
  • 1
  • 2
  • 12
  • The general idea of closing the plot and let the script continue is probably correct. I do not completely understand the pseudo-code (obviously pseudo-code never works so "obvious reasons" is not a useful explanation here). In any case you cannot call `plt.show()` before defining the slider. – ImportanceOfBeingErnest Dec 13 '17 at 10:35
  • @ImportanceOfBeingErnest I have edited the psuedo code with a version without using my actual data. I hope my question/problem is more clear now. – STJ Dec 13 '17 at 12:20
  • What is `plt.ion()` doing in your script? It should work as expected when removing it. – ImportanceOfBeingErnest Dec 13 '17 at 12:21
  • @ImportanceOfBeingErnest I used `plt.ion()` to prevent the terminal form hanging after closing the figure. This does not happen for this example but it does happen when I use my actual datasets (arrays with about 7000 elements). – STJ Dec 13 '17 at 12:58
  • I guess that is a completely different problem. In any case `plt.ion()` specifically prevents the window from taking over the event loop completely and hence any code coming after `show` is just executed right away. So the solution is not to turn interactive mode on at all. – ImportanceOfBeingErnest Dec 13 '17 at 13:17
  • @ImportanceOfBeingErnest Thank you. Would you be able to point out what the problem could be without me having to post my data arrays? Even a simple print statement after `plt.show()` is not executed because the terminal hangs. I have tried the above code with arrays of similar size to my data arrays and it does work fine. – STJ Dec 13 '17 at 14:06
  • By "I have tried the above code with arrays of similar size to my data arrays and it does work fine." you mean it works fine if you leave out `plt.ion()`? – ImportanceOfBeingErnest Dec 13 '17 at 14:08
  • @ImportanceOfBeingErnest Ah yes I meant that. That makes me suspect that it does not have to do with the size, also because a simple `print()` is not executed when using my real data arrays. – STJ Dec 13 '17 at 14:19
  • As far as I understand the issue is now solved, apart from the fact that using your real data makes the console freeze while some fake data lets the program run and exit correctly. In that case you need to come up with a [mcve] for that new issue. Does it depend on the use of updating functions and sliders or would it also show in a plot without those elements? Further anything related to the interplay between console and the program needs to be known, version numbers, how to call the script etc. – ImportanceOfBeingErnest Dec 13 '17 at 14:27

0 Answers0