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