def data(self, index, role):
		row = index.row()
		col = index.column()
		if role == Qt.DisplayRole:
			if row < len(self.navpoints): # row is a navpoint
				navpoint = self.navpoints[row]
				if col == 0:
					return Navpoint.tstr(navpoint.type)
				elif col == 1:
					return navpoint.code
				elif col == 2:
					return navpoint.long_name
			else: # row is a parking position
				pk = self.pk_pos[row - len(self.navpoints)]
				#EarthCoords, Heading, str (gate|hangar|misc|tie-down), str list (heavy|jets|turboprops|props|helos)
				if col == 0:
					return 'PKG'
				elif col == 1:
					return pk
				elif col == 2:
					pk_info = env.airport_data.ground_net.parkingPosInfo(pk)
					txt = pk_info[2].capitalize()
					if pk_info[3] != []:
						txt += ' for ' + ', '.join(pk_info[3])
					return txt
		
		elif role == Qt.ToolTipRole:
			coords = self.coordsForRow(row)
			if env.radarPos() == None:
				return str(coords)
			else:
				distance = env.radarPos().distanceTo(coords)
				return '%s°, %s' % (env.radarPos().headingTo(coords).readTrue(), dist_str(distance))
예제 #2
0
 def toolTipText(self):
     txt = ''
     if self.social_name != None:
         txt += self.social_name + '\n'
     txt += 'Position: '
     if self.position == None:
         txt += 'unknown'
     else:
         distance = env.radarPos().distanceTo(self.position)
         if distance < nearby_dist_threshold:
             txt += 'nearby'
         else:
             txt += '%s°, %s' % (env.radarPos().headingTo(
                 self.position).readTrue(), dist_str(distance))
     return txt
	def __init__(self, parent, external, port):
		'''
		external is a host (possibly localhost) for external FGCom instance, or None for internal (child process)
		'''
		QWidget.__init__(self, parent)
		self.setupUi(self)
		client_address = some(external, 'localhost'), port
		self.settings = FgcomSettings(socket(AF_INET, SOCK_DGRAM), client_address)
		self.controller = Ticker(self.settings.send, parent=self)
		self.frequency_combo.addFrequencies([(frq, descr) for frq, descr, t in env.frequencies])
		self.frequency_combo.addFrequencies(frequencies_always_proposed)
		if external == None: # child process
			self.onOff_button.setToolTip('Internal FGCom instance using local port %d' % port)
			ad = world_navpoint_db.findClosest(env.radarPos(), types=[Navpoint.AD]).code if env.airport_data == None else settings.location_code
			self.instance = InternalFgcomInstance(port, ['--airport=%s' % ad], self)
			self.instance.started.connect(self.processHasStarted)
			self.instance.finished.connect(self.processHasStopped)
			self.onOff_button.toggled.connect(self.switchFGCom)
		else: # creating box for external instance
			self.instance = None
			self.onOff_button.setToolTip('External FGCom instance on %s:%d' % client_address)
			self.onOff_button.setChecked(True) # keep checked (tested for RDF)
			self.onOff_button.setEnabled(False)
			self.PTT_button.setEnabled(True)
			self.controller.start(fgcom_controller_ticker_interval)
		self.PTT_button.pressed.connect(lambda: self.PTT(True))
		self.PTT_button.released.connect(lambda: self.PTT(False))
		self.softVolume_tickBox.clicked.connect(self.setVolume)
		self.frequency_combo.frequencyChanged.connect(self.setFrequency)
		self.updateRDF()
		self.RDF_tickBox.toggled.connect(self.updateRDF)
		self.onOff_button.toggled.connect(self.updateRDF)
		signals.localSettingsChanged.connect(self.updateRDF)
예제 #4
0
 def start(self, traffic_count):  # overrides (but calls) parent's
     p = lambda d: env.radarPos().moved(Heading(d, True), 1.5 * settings.
                                        map_range)
     env.ATCs.updateATC('N', p(360), 'North', None)
     env.ATCs.updateATC('S', p(180), 'South', None)
     env.ATCs.updateATC('E', p(90), 'East', None)
     env.ATCs.updateATC('W', p(270), 'West', None)
     SoloSessionManager.start(self, traffic_count)
예제 #5
0
 def indicatePoint(self, coords):
     if env.pointOnMap(coords):
         if not self.lockPanZoom_button.isChecked(
         ) and self.autoCentre_action.isChecked():
             self.scopeView.moveToShow(coords)
         self.scene.point_indicator.indicate(coords)
     else:  # point is off map
         hdg = env.radarPos().headingTo(coords)
         signals.statusBarMsg.emit('Point is off map to the %s' %
                                   hdg.approxCardinal(True))
예제 #6
0
 def spawnNewUncontrolledAircraft(self):
     rndpos = env.radarPos().moved(Heading(randint(1, 360), True),
                                   uniform(10, .8 * settings.radar_range))
     rndalt = StdPressureAlt(randint(1, 10) * 1000)
     if self.airbornePositionFullySeparated(rndpos, rndalt):
         acft_type = choice(self.uncontrolled_aircraft_types)
         params = SoloParams(Status(Status.AIRBORNE), rndpos, rndalt,
                             Heading(randint(1, 360), True),
                             cruise_speed(acft_type))
         params.XPDR_code = settings.uncontrolled_VFR_XPDR_code
         new_acft = self.mkAiAcft(acft_type, params, goal=None)
         if new_acft != None:
             self.uncontrolled_traffic.append(new_acft)
