Programovanie (2) v Jave
1-INF-166, LS 2017/18

Úvod · Pravidlá · Prednášky · Netbeans · Testovač · Test a skúška
· Vyučujúcich môžete kontaktovať pomocou e-mailovej adresy E-prg.png (bude odpovedať ten z nás, kto má príslušnú otázku na starosti alebo kto má práve čas).
· DÚ10 je zverejnená, odovzdávajte do stredy 16.5. 22:00.
· Bonusový projekt odovzdávajte do pondelka 21.5. 22:00 na testovači. Predvádzanie projektov bude po prvom termíne skúšky v stredu 23.5. (t.j. začneme medzi 11:45 a 12:00 v H6). Ak vtedy nemôžete prísť, kontaktujte vyučujúce, dohodneme iný termín.
· Opravný/náhradný test bude v pondelok 28.5. o 10:00 v F1-108. V prípade záujmu sa prihláste v AISe.
· Na termíny skúšok sa zapisujte v AIS. Termíny: streda 23.5. (riadny), štvrtok 7.6. (riadny alebo 1. opravný), streda 20.6. (1. alebo 2. opravný) plus v prípade potreby ešte jeden termín v poslednom týždni skúškového. Prípadné konflikty nám dajte vedieť čím skôr. Ďalšie informácie na stránke Test a skúška.


GraphGUI

Z Programovanie
Prejsť na: navigácia, hľadanie

Program GraphGUI ku skúške

Na skúške pri počítači budete pracovať s programom GraphGUI. Tento program aj jeho dokumentáciou dostanete k dispozícii na skúške, odporúčame vám ale sa s ňou oboznámiť vopred.

  • zip
  • JavaDoc dokumentácia
  • Autori J. Katreniaková, B. Brejová a traja minuloroční prváci Samuel Gurský, Jakub Šimo a Emanuel Tesař
  • Ak objavíte chybu, dajte čím skôr vedieť B. Brejovej

Do programu graphgui budete pridávať riešenia dvoch úloh

  • Tieto úlohy bude možné riešiť nezávisle na sebe a budú mať rovnakú váhu, nemusia však byť pre vás rovnako ťažké
  • V úlohe A bude treba doprogramovať nové dialógové okno do grafického prostredia
  • V úlohe B bude treba riešiť grafový problém metódou prehľadávania s návratom
  • Odovzdávate iba súbory Editor.java (úloha A) a GraphAlgorithm.java (úloha B), celé vaše riešenie by teda malo byť v týchto súboroch.
  • Ak kvôli ladeniu meníte iné časti programu, ubezpečte sa, že odovzdané súbory pracujú aj s pôvodnou verziou programu graphgui.
  • Pokiaľ v programe pre daný termín skúšky spravíme zmeny oproti vopred zverejnenej verzii, vysvetlíme ich v zadaní. Veľká väčšina programu však bude identická so zverejnenou.

Používanie programu

Po spustení GraphGui sa objaví grafický panel na editovanie neorientovaného grafu

  • Editor má tri módy: Add, Delete a Select
  • V móde Add kliknutie na prázdne miesto pridá vrchol, postupné kliknutie na dva vrcholy ich spojí hranou
  • V móde Delete môžete zmazať hranu alebo vrchol kliknutím na ne
  • V móde Select môžete označiť hranu alebo vrchol kliknutím na ne, kliknutím na prázdnu plochu označenie zrušíte
  • Označený vrchol má červený rámik, označená hrana je čiarkovaná
  • V pravom paneli sa vypisujú vlastnosti označeného vrcholu a hrany, dajú sa tam aj editovať
  • V ľavom paneli sú okrem tlačidiel na výber módu aj tlačidlá na spustenie riešenia úlohy A a B
  • Program je možné ovládať aj zadávaním textových príkazov do príkazového riadku v spodnej časti okna, zoznam príkazov je aj v položke Help
  • Graf (vrátane vyznačenej hrany a vrchola) je možné ukladať do súboru a naopak otvárať zo súboru.
    • V programe je však obmedzenie - meno súboru ani priečinku nesmie obsahovať medzery a iné biele znaky

