Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Diskuze: Obecná rada jak se neztratit v kódu

Aktivity
Avatar
meny.web
Člen
Avatar
meny.web:17.3.2018 12:20

Dobrý den,
rád bych se Vás zeptal trochu obecněji. Mám projekt na vykreslení schéma podniku do grafu. Chtěl jsem aby se schéma vygenerovalo na základě dvou seznamů - seznamu zaměstnanců a seznamu vazeb (podřízení a nadřízení), ale bohužel jsem se v tom dost ztrácel. Schéma se na základě vzorových dat už celkem hezky vykreslovalo, ale po nasazení fx prvků a následné editaci se to celé rozsypalo.. například když bylo potřeba odstranit zaměstnance, který měl pod sebou několik podřízených se přes tlačítko "Odebrat" musely odebrat i jeho vazby na ně (to proto, že jsem si ani nedovedl představit jak udělat to, aby podřízení zůstaly někde bokem v nějakém zásobníku). Takže u tlačítka bylo i několik metod na vyhledání, odebrání a překreslení grafu. No a pak mi z toho šla hlava kolem.. :) Jak se prosím v podobných problémech orientujete vy? Ještě doplním, že jsem jen začátečník.

Předem děkuji

 
Odpovědět
17.3.2018 12:20
Avatar
Atrament
Tvůrce
Avatar
Odpovídá na meny.web
Atrament:17.3.2018 14:53

Tím jak se neztratit v kódu se zabývá (mimo jiné) Softwareové inženýrství, a vysvětlovat Softwareové inženýrství je absolutně mimo možný rozsah komentářů zde na fóru. Prostě si najdi nějaké knihy o navrhováni software, o objektově orientovaném přístupu, a design patterns a pusť se do studia:)

 
Nahoru Odpovědět
17.3.2018 14:53
Avatar
meny.web
Člen
Avatar
meny.web:17.3.2018 17:59

Ok, takhle nasměrovat mě stačí. Kouknu na to.

Díky moc

 
Nahoru Odpovědět
17.3.2018 17:59
Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:17.3.2018 18:24

To se velmi těžko radí, když jsi sice pojmenoval problém, ale neřekls, v čem konkrétně se ztrácíš. Musíš znát logiku programu a pokud možno všechno programovat velmi obecně.
Viz třeba tento případ, kdy něco odebíráš a nebo přidáváš. Pokud ti to spadne na takové věci, tak neprogramuješ objektově. Už ve spoustě komentářích radím především začátečníkům, aby vždy, když naprogramují nějakou třídu, tak ji zkusili vyndat z projektu a spustit v podobném programu.
Dám ti příklad z praxe, kdy jsem teď sám toto pravidlo porušil a pak bych na to doplatil.

V práci máme projekt, který potřebuje mimo jiné vypisovat specifický log. Jak do mailu, tak na výstup. Navíc jsme si zmysleli přepínač, kterým log zapínáme na několika úrovních (třeba když potřebujeme podrobnější výpis exceptiony atd.) Všechno bylo fajn, až na to, že jsem si nevšiml, že jsem omylem do těchto logů implementoval načítání ze souboru. Všechno pochopitelně funguje, ale když jsem tu samou třídu chtěl potom vzít a použít na jiném místě, tak jsem si právě všiml, že jsem závislý na souboru, který vůbec v danou chvíli nepotřebuju použít. No a tak jsem to vyřešil přetížením parametru, ale musel jsem chvilku popřemýšlet nad strukturou celého kódu - jestli tím zase nic nenaruším atd.

Z toho textu jsem pochopil, že jsi asi přecházel ze swingu na FX. Přiznám se, že je to skvělý případ, kdy se ukáže, jak uvažuješ a jak počítáš do budoucna s upgradem. FX je založený naprosto na odlišném pohledu vykreslování, nežli swing. Nicméně je mnohem příjemnější, když opravuješ jednu třídu, která má na starost pouze a jenom vykreslování a nic jiného, než když máš ve třídě vykreslování a k tomu řadu dalších funkcí.
A nechci ani rozvádět porušování zapouzdření. Nováčci díky tomuto můžou mít spoustu problémů. Už jsem se setkal, že borec používal spoustu globálních proměnných a pak se taky v tom ztrácel, protože nevěděl, co se kde zrovna nastavuje. Potom musíš hodně dlouho pátrat v debuggeru a projíždět si několikrát vlákno dokola, abys třeba našel případnou chybu.

