Programozás alapjai 3 nagyházi feladat, egyszerű paint
Find a file
Gyürüs Bence a96ed7b7b0 update readme
2025-12-02 14:02:31 +01:00
src update readme 2025-12-02 13:58:01 +01:00
test/tests updare readme 2025-12-02 13:23:27 +01:00
.gitignore fix .gitignore 2025-12-01 00:08:13 +01:00
README.md update readme 2025-12-02 14:02:31 +01:00

Felhaszálói dokumentáció

Leírás:

A program egy egyszerű, a Windows Painthez hasonló rajzoló alkalmazás. A felhasználó egy vásznon készíthet rajzokat, különböző rajzeszközök például vonal, téglalap, kör, szabadkézi rajz, valamint radír segítségével.
A rajzoláshoz tartozó beállítások, mint az aktuális szín és ecsetvastagság megadása, a menüsoron vagy az eszköztáron keresztül érhetők el. A kész rajz elmenthető, később visszatölthető, illetve lehetőség van PNG formátumú kép exportálására is.

Felépítés:

A legfelső sávban található a menüsor, benne a Fájl menüponttal.
Itt érhetők el az alapvető műveletek: új, mentés, *megnyitá valamint az Exportálás (PNG).
A menüsor alatt helyezkedik el az eszköztár, ahol kiválasztható a kívánt rajzeszköz (vonal, téglalap, kör, szabadkézi rajz vagy radír), valamint innen állítható be az ecsetvastagság és a rajzeszköz színe.
A felhasználó által látható fennmaradó terület maga a rajzfelület (vászon).

Használat:

A rajzfelületre az egér lenyomásával és mozgatásával lehet rajzolni. A rajzeszközök közül a megfelelő gombra kattintva lehet kiválasztani, hogy milyen alakzatot szeretnénk rajzolni.
Szabadkézi rajz esetén a program az egér mozgását folyamatosan vonallá alakítja. A kör, a téglalap vagy az egyenes vonal rajzolásakor az egér lenyomása jelöli a kezdőpontot, elengedése pedig a végpontot. A radír eszköz kiválasztása után a felhasználó a vászon bizonyos részeit törölheti, melynek működése a szabadkézi rajz metódusához hasonló, csak fordított, ahol rajzol ott a már korábbi vonalakat törli.
Az ecsetvastagság a csúszkával állítható, a szín pedig a Szín választása gombra kattintva felugró ablakban adható meg.

Mentés:

A rajzot a Fájl → Mentés menüpont kiválasztásával lehet elmenteni. Mentéskor a program JSON formátumban tárolja a rajzelemeket, így azok később visszaállíthatók. Ilyenkor a felhasználó megadhatja a fájl nevét és helyét.

Megnyitás:

A Fájl → Megnyitás menüpontra kattintva lehet korábban elmentett rajzot betölteni. A megnyitás során a program JSON fájlból olvassa vissza a rajzelemeket, és helyreállítja azokat a vásznon.
A program csak a saját formátumában mentett JSON fájlokat tudja betölteni. Ha a kiválasztott fájl nem megfelelő, hibajelzést ad.

Exportálás:

A rajz PNG formátumban történő mentéséhez a Fájl → Exportálás PNG-be menüpont használható. A felhasználó megadhatja a fájl nevét és helyét, a program pedig a teljes rajzfelület tartalmát képként menti el.

Programozói dokumentáció

Feladat:

A nagyházi feladat célja egy egyszerű, Windows Painthez hasonló rajzolóprogram elkészítése volt. A programban a felhasználó egy rajzvásznon tud különböző alakzatokat rajzolni: egyenes vonalat, téglalapot, kört, valamint szabadkézi rajzot. Emellett elérhető egy radír eszköz is, amivel a már meglévő rajz egyes részei törölhetők. A rajz színe és vonalvastagsága állítható, a rajzokat el lehet menteni JSON formátumba, vissza lehet tölteni, illetve a teljes vászon PNG képként exportálható.

A program Java nyelven, Swing grafikus keretrendszerrel készült. A rajzelemek egy absztrakt ősosztályból származnak, így egy heterogén kollekcióban (ShapeList) könnyen együtt tárolhatók. A JSON mentéshez és visszatöltéshez a Jackson könyvtárat használja a program.

Osztályok:

Shapes package:

Class Shapes shapes/Shapes.java