Dôležité súčasti knižnice

Graph, Vertex, Edge

  • Rozhranie pre prácu s neorientovaným grafom.
  • Umožňuje pridávať aj mazať vrcholy aj hrany.
  • Vrcholy a hrany sú objekty typu Vertex a Edge.
  • Vrcholy aj hrany si pamätajú hodnotu (value) typu int, ktorá môže predstavovať napr. vzdialenosť dvoch miest, počet obyvateľov mesta a pod.
  • Vrcholy a hrany majú priradenú aj určitú farbu, ktorou sa vyfarbujú. Tato farba je uložená ako reťazec, napr. "white", "black", prípadne "#00ffff"
  • Vrcholy sú číslované 0,...,N-1, kde N je počet vrcholov, a v algoritmoch je možné k nim pristupovať buď cez tieto čísla (id) alebo cez samotné objekty typu Vertex. Po zmazaní vrchola sa id ostatných vrcholov môžu zmeniť.
  • Vrcholy majú uložené aj údaje o svojich súradniciach x a y, čo sú reálne čísla od 0 do 1.
  • Pre každú reálnu hranu grafu {u,v} sú v grafe dve inštancie rozhrania Edge. Jedna ide z u do v a druhá naopak. Graf je však neorientovaný, takže predstavujú to isté a majú tú istú hodnotu (value) a farbu. Jedna z nich je tzv primárna a je uložená v zozname všetkých hrán grafu.
  • Vo vašich častiach programu pristupujte k grafu len pomocou metód z rozhraní Graph, Vertex a Edge.
interface Graph {
  int getNumberOfVertices();
  int getNumberOfEdges();

  Iterable<Vertex> getVertices();
  Iterable<Edge> getEdges();

  Vertex getVertex(int id);

  Iterable<Vertex> adjVertices(Vertex vertex);
  Iterable<Integer> adjVertexIds(int id);
  Iterable<Edge> adjEdges(Vertex vertex);
  Iterable<Edge> adjEdges(int id);

  Vertex addVertex(double x, double y);
  void removeVertex(Vertex vertex);
  Edge addEdge(Vertex vertex1, Vertex vertex2);
  Edge addEdge(int id1, int id2);
  void removeEdge(Edge edge);
  void removeEdge(Vertex vertex1, Vertex vertex2);
  void removeEdge(int id1, int id2);

  Edge findEdge(Vertex vertex1, Vertex vertex2);
  Edge findEdge(int id1, int id2);

  void clear();
  void print(PrintStream out, boolean full);
  void read(Scanner scanner);

  void deselectEdge();
  void deselectVertex();
  void selectVertex(Vertex vertex);
  void selectEdge(Edge edge);
  Vertex getSelectedVertex();
  Edge getSelectedEdge();
}

interface Edge {
    Vertex getOrigin();
    int getOriginId();

    Vertex getDestination();
    int getDestinationId();

    boolean isEquivalent(Edge e); // su e a this ta ista hrana, prip. opacne?
    Edge getReverse();  //opacna reprezentacia tej istej hrany
    Edge getPrimary();  // bud tato hrana alebo jej opacna

    int getValue();
    void setValue(int value);

    String getColorName();
    void setColorName(String colorName);
    void setColorRgb(int red, int green, int blue);
}

interface Vertex {
    int getId();

    Iterable<Vertex> adjVertices();
    Iterable<Integer> adjVertexIds();
    Iterable<Edge> adjEdges();

    Edge findEdge(Vertex vertex);

    int getValue();
    void setValue(int value);

    double getX();
    void setX(double x);

    double getY();
    void setY(double y);

    String getColorName();
    void setColorName(String colorName);
    void setColorRgb(int red, int green, int blue);
}

GraphImplementation, VertexImplementation, EdgeImplementation

