AgentWiggles
AgentWiggles

Reputation: 45

Java MVC project - either I can't update the drawing, or I can't see it

I've got a project based around the Model-View-Controller paradigm, and I've been having a lot of trouble with getting it to work properly.

The program has 4 panels, which are supposed to allow me to modify an oval drawn on the screen in various ways. These seem to work fine, and after considerable trouble I was able to get them to display in the JFrame which holds the whole shebang. I've managed to get them to display by breaking away from the provided instructions, but when I do that, I can't seem to get the oval to update. However, if I follow the directions to the letter, I only ever see an empty frame.

The project had pretty specific directions, which I followed up to a point, but some of the documentation was unclear. I think what I'm missing must be something simple, since nothing is jumping out at me as not making sense. I have to admit though that my Java experience is limited and my experience with GUI design/paradigms is even more so.

Anyway, I've been searching the web and this site extensively trying to figure out what's wrong, but this is a somewhat specific example and honestly I just don't know enough about this to generalize any of the answers I've found online and figure out what's missing. I've been poring over this code for far too long now so I'm really hoping someone can help me out.

public class Model {
    private Controller controller;
    private View view;
    private MvcFrame mvcFrame;

    private int radius = 44;
    private Color color = Color.BLUE;
    private boolean solid = true;

    //bunch of mutators and accessors for the above variables

    public Model() {
        controller = new Controller(this);
        view = new View(this);
        mvcFrame = new MvcFrame(this);
    }
}

Here's the model class. This seems to be fairly simple. I think my understanding of what's going on here is solid, and nothing seems to be wrong. Included mostly for context.

public class Controller extends JPanel{
    private Model model;

    public Controller(Model model) {
        this.model = model;
        setBorder(BorderFactory.createLineBorder(Color.GREEN));
        setLayout(new GridLayout(4,1));
        add(new RadiusPanel(model));
        add(new ColorPanel(model));
        add(new SolidPanel(model));
        add(new TitlePanel(model));
    }
}

This is the Controller class. As far as I can tell, the setBorder, setLayout, and series of adds do nothing here. I had them commented out, but this is the way that the instructions told me to do things, so either there's a mistake there or something about my setup is wrong. However, when I did it this way, I would get an empty window (JFrame) but none of the panels would show up in it. What I did to fix this is put those add functions in the mvcFrame class:

public class MvcFrame extends JFrame {
    private Model model;

    public MvcFrame(Model model){
        this.model = model;
        //setLayout(new GridLayout(4,1));
        //add(new RadiusPanel(model));
        //add(new ColorPanel(model));
        //add(new SolidPanel(model));
        //add(new TitlePanel(model));

        //add(new View(model));


        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setSize(800,600);
        setVisible(true);
    }
}

So here's where things kind of started getting weird. The first block of commented out code is the same as what's in the Controller class. The reason I have it commented out is because that was just a lucky guess - it's not supposed to be like that according to the instructions. However, this did work for getting the panels to show up - but at that point I was still tearing my hair out trying to get the oval to display.

The other commented line ( add(new View(model)); ) was a different attempt at making things work. In this case, I put those add functions in the View class (see commented out code below). This actually worked to display both the oval and the panels, but that method wouldn't allow me to update the oval. Also, though I just had the oval displaying, I can't seem to figure out what exactly made that happen, and I can't seem to make it come back.

public class View extends JPanel{
private Model model;

    public View(Model model) {
        this.model = model;
        //setLayout(new GridLayout(4,1));
        //add(new RadiusPanel(model));
        //add(new ColorPanel(model));
        //add(new SolidPanel(model));
        //add(new TitlePanel(model));

        repaint();
    }

    @Override
    protected void paintComponent(Graphics g){
        super.paintComponent(g);

        //center of view panel, in pixels:
        int xCenter = getWidth()/2;
        int yCenter = getHeight()/2;

        int radius = model.getRadius();
        int xStart = xCenter - radius;
        int yStart = yCenter - radius;
        int xWidth = 2 * radius;
        int yHeight = 2 * radius;
        g.setColor(model.getColor());

        g.clearRect(0, 0, getWidth(), getHeight());

        if (model.isSolid()){
            g.fillOval(xStart, yStart, xWidth, yHeight);
        } else {
            g.drawOval(xStart, yStart, xWidth, yHeight);
        }        
    }
}

Kinda same idea as before - the commented out code is stuff I added to try to get things working, but is not based on the provided directions. In the case where that stuff was uncommented, I had the add(new View(model)); line from the mvcFrame line uncommented as well.

The various panel classes (SolidPanel, ColorPanel, etc) simply extend a class called ControlPanel which extends JPanel. These all seem to work as expected, not having much issue with them. There is also a driver which launches the GUI. This also seems to work as expected.

