文本搜索功能

1、介绍

根据匹配规则文本和是否正则标志,对待搜索文本进行搜索处理,结果为list[list[int, int]]类型,即各搜索结果的开始索引和结束索引。

在组件中对搜索结果进行标记,并根据当前索引跳转到指定位置和进行提示标签输出。

(1)GUI

  • 由一个QPlainTextEdit组件获取待搜索文本
  • 由一个QLineEdit组件获取匹配规则
  • 由一个QCheckBox组件获取是否为正则匹配
  • 由两个按钮QPushButton进行上一个、下一个控制跳转
  • 由一个QLabel组件输出当前索引信息和总的匹配结果数,初始是1/0

(2)状态量

  • search_index_list list[list[int, int]]类型,即各搜索结果的开始索引和结束索引。
  • search_index_this  int类型,当前索引,最小为0,提示标签输出时会+1,即表示第(search_index_this+1)个索引

2、模块

由于搜索业务是比较常见的功能,因此设计成模块,以便使用。

(1)window_api/search_ui_module.py

from PyQt5.QtWidgets import QPlainTextEdit, QLineEdit, QCheckBox, QLabel
import api.search

"""
搜索业务
@search_index_list  list[list[int 2]]类型,表示各组搜索匹配结果的开始索引和结束索引
@plainTextEdit  QPlainTextEdit文本框组件类型,从中获取待搜索文本
@lineEdit   QLineEdit行文本框组件类型,从中获取匹配规则文本
@checkBox   QCheckBox多选框组件类型,从中获取是否re匹配的标志
@label  QLabel组件类型,输出总搜索结果数和当前结果索引,这里当前索引实际是从1开始
"""


def search(search_index_list: list, plainTextEdit: QPlainTextEdit, lineEdit: QLineEdit, checkBox: QCheckBox,
           label: QLabel):
    # 初始化
    search_index_list.clear()
    # 获取待搜索文本和匹配规则
    s = plainTextEdit.toPlainText()
    pattern = lineEdit.text()
    plainTextEdit.clear()
    # 当都不为空字符串时,搜索
    if s != '' and pattern != '':
        # 获取匹配结果
        search_index_list = api.search.search_fun(s=s, pattern=pattern, re_flag=checkBox.isChecked())
        # 输出
        plainTextEdit.appendHtml(api.search.search_asAppendHtml(s=s, search_result=search_index_list, color='red'))
    else:
        plainTextEdit.setPlainText(s)
    # 跳转
    turn(search_index_list=search_index_list, plainTextEdit=plainTextEdit, search_index_this=0, label=label)
    # 返回当前索引为0
    return 0


"""
跳转到指定索引,并标签显示信息
@search_index_this  int类型,当前跳转的索引,显示时会+1
@label  QLabel组件类型,输出总搜索结果数和当前结果索引,这里当前索引实际是从1开始
@search_index_list  list[list[int 2]]类型,表示各组搜索匹配结果的开始索引和结束索引
@plainTextEdit  QPlainTextEdit文本框组件类型,从中获取待搜索文本
"""


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


"""
上一个
@search_index_this  int类型,当前跳转的索引,显示时会+1
@label  QLabel组件类型,输出总搜索结果数和当前结果索引,这里当前索引实际是从1开始
@search_index_list  list[list[int 2]]类型,表示各组搜索匹配结果的开始索引和结束索引
@plainTextEdit  QPlainTextEdit文本框组件类型,从中获取待搜索文本
"""


def search_pre(search_index_list: list, plainTextEdit: QPlainTextEdit, search_index_this: int, label: QLabel):
    length = len(search_index_list)
    if length > 0:
        if search_index_this - 1 >= 0:
            search_index_this = search_index_this - 1
        # 跳转
        turn(search_index_list=search_index_list, plainTextEdit=plainTextEdit, search_index_this=search_index_this,
             label=label)
    # 返回当前索引
    return search_index_this


"""
下一个
@search_index_this  int类型,当前跳转的索引,显示时会+1
@label  QLabel组件类型,输出总搜索结果数和当前结果索引,这里当前索引实际是从1开始
@search_index_list  list[list[int 2]]类型,表示各组搜索匹配结果的开始索引和结束索引
@plainTextEdit  QPlainTextEdit文本框组件类型,从中获取待搜索文本
"""


def search_next(search_index_list: list, plainTextEdit: QPlainTextEdit, search_index_this: int, label: QLabel):
    length = len(search_index_list)
    if length > 0:
        if search_index_this + 1 < length:
            search_index_this = search_index_this + 1
        # 跳转
        turn(search_index_list=search_index_list, plainTextEdit=plainTextEdit, search_index_this=search_index_this,
             label=label)
    # 返回当前索引
    return search_index_this

(2)api/search_module.py

import re

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


def search(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


"""
为搜索结果标记颜色
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 = '<pre>%s%s</pre>' % (result, s1)
    # 没有搜索结果
    if s == '':
        result = '<pre>%s</pre>' % s
    return result

3、事件和快捷键

(1)事件

  • 多选框的点击事件会触发搜索
  • 上一个和下一个按钮点击,会分别触发search_pre和search_next

(2)快捷键

当焦点在lineEdit组件时,即刚输入完匹配规则时,按下enter键或者return键,触发搜索方法

需要注意的是,通过组件的isFocus方法判断lineEdit组件是否获取焦距。

当一个窗口中,存在两组或以上的搜索时,针对同一快捷键只能设置一次快捷键,然后根据各搜索的lineEdit组件是否获得焦点而选择调用不同的搜索方法

QShortcut(QKeySequence("Enter"), self.window.lineEdit, self.which_focus)
QShortcut(QKeySequence("return"), self.window.lineEdit, self.which_focus)

def which_focus(self):
    if self.window.lineEdit.hasFocus():
        self.request_search()
        if self.window.lineEdit_2.hasFocus():
            self.response_search()

(3)主动调用搜索

除了事件和快捷键调用搜索相关方法,还可能是程序主动调用。

比如日志中点击查看,测试过程中输出变化

posted @ 2023-05-04 03:19  挖洞404  阅读(45)  评论(0编辑  收藏  举报