书上只是写了界面,顺便把功能完善了。

QLineEditDropList类 - 在普通的QLineEdit上,增加了下拉框(历史记录)。

package jqt;

import io.qt.core.QStringList;
import io.qt.core.QStringListModel;
import io.qt.core.Qt;
import io.qt.gui.QFocusEvent;
import io.qt.gui.QMouseEvent;
import io.qt.widgets.QCompleter;
import io.qt.widgets.QLineEdit;
import io.qt.widgets.QWidget;

public class QLineEditDropList extends QLineEdit{
    //增加下拉框
    QStringListModel model;
    QCompleter completer;
    QStringList history;
    public final Signal1<QMouseEvent> doubleClicked = new Signal1();
    public QLineEditDropList(){
        this(null);
    }
    public QLineEditDropList(QWidget parent){
        super(parent);
        history = new QStringList();
        model = new QStringListModel(this);
        completer = new QCompleter(model, this);
        completer.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive);
        completer.setCompletionMode(QCompleter.CompletionMode.UnfilteredPopupCompletion);
        setCompleter(completer);
    }

    @Override
    public void focusInEvent(QFocusEvent e){
        super.focusInEvent(e);
        completer.complete();
    }
    @Override
    public void mouseDoubleClickEvent(QMouseEvent e){
        doubleClicked.emit(e);
        super.mouseDoubleClickEvent(e);
    }
    public void add(String text){
        if (text.isEmpty())
            return;
        // 去重并限制数量
        history.removeAll(text);
        history.append(text);
        model.setStringList(history);
    }
}

QFindFileDialog类

package jqt;

import io.qt.core.*;
import io.qt.widgets.*;

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.regex.Pattern;

public class QFindFileDialog extends QDialog{
    QLabel namedLabel;
    QLabel lookInLabel;
    QLineEditDropList lookInLineEdit;
    QLineEdit namedLineEdit;
    QCheckBox includeSubdirectoriesCheckBox;
    QTableWidget tableWidget;
    QLabel messageLabel;
    QPushButton findButton;
    QPushButton stopButton;
    QStringList labels;
    Boolean stopSearch = false, includeSubdirectories = true;
    QDateTime lastCheck = QDateTime.currentDateTime().addSecs(-60);// 1分钟前
    //final QEvent.Type INVOKE_EVENT_TYPE = QEvent.Type.resolve(QEvent.Type.User.value() + 1);

