Wednesday, July 8, 2015

Small Nifty Dictionary in Python

We all have to look up words in a dictionary at some point or other. Whatever the reason it might be --  maybe because their jobs are text intensive, maybe because they a non-native speaker looking for the right pronunciation dictionaries are indispensible. There used to be some really good dictionary apps 6 years back. Now with google acting as the dictionary, a stand-alone dictionary app is kind of redundant. However these quick visits to google.com can slowly eat into our productive time. So I thought it will behelpful if a small little app sits in the corner of my screen not using any space which can act as a dictionary. So I wrote this app. The full app can fetch word meanings, IPA pronunciation guide and the actual pronunciation audio. The following code snippet implements the pronunciation part only.

I have written this to work exclusively in Linux -- the Python code calls a Bash script to grab the pronunciation files from the internet. The choice of bash script is to perform data manipulation using sed and grep and avoid long lines of Python codes. The following softwares needs to be pre-installed in the Linux box before the script can work -- Lynx, , mplayer, Python, wget. The last two will be installed by default in most Linux distros.

Install Lynx in Debian as

sudo apt-get install lynx

and in Red Hat systems as

yum -y install lynx

Install mplayer in Debian using sudo apt-get and in Red Hat systems with yum -y.


sudo apt-get install mplayer mplayer-gui

 Install the codecs needed to play mp3 files

https://help.ubuntu.com/community/RestrictedFormats

If you are reading this then probably the chances are that you already know a little bit of coding and a little bit about the Linux command-line. So you can modify the code to suit your needs. That means replacing mplayer by your choice of player or using a different online dictionary than google (I use google.com). Or if you are not feeling adventurous then just copy paste the codes in the relevant files, save the files in the same directory and run ./pronunciation.py. You will have a small dictionary app sitting in a small corner of your desktop not taking up any space. Now for the actual part -- The Code.

The app is broken into three parts -- 1. The graphical user interface (GUI) 2. The script which fetches data online. 3. The Python code which runs the GUI and loads the script in the background.

1 The GUI


The GUI is a GLADE file in XML format. Save it as pronunciation.glade. You can save it with a different filename if you want. You will need to modify the Gtk.Builder() import function to make sure that it imports the correct glade file.

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.16.1 -->
<interface>
  <requires lib="gtk+" version="3.10"/>
  <object class="GtkAboutDialog" id="aboutdialog1">
    <property name="can_focus">False</property>
    <property name="type_hint">dialog</property>
    <property name="program_name">getPronunciation</property>
    <property name="version">6.5.1</property>
    <property name="comments" translatable="yes">This program pronounces words. This is a frontend to the bash script pronounciation.sh (yes intentionally misspelled). You will need that script and lynx installed in order to run this program effectively. The script can be found at the website kheyali.blogpot.com. </property>
    <property name="authors">Saugata Chatterjee (saugata.ch@gmail.com) aka Jones
kheyali.blogspot.com</property>
    <property name="logo_icon_name">help-about</property>
    <property name="license_type">lgpl-2-1</property>
    <child internal-child="vbox">
      <object class="GtkBox" id="aboutdialog-vbox1">
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <property name="spacing">2</property>
        <child internal-child="action_area">
          <object class="GtkButtonBox" id="aboutdialog-action_area1">
            <property name="can_focus">False</property>
            <property name="layout_style">end</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="pack_type">end</property>
            <property name="position">0</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <property name="title" translatable="yes">getPronunciation v6.5</property>
    <property name="window_position">center</property>
    <child>
      <object class="GtkGrid" id="grid1">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <child>
          <object class="GtkEntry" id="entry1">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="placeholder_text" translatable="yes">Enter text here...</property>
            <signal name="activate" handler="play_word" swapped="no"/>
          </object>
          <packing>
            <property name="left_attach">0</property>
            <property name="top_attach">1</property>
            <property name="width">1</property>
            <property name="height">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="button1">
            <property name="label">gtk-media-play</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="use_stock">True</property>
            <signal name="clicked" handler="play_word" swapped="no"/>
          </object>
          <packing>
            <property name="left_attach">1</property>
            <property name="top_attach">1</property>
            <property name="width">1</property>
            <property name="height">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="button2">
            <property name="label" translatable="yes">Exit</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <signal name="clicked" handler="exit_program" swapped="no"/>
          </object>
          <packing>
            <property name="left_attach">2</property>
            <property name="top_attach">2</property>
            <property name="width">1</property>
            <property name="height">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkLabel" id="label2">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="label" translatable="yes">Type word into the box to get the pronunciation. </property>
          </object>
          <packing>
            <property name="left_attach">0</property>
            <property name="top_attach">0</property>
            <property name="width">1</property>
            <property name="height">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton" id="button3">
            <property name="label">gtk-about</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="use_stock">True</property>
            <signal name="clicked" handler="open_about_box" swapped="no"/>
          </object>
          <packing>
            <property name="left_attach">1</property>
            <property name="top_attach">2</property>
            <property name="width">1</property>
            <property name="height">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkLabel" id="errormsg">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
          </object>
          <packing>
            <property name="left_attach">0</property>
            <property name="top_attach">2</property>
            <property name="width">1</property>
            <property name="height">1</property>
          </packing>
        </child>
        <child>
          <placeholder/>
        </child>
        <child>
          <placeholder/>
        </child>
        <child>
          <placeholder/>
        </child>
      </object>
    </child>
  </object>
