文本搜索功能

Posted wd404

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了文本搜索功能相关的知识,希望对你有一定的参考价值。

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)主动调用搜索

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

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

使搜索功能快速响应 ui 搜索栏中键入的文本 [swift]

【中文标题】使搜索功能快速响应 ui 搜索栏中键入的文本 [swift]【英文标题】:make the search function respond quickly to the keyed in text in ui search bar [swift] 【发布时间】:2016-03-27 03:38:48 【问题描述】:

我一直在为我的 ios 应用程序开发搜索功能。目前我设法使它工作,但是只有当用户完成编写文本并单击搜索按钮时,搜索才会开始。即使键入第一个字母,如何通过查询开始快速响应?我目前正在使用带有 UIView 控制器的搜索栏,而不是 UITableViewController。

这是我的代码:

//
//  CoffeeListViewController.swift
//  CoffeeApp
//
//  Created by izzuddin on 14/03/2016.
//  Copyright © 2016 izzuddin. All rights reserved.
//

import UIKit
import CloudKit
import FBSDKCoreKit
import FBSDKLoginKit


class CoffeeListViewController: UIViewController 

    ////////////////////////////OUTLET

    @IBOutlet weak var tableview: UITableView!
    var coffees = [Coffee]()
    var filteredNames = [Coffee]()

    @IBOutlet weak var searchBar: UISearchBar!




    ////////////////////////////SPINNER

    var loadingView = UIView()
    var container = UIView()
    var activityIndicator = UIActivityIndicatorView()


    func showLoading() 

        let win:UIWindow = UIApplication.sharedApplication().delegate!.window!!
        self.loadingView = UIView(frame: win.frame)
        self.loadingView.tag = 1
        self.loadingView.backgroundColor = UIColor(red: 255/255, green: 255/255, blue: 255/255, alpha: 0)

        win.addSubview(self.loadingView)

        container = UIView(frame: CGRect(x: 0, y: 0, width: win.frame.width/3, height: win.frame.width/3))
        container.backgroundColor = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 0.6)
        container.layer.cornerRadius = 10.0
        container.layer.borderColor = UIColor.grayColor().CGColor
        container.layer.borderWidth = 0.5
        container.clipsToBounds = true
        container.center = self.loadingView.center


        activityIndicator.frame = CGRectMake(0, 0, win.frame.width/5, win.frame.width/5)
        activityIndicator.activityIndicatorViewStyle = .WhiteLarge
        activityIndicator.center = self.loadingView.center


        self.loadingView.addSubview(container)
        self.loadingView.addSubview(activityIndicator)

        activityIndicator.startAnimating()

    

    func hideLoading()
        UIView.animateWithDuration(0.0, delay: 1.0, options: .CurveEaseOut, animations: 
            self.container.alpha = 0.0
            self.loadingView.alpha = 0.0
            self.activityIndicator.stopAnimating()
            , completion:  finished in
                self.activityIndicator.removeFromSuperview()
                self.container.removeFromSuperview()
                self.loadingView.removeFromSuperview()
                let win:UIWindow = UIApplication.sharedApplication().delegate!.window!!
                let removeView  = win.viewWithTag(1)
                removeView?.removeFromSuperview()
        )
    





    ////////////////////////////LOAD DATA
    func call_data()
        self.showLoading()

        let container = CKContainer.defaultContainer()
        let publicData = container.publicCloudDatabase

        let query = CKQuery(recordType: "Coffee", predicate: NSPredicate(format: "TRUEPREDICATE", argumentArray: nil))
        publicData.performQuery(query, inZoneWithID: nil)  results, error in
            if error == nil  // There is no error
                for coffee in results! 
                    let newCoffee = Coffee()
                    newCoffee.name = coffee["Name"] as! String
                    newCoffee.cafe = coffee["Cafe"] as! String
                    newCoffee.rating = coffee["Rating"] as! Double
                    newCoffee.place = coffee["Place"] as? CLLocation

                    self.coffees.append(newCoffee)


                    dispatch_async(dispatch_get_main_queue(),  () -> Void in
                        self.tableview.reloadData()
                        self.hideLoading()

                    )
                
            
            else 
                print(error)
            
        
    




    //////////////////////////////////////////////////VIEW DID LOAD
    override func viewDidLoad() 
        super.viewDidLoad()
        call_data()

    





    //////////////////////////////////////////////////PREPARE SEGUE

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) 


        if segue.identifier == "AddCoffeeVC" 
            let destVC: AddCoffeeViewController = segue.destinationViewController as! AddCoffeeViewController
            destVC.delegate = self
        

        if segue.identifier == "showCoffeeVC" 
            let destVC: ShowCoffeeViewController = segue.destinationViewController as! ShowCoffeeViewController

            if let selectedIndexPath = self.tableview.indexPathForSelectedRow 
                let coffee: Coffee = self.coffees[selectedIndexPath.row]
                destVC.coffeeDetail = coffee
            

        

    








    ////////////////////////////PASSING BACK DATA

    extension CoffeeListViewController : AddCoffeeDelegate

    func viewController(vc: AddCoffeeViewController, didAddCoffee coffee: Coffee!) 
        self.coffees.append(coffee)

        //create the cloudkit record here
        let container = CKContainer.defaultContainer()
        let publicData = container.publicCloudDatabase


        let record = CKRecord(recordType: "Coffee")
        record.setValue(coffee.name, forKey: "Name")
        record.setValue(coffee.cafe, forKey: "Cafe")
        record.setValue(coffee.rating, forKey: "Rating")
        record.setValue(coffee.place, forKey: "Place")
        publicData.saveRecord(record, completionHandler:  record, error in
            if error != nil 
                print(error)
            
        )


        self.dismissViewControllerAnimated(true, completion: nil)
        self.tableview.reloadData()
    






    ////////////////////////////////////////////////// ADPOPT TABLE VIEW

    extension CoffeeListViewController : UITableViewDelegate, UITableViewDataSource

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int

        return self.coffees.count

    


 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
        let cell = tableView.dequeueReusableCellWithIdentifier("CoffeeCell", forIndexPath: indexPath)

        let coffee = coffees[indexPath.row]
        cell.textLabel!.text = coffee.name
        cell.detailTextLabel!.text = "\(coffee.rating)"
        return cell
    







    ////////////////////////////////////////////////// SEARCH BAR


    extension CoffeeListViewController : UISearchBarDelegate 

    func searchBarShouldEndEditing( searchBar: UISearchBar)-> Bool
        searchBar.showsCancelButton = true
        return searchBar.showsCancelButton.boolValue
    

    func searchBarShouldBeginEditing(searchBar: UISearchBar) -> Bool
        searchBar.showsCancelButton = true
        return searchBar.showsCancelButton.boolValue

    

    func searchBarCancelButtonClicked(searchBar: UISearchBar)
        searchBar.resignFirstResponder()
        searchBar.setShowsCancelButton(false, animated: false)
        call_data()


    

    func searchBarSearchButtonClicked(searchBar: UISearchBar)
        searchBar.resignFirstResponder()
    

    func searchBarTextDidEndEditing(searchBar: UISearchBar)

        var empty_array: [Coffee] = []

        for coffee in coffees
            if searchBar.text?.lowercaseString == coffee.name 
                empty_array.append(coffee)
            
        
        self.coffees = empty_array
        self.tableview.reloadData()

        NSLog("\(searchBar.text)")


    



【问题讨论】:

【参考方案1】:

您正在触发对searchBarTextDidEndEditing 的搜索。而是使用searchBar:textDidChange:,每次用户添加或删除字符时都会调用它。

(但是,如果您在这里使用 UISearchController 确实会更好。毕竟,这正是它的用途。)

【讨论】:

以上是关于文本搜索功能的主要内容,如果未能解决你的问题,请参考以下文章

如何使用在html文档中搜索文本的javascript制作功能搜索框[重复]

PyQt文本框搜索功能

在页面上使用文本字符串搜索功能,有没有办法让搜索忽略特定的 div?

在 PGAdmin 中搜索所有功能的文本

仅当搜索栏文本不为空时,如何运行过滤器功能?

如何使用文本框搜索功能返回同一列中的空值和值