Interacting with actors in scala swing applications - swing

I'm writing a small application in scala. The application processes simple log files. Because the processing takes some time, I've decided to let my application core extend Actor.
class Application extends Actor {
def react() {
loop {
react {
case Process(file) => // do something interesting with file...
}
}
}
}
The processing of a log file is triggered by clicking a button in the gui. The gui uses scala swing.
object Gui extends SimpleSwingApplication {
val application = new Application().start()
def top = new MainFrame {
val startButton = new Button
reactions += {
case ButtonClicked(`startButton`) => application ! Process(file)
}
}
}
Now, the application core needs to notify the gui about the current progress.
sender ! Progress(value) // whenever progress is made
I've solved this by creating a separate actor inside the gui. The actor is executed inside the edt thread. It listens to messages from the application core and updates the gui.
object Gui extends SimpleSwingApplication {
val actor = new Actor {
override val scheduler = new SchedulerAdapter {
def execute(fun: => Unit) { Swing.onEDT(fun) }
}
start()
def act() {
loop {
react {
case ForwardToApplication(message) => application ! message
case Progress(value) => progressBar.value = value
}
}
}
}
}
Since the application core needs to know about the sender of the message, I also use this actor to forward messages from the gui to the application core, making my actor the new sender.
reactions += {
case ButtonClicked(`startButton`) => actor ! ForwardToApplication(Process(file))
}
This code works just fine. My question: Is there a simpler way to do this? It whould be nice to simple use the reactions mechanism for my application messages:
reactions += {
case Progress(value) => progressBar.value = value
}
Any ideas how to achieve this?

I have extended on gerferras idea of making my application a swing.Publisher. The following class acts as intermediator between a swing.Reactor and an Actor.
import actors.Actor
import swing.Publisher
import swing.event.Event
import swing.Swing.onEDT
case class Send(event: Any)(implicit intermediator: Intermediator) {
intermediator ! this
}
case class Receive(event: Any) extends Event
case class Intermediator(application: Actor) extends Actor with Publisher {
start()
def act() {
loop {
react {
case Send(evt) => application ! evt
case evt => onEDT(publish(Receive(evt)))
}
}
}
}
Now my reactions can include both swing events and application events.
implicit val intermediator = Intermediator(application)
listenTo(intermediator, button)
reactions += {
case ButtonClicked(`button`) => Send(Process(file))
case Receive(Progress(value)) => progressBar.value = value
}
Note how the case class Send provides some syntactic sugar to easily create events and pass them to the intermediator.

