Projekt „Java Anwendung markdown2flashcards“ Teil 2 – Grafische Oberfläche mit JavaFX bauen
erweiterte pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.linformatik</groupId>
<artifactId>markdown2flashcards</artifactId>
<version>1.0-SNAPSHOT</version>
<name>markdown2flashcards</name>
<url>http://linformatik.de</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--GUI für Karteikasten -->
<!-- JavaFX Dependencies -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>19</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>19</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Maven Compiler Plugin für Java Version 21 -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
<!-- JFX Plugin für GUI-->
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.5</version>
<configuration>
<mainClass>de.linformatik.App</mainClass> <!-- Die Klasse mit der main-Methode -->
</configuration>
<executions>
<execution>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- JAR Plugin für Manifest -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifest>
<!-- Hauptklasse im Manifest definieren -->
<mainClass>de.linformatik.App</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<!-- Maven Clean Plugin für das Bereinigen des Projekts -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- Maven Resources Plugin für das Kopieren von Ressourcen -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<!-- Maven Surefire Plugin für Tests -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<!-- Maven Install Plugin für das Installieren der Artefakte -->
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<!-- Maven Deploy Plugin für das Bereitstellen der Artefakte -->
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- Maven Site Plugin für die Erstellung von Sites und Berichten -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<!-- Maven Project Info Reports Plugin -->
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</build>
</project>
Erweiterte App.java
package de.linformatik;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import java.io.File;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
public class App extends Application {
private int currentCardIndex = 0;
private List<Flashcard> flashcards = new ArrayList<>();
private Map<String, int[]> questionStatistics = new HashMap<>(); // Speichert richtige und falsche Antworten pro Frage
private Scene flashcardScene; // Speichern der Flashcard-Szene
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
// Flashcards laden
String flashcardsPath = loadFlashcardsPathFromConfig();
if (flashcardsPath != null) {
flashcards = loadFlashcards(flashcardsPath);
} else {
System.out.println("Kein Flashcards-Pfad gefunden.");
}
// Textfelder für Frage und Antwort
Text questionText = new Text();
Text answerText = new Text();
// Antwort anfangs unsichtbar
answerText.setVisible(false);
// Schriftgrößen für Frage, Antwort und Statistik setzen
questionText.setStyle("-fx-font-size: 18px;");
answerText.setStyle("-fx-font-size: 16px;");
// Button zum Weiterblättern der Karten
Button nextButton = new Button("Nächste Karte");
nextButton.setStyle("-fx-font-size: 14px;");
nextButton.setOnAction(event -> showNextCard(questionText, answerText));
// Button zum Zurückblättern der Karten
Button prevButton = new Button("Vorherige Karte");
prevButton.setStyle("-fx-font-size: 14px;");
prevButton.setOnAction(event -> showPreviousCard(questionText, answerText));
// Button zum Anzeigen der Antwort
Button showAnswerButton = new Button("Antwort anzeigen");
showAnswerButton.setStyle("-fx-font-size: 14px;");
showAnswerButton.setOnAction(event -> {
answerText.setVisible(true); // Antwort sichtbar machen
});
// Button für "Habs gewusst"
Button correctButton = new Button("Habs gewusst");
correctButton.setStyle("-fx-font-size: 14px;");
correctButton.setOnAction(event -> markAnswerCorrect(questionText));
// Button für "Habs nicht gewusst"
Button incorrectButton = new Button("Habs nicht gewusst");
incorrectButton.setStyle("-fx-font-size: 14px;");
incorrectButton.setOnAction(event -> markAnswerIncorrect(questionText));
// Buttons nebeneinander anordnen (mit HBox)
HBox answerButtons = new HBox(10, correctButton, incorrectButton);
answerButtons.setStyle("-fx-alignment: center;");
// Buttons für Navigieren und Statistik nebeneinander anordnen
HBox navButtons = new HBox(10, prevButton, nextButton, showAnswerButton);
navButtons.setStyle("-fx-alignment: center;");
// Button für Statistik anzeigen
Button statsButton = new Button("Statistik anzeigen");
statsButton.setStyle("-fx-font-size: 14px;");
statsButton.setOnAction(event -> showStatistics(primaryStage));
// Layout der GUI
VBox layout = new VBox(10, questionText, navButtons, answerText, answerButtons, statsButton);
layout.setStyle("-fx-padding: 20;");
// Anfangswerte der ersten Karte
updateCard(questionText, answerText);
// Erstelle und zeige die Szene
flashcardScene = new Scene(layout, 1400, 1000); // Größeres Fenster
primaryStage.setTitle("Flashcards");
primaryStage.setScene(flashcardScene);
primaryStage.show();
}
// Liest den Pfad der Flashcards aus der config.xml-Datei
private String loadFlashcardsPathFromConfig() {
try {
// XML-Dokument einlesen
File configFile = new File("config.xml");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(configFile);
// Den Tag <flashcardsPath> auslesen
NodeList nodeList = document.getElementsByTagName("flashcardsPath");
if (nodeList.getLength() > 0) {
Node node = nodeList.item(0);
return node.getTextContent().trim(); // Rückgabe des Pfads
}
} catch (Exception e) {
e.printStackTrace();
}
return null; // Falls keine Datei oder Fehler beim Lesen
}
// Lädt die Flashcards aus der angegebenen Datei
private List<Flashcard> loadFlashcards(String filename) {
List<Flashcard> flashcards = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
String line;
String currentQuestion = null;
StringBuilder currentAnswer = new StringBuilder(); // Antwort kann mehrere Zeilen haben
while ((line = br.readLine()) != null) {
line = line.trim(); // Entfernt unnötige Leerzeichen am Anfang und Ende der Zeile
// Wenn eine Frage erkannt wird
if (line.startsWith("## Frage")) {
// Falls bereits eine Frage und Antwort existieren, speichern wir sie
if (currentQuestion != null && currentAnswer.length() > 0) {
flashcards.add(new Flashcard(currentQuestion, currentAnswer.toString()));
}
// Extrahieren der Frage, nachdem "## Frage <Nummer>:" entfernt wurde
currentQuestion = line.substring(9).trim();
currentAnswer = new StringBuilder(); // Setze Antwort zurück
}
// Wenn eine Antwort erkannt wird
else if (line.startsWith("**Antwort:**")) {
// Füge die Antwortzeile zur bestehenden Antwort hinzu
currentAnswer.append(line.substring(12).trim());
currentAnswer.append("\n"); // Antwort auf neue Zeile setzen
}
}
// Letzte Frage und Antwort hinzufügen, wenn vorhanden
if (currentQuestion != null && currentAnswer.length() > 0) {
flashcards.add(new Flashcard(currentQuestion, currentAnswer.toString()));
}
} catch (IOException e) {
e.printStackTrace();
}
return flashcards;
}
// Aktualisiert die Anzeige der aktuellen Karte
private void updateCard(Text questionText, Text answerText) {
if (flashcards.size() > 0) {
Flashcard currentCard = flashcards.get(currentCardIndex);
questionText.setText("Frage: " + currentCard.question);
answerText.setText("Antwort: " + currentCard.answer);
answerText.setVisible(false); // Antwort anfangs unsichtbar
} else {
questionText.setText("Keine Flashcards gefunden.");
answerText.setText("");
}
}
// Zeigt die nächste Flashcard an
private void showNextCard(Text questionText, Text answerText) {
if (currentCardIndex < flashcards.size() - 1) {
currentCardIndex++;
updateCard(questionText, answerText);
}
}
// Zeigt die vorherige Flashcard an
private void showPreviousCard(Text questionText, Text answerText) {
if (currentCardIndex > 0) {
currentCardIndex--;
updateCard(questionText, answerText);
}
}
// Markiert eine Antwort als richtig und aktualisiert die Statistik für diese Frage
private void markAnswerCorrect(Text questionText) {
Flashcard currentCard = flashcards.get(currentCardIndex);
String question = currentCard.question;
questionStatistics.putIfAbsent(question, new int[]{0, 0}); // Wenn die Frage noch nicht existiert, initialisiere sie
questionStatistics.get(question)[0]++; // Richtige Antwort erhöhen
}
// Markiert eine Antwort als falsch und aktualisiert die Statistik für diese Frage
private void markAnswerIncorrect(Text questionText) {
Flashcard currentCard = flashcards.get(currentCardIndex);
String question = currentCard.question;
questionStatistics.putIfAbsent(question, new int[]{0, 0}); // Wenn die Frage noch nicht existiert, initialisiere sie
questionStatistics.get(question)[1]++; // Falsche Antwort erhöhen
}
// Zeigt die Statistik an
private void showStatistics(Stage primaryStage) {
VBox statisticsLayout = new VBox(10);
statisticsLayout.setStyle("-fx-padding: 20;");
// Durchlaufen der Fragen und deren Statistiken
for (Map.Entry<String, int[]> entry : questionStatistics.entrySet()) {
String question = entry.getKey();
int[] stats = entry.getValue();
Text statText = new Text(question + " - Richtige: " + stats[0] + ", Falsche: " + stats[1]);
statisticsLayout.getChildren().add(statText);
}
// Button zum Zurückkehren zur Flashcard-Seite
Button backButton = new Button("Zurück");
backButton.setOnAction(event -> primaryStage.setScene(flashcardScene)); // Zurück zur Flashcards-Szene
statisticsLayout.getChildren().add(backButton);
// Erstelle und zeige die Statistik-Seite
Scene statsScene = new Scene(statisticsLayout, 1400, 1000);
primaryStage.setScene(statsScene);
}
}
Flashcard.java
package de.linformatik;
public class Flashcard {
String question;
String answer;
Flashcard(String question, String answer) {
this.question = question;
this.answer = answer;
}
void show() {
System.out.println("Frage: " + this.question);
System.out.println("Antwort: " + this.answer);
System.out.println();
}
}
Konfiguration für den Pfad der Markdown-Datei config.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<flashcardsPath>J:\Meine Ablage\develop\markdown2flashcards\src\main\java\de\linformatik\flashcards.md</flashcardsPath>
</config>
markdown2flashcards.bat um die JAR Datei auszuführen
@echo off
echo Starte JavaFX-Anwendung...
mvn javafx:run -X > output.log 2>&1
echo Anwendung beendet.
pause
Markdowndatei mit den Fragen und Antworten
flashcards.md
## Frage: Was ist der Unterschied zwischen Ganzzahl- und Gleitkomma-Arithmetik?
**Antwort:** Ganzzahl-Arithmetik bezieht sich auf die Berechnungen mit ganzen Zahlen, während Gleitkomma-Arithmetik für reelle Zahlen verwendet wird.
## Frage: Was ist der Modulo-Operator?
**Antwort:** Der Modulo-Operator `%` gibt den Rest einer Division zurück.
## Frage: Was ist der Unterschied zwischen Addition und Multiplikation?
**Antwort:** Addition ist das Hinzufügen von Zahlen, Multiplikation ist die wiederholte Addition.
## Frage: Was ist eine Primzahl?
**Antwort:** Eine Primzahl ist eine natürliche Zahl größer als 1, die nur durch 1 und sich selbst teilbar ist.
## Frage: Was ist eine Potenz in der Mathematik?
**Antwort:** Eine Potenz besteht aus einer Basis und einem Exponenten und beschreibt eine wiederholte Multiplikation der Basis.
## Frage: Was ist die Binärdarstellung einer Zahl?
**Antwort:** Die Binärdarstellung ist die Darstellung einer Zahl im Zahlensystem zur Basis 2, bestehend nur aus 0 und 1.
## Frage: Was ist eine Fakultät?
**Antwort:** Die Fakultät einer Zahl \( n \) (geschrieben als \( n! \)) ist das Produkt aller natürlichen Zahlen von 1 bis \( n \).
## Frage: Was ist der Unterschied zwischen einem Bruch und einer Dezimalzahl?
**Antwort:** Ein Bruch stellt ein Verhältnis zwischen zwei Zahlen dar, während eine Dezimalzahl eine Darstellung mit einer Kommastelle ist.
## Frage: Was ist die Kommutativität von Addition und Multiplikation?
**Antwort:** Kommutativität bedeutet, dass die Reihenfolge der Operanden das Ergebnis nicht verändert (z. B. \( a + b = b + a \) oder \( a \times b = b \times a \)).
## Frage: Was ist der Unterschied zwischen einer linearen und einer exponentiellen Funktion?
**Antwort:** Eine lineare Funktion wächst konstant, während eine exponentielle Funktion exponentiell wächst (z. B. Verdopplung bei jedem Schritt).
## Frage: Was ist die Basis eines Zahlensystems?
**Antwort:** Die Basis eines Zahlensystems gibt an, mit wie vielen Ziffern Zahlen dargestellt werden (z. B. Basis 10 für Dezimalsystem, Basis 2 für Binärsystem).
## Frage: Was ist eine Logarithmusfunktion?
**Antwort:** Die Logarithmusfunktion ist die Umkehrfunktion der Exponentialfunktion und gibt an, zu welchem Exponenten eine Basis potenziert werden muss, um eine Zahl zu erhalten.
## Frage: Was ist der Unterschied zwischen einem Skalar und einem Vektor?
**Antwort:** Ein Skalar ist eine einzelne Zahl (z. B. Temperatur), während ein Vektor eine Größe mit Richtung und Betrag ist (z. B. Geschwindigkeit).
Printscreen der Applikation
