pyqt5-文本框搜索功能

1、介绍

该功能包括:

(1)根据匹配规则,在搜索文本中匹配。支持是否正则

(2)对匹配结果,在文本框组件中进行字体颜色标记;

标签显示当前索引和总的匹配个数,当前索引范围从1到匹配个数;

光标跳转到当前索引指向的匹配结果

(3)支持上一个,下一个跳转

2、进一步的设计

有考虑过,基于文本框搜索文本、多选框正则标志、文本框匹配规则任一发生变化,触发搜索。但是存在几方面问题:

  • 在文本框组件中对匹配结果进行字体颜色标记,会使用到clear和appendHtml方法,会改变文本进而触发搜索
  • 连续在文本框中输入,改变匹配规则或者搜索文本,会频繁调用搜索方法,增加cpu和内存消耗,容易引发卡顿
  • 在搜索文本的文本框中输入,而搜索过程中会调用clear方法,使光标丢失
  • 输入过程和搜索过程,都涉及到对组件的写入改变,容易导致多线程问题

以上问题,想要完全解决是非常困难的,且需要实现的过程是繁琐的,与带来的一丁点便利相比 ,并不值得。

实际方案:

  • 当日志加载,作为搜索文本时,主动调用搜索方法
  • 匹配规则输入结束时,可以enter键主动调用搜索方法
  • 禁止基于搜索相关变量的变化自动搜索

3、ui

4、代码

(1)自定义search.py,其中包含两个重要的函数

"""
搜索算法
返回结果是list,元素是二元list,表示各搜索组的起止索引。比如[[2,15], [20,33]]
"""
import re


def search_fun(s: str, pattern: str, re_flag: bool = False):
    """search_fun(s, pattern, re_flag) -> list"""
    result = list()
    i = 0
    while i < len(s):
        if re_flag:
            r = re.search(pattern=pattern, string=s[i:])
            if r is not None:
                result.append([i + r.start(), i + r.end()])
                i = i + r.end()
            else:
                break
        else:
            if pattern in s[i:]:
                start = s.index(pattern, i)
                i = start + len(pattern)
                result.append([start, i])
            else:
                break
    return result


# print(search_fun('abcdefg', 'de', True))

"""
为搜索结果标记颜色
search_result即search_fun函数的结果
"""


def search_asAppendHtml(s: str, search_result: list, color: str = 'red'):
    """search_asAppendHtml(s, search, color) -> str"""
    last_end = 0
    result = ''
    for i in range(len(search_result)):
        s1 = s[last_end:search_result[i][0]]
        s2 = s[search_result[i][0]: search_result[i][1]]
        # pre避免连续空白符被转为单个空格字符,s1也需要包含
        result = '%s%s<span style=color:%s>%s</span>' % (result, s1, color, s2)
        # 最后一组搜索,那么其之后的字符串也需要拼接
        if i == len(search_result) - 1:
            s1 = s[search_result[i][1]:]
            result = '%s%s' % (result, s1)
    # 没有搜索结果
    if result == '':
        result = s
    # 统一的pre
    result = '<pre>%s</pre>' % result
    return result

(2)业务代码

import sys

import sys

from PyQt5.QtGui import QKeySequence, QKeyEvent
from PyQt5.QtWidgets import QApplication, QWidget, QPlainTextEdit, QLineEdit, QCheckBox, QLabel, QShortcut, QPushButton
from PyQt5.uic import loadUi

from windows import search

"""
启动程序
"""


class my_window:
    def __init__(self):
        self.window = loadUi('ui/widget.ui')
        self.window: QWidget
        self.window.show()

        # 搜索结果
        self.search_index_list = list()
        # 当前索引,从0开始,显示是从1开始
        self.search_index_this = 0
        # 匹配文本框,enter键调用搜索方法
        self.window.lineEdit: QLineEdit
        QShortcut(QKeySequence("return"), self.window.lineEdit, self.search)
        QShortcut(QKeySequence("enter"), self.window.lineEdit,self.search)

        # 正则
        self.window.checkBox: QCheckBox
        self.window.checkBox.stateChanged.connect(self.search)
        # 上一个
        self.window.pushButton: QPushButton
        self.window.pushButton.clicked.connect(self.search_pre)
        # 下一个
        self.window.pushButton_2: QPushButton
        self.window.pushButton_2.clicked.connect(self.search_next)

    def search(self, x=None):
        self.window.lineEdit: QLineEdit
        if not self.window.lineEdit.hasFocus():
            print('未获得焦点')
            return
        # 初始化
        self.search_index_list = list()
        self.search_index_this = 0

        self.window.plainTextEdit: QPlainTextEdit
        self.window.lineEdit: QLineEdit
        s = self.window.plainTextEdit.toPlainText()
        pattern = self.window.lineEdit.text()
        # 搜索文本和匹配规则都不为空字符串,结束
        if s != '' and pattern != '':
            self.window.checkBox: QCheckBox
            # 获取匹配结果
            self.search_index_list = search.search_fun(s=s, pattern=pattern, re_flag=self.window.checkBox.isChecked())
            # 输出
            self.window.plainTextEdit.clear()
            self.window.plainTextEdit.appendHtml(
                search.search_asAppendHtml(s=s, search_result=self.search_index_list, color='red'))
        # 跳转
        self.turn()

    def search_pre(self):
        length = len(self.search_index_list)
        if length > 0:
            self.search_index_this -= 1
            if self.search_index_this < 0:
                self.search_index_this = length - 1
            self.turn()

    def search_next(self):
        length = len(self.search_index_list)
        if length > 0:
            self.search_index_this += 1
            if self.search_index_this >= length:
                self.search_index_this = 0
            self.turn()

    def turn(self):
        if len(self.search_index_list) > 0:
            self.window.plainTextEdit: QPlainTextEdit
            # 跳转
            cur = self.window.plainTextEdit.textCursor()
            cur.setPosition(self.search_index_list[self.search_index_this][0])
            self.window.plainTextEdit.setTextCursor(cur)
            self.window.plainTextEdit.setFocus()
        # 标签标记
        self.window.label: QLabel
        self.window.label.setText('%d/%d' % (self.search_index_this+1, len(self.search_index_list)))


if __name__ == "__main__":
    app = QApplication([])
    w = my_window()
    sys.exit(app.exec_())

 

posted @ 2023-05-01 16:19  挖洞404  阅读(321)  评论(0编辑  收藏  举报