Esempio n. 1
0
class Picker(QWidget):
    date_format = "%Y-%m-%d %HZ"
    run_format = "%d %B %Y / %H%M UTC"

    async = AsyncThreads(2, debug)

    def __init__(self, config, **kwargs):
        """
        Construct the main picker widget: a means for interactively selecting
        which sounding profile(s) to view.
        """
        
        super(Picker, self).__init__(**kwargs)
        self.data_sources = data_source.loadDataSources()
        self.config = config
        self.skew = None

        ## default the sounding location to OUN because obviously I'm biased
        self.loc = None
        ## the index of the item in the list that corresponds
        ## to the profile selected from the list
        self.prof_idx = []
        ## set the default profile type to Observed
        self.model = "Observed"
        ## this is the default model initialization time
        self.run = [ t for t in self.data_sources[self.model].getAvailableTimes() if t.hour in [0, 12] ][-1]

        urls = data_source.pingURLs(self.data_sources)
        self.has_connection = any( urls.values() )

        ## initialize the UI
        self.__initUI()

    def __initUI(self):
        """
        Initialize the main user interface.
        """

        ## Give the main window a layout. Using GridLayout
        ## in order to control placement of objects.

        self.layout = QGridLayout()
        self.setLayout(self.layout)

        self.view = self.create_map_view()
        self.view.hasInternet(self.has_connection)

        self.button = QPushButton('Generate Profiles')
        self.button.clicked.connect(self.complete_name)
        self.button.setDisabled(True)

        self.select_flag = False
        self.all_profs = QPushButton("Select All")
        self.all_profs.clicked.connect(self.select_all)
        self.all_profs.setDisabled(True)

        self.save_view_button = QPushButton('Save Map View as Default')
        self.save_view_button.clicked.connect(self.save_view)

        self.profile_list = QListWidget()
        self.profile_list.setSelectionMode(QAbstractItemView.MultiSelection)
        self.profile_list.setDisabled(True)

        ## create subwidgets that will hold the individual GUI items
        self.left_data_frame = QWidget()
        self.right_map_frame = QWidget()
        ## set the layouts for these widgets
        self.left_layout = QVBoxLayout()
        self.right_layout = QGridLayout() #QVBoxLayout()
        self.left_data_frame.setLayout(self.left_layout)
        self.right_map_frame.setLayout(self.right_layout)

        times = self.data_sources[self.model].getAvailableTimes()

        ## create dropdown menus
        models = sorted(self.data_sources.keys())
        self.model_dropdown = self.dropdown_menu(models)
        self.model_dropdown.setCurrentIndex(models.index(self.model))

        projs = [ ('npstere', 'Northern Hemisphere'), ('merc', 'Tropics'), ('spstere', 'Southern Hemisphere') ]
        if self.config.has_section('map'):
            proj = self.config.get('map', 'proj')
            proj_idx = zip(*projs)[0].index(proj)
        else:
            proj_idx = 0
        self.map_dropdown = self.dropdown_menu(zip(*projs)[1])
        self.map_dropdown.setCurrentIndex(proj_idx)

        self.run_dropdown = self.dropdown_menu([ t.strftime(Picker.run_format) for t in times ])
        try:
            self.run_dropdown.setCurrentIndex(times.index(self.run))
        except ValueError:
            print "Run dropdown is missing its times ... ?"
            print times

        ## connect the click actions to functions that do stuff
        self.model_dropdown.activated.connect(self.get_model)
        self.map_dropdown.activated.connect(self.get_map)
        self.run_dropdown.activated.connect(self.get_run)

        ## Create text labels to describe the various menus
        self.type_label = QLabel("Select Sounding Source")
        self.date_label = QLabel("Select Forecast Time")
        self.map_label = QLabel("Select Map Area")
        self.run_label = QLabel("Select Cycle")
        self.date_label.setDisabled(True)

        ## add the elements to the left side of the GUI
        self.left_layout.addWidget(self.type_label)
        self.left_layout.addWidget(self.model_dropdown)
        self.left_layout.addWidget(self.run_label)
        self.left_layout.addWidget(self.run_dropdown)
        self.left_layout.addWidget(self.date_label)
        self.left_layout.addWidget(self.profile_list)
        self.left_layout.addWidget(self.all_profs)
        self.left_layout.addWidget(self.button)

        ## add the elements to the right side of the GUI
        self.right_layout.setColumnMinimumWidth(0, 500)
        self.right_layout.addWidget(self.map_label, 0, 0, 1, 1)
        self.right_layout.addWidget(self.save_view_button, 0, 1, 1, 1)
        self.right_layout.addWidget(self.map_dropdown, 1, 0, 1, 2)
        self.right_layout.addWidget(self.view, 2, 0, 1, 2)

        ## add the left and right sides to the main window
        self.layout.addWidget(self.left_data_frame, 0, 0, 1, 1)
        self.layout.addWidget(self.right_map_frame, 0, 1, 1, 1)
        self.left_data_frame.setMaximumWidth(280)

    def create_map_view(self):
        """
        Create a clickable map that will be displayed in the GUI.
        Will eventually be re-written to be more general.

        Returns
        -------
        view : QWebView object
        """

        view = MapWidget(self.data_sources[self.model], self.run, self.async, width=800, height=500, cfg=self.config)
        view.clicked.connect(self.map_link)

        return view

    def dropdown_menu(self, item_list):
        """
        Create and return a dropdown menu containing items in item_list.

        Params
        ------
        item_list : a list of strings for the contents of the dropdown menu

        Returns
        -------
        dropdown : a QtGui.QComboBox object
        """
        ## create the dropdown menu
        dropdown = QComboBox()
        ## set the text as editable so that it can have centered text
        dropdown.setEditable(True)
        dropdown.lineEdit().setReadOnly(True)
        dropdown.lineEdit().setAlignment(Qt.AlignCenter)

        ## add each item in the list to the dropdown
        for item in item_list:
            dropdown.addItem(item)

        return dropdown

    def update_list(self):
        """
        Update the list with new dates.

        :param list:
        :return:
        """

        if self.select_flag:
            self.select_all()
        self.profile_list.clear()
        self.prof_idx = []
        timelist = []

        fcst_hours = self.data_sources[self.model].getForecastHours()
        if fcst_hours != [ 0 ]:
            self.profile_list.setEnabled(True)
            self.all_profs.setEnabled(True)
            self.date_label.setEnabled(True)
            for fh in fcst_hours:
                fcst_str = (self.run + date.timedelta(hours=fh)).strftime(Picker.date_format) + "   (F%03d)" % fh
                timelist.append(fcst_str)
        else:
            self.profile_list.setDisabled(True)
            self.all_profs.setDisabled(True)
            self.date_label.setDisabled(True)

        for item in timelist:
            self.profile_list.addItem(item)
 
        self.profile_list.update()
        self.all_profs.setText("Select All")
        self.select_flag = False

    def update_run_dropdown(self):
        """
        Updates the dropdown menu that contains the model run
        information.
        :return:
        """

        getTimes = lambda: self.data_sources[self.model].getAvailableTimes()
        
        def update(times):
            times = times[0]
            self.run_dropdown.clear()

            if self.model == "Observed":
                self.run = [ t for t in times if t.hour in [ 0, 12 ] ][-1]
            else:
                self.run = times[-1]

            for data_time in times:
                self.run_dropdown.addItem(data_time.strftime(Picker.run_format))

            self.run_dropdown.update()
            self.run_dropdown.setCurrentIndex(times.index(self.run))

        self.async_id = self.async.post(getTimes, update)

    def map_link(self, point):
        """
        Change the text of the button based on the user click.
        """
        if point is None:
            self.loc = None
            self.disp_name = None
            self.button.setText('Generate Profiles')
            self.button.setDisabled(True)
        else:
            self.loc = point #url.toString().split('/')[-1]
            if point['icao'] != "":
                self.disp_name = point['icao']
            elif point['iata'] != "":
                self.disp_name = point['iata']
            else:
                self.disp_name = point['srcid'].upper()

            self.button.setText(self.disp_name + ' | Generate Profiles')
            if self.has_connection:
                self.button.setEnabled(True) 

    @crasher(exit=False)
    def complete_name(self):
        """
        Handles what happens when the user clicks a point on the map
        """
        if self.loc is None:
            return
        else:
            self.prof_idx = []
            selected = self.profile_list.selectedItems()
            for item in selected:
                idx = self.profile_list.indexFromItem(item).row()
                if idx in self.prof_idx:
                    continue
                else:
                    self.prof_idx.append(idx)

            fcst_hours = self.data_sources[self.model].getForecastHours()

            if fcst_hours != [0] and len(self.prof_idx) > 0 or fcst_hours == [0]:
                self.prof_idx.sort()
                self.skewApp()

    def get_model(self, index):
        """
        Get the user's model selection
        """
        self.model = self.model_dropdown.currentText()

        self.update_run_dropdown()
        self.async.join(self.async_id)

        self.update_list()
        self.view.setDataSource(self.data_sources[self.model], self.run)

    def get_run(self, index):
        """
        Get the user's run hour selection for the model
        """
        self.run = date.datetime.strptime(self.run_dropdown.currentText(), Picker.run_format)
        self.view.setCurrentTime(self.run)
        self.update_list()

    def get_map(self):
        """
        Get the user's map selection
        """
        proj = {'Northern Hemisphere':'npstere', 'Tropics':'merc', 'Southern Hemisphere':'spstere'}[self.map_dropdown.currentText()]
        self.view.setProjection(proj)

    def save_view(self):
        """
        Save the map projection to the config file
        """
        self.view.saveProjection(self.config)

    def select_all(self):
        items = self.profile_list.count()
        if not self.select_flag:
            for i in range(items):
                if self.profile_list.item(i).text() in self.prof_idx:
                    continue
                else:
                    self.profile_list.item(i).setSelected(True)
            self.all_profs.setText("Deselect All")
            self.select_flag = True
        else:
            for i in range(items):
                self.profile_list.item(i).setSelected(False)
            self.all_profs.setText("Select All")
            self.select_flag = False

    def skewApp(self, filename=None):
        """
        Create the SPC style SkewT window, complete with insets
        and magical funtimes.
        :return:
        """

        profs = []
        dates = []
        failure = False

        exc = ""

        ## if the profile is an archived file, load the file from
        ## the hard disk
        if filename is not None:
            model = "Archive"
            prof_collection, stn_id = self.loadArchive(filename)
            disp_name = stn_id
            prof_idx = range(len(dates))

            run = prof_collection.getCurrentDate()
            fhours = None
            observed = True
        else:
        ## otherwise, download with the data thread
            prof_idx = self.prof_idx
            disp_name = self.disp_name
            run = self.run
            model = self.model
            observed = self.data_sources[model].isObserved()

            if self.data_sources[model].getForecastHours() == [ 0 ]:
                prof_idx = [ 0 ]

            ret = loadData(self.data_sources[model], self.loc, run, prof_idx)

            if isinstance(ret[0], Exception):
                exc = ret[0]
                failure = True
            else:
                prof_collection = ret[0]

            fhours = [ "F%03d" % fh for idx, fh in enumerate(self.data_sources[self.model].getForecastHours()) if idx in prof_idx ]

        if not failure:
            prof_collection.setMeta('model', model)
            prof_collection.setMeta('run', run)
            prof_collection.setMeta('loc', disp_name)
            prof_collection.setMeta('fhour', fhours)
            prof_collection.setMeta('observed', observed)

            if not observed:
                # If it's not an observed profile, then generate profile objects in background.
                prof_collection.setAsync(Picker.async)

            if self.skew is None:
                # If the SPCWindow isn't shown, set it up.
                self.skew = SPCWindow(parent=self.parent(), cfg=self.config)
                self.skew.closed.connect(self.skewAppClosed)
                self.skew.show()

            self.focusSkewApp()
            self.skew.addProfileCollection(prof_collection)

    def skewAppClosed(self):
        """
        Handles the user closing the SPC window.
        """
        self.skew = None

    def focusSkewApp(self):
        if self.skew is not None:
            self.skew.activateWindow()
            self.skew.setFocus()
            self.skew.raise_()

    def loadArchive(self, filename):
        """
        Get the archive sounding based on the user's selections.
        Also reads it using the Decoders and gets both the stationID and the profile objects
        for that archive sounding.
        """

        try:
            dec = SPCDecoder(filename)
        except Exception as e:
            try:
                dec = BufDecoder(filename)
            except:
                raise IOError("Could not figure out the format of '%s'!" % filename)

        profs = dec.getProfiles()
        stn_id = dec.getStnId()

        return profs, stn_id

    def hasConnection(self):
        return self.has_connection
