I want to use this code to display generic Javafx Scene from my program
class JFXWindowDisplay(myScene:Scene) extends Application {
def start( stage: Stage) {
stage.setTitle("My JavaFX Application")
stage.setScene(myScene)
stage.show()
}
}
//OBJECT NEEDED TO RUN JAVAFX : http://stackoverflow.com/questions/12124657/getting-started-on-scala-javafx-desktop-application-development
object JFXWindowDisplay extends App {
override def main(args: Array[String]) {
Application.launch(classOf[JFXWindowDisplay], args: _*)
}
}
How can i pass Scene argument to the compagnon object ?
For example, i want to create a program which open a new javafx windows from scala script execution of this :
class myProgram() extends App {
val root = new StackPane
root.getChildren.add(new Label("Hello world!"))
val myScene = new Scene(root, 300, 300))
// then run ControlTest object with this scene
ControlTest(myScene)
}
You could prepare two or more traits each with his specific scene and then mix the one you need to the application class
trait SceneProvider {
def scene: Scene
}
trait Scene1 extends SceneProvider {
val root = new StackPane
root.getChildren.add(new Label("Hello world!"))
def scene = new Scene(root, 300, 300)
}
trait Scene2 extends SceneProvider {
val root = new StackPane
root.getChildren.add(new Label("Hello world 2!"))
def scene = new Scene(root, 300, 300)
}
trait JFXWindowDisplay {
self: SceneProvider =>
def start(stage: Stage) {
stage.setTitle("My JavaFX Application")
stage.setScene(scene)
stage.show()
}
}
class JFXWindowDisplay1 extends Application with JFXWindowDisplay with Scene1
class JFXWindowDisplay2 extends Application with JFXWindowDisplay with Scene2
object JFXMain extends App {
override def main(args: Array[String]) {
//here you chose an application class
Application.launch(classOf[JFXWindowDisplay1], args: _*)
}
}
EDIT: now this works, based on your comments.
Related
In my Scala application, I load the FXML file in the constructor of the controller and set the controller with fxmlLoader.setController(this).
UPDATE (1): A more comprehensive example:
abstract class Controller[A <: Parent] {
val root: A = loadRoot()
private val stage: Stage = new Stage()
def openWindow(): Unit = {
stage.setScene(new Scene(root))
stage.show()
stage.toFront()
}
private def loadRoot(): A = {
val loader = new FXMLLoader(getDefaultLocation())
loader.setController(this)
loader.load()
}
def getDefaultLocation(): URL = ???
}
--
class SampleController private() extends Controller[VBox] {
#FXML private var text: TextField = _
#FXML def initialize(): Unit = {
text.textProperty().set("That is some text.")
}
}
object SampleController {
def apply(): SampleController = new SampleController()
}
UPDATE (2): SampleController() is called whithin an Akka actor:
val controller = SampleController()
Platform.runLater(() => controller.openWindow())
I now experience that sometimes the initialize method is called before the c variables are bound. Can anyone think of any circumstances when that can happen?
I struggle to understand why the window I'm doing with openGL stays black.
I don't see where I made a mistake in my code :
import com.jogamp.opengl.awt.GLCanvas
import com.jogamp.opengl.{GL, GLAutoDrawable, GLCapabilities, GLEventListener, GLProfile}
import javax.swing.{JFrame, WindowConstants}
class Game extends JFrame ("Just a window OMG.") with GLEventListener {
val profile: GLProfile = GLProfile.get(GLProfile.GL4)
val capabilities = new GLCapabilities(profile)
val canvas = new GLCanvas(capabilities)
this.setName("Just a window OMG.")
this.getContentPane.add(canvas)
this.setSize(800, 600)
this.setLocationRelativeTo(null)
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
this.setVisible(true)
this.setResizable(false)
canvas.requestFocusInWindow
def play(): Unit = {
}
override def display(drawable: GLAutoDrawable): Unit = {
val gl = drawable.getGL.getGL4
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
gl.glFlush()
}
override def dispose(drawable: GLAutoDrawable): Unit = {}
override def init(drawable: GLAutoDrawable): Unit = {
val gl = drawable.getGL.getGL4
gl.glClearColor(1f, 0f, 0f, 1.0f)
}
override def reshape(drawable: GLAutoDrawable, x: Int, y: Int, width: Int, height: Int): Unit = {}
}
object Main {
def main(args: Array[String]): Unit = {
val game = new Game()
game.play()
}
}
I also tried to put the glClear inside the display method and also put a glClearColor in the init method.
EDIT:
I found it.
Actually the display and init meth were never called.
The listener wasn't attached to the canvas and then it never received any event.
The problem is that I was missing the line
canvas.addGLEventListener(this)
just after the canvas initialisation.
(this line)
val canvas = new GLCanvas(capabilities)
(I'm answering my own question)
So actually the problem was that the display and init method were never called.
As far as I understood it, the GLEventListener is waiting for event and those would trigger the call of the init and display method.
The "thing" that would notice the GLEventListener is the canvas, yet my canvas and the GLEventListener weren't binded.
To do so I added the line
canvas.addGLEventListener(this)
Just after I initialized the canvas and it then I could notice the init and display method called.
a couple of weeks ago I started programming in Scala and using Akka Actors. I'm trying to implement a scala programm that uses an Akka ActorSystem to serve multiple UI's. I am currently trying to setup an ScalaFx app and connect it through an internal actor to my actor system. Therefore I coded the following code to setup my actor system and my controller:
object Game {
// Main class of the game in which the actor system gets started
def main(args: Array[String]): Unit = {
val actorSystem = ActorSystem.create("gameActorSystem")
val controller = actorSystem.actorOf(Props[Controller], "controller")
var gui = actorSystem.actorOf(Props(new GuiApp.GuiActor(controller)), "guiActor")
println("Start Game")
gui ! StartGame
gui ! "Hello ScalaFx App."
}
}
class Controller extends Actor {
private val observers = scala.collection.mutable.SortedSet.empty[ActorRef]
override def receive: Receive = {
case StartGame => startGame()
case RegisterObserver => observers += sender(); sender ! PrintMessage("Subscription from: [" + sender().toString() + "]")
}
... controller code
}
Then I implemented a simple ScalaFx App:
object GuiApp extends JFXApp {
class GuiActor(controller: ActorRef) extends Actor {
controller ! RegisterObserver
override def receive: Receive = {
case StartGame => start()
case s:String => Platform.runLater{
label.text = "Message: " + s
}
}
}
def start(): Unit = {
val args = Array.empty[String]
main(args)
}
val label = new Label("Loading ...")
stage = new PrimaryStage {
title = "ScalaFX Game"
scene = new Scene {
root = new BorderPane {
padding = Insets(25)
center = label
}
}
}
}
So what I'm basically trying to do is to start my scala program, then register the akka system and register the controller and the gui actor. After registering the gui actor I want to start the GUI and then tell the GUI to set the "Hello ScalaFx App." String onto the GUI.
It's working so far, that the ScalaFx GUI starts up, but the initial content of the GUI with "Loading ..." is not being replaced by the "Hello ScalaFx App." String. Can somebody tell me if my approach is correct in any way or am I doing something entirely wrong?
If I have a controller named HomeController that receives a request like GET /foo with a header X-Foo: Bar, I would like to create a WS client filter that will read the RequestHeader in the context and copy the header value to the outgoing WS request.
Example Controller:
import play.api.libs.ws.{StandaloneWSRequest, WSClient, WSRequest, WSRequestExecutor, WSRequestFilter}
import play.api.mvc._
import scala.concurrent.ExecutionContext
#Singleton
class HomeController #Inject()(cc: ControllerComponents,
myWsClient: MyWSClient)
(implicit executionContext: ExecutionContext)
extends AbstractController(cc) {
def index = Action.async {
myWsClient.url("http://www.example.com")
.get()
.map(res => Ok(s"${res.status} ${res.statusText}"))(executionContext)
}
}
The wrapper around WSClient that introduces the filter:
#Singleton
class MyWSClient #Inject()(delegate: WSClient, fooBarFilter: FooBarFilter) extends WSClient {
override def underlying[T]: T = delegate.underlying.asInstanceOf[T]
override def url(url: String): WSRequest = {
delegate.url(url)
.withRequestFilter(fooBarFilter)
}
override def close(): Unit = delegate.close()
}
And finally the WS filter itself:
#Singleton
class FooBarFilter extends WSRequestFilter {
override def apply(executor: WSRequestExecutor): WSRequestExecutor = {
(request: StandaloneWSRequest) => {
request.addHttpHeaders(("X-Foo", "<...>")) // INSERT CORRECT VALUE HERE!
executor.apply(request)
}
}
}
In the end, the expectation is that the request GET http://www.example.com contains the header X-Foo: Bar.
The special requirements that make this more interesting are:
You can modify the MyWsClient class.
You can modify the FooBarFilter class
You can create HTTP controller filters (play.api.mvc.(Essential)Filterif it helps.
You can create other classes/objects/etc
You cannot modify the controller (because in our situation, we can't expect all existing controllers to be modified.
The solution should work even if there's a a "service" layer between the controller and the WSClient invocation and doesn't involve passing down objects everywhere.
The solution can alter other Play/Akka mechanisms, like the default Dispatcher
I haven't tried to put it into actual code and test if this works but here is an idea: it looks like since Play 2.1 Http.Context is propagated even across async call. And there is Http.Context._requestHeader. So what you can try to do is to change MyWSClient and FooBarFilter like this:
#Singleton
class MyWSClient #Inject()(delegate: WSClient) extends WSClient {
override def underlying[T]: T = delegate.underlying.asInstanceOf[T]
override def url(url: String): WSRequest = {
val fooHeaderOption = Http.Context.current()._requestHeader().headers.get(FooHeaderFilter.fooHeaderName)
val baseRequest = delegate.url(url)
if (fooHeaderOption.isDefined)
baseRequest.withRequestFilter(new FooHeaderFilter(fooHeaderOption.get))
else
baseRequest
}
override def close(): Unit = delegate.close()
class FooHeaderFilter(headerValue: String) extends WSRequestFilter {
import FooHeaderFilter._
override def apply(executor: WSRequestExecutor): WSRequestExecutor = {
(request: StandaloneWSRequest) => {
request.addHttpHeaders((fooHeaderName, headerValue))
executor.apply(request)
}
}
}
object FooHeaderFilter {
val fooHeaderName = "X-Foo"
}
}
The idea is simple: extract the header from the Http.Context.current() when WSRequest is created and attach it to the request using a WSRequestFilter
Update: make it work in Scala API
As it was pointed out in the comment, this approach doesn't work in Scala API because Http.Context is not initialized and is not passed between threads. To make it work a higher level magic is required. Namely you need:
Easy: A Filter that will init Http.Context for Scala-handled requests
Hard: Override ExecutorServiceConfigurator for Akka's default dispatcher to create a custom ExecutorService that will pass Http.Context between thread switches.
The filter is trivial:
import play.mvc._
#Singleton
class HttpContextFilter #Inject()(implicit ec: ExecutionContext) extends EssentialFilter {
override def apply(next: EssentialAction) = EssentialAction { request => {
Http.Context.current.set(new Http.Context(new Http.RequestImpl(request), null))
next(request)
}
}
}
And the add it to the play.filters.enabled in the application.conf
The hard part is something like this:
class HttpContextWrapperExecutorService(val delegateEc: ExecutorService) extends AbstractExecutorService {
override def isTerminated = delegateEc.isTerminated
override def awaitTermination(timeout: Long, unit: TimeUnit) = delegateEc.awaitTermination(timeout, unit)
override def shutdownNow() = delegateEc.shutdownNow()
override def shutdown() = delegateEc.shutdown()
override def isShutdown = delegateEc.isShutdown
override def execute(command: Runnable) = {
val newContext = Http.Context.current.get()
delegateEc.execute(() => {
val oldContext = Http.Context.current.get() // might be null!
Http.Context.current.set(newContext)
try {
command.run()
}
finally {
Http.Context.current.set(oldContext)
}
})
}
}
class HttpContextExecutorServiceConfigurator(config: Config, prerequisites: DispatcherPrerequisites) extends ExecutorServiceConfigurator(config, prerequisites) {
val delegateProvider = new ForkJoinExecutorConfigurator(config.getConfig("fork-join-executor"), prerequisites)
override def createExecutorServiceFactory(id: String, threadFactory: ThreadFactory): ExecutorServiceFactory = new ExecutorServiceFactory {
val delegateFactory = delegateProvider.createExecutorServiceFactory(id, threadFactory)
override def createExecutorService: ExecutorService = new HttpContextWrapperExecutorService(delegateFactory.createExecutorService)
}
}
and register at using
akka.actor.default-dispatcher.executor = "so.HttpContextExecutorServiceConfigurator"
Don't forget to update the "so" with you real package. Also if you use more custom executors or ExecutionContexts, you should patch (wrap) them as well to pass Http.Context along the asynchronous calls.
http://docs.scala-lang.org/style/naming-conventions.html suggests that all objects are camelCase with a capital first letter except when attempting to mimic a package or a function. But what about mimicing a val?
class MyFrame extends javax.swing.JFrame {
object myBox extends javax.swing.Box(javax.swing.BoxLayout.X_AXIS) {
object myLabel extends javax.swing.JLabel {
import scala.collection.JavaConverters._
object myFont extends java.awt.Font(Map(java.awt.font.TextAttribute.FOREGROUND -> java.awt.Color.RED).asJava)
setFont(myFont)
}
add(myLabel)
}
add(myBox)
setSize(100, 60)
}
object Main {
def main(args: Array[String]) {
val myFrame = new MyFrame
myFrame.myBox.myLabel.setText("Hello, World!")
myFrame.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE)
myFrame.setVisible(true)
}
}
Or
class MyFrame extends javax.swing.JFrame {
object MyBox extends javax.swing.Box(javax.swing.BoxLayout.X_AXIS) {
object MyLabel extends javax.swing.JLabel {
import scala.collection.JavaConverters._
object MyFont extends java.awt.Font(Map(java.awt.font.TextAttribute.FOREGROUND -> java.awt.Color.RED).asJava)
setFont(MyFont)
}
add(MyLabel)
}
add(MyBox)
setSize(100, 60)
}
object Main {
def main(args: Array[String]) {
val myFrame = new MyFrame
myFrame.MyBox.MyLabel.setText("Hello, World!") // Seems a bit weird to me
myFrame.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE)
myFrame.setVisible(true)
}
}
Which one is better?
Just use camelCase for package objects.
for embedded objects in class use Upper camelCase.