Přiznám se, že nechápu celkem tento ticket. K tomu, aby ses neztratil v kódu napomáhají komentáře, unitové testy (pokud si zvyklý je dělat a umíš je dobře napsat, tak pak se jen směješ), a hlavně dobrá implementace OOP. Pokud máš nějaké třídy, které jsou na sobě naprosto závislé, je to chyba. Samozřejmě, že je to pohodlnější, když tam přímo nastavíš vazby, ale pokud není třída soukromá a tudíž bys věděl, že bude vždy součástí nějaké jiné třídy a nikdy jinak, tak by každá třída měla stát sama o sobě a měla by mít i využití v jiném projektu. Takže moje rada, kterou dávám začátečníkům vždy - když naprogramuješ třídu a chceš mít aspoň malou jistotu, že programuješ objektově, vytvoř nový projekt, nasimuluj prostředí stávajícího projektu a spusť / použij tu třídu v jiném projektu. Doteď jsem nenašel žádný případ, kdy by měla být třída vázaná pouze na jeden projekt. To už se skoro 30 let nedělá.

Nahoru Odpovědět
17.3.2018 18:24
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Avatar
meny.web
Člen
Avatar
meny.web:18.3.2018 17:26

Dobrý den,
jak jsem si pročítal Vaší odpověď, tak jsem si všiml, že jsem to nenapsal úplně srozumitelně.., sorry. Na JavaFX jsem nepřešel ze Swingu, ale postupně jsem jen doplňoval prvky. Prvky jako seznamy, formuláře, tlačítka apod. Nejprve jsem měl k FX jen jediný holý panel, ve kterém se vykresloval graf. Vše fungovalo při ruční změně vzorových dat.

A pak jsem se začal ztrácet při implementaci těch seznamů a hlavně formulářů, tlačítek, pomocí kteých jsem, přidával, upravoval nebo odebíral záznamy. Ať už záznamy do seznamu zaměstnanců nebo do seznamu vazeb.

Například právě formulář pro přidání zaměstnance přidal jméno a příjmení do seznamu, ale dále pak ještě musel tohoto zaměstnace přidat do výběru možností ve dvou ComboBoxech, které sloužily pro přidání záznamu vazby. Jeden ComboBox pro výběr nadřízeného, druhý pro výběr podřízeného. To vše kvůli tomu, aby uživatel jen vybral přidaného zaměstnace a nemusel například zadávat jeho unikátní klíč, který by se mu automaticky přidělil a někde u seznamu zaměstnanců zobrazoval. No a právě ten kód (a podobné ovládání aplikace), co tohle dělá, mi hrozně motal hlavu..

Nepochybuji o tom, že v kódu byla spousta chyb.. ale ta třída pro vykreslení grafu byla podlě mě i celkem přenositelná. Vlastně vše co jako vstup potřebovala bylo seznam zaměstnanců a vazeb. Dále se jen z nadřazené třídy zavolalo několik metod.

Unitové testy jsem nedělal, ale mají se dělat i když má třída jen jediný účel? Na ITnetwork je třeba k unitovým testům nasáno tohle: "Nemá úplně smysl pomocí unit-testů testovat, zda metoda obsahující databázový dotaz, která je použita v jednom kontroleru (nebo nějaké jiné řídící vrstvě) vrací co má. Naopak dává velmi dobrý smysl testovat např. validátor telefonních čísel, který používáme na 20 místech nebo dokonce v několika aplikacích." To je právě můj případ ne? Když metoda vezme vazby a poskládá je do matice.

Jestli jejich absence byla tou podstatnou chybou jak přispět k lepší čitelnosti, tak bych na tom určitě zapracoval.

Děkuji

 
Nahoru Odpovědět
18.3.2018 17:26
Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:19.3.2018 10:27

Unitové testy má cenu dělat kdykoliv. Pouze, pokud voláš void metody, které jen volají jiné metody, tak takové testy není třeba dělat. Ale má to smysl. Je to sice sračka, ale právě v pozdějších updatech budeš rád, že si s tím dáš práci.
Jelikož nevidíme tvůj kód a jestli je pracovní, tak ani vidět nemůžeme (přeci jen už je to tvoje know how), tak se špatně soudí. Nicméně pokud máš v kódu jen jednu metodu, je to chyba.
Třeba jak jsi to teď popisoval.
Možná se pletu, ale nemáš to náhodou tak, že když implementuješ nějaký seznam ve formuláři, tak pak jak jsi popisoval:

Například právě formulář pro přidání zaměstnance přidal jméno a příjmení do seznamu, ale dále pak ještě musel tohoto zaměstnace přidat do výběru možností ve dvou ComboBoxech

, tak bych se docela divil, kdybys nedal všechno do eventů. Přitom by bylo krásné, kdybys právě vytvořil metody, které by něco někam přidávali a odebírali. A potom už jen zavoláš s příslušnými parametry (třeba jeden parametr, který by obsahoval hodnotu pro přidání a druhý parametr kam se má něco přidat) a všechno řešíš v těchto metodách. No a pak bys tyto metody otestoval lehce. Mohly by být prázdné, ale ty bys otestoval výsledek. Prostě když je použiješ, že se doopravdy v seznamu promítla nějaká změna.

