4
\$\begingroup\$

I am working on a vanilla Python menu system for the Linux console. The idea is to provide as much of the same functionality that a traditional DE provides - basic menus for accomplishing common tasks. Mostly it currently just launches specific packages, for example by default it uses "links2 -g google.com" when the user chooses web browser in the menu.

I feel that the code is ugly and could definitely use some optimization. You will see a lot of code commented out. It relies on directfb for much of its intended purpose; however, it can be used without it as well.

I think overall I am doing pretty good, but it is starting to feel a lot like a bad hack. Any advice on how to perform my functions better would be appreciated. For a specific example, I'd love to have the code search for the objs.pickle file in the current directory, and in its absence, run firstrun(), while skipping over firstrun() and loading the variables in objs.pickle.

I also feel like there must be a better way to store data than pickle.

# A simple menu system for use with TTY to launch various applications
# using framebuffer
#
# FBtui.py
# version 0.1.1
# By Matthew Ruane
# Changelog
# 0.1.1
#
# Added Quick Launch 
# Added Settings to change default quick launch apps
# Added Settings Save Feature Using Pickle

import os
import json
import pickle
import math

global is_savefile  # 0 = false 1 = true
is_savefile = 0     # On first run, fbtui will create one if 0 and set to 1

global quicklaunch_list
quicklaunch_list = []

global browser_cmd
browser_cmd = ''

global browser_cmd_default
browser_cmd_default = "links2 -g google.com"

global filebrowser_cmd
filebrowser_cmd = ''

global filebrowser_cmd_default
filebrowser_cmd_default = "mc"


global mudclient_cmd
mudclient_cmd = ''

global mudclient_cmd_default
mudclient_cmd_default = "tt++"

global textedit_cmd
textedit_cmd = ''

global textedit_cmd_default
textedit_cmd_default = "nano"

# quicklaunch_list 0 = browser 1 = filebrowser 2 = mudclient 3 = textedit 4 = is_savefile

#global quicklaunch_list
#quicklaunch_list = [browser_cmd, filebrowser_cmd, mudclient_cmd, textedit_cmd, is_savefile]
#quicklaunch_list = []

global filename
filename = "fbtui.obj"

def clear():
    """Clear the screen"""
    
    os.system("clear")


def show_settings():
    """ Show current command registrations for Quick Launch """

    print quicklaunch_list
    print
    raw_input("Press Enter")

def load_session():
    """ Loads session variables from fbtui_settings.pickle """

    global browser_cmd, filebrowser_cmd, mudclient_cmd, textedit_cmd, mudclient_cmd, quicklaunch_list

    with open('objs.pickle') as f:
        browser_cmd, filebrowser_cmd, mudclient_cmd, textedit_cmd, is_savefile = pickle.load(f)
        #quicklaunch_list = pickle.load(f)
    
""" 
    filehandler = open('fbtui.py', 'r')
    quicklaunch_list = pickle.load(filehandler)
""" 
    
    


def save_session():
    """ Saves session variables using pickle """

    global browser_cmd, filebrowser_cmd, mudclient_cmd, textedit_cmd, is_savefile, quicklaunch_list
    
    # now saving with json
    # and back to pickle , no more json for now
    
    print "about to write the following to save file:"
    print quicklaunch_list
    raw_input("Press Enter")
    
    with open('objs.pickle', 'w') as f:
        pickle.dump([browser_cmd, filebrowser_cmd, mudclient_cmd, textedit_cmd, is_savefile], f)
        #pickle.dump(quicklaunch_list, f)


""" 
    
    filehandler = open('fbtui.obj', 'w')
    pickle.dump(quicklaunch_list, 'fbtui.obj')
"""
    
        

def quicklaunch_setup():
    """ Quick Launch Setup Wizard """
    global browser_cmd, filebrowser_cmd, mudclient_cmd, textedit_cmd, quicklaunch_list
        
    clear()
    print "Follow the prompts, typing 'default' restores the default values."
    
    x1 = raw_input("Command for Web Browser: ")