Maybe this is simpler but don't know if it's better. Instead of making your application backend an actor, you can create an anonymous actor every time you need to process the file:
reactions += {
case ButtonClicked(`startButton`) => application.process(file, { v: Int => Swing.onEDT(progressBar.value = v) })
}
For the progress update part, you can pass a callback to the process method to be executed every time a new progress is made:
import scala.actors.Actor.actor
def process(f: File, progress: Int => Unit) {
actor {
// process file while notifying the progress using the callback
progress(n)
}
}
Alternatively (haven't tested) you could make your application a scala.swing.Publisher and, instead of using the callback, publish and event every time. So the code could be:
listenTo(startButton, application) //application is a Publisher
reactions += {
case ButtonClicked(`startButton`) => application.process(file)
case Progress(v) => progressBar.value = v
}
And in the application:
import scala.actors.Actor.actor
def process(f: File) {
actor {
// process file while notifying the progress using an scala.swing.event.Event
publish(Progess(n))
}
}

Related

Can we use reflection to instantiate custom TypeScript model from a JSON string?

Is it possible to clone a JSON-generated object or string into a Typescript class which I created? We are building a model of our API using Typescript classes. There’s a base class which they all extend which has common/helper methods. When we do JSON.parse(response) to auto-generate objects it creates simple objects and not our custom objects.
Is there a way we can convert those JSON-generated objects into our custom objects, so long as the field names match up? And, to make things more robust, can this but done where our custom objects’ fields are other custom objects and/or arrays of them?
Here is our code, with comments of what we’d like to achieve.
base-model.ts
export class BaseModelObject {
uuid: string; // All of our objects in our model and JSON have this required field populated
matchUUIDs<T extends BaseModelObject>( obj: T): boolean {
return obj.uuid == this.uuid;
}
}
child-model.ts
import { BaseModelObject } from 'base-model';
export class Child extends BaseModelObject {
}
parent-model.ts
import { BaseModelObject } from 'base-model';
import { Child } from 'child-model';
export class Parent extends BaseModelObject {
children: Child[];
}
JSON payload
{
'uuid': '0632a35c-e7dd-40a8-b5f4-f571a8359c1a',
'children': [
{
'uuid': 'd738c408-4ae9-430d-a64d-ba3f085175fc'
},
{
'uuid': '44d56a0d-ad2d-4e85-b5d1-da4371fc0e5f'
}
]
}
In our components and directives and such, we hope to use the helper function in BaseModelObject:
Component code
let parent: Parent = JSON.parse(response);
console.log(parent.uuid); // Works! 0632a35c-e7dd-40a8-b5f4-f571a8359c1a
// Want this to print ‘true’, but instead we get TypeError: parebt.matchUUID is not a function
console.log(parent.matchUUID(‘0632a35c-e7dd-40a8-b5f4-f571a8359c1a’));
// Want this to print ‘true’, but instead we get TypeError: parent.children[0].matchUUID is not a function
console.log(parent.children[0].matchUUID(‘d738c408-4ae9-430d-a64d-ba3f085175fc’));
The problem is that JSON.parse() is not creating our classes, it’s creating simple objects with key/value pairs. So we’re thinking of “cloning” the JSON-generated object into an instance of our class, like this:
base-model.ts
export class BaseModelObject {
[key: string]: any;
matchUUIDs<T extends BaseModelObject>( obj: T): boolean {
return obj['uuid'] == this['uuid'];
}
cloneFields(obj: any) {
for (let prop in obj) {
this[prop] = obj[prop];
}
}
}
Component code
let parent: Parent = new Parent(); // Creates instance of our class
parent.cloneFields(JSON.parse(response)); // Copy JSON fields to our object
console.log(parent.matchUUID('0632a35c-e7dd-40a8-b5f4-f571a8359c1a')); // prints 'true'
console.log(parent.children[0].matchUUID('d738c408-4ae9-430d-a64d-ba3f085175fc')); // Still throws TypeError: parent.children[0].matchUUID is not a function
The problem now rests in the fact that the cloning of the Parent object did not recursively clone the JSON-generated Child objects into instances of our custom Child class.
Since our Parent object is typed at compile-time and it knows that the data type of the children array is Child[] (our custom class), is there a way to use reflection to instantiate the right class?
Our logic would need to say:
Create an instance of our custom class
Tell our instance to clone the fields from the JSON-generated object
Iterate over the fields in the JSON-generated object
For each field name from the JSON-generated object, find the "type definition" in our custom class
If the type definition is not a primitive or native Typescript type, then instantiate a new instance of that "type" and then clone it's fields.
(and it would need to recursively traverse the whole JSON object structure to match up all other custom classes/objects we add to our model).
So something like:
cloneFields(obj: any) {
for (let prop in obj) {
let A: any = ...find the data type of prop...
if(...A is a primitive type ...) {
this[prop] = obj[prop];
} else {
// Yes, I know this code won't compile.
// Just trying to illustrate how to instantiate
let B: <T extends BaseModelUtil> = ...instantiate an instance of A...
B.cloneFields(prop);
A[prop] = B;
}
}
}
Is it possible to reflect a data type from a class variable definition and then instantiate it at runtime?
Or if I'm going down an ugly rabbit hole to which you know a different solution, I'd love to hear it. We simply want to build our custom objects from a JSON payload without needing to hand-code the same patterns over and over since we expect our model to grow into dozens of objects and hundreds of fields.
Thanks in advance!
Michael
There are several ways to do that, but some requires more work and maintenance than others.
1. Simple, a lot of work
Make your cloneFields abstract and implement it in each class.
export abstract class BaseModelObject {
uuid: string;
matchUUIDs<T extends BaseModelObject>( obj: T): boolean {
return obj.uuid == this.uuid;
}
abstract cloneFields(obj: any);
}
class Parent extends BaseModelObject {
children: Child[];
cloneFields(obj: any) {
this.children = obj.children?.map(child => {
const c = new Children();
c.cloneFields(child);
return c;
});
}
}
2. Simple, hacky way
If there is no polymorphism like:
class Parent extends BaseModelObject {
children: Child[] = [ new Child(), new ChildOfChild(), new SomeOtherChild() ]
}
Property names mapped to types.
const Map = {
children: Child,
parent: Parent,
default: BaseModelObject
}
export class BaseModelObject {
uuid: string;
matchUUIDs<T extends BaseModelObject>( obj: T): boolean {
return obj.uuid == this.uuid;
}
cloneFields(obj: any) {
for (const prop in obj) {
if (obj.hasOwnProperty(prop)) {
this[prop] = obj[prop]?.map(child => { // You have to check it is an array or not,..
const c = new (Map[prop])();
c.cloneFields(child);
return c;
});
}
}
}
}
You can serialize hints into that JSON. Eg. property type with source/target type name and use it to resolve right types.
3. Reflection
Try tst-reflect. It is pretty advanced Reflection system for TypeScript (using custom typescript transformer plugin).
I'm not going to write example, it would be too complex and it depends on your needs.
You can use tst-reflect to list type's properties and get their types. So you'll be able to validace parsed data too.
Just some showcase from its README:
import { getType } from "tst-reflect";
function printTypeProperties<TType>()
{
const type = getType<TType>(); // <<== get type of generic TType ;)
console.log(type.getProperties().map(prop => prop.name + ": " + prop.type.name).join("\n"));
}
interface SomeType {
foo: string;
bar: number;
baz: Date;
}
printTypeProperties<SomeType>();
// or direct
getType<SomeType>().getProperties();
EDIT:
I created a package ng-custom-transformers that simplifies this a lot. Follow its README.
DEMO
EDIT old:
Usage with Angular
Angular has no direct support of custom transformers/plugins. There is a feature request in the Angular Github repo.
But there is a workaround.
You have to add ngx-build-plus. Run ng add ngx-build-plus.
That package defines "plugins".
Plugins allow you to provide some custom code that modifies your webpack configuration.
So you can create plugin and extend Angular's webpack configuration. But here comes the sun problem. There is no public way to add the transformer. There were AngularCompilerPlugin in webpack configuration (#ngtools/webpack) which had private _transformers property. It was possible to add a transformer into that array property. But AngularCompilerPlugin has been replaced by AngularWebpackPlugin which has no such property. But is is possible to override method of AngularWebpackPlugin and add a transformer there. Getting an instance of the AngularWebpackPlugin is possible thanks to ngx-build-plus's plugins.
Code of the plugin
const {AngularWebpackPlugin} = require("#ngtools/webpack");
const tstReflectTransform = require("tst-reflect-transformer").default;
module.exports.default = {
pre() {},
post() {},
config(cfg) {
// Find the AngularWebpackPlugin in the webpack configuration; angular > 12
const angularWebpackPlugin = cfg.plugins.find((plugin) => plugin instanceof AngularWebpackPlugin);
if (!angularWebpackPlugin) {
console.error("Could not inject the typescript transformer: AngularWebpackPlugin not found");
return;
}
addTransformerToAngularWebpackPlugin(angularWebpackPlugin, transformer);
return cfg;
},
};
function transformer(builderProgram) {
return tstReflectTransform(builderProgram.getProgram());
}
function addTransformerToAngularWebpackPlugin(plugin, transformer) {
const originalCreateFileEmitter = plugin.createFileEmitter; // private method
plugin.createFileEmitter = function (programBuilder, transformers, getExtraDependencies, onAfterEmit, ...rest) {
if (!transformers) {
transformers = {};
}
if (!transformers.before) {
transformers = {before: []};
}
transformers.before = [transformer(programBuilder), ...transformers.before];
return originalCreateFileEmitter.apply(plugin, [programBuilder, transformers, getExtraDependencies, onAfterEmit, ...rest]);
};
}
Then it is required to execute ng commands (such as serve or build) with --plugin path/to/the/plugin.js.
I've made working StackBlitz demo.
Resources I've used while preparing the Angular demo:
https://indepth.dev/posts/1045/having-fun-with-angular-and-typescript-transformers
https://medium.com/#morrys/custom-typescript-transformers-with-angular-for-angular-11-12-and-13-40cbdc9cca7b

Java Key Bindings using Groovy Swing Builder

I am trying to write in Groovy application, using "Calculator like" keys functionality. So I would like swing action to be invoked, when I:
press JButton
press corresponding keyboard key, no matther whether there is focus on JButton or not.
Probably the best solution is to use Java Key Bindings, as described here:
https://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html
Following combination of Groovy and Java seems to work well:
package packageSwingTest4
import groovy.swing.SwingBuilder
import javax.swing.*
import java.awt.*
import javax.swing.WindowConstants as WC
class SwingTest4 {
def static b
static void main(args) {
def swing = new SwingBuilder()
def f2Action = swing.action(name:"F2", shortDescription:"F2 button", accelerator:"F2") {
println "F2 pressed"
}
swing.edt {
def f=frame(title: 'Keborad Binding test', size:[400,300],defaultCloseOperation: JFrame.EXIT_ON_CLOSE, show: true) {
panel {
b = button(text: 'F2', actionPerformed:{println "FFFF2222 pressed"})
b.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("F2"),"F2 pressed")
b.getActionMap().put("F2 pressed",f2Action)
}
}
}
}
}
But I do not like to combine Groovy/Java in this way, namely as I will:
- loose nice structure of Groovy code related to structure of GUI
- I have to define Actions separately and not "nicely" as in case of Groovy swing builder
Do you know about "cleaner" solution of my problem using pure Groovy/ SwingBuilder? I would prefer to use Key Bindings, not Key Listener..
Thank you for help and recommendations
Not 100% sure, but do you mean like this:
import groovy.swing.*
import javax.swing.*
def swing = new SwingBuilder()
def f2Action = swing.action(name:"F2 text", shortDescription:"F2 button", keyStroke:"F2") {
println "F2 pressed"
}
swing.edt {
frame(title: 'Keborad Binding test', size:[400,300],defaultCloseOperation: JFrame.HIDE_ON_CLOSE, show: true) {
panel {
button(text: 'F2',
actionPerformed:{ println "FFFF2222 pressed" }) {
action(f2Action)
}
}
}
}
def swing = new SwingBuilder()
def f2Action = swing.action(name:"F2 text", shortDescription:"F2 button", focus: JComponent.WHEN_IN_FOCUSED_WINDOW, keyStroke:"F2") {
println "F2 pressed"
}
def f3Action = swing.action(name:"F3 text", shortDescription:"F2 button", keyStroke:"F3") {
println "F3 pressed"
}
swing.edt {
frame(title: 'Keborad Binding test', size:[400,300],defaultCloseOperation: JFrame.HIDE_ON_CLOSE, show: true) {
panel {
button('F2') {action(f2Action)}
button('F3') {action(f3Action)}
}
}
}