Hlavně potom i ty komentáře a principy OOP. Unit testy jsou takové berličky pro budoucí update. V podstatě smysl unitových testů je v tom, že když je dobře napíšeš, tak potom můžeš kód upravovat jak chceš, ale když je spustíš, tak si ověříš, že jsou všechny stávající funkce zachovány. Tím máš i pak jistotu, žes nic neposral:)

Pro lepší čitelnost slouží hlavně komentáře. Když aktuálně něco programuješ, tak je ti všechno jasné a i víš, proč to či ono děláš. Ale otevřeš-li ten projekt za půl roku, Tak pak i přemýšlíš, proč jsi vlastně tuto metodu dělal. A to je třeba jeden z hlavních bodů, co bys měl komentovat. Ne to, jak ta metoda funguje (to vyčteš z jejího těla). Ale proč ta metoda vlastně je, proč se využívá, kdo a za jakých podmínek ji může použít atd. atd. atd. Prostě dokumentovat myšlenku, abys věděl, k čemu ta metoda je. Když uvidíš, že má metoda třeba návratový typ int, tak je blbost do dokumentace psát: Metoda vrací to a to číslo. To přece vidíš v její hlavičce. Ale nevíš, k čemu to číslo je. A to je důležité komentovat.

No a fakt ta nezbytná věc - dobrá struktura kódu. To se týká i metod. Prostě je nesmysl, když třeba nějaká metoda zobrazuje formulář a pracuje s nějakou událostí (třeba jen v tom smyslu, že vrací nějakou položku), tak do této metody rvát i přidávání a odebírání ze seznamu.
Ano, ušetříš tím nějaký ten řádek a kód je na oko přehlednější. Ale pak se v tom začneš takto ztrácet.

Je to to samé, jako třeba zkracování v proměnných.
Ono jde v Javě, jako v jiných jazycích deklarovat proměnné třeba takto:

public class ExampleClass {

        private int a = 0, b = 1, c = 3, d, e;
}

Ale když toto ukážeš zkušenému programátorovi, tak tě minimálně přerazí (jako třeba já:) toto je ukázka skutečného kódu, jaký já teď musím předělávat po bývalém kolegovi.... nejraději bych ho zabil!!!!!)

CO DO PRDELE ZNAMENÁ TO a, b, c????

Daleko lepší je napsat:

public class ExampleClass {

        private int hodnotaZadanaUzivatelem;
        private int vychoziHodnotaProNacteniZDatabaze = 1;
        private int celkovyPocetClenuVeSkupine;
        //atd...
}

Já doporučuju vyhýbat se takovémuto zkracování kódu. Ono to sice jde, ale pak je to děs, když to po tobě někdo přebírá nebo to chceš po nějaké době upravovat.
Toto je citát Martina Fowlera, který třeba ani začátečníci moc neznají, ale je výstižný a není k němu potřeba žádný komentář:

Any fool can write code that a computer can understand. Good programmers write code that humans can understand.

Zkracovat kód znamená v podstatě využívat principy OOP (dědičnost, rozhraní, abstraktní třídy, atd.) Ne to, že když to jde narvat do jedné metody, tak to tam šoupnu:)
Možná to neděláš, ale třeba jo a třeba toto ti to potom znepřehledňuje. Ptej se sám sebe - jestli ta dotyčná metoda vykonává pouze to, na co je stavěná a nebo jestli ještě k tomu dělá řadu dalších blbin, které bys od jejího názvu nečekal

Nahoru Odpovědět
19.3.2018 10:27
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Avatar
meny.web
Člen
Avatar
meny.web:19.3.2018 11:56

Zdravím,
já jsem jen samouk a celý projekt dělám kvůli tomu, abych se postupně učil, takže to sem nahrát můžu. Do těch testů se určitě pustím, děkuji za informace.

Hodně věcí je v těch Eventech, protože přes metody mi to nějak nereagovalo.. Myslím, že kvůli tomu nahrávání dat do seznamu přes RowFactory. To jsem celé dělal podle nějakého tutoriálu.

Zde je kód hlavní metody

package main;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.collections.ObservableList;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.*;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.Callback;
import javax.imageio.ImageIO;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.util.*;

/**
 * Created by uzivatel on 5.6.17.
 */
public class Main extends Application {

    //rozměry okna aplikace
    public static final double width = 600;
    public static final double height = 500;

    //šířka všech tlačítek
    private double buttonWidth = (width / 5.8) - (10 * 2);

