用javafx框架tornadofx做了个天气预报的程序-LMLPHP


class WeatherApp : App(WeatherView::class)
class WeatherView : View("十五天天气预报") {
    val weatherVM: WeatherViewModel by inject()
    val controller: WeatherController by inject()
    var hdata: XYChart.Series<String, Number> by singleAssign()
    var ldata: XYChart.Series<String, Number> by singleAssign()
    override val root = borderpane {

        bottom = hbox(4) {
            progressbar(controller.taskProgress)
            label(controller.taskMessage)
            visibleWhen { controller.taskRunning }
            paddingAll = 4
        }
        left = vbox(4) {
//            button("refresh") {
//                action {
//                    controller.refresh()
//                }
//            }
            textfield(weatherVM.city)
            listview(controller.cities) {
                bindSelected(weatherVM.city)
                onUserSelect(1) {
                    controller.refresh()
                }
            }
        }
        center = tableview(controller.weathers) {
            column("date", Weather::dateProperty)
            column("type", Weather::typeProperty)
            column("高温℃", Weather::highProperty)
            column("低温℃", Weather::lowProperty)
            column("风向", Weather::fxProperty)
            bindSelected(weatherVM)
            smartResize()
        }
        right = linechart("十五天天气预报", CategoryAxis(), NumberAxis()) {
            id="lc"
            prefWidth=600.0

            // 设置成false可避免在第一次显示数据时,x轴的下标堆积在一起
            animated=false

            series("高温") {
                hdata = this
            }
            series("低温") {
                ldata = this
            }
        }
    }
}


class WeatherController : Controller() {
    val api: Rest by inject()
    val weathers = observableListOf<Weather>()
    val cities = observableListOf<String>()
    val weatherVM: WeatherViewModel by inject()
    val v: WeatherView by inject()
    val taskRunning = booleanProperty()
    val taskMessage = stringProperty()
    val taskProgress = doubleProperty()

    fun refresh() {
        v.title = weatherVM.city.value.split(",").first().plus(" 未来十五天天气预报")
        val t = task {
            loadWeathers(weatherVM.city.value.toString().split(",").last())
            updateMessage("Loading weathers")
            updateProgress(0.4, 1.0)
        }
        t.run()

        taskRunning.bind(t.runningProperty())
        taskMessage.bind(t.messageProperty())
        taskProgress.bind(t.progressProperty())
    }

    // 文件中读取城市列表
//    fun loadCities1() = GlobalScope.launch {
//        cities.clear()
//        // 使用hutool的FileUtil工具类,可正常读取resources下的资源文件
////        FileUtil.readLines("city.csv", "gbk").forEach {
////            cities.add(it)
////        }
//
//        // 使用tornadofx框架的resources无法读取文件内容,原因不明
////        File(resources.url("city.csv").toExternalForm()).readLines().forEach {
////            cities.add(it)
////        }
//
//        File(javaClass.getResource("/city.csv").toURI()).readLines().forEach {
//            cities.add(it)
//        }
//    }

    // 从文件中读取城市名称及其代码数据,填充到listview中
    fun loadCities() {
        cities.clear()
        run {
            File(javaClass.getResource("/city.csv").toURI()).readLines().forEach {
                cities.add(it)
            }
        }
    }


    // citykey:城市ID,如:"101030100"
    fun loadWeathers(citykey: String) {
        // 更新前清空数据
        weathers.clear()
        v.hdata.data.clear()
        v.ldata.data.clear()

//        安徽 101030100
        val json = api.get(citykey).one()
        val dates = JsonPath.read(json.toString(), "$.data.forecast[*].ymd") as List<String>
        val types = JsonPath.read(json.toString(), "$.data.forecast[*].type") as List<String>
        val city = JsonPath.read(json.toString(), "$.cityInfo.city") as String
        val time = JsonPath.read(json.toString(), "$.time") as String
        val quality = JsonPath.read(json.toString(), "$.data.quality") as String
        val wendu = JsonPath.read(json.toString(), "$.data.wendu") as String
        val shidu = JsonPath.read(json.toString(), "$.data.shidu") as String
        val pm25 = JsonPath.read(json.toString(), "$.data.pm25") as Double
        val pm10 = JsonPath.read(json.toString(), "$.data.pm10") as Double
        val highs = JsonPath.read(json.toString(), "$.data.forecast[*].high") as List<String>
        val lows = JsonPath.read(json.toString(), "$.data.forecast[*].low") as List<String>
        val fxs = JsonPath.read(json.toString(), "$.data.forecast[*].fx") as List<String>
        dates.indices.forEach {
            val w = Weather()
            w.date = dates[it]
            w.type = types[it]
            w.fx = fxs[it]
            w.city = city
            w.quality = quality
            w.time = time
            w.high = highs[it].split(" ").last().trimEnd('℃')
            w.low = lows[it].split(" ").last().trimEnd('℃')
            weathers.add(w)


            val date = dates[it].split("-").drop(1).joinToString("/")
            v.hdata.data(date, w.high.toInt()) {
                node.add(Text(w.high))
                node.tooltip(w.type)
//                Tooltip.install(node, Tooltip(w.high))
            }
            v.ldata.data(date, w.low.toInt()) {
                node.add(Text(w.low))
            }
        }
        // 通过控件id 找到要操作的控件
        (primaryStage.scene.lookup("#lc") as LineChart<String,Number>).title=v.title
//        (v.root.right as LineChart<String,Number>).title=v.title
        v.title+="  当前天气:$quality,  更新时间:$time,  温度:$wendu,  湿度:$shidu,  pm25:$pm25,  pm10:$pm10"

//        read(json, "$.store.book[*].author")
//        val d=json.get("data")
//       val forecast= JSONUtil.parseObj(d).get("forecast")
//        println(forecast.toString())
//        println(JSONUtil.parseObj().get("ymd"))
//        weathers.addAll(api.get("101030100").list().toModel())
    }


    fun loadWeathers1(citykey: String) {
        weathers.clear()
        // 此方法无法正确得到想要的数据
        weathers.addAll(api.get(citykey).list().toModel())
    }

    init {
        api.baseURI = "http://t.weather.sojson.com/api/weather/city/"
        loadCities()
    }
}


class Weather : JsonModel {
    val cityProperty = stringProperty("")
    var city by cityProperty

    val dateProperty = stringProperty()
    var date by dateProperty

    val typeProperty = stringProperty()
    var type by typeProperty

    val timeProperty = stringProperty()
    var time by timeProperty


    val highProperty = stringProperty()
    var high by highProperty

    val lowProperty = stringProperty()
    var low by lowProperty

    val shiduProperty = stringProperty("34%")
    var shidu by shiduProperty

    val qualityProperty = stringProperty("良")
    var quality by qualityProperty

    val wenduProperty = stringProperty("16")
    var wendu by wenduProperty

    val fxProperty = stringProperty("西风")
    var fx by fxProperty

    override fun toString() = "$city"

    override fun updateModel(json: JsonObject) = with(json) {

    }
}

class WeatherViewModel : ItemViewModel<Weather>() {
    val city = bind(defaultValue = "") { item?.cityProperty }
    val type = bind { item?.typeProperty }
    val date = bind{ item?.dateProperty }
    val time = bind { item?.timeProperty }
    val high = bind { item?.highProperty }
    val low = bind { item?.lowProperty }
    val fx = bind { item?.fxProperty }
    val quality = bind { item?.qualityProperty }
}

10-16 21:01