Az Shapes egy absztrakt ősosztály, minden konkrét rajzelem, forma ebből származik. Itt vannak a közös adatok és metódusok. Főbb tagváltozók:

  • Color color az alakzat színe.
  • float lineWidth a vonalvastagság. Ezek csak itt vannak eltárolva, a gyerek osztályok innen tudnak hozzáférni, mivel ezek protectedek. Főbb tagfüggvények:
  • Konstruktorok: alapértelmezett, a Jackson miatt és (Color c, float w) paraméteres konstruktor a szín és vastagság beállítására.
  • public void MousePressed(MouseEvent e) az egér lenyomásakor hívódik, csak leszármazottakban kerül megvalósításra.
  • public void draw(MouseEvent e) az egér húzása közben hívódik, módosítja az alakzat geometriáját.
  • public void render(Graphics2D g) beállítja a színt, vonalvastagságot, majd a konkrét alakzat kirajzolása a gyerekosztályokban történik.
  • Getterek/setterek színre és vonalvastagságra. A Shapes osztály JSON annotációi biztosítják, hogy a Jackson tudja, hogy melyik konkrét gyerekosztályt kell létrehozni visszatöltéskor.
Class Line shapes/Line.java

Egy egyenes vonal tárolására szolgáló osztály. Tagváltozók:

  • Point start a vonal kezdőpontja.
  • Point end a vonal végpontja. Főbb tagfüggvények:
  • Konstruktorok: alapértelmezett és (Color c, float w) paraméteres.
  • MousePressed(MouseEvent e) a kezdőpont beállítása az aktuális egérpozícióra.
  • draw(MouseEvent e) a végpont folyamatos frissítése az egér aktuális pozíciójával.
  • render(Graphics2D g) Line2D segítségével kirajzolja a vonalat.
  • Getterek/setterek a kezdő és végpontokhoz.
Class Square shapes/Square.java

Téglalap/négyszög tárolására szolgáló osztály (a név Square, de a két pontból általában téglalap keletkezik). Tagváltozók:

  • Point start az első sarok.
  • Point end az aktuális, átlósan szemközti csúcsának a pontja. Tagfüggvények:
  • Konstruktorok: alapértelmezett és (Color c, float w) paraméteres.
  • MousePressed eltárolja a kezdőpontot.
  • draw az egérmozgatás alapján frissíti a második pontot.
  • render rajzol 4db vonalat aminek a kezdő és vég pontja a 2 szemközti sarok alapján van meghatározva.
  • Getterek/setterek a start/end pontokra.
class Circle shapes/Circle.java

Körrészlet/kör tárolása. Tagváltozók:

  • Point start a kör keződpontja.
  • Point end a kör végpontja. Főbb függvények:
  • Konstruktorok: alapértelmezett és (Color c, float w) paraméteres.
  • MousePressed start beállítása
  • draw end beállítása
  • render a start-ból és az end-ből készíti el a kört vagy elipszist
  • Getterek/setterek a center és edge mezőkre.
Class Points shapes/Points.java

A szabadkézi vonal és a radír megvalósítására szolgáló osztály. Lényegében egymás után következő pontok listáját tárolja. Tagváltozók:

  • LinkedList<Point> points a rajzolt pontok sorozata. Tagfüggvények:
  • Konstruktorok: alapértelmezett és (Color c, float w) paraméteres.
  • MousePressed az első pont hozzáadása.
  • draw minden egérmozgatáskor új pont kerül a listára.
  • render egymást követő pontokat rövid vonalakkal köti össze, így jön létre a szabadkézi vonal.
  • Getter/setter a pontlistához. Az ERASER mód is egy Points példány, csak a színe a háttér színe, így radírként működik, mivel a háttérszín nem változtatható.
Class ShapeList shapes/ShapeList.java

A ShapeList a rajz összes alakzatát tárolja egy LinkedList<Shapes> listában. Mezők:

  • LinkedList<Shapes> shapes a rajzelemek listája. Főbb tagfüggvények:
  • addShape(Shapes s, MouseEvent e) új alakzat hozzáadása a listához és az első egér esemény átadása.
  • editLastShape(MouseEvent e) a legutoljára hozzáadott alakzat módosítása az aktuális egérpozícióval (rajzolás közben).
  • render(Graphics2D g) végigmegy az összes eltárolt alakzaton és meghívja a render metódusukat.
  • clear() az összes alakzat törlése.
  • size() a lista elemszámát adja vissza.
  • getShape(int i) visszaadja az i-edik alakzatot.
  • getShapes() / setShapes() a teljes lista lekérdezése és beállítása (JSON betöltéskor hasznos).
  • remove(Shapes s) egy konkrét alakzat eltávolítása, ha benne van a listában.