    //výška tlačítek
    private double buttonHeight = 50;

    //grafické prvky aplikace
    private BorderPane root = new BorderPane();

    //levý box s menu
    private VBox right = new VBox(5.0);

    //pravý box pro prostory editace položek a vazeb
    private VBox left = new VBox();

    //pravý box pro prostory editace položek a vazeb
    private VBox menu = new VBox(9.0);

    //název aplikace
    private VBox head = new VBox();
    //private Image headings = new Image(getClass().getResourceAsStream("map.png"), 35, 35, false, false);
    //private Label headings = new Label("Organization");

    //centrální prostřední část pro graf
    private VBox central = new VBox();

    //tlačítka hlavního menu
    //private Button employeeButton = new Button("Zaměstnanci");
    private Image employeeImage = new Image(getClass().getResourceAsStream("users.png"), 20, 20, false, false);
    private Button employeeButton = new Button("Osoby", new ImageView(employeeImage));

    //private Button edgesButton = new Button("Vazby");
    private Image edgesImage = new Image(getClass().getResourceAsStream("hierarchy.png"), 20, 20, false, false);
    private Button edgesButton = new Button("Vazby", new ImageView(edgesImage));

    //prvky pro přidání nového záznamu
    private HBox addEmployeeBox = new HBox();

    private TextField jobField = new TextField();
    private TextField nameField = new TextField();
    private TextArea descriptionField = new TextArea();

    private Button employeeAdd = new Button("Přidat");

    //tabulka s daty pro zobrazení a editaci zaměstnanců
    private TableView employeeList = new TableView();
    private TableColumn firstColumnEmployeeList = new TableColumn("Název");
    private TableColumn secondColumnEmployeeList = new TableColumn("Jméno");

    //tabulka pro zobrazení a editaci vazeb mezi zaměstnanci
    private TableView edgesList = new TableView();
    private TableColumn<Edge, String> firstColumnEdgesList = new TableColumn("Počátek");
    private TableColumn<Edge, String> secondColumnEdgesList = new TableColumn("Konec");

    private HBox addEdgeBox = new HBox();

    private Button edgeAdd = new Button("Přidat");

    private ComboBox sourceComboBox = new ComboBox();
    private ComboBox targetComboBox = new ComboBox();

    ComboBox<Item> com = new ComboBox<>();

    MenuBar menuBar = new MenuBar();

    //menu File
    Menu menuFile = new Menu("Soubor");

    //new file
    MenuItem newFile = new MenuItem();
    Label lblNew = new Label("Nový");

    //uložit soubor
    MenuItem fileSave = new MenuItem();
    Label lblSave = new Label("Uložit");

    //menu pro otevřít soubor
    MenuItem fileOpen = new MenuItem();
    Label lblOpen = new Label("Otevřít");

    //menu pro otevřít soubor
    MenuItem saveImage = new MenuItem();
    Label saveImagelabel = new Label("Export do PNG");

    //menu Edit
    Menu menuEdit = new Menu("Nápověda");

