Best way to program a gui

I know how to program java-swing applications.
But what with scala. Do I use swing, can I use swing together with scala or do I use a higher-level API ?

It may or may not make sense for your needs, but many Scala programmers build their GUIs in the browser – since Scala compiles to JavaScript, you can use browser-native frameworks like React, or Scala-centric ones like Laminar, to build your user interfaces that way.

1 Like

Is there something like kemal (crystal language),

I used to write a fair amount of Swing code in Scala directly, and that worked fine. But you might also want to check out GitHub - scala/scala-swing: Scala wrappers for Java's Swing API for desktop GUIs

1 Like

Another option:

JavaFX

or

ScalaFX

Setup is more difficult but you have a WYSIWYG GUI builder based on XML.

Use of Swing has no additional setup required.

HTHs

1 Like

If you know Swing well then perhaps the shortest path is to write a few of your own simple wrappers around the stuff in Swing you use the most to both take advantage of the Scala abstractions mechanism you find most useful and your Java Swing knowledge.

Here is an example of how “thin wrappers” can be used in a graphics library for teaching beginner programming that connects directly with Swing with no other dependency:

“Thin wrapper” example:

1 Like

I’m currently trying scalafx.

1 Like

Yes, ScalaFX is an option. I prefer to use raw JavaFX in Scala simply because I already know it, and don’t want to learn new syntax for what is essentially a wrapper around JavaFX.

scalafx is really easy and simple.
Here an example of how to calculate the double of an integer.


import scalafx.application.JFXApp3
import scalafx.scene.Scene
import scalafx.scene.layout.HBox
import scalafx.Includes._
import scalafx.scene.control._
import scalafx.event.ActionEvent

object MyProgram extends JFXApp3 {
  override def start(): Unit = {
    stage = new JFXApp3.PrimaryStage {
      title = "MyProgram"
      scene = new Scene(400,400) {
        //fill = Color.rgb(38, 38, 38)
        var textField = new TextField
        textField.layoutX = 20
        textField.layoutY = 20
        val button = new Button("Calculate")
        button.layoutX = 20
        button.layoutY = 50
        val label = new Label("MyLabel")
        label.layoutX = 20
        label.layoutY = 80
        button.onAction = (e:ActionEvent) => {
          val myStringInput:String = textField.getText()
          val input = myStringInput.toInt
          val output = input.toInt * 2
          val myStringOutput = output.toString
          label.setText(myStringOutput)
        }
        content = List(textField,button, label) }}}}

2 Likes

That is nice and short. Cool!

1 Like

Problem i’m unable to draw a simple “sine wave function”. Someone knows how ?

I programmed a scalafx solution :

import scalafx.scene.control.*
import scalafx.scene.layout.HBox
import scalafx.event.ActionEvent
import scalafx.scene.layout.StackPane
import scalafx.scene.canvas.{Canvas, GraphicsContext}
import scalafx.Includes.*

import scalafx.application.JFXApp3
import scalafx.scene.Scene
import scalafx.scene.paint._
import scalafx.scene.paint.Color
import scalafx.scene.shape.{Circle, Rectangle}
import scala.math._

def intToColor(argb: Int): Color = {
  val alpha = (argb >> 24) & 0xFF
  val red = (argb >> 16) & 0xFF
  val green = (argb >> 8) & 0xFF
  val blue = argb & 0xFF

  Color.rgb(red, green, blue, alpha / 255.0)
}
var rl:List[Rectangle]=
  var x1=Rectangle(1, 1, 2, 2)
  var x2: List[Rectangle] = List(x1)
  for (a <- 1 to 350) {
    val y = ((sin(a / 30.0) + 1) * 100).toInt
    val z: Rectangle = Rectangle(a, y, 2, 2)
    val i=128*256*256*256+a*256*256+(256-a)*256+a/2
    val p=intToColor(i);
    z.setFill(p)
    x2 = z :: x2
  }
  x2

var aScene: Scene = new Scene(400, 400)
  {
    fill = Color.White
    content=rl
  }

object MyProgram extends JFXApp3 {
  override def start(): Unit = {
    stage = new JFXApp3.PrimaryStage {
      title = "MyProgram"
      scene = aScene
    }
  }
}

Problem , everything is drawn at once. I cannot sleep.

Here my latest solution,


import scalafx.scene.control.*
import scalafx.scene.layout.HBox
import scalafx.event.ActionEvent
import scalafx.scene.layout.StackPane
import scalafx.scene.canvas.{Canvas, GraphicsContext}
import scalafx.Includes.*

import scalafx.application.JFXApp3
import scalafx.scene.Scene
import scalafx.scene.paint._
import scalafx.scene.paint.Color
import scalafx.scene.shape.{Circle, Rectangle}
import scala.math._

def intToColor(argb: Int): Color = {
  val alpha = (argb >> 24) & 0xFF
  val red = (argb >> 16) & 0xFF
  val green = (argb >> 8) & 0xFF
  val blue = argb & 0xFF

  Color.rgb(red, green, blue, alpha / 255.0)
}
var rl:List[Rectangle]=
  var x1=Rectangle(1, 1, 2, 2)
  var x2: List[Rectangle] = List(x1)
  for (a <- 1 to 350) {
    val y = ((sin(a / 30.0) + 1) * 100).toInt
    val z: Rectangle = Rectangle(a, y, 2, 2)
    val i=128*256*256*256+a*256*256+(256-a)*256+a/2
    val p=intToColor(i);
    z.setFill(p)
    x2 = z :: x2
  }
  x2

