Яндекс.переводчик для Linux на Python+GTK3

    Приветствую хабражителей!


    Довольно давно возникла необходимость в мультиязычном онлайн переводчике при закрытом браузере.


    Нет, так то я и словарем в "особо тяжелых" случаях пользоваться не брезгую, но иногда приходится читать немаленький текст, и не все слова я знаю, как следствие теряется контекст.


    image


    Вначале был translate-shell...


    Translate-shell вещь достаточно удобная, особненно, если пишешь, допустим, в Vi/vim. Переключился в соседнюю консоль и переводи.


    Ключи простейшие и запоминаются легко.


    $ trans -b -e yandex -t en "Простые ключи."
    Simple keys.
    или
    $ echo -e "\nС перенаправлением.\n"|trans -b -e yandex -t en
    
    With redirection.

    Но этого как обычно было мало. Захотелось графики.


    Работа в SublimeText и Zeal несколько удобней, нежели голая консоль.


    image


    Хм… Подумал-подумал, и решил прикрутить переводчик на горячую клавишу.
    Выбор пал на gxmessage. Zenity меня не устроил, уж и не помню почему.
    Был написан код:


    #!/bin/bash
    # Name script trans.sh
    TEXT=$(xclip -selection primary -o)
    TRANS=$(trans -e yandex -hl en -tl ru -b "$TEXT")
    if [ "$TRANS" ]; then
    gxmessage -geometry 1000x800 -bg "#aaaaaa" -title "Translate" -wrap -center -font  "Menlo Regular 24" "$TRANS"
    
    else
    gxmessage -geometry 1000x800 -bg "#aaaaaa" -title "Translate" -wrap -center -font  "Menlo Regular 24" "Нет связи с сервисом"
    fi

    И назначен на Ctrl+1. В IceWm это легко. Правим $HOME/.icewm/keys


    key "Ctrl+1" $HOME/progs/trans.sh

    Некоторое время меня это устраивало. На "контрол+1" с английского, "контрол+2" на английский. Хорошо...


    image


    Но тут translate-shell начал давать сбои.


    image


    Не знаю, что произошло у разрабов, но меня это совсем огорчило.
    Я решил, пора, давно на питоне не писал.


    Недолгие поиски в гугл привели меня к Yandex.API, Python и GTK+ 3 в виде модуля gi.


    Почему gi, ведь обычно писал на PyQt5? Он на моей машине работает значительно быстрее. Машина не новая, памяти тоже(по современным меркам) не ахти.


    image


    Модуль GTK+ 3 для python был для меня вновинку. Целый вечер разбирался с синтаксисом на сайтах раз, два и три.


    Так родился код:
    #!/usr/bin/env python3.6
    # -*- coding: utf-8 -*-
    import sys
    import requests
    import gi
    gi.require_version('Gtk', '3.0')
    from gi.repository import Gtk
    from gi.repository import Gdk
    from gi.repository import Pango
    import warnings
    warnings.filterwarnings("ignore")
    import os
    CURRDIR = os.path.dirname(os.path.abspath(__file__))
    ICON = os.path.join(CURRDIR, 'yandex-48.xpm')
    
    headers = {
        'User-Agent': ('Mozilla/5.0 (Windows NT 6.0; rv:14.0) Gecko/20100101 '
                       'Firefox/14.0.1'),
        'Accept':
        'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Language':
        'ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3',
        'Accept-Encoding':
        'gzip, deflate',
        'Connection':
        'keep-alive',
        'DNT':
        '1'
    }
    URLDETECT = "https://translate.yandex.net/api/v1.5/tr.json/detect"
    URLTRANS = "https://translate.yandex.net/api/v1.5/tr.json/translate"
    KEY = "you-API-key"
    
    def clip():
        clipboard = Gtk.Clipboard.get(Gdk.SELECTION_PRIMARY)
        clip = clipboard.wait_for_text()
        return clip
    
    def detect():
        params = {"key": KEY, "text": clip(), "lang": 'ru'}
        respdetect = requests.get(URLDETECT, params=params, headers=headers).json()
        if 'lang' in respdetect.keys():
            respdetect = respdetect
        else:
            respdetect = {'code': 200, 'lang': 'en'}
        langtetect = respdetect["lang"]
        if langtetect != 'ru':
            langout = langtetect + '-ru'
        else:
            langout = 'ru-en'
        return langout
    
    def translate():
        params = {"key": KEY, "text": clip(), "lang": detect()}
        response = requests.get(URLTRANS, params=params, headers=headers).json()
        if 'text' in response.keys():
            response = response
        else:
            response = {'code': 200, 'lang': 'en-ru', 'text': ['the buffer is empty']
            }
        output = ''.join(response["text"])
        return output
    
    class TextViewWindow(Gtk.Window):
        def __init__(self):
            Gtk.Window.__init__(self, title=f"Yandex Translator {detect()}")
            self.set_default_size(1000, 350)
            self.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
            self.grid = Gtk.Grid()
            self.add(self.grid)
            self.create_textview()
            self.create_toolbar()
            self.key_Esc = Gdk.keyval_from_name("Escape")
            self.connect("key-press-event", self._key)
    
        def create_toolbar(self):
            toolbar = Gtk.Toolbar()
            self.grid.attach(toolbar, 1, 1, 1, 1)
            new_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_CLOSE)
            new_button.set_is_important(True)
            toolbar.insert(new_button, 0)
            new_button.connect("clicked", self.on_button_clicked, self.tag_bold)
            new_button.show()
    
        def on_button_clicked(self, widget, tag):
            Gtk.main_quit()
    
        def create_textview(self):
            scrolledwindow = Gtk.ScrolledWindow()
            scrolledwindow.set_hexpand(True)
            scrolledwindow.set_vexpand(True)
            self.grid.attach(scrolledwindow, 0, 0, 2, 1)
            self.textview = Gtk.TextView()
            self.textbuffer = self.textview.get_buffer()
            self.textbuffer.set_text(f"{translate()}")
            scrolledwindow.add(self.textview)
            self.textview.set_wrap_mode(Gtk.WrapMode.WORD)
            self.tag_bold = self.textbuffer.create_tag("bold",
                                                       weight=Pango.Weight.BOLD)
            self.textview.modify_font(Pango.FontDescription('Menlo Regular 24'))
    
        def _key(self, widg, event):
            if event.keyval == self.key_Esc:
                Gtk.main_quit()
    
    win = TextViewWindow()
    win.connect("destroy", Gtk.main_quit)
    win.set_icon_from_file(ICON)
    win.show_all()
    Gtk.main()
    

    В итоге имею автопереводчик на горячей клавише.


    Всем спасибо за внимание.


    Проект полностью на github.


    P.S.: Буду рад критике кода.

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 7

      –2
      :)
        0

        первое что бросилось в глаза… это был вот этот кусок. похожее есть в


            if 'lang' in respdetect.keys():
                respdetect = respdetect
            else:
                respdetect = {'code': 200, 'lang': 'en'}

        Почему бы не заменить на


            if  'lang' not in respdetect.keys():
                respdetect = {'code': 200, 'lang': 'en'}

        во-вторых, не всегда requests.get вернет объект response из которого можно вытащить json. я бы, все таки, проверил что response code == 200 (или requests.codes.ok); или использовал response.raise_for_errors().


        ну и самое интересное — а что произойдет, если clipboard изменится между вызовом detect() и translate()? понимаю, что маловероятно, но это race condition. посоветовал бы сохранять состояние клипборда в переменную, а потом уже детектить язык и переводить.

          0
          Почему бы не заменить на

          Ну в общем то, да. Так лучше.
          посоветовал бы сохранять состояние клипборда в переменную

          А вот насчет этого даже и не знаю. Там же тысячные доли секунды.
          +1
          В скрипте специально оставлен API ключ?
          У него есть лимит бесплатного использования, кроме того любой желающий может получить свой.
            0
            Спасибо. Исправил.
            0
            if langtetect != 'ru':
                    langout = langtetect + '-ru'
                else:
                    langout = 'ru-en'
            

            Зачем так, если можно в одну строку:
               langout = 'ru-' + langtetect
            

            А еще лучше так:
               langout = 'ru-{}'.format(langtetect)
            

            Или даже так:
               langout = f'ru-{langtetect}'
            
              0
              Зачем так, если можно в одну строку

              И будет переводить в одну сторону.

            Only users with full accounts can post comments. Log in, please.