Wednesday, August 5, 2020

PyQT5 and wxPython Implimentation of ROT13

As stated on this wikipedia page, ROT13 ("rotate by 13 places", sometimes hyphenated ROT-13) is a simple letter substitution cipher (a secret or disguised way of writing) that replaces a letter with the 13th letter after it, in the alphabet.


There is an implimentation in javascript at rot13.com as seen below. Here on this blog we will walk through implimenting it in python desktop GUI (both PyQT5 and wxPython).



Reviewing where rot13 was used in python
The same secret or disguised way of writing is behind the python built-in module "The Zen of Python, by Tim Peters" (import this).

When you run/import the 'this' module for the first time, it display some strings as seen below.

If you access the location of the 'this' module and open it in text editor, you will find a abstract "s" string variable. So where is the beautiful and explicit text above coming from?


s = """Gur Mra bs Clguba, ol Gvz Crgref

Ornhgvshy vf orggre guna htyl.
Rkcyvpvg vf orggre guna vzcyvpvg.
Fvzcyr vf orggre guna pbzcyrk.
Pbzcyrk vf orggre guna pbzcyvpngrq.
Syng vf orggre guna arfgrq.
Fcnefr vf orggre guna qrafr.
Ernqnovyvgl pbhagf.
Fcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.
Nygubhtu cenpgvpnyvgl orngf chevgl.
Reebef fubhyq arire cnff fvyragyl.
Hayrff rkcyvpvgyl fvyraprq.
Va gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.
Gurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.
Nygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.
Abj vf orggre guna arire.
Nygubhtu arire vf bsgra orggre guna *evtug* abj.
Vs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.
Vs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.
Anzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"""

d = {}
for c in (65, 97):
    for i in range(26):
        d[chr(i+c)] = chr((i+13) % 26 + c)

print("".join([d.get(c, c) for c in s]))

It turns out that the module uses the rot13 algorithm to display beautiful text on the front end. This is easily verified by copy the string into rot13.com as shown below.






ROT13 Desktop App
Lets make this a GUI desktop app using python, so we don't have to be online anytime we want to cipher some written text.

First things first, lets get the algorithm to run as a script.

The Rot13 algorithm in python can be written in several ways. For example, in the (import this) module, a nexted 'for loop' was used as seen above.

However, in this guide I will make use of the python string method maketrans() that returns a mapping table for translation usable for translate() method. Read more on this tutorialspoint.com page.

The script should look like this below:-

rot13trans = str.maketrans('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', 'NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm')

raw_text = 'Hello'
rot13_result = raw_text.translate(rot13trans)
rot13_result



A completely different approach use in the "this module" is seen below:-

s = 'Hello'

d = {}
for c in (65, 97):
    for i in range(26):
        d[chr(i+c)] = chr((i+13) % 26 + c)

print ("".join([d.get(c, c) for c in s]))




The GUI

You can find the complete source code on this github page.




That is it!

Friday, July 31, 2020

Mapping African countries open for international tourism

On the 14th of February 2020, the COVID-19 pandemic was confirmed to have spread to Africa, which leads to shutting down of many activities including the 'International Tourism' sector. The first confirmed case was in Egypt, and the first confirmed case in sub-Saharan Africa was in Nigeria. Learn more on this Wikipedia page.

Five months later, a lot has changed, and some of the Africa countries are reopening for tourism activities. Check "Africa Reopening For International Tourism – A-Z List of Countries" for more details.

Given this, I decided to create a map to show the current reopening status (as at the 31st of July, 2020) of each country as either: 'Not Open for Tourism', 'Reopening Soon', or 'Now Open'.

As at the 31st of July, 2020 the Africa international tourism opening status is as follow:-
a) Reopening Soon = 13 countries
b) Not Open For Tourism = 28 countries
c) Now Open = 10 countries

The status of the four countries is unknown, and these countries are Western Sahara, Ivory Coast, Comoros and Swaziland.

See detailed breakdown on the table below...



