We closed this forum 18 June 2010. It has served us well since 2005 as the ALPHA forum did before it from 2002 to 2005. New discussions are ongoing at the new URL http://forum.processing.org. You'll need to sign up and get a new user account. We're sorry about that inconvenience, but we think it's better in the long run. The content on this forum will remain online.
IndexProgramming Questions & HelpSyntax Questions › Advice about my UI Code
Pages: 1 2 
Advice about my UI Code? (Read 2831 times)
Advice about my UI Code?
Jan 1st, 2010, 5:56am
 
Hi Everyone,

I am wondering if anyone could take a peak at my code for an interface I'm working on. blindfish has suggested that I take a look at this http://processing.org/discourse/yabb2/num_1245861435.html#3 for some tips on how to handle mouse events from inside a class.

I've read it and sort of understand it but don't really understand why/if my approach is bad practice. I still have a long way to go with it so I want make sure I'm on the right track to begin with.

I guess what I'm looking for is if there are other ways to achieve the same functionality as well as avoiding any bad programming habits as I.. hack and learn  Undecided

So far I've been learning most of what I know from the processing handbook and this forum.

Any help would be most appreciated,

Dan.

here's a link to where it's at.
http://www.openprocessing.org/visuals/?visualID=6728

And this is just to show the level of complexity I am up against. So to sum up it will be a mostly image buttons with a number entry text feild all running under multiple menu based layouts.

...


Re: Advice about my UI Code?
Reply #1 - Jan 1st, 2010, 7:28am
 
The object-oriented approach is very good for coping with complex use-cases like this.
( The mediator pattern might be useful: http://www.javacamp.org/designPattern/mediator.html )

Create a mouse event handling class in which you can register mouse event-handlers based on
1) the area in which a mouse click has been registered
2) the 'page' in the UI the user is working in

The goal of all this is to abstract this code as much as possible from the specific scenarios the user can work in and also to separate this code from the rest of the program, you don't want mouse-handler code to be all over the place.

I hope this isn't too abstract (it probably is).
If you want specifics, just ask.
Re: Advice about my UI Code?
Reply #2 - Jan 1st, 2010, 7:59am
 
I'm not sure I'm in a position to say what is and isn't 'best practice' but I've certainly found being able to handle mouse events inside a class useful.  This is mostly because it puts the code where it 'belongs' - rather than sitting in separate mousePressed()/mouseReleased() methods.  When you come back to your programme later there won't be any doubt what the mouse handling code is intended for - and it also avoids jumbling up mouse handling for different classes into the same generic central methods.

I did also experiment with handling key presses inside a class using registerKeyEvent(), but haven't seen that done much elsewhere.  One other option to move code into your class is to use registerDraw() - you then define a draw() method in your class and don't have to put anything into the main draw loop.  This again is a neat way to avoid mixing code intended for different classes into the main draw loop, but I have found a couple of issues, the main one being that things are drawn in the order they are registered and I've yet to find a way to easily change the order on the fly...
Re: Advice about my UI Code?
Reply #3 - Jan 10th, 2010, 4:06pm
 
Don't know if it would be useful to you, but I've been playing around with 'layer management' using registerDraw - there's a demo at: http://www.openprocessing.org/visuals/?visualID=6890

One thing that will almost certainly help is knowing that you can also unregister event listeners. So 'unregisterDraw()' means your object's draw loop isn't run (i.e. it is hidden).  unregisterMouseEvent() means mouseEvents are no longer registered on the object (which they are even if it's not drawn!).

With your UI that means you can set everything up at the start and simply un-register, re-register objects as required.  In that case I'd have an object for each layer/section of the interface, populated with button/widget classes etc that use standard display methods called from the layer class (rather than using registerDraw on these sub-objects).  That way you can then registerDraw on the layer class when it's needed and unregisterDraw when it's not (remembering to do the same with mouseEvents).

A couple of notes of caution though:

1.  It seems you can register an object more than once with registerDraw().  This means the object's draw method will be called multiple times (depending on how many times it's registered)... So you need to keep track when you register an object  - if need be it appears it's safe to call unregisterDraw() on an object that isn't registered, so if in doubt it might be worth calling that before registering.

