I made a small application in Akka that has a hierarchy of actors (there is an ActorA that has several ActorB actors, and these ActorB actors have several ActorC actors). Now I'd like to add a small UI. This UI doesn't have any buttons, but just some labels that are modified when the ActorC actors receive certain messages from other actors. The problem that I have is that I must create a label for each ActorC actor, but the number (of ActorC actors) is not always the same, so I must create the ActorC actors in the first place, and then I must create the labels. I searched for tutorials on Scala Swing, but i just found old documentation or simple examples that are not useful in my case (something like press a button to do something). I even saw that several people used Java Swing in their Akka applications. I found a tool called Kamon, but i need to create an interface, not just monitoring my actors. Is there someone that can help me?
Swing is deprecated in favor of JavaFX. That said, JavaFX allows you to create a "rich client" application. It sounds like too heavy of a solution for what you want.
Perhaps a simple web UI? Have you looked at playframework.com? Your interface would just be some web pages that can talk to the actors. Play and Akka work well together, and it's pretty easy to get started with Play using available tutorials and templates.
There are two aspects to your design. The first aspect is the communication between your UI and your working actors. The second is the operation of your UI.
A scenario might be that your UI starts up and fires up a Swing EDT thread to display your status window. An actor is also created and it reaches out to your Actor A and says "ready to go". Actor A forwards the message to all Actor Bs, which forward it to the Actor Cs. Each Actor C then registers itself with the UI by sending a message "I am here" and "this is my status".
def receive = {
...
case ReadyToGo =>
myMonitor = sender
sender ! "ready"
}
The UI-Actor takes each of the registration messages and maps it to a label, creating it if needed. As each Actor C changes its state, it sends a new message to UI-Actor, saying "I am still here" and "this is my status".
The UI-Actor is the go-between the Akka world and the UI. This is the tricky part, since the UI-Actor runs on a different thread than the Swing event-dispatch-thread. It must put a task on the Swing queue for processing by the Swing thread in a thread-safe manner. Some rough code:
// assume labelMap is a map from ActorRef to Label, ordered by insertion
def receive = {
case IAmHere(status) =>
val actorC = sender
if ( ! (labelMap contains actorA) ) {
addStatusLabel(actorC, status)
}
case IAmStillHere(status) =>
updateStatusLabel(actorC, status)
}
def addStatusLabel(actorC: ActorRef, status: String) = {
Swing.onEDT {
labelMap(actorC) = new Label(status)
// recalculate the panel size
// repaint
}
}
def updateStatusLabel(actorC: ActorRef, status: String) = {
Swing.onEDT {
labelMap(actorC).text = status
// repaint
}
}
I leave it up to you to lay out the UI, the container, whether statuses are Strings or not, how to handle Actor Cs that go missing, etc.
Related
I would like to offer the opportunity to view output from the same data, in a spreadsheet, TBA sidebar and, ideally another type of HTML window for output created, for example, with a JavaScript Library like THREE.
The non Google version I made is a web page with iframes that can be resized, dragged and opened/closed and, most importantly, their content shares the same record object in the top window. So, I believe, perhaps naively, something similar could be made an option inside this established and popular application.
At the very least, the TBA trial has shown me it useful to view and manipulate information from either sheet or TBA. The facility to navigate large building projects, clone rooms and floors, and combine JSON records (stored in depositories like myjson) for collaborative work is particularly inspiring for me.
I have tried using the sidebar for different HTML files, but the fact only one stays open is not very useful, and frankly, sharing record objects is still beyond me. So that is the main question. Whether Google people would consider an extra window type is probably a bit ambitious, but I think worth asking.
You can't maintain a global variable across calls to HtmlService. When you fire off an HtmlService instance, which runs in the browser, the server side code that launched it exits.
From that point control is client side, in the HtmlService code. If you then launch a server side function (using google.script.run from client side), a new instance of the server side script is launched, with no memory of the previous instance - which means that any global variables are re-initialized.
There are a number of techniques for peristing values across calls.
The simplest one of course is to pass it to the htmlservice in the first place, then to pass it back to server side as an argument to google.script.run.
Another is to use property service to hold your values, and they will still be there when you go back, but there is a 9k maximum entry size
If you need more space, then the cache service can hold 100k in a single entry and you can use that in the same way (although there is a slight chance it will be cleaned away -- although it's never happened for me)
If you need even more space, there are techniques for compressing and/or spreading a single object across several cache entries - as documented here http://ramblings.mcpher.com/Home/excelquirks/gassnips/squuezer. This same method supports Google Drive, or Google cloud storage if you need to persist data even longer
Of course you can't pass non-stringifiable objects like functions and so on, but you can postpone their evaluation and allow the initialized server side script to evaulate them, and even share the same code between server, client or across projects.
Some techniques for that are described in these articles
http://ramblings.mcpher.com/Home/excelquirks/gassnips/nonstringify
http://ramblings.mcpher.com/Home/excelquirks/gassnips/htmltemplateresuse
However in your specific example, it seems that the global data you want is fetched from an external api call. Why not just retrieve it client side in any case ? If you need to do something with it server side, then pass it to the server using google.script.run.
window.open and window.postMessage() solved both the problems I described above.
I hope you will be assured from the screenshot and code that the usefulness of Google sheets can be extended for the common good. At the core is the two methods for inputting, copying and reviewing textual data - spreadsheet for a slice through a set of data, and TBA for navigation of associations in the Trail (x axis) and Branches (y axis), and for working on Aspects (z axis) of the current selection that require attention, in collaborations, from different interests.
So, for example, a nurse would find TBA useful for recording many aspects of an examination of a patient, whereas a pharmacist might find a spreadsheet more useful for stock control. Both record their data in a common object I call 'nset' (hierarchy of named sets), saved in the cloud and available for distribution in collaborative activities.
TBA is also useful for cloning large sets of records. For example, one room, complete with furniture can be replicated on one floor, then that floor, complete with rooms can be replicated for a complete tower.
Being able to maintain parallel nset objects in multiple monitor windows by postMessage means unrivalled opportunities to display the same data in different forms of multimedia, including interactive animation, augmented reality, CNC machine instruction, IOT controls ...
Here is the related code:
From the TBA in sidebar:
window.addEventListener("message", receiveMessage, false);
function openMonitor(nset){
var params = [
'height=400',
'width=400'
].join(',');
let file = 'http://glasier.hk/blazer/model.html';
popup = window.open(file,'popup_window', params);
popup.moveTo(100,100);
}
var popup;
function receiveMessage(event) {
let ed,nb;
ed = event.data;
nb = typeof ed === "string"? ed : nb[0];
switch(nb){
case "Post":
console.log("Post");
popup.postMessage(["Refreshing nset",nset], "http:glasier.hk");
break;
}
}
function importNset(){
google.script.run
.withSuccessHandler(function (code) {
root = '1grsin';
trial = 'msm4r';
orig = 'ozs29';
code = orig;
path = "https://api.myjson.com/bins/"+code;
$.get(path)
.done((data, textStatus, jqXHR) => {
nset = data;
openMonitor(nset);
cfig = nset.cfig;
start();
})
})
.sendCode();
}
From the popup window:
$(document).ready(function(){
name = $(window).attr("name");
if(name === "workshop"){
tgt = opener.location.href;
}
else{
tgt = "https://n-rxnikgfd6bqtnglngjmbaz3j2p7cbcqce3dihry-0lu-script.googleusercontent.com"
}
$("#notice").html(tgt);
opener.postMessage("Post",tgt);
$(window).on("resize",function(){
location.reload();
})
})
}
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event) {
let ed,nb;
ed = event.data;
nb = typeof ed === "string"? ed : ed[0];
switch(nb){
case "Post": popup.postMessage(["nset" +nset], "*"); break;
default :
src = event.origin;
notice = [ed[0]," from ",src ];
console.log(notice);
// $("#notice").html(notice).show();
nset = ed[1];
cfig = nset.cfig;
reloader(src);
}
}
I should explain that the html part of the sidebar was built on a localhost workshop, with all styles and scripts compiled into a single file for pasting in a sidebar html file. The workshop also is available online. The Google target is provided by event.origin in postMessage. This would have to be issued to anyone wishing to make different monitors. For now I have just made the 3D modelling monitor with Three.js.
I think, after much research and questioning around here, this should be the proper answer.
The best way to implement global variables in GAS is through userproperties or script properties.https://developers.google.com/apps-script/reference/properties/properties-service. If you'd rather deal with just one, write them to an object and then json.stringify the object (and json.parse to get it back).
So I'm seeing some bizarre behavior in an appwidget that I wrote.
The widget itself is quite simple - it reads a few values from persistent storage and then displays them as text. Nothing fancy in the layout - just a FrameLayout root element with some Linear Layout and TextView children.
The widget has a simple configuration activity associated with it.
The bizarre behavior is that the widget will initially show "Problem Loading Widget" after the user closes the configuration activity, and then after a few seconds it shows a "Google Sound Search" button (and clicking on the button actually does launch Google Sound Search). Then, after a few more seconds, it finally shows the expected display.
I am away from my code right now, so I'll have to wait until tonight to post code snippets. However, in the meantime, can anyone provide some insight into how such a thing could happen? Has anyone else ever experienced this? How could another widget "hijack" mine?
Thanks,
-Ron
Here are some screenshots:
There are a couple of issues with your widget and there are answers to all of them (although you didn't post any code so some of my statements are based on assumptions):
"Problem loading widget": this is the default view Android uses before the widget is initialized and the layout updated. Simply add the following line to your widget xml configuration (to show a loading message instead of the problem message):
android:initialLayout="#layout/my_cool_widget_loading_message"
If the widget shows the wrong layout then you probably have an issue in the widget's onReceive method. onReceive is called for all the widgets no matter whether the broadcast is for that specific widget. Android's AppWidgetProvider filters the broadcasts by appwidget Id and dispatches to the other methods (like onUpdate).
See also: https://developer.android.com/reference/android/appwidget/AppWidgetProvider.html#onReceive(android.content.Context, android.content.Intent).
If you override onReceive (which I assume you do), you need to call through to super.onReceive(Context, Intent) to make sure your other methods don't get calls meant for other widgets.
Now for the configuration of the widget. If you follow the Google documentation then it will all work nicely. The only improvement I'd do is what my other answer that you reference suggests (https://stackoverflow.com/a/14991479/534471). This will NOT send out two broadcasts. The setResult()/finish() part does only terminate the config Activity and let Android know whether to actually add the widget or not (depending on whether the result is RESULT_CANCELED or RESULT_OK.
From your own answer I can see why your code wouldn't work. The code is:
Intent intent = new Intent();
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {mAppWidgetId});
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, intent);
sendBroadcast(intent);
finish();
First of all there's no need to add the appWidgetId twice, use the AppWidgetManager.EXTRA_APPWIDGET_IDS version and you're good. Second you're using the same Intent to return as a result for the Activity. AFAIK it's not documented what happens when you do set an action on that Intent but my experience with Android widgets is that you need to stick exactly to the documentation or you'll end up having strange issues (like the ones you encounter). So please use two different Intents.
Activity result:
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
Broadcast:
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, this, MyWidget.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {mAppWidgetId});
sendBroadcast(intent);
ok, so I figured it out. Posting here in case anyone else runs into this. I think that the Android Developer docs are a little misleading here.
The problem was that in my configuration Activity, I had this code at the end:
Intent intent = new Intent();
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {mAppWidgetId});
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, intent);
sendBroadcast(intent);
finish();
Providing an intent with the extra EXTRA_APPWIDGET_ID is recommended by the documentation provided by google.
However, that same document says that you have to update the widget's view by creating a RemoteView and calling AppWidgetManager.updateAppWidget() like so:
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.example_appwidget);
appWidgetManager.updateAppWidget(mAppWidgetId, views);
I didn't like the idea of placing the presentation logic in both the configuration activity and the widget class, so I instead decided to broadcast an intent at the end of the configuration activity to tell the widget to redraw itself. That's why I have setResult() AND sendBroadcast() at the end of the activity. The documentation further states that the onUpdate() callback will not be called when using a configuration activity. So this seemed neccessary. I added the ACTION_APPWIDGET_UPDATE and the EXTRA_APPWIDGET_IDS to the intent so that it would trigger the onUpdate() method. This practice was recommended by this SO answer (albeit without being included in the activity result intent - but I tried separating the two and it had no effect).
Now I'm not certain exactly how the "Google Sound Search" widget got in there, nor do I fully understand the mechanics of how the intents interacted to produce the observed results. However, as soon as I replaced my code above with the code stated in the docs, the widget was updated properly.
Intent resultIntent = new Intent();
resultIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultIntent);
finish();
This seems to contradict the documentation's statement that the configuration activity must update the widget's view. Simply providing the configuration activity result as below triggers the onUpdate() method in the widget, thus allowing the widget to redraw itself. I confirmed the behavior on an emulator running API 23 and also on a Samsung device running Samsung's android flavor.
I created WPF application using c#.I have to get more data in here from mysql databse.I'm use for this one ODBC 3.51 connector.when data loading freeze my application.
I'm try to fix that problem using thread.But I can't able to do this one using thread. Please suggest to way for solve my problem...
Use a BackgroundWorker-class. It's usage is very simple and it is used quite often for tasks such as loading data. The following example shows you it's usage:
BackgroundWorker bgWorker = new BackgroundWorker() { WorkerReportsProgress=true};
bgWorker.DoWork += (s, e) => {
// Load here your data
// Use bgWorker.ReportProgress(); to report the current progress
};
bgWorker.ProgressChanged+=(s,e)=>{
// Here you will be informed about progress and here it is save to change/show progress.
// You can access from here savely a ProgressBars or another control.
};
bgWorker.RunWorkerCompleted += (s, e) => {
// Here you will be informed if the job is done.
// Use this event to unlock your gui
};
bgWorker.RunWorkerAsync();
The use of the BackgroundWorker allows the UI-thread to continue it's processing and therefore the application rests responsive during loading. But because of this, you have also to ensure that no actions can take place that rely on the loaded data. A very simple solution is to set your main UI elements IsEnabled-property to false and in RunWorkerCompleted you set it to true. With a little fantasy you can improve this dumb behaviour tp a nice UI-experience (depending on the App).
It is general a good a advice to do long time operations in a separate thread (BackgroundWorker). One caveat is there: Do not create WPF-Elements in the DoWork-event. This will not do because all derived types of DependencyObject must be created in the same thread they are used.
There are other solutions to do this, for example creating directly a thread or with the event based async pattern, but I recommend to use BackgroundWorker for your task because it handels for you the plumbing. In the end, the result is the same but the way to it is much easier.
how to make a game tutorial as farmville game tutorial to teach the user to play the game. i have recently involve in make that kind of game tutorial for its user by actionscript 3, can anyone give me a guide to do it? any help is appreciated~
A common way to do this is similar to the "achievements system" many games employ, which utilises the a system similar to the Observer design pattern.
You'd set up a globally-accesible receiver object, with a function to receive a data packet of some sort.
Then, every function that should effect the tutorial info being shown would message this receiver object, and tell it of it's performed action.
So, say for instance, that you have a message box up to tell the player to "Chop down 10 trees".
You could then have a Receiver object with a function like TutorialMessage(var Action:String, var Parameter:object):void
When they perform the "Chop down tree" action, then end of the chopDownTree() function would contain a call to the receiver object's messaging function Receiver.TutorialMessage("TreeChoppedDown", 1) (ie, Chopped down 1 tree).
That receiver would then, essentially, run a massive switch case to determine the nature of the action, and interpret would to do based on your logic. In this case, it would add the value of Parameter to some counter variable, and when it reached 10, would display the next tutorial message.
I have written a client<>server based multiplayer game and have finished the basics (it's a flash client written in actionscript 3). The next step will be implementing a system which will allow me to easily add abilities to the game.
The problem here is that an ability can do so many things. Eg. player A uses ability "repair" on player B, the following will have to happen:
Player A sends message to server informing about the action
Player A is now showing a "repair casted" animation on its own ship
The server has to inform all players near that ship A now has the "repair casted" animation
The server has to increase the "health" of player B because it has been repaired by player A
The server has to inform all nearby players that player B now has a different health value
The server has to inform all nearby players that player B should show the "being repaired" animation
That's just an example, a lot of things have to happen for 1 simple ability. I could go into every object and add lines of code just for this spell. However, that would become a mess when I need to add a lot (~50) abilities. Also notice that some abilities do whole other things than others, some will have to show animations, some won't. Some will have to damage, some will have to increase statistics, etc.
So, how is such "ability system" usually handled both client and server side?
On client side never do a thing without the consent of the server. So client should only send the request of an ability and an optional target or some value. Also client should have to listen server for any commands. On the other hand, server should implement a function or classes (whatever you feel comfortable) to control ability actions. Whenever a client requests an ability to cast ability function should be called. As first task, function should check prerequisites. If they fail, client should be notified. Otherwise, a use repair ability ok signal should be passed to client. Then the function finds and send required commands to any nearby ships while updating their statuses. This method will guarantee that abilities cannot be abused by clients.
To understand it check the following, this is for illustration of the method, so dont tell me its not good to use so many ifs.
Client
function massRepair() {
server.send("USE mass_repair;"); //no decisions in here
}
function recive(command, target) {
if(command=="USE mass_repair OK")
massRepairAnim.gotoAndPlay(0);
if(command=="ANIM mass_repair") {
massRepairAnim.setTarget(target);
massRepairAnim.gotoAndPlay(0);
}
if(command==/SET ENERGY [0-9]/) {
value=getValue(command);
setEnergy(value);
}
if(command==/SET HEALTH [0-9]/) {
value=getValue(command);
setHealth(value);
}
if(command=="ANIM self_repair") {
selfRepairAnim.gotoAndPlay(0);
}
if(command=="USE mass_repair FAIL NOENERGY") {
display("Not enough energy to use the skill");
}
}
somebutton.click=function() { massRepair(); }
Server
function userMassRepair(Ship owner) {
if(owner.energy<30) {
send(owner, "USE mass_repair FAIL NOENERGY");
return false;
}
owner.energy-=30;
send(owner, "SET ENERGY "+owner.energy); //every data sent is absolute
send(owner, "USE mass_repair OK");
foreach(ship in owner.shipsInRange(100)) {
if(owner.ally(ship)) {
ship.health+=10;
ship.send("SET HEALTH " + ship.health);
ship.send("ANIM mass_repair "+owner.position);
ship.send("ANIM self_repair");
}
}
}
It looks like a lot of things, but it's really just a permutation of several standard components. Most actions will consist of changes to intrinsic properties and some cosmetics to be shown on the client, and there are only a handful of different ways to do those.
You need something like the following:
a standard way of sending actions to the server, which allows for convenient extraction of arbitrary parameters (as each action may have some very specific arguments)
a message type from server to clients to start playing a specified animation on a specified ship
a message type from server to clients increasing the health value on a ship
Then your server code for the action you mention looks like this pseudocode:
if (message.type == REPAIR)
{
repairer = message.actor;
repaired = message.target;
// Insert verification code here to check that the 2 ships are close enough,
// that the repairer is entitled to perform this action, etc
// Verification has passed: do the work
broadcastToAllClients(new Message(type=PLAY_ANIM, target=repairer, value=REPAIR_CASTED));
broadcastToAllClients(new Message(type=CHANGE_PROPERTY, target=repaired, proerty=HEALTH, value=5)); // 5 is the amount
broadcastToAllClients(new Message(type=PLAY_ANIM, target=repaired, value=BEING_REPAIRED));
}
That is very easy to extend to any number of actions, and will just require a few extra message types to accommodate the different things that can change or can be displayed.