    public QFindFileDialog(){
        this(null);
    }
    public QFindFileDialog(QWidget parent){
        super(parent);
        namedLabel = new QLabel("文件名(&F)");
        namedLineEdit = new QLineEdit();
        namedLabel.setBuddy(namedLineEdit);

        lookInLabel = new QLabel("指定目录(&D)");
        lookInLineEdit = new QLineEditDropList();
        lookInLabel.setBuddy(lookInLineEdit);

        includeSubdirectoriesCheckBox = new QCheckBox("包含子目录");
        labels = new QStringList();
        labels.append("文件名字");
        labels.append("是否修改");
        labels.append("大小(字节)");
        labels.append("所在位置");

        tableWidget = new QTableWidget();
        tableWidget.setColumnCount(4);
        tableWidget.setHorizontalHeaderLabels(labels);
        // 设置表格的列自适应策略
        QHeaderView header = tableWidget.horizontalHeader();
        header.setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents); // 内容自适应
        header.setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents); // 内容自适应
        header.setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents); // 内容自适应
        header.setStretchLastSection(true); // 最后一列拉伸填充

        // 设置表格大小策略
        tableWidget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding);
        messageLabel = new QLabel("");
        messageLabel.setFrameShape(QFrame.Shape.Panel);
        messageLabel.setFrameShadow(QFrame.Shadow.Sunken);

        findButton = new QPushButton("查找(&V)");
        stopButton = new QPushButton("停止(&S)");

        QGridLayout leftLayout = new QGridLayout();
        leftLayout.setRowStretch(3, 1); // 让表格所在行(第3行)可以拉伸
        leftLayout.addWidget(namedLabel, 0, 0);
        leftLayout.addWidget(namedLineEdit, 0, 1);
        leftLayout.addWidget(lookInLabel, 1, 0);
        leftLayout.addWidget(lookInLineEdit, 1, 1);
        leftLayout.addWidget(includeSubdirectoriesCheckBox, 2, 0, 1, 2);
        leftLayout.addWidget(tableWidget, 3, 0, 1, 2);
        leftLayout.addWidget(messageLabel, 4, 0, 1, 2);

        QVBoxLayout rightLayout = new QVBoxLayout();
        rightLayout.addWidget(findButton);
        rightLayout.addWidget(stopButton);
        rightLayout.addStretch();

        QHBoxLayout mainLayout = new QHBoxLayout();
        // 设置主布局拉伸因子(左侧优先拉伸)
        mainLayout.setStretchFactor(leftLayout, 1);
        mainLayout.setStretchFactor(rightLayout, 0);
        mainLayout.addLayout(leftLayout);
        mainLayout.addLayout(rightLayout);
        setLayout(mainLayout);

        setWindowTitle("搜索文件");

        includeSubdirectoriesCheckBox.setChecked(true);

        findButton.clicked.connect(this::find);
        stopButton.clicked.connect(this::stop);
        includeSubdirectoriesCheckBox.checkStateChanged.connect(this::includeSubDirs);
        initDrivers();
    }

    public void initDrivers(){
        QList<QStorageInfo> drives = QStorageInfo.mountedVolumes();
        for(QStorageInfo drive : drives){
            String root = drive.rootPath();
            if (root.length() >= 2 &&
                    root[1] == ':' &&
                    ((Character)root[0]).isLetter()){
                lookInLineEdit.add(root);
            }
        }
    }
    public void find(){
        findButton.setEnabled(false);
        stopButton.setEnabled(true);
        tableWidget.setRowCount(0);   // 行数设为0
        stopSearch = false;
        messageLabel.setText("正在搜索...");
        new Thread(()->{
            try{
                searchFiles(namedLineEdit.text(), lookInLineEdit.text());
            } catch(Exception e){
                messageLabel.setText("搜索出错: " + e.getMessage());
            } finally{
                findButton.setEnabled(true);
                stopButton.setEnabled(false);
            }
        }).start();
    }
    public void stop(){
        findButton.setEnabled(true);
        stopButton.setEnabled(false);
        stopSearch = true;
    }
    public void includeSubDirs(){
        includeSubdirectories = includeSubdirectoriesCheckBox.isChecked();
    }
    /**
     * 搜索指定目录下符合文件名模式的文件,并返回这些文件所在目录的列表
     *
     * @param fileNamePattern 文件名模式(支持通配符*和?,自动处理转义)
     * @param directory       搜索的起始目录
     * @return 包含匹配文件所在目录的列表(去重) or 数量
     */

    public long searchFiles(String fileNamePattern, String directory) {
        Path startDir = Paths.get(directory);
        final Path finalStartDir = Paths.get(directory).toAbsolutePath().normalize();

        String regex = convertWildcardToRegex(fileNamePattern);
        Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
        //Set<Path> directories = new LinkedHashSet<>();

        Files.walkFileTree(startDir, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
                if (stopSearch){
                    messageLabel.setText("搜索停止");
                    return FileVisitResult.TERMINATE;
                }

                // 统一转换为标准路径后再比较
                Path normalizedDir = dir.toAbsolutePath().normalize();
                if (!includeSubdirectories && !normalizedDir.equals(finalStartDir)) {
                    return FileVisitResult.SKIP_SUBTREE;  // 阻止进入子目录
                }
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                if (stopSearch) {
                    messageLabel.setText("搜索停止");
                    return FileVisitResult.TERMINATE;
                }

                if (pattern.matcher(file.getFileName().toString()).matches()) {
                    //directories.add(file.getParent());
                    int row = tableWidget.rowCount();
                    tableWidget.insertRow(row);
                    tableWidget.setItem(row, 0, new QTableWidgetItem(file.getFileName().toString()));
                    tableWidget.setItem(row, 1, new QTableWidgetItem(isFDModified(file.toString()).toString()));
                    tableWidget.setItem(row, 2, new QTableWidgetItem(getFDSize(file.toString()).toString()));
                    tableWidget.setItem(row, 3, new QTableWidgetItem(file.getParent().toString()));
                }
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFileFailed(Path file, IOException e) {
                if (e instanceof AccessDeniedException) {
                    System.err.println("访问被拒绝: " + file);
                    return FileVisitResult.SKIP_SUBTREE; // 跳过无权限目录
                }
                return FileVisitResult.CONTINUE;
            }
        });

        /*
        return directories.stream()
                .map(Path::toString)
                .collect(Collectors.toList());
         */
        if (!stopSearch)
            messageLabel.setText("找到%1个文件".arg(tableWidget.rowCount()));
        return tableWidget.rowCount();
    }

    /**
     * 将通配符模式转换为正则表达式
     *
     * @param pattern 包含通配符的文件名模式
     * @return 对应的正则表达式
     */
    private String convertWildcardToRegex(String pattern) {
        StringBuilder regex = new StringBuilder();
        for (char c : pattern.toCharArray()) {
            switch (c) {
                case '*':
                    regex.append(".*");
                    break;
                case '?':
                    regex.append(".");
                    break;
                case '.':
                case '^':
                case '$':
                case '+':
                case '|':
                case '(':
                case ')':
                case '[':
                case ']':
                case '{':
                case '}':
                case '\\':
                    regex.append('\\').append(c);
                    break;
                default:
                    regex.append(c);
            }
        }
        return "^" + regex + "$"; // 完全匹配文件名
    }
    // 获取目录或者指定文件大小(字节)
    public Long getFDSize(String path) {
        QFileInfo fileInfo = new QFileInfo(path);
        // 如果是文件,直接返回文件大小
        if (fileInfo.isFile()) {
            return fileInfo.size();
        }
        long totalSize = 0;
        QDir dir = new QDir(path);
        dir.setFilter(QDir.Filter.Dirs, QDir.Filter.Files, QDir.Filter.NoDotAndDotDot, QDir.Filter.Hidden, QDir.Filter.NoSymLinks);

        QList<QFileInfo> entries = dir.entryInfoList();
        for (QFileInfo entry : entries) {
            if (entry.isDir()) {
                totalSize += getFDSize(entry.absoluteFilePath());
            } else {
                totalSize += entry.size();
            }
        }
        return totalSize;
    }
    // 检查目录或者指定文件是否被修改(与上次记录时间比较)
    public Boolean isFDModified(String path) {
        QFileInfo info = new QFileInfo(path);

        if (!info.exists()) return false;

        if (info.isFile()) {
            return info.lastModified() > lastCheck;
        }

        if (info.isDir()) {
            if (info.lastModified() > lastCheck) {
                return true;
            }
            // 递归检查子项
            QDir dir = new QDir(path);
            dir.setFilter(QDir.Filter.Dirs, QDir.Filter.Files, QDir.Filter.NoDotAndDotDot, QDir.Filter.Hidden, QDir.Filter.NoSymLinks);
            QList<QFileInfo> entries = dir.entryInfoList();
            for (QFileInfo entry : entries) {
                if (entry.lastModified() > lastCheck) {
                    return true;
                }
                if (entry.isDir() && isFDModified(entry.absoluteFilePath())) {
                    return true;
                }
            }
        }
        return false;
    }
}

调用函数

package one;

import jqt.Jqt;
import jqt.QFindFileDialog;

import java.util.List;

public class Main{
    public static void main(String[] args){
        Jqt qt = new Jqt(args, null);
        QFindFileDialog fd = new QFindFileDialog();
        fd.show();
        qt.run();
    }
}

测试结果

 

posted on 2025-05-06 13:54  dalgleish  阅读(18)  评论(0)    收藏  举报