Board index FlightGear Development Canvas

Buttons from vectors: Detect click upon self

Canvas is FlightGear's new fully scriptable 2D drawing system that will allow you to easily create new instruments, HUDs and even GUI dialogs and custom GUI widgets, without having to write C++ code and without having to rebuild FlightGear.

Buttons from vectors: Detect click upon self

Postby Bjoern » Tue Mar 03, 2020 12:52 am

I have a column of buttons I am creating from a vector, the length of which varies.

Code: Select all
   for(var i=1; i < size(SampleList);i+=1) {
      # Add a simple label...
      label = canvas.gui.widgets.Label.new(scrollarea_content, canvas.style, {wordWrap: 0})
                  .setText("Button: "~SampleList[i][0]);
      list.addItem(label);
      # Add a simple button...
      SampleList[i][1] = canvas.gui.widgets.Button.new(scrollarea_content, canvas.style, {})
                   .setText("Text: "~SampleList[i][0])
                   .listen("clicked", func() { print("I clicked button "~SampleList[i][1]); });
      list.addItem(SampleList[i][1]);
   }


For now, all I want is "I clicked button 10" in the console when I clicked button 10, but all I get is "index out of bounds" errors.

Are the listeners only properly set up after the loop has finished? Any way around this?
Last edited by Bjoern on Tue Mar 03, 2020 11:21 pm, edited 1 time in total.
Bjoern
 
Posts: 484
Joined: Fri Jan 06, 2012 11:00 pm
Location: TXL (RIP)
Version: Next
OS: ArchLinux

Re: Buttons from vecors: Detect click upon self

Postby wkitty42 » Tue Mar 03, 2020 6:04 am

make sure you are counting properly... remember programmers start counting at zero so your "10" would actually be 9 in the index... if that's the problem you're having, that is...
"You get more air close to the ground," said Angalo. "I read that in a book. You get lots of air low down, and not much when you go up."
"Why not?" said Gurder.
"Dunno. It's frightened of heights, I guess."
User avatar
wkitty42
 
Posts: 9146
Joined: Fri Feb 20, 2015 4:46 pm
Location: central NC, USA
Callsign: wk42
Version: git next
OS: Kubuntu 20.04

Re: Buttons from vecors: Detect click upon self

Postby Hooray » Tue Mar 03, 2020 8:47 am

wkitty42 wrote in Tue Mar 03, 2020 6:04 am:make sure you are counting properly... remember programmers start counting at zero so your "10" would actually be 9 in the index... if that's the problem you're having, that is...


Right, that's very important - I also believe this to be the issue here. However, I haven't really looked or execute said code.
That being said, I noticed that you are using multi-dimensional arrays - personally, I believe it's a bad idea to use them until you fully understand stand-alone (single-dimensional) arrays/vectors.

The error you posted, implies an "invalid access to an array", specifically saying that the index you passed into the array access is not valid.

Therefore, you need to look at all statements that access an array using an index, i.e. array[index].

The next step is to assure that all indexes are indeed correct - to do that, put a print() statement before the statement to log all index numbers to the console.

Almost certainly, you are going to see where the problem lies - and I suppose, it will also almost certainly point to your multi-dimensional vector.

Besides, when you do post code here, it is a good idea to always post a self-contained piece of code, i.e. something that people can run via the Nasal console - I am saying that, because I am fairly sure the problem is due to the way you set up your data structure (the multi-dimensional vector)

Finally, the way you are using Nasal vectors here is a bit unusual - most people would use a Nasal hash instead, so that they don't have to deal with indexes, but can use identifiers to look up elements from their hash. I am saying that because I do remember you previously mixing up Nasal and Lua related concepts in another topic, right ?

Again, regardless of what the issue is here, it is more important to develop the mindset to properly troubleshoot this - e.g. by removing index lookups from the loop body, replacing those with constant strings - and if that solves the problem, you know that it's related to your array accesses inside the loop body, so that the next step would be logging your index numbers to the console.

At that point, you will probably also want to review the setup of your data structure.