    //aplikace
    @Override
    public void start(Stage primaryStage) throws Exception {

        //nová instance dat
        Data data = new Data();
        data.setStage(primaryStage);
        data.newFile();

        Controler controler = new Controler();

        //nová instance grafického modelu
        Model model = new Model(data.getEmployee(), data.getEdges(), true);

        //podokno pro kreslení
        Pane canvas = new Pane();
        canvas.setPrefSize(width, height);

        canvas.getChildren().addAll(model.getData(), model.getLines());

        /**
         * Prvky levé části aplikace; respektive menu
         */
        left.setPrefWidth(width / 5.8);
        left.setStyle("-fx-background-color: #2B2A2F;");

        menu.setStyle("-fx-background-color: #2B2A2F;");
        menu.setPadding(new Insets(12, 10, 10, 10));

        //prvky s názvem aplikace
        head.setPrefWidth(width / 5.8);
        head.setMinHeight(110);
        head.setAlignment(Pos.CENTER);
        final ImageView selectedImage = new ImageView();
        Image image1 = new Image(Main.class.getResourceAsStream("map.png"));

        selectedImage.setImage(image1);

        head.getChildren().add(selectedImage);
        head.setStyle("-fx-background-color: #DC414B;");

        left.getChildren().add(head);

        jobField.setId("nazev");
        jobField.setPromptText("Pozice");
        jobField.setMaxWidth(firstColumnEmployeeList.getPrefWidth());
        nameField.setId("jmeno");
        nameField.setPromptText("Jméno");
        nameField.setMaxWidth(secondColumnEmployeeList.getPrefWidth());
        descriptionField.setMaxWidth(380);
        descriptionField.setMaxHeight(50);
        descriptionField.setId("popis");

        //in this event is added a new employee to data list
        employeeAdd.setOnAction(e -> {

            Item employee = null;
            String keyOfCell = "";

            //actual size of data list with employee, plus one means number
            //for new item
            int size = data.getDataListSize() + 1;

            employee = controler.addNewEmployee(size, jobField, nameField);
            keyOfCell = controler.addNewItemToTemp(employee);

            data.getEmployee().add(employee);
            data.getTemp().add(keyOfCell);
            jobField.clear();
            nameField.clear();

        });

        employeeButton.setPrefWidth(buttonWidth);
        employeeButton.setPrefHeight(buttonHeight);
        employeeButton.setPadding(new Insets(0, 0, 0, 0));
        employeeButton.setId("menuButton");
        employeeButton.setContentDisplay(ContentDisplay.TOP);
        menu.getChildren().add(employeeButton);

        addEmployeeBox.getChildren().addAll(
                jobField,
                nameField,
                employeeAdd
        );
        addEmployeeBox.setSpacing(3);

        employeeButton.setOnAction(e -> {
            right.getChildren().clear();
            right.getChildren().addAll(
                    employeeList,
                    addEmployeeBox
            );

        });

        sourceComboBox.setItems(data.getTemp());
        targetComboBox.setItems(data.getTemp());

        sourceComboBox.setPrefWidth(firstColumnEmployeeList.getPrefWidth());
        targetComboBox.setPrefWidth(firstColumnEmployeeList.getPrefWidth());

        //this method makes a new edge when the add button is pressed
        edgeAdd.setOnAction(e -> {

            //new edge, now is null
            Edge edge = null;

            //now the new edge is inicialized
            edge = controler.addNewEdge(data.getEmployee(), sourceComboBox, targetComboBox);
            data.getEdges().add(edge);
            canvas.getChildren().clear();
            model.update(data.getEmployee(), data.getEdges());
            canvas.getChildren().addAll(model.getData(), model.getLines());

            String i = "";
            i = com.getSelectionModel().getSelectedItem().getKey();
            System.out.println(i);
            i = com.getSelectionModel().getSelectedItem().getTitle();
            System.out.println(i);
        });

        addEdgeBox.getChildren().addAll(sourceComboBox, targetComboBox, edgeAdd);
        addEdgeBox.setSpacing(3);

        edgesButton.setPrefWidth(buttonWidth);
        edgesButton.setPrefHeight(buttonHeight);
        edgesButton.setId("menuButton");
        edgesButton.setContentDisplay(ContentDisplay.TOP);
        edgesButton.setOnAction(e -> {
            right.getChildren().clear();
            right.getChildren().addAll(edgesList, addEdgeBox, com);
        });
        menu.getChildren().add(edgesButton);

        /*
          Prvky centrální části aplikace
         */
        central.setPrefSize(width, height);
        central.setStyle("-fx-background-color: darkgray;");

        /*
          Pravá strana
         */
        right.setPrefWidth(width * 2 / 4);
        right.setStyle("-fx-background-color: gray;");
        right.setPadding(new Insets(10));

        employeeList.getColumns().addAll(firstColumnEmployeeList, secondColumnEmployeeList);
        employeeList.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        employeeList.setRowFactory(
                tableView -> {
                    final TableRow<Item> row = new TableRow<>();
                    final ContextMenu rowMenu = new ContextMenu();

                    MenuItem removeItem = new MenuItem("Smazat");
                    removeItem.setOnAction(event -> {

                        int option = 0;
                        Confirm confirm = new Confirm(new String[]{"Ano", "Ne"},
                                "Potvrzení", "Odebrání položky znamená nevratnou změnu.\n Přejete si pokračovat?",
                                400, 120);
                        confirm.showAndWait();
                        option = confirm.getOption();

                        if (option == 1) {

                            ObservableList<Item> sel = employeeList.getSelectionModel().getSelectedItems();
                            for (Item m : sel) {

                                List<String> dependencies = new ArrayList<>(model.getDependencies(m.getKey()));

                                data.removeItemFromEmployee(dependencies);
                                data.removeItemFromTemporaryList(dependencies);
                                data.removeItemFromEdgeSource(dependencies);
                                data.removeItemFromEdgeTarget(dependencies);

                            }
                        }

                        canvas.getChildren().clear();
                        model.update(data.getEmployee(), data.getEdges());
                        canvas.getChildren().addAll(model.getData(), model.getLines());
                    });
                    rowMenu.getItems().addAll(removeItem);

                    // only display context menu for non-null items:
                    row.contextMenuProperty().bind(
                            Bindings.when(Bindings.isNotNull(row.itemProperty()))
                                    .then(rowMenu)
                                    .otherwise((ContextMenu) null));
                    return row;
                });
        employeeList.setEditable(true);

        firstColumnEmployeeList.setCellValueFactory(
                new PropertyValueFactory<Item, String>("title")
        );
        firstColumnEmployeeList.setCellFactory(TextFieldTableCell.forTableColumn());

        firstColumnEmployeeList.setOnEditCommit(
                new EventHandler<TableColumn.CellEditEvent<Item, String>>() {
                    @Override
                    public void handle(TableColumn.CellEditEvent<Item, String> t) {
                        t.getTableView().getItems().get(
                                t.getTablePosition().getRow()).setTitle(t.getNewValue());
                    }
                }
        );

        secondColumnEmployeeList.setCellValueFactory(
                new PropertyValueFactory<Item, String>("name")
        );
        secondColumnEmployeeList.setCellFactory(TextFieldTableCell.forTableColumn());

        secondColumnEmployeeList.setOnEditCommit(
                new EventHandler<TableColumn.CellEditEvent<Item, String>>() {
                    @Override
                    public void handle(TableColumn.CellEditEvent<Item, String> t) {
                        t.getTableView().getItems().get(
                                t.getTablePosition().getRow()).setName(t.getNewValue());
                    }
                }
        );

        employeeList.setItems(data.getEmployee());

        edgesList.getColumns().addAll(firstColumnEdgesList, secondColumnEdgesList);
        edgesList.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);

