Compare commits

...

3 Commits

Author SHA1 Message Date
fa3bc3cafe Got resource bundling working 2022-04-01 16:46:57 -07:00
a5ef038823 Add and delete works now! 2022-04-01 16:08:02 -07:00
58b32a6cb3 List rendering finally works! 2022-04-01 11:12:11 -07:00
7 changed files with 210 additions and 13 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
metar
metar.rcc

View File

@@ -23,7 +23,68 @@ Kirigami.ApplicationWindow {
onTriggered: addAirport.open()
}
}
Kirigami.CardsListView {
anchors.fill: parent
id: cardLayout
model: logic.metarList
delegate: metarListDelegate
}
}
Component {
id: metarListDelegate
Kirigami.AbstractCard {
contentItem: Item {
implicitWidth: delegateLayout.implicitWidth
implicitHeight: delegateLayout.implicitHeight
ColumnLayout {
id: delegateLayout
anchors {
left: parent.left
top: parent.top
right: parent.right
}
RowLayout {
Layout.fillWidth: true
Kirigami.Heading {
text: stationId
}
Kirigami.Heading {
text: flightCategory
// Dynamically set the color based on the flight category
Component.onCompleted: {
color = Qt.binding(function() {
if (flightCategory == "VFR") {
return "green";
} else if (flightCategory == "MVFR") {
return "blue";
} else if (flightCategory == "IFR") {
return "red";
} else if (flightCategory == "LIFR") {
return "fuchsia";
} else {
return "white";
}
});
}
}
Controls.Button {
Layout.alignment: Qt.AlignRight
icon.name: "edit-delete"
onClicked: logic.deleteAirport(stationId)
}
}
Controls.Label {
text: rawMetar
Layout.fillWidth: true
wrapMode: Text.WordWrap
}
}
}
}
}
Kirigami.OverlaySheet {
id: addAirport

BIN
metar

Binary file not shown.

136
metar.nim
View File

@@ -1,4 +1,5 @@
import std/[httpclient, times, xmlparser, xmltree]
import std/[httpclient, os, sequtils, sugar, strutils, xmlparser, xmltree]
import Tables
import strformat
import NimQml
import macros
@@ -10,7 +11,11 @@ proc getMetar(client: HttpClient, code: string): XMLNode =
parseXml(metarData)
proc childString(node: XMLnode, name: string): string =
node.child(name).innerText
let node = node.child(name)
if node != nil:
result = node.innerText
else:
result = ""
type SkyCondition = object
skyCover: string
@@ -29,7 +34,7 @@ type MetarData = object
flightCategory: string
skyConditions: seq[SkyCondition]
proc fromXml*(xmlData: XMLNode): MetarData =
proc newMetarData*(xmlData: XMLNode): MetarData =
let metar = xmlData.child("data").child("METAR")
result = MetarData(
@@ -39,7 +44,7 @@ proc fromXml*(xmlData: XMLNode): MetarData =
temperature: metar.childString("temp_c"),
dewPoint: metar.childString("dewpoint_c"),
windHeading: metar.childString("wind_dir_degrees"),
visibility: metar.childString("wisibility_statute_mi"),
visibility: metar.childString("visibility_statute_mi"),
altimiter: metar.childString("altim_in_hg"),
flightCategory: metar.childString("flight_category")
)
@@ -50,31 +55,129 @@ proc fromXml*(xmlData: XMLNode): MetarData =
skyCover: skyConditionXml.attr("sky_cover"),
cloudBaseAgl: skyconditionXml.attr("cloud_base_ft_agl")))
type AirportRoles {.pure.} = enum
RawMetar = UserRole + 1
FlightCategory = UserRole + 2
StationId = UserRole + 3
proc readConfig(): seq[string] =
let metarConfigDir = getConfigDir() & "/metarweather"
let metarConf = metarConfigDir & "/metarweather.conf"
if not dirExists(metarConfigDir):
createDir(metarConfigDir)
if not fileExists(metarConf):
return @[]
let config = readFile(metarConf)
if config.isEmptyOrWhiteSpace:
return @[]
return config.strip().split(",")
proc writeConfig(airports: seq[string]) =
let config = airports.join(",")
let metarConfigDir = getConfigDir() & "/metarweather"
let metarConf = metarConfigDir & "/metarweather.conf"
writeFile(metarConf, config)
proc getMetars(airports: seq[string]): seq[MetarData] =
let client = newHttpClient()
airports.map(airportCode => client.getMetar(airportCode)).map(metarXml => newMetarData(metarXml))
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)
of AirportRoles.StationId: result = newQVariant(airport.stationId)
method roleNames(self: MetarList): Table[int, string] =
{ AirportRoles.RawMetar.int:"rawMetar",
AirportRoles.FlightCategory.int:"flightCategory",
AirportRoles.StationId.int:"stationId"}.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 getMetarList(self: ApplicationLogic): QVariant {.slot.} =
return newQVariant(self.metarList)
proc metarListChanged(self: ApplicationLogic, metarList: QVariant) {.signal.}
proc newApplicationLogic*(app: QApplication): ApplicationLogic =
new(result)
proc newApplicationLogic*(app: QApplication, airports: seq[MetarData]): ApplicationLogic =
new(result, delete)
result.app = app
result.metarList = newMetarList(airports)
result.setup()
## Real methods below here
proc refresh(self: ApplicationLogic) {.slot.} =
echo "Refresh called"
let airports = self.metarList.airports.map(metar => metar.stationId)
self.metarList.delete
self.metarList = newMetarList(airports.getMetars())
self.metarListChanged(newQVariant(self.metarList))
proc addAirport(self: ApplicationLogic, code: string) {.slot.} =
echo &"Add airport called {code}"
let airports = self.metarList.airports.map(metar => metar.stationId) & code
writeConfig(airports)
self.metarList.delete
self.metarList = newMetarList(airports.getMetars())
self.metarListChanged(newQVariant(self.metarList))
proc deleteAirport(self: ApplicationLogic, code: string) {.slot.} =
echo &"Delete {code}"
let airports = self.metarList.airports.map(metar => metar.stationId)
.filter(stationId => stationId != code)
writeConfig(airports)
self.metarList.delete
self.metarList = newMetarList(airports.getMetars())
self.metarListChanged(newQVariant(self.metarList))
QtProperty[QVariant] metarList:
read = getMetarList
notify = metarListChanged
proc mainProc() =
let app = newQApplication()
@@ -83,11 +186,18 @@ proc mainProc() =
let engine = newQQmlApplicationEngine()
defer: engine.delete()
let logic = newApplicationLogic(app)
let airports = readConfig()
let metarData = getMetars(airports)
let logic = newApplicationLogic(app, metarData)
let logicVariant = newQVariant(logic)
engine.setRootContextProperty("logic", logicVariant)
engine.load("main.qml")
let appDirPath = app.applicationDirPath & "/" & "metar.rcc"
QResource.registerResource(appDirPath)
engine.load(newQUrl("qrc:///main.qml"))
app.exec()
when isMainModule:

18
metar.nimble Normal file
View File

@@ -0,0 +1,18 @@
# Package
version = "0.1.0"
author = "Zoe Moore"
description = "Aviation metar weather checking app"
license = "GPL-3.0-or-later"
bin = @["metar"]
# Dependencies
requires @["nim >= 1.4.8", "nimqml >= 0.9.0"]
task build, "Compile the binary":
exec ("nim -d:ssl -d:release c metar")
before build:
exec ("rcc --binary resources.qrc -o metar.rcc")

6
resources.qrc Normal file
View File

@@ -0,0 +1,6 @@
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource>
<file>main.qml</file>
</qresource>
</RCC>