#   print x1    
    browser_cmd = x1
    quicklaunch_list.insert(0, browser_cmd)
    
    x2 = raw_input("Command for File Browser: ")
#   print x2
    filebrowser_cmd = x2
    quicklaunch_list.insert(1, filebrowser_cmd)
    
    x3 = raw_input("Command For Mud Client: ")
#   print x3
    mudclient_cmd = x3
    quicklaunch_list.insert(2, mudclient_cmd)
    
    x4 = raw_input("Command for Text Editor: ")
#   print x4
    textedit_cmd = x4
    quicklaunch_list.insert(3, mudclient_cmd)
    

    clear()
    
    print "Browser Command set to: ", browser_cmd
    print "File Browser Command set to: ", filebrowser_cmd
    print "Mud Client Command set to: ", mudclient_cmd
    print "Text Editor Command set to: ", textedit_cmd
    print
    raw_input("Press Enter")
    save_session()
    settingsmenu()

def ask_restore_defaults():
    """ Ask User If They Want To Restore Quick Launch Details """
    global browser_cmd, filebrowser_cmd, mudclient_cmd, textedit_cmd
    clear()
    print "Would you like to restore the Quick Launch defaults?"
    print "Answer 'n' to skip restore and set up Quick Launch."
    print "'q' to return to previous menu."
    
    x = raw_input("Restore Defaults? y/n/q ")
    
    if x == "y":
        browser_cmd = browser_cmd_default
        filebrowser_cmd = filebrowser_cmd_default
        mudclient_cmd = mudclient_cmd_default
        textedit_cmd = textedit_cmd_default
        save_session()
        settingsmenu()
    elif x == "n":
        quicklaunch_setup()
    elif x == "q":
        settingsmenu()

def defaultapps():
    """ Change default apps used for quick launch """
    clear()
    x = raw_input("Begin Quick Launch Setup? y/n: ")
    if x == 'y':
    
        quicklaunch_setup()
        clear()
        raw_input("Quick Launch Setup Complete.")
        settingsmenu()
    else:
        settingsmenu()
    

def packagesinstalled():
    """ Report to user that packageinstall() has finished """

    clear()
    print "Installation Commands Completed."
    print "There May Or May Not Have Been Errors"
    print "Depending on your repo configuration."
    print
    raw_input("PRESS ENTER")
    MainMenu()

def packageinstall():
    """ Install needed packages """

    os.system("sudo apt-get install directfb")
    os.system("sudo apt-get install fbterm")
    os.system("sudo apt-get install links2")
    os.system("sudo apt-get install elinks")
    os.system("sudo apt-get install mc")
    os.system("sudo apt-get install tt++")
    os.system("sudo apt-get install nano")
    os.system("sudo apt-get install pianobar")
    packagesinstalled()


def settingsmenu():
    """ Make Sure Dependencies Are Installed """
    clear()
    print "|++++++++++++++SETTINGS+++++++++++++++++++++++|"
    print "|                                             |"
    print "|   1. Install Needed Packages                |"
    print "|   2. Change Default Applications            |"
    print "|   3. Restore Setting Defaults               |"
    print "|   4. Main Menu                              |"
    print "|+++++++++++++++++++++++++++++++++++++++++++++|"
    print
    print
    
    x = raw_input("Settings Menu: ")
    if x == '1':
        packageinstall()
        settingsmenu()
    elif x == '4':
        MainMenu()
    elif x == '2':
        defaultapps()
    elif x == '3':
        ask_restore_defaults()
    else:
        settingsmenu() 