2.  Unregistering then re-registering mouseEvents immediately on the same object is risky; though probably not so much of an issue for you...
Re: Advice about my UI Code?
Reply #4 - Jan 10th, 2010, 4:27pm
 
Thanks for all the great advice guys!

I will try to digest during the week in my very limited free time. Smiley

Cheers,

Dan.

p.s. I wish the holidays were not over so quickly Sad
Re: Advice about my UI Code?
Reply #5 - Jan 22nd, 2010, 7:44pm
 
Ok so I'm back at work now and things just get more an more busy. I had a spare moment to see if I can figure out how to get an event driven button class to be a composite object of a layout class.

I have pretty much just guessed how it might work due to my limited time. Could anyone tell me what rules I am breaking here and what the work arounds might be?

I understand if that's asking too much. I'd be happy event if someone could just point me in the right direction! Smiley Thanks.


Quote:
Layout lo;// Declare the object
void setup() {

 size(100, 100);
 smooth();
 noStroke();
 lo = new Layout();
}

void draw() {
 background(0);
 lo.display();
}

class Layout {


 PImage BaseImage_01 = loadImage("keyboard_01_off.jpg"); //load "01_off"
 PImage PressImage_01 = loadImage("keyboard_01_hit.jpg"); //load "01_off"
 
 Spot sp1 = new Spot(20,70,30);
 Spot sp2 = new Spot(50,25,30);
 
 Button overButton = null;
 Button clickedButton = null;
 Button releasedButton = null;
 Button b = new Button(200, 200, 52, color(204), color(255), color(102),BaseImage_01,PressImage_01, 2);
 registerDraw(b);
 registerMouseEvent(b);




 void display() {
   sp1.display();
   sp2.display();
   if(overButton != null)
     println("Mouse is over " + overButton.id);
   if(clickedButton != null)
     println("Mouse has clicked " + clickedButton.id);
   if(releasedButton != null)
     println("Mouse has released " + releasedButton.id);

   // These must be last 2 lines in draw method
   overButton = null;  
   clickedButton = null;
   releasedButton = null;
 }
}



class Spot {
 float x, y, diameter;

 Spot(float xpos, float ypos, float dia) {
   x = xpos; // Assign 33 to x
   y = ypos; // Assign 50 to y
   diameter = dia; // Assign 30 to diameter
 }

 void display() {
   ellipse(x, y, diameter, diameter);
 }
}

public class Button {
 int x, y;
 int size;
 color baseGray;
 color overGray;
 color pressGray;
 boolean over = false;
 boolean pressed = false;
 boolean released = false;
 PImage baseImage;
 PImage pressImage;
 int id;

 Button(int xp, int yp, int s, color b, color o, color p,PImage bi, PImage pi,int ident) {
   x = xp;
   y = yp;
   size = s;
   id = ident;
   baseGray = b;
   overGray = o;
   pressGray = p;
   baseImage = bi;
   pressImage = pi;
 }

 void mouseEvent(MouseEvent event){
   if ((mouseX >= x) && (mouseX <= x+size) && (mouseY >= y) && (mouseY <= y+size)) {  
     over = true;
     overButton = this;
     switch(event.getID()){
     case MouseEvent.MOUSE_PRESSED:
       pressed = true;
       clickedButton = this;
       break;
     case MouseEvent.MOUSE_RELEASED:
       released = true;
       pressed = false;
       releasedButton = this;
       break;
     }
   }
   else {
     over = false;
     pressed = false;
     released = false;
   }
 }

 void draw() {
   fill(baseGray);
   stroke(255);      
   if (pressed != true) {  
     rect(x, y, size, size);
     image(baseImage,x,y);
   }
   else {
     rect(x, y, size, size);
     image(pressImage,x,y);
   }
 }
}




