| src | ||
| test/tests | ||
| .gitignore | ||
| README.md | ||
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
Shapesosztá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)–Line2Dsegí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–startbeállításadraw–endbeállításarender– astart-ból és azend-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
Pointspé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 arendermetó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)éssetDrawingPanel(DrawingPanel p)– referenciák beállítása atoolBar-hoz és adrawingPanel-hezmousePressed(MouseEvent e)– mód alapján új alakzat létrehozása amap, amiben benn van a callback függvény és ezt meghívva átadja a módnak megfelelő alakzatot aShapeList-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)éssetLineWidth(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 Controllerrendermetódusát, ami az összes alakzatot kirajzolja. - Regisztrálja a
MouseListenerésMouseMotionListenerpé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 visszakapottShapeListbeá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 egyBufferedImage-et a panel méretével, meghívja a panelpaintmetódusát, majdImageIO.write-tel elmenti PNG-be.saveToJSON(ShapeList list)–JFileChooser-rel bekéri a fájlnevet, majdObjectMappersegítségével JSON-ként kiírja a ShapeListet.loadFromJSON()– beolvas egy JSON fájlt,ObjectMapper.readValuesegítségével visszaalakítjaShapeList-té, és visszaadja azt. Hibák eseténJOptionPanesegí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– aserialize(Color value, JsonGenerator gen, SerializerProvider provider)metódus aColorobjektum ARGB értékét írja ki egyetlen egész számként.ColorDeserializer– adeserialize(JsonParser p, DeserializationContext ctxt)metódus visszaalakítja az egész számotColorobjektummá.
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ő
IOExceptionesetén a kivétel stack trace kiírása mellett a felhasználó egyJOptionPane.showMessageDialogablakban 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.
- JSON mentés vagy betöltés közben fellépő
-
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
isSavedflag. Ha a rajz nincs elmentve, egyJOptionPanekérdéses ablak jelenik meg (Igen/Nem/Mégse), így a felhasználó dönthet a mentésről.
- Új rajz, megnyitás vagy kilépés előtt ellenőrzésre kerül az
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