ScalaFX: возможно ли определять элементы управления в объектах, отличных от объекта приложения? - PullRequest
0 голосов
/ 21 сентября 2018

Я пытаюсь сделать следующее: иметь ScalaFX приложение с несколькими хорошо заказанными object с именами Buttons, Labels, Checkboxes и так далее, чтобы все было хорошо и вorder.

Вот небольшой пример, чтобы показать, что я имею в виду:

package ButtonsAndLabel

import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.scene.Scene
import scalafx.scene.control.{ Button, Label }
import scalafx.event.ActionEvent

object Main extends JFXApp {

  stage = new JFXApp.PrimaryStage {
    title = "Test-Program"

    scene = new Scene(300, 200) {
      val label = new Label("Nothing happened yet") {
        layoutX = 20
        layoutY = 20
      }
      val button1 = new Button("Button 1") {
        layoutX = 20
        layoutY = 50
        onAction = (e: ActionEvent) => {
          label.text = "B1 klicked"
        }
      }
      val button2 = new Button("Button 2") {
        layoutX = 20
        layoutY = 80
        onAction = (e: ActionEvent) => {
          label.text = "B2 klicked"
        }
      }

      content = List(label, button1, button2)
    }
  }
}

Этот код показывает окно с меткой и двумя кнопками, а кнопки изменяют текст метки.

Это прекрасно работает.

Но когда мой код растет с гораздо большим количеством элементов управления, все становится грязно.

Вот почему я попытался перенести элементы управления в другие object s.(в разных файлах).Я поместил метку в объект с именем Labels:

package ButtonsAndLabel

import scalafx.scene.control.Label
import scalafx.event.ActionEvent

object Labels {
  val label = new Label("Nothing happened yet") {
    layoutX = 20
    layoutY = 20
  }
}

, когда я импортирую его в основной файл с помощью

import Labels.label

, все работает нормально.

Но затем я пытаюсь поместить кнопки в Buttons объект:

package ButtonsAndLabel

import scalafx.scene.control.Button
import scalafx.event.ActionEvent
import Labels.label

object Buttons {
  val button1 = new Button("Button 1") {
    layoutX = 20
    layoutY = 50
    onAction = (e: ActionEvent) => {
      label.text = "B1 klicked"
    }
  }
  val button2 = new Button("Button 2") {
    layoutX = 20
    layoutY = 80
    onAction = (e: ActionEvent) => {
      label.text = "B2 klicked"
    }
  }
}

, который выдает сообщение об ошибке при попытке компиляции:

[error]  found   : scalafx.event.ActionEvent => Unit
[error]  required: javafx.event.EventHandler[javafx.event.ActionEvent]
[error]     onAction = (e: ActionEvent) => {

и теперь я застрял, потому что я не знаю Java .

Кто-нибудь знает, возможно ли вообще то, что я пытаюсь сделать?

До сих пор я не нашел ничего об этом в сети.Проблема не удерживает меня от написания нужной мне программы, но последнее написанное мной приложение было беспорядочным со всеми элементами управления в одном файле.

Я пропускаю что-то очевидное здесь?

Любая помощь будет очень признательна.

1 Ответ

0 голосов
/ 22 сентября 2018

Во-первых, ваш подход совершенно нормален.

Ошибка, которую вы видите, на самом деле не имеет ничего общего с Java - она ​​выводится компилятором Scala !Все, что он говорит, это то, что ему был предоставлен один тип элемента (в данном случае, функция, которая принимает scalafx.event.ActionEvent и возвращает Unit), когда она ожидала другой тип элемента (экземпляр javafx.event.EventHandler[javafx.event.ActionEvent], в этомcase).

ScalaFX - это просто набор Scala -обязательных оболочек для библиотеки JavaFX ;без функций преобразования implicit, которые конвертируют между двумя наборами элементов, компилятор Scala будет жаловаться на поиск элементов ScalaFX , когда ему нужны элементы JavaFX , и наоборот .

Решение состоит в том, чтобы обеспечить добавление следующих import к каждому из ваших ScalaFX исходных файлов:

import scalafx.Includes._

(У вас есть это в верхней части вашего основного исходного файла, но не остальные.)

Это обеспечит преобразование вашего обработчика ScalaFX ActionEvent в JavaFX эквивалент, тем самым облегчая вашу жизнь.

Это очень распространенный тип ошибки с ScalaFX , который почти всегда исправляется указанием выше import.(Если import не решит вашу проблему, то у вас обычно будет подлинный случай путаницы типов, когда вы просто использовали неправильный тип объекта.)

Итак, вот что я думаюВаш код должен выглядеть следующим образом:

Main.scala:

import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.scene.Scene
import buttonsandlabel._

object Main extends JFXApp {

  stage = new JFXApp.PrimaryStage {
    title = "Test-Program"

    scene = new Scene(300, 200) {
      content = List(Labels.label, Buttons.button1, Buttons.button2)
    }
  }
}

buttonsandlabel/Labels.scala:

package buttonsandlabel

import scalafx.Includes._
import scalafx.scene.control.Label

object Labels {
  val label = new Label("Nothing happened yet") {
    layoutX = 20
    layoutY = 20
  }
}

buttonsandlabel/Buttons.scala:

package buttonsandlabel

import scalafx.Includes._
import scalafx.scene.control.Button
import scalafx.event.ActionEvent
import Labels.label

object Buttons {
  val button1 = new Button("Button 1") {
    layoutX = 20
    layoutY = 50
    onAction = (e: ActionEvent) => {
      label.text = "B1 klicked"
    }
  }
  val button2 = new Button("Button 2") {
    layoutX = 20
    layoutY = 80
    onAction = (e: ActionEvent) => {
      label.text = "B2 klicked"
    }
  }
}

(Обратите внимание, что имена пакетов, как правило, все строчные.)

Одна вещь, о которой вам нужно знать, это Поток приложений JavaFX : весь ваш кодкоторый взаимодействует с ScalaFX (или JavaFX ) должен выполняться в этом потоке.Если вы обращаетесь к ScalaFX / JavaFX из другого потока, вы получите исключение ошибки.(Это гарантирует, что все такие приложения поточно-безопасны .) Если вы не знакомы с многопоточностью, не беспокойтесь, ScalaFX инициализирует ваше приложение таким образом, чтодовольно тривиально.Обычно все, что нужно, - это чтобы ваш код инициализации перешел в конструктор вашего основного объекта приложения (объекта, который расширяет JFXApp).

Когда вы начинаете создавать ScalaFX элементы в других классах и объектах, вам нужно проявлять особую осторожность.object инициализируется при первой ссылке.Если на него сначала ссылается код, который не выполняется в потоке приложения JavaFX , то вы получите исключения ошибок потока.Один из возможных вариантов - поместить такой код в элементы def или lazy val, чтобы они выполнялись только при прямой ссылке.

В качестве альтернативы вам, возможно, придется вызывать код через scalafx.application.Platform.runLater().

Для получения дополнительной информации о потоке приложений JavaFX см. Документацию JavaFX .

...