Re: Advice about my UI Code?
Reply #6 - Jan 23rd, 2010, 6:51am
 
The code just below class Layout should be in a Layout constructor.

[EDIT] That and some other adjustments... See below.
Code:
Layout lo;// Declare the object
Button overButton;
Button clickedButton;
Button releasedButton;

void setup() {

size(300, 300);
smooth();
noStroke();
lo = new Layout();
}

void draw() {
background(0);
lo.display();
}

class Layout {
 Spot sp1;
 Spot sp2;

Layout() {
PImage BaseImage_01 = loadImage("keyboard_01_off.jpg"); //load "01_off"
PImage PressImage_01 = loadImage("keyboard_01_hit.jpg"); //load "01_off"

sp1 = new Spot(20,70,30);
sp2 = new Spot(50,25,30);

Button b = new Button(200, 200, 52, color(204), color(255), color(102),BaseImage_01,PressImage_01, 2);
registerDraw(b);
registerMouseEvent(b);
}



void display() {
  sp1.display();
  sp2.display();
  if(overButton != null)
    println("Mouse is over " + overButton.id);
  if(clickedButton != null)
    println("Mouse has clicked " + clickedButton.id);
  if(releasedButton != null)
    println("Mouse has released " + releasedButton.id);

  // These must be last 2 lines in draw method
  overButton = null;  
  clickedButton = null;
  releasedButton = null;
}
}



class Spot {
float x, y, diameter;

Spot(float xpos, float ypos, float dia) {
  x = xpos; // Assign 33 to x
  y = ypos; // Assign 50 to y
  diameter = dia; // Assign 30 to diameter
}

void display() {
  ellipse(x, y, diameter, diameter);
}
}

public class Button {
int x, y;
int size;
color baseGray;
color overGray;
color pressGray;
boolean over = false;
boolean pressed = false;
boolean released = false;
PImage baseImage;
PImage pressImage;
int id;

Button(int xp, int yp, int s, color b, color o, color p,PImage bi, PImage pi,int ident) {
  x = xp;
  y = yp;
  size = s;
  id = ident;
  baseGray = b;
  overGray = o;
  pressGray = p;
  baseImage = bi;
  pressImage = pi;
}


void mouseEvent(MouseEvent event){
  if ((mouseX >= x) && (mouseX <= x+size) && (mouseY >= y) && (mouseY <= y+size)) {  
    over = true;
    overButton = this;
    switch(event.getID()){
    case MouseEvent.MOUSE_PRESSED:
pressed = true;
clickedButton = this;
break;
    case MouseEvent.MOUSE_RELEASED:
released = true;
pressed = false;
releasedButton = this;
break;
    }
  }
  else {
    over = false;
    pressed = false;
    released = false;
  }
}

void draw() {
  fill(baseGray);
  stroke(255);
  rect(x, y, size, size);
  if (pressed) {  
    image(pressImage,x,y);
  }
  else {
    image(baseImage,x,y);
  }
}
}
Re: Advice about my UI Code?
Reply #7 - Jan 23rd, 2010, 8:48pm
 
Thanks Phil!

As far as the constructor goes. I was planning on having a Layout1 class and a layout2,3,4 class and filling them up with all sorts of gui objects and then have the layout class objects drawn based a radio button selection.

I'm trying to figure out a best approach. I'm not sure it even needs layout to be a class!?

I would love to have a layout class that is flexable enough to be able to populate with all sorts of gui objects on a need by need basis, but how can I make a constructor for that layout class to have infinte object input possibilies?

Hope this makes sense. Can you guys recommend any processing books that cover these sorts of issues in detail? I have the processing handbook and it's great but it doesn't seem to talk much about inheritance or how to manage registering/de-registering events.

Thanks again!

also I am perplexed as to why registering draw outside of the main draw loop causes my ellipse to draw incorrectly. Here's a simple example:
http://www.openprocessing.org/visuals/?visualID=7114
any ideas?
Re: Advice about my UI Code?
Reply #8 - Jan 24th, 2010, 1:41am
 
