Skip to content

Commit

Permalink
A new search engine for kanjis and primitives. A comma-separated list…
Browse files Browse the repository at this point in the history
… of search terms can be used and best matching results are returned. Database is searched for following fields: Keywords, primitive names, meanings, Heisig stories/comments and Koohi stories, radicals and kanji readings (in this order). Exact matches are prioritized for partials.

User can also search for multiple occurrences of a search term (e.g. '2*mouth' or 'mouth*2')

A new search bar is added into Lookup window, giving real-time search results. Click on the result bubble to view the corresponding item or press Enter to immediately select the first item.
  • Loading branch information
mjuhanne committed Oct 27, 2023
1 parent c07f6db commit 09b077a
Show file tree
Hide file tree
Showing 5 changed files with 536 additions and 1 deletion.
4 changes: 3 additions & 1 deletion addon/kanji.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from . import config
from . import text_parser
from .kanji_confirm_dialog import KanjiConfirmDialog

from .search_engine import SearchEngine

kanji_db_path = addon_path("kanji.db")
user_db_path = user_path("user.db")
Expand Down Expand Up @@ -100,6 +100,8 @@ def initialize(self):
except sqlite3.OperationalError:
pass

self.search_engine = SearchEngine(self.crs)

# Close db
def shutdown(self):
self.crs.close()
Expand Down
4 changes: 4 additions & 0 deletions addon/lookup_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from . import fonts
from . import config

from .power_search_bar import PowerSearchBar

key_sequence = QKeySequence("Ctrl+Shift+K")
key_sequence_txt = key_sequence.toString(QKeySequence.SequenceFormat.NativeText)
Expand Down Expand Up @@ -51,6 +52,8 @@ def __init__(self, parent=None):
search_btn.clicked.connect(self.on_search_submit)
search_lyt.addWidget(search_btn)

self.power_search_bar = PowerSearchBar(search_lyt, lyt, 20, search_btn.sizeHint().height()*1.5, self.search)

self.keep_tab_on_search_box = QCheckBox("Keep tabs open")
self.keep_tab_on_search_box.setChecked(False)
self.keep_tab_on_search_box.setFocusPolicy(Qt.FocusPolicy.NoFocus)
Expand Down Expand Up @@ -168,6 +171,7 @@ def on_bridge_cmd(self, cmd):
if not handle_bridge_action(cmd, lookup_window=self):
print("Unhandled bridge command:", cmd)


def on_search_submit(self):
text = self.search_bar.text()
self.search(text)
Expand Down
123 changes: 123 additions & 0 deletions addon/power_search_bar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
from . import util
import aqt
from aqt.qt import *


class ResultsBar():

def __init__(self, layout, max_results, result_button_size, on_select_result_function, hide_empty_buttons=True):
self.hide_empty_buttons = hide_empty_buttons
self.on_select_result_function = on_select_result_function
self.max_results = max_results
self.button_size = result_button_size

# search result buttons
self.buttons_lyt = QHBoxLayout()

layout.addLayout(self.buttons_lyt)
self.buttons_lyt.setSpacing(3)
self.buttons_lyt.setAlignment(Qt.AlignmentFlag.AlignLeft)
self.buttons = []

if aqt.theme.theme_manager.night_mode:
btn_style_sheet = \
"color: #e9e9e9;" \
"background-color: #454545;"
else:
btn_style_sheet = \
"color: #202020;" \
"background-color: #e9e9e9;"

btn_style_sheet += \
"font-size: 20px;" \
"border-style: ridge;" \
"border-width: 2px;" \
"border-radius: 6px;" \
"padding: 2px;"

for i in range(max_results):
result_btn = QPushButton(' ', objectName='result_btn_' + str(i+1))
result_btn.setStyleSheet(btn_style_sheet)
result_btn.setFixedWidth(result_button_size)
result_btn.setFixedHeight(result_button_size)
result_btn.setFocusPolicy(Qt.FocusPolicy.NoFocus)
result_btn.clicked.connect(lambda state, x=result_btn: self.on_button_click(x))
result_btn.character = None
if self.hide_empty_buttons:
result_btn.setVisible(False)
else:
result_btn.setText('')
self.buttons_lyt.addWidget(result_btn)
self.buttons.append(result_btn)


def set_contents(self, contents):
idx = 0
for r in contents:
if idx < self.max_results:
btn = self.buttons[idx]
btn.setVisible(True)
if r[0] == '[':
# [primitive] tag -> convert to image
img = r[1:-1]
path = util.addon_path('primitives','%s.svg' % img)
btn.setText('')
icon_size = int(self.button_size*0.8)
icon = QIcon(path)
btn.setIcon(icon)
btn.setIconSize(QSize(icon_size,icon_size))
else:
# normal unicode kanji character
btn.setText(r)
btn.setIcon(QIcon())
btn.character = r
idx += 1
for i in range(idx,self.max_results):
# clear/hide rest of the buttons
btn = self.buttons[i]
btn.character = None
if self.hide_empty_buttons:
btn.setVisible(False)
else:
btn.setText('')
btn.setIcon(QIcon())


def on_button_click(self, button):
text = button.text()
if button.character is not None:
self.on_select_result_function(button.character)



class PowerSearchBar():

def __init__(self, search_bar_layout, search_results_layout, max_results, result_button_width, on_select_result_function, hide_empty_result_buttons=True):

self.on_select_result_function = on_select_result_function
self.max_results = max_results

# search bar
self.input_bar = QLineEdit()
self.input_bar.setPlaceholderText("")
self.input_bar.returnPressed.connect(self.on_power_search_submit)
self.input_bar.textChanged.connect(self.on_power_search_changed)
search_bar_layout.addWidget(self.input_bar)

self.results_bar = ResultsBar(search_results_layout, max_results, result_button_width, on_select_result_function, hide_empty_result_buttons)


def on_power_search_changed(self):
text = self.input_bar.text()
result = aqt.mw.migaku_kanji_db.search_engine.search(text, self.max_results)
self.results_bar.set_contents(result)

def on_power_search_submit(self):
# do not do any additional searches but just select the first match
btn = self.results_bar.buttons[0]
if btn.character is not None:
self.on_select_result_function(btn.character)

def clear(self):
self.input_bar.setText("")
self.results_bar.set_contents([])
Loading

0 comments on commit 09b077a

Please sign in to comment.