The main problem I'm having is that I can't get the oval to show up, and the one time I could make it show up, none of the options for changing it seemed to work. I feel like I'm close but I'm just at a loss for other things to try out at this point.

Anyone who can help will have my sincerest gratitude.

Upvotes: 4

Views: 1013

Answers (3)

Grammar
Grammar

Reputation: 1

Although I had a different problem, the line in view:

g.clearRect(0, 0, getWidth(), getHeight());

Solved everything for me on my project because it was not erasing the previous Oval, so I could only see changes if I made it bigger. Thank you.

Upvotes: 0

Ben
Ben

Reputation: 57257

Here's a very hasty rewrite of the thing, "just to get it working".

main.java

public class main {
    public static void main(String[] args) {
        // The JFrame could be created here, since it lasts the life
        // of the program.

        //...then, later, the model.
        Model mdl = new Model();    

        // ...and then move on to applying the view and control to the frame.
    }
}

Controller.java

// Nothing interesting here, added for consistency.
public class Controller {
    private final Model model;

    public Controller(Model model) {
        // The frame is shown automatically in the model here.
        this.model = model;

        // The frame's setVisible is a control issue, should be called
        // from in here, not automatically in the model.
    }
}

MvcFrame.java

import javax.swing.JFrame;

public class MvcFrame extends JFrame {
    private final Model model;

    public MvcFrame(Model model){
        this.model = model;
        // Anytime you add anything to a JFrame, use the content pane.
        this.getContentPane().add(model.getView());

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // This line centers the frame on your screen.
        setLocationRelativeTo(null);
        setSize(800,600);
        // The frame won't paint until it's visible.
        // This means if you check dimensions, everything will be 0 x 0
        // until this line is called.
        setVisible(true);
    }
}

Model.java

import java.awt.Color;

public class Model {
    private final Controller controller;  // Not used yet
    private final View view;
    private final MvcFrame mvcFrame; // Not used yet

    // Mutators and accessors needed for these guys (set/get)
    private final int radius = 44;
    private final Color color = Color.BLUE;
    private final boolean solid = true;

    public Model() {
        controller = new Controller(this);
        view = new View(this);
        mvcFrame = new MvcFrame(this);
    }

    public View getView() {
        return view;        
    }

    public int getRadius() {
        return radius;      
    }

    public Color getColor() {
        return color;       
    }

    public boolean isSolid() {
        return solid;       
    }
}

View.java

import java.awt.Graphics;
import javax.swing.JPanel;

public class View extends JPanel{
    private final Model model;

    public View(Model model) {
        this.model = model;
    }

    @Override
    protected void paintComponent(Graphics g){
        super.paintComponent(g);

        //center of view panel, in pixels:
        int xCenter = getWidth()/2;
        int yCenter = getHeight()/2;

        int radius = model.getRadius();
        int xStart = xCenter - radius;
        int yStart = yCenter - radius;
        int xWidth = 2 * radius;
        int yHeight = 2 * radius;
        g.setColor(model.getColor());

        g.clearRect(0, 0, getWidth(), getHeight());

        if (model.isSolid()){
            g.fillOval(xStart, yStart, xWidth, yHeight);
        } else {
            g.drawOval(xStart, yStart, xWidth, yHeight);
        }        
    }
}

Upvotes: 2

Ben
Ben

Reputation: 57257

Your question is a bit rambling and long-winded, and I'm not sure why you have commented out critical parts of the code, but the first question is:

//add(new View(model));

Are you adding the view to the frame? The oval could be drawing just fine, but the view isn't added.


A problem is most likely in this code:

public class MvcFrame extends JFrame {
    ...

    public MvcFrame(Model model){
        ...
        //setLayout(new GridLayout(4,1));
        //add(new RadiusPanel(model));
        ...
    }
}

When you call add directly, it references the superclass (JFrame). Unfortunately JFrames are sneaky since they have a contentPane which holds the layout. Further sneakiness: that content pane is a null layout and can only be changed by putting in your own panel.

So, you should probably make something like this. Even if you don't follow exactly, the methods should help you a lot:

...
JPanel pnl = new JPanel(new GridLayout(4, 1));
this.setContentPane(pnl);
pnl.add(new RadiusPanel(model));
...

If you don't want to set the content pane explicitly, you can use this.getContentPane().add(foo).

The null layout issue may also be affecting your oval drawing, since when you add the JPanel, its size is not specified so defaults to (0,0).


Also, not sure why your controller extends JPanel. Your view should be available to the controller, and should be the only thing with any swing components in it.

Upvotes: 1

Related Questions