Esempio n. 2
0
class Picker(QWidget):
    date_format = "%Y-%m-%d %HZ"
    run_format = "%d %B %Y / %H%M UTC"

    async_obj = AsyncThreads(2, debug)

    def __init__(self, config, **kwargs):
        """
        Construct the main picker widget: a means for interactively selecting
        which sounding profile(s) to view.
        """

        super(Picker, self).__init__(**kwargs)
        self.data_sources = data_source.loadDataSources()
        self.config = config
        self.skew = None

        # default the sounding location to OUN because obviously I'm biased
        self.loc = None
        # the index of the item in the list that corresponds
        # to the profile selected from the list
        self.prof_idx = []
        # set the default profile type to Observed
        self.model = "Observed"
        # this is the default model initialization time
        self.all_times = sorted(
            self.data_sources[self.model].getAvailableTimes())
        self.run = [t for t in self.all_times if t.hour in [0, 12]][-1]

        urls = data_source.pingURLs(self.data_sources)
        self.has_connection = any(urls.values())
        self.strictQC = True

        # initialize the UI
        self.__initUI()

    def __initUI(self):
        """
        Initialize the main user interface.
        """

        # Give the main window a layout. Using GridLayout
        # in order to control placement of objects.

        self.layout = QGridLayout()
        self.setLayout(self.layout)

        self.view = self.create_map_view()
        self.view.hasInternet(self.has_connection)

        self.button = QPushButton('Generate Profiles')
        self.button.clicked.connect(self.complete_name)
        self.button.setDisabled(True)

        self.select_flag = False
        self.all_profs = QPushButton("Select All")
        self.all_profs.clicked.connect(self.select_all)
        self.all_profs.setDisabled(True)

        self.save_view_button = QPushButton('Save Map View as Default')
        self.save_view_button.clicked.connect(self.save_view)

        self.profile_list = QListWidget()
        self.profile_list.setSelectionMode(QAbstractItemView.MultiSelection)
        self.profile_list.setDisabled(True)

        # create subwidgets that will hold the individual GUI items
        self.left_data_frame = QWidget()
        self.right_map_frame = QWidget()
        # set the layouts for these widgets
        self.left_layout = QVBoxLayout()
        self.right_layout = QGridLayout()  # QVBoxLayout()
        self.left_data_frame.setLayout(self.left_layout)

        self.right_map_frame.setLayout(self.right_layout)
        #print(self.run)
        self.cal = Calendar(self, dt_avail=self.run)
        self.cal.setSelectedDate(self.run)
        self.cal.clicked.connect(self.update_from_cal)
        self.cal_date = self.cal.selectedDate()
        filt_times = [
            t for t in self.all_times
            if t.day == self.cal_date.day() and t.year == self.cal_date.year()
            and t.month == self.cal_date.month()
        ]

        # create dropdown menus
        models = sorted(self.data_sources.keys())
        self.model_dropdown = self.dropdown_menu(models)
        self.model_dropdown.setCurrentIndex(models.index(self.model))

        # Setup the map
        projs = [('npstere', 'Northern Hemisphere'), ('merc', 'Tropics'),
                 ('spstere', 'Southern Hemisphere')]
        if ('map', 'proj') in self.config:
            proj = self.config['map', 'proj']
            proj_idx = list(zip(*projs))[0].index(proj)
        else:
            proj_idx = 0
        self.map_dropdown = self.dropdown_menu(list(zip(*projs))[1])
        self.map_dropdown.setCurrentIndex(proj_idx)

        # Set up the run dropdown box and select the correct index
        self.run_dropdown = self.dropdown_menu(
            [t.strftime(Picker.run_format) for t in filt_times])
        try:
            self.run_dropdown.setCurrentIndex(filt_times.index(self.run))
        except ValueError as e:
            logging.error("Run dropdown is missing its times ... ?")
            logging.exception(e)
        # connect the click actions to functions that do stuff
        self.model_dropdown.activated.connect(self.get_model)
        self.map_dropdown.activated.connect(self.get_map)
        self.run_dropdown.activated.connect(self.get_run)

        # Create text labels to describe the various menus
        self.type_label = QLabel("Select Sounding Source")
        self.date_label = QLabel("Select Forecast Time")
        self.map_label = QLabel("Select Map Area")
        self.run_label = QLabel("Select Cycle")
        self.date_label.setDisabled(True)

        # add the elements to the left side of the GUI
        self.left_layout.addWidget(self.type_label)
        self.left_layout.addWidget(self.model_dropdown)
        self.left_layout.addWidget(self.run_label)
        self.left_layout.addWidget(self.cal)
        self.left_layout.addWidget(self.run_dropdown)
        self.left_layout.addWidget(self.date_label)
        self.left_layout.addWidget(self.profile_list)
        self.left_layout.addWidget(self.all_profs)
        self.left_layout.addWidget(self.button)

        # add the elements to the right side of the GUI
        self.right_layout.setColumnMinimumWidth(0, 500)
        self.right_layout.addWidget(self.map_label, 0, 0, 1, 1)
        self.right_layout.addWidget(self.save_view_button, 0, 1, 1, 1)
        self.right_layout.addWidget(self.map_dropdown, 1, 0, 1, 2)
        self.right_layout.addWidget(self.view, 2, 0, 1, 2)

        # add the left and right sides to the main window
        self.layout.addWidget(self.left_data_frame, 0, 0, 1, 1)
        self.layout.addWidget(self.right_map_frame, 0, 1, 1, 1)
        self.left_data_frame.setMaximumWidth(280)

    def create_map_view(self):
        """
        Create a clickable map that will be displayed in the GUI.
        Will eventually be re-written to be more general.

        Returns
        -------
        view : QWebView object
        """

        # minimumWidth=800, minimumHeight=500,
        view = MapWidget(self.data_sources[self.model],
                         self.run,
                         self.async_obj,
                         cfg=self.config)
        view.clicked.connect(self.map_link)

        return view

    def dropdown_menu(self, item_list):
        """
        Create and return a dropdown menu containing items in item_list.

        Params
        ------
        item_list : a list of strings for the contents of the dropdown menu

        Returns
        -------
        dropdown : a QtGui.QComboBox object
        """
        logging.debug("Calling full_gui.dropdown_menu")
        # create the dropdown menu
        dropdown = QComboBox()
        # set the text as editable so that it can have centered text
        dropdown.setEditable(True)
        dropdown.lineEdit().setReadOnly(True)
        dropdown.lineEdit().setAlignment(Qt.AlignCenter)

        # add each item in the list to the dropdown
        for item in item_list:
            dropdown.addItem(item)

        return dropdown

    def update_from_cal(self, dt, updated_model=False):
        """
        Update the dropdown list and the forecast times list if a new date
        is selected in the calendar app.
        """

        self.update_run_dropdown(updated_model=updated_model)

        self.view.setDataSource(self.data_sources[self.model], self.run)
        self.update_list()

    def update_list(self):
        """
        Update the list with new forecast times.

        :param list:
        :return:
        """
        logging.debug("Calling full_gui.update_list")
        if self.select_flag:
            self.select_all()
        self.profile_list.clear()
        self.prof_idx = []
        timelist = []

        # If the run is outside the available times.
        if self.run == date.datetime(1700, 1, 1, 0, 0, 0):
            self.profile_list.setDisabled(True)
            self.all_profs.setDisabled(True)
            self.date_label.setDisabled(True)
        else:
            fcst_hours = self.data_sources[self.model].getForecastHours()
            if fcst_hours != [0]:
                self.profile_list.setEnabled(True)
                self.all_profs.setEnabled(True)
                self.date_label.setEnabled(True)
                for fh in fcst_hours:
                    fcst_str = (self.run + date.timedelta(hours=fh)).strftime(
                        Picker.date_format) + "   (F%03d)" % fh
                    timelist.append(fcst_str)
            else:
                self.profile_list.setDisabled(True)
                self.all_profs.setDisabled(True)
                self.date_label.setDisabled(True)

        # Loop throught the timelist and each string to the list
        for item in timelist:
            self.profile_list.addItem(item)

        self.profile_list.update()
        self.all_profs.setText("Select All")
        self.select_flag = False

    def update_datasource_dropdown(self, selected="Observed"):
        """
        Updates the dropdown menu that contains the available
        data sources
        :return:
        """
        logging.debug("Calling full_gui.update_datasource_dropdown")

        for i in range(self.model_dropdown.count()):
            self.model_dropdown.removeItem(0)

        self.data_sources = data_source.loadDataSources()
        models = sorted(self.data_sources.keys())
        for model in models:
            self.model_dropdown.addItem(model)

        self.model_dropdown.setCurrentIndex(models.index(selected))
        self.get_model(models.index(selected))

    def update_run_dropdown(self, updated_model=False):
        """
        Updates the dropdown menu that contains the model run
        information.
        :return:
        """
        logging.debug("Calling full_gui.update_run_dropdown")

        if self.model.startswith("Local"):
            url = self.data_sources[self.model].getURLList(
                outlet="Local")[0].replace("file://", "")

            def getTimes():
                return self.data_sources[self.model].getAvailableTimes(url)
        else:

            def getTimes():
                return self.data_sources[self.model].getAvailableTimes(
                    dt=self.cal_date)

        self.cal_date = self.cal.selectedDate()

        # Function to update the times.
        def update(times):
            self.run_dropdown.clear(
            )  # Clear all of the items from the dropdown
            times = times[0]
            time_span = self.data_sources[self.model].updateTimeSpan()
            for outlet in time_span:
                if np.asarray(outlet).all() == None:
                    span = True
                else:
                    dt_earliest = outlet[0]
                    dt_avail = outlet[1]
                    span = False
            if span is True and len(times) > 0:
                dt_avail = max(times)
                dt_earliest = min(times)
            self.cal.setLatestAvailable(dt_avail)
            self.cal.setEarliestAvailable(dt_earliest)
            self.cal_date = self.cal.selectedDate()
            self.cal.update()

            # Filter out only times for the specified date.
            filtered_times = []
            for i, data_time in enumerate(times):
                if data_time.day == self.cal_date.day(
                ) and data_time.year == self.cal_date.year(
                ) and data_time.month == self.cal_date.month():
                    self.run_dropdown.addItem(
                        data_time.strftime(Picker.run_format))
                    filtered_times.append(i)

            if len(filtered_times) > 0:
                filtered_times = np.sort(np.asarray(filtered_times))
                times = times[filtered_times.min():filtered_times.max() + 1]
                # Pick the index for which to highlight
                if self.model == "Observed":
                    try:
                        # Try to grab the 0 or 12 UTC data for this day (or 3 or 15 if before 5/1/1957)
                        if self.cal_date.toPython() >= date.datetime(
                                1957, 5, 1).date():
                            synoptic_times = [0, 12]
                        else:
                            synoptic_times = [3, 15]
                        self.run = [
                            t for t in times
                            if t.hour in synoptic_times and t.day ==
                            self.cal_date.day() and t.month == self.cal_date.
                            month() and t.year == self.cal_date.year()
                        ][-1]
                    except Exception as e:
                        logging.exception(e)
                        self.run = times[-1]
                else:
                    self.run = times[-1]
            else:
                self.run = date.datetime(1700, 1, 1, 0, 0, 0)
            self.run_dropdown.update()
            if len(filtered_times) > 0:
                self.run_dropdown.setEnabled(True)
                self.run_dropdown.setCurrentIndex(times.index(self.run))
            elif len(filtered_times) == 0:
                if self.model == "Observed":
                    string = "obs"
                else:
                    string = "runs"
                self.run_dropdown.addItem(
                    self.tr("- No " + string + " available - "))
                self.run_dropdown.setCurrentIndex(0)
                self.run_dropdown.update()
                self.run_dropdown.setEnabled(False)

        # Post the getTimes to update.  This will re-write the list of times in the dropdown box that
        # match the date selected in the calendar.
        async_id = self.async_obj.post(getTimes, update)
        self.async_obj.join(async_id)

    def map_link(self, point):
        """
        Change the text of the button based on the user click.
        """
        logging.debug("Calling full_gui.map_link")
        if point is None:
            self.loc = None
            self.disp_name = None
            self.button.setText('Generate Profiles')
            self.button.setDisabled(True)
        elif self.model == "Local WRF-ARW":
            self.loc = point
            self.disp_name = "User Selected"
            self.button.setText(self.disp_name + ' | Generate Profiles')
            self.button.setEnabled(True)
            self.areal_lon, self.areal_y = point

        else:
            self.loc = point  # url.toString().split('/')[-1]
            if point['icao'] != "":
                self.disp_name = point['icao']
            elif point['iata'] != "":
                self.disp_name = point['iata']
            else:
                self.disp_name = point['srcid'].upper()

            self.button.setText(self.disp_name + ' | Generate Profiles')
            if self.has_connection:
                self.button.setEnabled(True)

    @crasher(exit=False)
    def complete_name(self):
        """
        Handles what happens when the user clicks a point on the map
        """
        logging.debug("Calling full_gui.complete_name")
        if self.loc is None:
            return
        else:
            self.prof_idx = []
            selected = self.profile_list.selectedItems()
            for item in selected:
                idx = self.profile_list.indexFromItem(item).row()
                if idx in self.prof_idx:
                    continue
                else:
                    self.prof_idx.append(idx)

            fcst_hours = self.data_sources[self.model].getForecastHours()

            if fcst_hours != [0] and len(self.prof_idx) > 0 or fcst_hours == [
                    0
            ]:
                self.prof_idx.sort()
                n_tries = 0
                while True:
                    try:
                        self.skewApp(ntry=n_tries)
                    except data_source.DataSourceError as e1:
                        # We've run out of data sources. Uh-oh.
                        logging.exception(e1)
                        if self.skew is not None:
                            self.skew.closeIfEmpty()
                        raise IOError(
                            "No outlet found with the requested profile!")
                    except Exception as e:
                        if debug:
                            print(traceback.format_exc())
                        n_tries += 1
                        logging.exception(e)
                    else:
                        break

    def get_model(self, index):
        """
        Get the user's model selection
        """
        logging.debug("Calling full_gui.get_model")
        self.model = self.model_dropdown.currentText()

        self.update_from_cal(None, updated_model=True)

    def get_run(self, index):
        """
        Get the user's run hour selection for the model
        """
        logging.debug("Calling full_gui.get_run")
        self.run = date.datetime.strptime(self.run_dropdown.currentText(),
                                          Picker.run_format)
        self.view.setCurrentTime(self.run)
        self.update_list()

    def get_map(self):
        """
        Get the user's map selection
        """
        logging.debug("Calling full_gui.get_map")
        proj = {
            'Northern Hemisphere': 'npstere',
            'Tropics': 'merc',
            'Southern Hemisphere': 'spstere'
        }[self.map_dropdown.currentText()]
        self.view.setProjection(proj)

    def save_view(self):
        """
        Save the map projection to the config file
        """
        self.view.saveProjection(self.config)

    def select_all(self):
        logging.debug("Calling full_gui.select_all")
        items = self.profile_list.count()
        if not self.select_flag:
            for i in range(items):
                if self.profile_list.item(i).text() in self.prof_idx:
                    continue
                else:
                    self.profile_list.item(i).setSelected(True)
            self.all_profs.setText("Deselect All")
            self.select_flag = True
        else:
            for i in range(items):
                self.profile_list.item(i).setSelected(False)
            self.all_profs.setText("Select All")
            self.select_flag = False

    def skewApp(self, filename=None, ntry=0):
        logging.debug("Calling full_gui.skewApp")
        """
        Create the SPC style SkewT window, complete with insets
        and magical funtimes.
        :return:
        """

        logging.debug("Calling full_gui.skewApp")

        failure = False

        exc = ""

        # if the profile is an archived file, load the file from
        # the hard disk
        if filename is not None:
            logging.info("Trying to load file from local disk...")

            model = "Archive"
            prof_collection, stn_id = self.loadArchive(filename)
            logging.info(
                "Successfully loaded the profile collection for this file...")
            disp_name = stn_id
            observed = True
            fhours = None

            # Determine if the dataset passed was from a model or is observed
            if len(prof_collection._dates) > 1:
                prof_idx = self.prof_idx
                fhours = [
                    "F%03d" % fh for idx, fh in enumerate(self.data_sources[
                        self.model].getForecastHours()) if idx in prof_idx
                ]
                observed = False
            else:
                fhours = None
                observed = True

            run = prof_collection.getCurrentDate()

        else:
            # otherwise, download with the data thread
            logging.info("Loading a real-time data stream...")
            prof_idx = self.prof_idx
            disp_name = self.disp_name
            run = self.run
            model = self.model
            observed = self.data_sources[model].isObserved()

            if self.data_sources[model].getForecastHours() == [0]:
                prof_idx = [0]

            logging.info("Program is going to load the data...")
            ret = loadData(self.data_sources[model],
                           self.loc,
                           run,
                           prof_idx,
                           ntry=ntry)

            # failure variable makes sure the data actually exists online.
            if isinstance(ret[0], Exception):
                exc = ret[0]
                failure = True
                logging.info(
                    "There was a problem with loadData() in obtaining the data from the Internet."
                )
            else:
                logging.info("Data was found and successfully decoded!")
                prof_collection = ret[0]

            fhours = [
                "F%03d" % fh for idx, fh in enumerate(self.data_sources[
                    self.model].getForecastHours()) if idx in prof_idx
            ]

        # If the observed or model profile (not Archive) successfully loaded)
        if not failure:
            prof_collection.setMeta('model', model)
            prof_collection.setMeta('run', run)
            prof_collection.setMeta('loc', disp_name)
            prof_collection.setMeta('fhour', fhours)
            prof_collection.setMeta('observed', observed)

            if not prof_collection.getMeta('observed'):
                # If it's not an observed profile, then generate profile objects in background.
                prof_collection.setAsync(Picker.async_obj)

            if self.skew is None:
                logging.debug("Constructing SPCWindow")
                # If the SPCWindow isn't shown, set it up.
                self.skew = SPCWindow(parent=self.parent(), cfg=self.config)
                self.parent().config_changed.connect(
                    self.skew.centralWidget().updateConfig)
                self.skew.closed.connect(self.skewAppClosed)
                self.skew.show()

            logging.debug("Focusing on the SkewApp")
            self.focusSkewApp()
            logging.debug("Adding the profile collection to SPCWindow")
            self.skew.addProfileCollection(prof_collection,
                                           check_integrity=self.strictQC)
        else:
            print("There was an exception:", exc)

            raise exc

    def skewAppClosed(self):
        """
        Handles the user closing the SPC window.
        """
        self.skew = None

    def focusSkewApp(self):
        if self.skew is not None:
            self.skew.activateWindow()
            self.skew.setFocus()
            self.skew.raise_()

    def keyPressEvent(self, e):
        if e.key() == 61 or e.key() == 45:
            self.view.keyPressEvent(e)

    def loadArchive(self, filename):
        """
        Get the archive sounding based on the user's selections.
        Also reads it using the Decoders and gets both the stationID and the profile objects
        for that archive sounding.  Tries a variety of decoders available to the program.
        """
        logging.debug(
            "Looping over all decoders to find which one to use to decode User Selected file."
        )
        for decname, deccls in getDecoders().items():
            try:
                dec = deccls(filename)
                break
            except Exception as e:
                logging.exception(e)
                dec = None
                continue

        if dec is None:
            raise IOError("Could not figure out the format of '%s'!" %
                          filename)
        # Returns the set of profiles from the file that are from the "Profile" class.
        logging.debug('Get the profiles from the decoded file.')
        profs = dec.getProfiles()
        stn_id = dec.getStnId()

        return profs, stn_id

    def hasConnection(self):
        return self.has_connection

    def setStrictQC(self, val):
        self.strictQC = val