        edgesList.setRowFactory(
                tableView -> {
                    final TableRow<Edge> row = new TableRow<>();
                    final ContextMenu rowMenu = new ContextMenu();

                    MenuItem removeItem = new MenuItem("Smazat");
                    removeItem.setOnAction(new EventHandler<ActionEvent>() {

                        @Override
                        public void handle(ActionEvent event) {
                            int option = 0;
                            Confirm confirm = new Confirm(new String[]{"Ano", "Ne"},
                                    "Potvrzení", "Odebrání položky znamená nevratnou změnu.\n Přejete si pokračovat?",
                                    400, 120);
                            confirm.showAndWait();
                            option = confirm.getOption();

                            if (option == 1) {

                                ObservableList<Edge> sel = edgesList.getSelectionModel().getSelectedItems();
                                for (Edge m : sel) {
                                    data.getEdges().remove(m);
                                }
                                canvas.getChildren().clear();
                                model.update(data.getEmployee(), data.getEdges());
                                canvas.getChildren().addAll(model.getData(), model.getLines());

                            }
                        }
                    });
                    rowMenu.getItems().addAll(removeItem);

                    // only display context menu for non-null items:
                    row.contextMenuProperty().bind(
                            Bindings.when(Bindings.isNotNull(row.itemProperty()))
                                    .then(rowMenu)
                                    .otherwise((ContextMenu) null));
                    return row;
                });

        edgesList.setEditable(true);

        sourceComboBox.getSelectionModel().selectFirst();
        targetComboBox.getSelectionModel().selectFirst();

        com.setItems(data.getEmployee());

        Callback<ListView<Item>, ListCell<Item>> factory = lv -> new ListCell<Item>() {
            @Override
            protected void updateItem(Item item, boolean empty) {
                super.updateItem(item, empty);
                setText(empty ? "" : item.getName());
            }
        };

        com.setCellFactory(factory);
        com.setButtonCell(factory.call(null));

        firstColumnEdgesList.setCellValueFactory(new PropertyValueFactory<>("source"));
        firstColumnEdgesList.setCellFactory(ComboBoxTableCell.forTableColumn(data.getTemp()));

        firstColumnEdgesList.setOnEditCommit(t -> {
            t.getTableView().getItems().get(
                    t.getTablePosition().getRow()).setSource(t.getNewValue());
            canvas.getChildren().clear();
            model.update(data.getEmployee(), data.getEdges());
            canvas.getChildren().addAll(model.getData(), model.getLines());
        });

        secondColumnEdgesList.setCellValueFactory(new PropertyValueFactory<>("target"));
        secondColumnEdgesList.setCellFactory(ComboBoxTableCell.forTableColumn(data.getTemp()));

        secondColumnEdgesList.setOnEditCommit(t -> {
            t.getTableView().getItems().get(
                    t.getTablePosition().getRow()).setTarget(t.getNewValue());
            canvas.getChildren().clear();
            model.update(data.getEmployee(), data.getEdges());
            canvas.getChildren().addAll(model.getData(), model.getLines());
        });

        edgesList.setItems(data.getEdges());

        menuFile.setId("barline");
        menuEdit.setId("barline");
        menuFile.getItems().addAll(newFile, fileSave, fileOpen, saveImage);
        menuBar.getMenus().addAll(menuFile, menuEdit);