var aScene: Scene = new Scene(400, 400)
  {
    fill = Color.White
    content=rl
  }

object MyProgram extends JFXApp3 {
  override def start(): Unit = {
    stage = new JFXApp3.PrimaryStage {
      title = "MyProgram"
      scene = aScene
    }
  }
}

Problem everything is drawn at once. I cannot sleep between the drawings of the individual points. I.e. to do for instance movement.

Cross-post,
https://www.reddit.com/r/scala/comments/1h2mvaw/scalafx_type_error/

Is the problem that you would like more program control over what is drawn? I notice you are using ScalaFX’s way of setting content of the pane at the time the pane is created. That is only for simple demos. Instead, create the pane first, and then add or remove nodes from the pane’s children whenever you need to. The graphics appear when you add nodes and disappear when you remove nodes. You can use JavaFX’s animation if you want an animated display that adds and removes nodes under timed control. Also, might be easier to draw the sine curve with PolyLine instead of creating lots of Rectangle?

Here is an example of a sine wave that keeps redrawing itself. Implemented using Animation.

import scalafx.application.JFXApp3
import scalafx.scene.Scene
import scalafx.scene.chart.{LineChart, NumberAxis, XYChart}
import scalafx.animation.{KeyFrame, Timeline}
import scalafx.util.Duration
import scalafx.Includes.*

object AnimatedSineWave extends JFXApp3 {
  private val MaxX = 360

  override def start(): Unit = {
    val xAxis = new NumberAxis(0, MaxX, 45):
      label = "X"
    val yAxis = new NumberAxis(-120, 120, 20):
      label = "Y"

    val lineChart = new LineChart[Number, Number](xAxis, yAxis):
      title = "Sine Wave Animation"
      legendVisible = false
      animated = false // do not show trails

    val series = new XYChart.Series[Number, Number]()
    lineChart.getData.add(series)

    // Timeline for animation
    val timeline = new Timeline:
      cycleCount = Timeline.Indefinite
      keyFrames = Seq(
        KeyFrame(
          Duration(10),
          onFinished = _ => {
            if series.data().size > MaxX then
              // Start from the beginning
              series.data().clear()
            else
              // Add the next point in the sine wave
              val x = series.data().size
              val y = 100 * Math.sin(2 * Math.PI * x / 360d)
              series.data() += XYChart.Data[Number, Number](x, y)
          }
        )
      )
    timeline.play()

    stage = new JFXApp3.PrimaryStage:
      title = "Animated Sine Wave"
      scene = new Scene(800, 600):
        root = lineChart
  }
}

The plot is made using LineChart. The data is managed using XYChart.Series. The animation adds adds one point at a time to the series (every 10ms), using Timeline, that updates the LineChart. When the and of the axis is reached it removes all datapoints from the series and and start from the beginning adding points.

1 Like

For the sake of completeness my latest implementation,

import scalafx.scene.control.*
import scalafx.event.ActionEvent
import scalafx.scene.layout.StackPane
import scalafx.Includes.*

import scalafx.scene.canvas.{Canvas, GraphicsContext}
import scalafx.scene.layout.{HBox,Pane}
import scalafx.application.JFXApp3
import scalafx.scene.Scene
import scalafx.scene.paint._
import scalafx.scene.paint.Color
import scalafx.scene.shape.{Circle, Rectangle}
import scala.math._

def intToColor(argb: Int): Color = {
  val alpha = (argb >> 24) & 0xFF
  val red = (argb >> 16) & 0xFF
  val green = (argb >> 8) & 0xFF
  val blue = argb & 0xFF
  Color.rgb(red, green, blue, alpha / 255.0)}

val rectangle1 = new Rectangle { x = 50 ;  y = 50 ; width = 100 ; height = 100 ; fill = Color.Blue }

def addchildren(myroot:Pane): Unit =
  myroot.children.add(rectangle1)

def addSine(myroot:Pane): Unit =
  for (_x2 <- 1 to 600) {
    var _x = _x2
    var _y = (1 + sin(_x2 / 30.0)) * 100
    val i = 192 * 256 * 256 * 256 + _x * 256 * 256 + (256 - _x) * 256 + _x / 2
    val p = intToColor(i);
    var myrect = new Rectangle { x = _x ; y = _y ; width = 2 ; height = 2 ; fill = p }
    myroot.children.add(myrect)}

object MyProgram extends JFXApp3 {
  val myroot = new Pane {}
  override def start(): Unit = {
    stage = new JFXApp3.PrimaryStage {
      title = "MyProgram"
      width = 800
      height = 600
      scene = new Scene(myroot)
    }}
  addchildren(myroot)
  addSine(myroot)}

Yes, using the Canvas drawing model. You can also draw points and line segments instead of rectangles if you prefer. The trouble with a canvas is you need to erase and redraw if you want any animation. And Canvases cannot be smoothly scaled: They are bitmaps. My original implementation of my app (a music notation editor) in Objective-C had to use a canvas model (which in Obj-C did scale because it was Display Postscript and not a mere bitmap), but when I translated it to JavaFX, converted to the scene graph model. All the graphics works much faster and scales smoothly because JavaFX does all the thinking about managing the graphics elements. Now the only reason I use a Canvas model is to create individual bitmaps for button icons.