Philippe Khin
Create a GUI to read Lang8 webscraped posts using Python PyQt
4 December 2016
First diving into making GUI
pyqt
python
lang8

In the previous post,  I've discussed about how to use Python library such as BeautifulSoup and Selenium to fetch all the user's post on Lang8. As a result, I get like more than 1500 posts (the user whom posts were scraped is a regular poster !), where each entry post is stored in a file.

Now, to make it more convenient to read for me, I've decided to use PyQt, a Python GUI library. Here's a quick tutorial I recommend to start with PyQt. It comes with a GUI builder, so you can build a GUI really quickly ! You only have to write the business logic after having a decent GUI.

Here's the final result :

So here's a basic design for reading the entries. And here's the code for the business logic part, i.e. the functions that are called when a button is clicked.

# GUI program to read yamasv's entries from lang8, whose entries were scraped by the program
# in scrapeLang8.py file.
# The lang8progGUI.vbs is a file to launch a .bat file without opening a terminal Windows
# Each entry is stored in a .txt file with a number, the most recent has the greatest number
# pyuic5 -x lang8UI.ui -o lang8UI.py

from PyQt5 import QtCore, QtGui, QtWidgets
from lang8UI import Ui_Dialog
import os, sys
from random import randint


class Lang8UIprogram(Ui_Dialog):

    def __init__(self, dialog):
        Ui_Dialog.__init__(self)
        self.setupUi(dialog)

        # When the GUI is launched, it displays the last entry read,
        # by checking out from the lastReadEntryIndex.txt file,
        # which contains a the entry number.
        self.last_read_entry_index = int(open("lastReadEntryIndex.txt").read())
        last_read_entry_text       = open("yamasvEntries\\%d.txt" % self.last_read_entry_index, \
                                      encoding="utf-8").read()
        self.current_page_index    = self.last_read_entry_index
        self.currentPageDisplay.setText(str(self.current_page_index)+"/"+str(entries_total))
        self.textBrowser.setPlainText(last_read_entry_text)

        self.random_page_index   = randint(1, entries_total)
        random_page_text         = open("yamasvEntries\\%d.txt" % self.random_page_index, \
                                    encoding="utf-8").read()

        self.next_page_index     = self.current_page_index + 1 if self.current_page_index < entries_total \
                                    else self.current_page_index
        next_page_text           = open("yamasvEntries\\%d.txt" % self.next_page_index, \
                                    encoding="utf-8").read()

        self.previous_page_index = self.current_page_index - 1 if self.current_page_index > 1 \
                                    else self.current_page_index
        previous_page_text       = open("yamasvEntries\\%d.txt" % self.previous_page_index, \
                                    encoding="utf-8").read()

        # Describe what function to call, when the specific button is clicked
        self.randomButton.clicked.connect(self.randomPageDisplay(random_page_text))
        self.nextButton.clicked.connect(self.nextPageDisplay(next_page_text))
        self.previousButton.clicked.connect(self.previousPageDisplay(previous_page_text))
        self.pageSearchButton.clicked.connect(self.searchPage)


    def randomPageDisplay(self, *args):
        def randomPage():
            self.random_page_index = randint(1, entries_total)
            self.updateScreenWith(self.random_page_index)
        return randomPage

    def nextPageDisplay(self, *args):
        def nextPage():
            self.next_page_index = self.current_page_index + 1 if self.current_page_index < entries_total \
                                    else self.current_page_index
            self.updateScreenWith(self.next_page_index)
        return nextPage

    def previousPageDisplay(self, *args):
        def previousPage():
            self.previous_page_index = self.current_page_index - 1 if self.current_page_index > 1 \
                                        else self.current_page_index
            self.updateScreenWith(self.previous_page_index)
        return previousPage


    def updateScreenWith (self, page_to_display_index):
        page_to_display_text = open("yamasvEntries\\%d.txt" % page_to_display_index, \
                                encoding="utf-8").read()
        self.textBrowser.setPlainText(page_to_display_text)
        self.current_page_index = page_to_display_index
        self.currentPageDisplay.setText(str(self.current_page_index)+"/"+str(entries_total))
        f = open("lastReadEntryIndex.txt", "w")
        f.write(str(self.current_page_index))
        f.close()

    def searchPage(self, *args):
        if self.pageSearchLine.text().isnumeric() and \
        int(self.pageSearchLine.text()) in range (1, entries_total+1):
            page_to_search_index = int(self.pageSearchLine.text())
            self.updateScreenWith(page_to_search_index)



if __name__ == '__main__':

    entries_total = len(os.listdir("yamasvEntries"))

    app           = QtWidgets.QApplication(sys.argv)
    dialog        = QtWidgets.QDialog()
    prog          = Lang8UIprogram(dialog)
    dialog.show()
    sys.exit(app.exec_())

The code speaks for itself, basically I save the index of the last entry I've read in a file called lastReadEntryIndex.txt.  So that when I close the application, it saves my last read entry and reopen it next time I open the application. So this solved my problem which was to remember which post on which page I've read the last time I visited this user entries page. The search button allows me to search for an entry by the entry number. A future feature I'd like to implement is searching by key words. But for now, it's enough to serve its primary purpose. Pretty handy right !

It's quite handy to have a desktop application to improve my Japanese by reading these entries, but it's even better to have it on my smartphone, so I can read them on the train etc.. !

So on the next post, I'm gonna show you, how to create the same application, but in a simple Android appplication version !

Download the app

And if you are interested, here is the APK file for the simple Android application to read these entries, described in this post.

All credit goes to yamasv, I post the app here with his approval :)

For the complete code, please check my GitHub repository.

© 2020, Philippe Khin