        newFile.setId("bar");
        lblNew.setPrefWidth(150);
        lblNew.setWrapText(true);
        newFile.setGraphic(lblNew);
        newFile.setOnAction(e -> {

            int option = 0;
            Confirm confirm = new Confirm(new String[]{"Ano", "Ne"},
                    "Potvrzení", "Bez uložení dojde k nevratým změnám.\n Přejete si pokračovat?",
                    400, 120);
            confirm.showAndWait();
            option = confirm.getOption();

            if (option == 1) {
                canvas.getChildren().clear();
                data.getEmployee().clear();
                data.getEdges().clear();
                Read empty = new Read(primaryStage);
            }
        });

        fileSave.setId("bar");
        lblSave.setPrefWidth(150);
        lblSave.setWrapText(true);
        fileSave.setGraphic(lblSave);
        fileSave.setOnAction(e -> {
            Read save = new Read(primaryStage, data.getEmployee(), data.getEdges());
            save.writeToFile();
        });

        fileOpen.setId("bar");
        lblOpen.setPrefWidth(150);
        lblOpen.setWrapText(true);
        fileOpen.setGraphic(lblOpen);
        fileOpen.setOnAction(e -> {

            int option = 0;
            Confirm confirm = new Confirm(new String[]{"Ano", "Ne"},
                    "Potvrzení", "Bez uložení dojde k nevratým změnám.\n Přejete si pokračovat?",
                    400, 120);
            confirm.showAndWait();
            option = confirm.getOption();

            if (option == 1) {

                data.printData();
                Read open = new Read(primaryStage);
                open.readFromFile();

                if (open.checkOpenedFile() != null) {

                    data.getEmployee().clear();
                    data.getEdges().clear();
                    data.getEmployee().addAll(open.getEmployees());
                    data.getEdges().addAll(open.getEdges());
                    data.getTemp().clear();
                    data.fillTemp();
                    if (data.getTemp().size() >= 1) {
                        for (int i = 0; i < data.getTemp().size(); i++) {
                            if (i == 0) {
                                sourceComboBox.getSelectionModel().select(i);
                                targetComboBox.getSelectionModel().select(i + 1);
                            }
                        }
                    }
                    canvas.getChildren().clear();
                    model.update(data.getEmployee(), data.getEdges());
                    canvas.getChildren().addAll(model.getData(), model.getLines());

                }

            }
        });

        saveImage.setId("bar");
        lblOpen.setPrefWidth(150);
        lblOpen.setWrapText(true);
        saveImage.setGraphic(saveImagelabel);
        saveImage.setOnAction(e -> {

            FileChooser fileChooser = new FileChooser();

            //Set extension filter
            fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("png files (*.png)", "*.png"));

            //Prompt user to select a file
            File file = fileChooser.showSaveDialog(null);

            if(file != null){
                try {
                    //Pad the capture area
                    WritableImage writableImage = new WritableImage((int)canvas.getWidth(), (int)canvas.getHeight());
                    canvas.snapshot(null, writableImage);
                    RenderedImage renderedImage = SwingFXUtils.fromFXImage(writableImage, null);
                    //Write the snapshot to the chosen file
                    ImageIO.write(renderedImage, "png", file);
                } catch (IOException ex) { ex.printStackTrace(); }
            }
        });

        central.getChildren().add(canvas);
        right.getChildren().addAll(
                employeeList,
                addEmployeeBox
        );

        left.getChildren().add(menu);

        root.setTop(menuBar);
        root.setLeft(left);
        root.setCenter(central);
        root.setRight(right);

        Scene scene = new Scene(root);
        scene.getStylesheets().add(Main.class.getResource("style.css").toExternalForm());

        primaryStage.setMinHeight(height + 44);
        primaryStage.setMinWidth(width + (width * 2 / 4) + (width / 5.8));
        primaryStage.setTitle("Aplikace");
        primaryStage.setScene(scene);
        primaryStage.show();

        //metoda sloužící pro upozornění na zavření aplikace
        //dále upozorňuje uživatele na uložení jeho souborů
        primaryStage.setOnCloseRequest((WindowEvent we) -> {
            int option = 0;
            String[] buttons = {"Ano", "Ne"};
            Confirm window = new Confirm(buttons, "Ukončení aplikace",
                    "Opravdu si přejete ukončit aplikaci?\n Nezapoměňte si soubory uložit.",
                    400, 120);
            window.showAndWait();
            option = window.getOption();
            if (option == 1) {
                System.exit(-1);
            }
        });

    }

    //hlavní vlákno aplikace
    public static void main(String[] args) {

        launch(args);

    }
}
 