def internetmenu():
    """ Print List of Internet Using Apps"""
    clear()
    print "|++++++++++++++++INTERNET++MENU++++++++++++++++|"
    print "|  1. Links2 with image support                |"
    print "|  2. Elinks                                   |"
    print "|  3. TinTin Mud Client                        |"
    print "|  4. PianoBar                                 |"
    print "|  ------------------------------------------  |"
    print "|  5. Main Menu                                |"
    print "|++++++++++++++++++++++++++++++++++++++++++++++|"
    print
    print
    print
    x = raw_input("Internet Menu: ")
    
    if x == '1':
        clear()
        os.system('links2 -g google.com')
        internetmenu()
    elif x == '2':
        clear()
        os.system("elinks google.com")
        internetmenu()
    elif x == '3':
        clear()
        os.system('tt++')
        internetmenu()
    elif x == '4':
        clear()
        os.system('pianobar')
        internetmenu()
    elif x == '5':
        MainMenu()
    else:
        internetmenu()

def MainMenu():
    """Print Main Menu and Handle Main Menu  Input"""

    global browser_cmd, filebrowser_cmd, mudclient_cmd, textedit_cmd, quicklaunch_list

    clear() 
    print "                   FBTUI 0.1a                  "
    print "-----------------------------------------------"
    
    print "|++++++++++++++++++MAIN+MENU+++++++++++++++++++|"
    print "|   1. Internet                                |"
    print "|   2. Files                                   |"    # Load Files Apps Menu
    print "|   3. Settings                                |"  # Load Settings Apps Menu
    print "|   4. Apps                                    |"      # Load Apps Menu
    print "|   -----------------------------------------  |"
    print "|   5. Exit                                    |"
    print "|++++++++++++++++++++++++++++++++++++++++++++++|"
    print
    print "Quick Launch:  web   files   mud   text   shell "
    print "               save  load    fix   sshow        "
    print
    x = raw_input("Main Menu: ")

    if x == '1':
        internetmenu()
    elif x == '2':
        # filesmenu()
        clear()
        os.system('mc')
        MainMenu()
    elif x == '3':
        # settingsmenu()
        settingsmenu()
    elif x == '4':
        # appsmenu()
        MainMenu()
    elif x == '5':
        clear()
        exit()
    elif x == "web":
        os.system(browser_cmd)
        MainMenu()
    elif x == "files":
        os.system(filebrowser_cmd)
        MainMenu()
    elif x == "mud":
        os.system(mudclient_cmd)
        MainMenu()
    elif x == "text":
        os.system(textedit_cmd)
        MainMenu()
    elif x == "shell":
        os.system("bash")
        MainMenu()
    elif x == "save":
        save_session()
        MainMenu()
    elif x == "fix":
        firstrun()
        MainMenu()
    elif x == "load":
        load_session()
        MainMenu()
    elif x == "sshow":
        show_settings()
        MainMenu()
    else:
        MainMenu()

def firstrun():
    """ Creates a save file on first run """

    global quicklaunch_list, browser_cmd, filebrowser_cmd, mudclient_cmd, textedit_cmd, is_savefile

    browser_cmd = "links2 -g google.com"
    filebrowser_cmd = "mc"
    mudclient_cmd = "tt++"
    textedit_cmd = "nano"

#   print "First Run Setup Will Now Install Needed Files"
#   print "You may be need to be root."
#   print
#   raw_input("Press Enter")    
#   packageinstall()

    clear()

    print "Browser command set to: ", browser_cmd
    print "File manager command set to: ", filebrowser_cmd
    print "Mud Client command set to: ", mudclient_cmd
    print "Text Editor command set to: ", textedit_cmd
    print
    print "First Run Setup will now create a save file."
    raw_input("Press Enter")
    is_savefile += 1
    save_session()
    print "Save file created."
    raw_input("Press Enter")
    MainMenu()



def check_savefile():
    """ Checks whether or not a save file has been created """
    
    global is_savefile
    
    if is_savefile < 1:
        clear()
        print "This appears to be the first time FBtui is run."
        print "Initializing First Run setup."
        raw_input()
        firstrun()
    else:
        clear()
        print "Save File Found and Loaded."
        raw_input("Press Enter")
        load_session()
        MainMenu()