Well, I am not sure to understand the role of the Layout class. I admit I have just moved around stuff to get it working, without taking attention to logical organization...
Is your layout class actually a kind of panel (container of components), used to manage kind of tabs?

Quote:
how can I make a constructor for that layout class to have infinte object input possibilies?

You don't need to give infinite responsibilities to the constructor... Actually, many constructors have no parameters at all. Often you create an object, then add stuff to it. Common scheme looks like:
Code:
Panel p = new Panel(); // Can add size, pos, or change afterward
Button b1 = new Button("Go!", 100, 20); // Dims
p.add(b1); // Default pos
Button b2 = new Button("Click me"); // Default dims
p.add(b2, 10, 50); // Abs. pos, or set it relative to first button
and so on.
The panel usually stores its components in an ArrayList.

Quote:
why registering draw outside of the main draw loop causes my ellipse to

I don't understand what you do. The example you point to seems to work correctly. What do you mean by "draw incorrectly"?
Re: Advice about my UI Code?
Reply #9 - Jan 24th, 2010, 2:01am
 
(I ramble too much - I see PhilHo already answered the question rather more succinctly!)

If your layout just needs to handle the generics of layout and also act as a container for GUI elements you could quite simply include a container property (e.g. an ArrayList) which you could then populate with the GUI elements.  Assuming all your GUI elements have a commonly named display method (rather than using registerDraw (which I wouldn't use in this case)) you could then iterate over this container and call the display method on the GUI elements in the layout Object's display (or draw) method.  Then all you need to do to hide/show that set of GUI elements is hide/show the layout object  (i.e. not call (or call) its draw method; or unregisterDraw/registerDraw).

You'd then need to have overloaded Layout constructors to handle how you want to add objects to the container - e.g. individually; or from a container type (or as PhilHo suggests add them after the Layout is constructed).  I've got an example of that in the bfLayers class of my layers demo.  I iterate through any passed arrays and add each object individually since I need to create a bfLayer object for each one.  In your case I suspect you could just insert the passed container into the ArrayList in one step.
Re: Advice about my UI Code?
Reply #10 - Jan 24th, 2010, 3:15am
 
Quote:
Is your layout class actually a kind of panel (container of components), used to manage kind of tabs?


Yes for the sake of this discussion that is essentially my first goal. So in your example what would the constructor need to look like for the Panel class? would it need to be...
Code:
Panel(int size,int pos){
mySize = size;
myPos = pos;
}

...and then are you saying choose to expose/edit on these attributes on the p object?

Quote:
I don't understand what you do. The example you point to seems to work correctly. What do you mean by "draw incorrectly"?

if you comment out //registerDraw(b);
you will see the ellipse have a much smoother edge to it. But I think blindfish has suggested I don't use this method in this instance anyway.

Re: Advice about my UI Code?
Reply #11 - Jan 24th, 2010, 8:04am
 
The problem is nothing to do with using registerDraw().  Try adding "noStroke();" before drawing the ellipse in the main draw loop.  the ugly outline is caused by the stroke(255) in your button's draw method (which simply isn't called if you comment out 'registerDraw(b);'):  'noStroke()' in setup only gets applied during setup...

I think PhiLho is suggesting a constructor with no reference to GUI objects as arguments (so your suggested constructor would work; though wouldn't you want pos to be a PVector so you have x/y coordinates?).  Your layout class should then include a container property and, I guess to do things 'the Right Way', methods that allow you to add GUI objects to the container (rather than referencing the container property directly).  That does simplify things in terms of creating the layout object itself, and avoids the need for all the overloaded constructors that I resorted to...

And no, I don't think I would use registerDraw on the GUI objects.  If you do, to hide all GUI elements in a layout you would have to unregisterDraw each and every one.  If you're calling the display methods for the GUI objects from within the layout object's registered draw method you only have to unregisterDraw the layout to hide all the GUI elements.  If nothing else this seems more logical (to me at least): you hide the container and everything within it is also hidden.
Re: Advice about my UI Code?
Reply #12 - Jan 25th, 2010, 4:29am
 
Quote:
The problem is nothing to do with using registerDraw().  Try adding "noStroke();" before drawing the ellipse in the main draw loop.  the ugly outline is caused by the stroke(255) in your button's draw method (which simply isn't called if you comment out 'registerDraw(b);'):  'noStroke()' in setup only gets applied during setup...

Thanks for spotting this Blindfish.

Here is a simplified example of an ArrayList:
Quote:
// A watered down version of the ArrayList example 
ArrayList balls;
void setup() {
noStroke();
  smooth();
  size(200, 200);
  balls = new ArrayList();  // Create an empty ArrayList
  balls.add(new Ball(width/2, 50, 48));  
  balls.add(new Ball(width/2, 100, 30));
}

void draw() {
  background(0);
  for (int i = balls.size()-1; i >= 0; i--) {
    Ball ball = (Ball) balls.get(i);
    ball.display();
  }  
}

class Ball{
  float x,y,ballWidth;
  Ball(float xpos, float ypos, float dia){
    x = xpos;
    y = ypos;
    ballWidth = dia;
  }
  void display(){
    ellipse(x, y, ballWidth,ballWidth);
  }
}


class Panel{

}



PhilHo,

In your example you add what appears to be an object from a Panel class.
Quote:
Panel p = new Panel(); // Can add size, pos, or change afterward
Button b1 = new Button("Go!", 100, 20); // Dims
p.add(b1); // Default pos


How do I convert my ArrayList to a class that I can use as a container to hold my dynamic gui elements?
Re: Advice about my UI Code?
Reply #13 - Jan 25th, 2010, 5:02am
 
danielt wrote on Jan 25th, 2010, 4:29am:
How do I convert my ArrayList to a class that I can use as a container to hold my dynamic gui elements

You don't "convert" the ArrayList, you include it in the Panel class. It is called composition and today it is more valued than inheritance...
Dummy example:
Code:
class Panel
{
int posX, posY; // Panel position
ArrayList components = new ArrayList();

Panel(int x, int y)
{
posX = x;
posY = y;
}
void add(Component c)
{
components.add(c);
}
Component get(int i)
{
return (Component) components.get(i); // Should test bounds...
}
void display()
{
pushMatrix();
translate(posX, posY);
for (int i = 0; i < components.size(); i++)
{
Component c = get(i);
c.display();
}
popMatrix();
}
}
Re: Advice about my UI Code?
Reply #14 - Jan 28th, 2010, 7:14pm
 
I'm having trouble wrapping my head around this one here's what I have so far:
Quote:
Panel p;// Declare the object
Spot sp;

void setup() {
  size(100, 100);
  smooth();
  noStroke();
  p = new Panel(0,0);
  sp = new Spot(20,70,30);
  p.add(sp);
}

void draw() {
  background(0);
  p.display();
}

class Panel
{
  int posX, posY; // Panel position
  ArrayList components = new ArrayList();

  Panel(int x, int y)
  {
    posX = x;
    posY = y;
  }

  void add(Component c)
  {
    components.add(c);
  }

  Component get(int i)
  {
    return (Component) components.get(i); // Should test bounds...
  }

  void display()
  {
    pushMatrix();
    translate(posX, posY);
    for (int i = 0; i < components.size(); i++)
    {
      Component c = get(i);
      c.display();
    }
    popMatrix();
  }




class Spot {
  float x, y, diameter;

  Spot(float xpos, float ypos, float dia) {
    x = xpos; // Assign 33 to x
    y = ypos; // Assign 50 to y
    diameter = dia; // Assign 30 to diameter
  }

  void display() {
    ellipse(x, y, diameter, diameter);
  }
}



What do you mean when you say should test bounds?
Also I have never seen this done before. Do I need a Component class for this c object to work?
Quote:
  void add(Component c)
  {
    components.add(c);
  }


Sorry for all the questions. I AM trying..  Undecided
Pages: 1 2