为数独求解器构建 GUI(完整的 ASCII 示例)

Building a GUI for a Sudoku Solver (Complete with ASCII Example)(为数独求解器构建 GUI(完整的 ASCII 示例))

本文介绍了为数独求解器构建 GUI(完整的 ASCII 示例)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

.

概览,示例

大家好,

我创建了一个基本的数独求解器,可以相当快地解决大多数问题.我还有很多工作要做,才能让它解决最困难的问题,但我想先尝试实现一个基本的 JFrame GUI.

我过去曾使用过互联网小程序,但以前从未使用过 JFrames.

我想创建类似于下图的东西(对于初学者):

-----------------------------------------------------------------------------------------------!数独求解器 1.0 - [] X !-------------------------------------------------------------------------------------------------------------!_____________ _____________ _____________ _____________ _____________ _____________ !!|_ _ _ |_ _ _ |_ _ _ ||_ _ _ |_ _ _ |_ _ _ |!!|!5!!_!!_!|!_!!_!!_!|!6!!_!!1!||!5!!7!!2!|!4!!9!!3!|!6!!8!!1!|!!|_ _ _ |_ _ _ |_ _ _ ||_ _ _ |_ _ _ |_ _ _ |!!|!6!!_!!_!|!_!!_!!2!|!4!!_!!_!||!6!!1!!3!|!8!!5!!2!|!4!!7!!9!|!!|_ _ _ |_ _ _ |_ _ _ ||_ _ _ |_ _ _ |_ _ _ |!!|!_!!_!!_!|!7!!_!!1!|!_!!_!!2!||!8!!4!!9!|!7!!6!!1!|!3!!5!!2!|!!-_____________-_____________-______________- -_____________-_____________-______________- !!|_ _ _ |_ _ _ |_ _ _ ||_ _ _ |_ _ _ |_ _ _ |!!|!_!!_!!4!|!_!!2!!_!|!_!!3!!_!||!1!!6!!4!|!9!!2!!7!|!5!!3!!8!|!!|_ _ _ |_ _ _ |_ _ _ |.....|_ _ _ |_ _ _ |_ _ _ |!!|!_!!3!!_!|!_!!_!!_!|!_!!9!!_!||>||!2!!3!!8!|!5!!1!!6!|!7!!9!!4!|!!|_ _ _ |_ _ _ |_ _ _ |'---' |_ _ _ |_ _ _ |_ _ _ |!!|!_!!_!!_!|!_!!4!!_!|!_!!_!!_!||!7!!9!!5!|!3!!4!!8!|!1!!2!!6!|!!-_____________-_____________-______________- -_____________-_____________-______________- !!|_ _ _ |_ _ _ |_ _ _ ||_ _ _ |_ _ _ |_ _ _ |!!|!_!!2!!_!|!1!!_!!5!|!9!!_!!_!||!4!!2!!7!|!1!!8!!5!|!9!!6!!3!|!!|_ _ _ |_ _ _ |_ _ _ ||_ _ _ |_ _ _ |_ _ _ |!!|!_!!_!!_!|!6!!_!!_!|!_!!_!!5!||!3!!8!!1!|!6!!7!!9!|!2!!4!!5!|!!|_ _ _ |_ _ _ |_ _ _ ||_ _ _ |_ _ _ |_ _ _ |!!|!_!!_!!6!|!_!!3!!_!|!_!!_!!7!||!9!!5!!6!|!2!!3!!4!|!8!!1!!7!|!!-_____________-_____________-______________- -_____________-_____________-______________- !!!!.----------------------------------------------------------------------------------------------.!!||!!|在 9.096 毫秒内解决难题 |完全解决:真|!!||!!'----------------------------------------------------------------------------------------------' !!!-------------------------------------------------------------------------------------------------------------

.

具体说明

:左拼图

  • 应明确定义 9x9 部分(中间的线;单独的框)
  • 文本框应该只接受数字/只允许输入一个数字(如果可能的话)

:正确的谜题

  • 应明确定义 9x9 部分(中间的线;单独的框)
  • 框是否可以编辑无关紧要,只要它们可以显示结果即可

:按钮在中心

  • 应该运行 [SudokuPuzzle].solve();

:底部文本框

  • 不应编辑

.

我在寻找什么

我从过去的经验中知道这都可以在 JFrame 中完成,但是因为我自己从未构建过一个,所以我不太确定是哪个

(注意:数值是随机的)

用法

你所要做的就是实现接口:

公共接口 SudokuImplementation {void goButtonPressed(Integer[][] leftSudokuValues, SudokuController resultAcceptor);}

只需在此方法中进行所有计算并使用 resultAcceptor.setSudokuResult()

存储结果