예제 #7
0
 def new_arrival_APP(self, entry_point):
     type_choice = self.parkable_aircraft_types if self.parkable_aircraft_types != [] else self.playable_aircraft_types
     # must be landable too
     rwy_choice = [
         rwy for rwy in env.airport_data.allRunways()
         if rwy.use_for_arrivals
     ]
     if rwy_choice == []:
         rwy_choice = env.airport_data.allRunways()
     pop_all(
         type_choice,
         lambda t: all(not rwy.acceptsAcftType(t) for rwy in rwy_choice))
     if type_choice == []:
         return None
     acft_type = choice(type_choice)
     ils = any(rwy.hasILS() for rwy in
               rwy_choice) and random() >= settings.solo_ILSvsVisual_balance
     if entry_point == None:
         hdg = Heading(randint(1, 360), True)
         pos = env.radarPos().moved(
             hdg.opposite(),
             uniform(.33 * settings.radar_range,
                     .75 * settings.radar_range))
     else:
         pos = entry_point.coordinates
         hdg = pos.headingTo(env.radarPos())
     alt = StdPressureAlt.fromFL(
         10 * randint(settings.solo_APP_ceiling_FL_min // 10,
                      settings.solo_APP_ceiling_FL_max // 10))
     if not self.airbornePositionFullySeparated(pos, alt):
         return None
     ias = restrict_speed_under_ceiling(
         cruise_speed(acft_type), alt,
         StdPressureAlt.fromFL(150))  # 5000-ft anticipation
     params = SoloParams(Status(Status.AIRBORNE), pos, alt, hdg, ias)
     params.XPDR_code = env.nextSquawkCodeAssignment(XPDR_range_IFR_ARR)
     return self.mkAiAcft(acft_type, params, ils)
	def __init__(self, socket, address):
		self.socket = socket
		self.address = address
		try:
			self.frq = env.frequencies[0][0]
		except IndexError:
			self.frq = frequencies_always_proposed[0][0]
		self.ptt = False  # "push to talk"
		self.vol = 1      # output volume
		pos = env.radarPos()
		# packet format has 3 slots to fill: PTT, frq, vol
		self.packet_format = 'PTT=%d'
		self.packet_format += ',LAT=%f,LON=%f,ALT=%f' % (pos.lat, pos.lon, env.elevation(pos))
		self.packet_format += ',COM1_FRQ=%s,COM2_FRQ=121.850'
		self.packet_format += ',OUTPUT_VOL=%f,SILENCE_THD=-60'
		self.packet_format += ',CALLSIGN=%s' % FGCom_callsign()
예제 #9
0
 def handoverGuard(self, acft, atc):
     if acft.coords().distanceTo(
             env.radarPos()) <= settings.solo_CTR_range_dist:
         return 'Aircraft is still in your airspace.'
     # Check if expected receiver
     dist_key_expected = lambda atc: env.ATCs.getATC(
         atc).position.distanceTo(acft.goal.coordinates)
     expected_receiver = min(env.ATCs.knownATCs(), key=dist_key_expected)
     if atc != expected_receiver:
         return 'Destination is %s; hand over to %s.' % (acft.goal,
                                                         expected_receiver)
     # Check if closest ATC
     dist_key_closest = lambda atc: env.ATCs.getATC(
         atc).position.distanceTo(acft.params.position)
     if atc != min(env.ATCs.knownATCs(), key=dist_key_closest):
         return 'ACFT not near enough this neighbour\'s airspace.'
 def __init__(self, parent=None):
     QDialog.__init__(self, parent)
     self.setupUi(self)
     self.installEventFilter(RadioKeyEventFilter(self))
     self.magneticDeclination_infoLabel.setText(
         some(env.readDeclination(), 'N/A'))
     self.radarPosition_infoLabel.setText(str(env.radarPos()))
     if env.airport_data == None:
         self.airport_tab.setEnabled(False)
     else:
         self.airportName_infoLabel.setText(env.locationName())
         self.airportElevation_infoLabel.setText(
             '%.1f ft' % env.airport_data.field_elevation)
         table_model = RwyInfoTableModel(self)
         self.runway_tableView.setModel(table_model)
         for i in range(table_model.columnCount()):
             self.runway_tableView.resizeColumnToContents(i)
예제 #11
0
 def new_arrival_TWR(self):
     acft_type = choice(
         self.parkable_aircraft_types if self.parkable_aircraft_types != []
         else self.playable_aircraft_types)
     ils = random() >= settings.solo_ILSvsVisual_balance
     rwy_ok = lambda rwy: rwy.acceptsAcftType(acft_type) and (not ils or rwy
                                                              .hasILS())
     rwy = rnd_rwy([
         rwy
         for rwy in env.airport_data.allRunways() if rwy.use_for_arrivals
     ], rwy_ok)
     if rwy == None:
         return None
     dthr = rwy.threshold(dthr=True)
     try:
         furthest = max([
             dthr.distanceTo(acft.params.position)
             for acft in self.controlled_traffic if acft.isInboundGoal()
         ])
         dist = max(
             furthest + uniform(1, 2) *
             distance_flown(TTF_separation, cruise_speed(acft_type)),
             settings.solo_TWR_range_dist)
     except ValueError:
         dist = settings.solo_TWR_range_dist / 2
     if dist > min(settings.solo_TWR_range_dist * 1.5,
                   settings.radar_range - 10):
         return None  # to protect from creating aircraft out of radar range
     status = Status(Status.LANDING, arg=rwy.name) if ils else Status(
         Status.AIRBORNE)
     hdg = rwy.appCourse()
     alt = GS_alt(env.elevation(dthr), rwy.param_FPA,
                  max(2, dist if ils else dist - 2))
     params = SoloParams(status,
                         env.radarPos().moved(hdg.opposite(), dist), alt,
                         hdg, touch_down_speed(acft_type))
     params.XPDR_code = env.nextSquawkCodeAssignment(XPDR_range_IFR_ARR)
     acft = self.mkAiAcft(acft_type, params, ils)
     acft.instructions.append(
         Instruction(Instruction.EXPECT_RWY, arg=rwy.name))
     if ils:
         acft.instructions.append(Instruction(Instruction.CLEARED_APP))
     return acft
예제 #12
0
 def start(self, traffic_count):  # overrides (but calls) parent's
     self.parkable_aircraft_types = \
      [t for t in self.playable_aircraft_types if env.airport_data.ground_net.parkingPositions(acftType=t) != []]
     # Start errors (cancels start)
     if settings.solo_role_GND and self.parkable_aircraft_types == []:
         QMessageBox.critical(
             self.gui, 'Insufficient ground data',
             'You cannot play solo GND with no parkable ACFT type.')
         return
     # Start warnings
     if (settings.solo_role_GND or settings.solo_role_TWR
         ) and settings.radar_signal_floor_level > max(
             0, env.airport_data.field_elevation):
         QMessageBox.warning(
             self.gui, 'Radar visibility warning',
             'You are playing solo TWR/GND with radar signal floor above surface.'
         )
     if settings.solo_role_DEP and settings.solo_ARRvsDEP_balance == 0:
         QMessageBox.warning(self.gui, 'No departures warning',
                             'You are playing DEP with no departures set.')
     if settings.solo_role_APP and settings.solo_ARRvsDEP_balance == 1:
         QMessageBox.warning(self.gui, 'No arrivals warning',
                             'You are playing APP with no arrivals set.')
     # Set up ATC neighbours
     env.ATCs.updateATC('CTR', env.radarPos(), 'En-route control centre',
                        None)
     if settings.solo_role_GND:
         env.ATCs.updateATC('Ramp', None, 'Apron/gate services', None)
     else:
         env.ATCs.updateATC('GND', None, 'Airport ground', None)
     if not settings.solo_role_TWR:
         env.ATCs.updateATC('TWR', None, 'Tower', None)
     if not settings.solo_role_APP:
         env.ATCs.updateATC('APP', None, 'Approach', None)
     if not settings.solo_role_DEP:
         env.ATCs.updateATC('DEP', None, 'Departure', None)
     SoloSessionManager.start(self, traffic_count)
예제 #13
0
def inTWRrange(params):
    return params.position.distanceTo(env.radarPos()) <= settings.solo_TWR_range_dist \
     and params.altitude.diff(StdPressureAlt.fromFL(settings.solo_TWR_ceiling_FL)) < 0
예제 #14
0
    def run(self):
        ## PREPARING QUERY
        pos = env.radarPos()
        qdict = {
            'username': settings.MP_social_name,
            'lon': pos.lon,
            'lat': pos.lat,
            'range': some(settings.ORSX_handover_range, settings.radar_range),
            'xmlVersion': '1.0',
            'contacts': ','.join(
                acft.identifier for acft in
                env.radar.contacts())  # should this be all FGMS connections?
        }
        if settings.publicised_frequency != None:
            qdict['frequency'] = str(settings.publicised_frequency)
        server_response = server_query('getFlightplans', qdict)
        ## USING RESPONSE
        if server_response != None:
            try:
                ww_root = ElementTree.fromstring(server_response)
            except ElementTree.ParseError as parse_error:
                print('Parse error in SX server data: %s' % parse_error)
                return
            new_ATCs = []

            # ATCs first
            for ww_atc in ww_root.find('atcsInRange').iter(
                    'atc'):  # NOTE the server sends the full list each time
                atc = ATC(ww_atc.find('callsign').text)
                atc.social_name = ww_atc.find('username').text
                atc.position = EarthCoords(float(ww_atc.find('lat').text),
                                           float(ww_atc.find('lon').text))
                ww_frq = ww_atc.find('frequency').text
                try:
                    atc.frequency = CommFrequency(ww_frq)
                except ValueError:
                    atc.frequency = None
                new_ATCs.append(atc)
            self.ATCs_on_last_run = new_ATCs

            # Then strip data (contact claims and handover)
            for ww_flightplan in ww_root.iter(
                    'flightplan'
            ):  # NOTE the server only sends those when something changes
                ww_header = ww_flightplan.find('header')
                ww_callsign = ww_header.find('callsign').text
                ww_owner = ww_header.find('owner').text
                if ww_owner == None:
                    if ww_callsign in self.current_contact_claims:
                        del self.current_contact_claims[ww_callsign]
                else:
                    self.current_contact_claims[ww_callsign] = ww_owner

                if ww_header.find(
                        'handover'
                ).text == settings.session_manager.myCallsign(
                ):  # RECEIVE A STRIP!
                    strip = Strip()
                    strip.writeDetail(received_from_detail, ww_owner)
                    strip.writeDetail(
                        assigned_SQ_detail,
                        ck_int(ww_header.find('squawk').text, base=8))
                    strip.writeDetail(assigned_altitude_detail,
                                      ww_header.find('assignedAlt').text)
                    # Ignored from WW header above: <flags>, <assignedRunway>, <assignedRoute>, <status>, <flight>
                    # Ignored from WW data below: <fuelTime>; used with ulterior motive: <pilot>
                    ww_data = ww_flightplan.find('data')
                    # ATC-pie hides a token in <pilot>, wake turb. on its left and callsign to its right
                    # e.g. <pilot>M__ATC-pie__X-FOO</pilot> for M turb. and X-FOO strip callsign
                    # If the token is absent, we know the strip is from OpenRadar
                    hidden_tokens = some(ww_data.find('pilot').text,
                                         '').split(ATCpie_hidden_string,
                                                   maxsplit=1)
                    if len(
                            hidden_tokens
                    ) == 1:  # hidden marker NOT present; previous strip editor was OpenRadar
                        strip.writeDetail(FPL.CALLSIGN, ww_callsign)
                    else:  # recognise strip edited with ATC-pie
                        strip.writeDetail(FPL.WTC, hidden_tokens[0])
                        strip.writeDetail(FPL.CALLSIGN, hidden_tokens[1])
                    strip.writeDetail(FPL.FLIGHT_RULES,
                                      ww_data.find('type').text)
                    strip.writeDetail(FPL.ACFT_TYPE,
                                      ww_data.find('aircraft').text)
                    strip.writeDetail(FPL.ICAO_DEP,
                                      ww_data.find('departure').text)
                    strip.writeDetail(FPL.ICAO_ARR,
                                      ww_data.find('destination').text)
                    strip.writeDetail(FPL.ROUTE, ww_data.find('route').text)
                    strip.writeDetail(FPL.CRUISE_ALT,
                                      ww_data.find('cruisingAlt').text)
                    spd = ck_int(ww_data.find('trueAirspeed').text)
                    if spd != None:
                        strip.writeDetail(FPL.TAS, Speed(spd))
                    strip.writeDetail(FPL.COMMENTS,
                                      ww_data.find('remarks').text)
                    # Possibly ignored details (OpenRadar confuses FPLs and strips): DEP time, EET, alt. AD, souls [*]
                    signals.receiveStrip.emit(strip)
                    send_update(ww_callsign, strip)  # Acknowledge strip
예제 #15
0
def map_loc_str(pos):
    if env.pointOnMap(pos):
        return ' near %s' % env.navpoints.findClosest(pos)
    else:
        return ' far %s' % env.radarPos().headingTo(pos).approxCardinal(True)
예제 #16
0
    def generateAircraftAndStrip(self):
        start_angle = uniform(0, 360)
        start_pos = env.radarPos().moved(Heading(start_angle, True),
                                         settings.solo_CTR_range_dist)
        end_pos = env.radarPos().moved(
            Heading(start_angle + 90 + uniform(1, 179), True),
            settings.solo_CTR_range_dist)
        transit_hdg = start_pos.headingTo(end_pos)
        dep_ad = world_navpoint_db.findClosest(env.radarPos().moved(transit_hdg.opposite(), \
          uniform(1.2 * settings.map_range, 5000)), types=[Navpoint.AD])
        dest_ad = world_navpoint_db.findClosest(env.radarPos().moved(transit_hdg, \
          uniform(1.2 * settings.map_range, 5000)), types=[Navpoint.AD])
        if env.pointOnMap(dep_ad.coordinates) or env.pointOnMap(
                dest_ad.coordinates):
            return None, None

        candidate_midpoints = [p for code in settings.solo_CTR_routing_points \
          for p in env.navpoints.findAll(code, types=[Navpoint.NDB, Navpoint.VOR, Navpoint.FIX]) \
          if start_pos.distanceTo(p.coordinates) < start_pos.distanceTo(end_pos)]
        midpoint = None if candidate_midpoints == [] else choice(
            candidate_midpoints)

        FLd10 = randint(settings.solo_CTR_floor_FL // 10,
                        settings.solo_CTR_ceiling_FL // 10)
        if settings.solo_CTR_semi_circular_rule == SemiCircRule.E_W and (FLd10 % 2 == 0) != (transit_hdg.magneticAngle() >= 180) \
         or settings.solo_CTR_semi_circular_rule == SemiCircRule.N_S and (FLd10 % 2 == 1) != (90 <= transit_hdg.magneticAngle() < 270):
            FLd10 += 1
            if 10 * FLd10 > settings.solo_CTR_ceiling_FL:
                return None, None
        p_alt = StdPressureAlt.fromFL(10 * FLd10)
        if not self.airbornePositionFullySeparated(start_pos, p_alt):
            return None, None
        acft_type = choice(self.playable_aircraft_types)
        hdg = start_pos.headingTo(some(midpoint, dest_ad).coordinates)
        params = SoloParams(Status(Status.AIRBORNE), start_pos, p_alt, hdg,
                            cruise_speed(acft_type))
        params.XPDR_code = env.nextSquawkCodeAssignment(XPDR_range_IFR_transit)
        new_acft = self.mkAiAcft(acft_type, params, dest_ad)
        dist_key = lambda atc: env.ATCs.getATC(atc).position.distanceTo(
            start_pos)
        received_from = min(env.ATCs.knownATCs(), key=dist_key)

        strip = Strip()
        strip.writeDetail(FPL.CALLSIGN, new_acft.identifier)
        strip.writeDetail(FPL.ACFT_TYPE, new_acft.aircraft_type)
        strip.writeDetail(FPL.WTC, wake_turb_cat(new_acft.aircraft_type))
        strip.writeDetail(FPL.FLIGHT_RULES, 'IFR')
        strip.writeDetail(FPL.ICAO_DEP, dep_ad.code)
        strip.writeDetail(FPL.ICAO_ARR, dest_ad.code)
        strip.writeDetail(FPL.CRUISE_ALT,
                          env.readStdAlt(new_acft.params.altitude))
        strip.writeDetail(assigned_altitude_detail,
                          strip.lookup(FPL.CRUISE_ALT))
        strip.writeDetail(assigned_SQ_detail, new_acft.params.XPDR_code)
        strip.writeDetail(received_from_detail, received_from)
        if midpoint != None:
            strip.insertRouteWaypoint(midpoint)

        new_acft.instructions.append(
            Instruction(Instruction.FOLLOW_ROUTE,
                        arg=strip.lookup(parsed_route_detail).dup()))
        return new_acft, strip
예제 #17
0
 def __init__(self, parent=None):
     QWidget.__init__(self, parent)
     self.setupUi(self)
     self.lockPanZoom_button.setIcon(QIcon(IconFile.button_lockRadar))
     self.mouse_info.clear()
     self.scene = RadarScene(self)
     self.scopeView.setScene(self.scene)
     self.courseLineFlyTime_edit.setValue(self.scene.speedMarkCount())
     self.last_airfield_clicked = None
     self.LDG_disp_actions = {}
     # BG IMAGES menu
     self.rebuildImgToggleMenu()
     # Nav menu
     nav_menu = QMenu()
     self._addMenuToggleAction(nav_menu, 'Navaids', True,
                               self.scene.layers[Layer.NAV_AIDS].setVisible)
     self._addMenuToggleAction(
         nav_menu, 'Fixes', False,
         self.scene.layers[Layer.NAV_FIXES].setVisible)
     self._addMenuToggleAction(
         nav_menu, 'RNAV points', False,
         self.scene.layers[Layer.RNAV_POINTS].setVisible)
     nav_menu.addSeparator()
     self._addMenuToggleAction(
         nav_menu, 'Airfields', False,
         self.scene.layers[Layer.NAV_AIRFIELDS].setVisible)
     self.nav_menuButton.setMenu(nav_menu)
     # AD menu
     AD_menu = QMenu()
     self._addMenuToggleAction(AD_menu, 'Ground routes', False,
                               self.scene.showGroundNetworks)
     self._addMenuToggleAction(AD_menu, 'Taxiway names', False,
                               self.scene.showTaxiwayNames)
     self._addMenuToggleAction(AD_menu, 'Highlight TWYs under mouse', True,
                               self.scene.highlightEdgesOnMouseover)
     self._addMenuToggleAction(AD_menu, 'RWY names always visible', False,
                               self.scene.setRunwayNamesAlwaysVisible)
     AD_menu.addSeparator()
     self._addMenuToggleAction(
         AD_menu, 'Parking positions', True,
         self.scene.layers[Layer.PARKING_POSITIONS].setVisible)
     self._addMenuToggleAction(
         AD_menu, 'Holding lines', False,
         self.scene.layers[Layer.HOLDING_LINES].setVisible)
     self._addMenuToggleAction(
         AD_menu, 'Taxiway centre lines', False,
         self.scene.layers[Layer.TAXIWAY_LINES].setVisible)
     self._addMenuToggleAction(
         AD_menu, 'Other objects', True,
         self.scene.layers[Layer.AIRPORT_OBJECTS].setVisible)
     self.AD_menuButton.setMenu(AD_menu)
     # LDG menu
     if env.airport_data == None:
         self.LDG_menuButton.setEnabled(False)
     else:
         LDG_menu = QMenu()
         self.syncLDG_action = self._addMenuToggleAction(
             LDG_menu, 'Sync with arrival runway selection', True,
             self.setSyncLDG)
         for rwy in env.airport_data.allRunways(
                 sortByName=True
         ):  # all menu options set to False below; normally OK at start
             txt = 'RWY %s' % rwy.name
             if rwy.ILS_cat != None:
                 txt += ' (%s)' % rwy.ILS_cat
             action = self._addMenuToggleAction(
                 LDG_menu,
                 txt,
                 False,
                 lambda b, r=rwy.name: self.scene.showLandingHelper(r, b))
             action.triggered.connect(
                 lambda b: self.syncLDG_action.setChecked(False)
             )  # cancel sync when one is set manually (triggered)
             self.LDG_disp_actions[rwy.name] = action
         LDG_menu.addSeparator()
         self._addMenuToggleAction(LDG_menu, 'Slope altitudes', True,
                                   self.scene.showSlopeAltitudes)
         self._addMenuToggleAction(LDG_menu, 'LOC interception cones',
                                   False, self.scene.showInterceptionCones)
         self.LDG_menuButton.setMenu(LDG_menu)
     # ACFT menu
     ACFT_menu = QMenu()
     self._addMenuToggleAction(ACFT_menu, 'Filter out unlinked GND modes',
                               False,
                               lambda b: self.scene.showGndModes(not b))
     ACFT_menu.addSeparator()
     self._addMenuToggleAction(ACFT_menu,
                               'Selected ACFT course/assignments', True,
                               self.scene.showSelectionAssignments)
     self._addMenuToggleAction(ACFT_menu, 'All courses/vectors', False,
                               self.scene.showVectors)
     self._addMenuToggleAction(ACFT_menu, 'All routes', False,
                               self.scene.showRoutes)
     ACFT_menu.addSeparator()
     self._addMenuToggleAction(ACFT_menu, 'Unlinked radar tags', True,
                               self.scene.showUnlinkedTags)
     self._addMenuToggleAction(ACFT_menu, 'Separation rings', False,
                               self.scene.showSeparationRings)
     self.ACFT_menuButton.setMenu(ACFT_menu)
     # OPTIONS menu
     options_menu = QMenu()
     self.autoCentre_action = self._addMenuToggleAction(
         options_menu, 'Centre on indications', False, None)
     self._addMenuToggleAction(
         options_menu, 'Show custom labels', True,
         self.scene.layers[Layer.CUSTOM_LABELS].setVisible)
     self.showRdfLine_action = self._addMenuToggleAction(
         options_menu, 'Show RDF line', False, self.scene.showRdfLine)
     options_menu.addSeparator()
     drawAirport_action = QAction('Draw additional airport...', self)
     drawAirport_action.triggered.connect(self.drawAdditionalAirport)
     resetAirports_action = QAction('Reset drawn airports', self)
     resetAirports_action.triggered.connect(self.scene.resetAirportItems)
     options_menu.addAction(drawAirport_action)
     options_menu.addAction(resetAirports_action)
     self.options_menuButton.setMenu(options_menu)
     # Other actions and signals
     self.lockPanZoom_button.toggled.connect(self.lockRadar)
     self.courseLineFlyTime_edit.valueChanged.connect(
         self.scene.setSpeedMarkCount)
     self.scene.mouseInfo.connect(self.mouse_info.setText)
     self.scene.addRemoveRouteNavpoint.connect(
         self.addRemoveRouteNavpointToSelection)
     self.scene.imagesRedrawn.connect(self.rebuildImgToggleMenu)
     self.zoomLevel_slider.valueChanged.connect(self.changeZoomLevel)
     self.scopeView.zoom_signal.connect(self.zoom)
     # External signal connections below. CAUTION: these must all be disconnected on widget deletion
     signals.selectionChanged.connect(self.mouse_info.clear)
     signals.runwayUseChanged.connect(self.updateLdgMenuAndDisplay)
     signals.localSettingsChanged.connect(self.updateRdfMenuAction)
     signals.navpointClick.connect(self.setLastAirfieldClicked)
     signals.indicatePoint.connect(self.indicatePoint)
     signals.mainWindowClosing.connect(self.close)
     # Finish up
     self.sync_LDG_display = True
     self.updateLdgMenuAndDisplay()
     self.updateRdfMenuAction()
     self.scopeView.moveToShow(env.radarPos())
     self.f_scale = lambda x: max_zoom_factor / exp(x * log(
         settings.map_range / max_zoom_range))  # x in [0, 1]
     self.changeZoomLevel(self.zoomLevel_slider.value())
예제 #18
0
    def updateDisplay(self):
        if self.radar_contact == None:
            self.info_area.setEnabled(False)
            return
        else:
            self.info_area.setEnabled(True)

        # AIRCRAFT BOX
        # Heading
        hdg = self.radar_contact.heading()
        self.aircraftHeading_info.setText('?' if hdg == None else hdg.read() +
                                          '°')
        # Alt./FL
        alt = self.radar_contact.xpdrAlt()
        if alt == None:
            self.aircraftAltitude_info.setText(
                'N/A' if settings.SSR_mode_capability in '0A' else '?')
        else:
            alt_str = env.readStdAlt(alt, step=None, unit=True)
            if not alt_str.startswith('FL') and env.QNH(
                    noneSafe=False) == None:
                alt_str += '  !!QNH'
            self.aircraftAltitude_info.setText(alt_str)
        # Ground speed
        groundSpeed = self.radar_contact.groundSpeed()
        if groundSpeed == None:
            self.aircraftGroundSpeed_info.setText('?')
        else:
            self.aircraftGroundSpeed_info.setText(str(groundSpeed))
        # Indicated airspeed speed
        ias = self.radar_contact.IAS()
        if ias == None:
            self.indicatedAirSpeed_info.setText(
                '?' if settings.SSR_mode_capability == 'S' else 'N/A')
        else:
            s = str(ias)
            if self.radar_contact.xpdrIAS() == None:
                s += '  !!estimate'
            self.indicatedAirSpeed_info.setText(s)
        # Vertical speed
        vs = self.radar_contact.verticalSpeed()
        if vs == None:
            self.aircraftVerticalSpeed_info.setText(
                'N/A' if settings.SSR_mode_capability in '0A' else '?')
        else:
            self.aircraftVerticalSpeed_info.setText('%+d ft/min' % vs)

        # ROUTE BOX
        coords = self.radar_contact.coords()
        strip = env.linkedStrip(self.radar_contact)
        route = None if strip == None else strip.lookup(parsed_route_detail)
        self._last_known_route = route
        if route == None:
            self.route_box.setEnabled(False)
        else:
            self.route_box.setEnabled(True)
            i_leg = route.currentLegIndex(coords)
            wpdist = coords.distanceTo(route.waypoint(i_leg).coordinates)
            self.legCount_info.setText('%d of %d' %
                                       (i_leg + 1, route.legCount()))
            self.legSpec_info.setText(route.legStr(i_leg))
            self.waypointAt_info.setText(dist_str(wpdist))
            try:  # TTF
                if groundSpeed == None:
                    raise ValueError('No ground speed info')
                self.waypointTTF_info.setText(TTF_str(wpdist, groundSpeed))
            except ValueError:
                self.waypointTTF_info.setText('?')

        # AIRPORT BOX
        if env.airport_data != None:
            airport_dist = coords.distanceTo(env.radarPos())
            self.airportBearing_info.setText(
                coords.headingTo(env.radarPos()).read())
            self.airportDistance_info.setText(dist_str(airport_dist))
            try:  # TTF
                if groundSpeed == None:
                    raise ValueError('No ground speed info')
                self.airportTTF_info.setText(TTF_str(airport_dist,
                                                     groundSpeed))
            except ValueError:
                self.airportTTF_info.setText('?')
예제 #19
0
	def extractSectorFile(self):
		txt, ignore = QFileDialog.getOpenFileName(self, caption='Select sector file to extract from')
		if txt != '':
			extract_sector(txt, env.radarPos(), settings.map_range)
			QMessageBox.information(self, 'Done extracting', \
				'Background drawings extracted.\nSee console for summary and files created in the output directory.')
예제 #20
0
	def __init__(self, launcher, parent=None):
		QMainWindow.__init__(self, parent)
		self.setupUi(self)
		self.central_workspace = WorkspaceWidget(self)
		self.setCentralWidget(self.central_workspace)
		self.installEventFilter(RadioKeyEventFilter(self))
		self.setAttribute(Qt.WA_DeleteOnClose)
		self.launcher = launcher
		settings.controlled_tower_viewer = FlightGearTowerViewer(self)
		settings.session_manager = SessionManager(self)
		self.setWindowTitle('%s - %s (%s)' % (self.windowTitle(), env.locationName(), settings.location_code))
		self.session_start_sound_lock_timer = QTimer(self)
		self.session_start_sound_lock_timer.setSingleShot(True)
		self.session_start_sound_lock_timer.timeout.connect(self.unlockSounds)
		
		## Restore saved dock layout
		try:
			with open(dock_layout_file, 'rb') as f: # Restore saved dock arrangement
				self.restoreState(f.read())
		except FileNotFoundError: # Fallback on default dock arrangement
			# left docks, top zone
			self.tabifyDockWidget(self.selection_info_dock, self.weather_dock)
			self.tabifyDockWidget(self.selection_info_dock, self.towerView_dock)
			self.tabifyDockWidget(self.selection_info_dock, self.navigator_dock)
			self.selection_info_dock.hide()
			# left docks, bottom zone
			self.tabifyDockWidget(self.instructions_dock, self.notepads_dock)
			self.tabifyDockWidget(self.instructions_dock, self.radio_dock)
			self.tabifyDockWidget(self.instructions_dock, self.FPLlist_dock)
			self.tabifyDockWidget(self.instructions_dock, self.CPDLC_dock)
			self.instructions_dock.hide()
			self.notepads_dock.hide()
			self.radio_dock.hide()
			self.CPDLC_dock.hide()
			# right docks
			self.rwyBoxes_dock.hide() # hiding this because bad position (user will drag 1st thing after raise)
			# bottom docks
			self.atcTextChat_dock.hide()
		
		## Permanent tool/status bar widgets
		self.selectionInfo_toolbar.addWidget(SelectionInfoToolbarWidget(self))
		self.METAR_statusBarLabel = QLabel()
		self.PTT_statusBarLabel = QLabel()
		self.RDF_statusBarLabel = QLabel()
		self.RDF_statusBarLabel.setToolTip('Current signal / last QDM')
		self.wind_statusBarLabel = QLabel()
		self.QNH_statusBarLabel = QLabel()
		self.QNH_statusBarLabel.setToolTip('hPa / inHg')
		self.clock_statusBarLabel = QLabel()
		self.alarmClock_statusBarButtons = [AlarmClockButton('1', self), AlarmClockButton('2', self)]
		self.statusbar.addWidget(self.METAR_statusBarLabel)
		self.statusbar.addPermanentWidget(self.PTT_statusBarLabel)
		self.statusbar.addPermanentWidget(self.RDF_statusBarLabel)
		self.statusbar.addPermanentWidget(self.wind_statusBarLabel)
		self.statusbar.addPermanentWidget(self.QNH_statusBarLabel)
		for b in self.alarmClock_statusBarButtons:
			self.statusbar.addPermanentWidget(b)
			b.alarm.connect(self.notification_pane.notifyAlarmClockTimedOut)
		self.statusbar.addPermanentWidget(self.clock_statusBarLabel)
		
		# Populate menus (toolbar visibility and airport viewpoints)
		toolbar_menu = QMenu()
		self.general_viewToolbar_action = self.general_toolbar.toggleViewAction()
		self.stripActions_viewToolbar_action = self.stripActions_toolbar.toggleViewAction()
		self.docks_viewToolbar_action = self.docks_toolbar.toggleViewAction()
		self.selectionInfo_viewToolbar_action = self.selectionInfo_toolbar.toggleViewAction()
		self.radarAssistance_viewToolbar_action = self.radarAssistance_toolbar.toggleViewAction()
		self.workspace_viewToolbar_action = self.workspace_toolbar.toggleViewAction()
		toolbar_menu.addAction(self.general_viewToolbar_action)
		toolbar_menu.addAction(self.stripActions_viewToolbar_action)
		toolbar_menu.addAction(self.docks_viewToolbar_action)
		toolbar_menu.addAction(self.selectionInfo_viewToolbar_action)
		toolbar_menu.addAction(self.radarAssistance_viewToolbar_action)
		toolbar_menu.addAction(self.workspace_viewToolbar_action)
		self.toolbars_view_menuAction.setMenu(toolbar_menu)
		
		if env.airport_data == None or len(env.airport_data.viewpoints) == 0:
			self.viewpointSelection_view_menuAction.setEnabled(False)
		else:
			viewPointSelection_menu = QMenu()
			viewPointSelection_actionGroup = QActionGroup(self)
			for vp_i, (vp_pos, vp_h, vp_name) in enumerate(env.airport_data.viewpoints):
				action = QAction('%s - %d ft ASFC' % (vp_name, vp_h + .5), self)
				action.setCheckable(True)
				action.triggered.connect(lambda ignore_checked, i=vp_i: self.selectIndicateViewpoint(i))
				viewPointSelection_actionGroup.addAction(action)
			actions = viewPointSelection_actionGroup.actions()
			viewPointSelection_menu.addActions(actions)
			self.viewpointSelection_view_menuAction.setMenu(viewPointSelection_menu)
			try:
				actions[settings.selected_viewpoint].setChecked(True)
			except IndexError:
				actions[0].setChecked(True)
		
		## Memory-persistent windows and dialogs
		self.solo_connect_dialog_AD = StartSoloDialog_AD(self)
		self.MP_connect_dialog = StartFlightGearMPdialog(self)
		self.start_student_session_dialog = StartStudentSessionDialog(self)
		self.recall_cheat_dialog = DiscardedStripsDialog(self, ShelfFilterModel(self, env.discarded_strips, False), 'Sent and deleted strips')
		self.shelf_dialog = DiscardedStripsDialog(self, ShelfFilterModel(self, env.discarded_strips, True), 'Strip shelf')
		self.environment_info_dialog = EnvironmentInfoDialog(self)
		self.about_dialog = AboutDialog(self)
		self.teaching_console = TeachingConsole(parent=self)
		self.unit_converter = UnitConversionWindow(parent=self)
		self.world_airport_navigator = WorldAirportNavigator(parent=self)
		self.quick_reference = QuickReference(parent=self)
		for w in self.teaching_console, self.unit_converter, self.world_airport_navigator, self.quick_reference:
			w.setWindowFlags(Qt.Window)
			w.installEventFilter(RadioKeyEventFilter(w))
		
		# Make a few actions always visible
		self.addAction(self.newStrip_action)
		self.addAction(self.newLinkedStrip_action)
		self.addAction(self.newFPL_action)
		self.addAction(self.newLinkedFPL_action)
		self.addAction(self.startTimer1_action)
		self.addAction(self.forceStartTimer1_action)
		self.addAction(self.startTimer2_action)
		self.addAction(self.forceStartTimer2_action)
		self.addAction(self.notificationSounds_options_action)
		self.addAction(self.quickReference_help_action)
		self.addAction(self.saveDockLayout_view_action)
		self.addAction(self.recallWindowState_view_action)
		
		# Populate icons
		self.newStrip_action.setIcon(QIcon(IconFile.action_newStrip))
		self.newLinkedStrip_action.setIcon(QIcon(IconFile.action_newLinkedStrip))
		self.newFPL_action.setIcon(QIcon(IconFile.action_newFPL))
		self.newLinkedFPL_action.setIcon(QIcon(IconFile.action_newLinkedFPL))
		self.teachingConsole_view_action.setIcon(QIcon(IconFile.panel_teaching))
		self.unitConversionTool_view_action.setIcon(QIcon(IconFile.panel_unitConv))
		self.worldAirportNavigator_view_action.setIcon(QIcon(IconFile.panel_airportList))
		self.environmentInfo_view_action.setIcon(QIcon(IconFile.panel_envInfo))
		self.generalSettings_options_action.setIcon(QIcon(IconFile.action_generalSettings))
		self.soloSessionSettings_system_action.setIcon(QIcon(IconFile.action_sessionSettings))
		self.runwaysInUse_options_action.setIcon(QIcon(IconFile.action_runwayUse))
		self.newLooseStripBay_view_action.setIcon(QIcon(IconFile.action_newLooseStripBay))
		self.newRadarScreen_view_action.setIcon(QIcon(IconFile.action_newRadarScreen))
		self.newStripRackPanel_view_action.setIcon(QIcon(IconFile.action_newRackPanel))
		self.popOutCurrentWindow_view_action.setIcon(QIcon(IconFile.action_popOutWindow))
		self.reclaimPoppedOutWindows_view_action.setIcon(QIcon(IconFile.action_reclaimWindows))
		self.primaryRadar_options_action.setIcon(QIcon(IconFile.option_primaryRadar))
		self.approachSpacingHints_options_action.setIcon(QIcon(IconFile.option_approachSpacingHints))
		self.runwayOccupationWarnings_options_action.setIcon(QIcon(IconFile.option_runwayOccupationMonitor))
		self.routeConflictWarnings_options_action.setIcon(QIcon(IconFile.option_routeConflictWarnings))
		self.trafficIdentification_options_action.setIcon(QIcon(IconFile.option_identificationAssistant))
		
		setDockAndActionIcon(IconFile.panel_ATCs, self.handovers_dockView_action, self.handover_dock)
		setDockAndActionIcon(IconFile.panel_atcChat, self.atcTextChat_dockView_action, self.atcTextChat_dock)
		setDockAndActionIcon(IconFile.panel_CPDLC, self.cpdlc_dockView_action, self.CPDLC_dock)
		setDockAndActionIcon(IconFile.panel_FPLs, self.FPLs_dockView_action, self.FPLlist_dock)
		setDockAndActionIcon(IconFile.panel_instructions, self.instructions_dockView_action, self.instructions_dock)
		setDockAndActionIcon(IconFile.panel_navigator, self.navpoints_dockView_action, self.navigator_dock)
		setDockAndActionIcon(IconFile.panel_notepads, self.notepads_dockView_action, self.notepads_dock)
		setDockAndActionIcon(IconFile.panel_notifications, self.notificationArea_dockView_action, self.notification_dock)
		setDockAndActionIcon(IconFile.panel_radios, self.fgcom_dockView_action, self.radio_dock)
		setDockAndActionIcon(IconFile.panel_runwayBox, self.runwayBoxes_dockView_action, self.rwyBoxes_dock)
		setDockAndActionIcon(IconFile.panel_selInfo, self.radarContactDetails_dockView_action, self.selection_info_dock)
		setDockAndActionIcon(IconFile.panel_racks, self.strips_dockView_action, self.strip_dock)
		setDockAndActionIcon(IconFile.panel_txtChat, self.radioTextChat_dockView_action, self.radioTextChat_dock)
		setDockAndActionIcon(IconFile.panel_twrView, self.towerView_dockView_action, self.towerView_dock)
		setDockAndActionIcon(IconFile.panel_weather, self.weather_dockView_action, self.weather_dock)
		
		# action TICKED STATES (set here before connections)
		self.windowedWorkspace_view_action.setChecked(settings.saved_workspace_windowed_view)
		self.verticalRwyBoxLayout_view_action.setChecked(settings.vertical_runway_box_layout)
		self.notificationSounds_options_action.setChecked(settings.notification_sounds_enabled)
		self.primaryRadar_options_action.setChecked(settings.primary_radar_active)
		self.routeConflictWarnings_options_action.setChecked(settings.route_conflict_warnings)
		self.trafficIdentification_options_action.setChecked(settings.traffic_identification_assistant)
		self.runwayOccupationWarnings_options_action.setChecked(settings.monitor_runway_occupation)
		self.approachSpacingHints_options_action.setChecked(settings.APP_spacing_hints)
		
		# action CONNECTIONS
		# non-menu actions
		self.newStrip_action.triggered.connect(lambda: new_strip_dialog(self, default_rack_name, linkToSelection=False))
		self.newLinkedStrip_action.triggered.connect(lambda: new_strip_dialog(self, default_rack_name, linkToSelection=True))
		self.newFPL_action.triggered.connect(lambda: self.FPLlist_pane.createLocalFPL(link=None))
		self.newLinkedFPL_action.triggered.connect(lambda: self.FPLlist_pane.createLocalFPL(link=selection.strip))
		self.startTimer1_action.triggered.connect(lambda: self.startTimer(0, False))
		self.forceStartTimer1_action.triggered.connect(lambda: self.startTimer(0, True))
		self.startTimer2_action.triggered.connect(lambda: self.startTimer(1, False))
		self.forceStartTimer2_action.triggered.connect(lambda: self.startTimer(1, True))
		# system menu
		self.soloSession_system_action.triggered.connect(lambda: self.startStopSession(self.start_solo))
		self.connectFlightGearMP_system_action.triggered.connect(lambda: self.startStopSession(self.start_FlightGearMP))
		self.teacherSession_system_action.triggered.connect(lambda: self.startStopSession(self.start_teaching))
		self.studentSession_system_action.triggered.connect(lambda: self.startStopSession(self.start_learning))
		self.reloadAdditionalViewers_system_action.triggered.connect(self.reloadAdditionalViewers)
		self.reloadBgImages_system_action.triggered.connect(self.reloadBackgroundImages)
		self.reloadColourConfig_system_action.triggered.connect(self.reloadColourConfig)
		self.reloadRoutePresets_system_action.triggered.connect(self.reloadRoutePresets)
		self.reloadEntryExitPoints_system_action.triggered.connect(self.reloadEntryExitPoints)
		self.announceFgSession_system_action.triggered.connect(self.announceFgSession)
		self.fgcomEchoTest_system_action.triggered.connect(self.radio_pane.performEchoTest)
		self.extractSectorFile_system_action.triggered.connect(self.extractSectorFile)
		self.repositionBgImages_system_action.triggered.connect(self.repositionRadarBgImages)
		self.measuringLogsCoordinates_system_action.toggled.connect(self.switchMeasuringCoordsLog)
		self.airportGateway_system_action.triggered.connect(lambda: self.goToURL(airport_gateway_URL))
		self.openStreetMap_system_action.triggered.connect(lambda: self.goToURL(mk_OSM_URL(env.radarPos())))
		self.soloSessionSettings_system_action.triggered.connect(self.openSoloSessionSettings)
		self.locationSettings_system_action.triggered.connect(self.openLocalSettings)
		self.systemSettings_system_action.triggered.connect(self.openSystemSettings)
		self.changeLocation_system_action.triggered.connect(self.changeLocation)
		self.quit_system_action.triggered.connect(self.close)
		# view menu
		self.saveDockLayout_view_action.triggered.connect(self.saveDockLayout)
		self.recallWindowState_view_action.triggered.connect(self.recallWindowState)
		self.handovers_dockView_action.triggered.connect(lambda: self.raiseDock(self.handover_dock))
		self.atcTextChat_dockView_action.triggered.connect(lambda: self.raiseDock(self.atcTextChat_dock))
		self.cpdlc_dockView_action.triggered.connect(lambda: self.raiseDock(self.CPDLC_dock))
		self.FPLs_dockView_action.triggered.connect(lambda: self.raiseDock(self.FPLlist_dock))
		self.instructions_dockView_action.triggered.connect(lambda: self.raiseDock(self.instructions_dock))
		self.navpoints_dockView_action.triggered.connect(lambda: self.raiseDock(self.navigator_dock))
		self.notepads_dockView_action.triggered.connect(lambda: self.raiseDock(self.notepads_dock))
		self.notificationArea_dockView_action.triggered.connect(lambda: self.raiseDock(self.notification_dock))
		self.fgcom_dockView_action.triggered.connect(lambda: self.raiseDock(self.radio_dock))
		self.runwayBoxes_dockView_action.triggered.connect(lambda: self.raiseDock(self.rwyBoxes_dock))
		self.radarContactDetails_dockView_action.triggered.connect(lambda: self.raiseDock(self.selection_info_dock))
		self.strips_dockView_action.triggered.connect(lambda: self.raiseDock(self.strip_dock))
		self.radioTextChat_dockView_action.triggered.connect(lambda: self.raiseDock(self.radioTextChat_dock))
		self.towerView_dockView_action.triggered.connect(lambda: self.raiseDock(self.towerView_dock))
		self.weather_dockView_action.triggered.connect(lambda: self.raiseDock(self.weather_dock))
		self.windowedWorkspace_view_action.toggled.connect(self.central_workspace.switchWindowedView)
		self.popOutCurrentWindow_view_action.triggered.connect(self.central_workspace.popOutCurrentWindow)
		self.reclaimPoppedOutWindows_view_action.triggered.connect(self.central_workspace.reclaimPoppedOutWidgets)
		self.newLooseStripBay_view_action.triggered.connect(lambda: self.central_workspace.addWorkspaceWidget(WorkspaceWidget.LOOSE_BAY))
		self.newRadarScreen_view_action.triggered.connect(lambda: self.central_workspace.addWorkspaceWidget(WorkspaceWidget.RADAR_SCREEN))
		self.newStripRackPanel_view_action.triggered.connect(lambda: self.central_workspace.addWorkspaceWidget(WorkspaceWidget.STRIP_PANEL))
		self.verticalRwyBoxLayout_view_action.toggled.connect(self.switchVerticalRwyBoxLayout)
		self.towerView_view_action.triggered.connect(self.toggleTowerWindow)
		self.addViewer_view_action.triggered.connect(self.addView)
		self.listViewers_view_action.triggered.connect(self.listAdditionalViews)
		self.activateAdditionalViewers_view_action.toggled.connect(self.activateAdditionalViews)
		self.removeViewer_view_action.triggered.connect(self.removeView)
		self.teachingConsole_view_action.triggered.connect(self.teaching_console.show)
		self.unitConversionTool_view_action.triggered.connect(self.unit_converter.show)
		self.worldAirportNavigator_view_action.triggered.connect(self.world_airport_navigator.show)
		self.environmentInfo_view_action.triggered.connect(self.environment_info_dialog.exec)
		# options menu
		self.runwaysInUse_options_action.triggered.connect(self.configureRunwayUse)
		self.notificationSounds_options_action.toggled.connect(self.switchNotificationSounds)
		self.primaryRadar_options_action.toggled.connect(self.switchPrimaryRadar)
		self.routeConflictWarnings_options_action.toggled.connect(self.switchConflictWarnings)
		self.trafficIdentification_options_action.toggled.connect(self.switchTrafficIdentification)
		self.runwayOccupationWarnings_options_action.toggled.connect(self.switchRwyOccupationIndications)
		self.approachSpacingHints_options_action.toggled.connect(self.switchApproachSpacingHints)
		self.generalSettings_options_action.triggered.connect(self.openGeneralSettings)
		# cheat menu
		self.pauseSimulation_cheat_action.toggled.connect(self.pauseSession)
		self.spawnAircraft_cheat_action.triggered.connect(self.spawnAircraft)
		self.killSelectedAircraft_cheat_action.triggered.connect(self.killSelectedAircraft)
		self.popUpMsgOnRejectedInstr_cheat_action.toggled.connect(self.setRejectedInstrPopUp)
		self.showRecognisedVoiceStrings_cheat_action.toggled.connect(self.setShowRecognisedVoiceStrings)
		self.ensureClearWeather_cheat_action.toggled.connect(self.ensureClearWeather)
		self.ensureDayLight_cheat_action.triggered.connect(self.towerView_pane.ensureDayLight)
		self.changeTowerHeight_cheat_action.triggered.connect(self.changeTowerHeight)
		self.recallDiscardedStrip_cheat_action.triggered.connect(self.recall_cheat_dialog.exec)
		self.radarCheatMode_cheat_action.toggled.connect(self.setRadarCheatMode)
		# help menu
		self.quickReference_help_action.triggered.connect(self.quick_reference.show)
		self.videoTutorial_help_action.triggered.connect(lambda: self.goToURL(video_tutorial_URL))
		self.FAQ_help_action.triggered.connect(lambda: self.goToURL(FAQ_URL))
		self.about_help_action.triggered.connect(self.about_dialog.exec)
		
		## More signal connections
		signals.openShelfRequest.connect(self.shelf_dialog.exec)
		signals.privateAtcChatRequest.connect(lambda: self.raiseDock(self.atcTextChat_dock))
		signals.stripRecall.connect(recover_strip)
		env.radar.blip.connect(env.strips.refreshViews)
		env.radar.lostContact.connect(self.aircraftHasDisappeared)
		signals.aircraftKilled.connect(self.aircraftHasDisappeared)
		env.strips.rwyBoxFreed.connect(lambda box, strip: env.airport_data.physicalRunway_restartWtcTimer(box, strip.lookup(FPL.WTC)))
		env.rdf.signalChanged.connect(self.updateRDF)
		signals.statusBarMsg.connect(lambda msg: self.statusbar.showMessage(msg, status_bar_message_timeout))
		signals.newWeather.connect(self.updateWeatherIfPrimary)
		signals.kbdPTT.connect(self.updatePTT)
		signals.sessionStarted.connect(self.sessionHasStarted)
		signals.sessionEnded.connect(self.sessionHasEnded)
		signals.towerViewProcessToggled.connect(self.towerView_view_action.setChecked)
		signals.towerViewProcessToggled.connect(self.towerView_cheat_menu.setEnabled)
		signals.stripInfoChanged.connect(env.strips.refreshViews)
		signals.fastClockTick.connect(self.updateClock)
		signals.fastClockTick.connect(env.cpdlc.updateAckStatuses)
		signals.slowClockTick.connect(strip_auto_print_check)
		signals.stripEditRequest.connect(lambda strip: edit_strip(self, strip))
		signals.selectionChanged.connect(self.updateStripFplActions)
		signals.receiveStrip.connect(receive_strip)
		signals.handoverFailure.connect(self.recoverFailedHandover)
		signals.sessionPaused.connect(env.radar.stopSweeping)
		signals.sessionResumed.connect(env.radar.startSweeping)
		signals.aircraftKilled.connect(env.radar.silentlyForgetContact)
		signals.rackVisibilityLost.connect(self.collectClosedRacks)
		signals.localSettingsChanged.connect(env.rdf.clearAllSignals)
		signals.localSettingsChanged.connect(self.updateRDF)
		
		## MISC GUI setup
		self.strip_pane.setViewRacks([default_rack_name]) # will be moved out if a rack panel's saved "visible_racks" claims it [*1]
		self.strip_pane.restoreState(settings.saved_strip_dock_state) # [*1]
		self.central_workspace.restoreWorkspaceWindows(settings.saved_workspace_windows)
		self.central_workspace.switchWindowedView(settings.saved_workspace_windowed_view) # keep this after restoring windows!
		
		self.subsecond_ticker = Ticker(signals.fastClockTick.emit, parent=self)
		self.subminute_ticker = Ticker(signals.slowClockTick.emit, parent=self)
		self.subsecond_ticker.start_stopOnZero(subsecond_tick_interval)
		self.subminute_ticker.start_stopOnZero(subminute_tick_interval)
		self.towerView_cheat_menu.setEnabled(False)
		self.solo_cheat_menu.setEnabled(False)
		self.updateClock()
		self.updateWeatherIfPrimary(settings.primary_METAR_station, None)
		self.updateStripFplActions()
		self.last_RDF_qdm = None
		self.updateRDF()
		self.updatePTT(0, False)
		# Disable some base airport stuff if doing CTR
		if env.airport_data == None:
			self.towerView_view_action.setEnabled(False)
			self.runwaysInUse_options_action.setEnabled(False)
			self.runwayOccupationWarnings_options_action.setEnabled(False)
		# Finish
		self.atcTextChat_pane.switchAtcChatFilter(None) # Show GUI on general chat room at start
		if speech_recognition_available:
			prepare_SR_language_files()