Play 2.4 ActionBuilder / ActionFunction, BodyParsers and JSON

In a Play 2.4 project, I created an Action that do three things.
check that a specific header is present. If not, returns a HTTP error.
use the header value to authenticate the user (a authentication function will be passed to that Action). If the auth fails returns an HTTP error
parse the JSON body to a case class and give it to the Action block code.
To do that, I used the Play Action composition mecanism explain in this page : https://www.playframework.com/documentation/2.4.x/ScalaActionsComposition
but more specifically, the final result I wanted is explained here :
https://www.playframework.com/documentation/2.4.x/ScalaActionsComposition#Putting-it-all-together
I succeeded to write this:
package actions
import play.api.libs.concurrent.Execution.Implicits._
import play.api.libs.json._
import play.api.mvc.Results._
import play.api.mvc.{WrappedRequest, _}
import scala.concurrent.Future
object Actions {
case class WithApiKeyRequest[A](apiKey: String, request: Request[A]) extends WrappedRequest[A](request)
case class ParsedJsonRequest[A](parsed: Any, request: Request[A]) extends WrappedRequest[A](request)
def AuthenticatedAndParsed[T, A](authencation: String => Future[_])(implicit reader: Reads[T]): ActionBuilder[ParsedJsonRequest] =
WithApiKeyHeaderAction andThen AuthentificationAction(authencation) andThen JsonAction
private[this] def WithApiKeyHeaderAction = new ActionBuilder[WithApiKeyRequest] {
override def invokeBlock[A](request: Request[A], block: (WithApiKeyRequest[A]) => Future[Result]): Future[Result] =
request.headers.get("ApiKey") match {
case Some(apiKey: String) => block(WithApiKeyRequest(apiKey, request))
case _ => Future.successful { BadRequest(Json.obj("errors" -> "ApiKey header needed")) }
}
}
private[this] def AuthentificationAction(authencationFunction: String => Future[_]) = new ActionFilter[WithApiKeyRequest] {
override protected def filter[A](request: WithApiKeyRequest[A]): Future[Option[Result]] =
authencationFunction(request.apiKey)
.map { _ => None } // Do not filter the request
.recover { case _ => Some(Unauthorized) }
}
private[this] def JsonAction[T](implicit reader: Reads[T]) = new ActionBuilder[ParsedJsonRequest] {
composeParser(BodyParsers.parse.json)
override def invokeBlock[A](request: Request[A], block: (ParsedJsonRequest[A]) => Future[Result]): Future[Result] = {
request.body.asInstanceOf[JsValue].validate[T].fold(
errors => Future { BadRequest(Json.obj("errors" -> JsError.toJson(errors))) },
(parsedJson: T) => block(ParsedJsonRequest(parsedJson, request))
)
}
}
}
It seems to work well but it's not perfect because I'm force to use the Any type in the case class ParsedJsonRequest[A](parsed: Any, request: Request[A]) because it seems that I can't do that:
case class ParsedJsonRequest[T, A](parsed: T, request: Request[A])
Is it possible to do that ?
Do you think I can improve my solution ? How ?
My question is not about how to do Action composition. I understand how it works and I succeeded to write my ActionBuilders and my wanted composition.
My question is about how to improve my composition.
Thanks
Jules
Rather than making a new ActionBuilder for a JsonRequest, I would simply use the AuthentificationAction ActionBuilder and pass it a json BodyParser:
AuthentificationAction(parse.json) {
request => // Note that the request has type Request[JsValue]
doStuffWithJson(request.body)
}
Any action using this builder, will get a Request[JsValue] rather than a Request[AnyContent].

