|
楼主 |
发表于 2023-12-31 22:00:29
|
显示全部楼层
@FishC
package com.example.javafxmw;
import gaijin3.*;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Pair;
import java.io.IOException;
import java.sql.*;
import java.util.Optional;
public class HelloApplication extends Application
{
// 定义全局变量
//定义一个TextArea控件作为全局变量
TextArea infoTextArea = new TextArea();
Button viewAccuracyButton = new Button("View Accuracy");
FlowPane flowPane = new FlowPane();
ScrollPane scrollPane = new ScrollPane();// 创建一个滚动面板对象
private int maxID = 0;
@Override
public void start(Stage stage) throws IOException {
MenuItem generateMenuItem = new MenuItem("Generate");
// 事件处理器代码
generateMenuItem.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
generateEquation();
}
});
MenuItem saveMenuItem = new MenuItem("Save");
saveMenuItem.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
saveEquations();
connectDatabase();
}
});
MenuItem readMenuItem = new MenuItem("Read");
readMenuItem.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
flowPane.getChildren().clear(); // 清空当前GUI界面
showAlert("习题已读取");
displayAllEquationsFromDatabase();
}
});
MenuItem checkMenuItem = new MenuItem("Check");
checkMenuItem.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
// 检查习题
checkEquations();
}
});
// View Accuracy按钮的点击事件监听器
viewAccuracyButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
// 显示饼状图
showAccuracyChart();
}
});
MenuItem deleteMenuItem = new MenuItem("Delete by serial number");
// Delete by serial number按钮的点击事件监听器,部分删除算数式
deleteMenuItem.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
deleteEquations();
}
});
MenuItem deleteMenuItem1 = new MenuItem("Delete all");
// Delete all按钮的点击事件监听器,全部删除
deleteMenuItem1.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
// 删除数据库中的所有算术式
deleteAllEquations();
// 清空界面上的所有算术式
flowPane.getChildren().clear();
showAlert("所有算术式删除成功!");
}
});
MenuItem mistakesEquationsCollection = new MenuItem("Redo Mistakes Equations");
Menu fileMenu = new Menu("File");
Menu saveMenu = new Menu("SaveEquation");
Menu readMenu = new Menu("ReadEquation");
Menu checkMenu = new Menu("CheckEquation");
Menu deleteMenu = new Menu("Delete");
Menu mistakesEquationsCollectionMenu = new Menu("MistakesEquationsCollection");
fileMenu.getItems().addAll(generateMenuItem);
saveMenu.getItems().add(saveMenuItem);
readMenu.getItems().add(readMenuItem);
checkMenu.getItems().add(checkMenuItem);
deleteMenu.getItems().addAll(deleteMenuItem,deleteMenuItem1);
mistakesEquationsCollectionMenu.getItems().add(mistakesEquationsCollection);
MenuBar menuBar = new MenuBar();
menuBar.getMenus().addAll(fileMenu);
menuBar.getMenus().add(saveMenu);
menuBar.getMenus().add(readMenu);
menuBar.getMenus().add(checkMenu);
menuBar.getMenus().add(deleteMenu);
menuBar.getMenus().add(mistakesEquationsCollectionMenu);
flowPane.setHgap(50);
flowPane.setVgap(30);
flowPane.setPadding(new Insets(20));
flowPane.setPrefSize(800, 560); // 设置 FlowPane 的大小
BorderPane borderPane = new BorderPane();
borderPane.setTop(menuBar);
borderPane.setCenter(flowPane);
//为了解决题目生成或多flowpane没有拖动条无法显示的问题
// 将FlowPane放在一个ScrollPane中
scrollPane.setContent(flowPane);// 设置滚动面板的内容为 flowPane,即将 flowPane 放入滚动面板中
scrollPane.setFitToHeight(true);// 可以让滚动面板的高度自适应内容的高度
borderPane.setCenter(scrollPane); // 将ScrollPane放置在中心位置
HBox bottomBox = new HBox(10, viewAccuracyButton);
HBox.setMargin(viewAccuracyButton, new Insets(50, 0, 20, 140));
borderPane.setBottom(bottomBox);
Scene scene = new Scene(borderPane, 800, 640);
stage.setTitle("口算习题薄");
stage.setScene(scene);
stage.show();
infoTextArea.setPrefSize(400, 110);
//将TextArea控件其设置为只读
infoTextArea.setEditable(false);
HBox showbottomBox = new HBox(140, viewAccuracyButton, infoTextArea);
HBox.setMargin(infoTextArea, new Insets(20, 0, 20, 0));
borderPane.setBottom(showbottomBox);
}
public static void main(String[] args) {
launch();
}
//为了使generate的事件处理器更美观特意拉出了这个类
public EquationCollection generateEquation() {
// 创建一个警示对话框
TextInputDialog dialog = new TextInputDialog("5");
dialog.setTitle("请输入算式数量");
dialog.setHeaderText(null);
dialog.setContentText("算式数量:");
// 显示对话框,并获取用户输入的算式数量
Optional<String> result = dialog.showAndWait();
if (result.isPresent())
{
int n = Integer.parseInt(result.get());
// 生成算式
EquationCollection equationCollection = generateEquations(n);
// 清空之前的内容
flowPane.getChildren().clear();
// 遍历算式集合并显示在GUI中
for (IEquation equation : equationCollection)
{
maxID++; // id自增
Label idLabel = new Label(String.valueOf(maxID)); // 获取当前最大ID,并将新的算式ID设为其加1
Label equationLabel = new Label(equation.toString());
TextField answerField = new TextField(); // 创建一个新的文本框
HBox equationBox = new HBox(10, idLabel, new Label("、"), equationLabel, answerField); // 将算式ID标签、顿号、算式标签和文本框放入水平布局
flowPane.getChildren().add(equationBox); // 将水平布局添加到 flowPane 中
}
// 连接数据库
connectDatabase();
}
return null;
}
// 生成算式的方法
public EquationCollection generateEquations(int n) {
EquationCollection equationCollection = new EquationCollection();
EquationCheckerOfRange equationChecker = new EquationCheckerOfRange(0, 100);
equationCollection.generate(n, equationChecker);
return equationCollection;
}
// 连接数据库的方法
public void connectDatabase() {
Connection conn = null;
Statement stmt = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/rjgzdatabase?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false",
"root", "Zqy173590939");
DatabaseMetaData dbMeta = conn.getMetaData();
System.out.println("当前用户:" + dbMeta.getUserName());
System.out.println("驱动版本:" + dbMeta.getDriverVersion());
System.out.println("MySQL版本:" + dbMeta.getDatabaseProductVersion());
stmt = conn.createStatement();
// 查询最大ID值
ResultSet rs = stmt.executeQuery("SELECT MAX(id) FROM equationtable");
if (rs.next()) {
maxID = rs.getInt(1);
}
} catch (SQLException se) {
se.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (stmt != null)
stmt.close();
if (conn != null)
conn.close();
} catch (SQLException se) {
se.printStackTrace();
}
}
System.out.println("Goodbye!");
}
private void saveEquations() {
Connection conn = null;
PreparedStatement pstmt = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/rjgzdatabase?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false",
"root", "Zqy173590939");
for (Node node : flowPane.getChildren()) {
if (node instanceof HBox) {
HBox equationBox = (HBox) node;
Label idLabel = (Label) equationBox.getChildren().get(0); // 获取算式ID标签
Label equationLabel = (Label) equationBox.getChildren().get(2);
TextField answerField = (TextField) equationBox.getChildren().get(3);
String equation = equationLabel.getText();
String answerText = answerField.getText();
// 检查答案字段是否为空
int answer = 0;
if (!answerText.isEmpty()) {
answer = Integer.parseInt(answerText);
}
// 计算正确结果
int result = calculateResult(equation);
// 保存到数据库
String sql = "INSERT INTO equationtable (id, Operand1, Operator, Operand2, Result) VALUES (?, ?, ?, ?, ?)";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, Integer.parseInt(idLabel.getText())); // 将算式ID插入到数据库中
pstmt.setInt(2, getOperand1(equation));
pstmt.setString(3, getOperator(equation));
pstmt.setInt(4, getOperand2(equation));
pstmt.setInt(5, result);
pstmt.executeUpdate();
}
}
System.out.println("算式保存成功!!!");
showAlert("保存成功!");
} catch (SQLException se) {
se.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (pstmt != null)
pstmt.close();
if (conn != null)
conn.close();
} catch (SQLException se) {
se.printStackTrace();
}
}
System.out.println("Goodbye!");
}
private int calculateResult(String equation) {
String[] parts = equation.split(" ");
int operand1 = Integer.parseInt(parts[0]);
int operand2 = Integer.parseInt(parts[2]);
String operator = parts[1];
int result = 0;
switch (operator) {
case "+":
result = operand1 + operand2;
break;
case "-":
result = operand1 - operand2;
break;
case "*": //此处预设乘法和除法便于以后相应的添加功能
result = operand1 * operand2;
break;
case "/":
result = operand1 / operand2;
break;
}
return result;
}
private int getOperand1(String equation) {
String[] parts = equation.split(" ");
return Integer.parseInt(parts[0]);
}
private String getOperator(String equation) {
String[] parts = equation.split(" ");
return parts[1];
}
private int getOperand2(String equation) {
String[] parts = equation.split(" ");
return Integer.parseInt(parts[2]);
}
private void showAlert(String message) {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("提示信息");
alert.setHeaderText(null);
alert.setContentText(message);
alert.showAndWait();
}
private void displayAllEquationsFromDatabase() {
Connection conn = null;
Statement stmt = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/rjgzdatabase?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false",
"root", "Zqy173590939");
stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM equationtable");
if (!rs.next()) {
showAlert("当前题库为空");
} else {
do {
int id = rs.getInt("id");
String equation = id + "、 " + rs.getInt("Operand1") + " " + rs.getString("Operator") + " " +
rs.getInt("Operand2") + " = " + rs.getInt("Result");
Label equationLabel = new Label(equation);
flowPane.getChildren().add(equationLabel);
} while (rs.next());
}
} catch (SQLException se) {
se.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (stmt != null)
stmt.close();
if (conn != null)
conn.close();
} catch (SQLException se) {
se.printStackTrace();
}
}
System.out.println("Goodbye!");
}
private void checkEquations() {
int totalEquations = 0; // 总题数
int correctAnswers = 0; // 答对题数
int incorrectAnswers = 0; // 答错题数
for (Node node : flowPane.getChildren()) {
if (node instanceof HBox) {
HBox equationBox = (HBox) node;
Label equationLabel = (Label) equationBox.getChildren().get(2);
TextField answerField = (TextField) equationBox.getChildren().get(3);
String equation = equationLabel.getText();
String answerText = answerField.getText();
// 检查答案字段是否为空
if (!answerText.isEmpty()) {
totalEquations++;
int userAnswer = Integer.parseInt(answerText);
int result = calculateResult(equation);
if (userAnswer == result) { // 答案正确
equationBox.setStyle("-fx-background-color: #99ff99"); // 设置背景色为绿色
correctAnswers++;
} else { // 答案错误
equationBox.setStyle("-fx-background-color: #ff9999"); // 设置背景色为红色
incorrectAnswers++;
}
}
}
}
double correctPercentage = (double) correctAnswers / totalEquations * 100;
double incorrectPercentage = (double) incorrectAnswers / totalEquations * 100;
infoTextArea.setText("统计信息:\n" +
"总题数:" + totalEquations + "\n" +
"正确题数:" + correctAnswers + "\n" +
"错误题数:" + incorrectAnswers + "\n" +
"正确率:" + String.format("%.2f", correctPercentage) + "%\n" +
"错误率:" + String.format("%.2f", incorrectPercentage) + "%");
}
private void showAccuracyChart() {
int totalEquations = 0; // 总题数
int correctAnswers = 0; // 答对题数
for (Node node : flowPane.getChildren()) {
if (node instanceof HBox) {
HBox equationBox = (HBox) node;
Label equationLabel = (Label) equationBox.getChildren().get(2);
TextField answerField = (TextField) equationBox.getChildren().get(3);
String equation = equationLabel.getText();
String answerText = answerField.getText();
// 检查答案字段是否为空
if (!answerText.isEmpty()) {
totalEquations++;
int userAnswer = Integer.parseInt(answerText);
int result = calculateResult(equation);
if (userAnswer == result) { // 答案正确
correctAnswers++;
}
}
}
}
// 创建一个饼状图数据
double correctPercentage = (double) correctAnswers / totalEquations * 100;
double incorrectPercentage = 100 - correctPercentage;
infoTextArea.setText("统计信息:\n" +
"总题数:" + totalEquations + "\n" +
"正确题数:" + correctAnswers + "\n" +
"错误题数:" + (totalEquations - correctAnswers) + "\n" +
"正确率:" + String.format("%.2f", correctPercentage) + "%\n" +
"错误率:" + String.format("%.2f", incorrectPercentage) + "%");
// 创建饼状图并显示
PieChart chart = new PieChart();
ObservableList<PieChart.Data> pieChartData = FXCollections.observableArrayList(
new PieChart.Data("Correct", correctAnswers),
new PieChart.Data("Incorrect", totalEquations - correctAnswers)
);
chart.setData(pieChartData);
chart.setTitle("Accuracy Chart");
// 创建一个新的Stage来显示饼状图
Stage chartStage = new Stage();
chartStage.setScene(new Scene(chart, 600, 400));
chartStage.show();
}
//为了使事件处理器的代码更简洁,刻意独立出了这个方法,没有具体意义
private void deleteEquations() {
// 读取数据库中所有题目展示到界面中
flowPane.getChildren().clear();
displayAllEquationsFromDatabase();
// 创建一个警示对话框
Dialog<Pair<String, String>> dialog = new Dialog<>();
dialog.setTitle("删除算术式范围");
dialog.setHeaderText(null);
// 设置对话框的按钮
ButtonType deleteButtonType = new ButtonType("删除", ButtonBar.ButtonData.OK_DONE);
dialog.getDialogPane().getButtonTypes().addAll(deleteButtonType, ButtonType.CANCEL);
// 创建并设置对话框的内容
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(20, 150, 10, 10));
TextField startIDTextField = new TextField();
TextField endIDTextField = new TextField();
grid.add(new Label("起始ID:"), 0, 0);
grid.add(startIDTextField, 1, 0);
grid.add(new Label("结束ID:"), 2, 0);
grid.add(endIDTextField, 3, 0);
dialog.getDialogPane().setContent(grid);
// 点击删除按钮时的逻辑
dialog.setResultConverter(dialogButton -> {
if (dialogButton == deleteButtonType) {
int startID = Integer.parseInt(startIDTextField.getText());
int endID = Integer.parseInt(endIDTextField.getText());
// 删除数据库中匹配的算术式
deleteEquationsByRange(startID, endID);
// 清空界面上的匹配的算术式
for (Node node : flowPane.getChildren()) {
if (node instanceof HBox) {
HBox equationBox = (HBox) node;
Label idLabel = (Label) equationBox.getChildren().get(0); // 获取算式ID标签
int id = Integer.parseInt(idLabel.getText());
if (id >= startID && id <= endID) {
flowPane.getChildren().remove(equationBox);
}
}
}
showAlert("算术式删除成功!");
}
return null;
});
dialog.showAndWait();
}
private void deleteEquationsByRange(int startID, int endID) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/rjgzdatabase?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false",
"root", "Zqy173590939");
String sql = "DELETE FROM equationtable WHERE id >= ? AND id <= ?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, startID);
pstmt.setInt(2, endID);
pstmt.executeUpdate();
} catch (SQLException se) {
se.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (pstmt != null)
pstmt.close();
if (conn != null)
conn.close();
} catch (SQLException se) {
se.printStackTrace();
}
}
System.out.println("Goodbye!");
}
// 删除数据库中的所有算术式
private void deleteAllEquations() {
Connection conn = null;
Statement stmt = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/rjgzdatabase?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false",
"root", "Zqy173590939");
stmt = conn.createStatement();
String sql = "DELETE FROM equationtable";
stmt.executeUpdate(sql);
} catch (SQLException se) {
se.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (stmt != null)
stmt.close();
if (conn != null)
conn.close();
} catch (SQLException se) {
se.printStackTrace();
}
}
System.out.println("Goodbye!");
}
}
现在数据库中新增了一个错题集表mistakesequationtable,表的结构跟equationtable完全一样,现在需要增加错题集功能,已经创建了错题集菜单栏和重做错题按钮,现在为Redo Mistakes Equations添加事件处理器。
要求修改checkequation的逻辑,使其链接数据库,点击check后,被判定为错题的题目直接保存在错题集表mistakesequationtable中,并且获取mistakesequationtable表中ID最大值,如果表中为空则从1开始编号,如果错题集中有数据则从获取的最大值+1开始标号。获取ID最大值可以参考已存在的方法。
点击Redo Mistakes Equations后,弹出警示对话框,提示用户输入本次想要重做多少道错题,确认后则从错题集中随机抽取出相应的数目显示在界面中,要求这部分区别于读取习题,错误的题目用户可以重做,要有输入框,重做后也可检查对错和查看正确率。
给出需要添加或修改的部分。 |
|