Class Controller Controller/Controller.java

A Controller a program központi vezérlője, koordinálja a rajzolást, a ShapeListet és a felhasználói beállításokat. Tagváltozók:

  • ToolBarPanel toolBar az eszköztár referenciája.
  • ShapeList shapeList a rajzolt alakzatok listája.
  • DrawingPanel drawingPanel a rajzfelület.
  • DrawingModes mode aktuális rajzolási mód.
  • Color color aktuális rajzszín.
  • float lineWidth aktuális vonalvastagság.
  • boolean isSaved mentettségi állapot.
  • Map<String, ShapeFactory> map különböző módokhoz tartozó gyárfüggvények. Főbb tagfüggvények:
  • Konstruktor, ami beállítja a ShapeListet és az alapértelmezett módot/színt/vastagságot.
  • setToolBar(ToolBarPanel bar) és setDrawingPanel(DrawingPanel p) referenciák beállítása a toolBar-hoz és a drawingPanel-hez
  • mousePressed(MouseEvent e) mód alapján új alakzat létrehozása a map, amiben benn van a callback függvény és ezt meghívva átadja a módnak megfelelő alakzatot a ShapeList-nek.
  • mouseDragged(MouseEvent e) az utolsó alakzat módosítása (editLastShape).
  • render(Graphics2D g) a ShapeList kirajzolása.
  • setMode(DrawingModes m) rajzolási mód állítása (eszközváltás).
  • setColor(Color c) és setLineWidth(float w) beállítja az aktuális rajzparamétereket.
  • clear() vászon törlése.
  • saved() visszaadja, hogy a rajz mentett állapotban van-e.
  • setSaved() mentés után hívódik, visszaállítja a mentett állapotot.
  • getDrawingMode() az aktuális mód lekérdezése (pl. UI visszajelzéshez).

ui package:

class DrawingPanel ui/DrawingPanel.java

A rajzvászon, ami a JPanel leszármazottja. Feladatai:

  • A paintComponent(Graphics g) metódusban hívja a Controller render metódusát, ami az összes alakzatot kirajzolja.
  • Regisztrálja a MouseListener és MouseMotionListener példányokat, melyek továbbadják az egéreseményeket a Controllernek.
  • setCustomCursor(URL url) az aktuális rajzeszközhöz tartozó egyedi kurzor (ikon) beállítása.
  • A panel mérete alapján történik a PNG export képmérete.

class ToolBarPanel ui/ToolBarPanel.java

Az eszköztár panel, amely a rajzeszköz gombokat, a színválasztót és a vonalvastagság csúszkát tartalmazza. Tagváltozók:

  • Hivatkozás a Controller-re, hogy gombnyomáskor beállíthassa a módot, színt, vastagságot. Főbb függvények:
  • Konstruktor, amely létrehozza a gombokat (vonal, négyzet, kör, szabadkézi, radír) és az ikonjaikat.
  • addButton(String name, String iconPath, DrawingModes mode) egy új gomb hozzáadása és eseménykezelő beállítása.
  • setActive(DrawingModes mode) kijelöli az aktuálisan aktív eszközt (keretszín változtatás Button-on).

class Button ui/Button.java

Egy egyszerű JButton leszármazott, ami támogatja az aktív/ passzív kinézetet. Fontosabb függvény:

  • setBorderColor(Color c) beállítja a gomb körüli szegély színét, így lehet kiemelni az aktuális eszközt.

class MenuBar ui/MenuBar.java

A felső menüsor, főleg a fájlműveletekhez. Fő funkciók:

  • Fájl -> Új: új rajz létrehozása, szükség esetén mentésre figyelmeztetéssel.
  • Fájl -> Megnyitás: ExportImportFunctions.loadFromJSON() hívása, majd a visszakapott ShapeList beállítása a Controllerben és a DrawingPanel újrarajzolása.
  • Fájl -> Mentés: ExportImportFunctions.saveToJSON(shapeList) meghívása.
  • Fájl -> Exportálás PNG-be: ExportImportFunctions.exportToPNG(drawingPanel).
  • Fájl -> Kilépés: kilépés előtti mentés figyelmeztetéssel.
class ExportImportFunctions export/ExportImportFunctions.java

Statikus segédosztály a fájlműveletekhez. Főbb metódusok:

  • exportToPNG(DrawingPanel canvas) létrehoz egy BufferedImage-et a panel méretével, meghívja a panel paint metódusát, majd ImageIO.write-tel elmenti PNG-be.
  • saveToJSON(ShapeList list) JFileChooser-rel bekéri a fájlnevet, majd ObjectMapper segítségével JSON-ként kiírja a ShapeListet.
  • loadFromJSON() beolvas egy JSON fájlt, ObjectMapper.readValue segítségével visszaalakítja ShapeList-té, és visszaadja azt. Hibák esetén JOptionPane segítségével jelenít meg hibaüzenetet.