To prepare the map, we will just link the status above to the map of Africa and the categorize the countries base on the status as seen below.



Stay safe and happy tourism.

Thursday, July 16, 2020

Fix 'wish.exe' cannot be found while running TkInter's GUI builder (PAGE)


This guide will show you how to fix 'wish.exe' cannot be found while running page gui builder for python on windows OS.

When you download PAGE and installed it on windows, you will get this error "Windows cannot find 'wish.exe'. Make sure you typed the name correctly, and then try again.".




This is because PAGE (the Python Automatic GUI Generator) requires Tcl/Tk ActiveTcl package. Tcl/Tk is required for executing PAGE. Tcl/Tk is required because PAGE is written in Tcl/Tk.



The solution
Head over to ActiveTcl page, download and install a copy for your windows OS.


You may be promted to register on the website before you download it, just follow the prompts and download a copy of ActiveTcl. Then run it as usual to install it.



With this out of the way, your PAGE TkInter GUI Generator software should lunch correctly as seen below.




Enjoy!

Tuesday, July 14, 2020

Extracting the Duration of Video file


Few days ago, I was working on a project where I have to lookup the duration of video files and compare the length duration on local disk with the corresponding duration of video online.

So, instead of going between video on local disk and online repeated, I thought what if I could use python to extract all the duration lengths into a table (CSV/Excel) and compare the columns.

Here is how I went about doing it. I made use of 'moviepy' which is a Python module for video editing, which can be used for basic operations (like cuts, concatenations, title insertions).


Installing moviepy
Use: pip install moviepy
This will some other dependent packages such as: decorator, tqdm, urllib3, idna, chardet, requests, proglog, numpy, pillow, imageio, imageio-ffmpeg



To be sure you installation was successful, try to import the module as seen above. If you got not error then your installation was successful and you are good to move on.

The poblem
Here I have some set of videos I downloaded from an online course and I listed their durations in a spreadsheet file as seen below. Now I want to compare each video duration/length to be sure it was downloaded correctly.



The code to extract the video duration to be compared withwhat was obtained online is as below:-

import glob
import datetime
from moviepy.editor import VideoFileClip



folder_path = r'C:\Users\Yusuf_08039508010\Desktop\videos_tut'
videoFiles = glob.glob(folder_path + str('\\*ts'))

# Converts Seconds to: hours, mins, seconds using 'datetime' module
def convert_sec(vid_seconds):
 return str(datetime.timedelta(seconds=vid_seconds))



for v in videoFiles:
    clip = VideoFileClip(v)
    # duration in seconds (create custom function to convert to: H:M:S)
    video_duration = clip.duration

    print (v.split('\\')[-1], convert_sec(int(video_duration)))

Basically, the code uses the 'glob' module to access all the videos in the folder and used the 'datetime' module to convert the video duration in seconds provided by the 'moviepy' module.

The result is a seen below;-


Thank you for reading.

Wednesday, July 8, 2020

PyQT5 and wxPython - Hex Color Spinner

Lets make a 'Hex Color Spinner' desktop app in both PyQT5 and wxPython.

This is going to be a simple desktop window frame that will spin out random hex color and value when a button is pressed.


By working through this app, we will get familiarized with:-
1) creating the GUI structure in both Qt designer and wxformbuilder.
2) connecting the widgets to functions.
3) read and update values on widgets.
4) change window's background color


Hexadecimal color code
A hex color code is made-up of '#' sign followed by six characters which include the combination of numbers between 0-9 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) and letters between A-F ("A", "B", "C", "D", "E", "F"). Notye that the letters can either be in upper or lower case.

So, for example: #F35EEA, #5BDB6B, #9FE091, #8DA7ED, #ADD075 etc will all yeild unique HEX colors.

To come up with such random string in python, there are many possible ways. Below is one way to do it.

# import 'choice' function from the random module...
from random import choice

# define a list of legal HEX characters...
hex = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "A", "B", "C", "D", "E", "F"]