Pomocné triedy reprezentujúce graf a jeho súčasti

  • Implementujú rozhrania popísané vyššie, notifikujú grafické prvky o zmenách v grafe.
  • Vo vašich častiach programu nevolajte priamo metódy týchto tried, používajte len rozhrania uvedené vyššie.

GraphGUI, Controller, GraphPane, GraphDrawing, State, layout.fxml

Triedy implementujúce samotné grafické rozhranie. Tieto triedy nemáte na skúške meniť ani používať ich časti, takže sú tu uvedené len pre zaujímavosť.

  • GraphGUI obsahuje metódu main, ktorá naštartuje aplikáciu
  • Controller obsahuje funkčné prvky okrem samotnej plochy s grafom (tlačidlá, menu, príkazový riadok, editovanie nastavení vrchola a hrany)
  • Súbor layout.fxml obsahuje rozloženie ovládacích prvkov na ploche a ich základné vlastnosti v FXML formáte
  • GraphDrawing vykresľuje graf do plochy pomocou kruhov, čiar a textov, stará sa o nastavovanie súradníc, farby a pod. podľa toho ako sa graf mení
  • GraphPane obsahuje funkčné časti práce s grafom - reakcie programu na klikanie do grafickej plochy
  • State je pomocná trieda obsahujúca stav programu (Add/Delete/Edit) a samotný graf

GraphAlgorithm

Trieda, ktorá má obsahovať implementáciu požadovaného grafového algoritmu z úlohy B.

  • Používajte iba metódy rozhraní Graph, Vertex, Edge.
  • Nemeňte hlavičky public metódy ani konštruktora, môžete si však pridať do triedy ďalšie premenné, metódy, prípadne aj pomocné triedy.

Konštruktor dostane graf, ktorý si uloží.

  • Podľa potreby pridajte inicializáciu vašich ďalších premenných.

Metóda performAlgorithm má vykonať samotný algoritmus

  • podľa pokynov v zadaní môže modifikovať graf
    • napr. často je úlohou nejako prefarbiť vrcholy alebo hrany
  • výsledok výpočtu vráti ako String
    • použite formát požadovaný v zadaní

Ukážkový príklad tejto triedy, ktorý nájde všetkých susedov označeného vrchola a preferbí ich na oranžovo. Vráti správu so zoznamom čísel týchto vrcholov.

  • Na skúške budete mať riešiť nejaký problém metódou prehľadávania s návratom (backtracking). Na prednáške sme videli problém najdlhšej cesty, uvidíme ešte maximálnu kliku
public class GraphAlgorithm {

    //PREMENNÉ TRIEDY, UPRAVTE SI PODĽA POTREBY
    private final Graph graph;

    // KONŠTRUKTOR: NEMEŇTE HLAVIČKU, TELO UPRAVTE PODĽA POTREBY
    public GraphAlgorithm(Graph graph) {
        this.graph = graph;
    }
    // METÓDA performAlgorithm: NEMEŇTE HLAVIČKU, TELO UPRAVTE PODĽA POTREBY
    public String performAlgorithm() {
        Vertex selected = graph.getSelectedVertex();

        // ak bol nejaky vrchol vybrany
        if (selected != null) {
            String result = "";
            for (Vertex other : selected.adjVertices()) {
                other.setColorName("orange");
                if (!result.isEmpty()) {
                    result = result + " ";
                }
                result = result + other.getId();
            }
            return result;
        }
        return "no vertex selected";
    }
}

Editor, EditorException

