Russ Python Tips and Techniques

= What = These are Tips and Techniques I have found useful in my Python programing User:Russ_hensel. I would be interested in your feedback. Let me know if you think the tip is useful/wrong/something everyone knows. A good set of this material relates to applications with a GUI. They are often not beginner techniques, but neither are they all advanced ones. Just starting Feb 2017 check history to see if progress is being made.

Ones Still To Be Written
Programing Tips
 * when devision by 0 is useful
 * code is multiple files - scratch file
 * msg, print( msg )
 * moving to 3.6 use fstrings
 * example files with functions Python Example Code

= Parameters for Applications =

There are various ways of storing start up parameters for applications. In the old days almost all windows programs had a "ini" file. Linux had/has "config" files. In both cases these are text files that are easily edited with a simple text editor. More recently xml files ( which i tend to hate ) have come into vogue. Finally some applications give you a gui for editing configuration values and then save them any way they want, perhaps in the "registry".

Using a gui is the easiest way for the user in particular the naive user. They are also a lot of work to implement and maintain. So I favor a flat, non-xml file. But what file? For Python my answere is a python.py file that has pretty much one class, Parameters, that is implemented as a singleton ( in what ever way you want to implement singletons ). These are easy to implement, easy to maintain, and very flexible. They can also do clever things like detecting what operating system they are running on and make automatic adjustments. There are no issues of converting type into strings for the file. Comments are easily included. Loading can print the progress of initialization.

I will put a brief sample here soon so you can see what a file looks like.

code comming here

= Logging =

I use the logging class that is part of standard python. I provide a button to view and edit the file. Why invent your own method. Here is some code:

code comming

= Restart = When an application ( here I am thinking about GUI applications ) starts there can be a considerable delay as all the modules are imported. Typically a restart is done after an exception or when parameters are changed. So I include a restart button on the GUI to quickly restart the application. In my case applications are usually started by constructing some application class. So what I do is break up the __init__ method into 2 parts, only the second needs to be run to restart the application. Here is some sample code:

def __init__(self ): """       create the application, its GUI is called self.gui which is         created in restart         """ ...... more code ......

self.restart   # continue in restart

#    def restart(self ): """       use to restart the app without ending it        parameters will be reloaded and the gui rebuilt        call from a button on the GUI         args: zip        ret:  zip ... all sided effects        """ self.no_restarts   += 1 # if we want to keep track if not( self.gui is None ): # determines if a restart or the start # !! # need to shut down other thread self.gui.root.destroy reload( parameters )

self.logger        = None

.... and so on ......

= Complex Tkinker Windows = I found it really easy to get confused in coding Tkinter because it is not a visual interface. So here are some tips:


 * I do a sketch of what I want on paper.
 * The GUI is built in a class.
 * I break the sketch into Tkinter frames each of which will layout into the window in some fairly simple scheme, often the pack layout will work.
 * Each individual frame is built in its own subroutine, it takes the parent frame as an argument and returns the frame that it builds. The returned frame is then placed in its parent.
 * References to the widgets are often not needed once the GUI is built, so local variable are often used. For widgets that need references you can use an instance variable of the class, or in some cases it it useful to store them in a dictionary.
 * Building helper classes for either building the widgets, or placing them or both is often useful.
 * You can see these techniques in my SmartTerminal GUI class.

= Threading with Tkinker =

Tkinker wants to own the processing of the application. If you make a button start a long running operation then the GUI blocks and becomes unresponsive. So you need some sort of threading. Here are some tips.

Polling withing Tkinker
You can poll in the Tkinter event loop and call any function ( no arguments I think ) using a Tkinter function. Use this to run something short. You can only run it once this way. self.gui.root.after( self.parameters.gt_delta_t, self.polling ) # self.gui.root = Tkinter root, .1 = .1 sec, self.polling = name of the function # ( note no which is a function call

To overcome the fact that it only runs once, ask for it again at the end of the function.

def polling( self, ): ..... do something quick

self.gui.root.after( .1, self.polling )

If you function throws an exception it will stop running. Try Except works for me:

def polling( self, ):

try: ..... do something quick except Exception, ex_arg: .... I log the error finally: self.gui.root.after( .1, self.polling )

Use Threading
Use real threading. I inherit from threading.Thread and implement a override of the run method. Use threading mechanisms to "message" back and fourth to the gui, Tkinter, thread. Seems to work fine.

= Calling across Threads =

If you have objects in 2 different threads calling across the threads can be very dangerous. I have worked out a method that I find useful/safe/easy. It does make some assumptions.


 * Both objects/threads have something resembling polling where checks can be made at a convient time to see if the other thread wants attention.

Step 1, Set Up Queues
self.queue_to_helper     = Queue.Queue( 20 )  # send from tkinker mainloop to helper here self.queue_fr_helper     = Queue.Queue( 20 )  # send back from helper thread to tkinker mainloop here

So how does this help making calls across the threads? What I am doing in SmartTerminal ( could change, see the code ) is to always pass a tuple of 3 parts:

( a_string, a_function, another_tuple_of_arguments_to_a_function )

if a_string is "call" then the queue item is for a function call ( other cases not discussed here ). Then the function ( remember functions are first class objects math.sin( x ) call to a function, math sin a function ) is combined with the arguments to make the call: a_function( arguments_to_a_function ). Almost, it will not work because argument_to_a_function is itself a tuple. And even if the tuple has five elements you are only passing one argument, the tuple, to the function. You need to unpack the arguments. How do you do that:

a_function( *another_tuple_of_arguments_to_a_function )

Here is the code with a little more context ( for more see SmartTerminal smart_terminal_helper.HelperThread.polling ) but not actual production code.

.........     ( action, function, function_args ) = self.rec_from_queue if action == "call": function( *function_args ) .........

more coming......

= Starting a Tkinter App = These applications start fine but often remain in the background with their window behind other windows. It would be good to bring it to the top of your windows. This code, may not be the best, seems to work at least on the Windows.

root.attributes("-topmost", True) root.attributes("-topmost", False) # if left to True window always on top

You need to get this to run once after the mainloop is started. You can do this with root.after  Some details left to you.

= Simple Techniques = These are techniques I use that I have not often seen documented.

Create a Local Reference
We often use "dotted" notation to reference non-local variables. If the variable is used several times we may want to create a local reference:

graph_live_time_zero  = self.parameters.graph_live_time_zero if   graph_live_time_zero   = 1: .....    elif  graph_live_time_zero   = 2: .....    else: ...... instead of:

if   self.parameters.graph_live_time_zero = 1: .....    elif  self.parameters.graph_live_time_zero = 2: .....    else: ......

Why:


 * Well Python searches for local references first, they are faster so your code is faster.
 * If something about the non-local reference changes, then changing the code is easier/less error prone.