文本搜索功能
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制作功能搜索框[重复]