Web search dialog that can automatically complete and enter keywords [Python] [Gtk]

out-1_13-100_20140921211605e7d.gif

Earlier, I used Zenity in a shell script to create a simple application to search in firefox ( Convenient regular shell script --web search dialog firefox-search.sh ), but this time it is converted to Python I will introduce the ported one. I made it for studying Gtk.

The overall concept is the same as when it was created with a shell script, but this time we have improved it so that it automatically completes the characters typed by utilizing the history.

Gtk.EntryCompletion

It can be easily implemented by using Gtk.EntryCompletion of gi.repository.

gtk.EntryCompletion: 16.4. EntryCompletion Objects:

However, I found it on Github because it can't translate for each space-separated word.

https://gist.github.com/evoL/1650115

Based on, I changed the rule so that the conversion is performed for each word separated by a space. Thank you evoL. By the way, the 50th line of the above site

 # add the matching word
current_text = "%s %s" % (current_text, model[iter][0])

Needs to be deleted. If there is this, the keyword that is completed by hitting halfway will be input twice.

(If you type "ubu" and select "ubuntu", "ubuntu ubuntu" will be entered.)

Composition etc.

Also, this time, in order to make it more like an application, I tried to divide the files and manage the settings in separate files. As the whole composition

It can be used in various browsers by rewriting config.py here, and you can easily change or add the site specified as an option. \ _ \ _ Init \ _ \ _. Py is just a file needed to handle it as a module, and this time it is empty. It seems better to process the version check here.

Also, I recently learned how to use Git easily, so I gave the code to GitHub.

http://github.com/ssh0/web_search It is OK to clone and fork.

Usability

When you run websearch.py,

Selection_005_20140921203310ded.png

A simple entry box like this appears, and when you enter characters, it behaves like the beginning of an article. If you press the Enter key when the input is completed, the search results will be displayed in a new tab in the browser specified in config.py.

Task

In the future, I would like to add a function that predicts and displays related words from the degree of relevance of words, like a general web search bar. Also, since typos are severe (one of the reasons I made this one), I would like to have a function such as deleting the mistakes. There is also a way to borrow the search engine of Google teacher as it is. Also, it seems that shell functions can be easily implemented. Is it a file search as a usage? You can do the same with the Unity lens, which is the desktop environment of Ubuntu, but it becomes heavy, so it is currently turned off. If it's as simple as running locate, it may be lighter and more comfortable to incorporate it here.

Anyway, the day has come when my search history, which was not useful until now, is useful.

Also, I got the feeling that Gtk could make something. I want to touch various things in the future.

Whole code

Finally, expose the code. As for the explanation, you can understand what you are doing by looking at the comments. Then refer to the official references listed above. I would appreciate any advice or tsukkomi.

websearch.py


#!/usr/bin/env python
# -*- coding:utf-8 -*-
#
# written by ssh0, September 2014
#
# NOTE: bug: This code doesn't run correctly when the browser hasn't run.

from gi.repository import Gtk
import os
import commands
import sys

import websearch.config as config
import websearch.prediction as prediction
logfile = config.logfile


class Window(Gtk.Window):

    def __init__(self):
        self.window = Gtk.Window.__init__(self, title="search")

        # list data for completion from logfile
        lists = []
        with open(logfile) as f:
            for s in f.readlines():
                lists += unicode(s, 'utf-8').split()[1:]
        lists = set(lists)
        liststore = Gtk.ListStore(str)
        for match in lists:
            liststore.append([match])

        self.entry = prediction.EntryMultiCompletion()
        self.entry.completion.set_model(liststore)
        self.entry.completion.set_text_column(0)
        self.entry.completion.set_popup_completion(popup_completion=True)
        self.entry.connect("activate", self.enter_callback)
        self.add(self.entry)

    def enter_callback(self, event):
        # get text from entry widget
        search_term = self.entry.get_text().split()

        # if text is None, do nothing
        if len(search_term) == 0:
            return 0

        # in config.py, site must be dictionary that
        # key is option argument and value is website's address
        site = config.site

        # find option
        option= search_term[0]
        if option in site:
            goto = site[option]
            del search_term[0]
            if len(search_term) == 0:
                return 0
        # if there is no option, go to default site
        else:
            goto = site['default-search']

        # search term are joined mostly with '+'
        if len(search_term) > 1:
            t = ' '.join(search_term)
        else:
            t = search_term[0]

        # save the log to logfile
        date = commands.getoutput('date +%F_%T')
        log =  date + ' ' + t + '\n'
        with open(logfile, 'a') as l:
            l.writelines(log)

        # go to website
        base = config.browser['default']
        goto = goto % t
        os.system(base + '"' + goto + '"')
        sys.exit()