Right Click on a Button / Scala

I'm currently trying to code a minesweeper using scala, but I can't find the way to listen to a right click on a button.
I've already searched on the Internet the way to do it, but I definitely was not able to find it.
If anyone could help me out, I would be really grateful :)
Thanks,
Schnipp
(Note: Scala is a new language to me and I am not a Java user, so I am sorry if my questions sound dumb)
EDIT:
I am trying to find (or implement) a function 'ButtonClickedRight' that could listen to a right-click on a button.
like this
import scala.swing._
import scala._
import scala.swing.event._
object Right extends MainFrame with App {
title = ""
visible = true
val b = new button("")
listenTo(b)
reactions += {
case ButtonClicked(`b`) => *code*
case ButtonClickedRight(`b`) => *code*
}
}
EDIT 2 --
I would like to know if the user has clicked on the Button "1" or not. The problem I have is that this code prints "Mouse clicked at " + e.point+" type "+e.modifiers when I click on the label but not on the button.
object App extends SimpleSwingApplication {
lazy val ui = new GridPanel(2,1) {
contents += new Button("1")
contents += new Label("2")
listenTo(mouse.clicks)
reactions += {
case e: MouseClicked =>
println("Mouse clicked at " + e.point+" type "+e.modifiers)
}
}
def top = new MainFrame {
contents = ui
visible = true
preferredSize = new Dimension(500,500)
}
}
Button events are fired through a specific publisher .mouse.clicks.
import scala.swing._
import scala.swing.event._
object App extends SimpleSwingApplication {
lazy val ui = new GridPanel(2,1) {
val button = new Button("1")
contents += button
contents += new Label("2")
listenTo(button.mouse.clicks) // !
reactions += {
case evt # MouseClicked(`button`, pt, _, _, _) =>
val which = evt.peer.getButton
if (which > 1) {
println(s"Mouse clicked at (${pt.x}; ${pt.y}) - button: $which")
}
}
}
lazy val top = new MainFrame {
contents = ui
size = new Dimension(500,500)
}
}
Note that at least on Linux my right button has number 3 not 2. You could also use the triggersPopup flag, but then you must ensure to monitor both MousePressed and MouseReleased, as this flag is platform-dependent.
I think that you are on the right path, for my understanding of scala swings I think that the problem is that you are not attaching the listener correctly. For one I would assign the button to a value and call listenTo only on it:
val button = new Button("1")
listenTo(button)
Then, in the reactions, I would write the pattern checking in the event that it comes from the button (probably redundant if you only call listenTo passing the button) and that it has the correct button:
case ButtonClicked(b) if b == button && b.peer.getButton == MouseEvent.BUTTON_2 => ...
So the code you provided in your edit would become:
object App extends SimpleSwingApplication {
lazy val ui = new GridPanel(2,1) {
val button = new Button("1")
contents += button
contents += new Label("2")
listenTo(button)
reactions += {
case evt # MouseClicked(b, pt, _, _, _) if b == button && evt.peer.getButton == java.awt.event.MouseEvent.BUTTON2 =>
println(s"Mouse clicked at (${pt.x}; ${pt.y}) - button: ${evt.peer.getButton}")
}
}
def top = new MainFrame {
contents = ui
visible = true
preferredSize = new Dimension(500,500)
}
}
The following works for me:
new Button {
listenTo(mouse.clicks)
reactions += {
case MouseClicked(_, _, c, _, _) => handleClick(c == 0)
}
}
def handleClick(isLeftClick: Boolean): Unit = {
//
}