def Main():
    """ Load Main Menu and Set Up Environment"""
    quicklaunch_list.insert(0, 'links2 -g google.com')
    quicklaunch_list.insert(1, 'mc')
    quicklaunch_list.insert(2, 'tt++')
    quicklaunch_list.insert(3, 'nano')
    # Calls check_savefile and  MainMenu()
    #save_session() 
#   check_savefile()
    #load_session()
    MainMenu()  


# Load Main()

Main()
\$\endgroup\$
0

2 Answers 2

1
\$\begingroup\$

DRY

These lines in the packageinstall function repeat much of the same code:

os.system("sudo apt-get install directfb")
os.system("sudo apt-get install fbterm")
os.system("sudo apt-get install links2")
os.system("sudo apt-get install elinks")
os.system("sudo apt-get install mc")
# etc.

You can eliminate the repetition with a for loop:

for command in ["directfb", "fbterm", ... ]
    os.system(f"sudo apt-get install {command}")

When you repeat the same ending line in both branches of an if/else like this:

if x == 'y':

    quicklaunch_setup()
    clear()
    raw_input("Quick Launch Setup Complete.")
    settingsmenu()
else:
    settingsmenu()

you can just move it after the if/else:

if x == 'y':
    quicklaunch_setup()
    clear()
    raw_input("Quick Launch Setup Complete.")

settingsmenu()

Simpler

This variable:

global is_savefile  # 0 = false 1 = true

could be a boolean. Instead of assigning 0 and 1 to it, assign True/False. Then there is no need for that comment.

I think you can eliminate the intermediate variable x1:

    x1 = raw_input("Command for Web Browser: ")
#   print x1    
    browser_cmd = x1
    quicklaunch_list.insert(0, browser_cmd)

This code is simpler:

    browser_cmd = raw_input("Command for Web Browser: ")
    quicklaunch_list.insert(0, browser_cmd)

The same applies to the other "x" variables in the quicklaunch_setup function.

Global

You have done a great job at partitioning your code into functions. However, it is not a good coding practice to have so many global variables. It is more common to pass values to functions and return values from functions.

Another approach is to use object-oriented programming and create a class.

Logic

Review the if/else statement in the ask_restore_defaults function. If the user enters anything except y/n/q, the code does nothing. Make sure that is the desired behavior. If not, then you could change:

elif x == "q":

to:

else:

Documentation

You've done a good job at adding docstring to the functions. You should also convert the comments at the top of the code to a docstring.

Comments

You will see a lot of code commented out.

Since there is no need for that clutter, it should be removed. This includes code inside docstrings like this:

""" 
    filehandler = open('fbtui.py', 'r')
    quicklaunch_list = pickle.load(filehandler)
""" 

You can store copies of your code in a version control system (like Git) for later retrieval if you ever feel the need to revisit prior code attempts.

Naming

In general, the functions and variables have meaningful names.

I suggest renaming clear as clear_screen.

Portability

I realize this question was posted many years ago when Python version 2.x was prevalent, but now that it is deprecated, consider porting to 3.x.

I'm not sure if the main guard was supported then, but it is common practice now:

if __name__ == '__main__' 
    Main()
\$\endgroup\$
1
\$\begingroup\$

This could be frustrating and frightening to users:

    os.system("sudo apt-get install directfb")
    os.system("sudo apt-get install fbterm")
    os.system("sudo apt-get install links2")
    os.system("sudo apt-get install elinks")
    os.system("sudo apt-get install mc")
    os.system("sudo apt-get install tt++")
    os.system("sudo apt-get install nano")
    os.system("sudo apt-get install pianobar")

Depending on the configuration of sudo, this could ask for the same password eight times. And there's no indication why this program is asking for it.

Suggested replacement:

    print("We need to install necessary packages")
    os.system("sudo apt-get install directfb fbterm links2 elinks mc tt++ nano pianobar")
\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.