这是实际显示 GUI 的方法:

 SudokuImplementation sudokuImplementation =新的 YourSuperSudoku();//<- 你的实现数独视图数独视图=新数独视图();sudokuView.setSudokuImplementation(sudokuImplementation);sudokuView.setVisible(true);

仅此而已!

代码

所有类都在默认包中 - 随心所欲地重构.以下是它们的列表:

  1. SudokuView - 主界面
  2. SudokuRun - 跑步者示例
  3. SudokuController - 允许以安全的方式控制视图
  4. SudokuImplementation - 数独实现的接口
  5. DummySudokuImplementation - 示例实现

1.数独视图:

import java.awt.*;导入 javax.swing.*;导入 javax.swing.text.*;导入 javax.swing.border.*;/*** 构造每个组件并创建它自己的控制器的视图.*/公共类 SudokuView 扩展 JFrame {SudokuController 控制器;公共无效 setSudokuImplementation(SudokuImplementation 侦听器){控制器.setListener(监听器);}/** 创建新表单 NewJFrame */公共数独视图(){控制器 = 新数独控制器();setTitle("数独解算器 1.0");getContentPane().add(createCenterPanel(), BorderLayout.CENTER);getContentPane().add(createBottomPanel(), BorderLayout.SOUTH);setMinimumSize(新维度(600, 300));盒();设置默认关闭操作(JFrame.EXIT_ON_CLOSE);}私人JPanel createBottomPanel(){JPanel 底部面板 = 新 JPanel(新 GridBagLayout());JLabel leftLabel = createLabel("left");JLabel rightLabel = createLabel("right");controller.bindLeftLabel(leftLabel);控制器.bindRightLabel(rightLabel);bottomPanel.add(leftLabel, getWholeCellConstraints());bottomPanel.add(new JSeparator(JSeparator.VERTICAL));bottomPanel.add(rightLabel, getWholeCellConstraints());bottomPanel.setBorder(new BevelBorder(BevelBorder.LOWERED));返回底部面板;}私人 JLabel createLabel(字符串文本){JLabel 标签 = 新的 JLabel(文本);label.setHorizo​​ntalAlignment(JLabel.CENTER);返回标签;}私人JPanel createCenterPanel(){JPanel centerPanel = new JPanel(new GridBagLayout());centerPanel.add(createLeftPanel(), getWholeCellConstraints());centerPanel.add(createCenterButton(), getPreferredSizeConstraint());centerPanel.add(createRightPanel(), getWholeCellConstraints());返回中心面板;}私有 GridBagConstraints getPreferredSizeConstraint() {//默认会做返回新的 GridBagConstraints();}私人 JButton createCenterButton() {JButton goButton = new JButton(">");controller.bindCenterButton(goButton);返回按钮;}私有静态最终插图 SixPixelInset = new Insets(6, 6, 6, 6);私人JPanel createRightPanel(){JPanel rightPanel = create3x3Panel(6);for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {JPanel panel2 = create3x3Panel(2);fillPanelWithNonEditable(panel2, i, j);rightPanel.add(panel2);}}rightPanel.setBorder(new EmptyBorder(sixPixelInset));返回右面板;}私人JPanel createLeftPanel(){JPanel leftPanel = create3x3Panel(6);for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {JPanel panel2 = create3x3Panel(2);fillPanelWithEditable(panel2, i, j);leftPanel.add(panel2);}}leftPanel.setBorder(new EmptyBorder(sixPixelInset));返回左面板;}私有 GridBagConstraints getWholeCellConstraints() {GridBagConstraints wholePanelCnstr = getPreferredSizeConstraint();WholePanelCnstr.fill = java.awt.GridBagConstraints.BOTH;WholePanelCnstr.weightx = 1.0;WholePanelCnstr.weighty = 1.0;返回整个PanelCnstr;}私人无效fillPanelWithEditable(JPanel面板,intmajorRow,intmajorColumn){for (int minorRow = 0; minorRow < 3; minorRow++) {for (int minorColumn = 0; minorColumn < 3; minorColumn++) {final JFormattedTextField editableField = createEditableField();int column = majorColumn * 3 + minorColumn;整数行 = 主要行 * 3 + 次要行;controller.bindLeftSudokuCell(行,列,可编辑字段);panel.add(editableField);}}}私人无效fillPanelWithNonEditable(JPanel面板,intmajorRow,intmajorColumn){for (int minorRow = 0; minorRow < 3; minorRow++) {for (int minorColumn = 0; minorColumn < 3; minorColumn++) {final JFormattedTextField editableField = createNonEditableField();int column = majorColumn * 3 + minorColumn;整数行 = 主要行 * 3 + 次要行;controller.bindRightSudokuCell(row, column, editableField);panel.add(editableField);}}}私人JPanel create3x3Panel(int gap){最终的 GridLayout gridLayout = new GridLayout(3, 3, 1, 1);gridLayout.setHgap(gap);gridLayout.setVgap(gap);JPanel 面板 = 新 JPanel(gridLayout);返回面板;}私人 JFormattedTextField createNonEditableField() {JFormattedTextField field = createEditableField();field.setEditable(false);field.setBackground(Color.WHITE);//否则不可编辑变为灰色返回字段;}私有 JFormattedTextField createEditableField() {JFormattedTextField 字段 = 新 JFormattedTextField();//只接受一位数字,不接受其他数字尝试 {field.setFormatterFactory(new DefaultFormatterFactory(new MaskFormatter("#")));} 捕捉(java.text.ParseException ex){}field.setPreferredSize(new Dimension(16, 30));field.setHorizo​​ntalAlignment(javax.swing.JTextField.CENTER);field.setText(" ");field.setBorder(null);返回字段;}}

2.数独:

import java.awt.EventQueue;导入 javax.swing.UIManager;公共类 SudokuRun 实现 Runnable {公共无效运行(){//******************** 这里可以交换你的真实实现SudokuImplementation sudokuImplementation = new DummySudokuImplementation();//***************************** *************** ********* **** * *数独视图数独视图=新数独视图();sudokuView.setSudokuImplementation(sudokuImplementation);sudokuView.setVisible(true);}公共静态无效主要(字符串参数[]){tryToSetSystemLookAndFeel();EventQueue.invokeLater(new SudokuRun());}私有静态无效 tryToSetSystemLookAndFeel() {尝试 {UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());} 捕捉(异常前){System.out.println("无法设置 LAF");}}}

3.数独控制器:

import java.awt.EventQueue;导入 java.awt.event.ActionEvent;导入 java.awt.event.ActionListener;导入 java.beans.PropertyChangeEvent;导入 java.beans.PropertyChangeListener;导入 javax.swing.JButton;导入 javax.swing.JFormattedTextField;导入 javax.swing.JLabel;公共类数独控制器 {JLabel 左标签,右标签;JFormattedTextField[][] 左数独,右数独;JButton 按钮;公共数独控制器(){leftSudoku = new JFormattedTextField[9][9];//标准数独大小rightSudoku = new JFormattedTextField[9][9];}无效 bindLeftLabel(JLabel 标签){左标签 = 标签;}无效 bindRightLabel(JLabel 标签){右标签 = 标签;}void bindLeftSudokuCell(final int row, final int column, JFormattedTextField field) {field.addPropertyChangeListener("值", new PropertyChangeListener() {//如果用户编辑字段比你可以在这里做点什么公共无效propertyChange(PropertyChangeEvent evt){if (evt.getNewValue() != null) {String newValue = (String) evt.getNewValue();userEditedValueAt(row, column, Integer.valueOf(newValue));}}});左数独[行][列] = 字段;}void userEditedValueAt(int row, int column, int value) {System.out.println("行的值改变:" + row + ",列:" + column + " 到 " + value);}void bindRightSudokuCell(int row, int column, JFormattedTextField field) {对数独[行][列] = 字段;}无效吐出数独(){System.out.println("左:");System.out.println(getPrettyPrinted(leftSudoku));System.out.println("右:");System.out.println(getPrettyPrinted(rightSudoku));}私人字符串 getPrettyPrinted(JFormattedTextField[][] 数独) {StringBuilder sb = new StringBuilder();for (int i = 0; i < 9; i++) {sb.append("|");对于 (int j = 0; j < 9; j++) {如果(数独[i][j]!= null){sb.append(数独[i][j].getText());} 别的 {sb.append("-");}sb.append("");}sb.append("|
");}返回 sb.toString();}无效绑定中心按钮(JButton goButton){this.goButton = goButton;goButton.addActionListener(new ActionListener() {公共无效actionPerformed(ActionEvent e){goButtonPressed();}});}数独实现监听器;public void setListener(SudokuImplementation listener) {this.listener = 监听器;}线程背景GroundThread;私人无效goButtonPressed(){如果(听众!= null){if (backGroundThread == null || (backGroundThread != null && !backGroundThread.isAlive())) {后台线程 = 新线程() {@覆盖公共无效运行(){listener.goButtonPressed(getLeftValues(), SudokuController.this);}};backGroundThread.start();}}}私有整数[][] getLeftValues() {整数[][] 值 = 新整数[9][9];for (int i = 0; i < 9; i++) {对于 (int j = 0; j < 9; j++) {if (!leftSudoku[i][j].getText().equals(" ")) {values[i][j] = Integer.valueOf(leftSudoku[i][j].getText());}}}返回值;}public void setSudokuResult(final Integer[][] 结果) {//任何 GUI 交互都必须在 EDT 上完成//我们不想阻塞计算所以我们选择invokeLater//与 invokeAndWait 不同.EventQueue.invokeLater(new Runnable() {公共无效运行(){for (int i = 0; i < 9; i++) {对于 (int j = 0; j < 9; j++) {对数独[i][j].setValue(String.valueOf(result[i][j]));}}}});}公共无效 setSudokuTime(最终字符串时间){EventQueue.invokeLater(new Runnable() {公共无效运行(){leftLabel.setText("<html>运行时间:<b>" + time);}});}公共无效 setSudokuCompleted(最终布尔完成){EventQueue.invokeLater(new Runnable() {公共无效运行(){rightLabel.setText("<html>完全解决:<b>" + 完成);如果(完成){吐出数独();}}});}}

4.数独实现:

公共接口 SudokuImplementation {void goButtonPressed(Integer[][] leftSudokuValues, SudokuController resultAcceptor);}

5.虚拟数独实现:

import java.util.concurrent.TimeUnit;/*** 模拟数独求解器.演示如何更新 GUI.整体* 构建实现,因此 GUI 永远不会冻结.*/类 DummySudokuImplementation 实现 SudokuImplementation {公共 DummySudokuImplementation() {}public void goButtonPressed(Integer[][] leftSudokuValues, SudokuController resultAcceptor) {System.out.println("长时间运行的计算模拟...");对于 (int i = 0; i < 50; i++) {resultAcceptor.setSudokuCompleted(false);resultAcceptor.setSudokuTime(String.valueOf(i * 50) + "ms");resultAcceptor.setSudokuResult(getRandomResult());等待时间();}resultAcceptor.setSudokuResult(leftSudokuValues);resultAcceptor.setSudokuCompleted(true);等待时间();System.out.println("完成!");}私人无效waitSomeTime(){尝试 {TimeUnit.MILLISECONDS.sleep(50);} 捕捉(InterruptedException ex){}}私有整数[][] getRandomResult() {整数[][] 随机结果 = 新整数[9][9];for (int i = 0; i < 9; i++) {对于 (int j = 0; j < 9; j++) {randomResult[i][j] = (int) (Math.random() * 9);}}返回随机结果;}}

说明

我并没有声称我的做法是最好的.我很想看到其他答案,比如说,所有视图都是用 MigLayout 完成的.这将是非常有启发性的.当 Sun 的实现只有一个时,我正在学习 Swing GUI,所以它在我的风格中占了上风.也就是说,我建议参考 Sun 的 Swing GUI 短期课程.它还包括一个简单的研究案例.读完之后,SudokuView 几乎整个部分都应该清楚了.

我确实将代码分开以使其更具可读性.这就是为什么控制器是另一个类,而不是视图的一部分.该视图仅用于构建小部件和布局,但为了使其简单(而不是创建更多类),我还在其中初始化控制器.

真正的工作是在控制器中.它包含最毛茸茸的细节......线程也在那里,所以它实际上做了什么并不那么明显.我从头开始实现了一个 Thread 类.还有另一种选择:使用 SwingWorker.这可能是陈词滥调,但要明确一点:我使用线程来使 GUI 随时响应.如果没有适当的线程,整个 GUI 将在计算发生时冻结.我决定从 Sudoku 的实现角度使其尽可能简单,例如非阻塞增量更新.

至于线程,了解哪些代码在哪个线程中运行至关重要.GUI 组件触发的每个操作都在 EDT(事件调度线程)上运行.如果您对其执行任何长时间运行的任务,GUI 将不会响应.所以我只是创建另一个线程(参见 goButtonPressed() 的实现)并启动它.之后,EDT 可以在不阻塞的情况下处理任何其他事件.

所以你的数独在一个特殊的后台线程中运行.它可以为所欲为,除非它必须更新 GUI.几乎可以肯定它会,因为那是部分更新的地方.这里有一个问题:如果您直接调用任何 GUI 组件(设置一些值),那么 GUI 将冻结.这是一种称为 EDT 调度违规的情况.与 Swing 的所有交互都应在 EDT 上完成,以避免任何冻结.怎么做?EDT 有专门的事件队列.您在队列中发布了一个更新事件.在 EDT 上,代码一直在监视传入的事件并相应地更新 GUI.所以基本上,这是后台线程和 EDT 之间的通信.在队列上发布事件您可以使用专门为此设计的特殊实用程序方法:EventQueue.invokeLater(new Runnable() {/* 这是您的 GUI 交互 *

本文标题为:为数独求解器构建 GUI(完整的 ASCII 示例)

基础教程推荐