By using a vector of hashes, you can have something like this:
Code: Select all
button[0].label = "Hello World";
button[1].label = "Hello Forum";
button[2].label = "Hello FlightGear";


i.e. much better readability, compared to dealing with index numbers.

If you do want to use an index, simply introduce an alias:

Code: Select all
var label = 0;
button[0][label] = "Hello World";


example taken from the wiki:
http://wiki.flightgear.org/Howto:Start_ ... ed_version
Code: Select all
# these are used self-explanatory indices - so that you don't need to remember each field's position:
var NUMBER=0; var ALTITUDE=1; var DISTANCE=2; var ANGLE=3; var LENGTH=4; var ID=5; var BRG=6;

var waypoints = [[1,1000,12,22,44,"none", 33], [2,1500,22,42,14,"none", 133]]
print(waypoints[0][ALTITUDE]) # prints 1
print(waypoints[0][DISTANCE]) # prints 1000
print(waypoints[1][ALTITUDE]) # prints 2
print(waypoints[1][DISTANCE]) # prints 1500
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 12707
Joined: Tue Mar 25, 2008 9:40 am
Pronouns: THOU

Re: Buttons from vecors: Detect click upon self

Postby Bjoern » Wed Mar 04, 2020 1:14 am

I do not wish to discuss keys vs indices, unless there is a technical limitation preventing me from using the latter to access vector items.

All I want is button n to access item n in the source vector when clicked.
Source vector length and therefor the number of buttons is supposed to be variable.

If not possible, another idea is setting up a fixed number of buttons and use scroll wheel input to dynamically change their labels and clickspot behavior via offset to fake a scrollable list. But that would depend upon detecting scroll wheel input in the first place.