Render json data to the view with play! scala 2.2.3

Here is a beginner question :
I have defined Event like this :
case class Event(id: Pk[Long], name: String, userId: Pk[Long])
object Event {
private val EventParser: RowParser[Event] = {
get[Pk[Long]]("id") ~
get[String]("name") ~
get[Pk[Long]]("userId") map {
case id ~ name ~ userId => Event(id, name, userId)
}
}
def findAll(): Seq[Event] = {
DB.withConnection { implicit connection =>
SQL("select * from events").as(EventParser *)
}
}
}
And I render it to the view like this :
def events = Action {
val events: Seq[Event] = Event.findAll()
Ok(views.html.events(events))
}
But I would like to return Json data.
Json.toJson(events) can't be used since events type is Seq.
I didn't find a good tutorial on the subject and I tried to follow this answer : play framework working with json objects in Scala but it doesn't seem to work with play 2.2.
So my question is : Do you know an easy way to render a sequence in Json to the view after accessing database?
Try this:
import play.api.libs.json._
object Event {
...
implicit def pkWrites[T : Writes]: Writes[Pk[T]] = Writes {
case anorm.Id(t) => implicitly[Writes[T]].writes(t)
case anorm.NotAssigned => JsNull
}
implicit val eventWrites = Json.writes[Event]
}