Trieda, ktorá má obsahovať implementáciu úlohy A na skúške.

  • V tejto úlohe budete mať spraviť dialógové okno, v ktorom sa budú dať meniť určité aspekty grafu predpísaným spôsobom
    • Úlohou teda môže byť určitým spôsobom pridávať alebo uberať vrcholy či hrany, nastavovať im súradnice, hodnoty alebo farby
  • Konštruktor dostane odkaz na graf (typu Graph), môže si ho uložiť a prípadne inicializovať ďalšie premenné
  • Potom program zavolá metódu edit, v ktorej sa má vykresliť dialógové okno. Keď užívateľ ukončí prácu s týmto oknom (spravidla stlačením gombíka Ok), vaša časť programu skontroluje správnosť zadaných údajov a ak sú správne, zmení graf príslušným spôsobom (volaním metód z rozhraní Graph, Vertex a Edit)
  • Ak sú údaje nesprávne, upovedomí používateľa okienkom s chybovou hláškou (napr. použitím už hotovej metódy showError) a nechá používateľa ďalej meniť hodnoty
  • Ak je to v zadaní špecifikované, váš kód môže za stanovených okolností vyhodiť výnimku typu EditorException
  • Iné výnimky by v ňom vznikať nemali, resp. by mali byť odchytené ešte v rámci vášho kódu

V tomto príklade sa v prípade, že je označená hrana, zobrazí sa dialóg, ktorým sa dá meniť farba tejto hrany podľa jej red, green a blue zložiek.

  • ak nie je označená hrana, metóda edit vyhodí výnimku
public class Editor {

    // POMOCNÁ TRIEDA PRE UKÁŽKOVÝ PRÍKLAD, MEŇTE PODĽA POTREBY
    class MyStage extends Stage {
        Edge edge;

        MyStage(Edge edge) {
            this.edge = edge;
            GridPane pan = new GridPane();
            MyStage dialog = this;

            Color c = Color.web(edge.getColorName());

            final TextField rText = new TextField((int)(c.getRed() * 255) + "");
            final TextField gText = new TextField((int)(c.getGreen() * 255) + "");
            final TextField bText = new TextField((int)(c.getBlue() * 255) + "");
            final Label rLabel = new Label("Red (0-255): ");
            final Label gLabel = new Label("Green (0-255): ");
            final Label bLabel = new Label("Blue (0-255): ");
            Button ok = new Button("OK");

            pan.add(rLabel, 0, 0);
            pan.add(rText, 1, 0);
            pan.add(gLabel, 0, 1);
            pan.add(gText, 1, 1);
            pan.add(bLabel, 0, 2);
            pan.add(bText, 1, 2);
            pan.add(ok, 1, 3);

            ok.setOnAction((ActionEvent event) -> {
                int r, g, b;
                try {
                    r = Integer.parseInt(rText.getText());
                    g = Integer.parseInt(gText.getText());
                    b = Integer.parseInt(bText.getText());
                } catch (Exception e) {
                    showError("Bad color value (should be a number)");
                    return;
                }
                try {
                    if (r >= 0 && g >= 0 && b >= 0
                    && r < 256 && g < 256 && b < 256) {
                        edge.setColorRgb(r, g, b);
                        dialog.close();
                    } else {
                        showError("Bad color value (should be in range 0-255)");
                    }
                } catch (Exception e) {
                    showError("Color could not be set");
                }
            });

            Scene sc = new Scene(pan);
            this.setScene(sc);
        }

        private void showError(String message) {
            Alert error = new Alert(AlertType.ERROR, message);
            error.setHeaderText(null);
            error.initOwner(this);
            error.showAndWait();
        }
    }

    // PREMENNÉ TRIEDY, UPRAVTE SI PODĽA POTREBY
    private Graph graph;

    // KONŠTRUKTOR: NEMEŇTE HLAVIČKU, TELO UPRAVTE PODĽA POTREBY
    public Editor(Graph graph) {
        this.graph = graph;
    }

    // METÓDA edit: NEMEŇTE HLAVIČKU, TELO UPRAVTE PODĽA POTREBY
    public void edit() throws EditorException {
        Edge edge = graph.getSelectedEdge();
        if (edge == null) {
            throw new EditorException("No edge selected");
        }
        MyStage dialog = new MyStage(edge);
        dialog.initStyle(StageStyle.UTILITY);
        dialog.initModality(Modality.APPLICATION_MODAL);
        dialog.showAndWait();
    }
}