处理 - 具有面向对象的编程问题

时间:2014-04-21 01:22:03

标签: java oop processing

所以基本上,我的目标是创建像MS Paint软件这样的程序,我可以用鼠标拖动绘制形状并改变颜色。这是我的一个很长的剧本。我对OOP相当新,所以我在尝试使所有功能协同工作时遇到了困难。

主要

Button button1, button2, button3, button4, button5, button6, button7;
Rect rect;
Circle circle;
int mode = 1;

void setup() {
    size(900,600);
    smooth();
    rect = new Rect(0,0,0,0, new PImage());
    circle = new Circle(0,0,0,0, new PImage());
    color gray = color(234);
    color black = color(0);
    color white  = color(255);
    color red = color(255,0,0);
    color green = color(0,255,0);
    color blue = color(0,0,255);

    button1 = new Button(10, 60, 20, white, gray, black); //draw rectangle function
    button2 = new Button(10, 100, 20, white, gray, black); //draw circle function
    button3 = new Button(10, 140, 20, red, gray, black); //option of color red
    button4 = new Button(10, 160, 20, green, gray, black); //option of color green
    button5 = new Button(10, 180, 20, blue, gray, black); //option of color blue
    button6 = new Button(10, 220, 20, black, gray, black); //fill entire shape
    button7 = new Button(10, 240, 20, white, gray, black); //fill nothing
}

void draw() {
    button1.setp();
    button2.setp();
}

void mousePressed() {
    if (button1.press()) { mode = 1; }
    if (button2.press()) { mode = 2; }
    if (button3.press()) { mode = 3; }
    if (button4.press()) { mode = 4; }
    if (button5.press()) { mode = 5; }
    if (button6.press()) { mode = 6; }
    if (button7.press()) { mode = 7; }
}

void manageButtons() {
    button1.update();
    button2.update();
    button3.update();
    button4.update();
    button5.update();
    button6.update();
    button7.update();

    button1.display();
    button2.display();
    button3.display();
    button4.display();
    button5.display();
    button6.display();
    button7.display();
}

void mouseReleased() {
    button1.release();
    button2.release();
    button3.release();
    button4.release();
    button5.release();
    button6.release();
    button7.release();
}

void mouseDragged() {
    //rect.drag();
}

按钮类

class Button {
    int x, y; // the x- and y-coordinate
    int size; // dimension (width & height)
    color baseGray; // Default gray value
    color overGray; // Value when the mouse is over
    color pressGray; // Value when the mouse is pressed
    boolean over = false; // true when the mouse is over
    boolean pressed = false;// true when pressed 

    Button(int xp, int yp, int s, color b, color o, color p) {
        x = xp;
        y = yp;
        size = s;
        baseGray = b;
        overGray = o;
        pressGray = p;
    }

    void setp() {
        background(255);
        manageButtons();
        //stroke();
        if (mode == 1) {
            rect.drawing();
        } else if (mode == 2) {
            circle.drawing();
        }
    }

    void update() {
        if ((mouseX >= x) && (mouseX <= x + size) && (mouseY >= y) && (mouseY <= y + size)) {
            over = true;
        } else {
            over = false;
        }
    }

    boolean press() {
        if (over) {
            pressed = true;
            return true;
        } else {
            return false;
        }
    }

    void release() {
        pressed = false;
        rect.release();
        circle.release();
    }

    void display() {
        if (pressed) {
            fill(pressGray);
        } else if (over) {
            fill(overGray);
        } else {
            fill(baseGray);
        }

        stroke(0);
        rect(x, y, size, size);
    }
}

圈子类

class Circle {
    int x, y;
    int xp, yp;
    PImage a;

    Circle(int dragx, int dragy, int movex, int movey, PImage image) {
        x = dragx;
        y = dragy;
        xp = movex;
        yp = movey;
        a = image;
    }

    void display() {
        smooth();
        background(255);
        a = get();
        stroke(0);
        fill(255); //255,255,10);
    }

    void drawing() {
        image(a, 0, 0); //background(a);
        float sizex = xp - x;
        float sizey = yp - y;
        if (mousePressed && mouseButton == LEFT) {
            ellipse(x, y, sizex, sizey);
        }
    }

    void press() {
        x = mouseX;
        y = mouseY;
    }

    void release() {
        xp = mouseX;
        yp = mouseY;
        noLoop();
        a = get();
        loop();
    }

    void drag() {
        xp = 80 + mouseX;
        yp = 80 + mouseY;
    }
}

矩形类