Code (don't mind the names):

addon-main.nas
Code: Select all
var unload = func(addon) {
   InitWindow.CtrlListWin.del();
}

var main = func(addon) {
  printlog("alert", "MyAddon initialized from path ", addon.basePath);
 
  foreach(var script; ['listcontrols.nas'])
    {
        var fname = addon.basePath ~ "/" ~ script;

        printlog("alert", "Load ", fname, " module");
      # Defines add-on namespace
        io.load_nasal(fname, "TestAddon");
    }
}


listcontrols.nas
Code: Select all
# Number of items to display
var num_items = 20;
# Define window width and height
var (winwidth,winheight) = (500,(30*0.5*num_items));
# Initialize window variable
var CtrlListWin = nil;

var ListScroll = nil;

var SampleList = [];

var InitList = func() {
   # Set empty vector
   SampleList = [];
   # Set length of vector
   setsize(SampleList, 1);
   SampleList[0] = "Test List";
   # Populate vector
   for(var i = 1; i < (num_items+1); i +=1) {   
         setsize(SampleList,(size(SampleList)+1));
         SampleList[i] = [nil, nil];
         SampleList[i][0] = (i);
    }
   
    return SampleList;
}


var InitWindow = func() {
   CtrlListWin = canvas.Window
      .new([winwidth,winheight],"dialog")
      .set('title','Controller Assignments');

   CtrlListWin.del = func() {
   call(canvas.Window.del, [], me);
   #Reset window variable
   CtrlListWin = nil;
   };

   var CtrlListWinCanvas = CtrlListWin.createCanvas()
      .set("background", "#A9A9A9");
   var WinRoot = CtrlListWinCanvas.createGroup();
      
   # Create a vbox as parent to the scroll area
   var list_vbox = canvas.VBoxLayout.new();
   # Add vbox to the main window
   CtrlListWin.setLayout(list_vbox);
   # Create scroll area as a child of the root group
   var scrollarea = canvas.gui.widgets.ScrollArea.new(WinRoot, canvas.style, {size: [winwidth, winheight]}).move(20, 100);
   # Add scroll area to the vbox
   list_vbox.addItem(scrollarea, 1);
   # Add content item to the scroll area, with style information
   var scrollarea_content = scrollarea.getContent()
          .set("font", "LiberationFonts/LiberationSans-Bold.ttf")
          .set("character-size", 16)
          .set("alignment", "left-center");
   # Add vbox item as child to the scroll area
   var list = canvas.VBoxLayout.new();
   scrollarea.setLayout(list);
   # Add title text
   var label = canvas.gui.widgets.Label.new(scrollarea_content, canvas.style, {wordWrap: 0})
                  .setText(" "~SampleList[0]);
   list.addItem(label);
   #print(size(SampleList));
   # Add vector items to the scroll area content item
   for(var i=1; i < size(SampleList);i+=1) {
      # Add a simple label...
      label = canvas.gui.widgets.Label.new(scrollarea_content, canvas.style, {wordWrap: 0})
                  .setText("Button: "~SampleList[i][0]);
      list.addItem(label);
      #print(i);
      # Add a simple button...
      SampleList[i][1] = canvas.gui.widgets.Button.new(scrollarea_content, canvas.style, {})
                   .setText("Text: "~SampleList[i][0])
                   .listen("clicked", func() { print(SampleList[i][0]); });
      list.addItem(SampleList[i][1]);
   }
      
}

# Window toggling function
var WindowToggle = func() {
   if(CtrlListWin == nil) {
      # Initialize list
      InitList();
      # Call window initialization function
      InitWindow();
   }
   else {
      CtrlListWin.hide();
      # Reset window variable
      CtrlListWin = nil;
   }
}


While we're at it: I want the window to close upon addon reloading. So far, I could not achieve this.
Bjoern
 
Posts: 484
Joined: Fri Jan 06, 2012 11:00 pm
Location: TXL (RIP)
Version: Next
OS: ArchLinux

Re: Buttons from vecors: Detect click upon self

Postby jsb » Sat Mar 07, 2020 11:58 am

Hi,

as InitWindow is a function (and not a hash) the following
addon-main.nas
Code: Select all
var unload = func(addon) {
   InitWindow.CtrlListWin.del();
}


should be replaced by something like this:

Code: Select all
var unload = func(addon) {
   if (CtrlListWin != nil and typeof(CtrlListWin["del"]) == "func")
    CtrlListWin.del();
}


This should solve the window close issue.
Note: using foo["bar"] instead of foo.bar avoids the " non-objects have no members" runtime error if bar is not a member/key in hash foo, however, foo must be a hash, otherwise you will see "extract from non-container" error.
jsb
 
Posts: 285
Joined: Sat Oct 25, 2014 9:17 pm
Location: Hamburg, Germany
Callsign: D-JSB
Version: next
OS: Win7/Linux

Re: Buttons from vectors: Detect click upon self

Postby jsb » Sat Mar 07, 2020 12:36 pm

Try this:
Code: Select all
# Number of items to display
var num_items = 20;
# Define window width and height
var (winwidth,winheight) = (500,(30*0.5*num_items));
# Initialize window variable
var CtrlListWin = nil;

var ListScroll = nil;

var SampleList = [];

var InitList = func() {
   var list = ["Test List"];
   for(var i = 1; i < (num_items+1); i +=1) {   
         append(list, [i, nil]);
    }   
    return list;
}

var InitWindow = func() {
   CtrlListWin = canvas.Window
      .new([winwidth,winheight],"dialog")
      .set('title','Controller Assignments');

   CtrlListWin.del = func() {
       call(canvas.Window.del, [], me);
       #Reset window variable
       CtrlListWin = nil;
   };

   var CtrlListWinCanvas = CtrlListWin.createCanvas()
      .set("background", "#A9A9A9");
   var WinRoot = CtrlListWinCanvas.createGroup();
     
   # Create a vbox as parent to the scroll area
   var list_vbox = canvas.VBoxLayout.new();
   # Add vbox to the main window
   CtrlListWin.setLayout(list_vbox);
   # Create scroll area as a child of the root group
   var scrollarea = canvas.gui.widgets.ScrollArea.new(WinRoot, canvas.style, {size: [winwidth, winheight]}).move(20, 100);
   # Add scroll area to the vbox
   list_vbox.addItem(scrollarea, 1);
   # Add content item to the scroll area, with style information
   var scrollarea_content = scrollarea.getContent()
          .set("font", "LiberationFonts/LiberationSans-Bold.ttf")
          .set("character-size", 16)
          .set("alignment", "left-center");
   # Add vbox item as child to the scroll area
   var list = canvas.VBoxLayout.new();
   scrollarea.setLayout(list);
   # Add title text
   var label = canvas.gui.widgets.Label.new(scrollarea_content, canvas.style, {wordWrap: 0})
                  .setText(" "~SampleList[0]);
   list.addItem(label);
   #print(size(SampleList));
   
   var _makeListener_button = func(i) {
       return func {
           print(SampleList[i][0]);
       };
   }
   
   # Add vector items to the scroll area content item
   for(var i=1; i < size(SampleList) - 1; i+=1) {
      # Add a simple label...
      label = canvas.gui.widgets.Label.new(scrollarea_content, canvas.style, {wordWrap: 0})
                  .setText("Button: "~SampleList[i][0]);
      list.addItem(label);
      #print(i);
      # Add a simple button...
      SampleList[i][1] = canvas.gui.widgets.Button.new(scrollarea_content, canvas.style, {})
                   .setText("Text: "~SampleList[i][0])
                   .listen("clicked", _makeListener_button(i));
      list.addItem(SampleList[i][1]);
   }
     
}

# Window toggling function
var WindowToggle = func() {
   if(CtrlListWin == nil) {
      # Initialize list
      SampleList = InitList();
      # Call window initialization function
      InitWindow();
   }
   else {
      CtrlListWin.del();
      # Reset window variable
      CtrlListWin = nil;
   }
}
jsb
 
Posts: 285
Joined: Sat Oct 25, 2014 9:17 pm
Location: Hamburg, Germany
Callsign: D-JSB
Version: next
OS: Win7/Linux

Re: Buttons from vectors: Detect click upon self

Postby Bjoern » Mon Mar 09, 2020 10:55 pm

Sweet, the code for the buttons works. Danke!

But the auto-window closing remains elusive with...

...addon-main...

Code: Select all
var unload = func(addon) {
   if (CtrlListWin != nil and typeof(CtrlListWin["del"]) == "func")
    CtrlListWin.del();
}


...and listcontrols:
Code: Select all
var InitWindow = func() {
  ...

   CtrlListWin.del = func() {
       call(canvas.Window.del, [], me);
       #Reset window variable
       CtrlListWin = nil;
   };

...

}
Bjoern
 
Posts: 484
Joined: Fri Jan 06, 2012 11:00 pm
Location: TXL (RIP)
Version: Next
OS: ArchLinux

Re: Buttons from vectors: Detect click upon self

Postby jsb » Tue Mar 10, 2020 10:22 pm

Strange, I believe it worked on my machine... I will check

EDIT:
Not obvious from the Nasal/canvas/gui.nas but a canvas window has the methods show(), hide() and isVisible() which are presumably coded in the C++ code for canvas.
Code: Select all
var WindowToggle = func() {
    print("WindowToggle");
   if(CtrlListWin == nil) {
      # Initialize list
      # Call window initialization function
      InitWindow();
   }
   else {
      if (CtrlListWin.isVisible()) {
          CtrlListWin.hide();
      }
      else {
          CtrlListWin.show();
      }
   }
}


In the property tree you can find /sim/gui/canvas/window[i]/visible "sometimes", I did not check this in detail.

Toggeling this prop via the property browser was not 100% stable for me but I saw such glitches with other unrelated bool props in the property browser so I would not give to much about it.
jsb
 
Posts: 285
Joined: Sat Oct 25, 2014 9:17 pm
Location: Hamburg, Germany
Callsign: D-JSB
Version: next
OS: Win7/Linux


Return to Canvas

Who is online

Users browsing this forum: No registered users and 5 guests