# loop through the list six time while selecting one character per loop...
hex_list = [str(choice(hex)) for x in range(6)]

# join the choosen characters and prefix with the '#' symbol...
hex_string = '#' + ''.join(hex_list)
print(hex_string)

Now that we have a python code that generates the hex color code, we will wrap it in a function to be called within our GUI.


PyQt5 GUI
For the GUI in pyqt, I made use of Qt designer to quickly created it as seen below.


The qt designer GUI consist of a QLable and a QPushButton arranged in a verticalLayot. I made use of verticalSpacer to give space above and belwo the widgets which were arranged vertically.


wxPython GUI
For the GUI in wxPython, I made use of wxFormBuilder to quickly created it as seen below.


The GUI is made off wxStaticText and wxButton arranged in a vertical BoxSizer. A spacer is used to give space above and belwo the widgets.


Running the App

In both cases above, we will generate the GUI file and call it in a main.py file which loads the GUI and then connect the button to the function that runs the hex color code generator above.


PyQt5 code




import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5 import uic

from random import choice


class MyWindow(QMainWindow):
 """docstring for MyWindow"""
 def __init__(self):
  super(MyWindow, self).__init__()
  uic.loadUi('gui_Project.ui', self)

  self.colorButton.clicked.connect(self.change_color_func)

  self.show()
  

 def change_color_func(self):
  # print('#####################')
  hex = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "A", "B", "C", "D", "E", "F"]

  hex_list = [str(choice(hex)) for x in range(6)]

  hex_string = '#' + ''.join(hex_list)
  print(hex_string)

  # ------------------------
  # Change the text of '#XXXXXX' to current hex text
  self.hex_text.setText(hex_string)

  # ------------------------
  # Change background color to current hex color
  self.setStyleSheet("background-color: {};".format(hex_string))


if __name__ == '__main__':
 app = QApplication(sys.argv)
 window = MyWindow()
 sys.exit(app.exec_())




wxpython code




import wx
from gui import MyWindow_1

from random import choice


class MyWindow_2(MyWindow_1):
 def __init__(self, parent):
  MyWindow_1.__init__ (self, parent)
  
 def change_color_func(self, event):
  # print('#####################')
  hex = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "A", "B", "C", "D", "E", "F"]

  hex_list = [str(choice(hex)) for x in range(6)]

  hex_string = '#' + ''.join(hex_list)
  print(hex_string)

  # ---------------
  # Change the text of '#XXXXXX' to current hex text
  self.hex_text.SetLabel(hex_string)
  self.Layout()

  # ------------------------
  # Change background color to current hex color
  self.SetBackgroundColour(hex_string)
  self.Refresh()


app = wx.App(0)
MyWindow_2(None).Show()
app.MainLoop()


The result should look like below....


Download the files from this github repository.

Tuesday, June 23, 2020

Python pandas get length (row count) of a dataframe

There are several ways of knowing the length of your dataframe rows. Here are three ways using index, shape and count methods.

Manually inspecting this dataframe below, we see that it has 10 rows (note the index count starts from 0). Now, let check for this information dynamically.



1) index
Using the index method, it returns the 'RangeIndex' start, stop and step for the dataframe. So, wrapping it in a len() function will give the stop value that is the length of the datafarme as seen below.





2) shape
Using the shape method will return a tuple like so (row, col), where the first element is row and the second element is column.




3) count
Using the count method returns a 'pandas.core.series.Series' that contains number of items for each column.



Enjoy!

Thursday, June 18, 2020

SVG2GeoJSON - SVG file with added Geo-Referencing


Recently, I had a task to convert an SVG image map into GeoJSON format for use in the GIS. This is a complicated thing to do because SVG vector images don't have spatial reference.

Fortunately, I stumbled on this NodeJS app package named svg2geojson written by Prognoz. This library converts an SVG image to a (slightly-incorrect) GeoJSON


