import std/[httpclient, times, xmlparser, xmltree] import Tables import strformat import NimQml import macros import typeinfo proc getMetar(client: HttpClient, code: string): XMLNode = let requestUrl = &"https://aviationweather.gov/adds/dataserver_current/httpparam?dataSource=metars&requestType=retrieve&format=xml&hoursBeforeNow=3&mostRecent=true&stationString={code}" let metarData = client.getContent(requestUrl) parseXml(metarData) proc childString(node: XMLnode, name: string): string = let node = node.child(name) if node != nil: result = node.innerText else: result = "" type SkyCondition = object skyCover: string cloudBaseAgl: string type MetarData = object rawText: string stationId: string observationTime: string temperature: string dewPoint: string windHeading: string windSpeedKnots: string visibility: string altimiter: string flightCategory: string skyConditions: seq[SkyCondition] proc newMetarData*(xmlData: XMLNode): MetarData = let metar = xmlData.child("data").child("METAR") result = MetarData( rawText: metar.childString("raw_text"), stationId: metar.childString("station_id"), observationTime: metar.childString("observation_time"), temperature: metar.childString("temp_c"), dewPoint: metar.childString("dewpoint_c"), windHeading: metar.childString("wind_dir_degrees"), visibility: metar.childString("visibility_statute_mi"), altimiter: metar.childString("altim_in_hg"), flightCategory: metar.childString("flight_category") ) for skyConditionXml in metar.findAll("sky_condition"): result.skyConditions.add( SkyCondition( skyCover: skyConditionXml.attr("sky_cover"), cloudBaseAgl: skyconditionXml.attr("cloud_base_ft_agl"))) type AirportRoles {.pure.} = enum RawMetar = UserRole + 1 FlightCategory = UserRole + 2 QtObject: type MetarList* = ref object of QAbstractListModel airports*: seq[MetarData] proc delete(self: MetarList) = self.QAbstractListModel.delete proc setup(self: MetarList) = self.QAbstractListModel.setup proc newMetarList*(airports: seq[MetarData]): MetarList = new(result, delete) result.airports = airports result.setup method rowCount(self: MetarList, index: QModelIndex = nil): int = self.airports.len method data(self: MetarList, index: QModelIndex, role: int): QVariant = if not index.isValid: return if index.row < 0 or index.row >= self.airports.len: return let airport = self.airports[index.row] let airportRole = role.AirportRoles case airportRole: of AirportRoles.RawMetar: result = newQVariant(airport.rawText) of AirportRoles.FlightCategory: result = newQVariant(airport.flightCategory) method roleNames(self: MetarList): Table[int, string] = { AirportRoles.RawMetar.int:"rawMetar", AirportRoles.FlightCategory.int:"flightCategory"}.toTable QtObject: type ApplicationLogic* = ref object of QObject app: QApplication metarList: MetarList proc delete*(self: ApplicationLogic) = self.QObject.delete self.metarList.delete proc setup(self: ApplicationLogic) = self.QObject.setup proc newApplicationLogic*(app: QApplication, airports: seq[MetarData]): ApplicationLogic = new(result, delete) result.app = app result.metarList = newMetarList(airports) result.setup() proc refresh(self: ApplicationLogic) {.slot.} = echo "Refresh called" proc addAirport(self: ApplicationLogic, code: string) {.slot.} = echo &"Add airport called {code}" proc deleteAirport(self: ApplicationLogic, code: string) {.slot.} = echo &"Delete {code}" proc getMetarList(self: ApplicationLogic): QVariant {.slot.} = return newQVariant(self.metarList) QtProperty[QVariant] metarList: read = getMetarList proc mainProc() = let app = newQApplication() defer: app.delete() let engine = newQQmlApplicationEngine() defer: engine.delete() let client = newHttpClient() let metarData = @[ newMetarData(client.getMetar("KOAK")), newMetarData(client.getMetar("KHWD")), ] let logic = newApplicationLogic(app, metarData) let logicVariant = newQVariant(logic) engine.setRootContextProperty("logic", logicVariant) engine.load("main.qml") app.exec() when isMainModule: mainProc() GC_fullcollect()