In this post, I will model a very simple Internal DSL in Scala that can execute this script:
Click the "OK".button;
As you can see, as is the case with internal DSLs, I couldn't quite get the syntax the way that I wanted, but it's pretty close. A lesson learned is that you can use internal DSLs in situations where you aren't too picky about the syntax. In my next post I will share an external DSL that has the exact syntax that I want (and some other benefits also).
Here's how to read the code (at the bottom of this post):
- Imports
- I create a Scala object (MehtodRunner) that extends App (basic Scala stuff)
- I create the Sikuli screen object (main object for executing Sikuli commands)
- I create the "Click" object (the first word in my syntax example above)
- I define a "the" method (second word in my syntax) which takes in a single Component as a parameter.
- When called, it creates an image filename and checks to see if the file exists or not.
- If it exists, it asks Sikuli to find a matching image on the screen and click it.
- If it does not exist, it asks Sikuli to capture the image and store it for the next time. See my last post for a more in depth explanation of how this works.
- The next section converts the "OK".button syntax into a Component object. It's basic Scala Internal DSL stuff. For more information on this topic, take a look at Designing Internal DSLs in Scala by Debasish Ghosh.
- There is a section that does the work of capturing screenshots in Sikuli (called from "the" method above). Again, see my last post for an explanation.
- Finally, I embed the Click the "OK".button; script inline. This is one of the benefits (and drawbacks) of an internal DSL.
----
import org.sikuli.script.ScreenHighlighter
import org.sikuli.script.Screen
import org.sikuli.script.Location
import org.sikuli.script.CapturePrompt
import org.sikuli.script.Observer
import org.sikuli.script.Subject
import java.io.File
import org.sikuli.script.Pattern
import javax.imageio.ImageIO
object MethodRunner extends App {
// =============================
// Sikuli screen object
var screen = new Screen()
// =============================
// Script Actions
object Click {
def the(in: Component) {
println("Click the " + compDesc(in))
var filename = safeFilename(in);
println(filename)
var file = new File(filename);
if (file.exists()) {
screen.click(new Pattern(filename));
} else {
captureComponent = in
cp.prompt("I don't know what the '" + compDesc(in) +
"' looks like. Please select it.");
}
}
}
// =============================
// this supports the syntax "OK".button
implicit def toComponentBuilder(in: String) =
new ComponentBuilder(in)
class ComponentBuilder(name: String) {
def button(): Component = {
return new button(name);
}
}
abstract class Component(val name: String)
class button(name: String) extends Component(name)
def compDesc(in: Component) = in.name + "." +
in.getClass().getSimpleName()
// =============================
// capture and store screenshots for Sikuli
var cp = new CapturePrompt(screen)
cp.addObserver(CaptureObserver)
var captureComponent: Component = null
var USER_HOME = System.getProperty("user.home");
def safeFilename(comp: Component) = USER_HOME +
"/Method/img/cache/" + comp.name.replaceAll("\\W+", "_") +
"_" + comp.getClass().getSimpleName() + ".png";
object CaptureObserver extends Observer {
def update(s: Subject) {
var img = cp.getSelection()
ImageIO.write(img.getImage(), "png",
new File(safeFilename(captureComponent)));
cp.close()
}
}
// =============================
// The Script
Click the "OK".button;
}
No comments:
Post a Comment