I have a problem with my custom subclass of Page. Here is a short example.
def main(args: Array[String]): Unit = {
val pane = new TabbedPane
pane.pages += new LanguagePage("common_yes", new Label)
println(pane.pages(0).isInstanceOf[LanguagePage])
}
class LanguagePage(languageKey: String, com: Component)
extends Page("", com, null) {
def method() {...}
}
When I run the program, false is printed. It would be nice to know why this happens and how I can access my added page again. In my case I need to run the method that the subclass has to adjust the title string based on the language that is set.
If you look at the source
class TabbedPane extends Component with Publisher {
object pages extends BufferWrapper[Page] {
def apply(n: Int) =
new Page(TabbedPane.this, peer.getTitleAt(n),
UIElement.cachedWrapper[Component]
(peer.getComponentAt(n).asInstanceOf[javax.swing.JComponent]),
peer.getToolTipTextAt(n))
you always get a new instance.
Looking at
class TabbedPane extends Component with Publisher {
object pages extends BufferWrapper[Page] {
def +=(t: Page): this.type = {
t.parent = TabbedPane.this
peer.addTab(t.title, null, t.content.peer, t.tip)
this
}
the argument t is not recorded at all.
But you can change the title using
pane.pages(0).title = "Title 2"
so you could define a logical page (a companion in addition to the Swing page) separately. Using a reference to the pane and the tab index this class can change the title.
Related
I would like to develop some of the webcomponents in a Polymer 2.0 project with scala.js. While there is a wonderful demo-project on github demonstrating how it works with Polymer 1.0. I cannot get something similar to work with Polymer 2.0 and the native Element-registration technique.
A simple facade might look like the following
#ScalaJSDefined
class PolymerElement extends PolymerBase {
def is: String = ""
def properties: js.Dynamic = js.Dynamic.literal()
}
#js.native
#JSGlobal("Polymer.Element")
class PolymerBase extends HTMLElement
The actual Element:
#JSExportTopLevel("MyElement")
#ScalaJSDefined
class MyElement extends PolymerElement {
private var label = "init"
override def is = "my-element"
override def properties = js.Dynamic.literal(
"label" -> Map(
"type" -> "String",
"value" -> "init",
"notify" -> true
).toJSDictionary
)
def testMe = {
println(label)
}
}
object MyElement {
#JSExportStatic
val is: String = MyElement.is
#JSExportStatic
val properties: js.Dynamic = MyElement.properties
}
No matter whether I take the old style element registration Polymer(MyElement) or the platform native variant window.customElement.define(MyElement.is, MyElement)
It obviously throws an exception as MyElement isn't instatiable with new MyElement.
It throws the exception:
Uncaught TypeError: Class constructor PolymerElement cannot be invoked without 'new'
Studying the Scala.js facade writing guide, I already tried a lot of facade variants declaring PolymerElement and PolymerBase abstract.
A possible solution that comes to my mind is, writing a native JavaScript Class, that indeed is instantiable and using #js.native facades on them. But I'm looking for a way to achieve it with something Scala.js 0.6.16 provides.
Updated version (2018-04)
Ok, this is possible helps to someone else too and I decided to publish my new version of it.
I'm using this pure ScalaJS solution to integrate with Polymer2/CustomElements.
My environment is:
Scala : 2.12
ScalaJS: 0.6.22
And also : "org.scala-js" %%% "scalajs-dom" % "0.9.2"
ScalaJS options:
"-P:scalajs:sjsDefinedByDefault"
I've created some ScalaJS facades for CustomElements and Polymer 2 and published them here - https://bitbucket.org/latestbit/scalymer/src/tip/src/main/scala/org/latestbit/sjs/polymer2/?at=default
They're not full-featured Polymer facades, just in the very beginning state, but they are working for me.
And you can use them easily without any hacks like:
#JSExportTopLevel(name = "TestElement")
class TestElement() extends Polymer.Element {
override def connectedCallback(): Unit = {
super.connectedCallback()
global.console.log(s"Attribute name ${getAttribute("name")}. Body is ${dom.document.querySelector("body")}")
global.console.log(s"${this.$.selectDynamic("testCl")}")
global.console.log(s"${$$("testCl")}")
}
}
object TestElement {
#JSExportStatic
def is = "test-element"
#JSExportStatic
def properties = js.Dictionary(
"name" -> js.Dictionary(
"type" -> "String"
)
)
}
Then register it also in Scala like:
object TestJsApplication {
def main() : Unit = {
Globals.customElements.define(TestElement.is,
js.constructorOf[TestElement]
)
}
}
The html part is usual:
<dom-module id="test-element">
<template>
<span id="testCl">Not much here yet.</span>
This is <b>[[name]]</b>.
</template>
</dom-module>
You will find the complete example here - https://bitbucket.org/latestbit/scalymer/src
An old try to solve (for historical purposes)
Ok, this is the best 'solution' I've found.
This is not solving it completely, but I hope it'll be a helpful trick while we're expecting sjs improvements in this area.
Get any library to 'mixin' js classes. I've used https://github.com/rse/aggregation.
Create your ScalaJS component but don't try to inherit it from Polymer.Element directly:
#ScalaJSDefined
#JSExportTopLevel("TestPolymerElement")
class TestPolymerElement extends js.Object {
def test = g.console.log("Hello from scala")
}
object TestPolymerElement {
#JSExportStatic
def is = "test-polymer-element"
}
Create a JS pure "companion" class to inherit Polymer.Element and ScalaJS component as a mixin and register it:
class TestPolymerElementJs extends aggregation(Polymer.Element,TestPolymerElement) {
}
customElements.define(TestPolymerElementJs.is, TestPolymerElementJs);
Also, you can define the properties and manage them in ScalaJS like:
#ScalaJSDefined
#JSExportTopLevel("TestPolymerElement")
class TestPolymerElement(val name : String) extends js.Object {
def test = g.console.log(s"Hello from ${name}")
}
object TestPolymerElement {
#JSExportStatic
def is = "test-polymer-element"
#JSExportStatic
def properties = js.Dictionary (
"name" -> js.Dictionary(
"type" -> "String"
)
)
}
I'm trying to persist Maps of properties as single JSON-encoded columns, as shown in this question.
The problem I'm having is that apparently transient properties cannot be set in the default map constructor. Given any transient field:
class Test {
//...
String foo
static transients = ['foo']
}
It seems that the map constructor (which Grails overrides in various ways) simply discards transient fields:
groovy:000> t = new Test(foo:'bar')
===> Test : (unsaved)
groovy:000> t.foo
===> null
While direct assignment (through the setter method) works as expected:
groovy:000> c.foo = 'bar'
===> bar
groovy:000> c.foo
===> bar
Is there a way to make the map constructor accept transient fields?
Or rather: is there a better way to persist a Map as a single JSON-encoded DB field, rather than the method shown in the linked question?
Here's the complete example:
import grails.converters.JSON
class JsonMap {
Map data
String dataAsJSON
static transients = ['data']
def afterLoad() { data = JSON.parse(dataAsJSON) }
def beforeValidate() { dataAsJSON = data as JSON }
}
I can set data using the setter (which will then be converted into dataAsJSON) but not using the map constructor.
The map constructor in GORM uses the data binding mechanism, and transient properties are not data-bindable by default. But you can override this using the bindable constraint
class Test {
//...
String foo
static transients = ['foo']
static constraints = {
foo bindable:true
}
}
I've also replied to your original question, that you don't need json conversion to achieve what you need. However, If you need json conversion badly, why don't you implement it in your getters/setters?
class Test {
String propsAsJson
static transients = ['props']
public Map getProps() {
return JSON.parse(propsAsJson)
}
public void setProps(Map props) {
propsAsJson = props as JSON
}
}
//So you can do
Test t = new Test(props : ["foo" : "bar"])
t.save()
In this way you encapsulate the conversion stuff, and in DB you have your properties as Json.
You can simplify your case by adding the JSON-conversion methods to your domain class, they should have nothing to do with GORMing:
class Test {
String title
void titleFromJSON( json ){
title = json.toStringOfSomeKind()
}
def titleAsJSON(){
new JSON( title )
}
}
I've written a Scala program that I'd like to be triggered through a UI (also in Swing). The problem is, when I trigger it, the UI hangs until the background program completes. I got to thinking that the only way to get around this is by having the program run in another thread/actor and have it update the UI as and when required. Updating would include a status bar which would show the file currently being processed and a progressbar.
Since Scala actors are deprecated, I'm having a tough time trying to plough through Akka to get some kind of basic multithreading running. The examples given on the Akka website are also quite complicated.
But more than that, I'm finding it difficult to wrap my head around how to attempt this problem. What I can come up with is:
Background program runs as one actor
UI is the main program
Have another actor that tells the UI to update something
Step 3 is what is confounding me. How do I tell the UI without locking up some variable somewhere?
Also, I'm sure this problem has been solved earlier. Any sample code for the same would be highly appreciated.
For scala 2.10
You can use scala.concurrent.future and then register a callback on completion. The callback will update the GUI on the EDT thread.
Lets do it!
//in your swing gui event listener (e.g. button clicked, combo selected, ...)
import scala.concurrent.future
//needed to execute futures on a default implicit context
import scala.concurrent.ExecutionContext.Implicits._
val backgroundOperation: Future[Result] = future {
//... do that thing, on another thread
theResult
}
//this goes on without blocking
backgroundOperation onSuccess {
case result => Swing.onEDT {
//do your GUI update here
}
}
This is the most simple case:
we're updating only when done, with no progress
we're only handling the successful case
To deal with (1) you could combine different futures, using the map/flatMap methods on the Future instance. As those gets called, you can update the progress in the UI (always making sure you do it in a Swing.onEDT block
//example progress update
val backgroundCombination = backgroundOperation map { partial: Result =>
progress(2)
//process the partial result and obtain
myResult2
} //here you can map again and again
def progress(step: Int) {
Swing.onEDT {
//do your GUI progress update here
}
}
To deal with (2) you can register a callback onFailure or handle both cases with the onComplete.
For relevant examples: scaladocs and the relevant SIP (though the SIP examples seems outdated, they should give you a good idea)
If you want to use Actors, following may work for you.
There are two actors:
WorkerActor which does data processing (here, there is simple loop with Thread.sleep). This actor sends messages about progress of work to another actor:
GUIUpdateActor - receives updates about progress and updates UI by calling handleGuiProgressEvent method
UI update method handleGuiProgressEvent receives update event.
Important point is that this method is called by Actor using one of Akka threads and uses Swing.onEDT to do Swing work in Swing event dispatching thread.
You may add following to various places to see what is current thread.
println("Current thread:" + Thread.currentThread())
Code is runnable Swing/Akka application.
import akka.actor.{Props, ActorRef, Actor, ActorSystem}
import swing._
import event.ButtonClicked
trait GUIProgressEventHandler {
def handleGuiProgressEvent(event: GuiEvent)
}
abstract class GuiEvent
case class GuiProgressEvent(val percentage: Int) extends GuiEvent
object ProcessingFinished extends GuiEvent
object SwingAkkaGUI extends SimpleSwingApplication with GUIProgressEventHandler {
lazy val processItButton = new Button {text = "Process it"}
lazy val progressBar = new ProgressBar() {min = 0; max = 100}
def top = new MainFrame {
title = "Swing GUI with Akka actors"
contents = new BoxPanel(Orientation.Horizontal) {
contents += processItButton
contents += progressBar
contents += new CheckBox(text = "another GUI element")
}
val workerActor = createActorSystemWithWorkerActor()
listenTo(processItButton)
reactions += {
case ButtonClicked(b) => {
processItButton.enabled = false
processItButton.text = "Processing"
workerActor ! "Start"
}
}
}
def handleGuiProgressEvent(event: GuiEvent) {
event match {
case progress: GuiProgressEvent => Swing.onEDT{
progressBar.value = progress.percentage
}
case ProcessingFinished => Swing.onEDT{
processItButton.text = "Process it"
processItButton.enabled = true
}
}
}
def createActorSystemWithWorkerActor():ActorRef = {
def system = ActorSystem("ActorSystem")
val guiUpdateActor = system.actorOf(
Props[GUIUpdateActor].withCreator(new GUIUpdateActor(this)), name = "guiUpdateActor")
val workerActor = system.actorOf(
Props[WorkerActor].withCreator(new WorkerActor(guiUpdateActor)), name = "workerActor")
workerActor
}
class GUIUpdateActor(val gui:GUIProgressEventHandler) extends Actor {
def receive = {
case event: GuiEvent => gui.handleGuiProgressEvent(event)
}
}
class WorkerActor(val guiUpdateActor: ActorRef) extends Actor {
def receive = {
case "Start" => {
for (percentDone <- 0 to 100) {
Thread.sleep(50)
guiUpdateActor ! GuiProgressEvent(percentDone)
}
}
guiUpdateActor ! ProcessingFinished
}
}
}
If you need something simple you can run the long task in a new Thread and just make sure to update it in the EDT:
def swing(task: => Unit) = SwingUtilities.invokeLater(new Runnable {
def run() { task }
})
def thread(task: => Unit) = new Thread(new Runnable {
def run() {task}
}).run()
thread({
val stuff = longRunningTask()
swing(updateGui(stuff))
})
You can define your own ExecutionContext which will execute anything on the Swing Event Dispatch Thread using SwingUtilities.invokeLater and then use this context to schedule a code which needs to be executed by Swing, still retaining the ability to chain Futures the Scala way, including passing results between them.
import javax.swing.SwingUtilities
import scala.concurrent.ExecutionContext
object OnSwing extends ExecutionContext {
def execute(runnable: Runnable) = {
SwingUtilities.invokeLater(runnable)
}
def reportFailure(cause: Throwable) = {
cause.printStackTrace()
}
}
case ButtonClicked(_) =>
Future {
doLongBackgroundProcess("Timestamp")
}.foreach { result =>
txtStatus.text = result
}(OnSwing)
I wrote my first console app in Scala, and I wrote my first Swing app in Scala -- in case of the latter, the entry point is top method in my object extending SimpleSwingApplication.
However I would like to still go through main method, and from there call top -- or perform other equivalent actions (like creating a window and "running" it).
How to do it?
Just in case if you are curious why, the GUI is optional, so I would like to parse the command line arguments and then decide to show (or not) the app window.
If you have something like:
object MySimpleApp extends SimpleSwingApplication {
// ...
}
You can just call MySimpleApp.main to start it from the console. A main method is added when you mix SimpleSwingApplication trait. Have a look at the scaladoc.
Here is the most basic example:
import swing._
object MainApplication {
def main(args: Array[String]) = {
GUI.main(args)
}
object GUI extends SimpleSwingApplication {
def top = new MainFrame {
title = "Hello, World!"
}
}
}
Execute scala MainApplication.scala from the command line to start the Swing application.
If you override the main method that you inherit from SimpleSwingApplication, you can do whatever you want:
object ApplicationWithOptionalGUI extends SimpleSwingApplication {
override def main(args: Array[String]) =
if (parseCommandLine(args))
super.main(args) // Starts GUI
else
runWithoutGUI(args)
...
}
I needed main.args to be usable in SimpleSwingApplication. I wanted to give a filename to process as an argument in CLI, or then use JFileChooser in case the command line argument list was empty.
I do not know if there is simple method to use command line args in SimpleSwingApplication, but how I got it working was to define, in demoapp.class:
class demoSSA(args: Array[String]) extends SimpleSwingApplication {
....
var filename: String = null
if (args.length > 0) {
filename = args(0)
} else {
// use JFileChooser to select filename to be processed
// https://stackoverflow.com/questions/23627894/scala-class-running-a-file-chooser
}
....
}
object demo {
def main(args: Array[String]): Unit = {
val demo = new demoSSA(args)
demo.startup(args)
}
}
Then start the app by
demo [args], by giving filename as a CLI argument or leaving it empty and program uses JFileChooser to ask it.
Is there a way to get to the args of main() in SimpleSwingApplication singleton object?
Because it doesn't work to parse 'filename' in overridden SimpleSwingApplication.main and to use 'filename' in the singleton object it needs to be declared (val args: Array[String] = null;), not just defined (val args: Array[String];). And singleton objecs inherited from SSA cannot have parameters.
(Edit)
Found one another way:
If whole demoSSA is put inside overridden main() or startup(), then top MainFrame needs to defined outside as top = null and re-declare it from startup() as this.top:
object demo extends SimpleSwingApplication {
var top: MainFrame = null
override def startup(args: Array[String]) {
... // can use args here
this.top = new MainFrame {
... // can use args here also
};
val t = this.top
if (t.size == new Dimension(0,0)) t.pack()
t.visible = true
}
}
But I think I like the former method more, with separated main object.It has at least one level indentation less than the latter method.
I'm trying to save a record with JsonObjectField (using lift-mongo-
record in Play framework) but it is empty in database collection.
That's my code:
Define classes:
class Wish extends MongoRecord[Wish] with MongoId[Wish] {
def meta = Wish
object body extends StringField(this, 1024)
object tags extends MongoListField[Wish, String](this)
object form extends JsonObjectField[Wish, Criterion](this, Criterion) {
def defaultValue = null.asInstanceOf[Criterion]
}
}
object Wish extends Wish with MongoMetaRecord[Wish] {
override def collectionName = "wishes"
}
case class Criterion (key: String, value: String) extends JsonObject[Criterion] {
def meta = Criterion
}
object Criterion extends JsonObjectMeta[Criterion]
I try to save record in that way:
Wish.createRecord.body("body").tags(List("tags", "test")).form(new Criterion("From", "Arbat")).save
And in mongodb collection I have smth like:
{ "_id" : ObjectId("4dfb6f45e4b0ad4484d3e8c6"), "body" : "body", "tags" : [ "tags", "test" ], "form" : { } }
What am I doing wrong?
Your code looks fine. Maybe try using MongoCaseClassField like:
object form extends MongoCaseClassField[Wish, Criterion](this)
case class Criterion (key: String, value: String){}
//btw you can leave out the "new" for case classes - it calls
//the automatically generated apply method
Wish.createRecord.body("body").tags(List("tags", "test"))
.form(Criterion("From","Arbat")).save
Here's an example for MongoCaseClassListField:
object form extends MongoCaseClassListField[Wish, Criterion](this)
Wish.createRecord.body("body").tags(List("tags","test"))
.form(List(Criterion("key1","val1"), Criterion("key2", "val2"))
.save