class ColorSerializer / ColorDeserializer export/ColorSerializer.java, export/ColorDeserializer.java

Ezek a Jackson-hoz tartozó adapter osztályok.

  • ColorSerializer a serialize(Color value, JsonGenerator gen, SerializerProvider provider) metódus a Color objektum ARGB értékét írja ki egyetlen egész számként.
  • ColorDeserializer a deserialize(JsonParser p, DeserializationContext ctxt) metódus visszaalakítja az egész számot Color objektummá.

enum DrawingModes ui/DrawingModes.java

Az elérhető rajzolási módok felsorolása:

  • LINE egyenes vonal rajzolása.
  • SQUARE téglalap rajzolása.
  • CIRCLE kör rajzolása.
  • POINTS szabadkézi rajz.
  • ERASER radír (háttérszínű szabadkézi rajz).

UML ábra:

classDiagram

%% ========================
%%       MAIN + UI
%% ========================

class Main {
    +main(String[] args)
}

class MenuBar {
    <<JMenuBar>>
    +MenuBar(Controller c, ShapeList list, DrawingPanel panel)
}

class ToolBarPanel {
    <<JPanel>>
    -Map~String, Button~ buttons
    +ToolBarPanel(Controller c)
    +addButton(String name, String iconPath, DrawingModes mode)
    +setActive(DrawingModes mode)
}

class Button {
    <<JButton>>
    +Button(String tooltip, Icon icon)
    +setBorderColor(Color c)
}

class DrawingPanel {
    <<JPanel>>
    -Controller controller
    +DrawingPanel(Controller c, ShapeList list)
    +paintComponent(Graphics g)
    +setCustomCursor(URL url)
}

class DrawingModes {
    <<enum>>
    LINE
    SQUARE
    CIRCLE
    POINTS
    ERASER
}

Main --> Controller
Main --> MenuBar
Main --> ToolBarPanel
Main --> DrawingPanel

MenuBar --> Controller
MenuBar --> ShapeList
MenuBar --> DrawingPanel

ToolBarPanel --> Button
ToolBarPanel --> DrawingModes
ToolBarPanel --> Controller

DrawingPanel --> Controller


%% ========================
%%       CONTROLLER
%% ========================

class Controller {
    -ToolBarPanel toolBar
    -ShapeList shapeList
    -DrawingPanel drawingPanel
    -DrawingModes mode
    -Color color
    -float lineWidth
    -boolean isSaved
    -Map~String, ShapeFactory~ factoryMap
    +Controller(ShapeList list)
    +setToolBar(ToolBarPanel bar)
    +setDrawingPanel(DrawingPanel p)
    +mousePressed(MouseEvent e)
    +mouseDragged(MouseEvent e)
    +render(Graphics2D g)
    +setMode(DrawingModes m)
    +setColor(Color c)
    +setLineWidth(float w)
    +clear()
    +saved() boolean
    +setSaved()
    +getDrawingMode() DrawingModes
}

Controller --> ShapeList
Controller --> DrawingPanel
Controller --> ToolBarPanel
Controller --> DrawingModes
Controller --> ShapeFactory


%% ========================
%%        SHAPES
%% ========================

class ShapeList {
    -LinkedList~Shapes~ shapes
    +addShape(Shapes s, MouseEvent e)
    +editLastShape(MouseEvent e)
    +render(Graphics2D g)
    +clear()
    +size() int
    +getShape(int i) Shapes
    +getShapes() LinkedList~Shapes~
    +setShapes(LinkedList~Shapes~ list)
    +remove(Shapes s)
}

class Shapes {
    <<abstract>>
    -Color color
    -float lineWidth
    +Shapes()
    +Shapes(Color c, float w)
    +MousePressed(MouseEvent e)
    +draw(MouseEvent e)
    +render(Graphics2D g)
    +getColor() Color
    +setColor(Color c)
    +getLineWidth() float
    +setLineWidth(float w)
}

class Line {
    -Point start
    -Point end
    +Line()
    +Line(Color c, float w)
    +MousePressed(MouseEvent e)
    +draw(MouseEvent e)
    +render(Graphics2D g)
    +getStart() Point
    +getEnd() Point
    +setStart(Point p)
    +setEnd(Point p)
}