Nahoru Odpovědět
19.3.2018 11:56
Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:19.3.2018 14:40

no tě bůh:)
projíždím to jen baj očko, ale snad se mi to jen zdá....:)
Ty máš fakt všechno narvané v jedné metodě?

Nahoru Odpovědět
19.3.2018 14:40
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:19.3.2018 14:57

Jako ty atributy máš i krásně okomentované, a i hezky pojmenované, to je fajn;)
ale už když jsem jich projížděl, tak se mi zdálo, že některé věci by mohly být v jiné třídě atd.

Takže ty máš problém v té struktuře.
Zrovna teď jsem to vysvětloval jedné kočce, kterou učím programovat. Když používáš OOP (a Java je celá v OOP i když zrovna teď jsi dokázal programovat struktorovaně:) ), tak můžeš využít tu největší výhodu, díky které se programování stalo nejen prací, ale i příjemným koníčkem:)

Dám ti to jako příklad nějaké stavebnice.
Vem si třeba Lečo (jmenuje se to lego, ale nemůžu jim dělat reklamu, tak používám substituci:) ). Chceš z leča postavit krásný dům a v něm zařízení.
Tak přece taky nezačneš tak, že nejdřív postavíš z leča základ jedné místnosti, do toho uděláš lečo nábytek, ten rovnou umístíš a až to bude sedět, tak uděláš z leča další místnosti a tak to budeš navalovat.
Asi bys nejdřív udělal z leča zdi a prostory.
Pak si uděláš plochu leča, na kterou to všechno umístíš (a je jedno kam - prostě někam)
Pak si uděláš z leča skříň, židle, stůl, postel, bazén, sprchu a kdo ví co ještě.
A potom budeš dávat jednotlivé kousky k sobě. A musí ti pochopitelně sedět. Když vyhradíš, že do jistého místa leča se vleze kostička leča, která má 4 puntíky, tak nebudeš dělat postel, která má pět puntíků.

No a to samé je programování.
Prostě si projekt rozvrhneš do co nejmenších logických oddílů.
Základní třída bude dávat tyto oddíly dohromady (klidně to může být ta populární třída Main)
A ostatní třídy budou řešit ty dotyčné vlastnosti.

Ono se to nezdá, ale třeba i na tak triviální projekt, jako je ten tvůj, bys mohl použít nějaký obecný balíček tools, do kterého si dáš pomocné třídy (já třeba mám v této třídě:
Delay - který mi obecně řeší pauzu vlákna, abych furt nemusel vypisovat celý try catch, ale zavolal si jen jeden řádek a vyjímku zachycuju v této třídě.
SwingColors a FXColors - tady logicky řeším barvičky. Takže když třeba potřebuju vygenerovat náhodnou barvu, tak mám na to už tool.
TFile - (jako tool file, protože třída File už existuje, tak aby se mi to nepletlo). Tady řeším obecně načítání, vytváření a přepisování souborů
TImage - práce s obrázky
Database - obecné connectiony, takže nemusím řešit nějaký statement, nastavování connectiony atd.atd.
Regex - abych rychle pracoval s regulárními výrazy
Log - který jsem si sám nastyloval
Mathic - tady si využívám různé matematické operace (od nalezení nejmenšího společného jmenovatele, až po pythagorovu větu)
Prostě toto jsou třeba pomocné třídy, které můžu v podstatě kdekoliv využít.
Ty bys mohl mít třeba třídy: Frame (pro vykreslení FXka a jeho komponent), Image (kde bys řešil ty obrázky, co tam máš), Employee (která by řešila všechno ohledně zaměstnanců) atd. atd. (přiznám se, že to projíždím jen tak, rychlo sem, rychlo tam. Takže to je fakt jen příklad.

Nicméně rvát všechno do jedné metody a hlavně do jedné třídy není to nejideálnější:) to se pak nediv, že se ztratíš:)
Jsi na ITnetworku a já tě velmi rád, jako každého odkazuju na OOP sekci. Je tu moc krásně udělaná a naučí tě to dívat se na programování i z tohoto pohledu (OOP). Nejen sekat příkazy jeden po druhém.
Jediné, co tady všem vyčítám, je porušování zapouzdření:( to je moje jediná výtka vůči těmto stránkám. Ale jinak jsou naprosto dokonalé a ty tutoriály tě hezky procvičí;) Neboj se za tyto stránky platit v podobě článků. Není to rozhodně drahé a věř, že se ti to vyplatí;)

Nahoru Odpovědět
19.3.2018 14:57
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Avatar
meny.web
Člen
Avatar
meny.web:20.3.2018 7:27

Mockrát děkuji, jdu se do toho pustit :)

 
Nahoru Odpovědět
20.3.2018 7:27
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 10.