class Rect {
    int x, y;
    int xp, yp;
    PImage a;

    Rect(int dragx, int dragy, int movex, int movey, PImage image) {
        x = dragx;
        y = dragy;
        xp = movex;
        yp = movey;
        a = image;
    }

    void display() {
        smooth();
        background(255);
        a = get();
        stroke(0);
        fill(255); //255,255,10);
    }

    void drawing() {
        image(a, 0, 0); //background(a);
        float sizex = xp - x;
        float sizey = yp - y;
        if (mousePressed && mouseButton == LEFT) {
            rect(x, y, sizex, sizey);
        }
    }

    void press() {
        x = mouseX;
        y = mouseY;
    }

    void release() {
        xp = mouseX;
        yp = mouseY;
        noLoop();
        a = get();
        loop();
    }

    void drag() {
        xp = mouseX;
        yp = mouseY;
    }
}

使用上面的处理脚本(java),我遇到了使用我创建的按钮使矩形和圆类正常工作 的问题。按钮1应该绘制矩形,按钮2应该绘制圆圈(到目前为止,只有这些功能可以工作。我还需要为它们应用颜色)。

我知道矩形和圆形类正常 因为我在将所有内容放在一起之前已经单独测试过它们。按钮(功能)工作但不正确(我应该使用鼠标将形状拖动到任何所需的位置,而这个程序只允许他们出现我放置它们的地方,而且只能从角落,好像我只是从x和y位置(0,0)拉伸形状。我唯一的问题是,我似乎无法正确地将按钮连接到形状函数,并使它们一起工作。

2 个答案:

答案 0 :(得分:1)

如果不看主要方法,很难评估代码的正确性。你的主要&#39; class的设置方式似乎是方法mousePressed()用于轮询按钮并根据状态设置绘图模式。

但是,如果不查看主要方法,我无法确定您是否正确轮询按钮。

如果您希望使用面向对象的方法,那么您将需要使用Observer pattern。从本质上讲,你的按钮都会引用主要的&#39;具有buttonClicked(Button btn)方法的对象。单击按钮时,它会运行“主要”按钮。 object的buttonClicked(Button btn)方法。提供的参数将是对单击按钮的引用,以便main可以选择要使用的适当模式。演示代码如下:

在大班:

//Give the button a reference to the main object
button1 = new button(this);

//receive notifications from the button
public buttonClicked(Button btn) {
    if(btn.equals(button1))
        mode = 1;
    if(...)
        ...
}

答案 1 :(得分:0)

快速查看代码,看起来对使用绘图命令,编写类以及如何进行交互存在一些困惑。 我会开始真的很基本:

  • 将所有内容拆分为简单易于理解的独立任务
  • 独立实施每项任务并进行测试
  • 根据需要简化和概括代码
  • 开始将实现整合到一个主项目中,逐一进行测试。

例如,让我们使用鼠标以交互方式绘制矩形的任务。如果在按下鼠标时存储鼠标的位置,则可以将它们与坐标之间的差异取为最近的鼠标坐标:矩形的尺寸:

//an object to store current mouse coordiates
PVector mouse = new PVector();
//...and the previous mouse coordinates
PVector pmouse = new PVector();

void setup(){
  size(400,400);
}
void draw(){
  background(255);
  //compute width,height as difference between current and previous mouse positions
  float w = mouse.x - pmouse.x;
  float h = mouse.y - pmouse.y;
  //draw the shape according to it's mode
  rect(pmouse.x,pmouse.y,w,h);
}
//set both previous and current mouse coordinates - this helps reset coordinates and finalize a shape 
void mouseSet(){
  pmouse.set(mouseX,mouseY);
  mouse.set(mouseX,mouseY);
}
void mousePressed(){
  mouseSet();
}
void mouseDragged(){//update only the current mouse position, leaving pmouse outdated
  mouse.set(mouseX,mouseY);
}
void mouseReleased(){
  mouseSet();
}

如果要渲染形状和形状本身的预览,使用图层(如在Photoshop中)可能很方便:一个图层渲染已绘制的图像,另一个图层临时渲染顶部。

幸运的是,通过PGraphics处理就像处理一样。 初始化PGraphics实例后,您可以使用您习惯的相同绘图命令绘制它。唯一的问题是您需要先调用beginDraw(),然后再调用endDraw()

关于PGraphics的另一个很酷的事情是它扩展了PImage,这意味着你可以将它显示为一个(更不用说,做图像处理):

PGraphics canvas;

size(400,400);
//create a PGrahpics layer
canvas = createGraphics(width,height);

//intialize drawing
canvas.beginDraw();

//draw something, pretty similar you'd draw in Processing
canvas.background(255);
canvas.rect(200,200,150,100);
//finish drawing
canvas.endDraw();

//PGraphics extends PImage, hence it can drawn as one
image(canvas,0,0);

在您的代码中,按钮类保持对矩形和其他对象的引用。 理想情况下,您希望对象为loosely coupled。这个想法是让类处理自己的功能,独立于主程序或其他类。尝试将Button类复制到新草图中并立即使用。目前,您需要复制其他不相关的类以及编译。目标是编写一个可以在任何草图中轻松重用的Button类。

回到主要功能,你需要:

  1. 绘图形状模式(矩形或椭圆形)
  2. 绘图颜色(来自颜色列表)
  3. 这意味着有多种形状和多种颜色,但每次使用一种颜色。将上述成分放在一起,如果更容易,您可以在没有GUI或类的情况下对功能进行原型设计。只需使用键盘快捷键替换此测试阶段的GUI按钮(使用1/2/3控制颜色,r用于矩形,c用于圆圈):

    PGraphics canvas;//a layer to persist shapes onto
    
    //shape modes
    int MODE_RECTANGLE = 0;
    int MODE_ELLIPSE = 1;
    //a reference to the currently selected shape mode
    int mode = MODE_RECTANGLE;
    //various colours
    color c1 = color(192,0,0);
    color c2 = color(225,225,0);
    color c3 = color(0,0,192);
    //a reference to the currently selected colour
    color current = c1;
    
    //an object to store current mouse coordiates
    PVector mouse = new PVector();
    //...and the previous mouse coordinates
    PVector pmouse = new PVector();
    
    void setup(){
      size(400,400);
      //setup ellipse mode to draw from corner like the rect()'s default setting
      ellipseMode(CORNER);
      strokeWeight(3);
    
      //initialise the canvas - this allows you to draw with the same commands, but as a separate layer
      canvas = createGraphics(width,height);
      canvas.beginDraw();
      //replicate ellipse mode and stroke weight in canvas layer as well (so what's being drawn matches preview)
      canvas.ellipseMode(CORNER);
      canvas.strokeWeight(3);
      canvas.background(255);
      canvas.endDraw();
    }
    void draw(){
      //draw the layer first
      image(canvas,0,0);
      //overlay the preview on top using 50% transparency (as a visual hint it's a preview)
      draw(g,127);
    }
    //a function that draws into a PGraphics layer (be it our canvas or Processing's)
    void draw(PGraphics g,int transparency){
      g.fill(current,transparency);
      //compute width,height as difference between current and previous mouse positions
      float w = mouse.x - pmouse.x;
      float h = mouse.y - pmouse.y;
      //draw the shape according to it's mode
      if(mode == MODE_ELLIPSE) {
        g.ellipse(pmouse.x,pmouse.y,w,h);
      }
      if(mode == MODE_RECTANGLE) {
        g.rect(pmouse.x,pmouse.y,w,h);
      }
    }
    //set both previous and current mouse coordinates - this helps reset coordinates and finalize a shape 
    void mouseSet(){
      pmouse.set(mouseX,mouseY);
      mouse.set(mouseX,mouseY);
    }
    void mousePressed(){
      mouseSet();
    }
    void mouseDragged(){//update only the current mouse position, leaving pmouse outdated
      mouse.set(mouseX,mouseY);
    }
    void mouseReleased(){
      //commit the shape to the canvas layer
      canvas.beginDraw();
      draw(canvas,255);
      canvas.endDraw();
      //set both mouse positions
      mouseSet();
    }
    //use keys to test: 1,2,3 = colours, r/c = shape mode
    void keyPressed(){
      if(key == '1') current = c1;
      if(key == '2') current = c2;
      if(key == '3') current = c3; 
      if(key == 'r') mode = MODE_RECTANGLE;
      if(key == 'c') mode = MODE_ELLIPSE; 
    }
    

    还记得以前我们如何使用过PGraphics吗?每个Processing小程序都有一个名为g的处理程序,因此这是一种使用单个函数绘制到多个PGraphics实例(Processing和我们的canvas)但使用不同透明度的快速而又脏的方法。

    使用这种方法值得注意的是,不是为绘制的每个形状存储多个Circle / Rectangle实例,我们只需将一次渲染到画布中并让它(一个PGraphics)对象存储像素。缺点是,一旦绘制出来,就无法检索以哪种顺序绘制的形状以及具有什么坐标/尺寸,但如果您不需要这些细节则更简单。

    如果您确实需要这些,可能值得查看PShape(尤其是GROUP,RECT,ELLIPSE选项)。

    现在让我们添加按钮!安东尼的建议很棒,简化的按钮点击建议非常适合处理。 通常你会使用Interface为按钮定义一个监听器,并让Processing sketch实现这个接口,但只需要一个函数负责处理一个很好的解决方法中的按钮。

    这是一个基本的实现:

    Button a = new Button("Button A",5,5,90,20,color(200),color(0));
    Button b = new Button("Button B",5,30,90,20,color(200),color(0));
    
    void draw(){
      background(255);
      //update button states based on mouse interaction
      a.update(mouseX,mouseY,mousePressed);
      b.update(mouseX,mouseY,mousePressed);
      //render buttons on screen
      a.draw();
      b.draw();
    }
    void onButtonClicked(Button btn){
      println(btn.label + " was pressed");
    }
    
    class Button{
      float w,h,x,y;//width, height and position
      color bg = color(200);//background colour
      color fg = color(0);//foreground colour
      String label;//text displayed
    
      boolean isOver,wasPressed;//button states
      int pw = 10;//padding on width
    
      boolean outline;//draw an outline or not
    
      Button(String label,float x,float y,float w,float h,color fg,color bg){
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
        this.label = label;
        this.fg = fg;
        this.bg = bg;
      }
      void update(int mx,int my,boolean md){
        //check bounding box
        isOver = ((mx >= x && mx <= (x+w))&&(my >= y && my <= (y+h)));
        if(isOver && md){
          //check if it was not previously pressed to call the onButtonClicked function only once (similar to debouncing)
          if(!wasPressed){
            onButtonClicked(this);
            wasPressed = true;
          }
        }else wasPressed = false;
      }
      void draw(){
        //pushStyle()/popStyle() isolates drawing styles (similar to how pushMatrix()/popMatrix() isolates coordinate transformations
        pushStyle();
        if(outline){
          strokeWeight(3);
          stroke(127);
        }else{
          noStroke();
        }
        fill(isOver ? fg : bg);//the ? : is a lazy one liner way of doing if/else: (booleanExpression ? doIfTrue : doIfFalse)
        rect(x,y,w,h);
        fill(isOver ? bg : fg);
        text(label,x+pw,y+h*.75);
        popStyle();
      }
    }
    

    现在,只使用键盘快捷键将此Button类添加到上一代码中是相当简单的:

    PGraphics canvas;//a layer to persist shapes onto
    
    //shape modes
    int MODE_RECTANGLE = 0;
    int MODE_ELLIPSE = 1;
    //a reference to the currently selected shape mode
    int mode = MODE_RECTANGLE;
    //various colours
    color c1 = color(192,0,0);
    color c2 = color(225,225,0);
    color c3 = color(0,0,192);
    //a reference to the currently selected colour
    color current = c1;
    
    //an object to store current mouse coordiates
    PVector mouse = new PVector();
    //...and the previous mouse coordinates
    PVector pmouse = new PVector();
    
    //UI
    //Button constructor: label, x,y, width, height, foreground, background
    Button rectMode = new Button("\u25A0",5,5,30,20,color(200),color(0));//beying lazy/having fun with text: the \u25A0 is using the unicode for a square shape as text http://www.fileformat.info/info/unicode/char/25a0/index.htm 
    Button ellipseMode = new Button("\u25CF",5,30,30,20,color(200),color(0));//http://www.fileformat.info/info/unicode/char/25CF/index.htm
    Button color1 = new Button("",5,55,30,20,color(255,0,0),c1);
    Button color2 = new Button("",5,80,30,20,color(255,255,0),c2);
    Button color3 = new Button("",5,105,30,20,color(00,0,255),c3);
    Button[] gui = new Button[] {rectMode, ellipseMode, color1, color2, color3};
    //reference to previous mode button and previous colour button
    Button prevMode = rectMode;
    Button prevColor = color1;
    
    void setup(){
      size(400,400);
      //setup ellipse mode to draw from corner like the rect()'s default setting
      ellipseMode(CORNER);
      strokeWeight(3);
    
      //initialise the canvas - this allows you to draw with the same commands, but as a separate layer
      canvas = createGraphics(width,height);
      canvas.beginDraw();
      //replicate ellipse mode and stroke weight in canvas layer as well (so what's being drawn matches preview)
      canvas.ellipseMode(CORNER);
      canvas.strokeWeight(3);
      canvas.background(255);
      canvas.endDraw();
    
      //ui outline current options
      rectMode.outline = true;
      color1.outline = true;
    }
    void draw(){
      //draw the layer first
      image(canvas,0,0);
      //overlay the preview on top using 50% transparency (as a visual hint it's a preview)
      draw(g,127);
      //update and draw UI
      for(int i = 0; i < gui.length; i++){
        gui[i].update(mouseX,mouseY,mousePressed);
        gui[i].draw();
      }
    }
    //a function that draws into a PGraphics layer (be it our canvas or Processing's)
    void draw(PGraphics g,int transparency){
      g.fill(current,transparency);
      //compute width,height as difference between current and previous mouse positions
      float w = mouse.x - pmouse.x;
      float h = mouse.y - pmouse.y;
      //draw the shape according to it's mode
      if(mode == MODE_ELLIPSE) {
        g.ellipse(pmouse.x,pmouse.y,w,h);
      }
      if(mode == MODE_RECTANGLE) {
        g.rect(pmouse.x,pmouse.y,w,h);
      }
    }
    //set both previous and current mouse coordinates - this helps reset coordinates and finalize a shape 
    void mouseSet(){
      pmouse.set(mouseX,mouseY);
      mouse.set(mouseX,mouseY);
    }
    void mousePressed(){
      mouseSet();
    }
    void mouseDragged(){//update only the current mouse position, leaving pmouse outdated
      mouse.set(mouseX,mouseY);
    }
    void mouseReleased(){
      //commit the shape to the canvas layer
      canvas.beginDraw();
      draw(canvas,255);
      canvas.endDraw();
      //set both mouse positions
      mouseSet();
    }
    void onButtonClicked(Button b){
      if(b == color1) current = c1;
      if(b == color2) current = c2;
      if(b == color3) current = c3;
      if(b == rectMode) mode = MODE_RECTANGLE;
      if(b == ellipseMode) mode = MODE_ELLIPSE;
    
      if(b == color1 || b == color2 || b == color3){
        b.outline = true;
        if(prevColor != null) prevColor.outline = false;
        prevColor = b;
      }
      if(b == rectMode || b == ellipseMode){
        b.outline = true;
        if(prevMode != null) prevMode.outline = false;
        prevMode = b;
      }
    }
    class Button{
      float w,h,x,y;//width, height and position
      color bg = color(200);//background colour
      color fg = color(0);//foreground colour
      String label;//text displayed
    
      boolean isOver,wasPressed;//button states
      int pw = 10;//padding on width
    
      boolean outline;//draw an outline or not
    
      Button(String label,float x,float y,float w,float h,color fg,color bg){
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
        this.label = label;
        this.fg = fg;
        this.bg = bg;
      }
      void update(int mx,int my,boolean md){
        //check bounding box
        isOver = ((mx >= x && mx <= (x+w))&&(my >= y && my <= (y+h)));
        if(isOver && md){
          //check if it was not previously pressed to call the onButtonClicked function only once (similar to debouncing)
          if(!wasPressed){
            onButtonClicked(this);
            wasPressed = true;
          }
        }else wasPressed = false;
      }
      void draw(){
        //pushStyle()/popStyle() isolates drawing styles (similar to how pushMatrix()/popMatrix() isolates coordinate transformations
        pushStyle();
        if(outline){
          strokeWeight(3);
          stroke(127);
        }else{
          noStroke();
        }
        fill(isOver ? fg : bg);//the ? : is a lazy one liner way of doing if/else: (booleanExpression ? doIfTrue : doIfFalse)
        rect(x,y,w,h);
        fill(isOver ? bg : fg);
        text(label,x+pw,y+h*.75);
        popStyle();
      }
    }
    

    请注意,部分代码正在处理以前选择的颜色和形状模式按钮。这实际上并不需要,但很高兴向用户显示当前选择的形状/颜色的一些反馈(在这种情况下以轮廓的形式)。

    在UI方面,还有很多值得探索的内容。例如,尽管在功能方面存在多个按钮,但它们主要用作两个单选按钮组。一旦掌握了OOP Basics,您可以考虑创建一个常见的GUIElement类,其他UI元素,如按钮/复选框/单选按钮/滑块可以使用(例如draw()函数,x,y,宽度,身高等)。然后每个班级都会专攻这个超级班级。例如,Button将扩展GUIElement,而ToggleButton将扩展Button。也许HBox或VBox可以方便地在水平或垂直组中分组元素。等玩得开心!

    drawing rectangles

    drawing circles