前不久,我为了提高考研英语的复习效率,随手写了个小脚本——自动提取待复习单词。
【插入一句】有兴趣的朋友可以来看看,或许可以帮助到你
传送门(第一版)
经过几天的使用,我发现复习效率确实变高啦,不用像以前那样一页页翻文档,英语真题词汇的复习时长也终于落在一个比较合理的区间。
但是随之而来我又发现了另一个问题,我确实可以快速把所有待复习单词整理出来,但是我通过复习,那些逐步掌握的单词(后续可能不太需要继续复习)没有办法实现自动去除高亮,这就会导致我们还必须手动回到文档然后把它去掉…这显然又提升了自己的工作量…又回到了最初的起点。
于是我便基于此,继续完善了这个小脚本,于是就有了这个小脚本2.0版本。
先给大家看看效果!
还是咱们熟悉的脚本开始!!
wow!!!!!虽然上次说自己懒不想做界面哈哈,光速打脸。(本人java GUI真的太菜,请忽视丑丑的界面)
所有单词会整齐地躺在这里
接下来,当然是来测试一下新功能:去除那些已经不需要再次复习的单词的高亮。当然,大家可能会发现这个界面既没有多选框,又没有按钮,那应该怎么选择单词…
比如我们文档中是这样的,我们想要把这两个单词的高亮去除
我们只需要单击,会有一个极其简陋的渲染提醒你,你已经选上了。
然后再点下一个(是的这里没有保留上一个的渲染 嘻嘻,但是后台已经记住了这个需要去除高亮的单词,并且我设置了防重复,所以就算你点好几次也没关系)
然后我们假装自己已经复习完了,可以关闭窗口了。
观众可能会问:???这就好了
接下来我们去文档看看是不是去除了高亮
再次运行脚本,确实已经没有这两个词了
好啦,演示结束,或许样式一般,交互性也没有那么舒服,但毕竟效率至上 ,咱就一切从简了,能用就行。
实现步骤很清晰:
从word文档拿到待复习数据 (上一版已实现)
利用实体类和List把所有数据保存起来
使用java GUI创建一个窗体表格,并作初始化(待实现)
随便调调样式,设置设置窗体大小之类的
把数据赋值给表格(待实现)
通过迭代器把List赋值给表格
设置一下监听事件(单击监听事件 窗口关闭监听事件)(待实现)
需要做的事有两件:
①记录鼠标点击的行的下标
②在窗口关闭时根据保存的下标来去除文档高亮
接下来直接上代码(相关开发环境和上一版一摸一样)
先来个实体类吧,单词和中文应该是一个整体
import lombok.Data;
@Data
public class Word {
private String english;
private String chinese;
}
然后
import com.spire.doc.Document;
import com.spire.doc.Section;
import com.spire.doc.TableCell;
import com.spire.doc.TableRow;
import com.spire.doc.documents.Paragraph;
import com.spire.doc.documents.TextSelection;
import com.spire.doc.fields.TextRange;
import com.spire.doc.interfaces.ITable;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
public class word_op_2 extends JFrame {
public static void main(String[] args) {
// 拿数据
getWords();
// 分数据
operateWords();
// 构建图形化界面
new word_op_2();
}
//======拿到文档数据==================================================================================================
//所有待复习单词原始数据
public static List<Word> words = new ArrayList<>();
//临时存储器
public static Word word = new Word();
public static void getWords() {
//加载Word源文档
Document doc = new Document();
doc.loadFromFile("C:\\Users\\23252\\Desktop\\真题词汇词组.docx");
//获取第一节
Section section = doc.getSections().get(0);
for (int i = 0; i < section.getTables().getCount(); i++) {
//获取第i个表格
ITable table = section.getTables().get(i);
//遍历每个表格的行
for (int j = 0; j < table.getRows().getCount(); j++) {
TableRow row = table.getRows().get(j);
//遍历每行的列
for (int k = 0; k < row.getCells().getCount(); k++) {
TableCell cell = row.getCells().get(k);
//某个单元格整体的内容
StringBuilder res = new StringBuilder();
//遍历单元格中的段落
for (int l = 0; l < cell.getParagraphs().getCount(); l++) {
Paragraph paragraph = cell.getParagraphs().get(l);
//遍历每个段落的子对象
for (int m = 0; m < paragraph.getChildObjects().getCount(); m++) {
Object obj = paragraph.getChildObjects().get(m);
if (obj instanceof TextRange) {
//获取文本
String text = ((TextRange) obj).getText();
//去除特殊符号
text = text.replaceAll("\r|\n", "");
//获取文本的高亮颜色(即突出显示颜色)
Color color = ((TextRange) obj).getCharacterFormat().getHighlightColor();
//判断是否高亮
if (!(color.getRGB() == 0)) {
res.append(text);
//判断是否有多段 进行分割
if (cell.getParagraphs().getCount() != 1 && m == paragraph.getChildObjects().getCount() - 1) {
//最后一段后面没东西 不需要再加分割
if (cell.getParagraphs().getCount() - 1 != l) {
res.append(" | ");
}
}
}
}
}
}
if (!res.toString().equals("")) {
//判断奇偶列
if ((k + 1) % 2 != 0) {
word.setEnglish(res.toString());
} else {
word.setChinese(res.toString());
Word temp = new Word();
temp.setEnglish(word.getEnglish());
temp.setChinese(word.getChinese());
words.add(temp);
// 清空全局变量
word.setChinese(null);
word.setEnglish(null);
}
}
}
}
}
}
//======构建页面表格==================================================================================================
//表格
static JTable table;
public word_op_2() {
setSize(600, 600);
setLocationRelativeTo(null);
/*创建表格*/
DefaultTableModel model = new DefaultTableModel();
model.addColumn("英文", new Vector<>(english_list));
model.addColumn("", new Vector<String>());
model.addColumn("", new Vector<String>());
model.addColumn("", new Vector<String>());
model.addColumn("汉语", new Vector<>(chinese_list));
table = new JTable(model);
JScrollPane jp = new JScrollPane(table);
JTableHeader head = table.getTableHeader();
//设置表头的大小
head.setPreferredSize(new Dimension(head.getWidth(), 30));
//设置表头字体大小
head.setFont(new Font("宋体", Font.BOLD, 16));
//设置表格的行宽
table.setRowHeight(30);
//设置表格行中字体大小
table.setFont(new Font("宋体", Font.ROMAN_BASELINE, 13));
/*设置表格中的内容居中*/
DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
renderer.setHorizontalAlignment(DefaultTableCellRenderer.CENTER);
table.setDefaultRenderer(Object.class, renderer);
this.add(jp);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//单机事件监听
table.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
getEditIndex(table.getSelectedRow());
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
});
//页面窗口状态监听(这里只用了关闭页面去除高亮)
addWindowListener(new WindowListener() {
@Override
public void windowOpened(WindowEvent e) {
}
@Override
public void windowClosing(WindowEvent e) {
//如果没做修改 就不需要重新保存文件了
if (set.size() != 0) {
removeHeightLight();
}
}
@Override
public void windowClosed(WindowEvent e) {
}
@Override
public void windowIconified(WindowEvent e) {
}
@Override
public void windowDeiconified(WindowEvent e) {
}
@Override
public void windowActivated(WindowEvent e) {
}
@Override
public void windowDeactivated(WindowEvent e) {
}
});
}
//======把源数据分类==================================================================================================
// 中文列 英文列
public static List<String> chinese_list = new ArrayList<>();
public static List<String> english_list = new ArrayList<>();
//将单词数据分为中文和英文两列 方便迭代器使用
public static void operateWords() {
for (Word value : words) {
english_list.add(value.getEnglish());
chinese_list.add(value.getChinese());
}
}
//======为关闭页面最准备===============================================================================================
//待修改的元素索引集合
public static Set<Integer> set = new HashSet<Integer>();
// 记录待修改的元素索引
public static void getEditIndex(int index) {
set.add(index);
}
//======关闭页面并去除高亮=============================================================================================
// 窗口关闭之后的高亮删除操作
public static void removeHeightLight() {
//加载Word源文档
Document doc = new Document();
doc.loadFromFile("C:\\Users\\23252\\Desktop\\真题词汇词组.docx");
for (Integer i : set) {
// 搜索关键字段落
TextSelection[] englishSelections = doc.findAllString(words.get(i).getEnglish(), false, false);
TextSelection[] chineseSelections = doc.findAllString(words.get(i).getChinese(), false, false);
//去除高亮
for (TextSelection english : englishSelections) {
english.getAsOneRange().getCharacterFormat().setHighlightColor(null);
}
for (TextSelection chinese : chineseSelections) {
chinese.getAsOneRange().getCharacterFormat().setHighlightColor(null);
}
}
doc.saveToFile("C:\\Users\\23252\\Desktop\\真题词汇词组.docx");
}
}
接下来的步骤和上一篇一摸一样,打jar包,写bat启动脚本,不知道的兄弟可以看上一篇哈
// 中文列 英文列
public static List<String> chinese_list = new ArrayList<>();
public static List<String> english_list = new ArrayList<>();
//将单词数据分为中文和英文两列 方便迭代器使用
public static void operateWords() {
for (Word value : words) {
english_list.add(value.getEnglish());
chinese_list.add(value.getChinese());
}
}
这个问题需要结合给列表赋值的代码来说
/*创建表格*/
DefaultTableModel model = new DefaultTableModel();
model.addColumn("英文", new Vector<>(english_list));
model.addColumn("", new Vector<String>());
model.addColumn("", new Vector<String>());
model.addColumn("", new Vector<String>());
model.addColumn("汉语", new Vector<>(chinese_list));
第一个原因是 :在初始化表格的时候我们发现它是把迭代器作为参数的,那就意味着放进迭代器里的第一级的东西他会帮你一个个遍历,但如果更深入的东西他就不会帮你遍历了。就像这样,迭代器只会帮你把一个个对象取出打印到页面上,而不会把英文和中文打印出来。
第二个原因:我们通过实操不难发现,表格要么是按列为单位添加,要么是按行为单位来添加,所以拆开之后一列全是英语,一列全是中文,正好方便插入。
@Override
public void mouseClicked(MouseEvent e) {
getEditIndex(table.getSelectedRow());
}
在处理函数内记住需要去除高亮的下标,这里选择 HashSet 的一个主要原因是因为它可以过滤重复元素,可以为后台处理减少很多不必要的重复操作
//待修改的元素索引集合
public static Set<Integer> set = new HashSet<Integer>();
// 记录待修改的元素索引
public static void getEditIndex(int index) {
set.add(index);
}
在用户关闭脚本的时候,触发监听函数(这里也是做个小小的判断,提高运行效率)
@Override
public void windowClosing(WindowEvent e) {
//如果没做修改 就不需要重新保存文件了
if (set.size() != 0) {
removeHeightLight();
}
}
然后在处理函数中去除高亮,这里利用了 Hashset中的下标和我们最初的源数据下标是一致的。所以直接拿来用就行了。(注意中英文都要去除高亮哦)
// 窗口关闭之后的高亮删除操作
public static void removeHeightLight() {
//加载Word源文档
Document doc = new Document();
doc.loadFromFile("C:\\Users\\23252\\Desktop\\真题词汇词组.docx");
for (Integer i : set) {
// 搜索关键字段落
TextSelection[] englishSelections = doc.findAllString(words.get(i).getEnglish(), false, false);
TextSelection[] chineseSelections = doc.findAllString(words.get(i).getChinese(), false, false);
//去除高亮
for (TextSelection english : englishSelections) {
english.getAsOneRange().getCharacterFormat().setHighlightColor(null);
}
for (TextSelection chinese : chineseSelections) {
chinese.getAsOneRange().getCharacterFormat().setHighlightColor(null);
}
}
doc.saveToFile("C:\\Users\\23252\\Desktop\\真题词汇词组.docx");
}
}
到这里,这一版的改动已经达到了我的预期,如果不出意外这应该就是最终版了。
但其实根据每个人的不同需求,还可以继续添加一些其他功能,比如从文档中随机出来几十个单词,不会的标记好,然后添加高亮…等等等等