JetBrains / lets-plot-kotlin

Grammar of Graphics for Kotlin
https://lets-plot.org/kotlin/
MIT License
430 stars 36 forks source link

`show()` should actually show a plot #51

Closed holgerbrandl closed 3 years ago

holgerbrandl commented 3 years ago

When trying to render a simple plot in a kotlin application

var p: Plot = lets_plot(data)
        p += geom_density(color = "dark_green", alpha = .3) { x = "rating"; fill = "cond" }
        p + ggsize(500, 250)
        p.show()

it just dies

Exception in thread "main" java.lang.IllegalStateException: Frontend context is not defined
    at jetbrains.letsPlot.LetsPlot$frontendContext$1.display(LetsPlot.kt:16)
    at jetbrains.letsPlot.intern.Plot.show(Core.kt:49)
    at org.kalasim.test.LetsPlotASimulation.main(DemoTests.kt:42)

It would be cool if (depending on the chosen backend (jfx, batik) a corresponding window could that simply shows the plot.

Currently the user has to dig through the code-base in particular https://github.com/JetBrains/lets-plot-kotlin/blob/master/demo/jvm-batik/src/main/kotlin/minimalDemo/Main.kt to to finally implement a utility to achieve this core functionality

fun Plot.showPlot() {

// Setup

    val BATIK_MESSAGE_CALLBACK = object : BatikMessageCallback {
        override fun handleMessage(message: String) {
            println(message)
        }

        override fun handleException(e: Exception) {
            if(e is RuntimeException) {
                throw e
            }
            throw RuntimeException(e)
        }
    }

    val SVG_COMPONENT_FACTORY_BATIK =
        { svg: SvgSvgElement -> BatikMapperComponent(svg, BATIK_MESSAGE_CALLBACK) }

    val AWT_EDT_EXECUTOR = { runnable: () -> Unit ->
        // Just invoke in the current thread.
        runnable.invoke()
    }

    SwingUtilities.invokeLater {

        // Create Swing Panel showing the plot.
        val plotSpec = toSpec()
        val plotSize = DoubleVector(600.0, 300.0)

        val component =
            MonolithicAwt.buildPlotFromRawSpecs(
                plotSpec,
                plotSize,
//                    plotMaxWidth = null,
                SVG_COMPONENT_FACTORY_BATIK, AWT_EDT_EXECUTOR
            ) {
                for(message in it) {
                    println("PLOT MESSAGE: $message")
                }
            }

        // Show plot in Swing frame.
        val frame = JFrame("The Minimal")
        frame.contentPane.add(component)
        frame.defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
        frame.pack()
        frame.isVisible = true
    }
}

Note: Clearly this extension lacks beauty as it is does not adjust the plot width when the user resizes the window. A better approach (similar how PyCharm does it in the ide) would be a small tabbed windows where all plots the users shows with .show() are grouped together. In kravis I've implemented a similar output viewer (although not really pretty as of now): image

holgerbrandl commented 3 years ago

Another nice way to support this would be to integrate it with the SciView in the IDE. Clearly, this would tie support to just the IDE, but would enable nice analytics when/after running kotlin applications for many users.

alshan commented 3 years ago

I agree, the show() method should show plot out of the box. I'm planning to add a simple window for beginning. Later we can develop it to a more sophisticated viewer, ideally supporting various plots (lets-plot, kravis).

What are your plans on the viewer you have in kravis already?

The SciView is also an option but I'm not sure now if it's available without the Python plugin. Also it's a "pro" feature not available in community edition.

holgerbrandl commented 3 years ago

Indeed I totally forgot that SciView is not just part of the professional edition.

I was thinking of adding at least a preview bar (simular to the one in SciView) to bypass the rather annoying forward/backward navigation panel. But until now this is more a vague idea than an actual plan (because of too many higher priority tasks).

alshan commented 3 years ago

Hi, a default Frontend context is now auto-configured and the show() is working, providing one of "Swing" JARs (lets-plot-batik or lets-plot-jfx) is present in classpath. Please checkout v1.3.0.

The "minimal demo" was updated as well.