To make use of this library, you need to update your SVG map by placing two 'GeoItems' inside a Prognoz MetaInfo element as a direct child of the '<svg>' element at the root of your document.

Now how do you find the X/Y and Longitude/Latitude attributes within the 'GeoItems' for your specific SVG image? This question is what I will attempt to answer in this blog post.

The svg2geojson documentation says: "These map opposing X/Y corners in your SVG coordinate space to Longitude/Latitude coordinates on the world. Note that the SVG coordinate space has Y increasing down (toward the south), while Latitude increases upwards (towards the north)."

That is something like the daigram below:-


Monday, May 25, 2020

No reimporting of module in Python script

Chances are you have been working with imported modules in python. And you usually import the modules from the very top of the script file.

What happen when you import same module twice or more?

The simple answer in nothing! In python when you import a module and you try to import it again in the same instance of a script it won't work!

That is why when you work with modules such as these listed below, it is easy to get confused that the modules work only the first time you ran them.

  • import __hello__
  • import this
  • import antigravity

You will only see the result of their import once.


import __hello__: will print the famous "Hello World!" as an output.




import this: will print the 'Zen of Python'




import antigravity: will open a browser link to python antigravity xkcdwebcomic.




If you try to run any of the above for the second time, none will work.


Obviouly, I don't see any reason why you will reimport a module. But if you do it for whatever reason, then the second import won't work.

Saturday, May 23, 2020

Fixed - Blog broken due to 'SyntaxHighlighter' Error 522

Few days ago, I noticed my blogger.com blog will load forever when you tried to access it.

The so many things started to flow in my mind. Could it be someone hacked my blog? Could it Google's blogger server isn't responding? Or what?

All my preliminary thoughts and checks didn't yield positive result.

Then a new idea pop in my mind to check the developer console whether I would lucky to find clue to what broke my blog. Behold, I saw some errors on about 8 lines pointing to same Javascript file which is not accessible. This Javascript file was from the 'Syntax Highlighter' plugin.



Just in case if you don't know, 'Syntax Highlighter' is a Javascript plugin that adds code highlighting within blog post. It was developed by Alex Gorbatchev.

I remembered, few years ago I added it to allow me highlight code snippets within my blog posts.

At the moment, the server that host the 'Syntax Highlighter' plugin is down with Error 522 which indicates that Cloudflare is unable to reach the origin web server and the request times out.




How I fixed it

I just accessed the theme settings and comment out the lines that reference the 'Syntax Highlighter' plugin.

Go to Theme >> Edit HTML and remove or comment out that has to do with 'Syntax Highlighter'. Note that the CSS and JS files are hosted on alexgorbatchev.com which is currently offline.



Viola! The error is gone and blog is back to live!



That is it!

Wednesday, April 29, 2020

Setting up Django Project and App on NameCheap Shared Hosting using cPanel

It isn't news anymore that many shared host accounts best know for hosting PHP web frameworks now support python web frameworks.

In this post, I will show you how to deploy python django web framework on "NameCheap Shared Hosting" using cPanel.

Django Project is a collection of Apps. A project most have at least one app.

Note: you can deploy django on any cpanel account that:-
1) Supports python apps and 
2) Provides access to 'Terminal/Command-Line'


Step 1: 
I am going to use an existing namecheap account (domain and hosting). If you don’t have account yet, signup for one.

Step 2: 
Login to you cpanel and create a 'subdomain'. Mine is at: https://django1.umaryusuf.com as seen below





At this point, when you load the URL it should display this…



Step 3: Setup Python App
Click on the python icon that says “Setup Python App” (it should be under ‘Software’ tab as seen below).

Click create button that says “Create Application”


Fill the form as seen below… and click on ‘Create’ button.



You should see a screen that look like this below, with a command to enter the virtual environment. Copy the path (source /home/xxx/virtualenv/django1/3.7/bin/activate && cd /home/xxx/django1), you will use it in the next step.


If you checked your “File Manager”, you should see these two folders created from the names you entered above.