</interface>


2 The bash script

Save the following script as pronunciation.sh in the same directory as the .glade file and the .py file. If the script is renamed then the Python code needs to be revised to reflect the change. I discuss this in the following section.


#!/bin/bash
# getWordPronunciation v6.1 - downloads mp3 files of word pronunciation. Uses the google dictionary but can be used for any other online dictionary. 
# Needs lynx to work.
# by JOnes ( Sep 22, 2014 )


# UPDATE v6.1: As of the latest version this script no longer is a stand-alone script. This is a background script which can be called from other GUI implementations specifically PyGTK. Instead of feedback to the terminal this script now provided exit status : 
# 1 - Lynx not installed
# 2 - Word not provided
# 3 - Badword

# Fancy colors in the terminal removed. No terminal output. Acts as a backend to a Python frontend.

# Usage : pronounciation.sh <wordlist> 


#echo -e "\n ${cyanf}getWordPronunciation v5.1${reset} - downloads mp3 files of word pronunciation. Uses the google dictionary but can be used for any other online dictionary. \n   by JOnes ( Sep 22, 2014 )"

# check if lynx is installed
if [ `which lynx` == "" ] ; then 
   #echo -e "    Install <lynx> for this script to work.\n\n   Exiting ......\n"
   exit 1
fi

# check if user provided word otherwise use default
if [ "$1" == "" ] ; then
#   echo -e "\n ${redf}${boldon}ERROR${boldoff}${reset} No word provided. \n ${greenf}Usage: getWordPronunciation.sh <word> ${reset}\n Aborting ..."
   exit 2
fi

  word=$1
  badword=0
  #check if mp3 file already exists. Then skip that word.
  if [ -f $word.mp3 ] ; then 
#    echo "Playing ..."
#    mplayer --really-quiet  $word.mp3
    mplayer  $word.mp3
    exit 0
  else
     link="http://ssl.gstatic.com/dictionary/static/sounds/de/0/$word.mp3"
     wget -q $link ||   badword=1
  fi
# if badword=1 then the word does not exist in the dictionary.
if [ "$badword" == "1" ]; then
#   echo "Word doesn't exist in online dictionary."
   exit 3
else
#   echo "Playing ..."
#   mplayer --really-quiet $word.mp3
   mplayer $word.mp3
fi

This script can also work as a stand alone CLI script. Just remove the relevant commented-out lines and it should work. Some system do not work with the mplayer --really-quiet option. If your system does then you can use that line instead.

3 The Python code

The python code can be saved with any filename as long as it ends with a .py extension. However the python code expects the .glade file and the script .sh file to be in the same directory. If they are not in the same directory then the path needs to be modified in the relevant lines. The relevant lines are

status = os.system("./pronunciation.sh $word")

This line runs the bash script pronunciation.sh. It uses the os python library to accomplish the task. In fact any command can be run using this function os.system().


builder.add_from_file("pronunciation.glade") 

This line imports the glade file and links the GUI elements with their actions. Change this to the filename of the glade file in case you saved under a different name. The Python code is given below.


#!/usr/bin/env python 

## Here we imported both Gtk library and the os module. 
from gi.repository import Gtk 
import os 

        
class Handler: 
 def exit_program(self, button):
  Gtk.main_quit()
 
  
# Pronounce the word
 def play_word(self, button):
  word = textbox.get_text()
      
  if word == "":
   errormsgbox.set_label("No word provided")
   return
   
# Here exported the 'word' variable from Python to the 'word' variable in the shell. 
  os.environ["word"] = word.lower() 
  
  status = os.system("./pronunciation.sh $word")
  
  if status == 0:
   errormsgbox.set_label("Word saved as " + word + ".mp3")
  
  if status == 1:
   errormsgbox.set_label("Lynx not installed. Please install Lynx by 'sudo apt-get install lynx' and then use this program.")
  
  if status == 768:
   errormsgbox.set_label("Word doesn't exist in dictionary. Check spelling.")

 def open_about_box(self, button):
  aboutwindow.run()
# to make the close button on the About box to work run() & hide() needs to be used instead of show() & hide()
  aboutwindow.hide()
  

# create a Gtk Builder object 
builder = Gtk.Builder()

# import the GUI from a glade file and attach it to the Gtk object
builder.add_from_file("pronunciation.glade")

# connect the actions of the different elements in the GUI to their corresponding functions which is "Handled" by this Python code
builder.connect_signals(Handler())

# get the About dialog box so that a function can Unhide/Hide it
aboutwindow = builder.get_object("aboutdialog1")

errorbox1 = builder.get_object("dialog-vbox1")

textbox = builder.get_object("entry1")

errormsgbox = builder.get_object("errormsg")


window = builder.get_object("window1")

# connect the delete event to the quit action. Whne the cross button is pressed or quit is chosen from the menu the window delete-event is invoked
window.connect("delete-event", Gtk.main_quit)

window.show_all()

Gtk.main()


The comment in the Python code serves their purpose in explaining what the lines following them are doing. Note: In Python indentation is very important and a copy paste might ruin the indentation. So make sure the file is indented correctly before running it.







No comments:

OK GOOGLE on Samsung Galaxy S7 doesn’t work

To make Ok Google detection work on Galaxy S7 (Galaxy series phones) we need to perform a couple of steps. 1. As long as Samsung S vo...