所以基本上,我的目标是创建像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)拉伸形状。我唯一的问题是,我似乎无法正确地将按钮连接到形状函数,并使它们一起工作。
答案 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类。
回到主要功能,你需要:
这意味着有多种形状和多种颜色,但每次使用一种颜色。将上述成分放在一起,如果更容易,您可以在没有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可以方便地在水平或垂直组中分组元素。等玩得开心!