class Square {
    -Point start
    -Point end
    +Square()
    +Square(Color c, float w)
    +MousePressed(MouseEvent e)
    +draw(MouseEvent e)
    +render(Graphics2D g)
    +getStart() Point
    +getEnd() Point
    +setStart(Point p)
    +setEnd(Point p)
}

class Circle { 
    -Point start 
    -Point end 
    +Circle() 
    +Circle(Color c, float w) 
    +MousePressed(MouseEvent e) 
    +draw(MouseEvent e) 
    +render(Graphics2D g) 
    +getStart() Point 
    +getEnd() Point 
    +setStart(Point p) 
    +setEnd(Point p) 
}

class Points {
    -LinkedList~Point~ points
    +Points()
    +Points(Color c, float w)
    +MousePressed(MouseEvent e)
    +draw(MouseEvent e)
    +render(Graphics2D g)
    +getPoints() LinkedList~Point~
    +setPoints(LinkedList~Point~ pts)
}

Shapes <|-- Line
Shapes <|-- Square
Shapes <|-- Circle
Shapes <|-- Points

ShapeList o-- Shapes


%% ========================
%%     EXPORT / IMPORT
%% ========================

class ExportImportFunctions {
    <<static>>
    +exportToPNG(DrawingPanel panel)
    +saveToJSON(ShapeList list)
    +loadFromJSON() ShapeList
}

class ColorSerializer {
    <<JsonSerializer<Color>>>
    +serialize(Color value, JsonGenerator gen, SerializerProvider prov)
}

class ColorDeserializer {
    <<JsonDeserializer<Color>>>
    +deserialize(JsonParser p, DeserializationContext ctxt) Color
}

Main --> ExportImportFunctions
Shapes --> ColorSerializer
Shapes --> ColorDeserializer

Hibakezelés

A program többféle hibát kezel:

  • Fájlműveletek hibái:

    • JSON mentés vagy betöltés közben fellépő IOException esetén a kivétel stack trace kiírása mellett a felhasználó egy JOptionPane.showMessageDialog ablakban tájékoztatást kap a hibáról.
    • Ha a felhasználó megszakítja a fájlválasztást (CANCEL), a művelet egyszerűen nem történik meg, a program futása folytatódik.
  • PNG export hibái:

    • Ha a vászon mérete 0 (még nem látszik a panel), az export metódus védekezik, és nem hoz létre üres képet.
    • Írási hiba esetén szintén felugró hibaüzenet jelenik meg.
  • Mentettségi állapot:

    • Új rajz, megnyitás vagy kilépés előtt ellenőrzésre kerül az isSaved flag. Ha a rajz nincs elmentve, egy JOptionPane kérdéses ablak jelenik meg (Igen/Nem/Mégse), így a felhasználó dönthet a mentésről.

Az egéreseményeknél a program feltételezi, hogy a mouse event-ek érvényesek; az alakzatok konstruktorai a szín és a vastagság állapotából indulnak ki, azok helyességét a Controller garantálja (UI-ból jönnek).

Tesztek

A tesztekkel a Shape-ből létrejövő osztályokat valamint a ShapeListet és a Controller-t tesztelem.

Mappa struktúra

nagyhazi/  
├── src/  
│   ├── Main.java  
│   ├── Controller/  
│   │   └── Controller.java  
│   ├── shapes/  
│   │   ├── Shapes.java  
│   │   ├── ShapeFactory.java  
│   │   ├── ShapeList.java  
│   │   ├── Line.java  
│   │   ├── Circle.java  
│   │   ├── Square.java  
│   │   └── Points.java  
│   ├── ui/  
│   │   ├── Button.java  
│   │   ├── DrawingPanel.java  
│   │   ├── DrawingModes.java  
│   │   ├── MenuBar.java  
│   │   └── ToolBarPanel.java  
│   ├── export/  
│   │   ├── ColorDeserializer.java  
│   │   ├── ColorSerializer.java
|   |   └── ExportImportFunctions.java
│   └── icons/  
│       ├── line.png  
│       ├── square.png  
│       ├── circle.png  
│       ├── points.png  
│       └── eraser.png  
│  
├── out/  
│   └── (buildelt .class fájlok)  
│  
├── test/  
│   └── tests/  
│       ├── ControllerTest.java  
│       ├── LineTest.java  
│       ├── CircleTest.java  
│       ├── SquareTest.java  
│       ├── PointsTest.java  
│       └── ShapeListTest.java  
│  
├── .gitignore  
├── nagyhazi.iml  
└── README.md