def main():
    win = Window()
    win.connect("delete-event", Gtk.main_quit)
    win.show_all()
    Gtk.main()

if __name__ == '__main__':
    main()

config.py


#!/usr/bin/env python
# -*- coding:utf-8 -*-
#
# written by ssh0, September 2014

logfile = "/home/ssh0/Dropbox/log.txt"

# choose the default browser by comment out
# or you can edit to any browser
browser = {"default":
           "firefox -new-tab "
#           "chromium-browser -new-tab "
#           "google-chrome -new-tab "
#           "opera -newtab "
            ,
           }


site = {
        # default:Google search
        "default-search": r"https://www.google.co.jp/#q=%s",

        # "w": Wikipedia
        "-w": r"https:ja.wikipedia.org/wiki/%s",

        # "n":niconico video
        "-n": r"http://www.nicovideo.jp/search/%s",

        # "p":Google image search
        #"-p": r"https://www.google.com/search?q=%s&um=1&ie=UTF-8&hl=ja&tbm=isch&source=og&sa=N&tab=wi",

        # "y":Search on Youtube
        "-y": r"http://www.youtube.com/results?search_query=%s&sm=3",

        # "rt"Yahoo real-time search
        "-rt": r"http://realtime.search.yahoo.co.jp/search?p=%s&ei=UTF-8",

        # "sc"Google Scholar Search
        "-sc": r"http://scholar.google.co.jp/scholar?q=%s&hl=ja&as_sdt=0,5",

        # "-t":translation
        "-t": r"http://ejje.weblio.jp/content/%s"
        }

prediction.py


#!/usr/bin/env python
# -*- coding:utf-8 -*-
#
# written by ssh0, September 2014
#reference: https://gist.github.com/evoL/1650115

from gi.repository import Gtk
import config
logfile = config.logfile


class EntryMultiCompletion(Gtk.Entry):
    def __init__(self):
        Gtk.Entry.__init__(self)
        self.completion = Gtk.EntryCompletion()

        # customize the matching function to match multiple space
        # separated words
        self.completion.set_match_func(self.match_func, None)

        # handle the match-selected signal, raised when a completion
        # is selected from popup
        self.completion.connect('match-selected', self.on_completion_match)
        self.set_completion(self.completion)

    def match_func(self, completion, key_string, iter, data):
        model = self.completion.get_model()
        modelstr = model[iter][0]

        # check if the user has typed in a space char,
        # get the last word and check if it matches something
        if ' ' in key_string:
            last_word = key_string.split()[-1]
            return modelstr.startswith(last_word)

        # we have only one word typed
        return modelstr.startswith(key_string)

    def on_completion_match(self, completion, model, iter):
        current_text = self.get_text()

        # if more than a word has been typed, we throw away the
        # last one because we want to replace it with the matching word
        # note: the user may have typed only a part of the entire word
        #       and so this step is necessary
        if ' ' in current_text:
            current_text = ' '.join(current_text.split()[:-1])
            print current_text
            current_text = '%s %s' % (current_text, model[iter][0])
            print current_text
        else:
            current_text = model[iter][0]
            print current_text

        # set back the whole text
        self.set_text(current_text)
        # move the cursor at the end
        self.set_position(-1)

        # stop the event propagation
        return True

Postscript

2014/09/24: In websearch.py, if "," etc. are included, it will not work properly, so I added double quotation marks to both ends of the address. With this, the necessary character conversion is performed on the browser side.

Recommended Posts

Web search dialog that can automatically complete and enter keywords [Python] [Gtk]
Automatically search and download YouTube videos with Python
Let's make an app that can search similar images with Python and Flask Part1
Let's make an app that can search similar images with Python and Flask Part2
How to start a simple WEB server that can execute cgi of php and python
[Python] Depth-first search and breadth-first search
Streamline web search with python