Good sources on general program layout (preferably Python or C++) - c++

So I can do a little C++ console stuff with classes (nothing too fancy I think) and just started with Python (awesome language, it's like C++ without worries) and Tkinter.
The thing is that I don't really have a clue how in general a program with a GUI is structured. I know you have to separate the interface from the internal workings, but that's about it. As an example I am working on a small app that converts Excel tables to LaTeX tables and so far I have this:
from Tkinter import *
class ExcelToLateX:
def __init__(self,master):
self.convert = Button(master,text="Convert",command=self.Conversion)
self.convert.pack(side=BOTTOM,fill=X)
self.input=Input(master,40)
self.output=Output(master,40)
def Conversion(self):
self.output.Write(self.input.Read())
class Input:
def __init__(self,master,x):
self.u=Text(master,width=x)
self.u.pack(side=LEFT)
self.u.insert(1.0,"Paste Excel data here...")
def Read(self):
return self.u.get(1.0,END)
class Output:
def __init__(self,master,x):
self.v=Text(master,width=x)
self.v.pack(side=RIGHT)
self.v.insert(1.0,"LaTeX code")
def Write (self,input):
self.input=input
if self.v.get(1.0,END)=="":
self.v.insert(1.0,self.input)
else:
self.v.delete(1.0,END)
self.v.insert(1.0,self.input)
#Test script
root=Tk()
Window=ExcelToLateX(root)
root.mainloop()
So I have two Text widgets that can read and write stuff and a (for now) empty conversion class that will take Excel tables and spew out LaTeX code. I have no idea if this is the way to go (so any comments/tips are appreciated).
So in short I have two questions:
Are there any widely acknowledged sources that provide information on how a program with a GUI is structured? (preferably Python and Tkinter because that's what I'm doing right know, although it may be a bit more general (cross-language))
Is my current application any good when it comes to structure, and if not, what are some rules of thumb and things I can improve?

I'm just going to throw a couple short comments into the hat. I don't have experience with Tkinter, so my knowledge derives from PyQt4 experience.
Right now you are using composition for your classes, by making the single widget a member attribute. This can obviously work but a useful pattern is to subclass a GUI widget, and then compose the layout by adding more child widgets and parenting to that class. See the examples on this random Tkinter tutorial link I found: http://zetcode.com/tutorials/tkintertutorial/
class Example(Frame):
def __init__(self, parent):
super(Example, self).__init__(parent)
...
And just as a general python convention, you should try and stick with capitalization for your class names, and camelCase or under_score for class instance methods and variables. As you have it, you are using capital for instances (Window =) and methods (Write)
Also, if you aren't going to be subclassing Tkinter widgets, make sure to at least use the new-style classes by subclassing from object: http://realmike.org/blog/2010/07/18/introduction-to-new-style-classes-in-python/
You might also want to nest that last part of your code where you run the event loop inside of the classic python idiom:
if __name__ == "__main__":
root=Tk()
window = ExcelToLateX(root)
root.mainloop()
It prevents your app from immediately being executed if you were to import this module into another application as a library.

Related

Using Metaclasses for self-registering plugins? (Approach help)

I'm trying to write a Python (2.7) library which loads certain classes at runtime. These classes contain a predefined set of methods.
My approach is to define a few Metaclasses which I work with in my library. I would for example define a "Navigation" Metaclass and work with it in the library. Then someone could write a class "Mainmenu" which contains some type of type definition that it is a "Navigation" plugin. And then the Library could use this class.
I am able to load modules and I'm able to write Metaclasses. My problem lies in combining these two things.
First there is the problem that I want the "plugin-classes" to be in a different (configurable) folder. So I can not do:
__metaclass__ = Navigation
because the Navigation class is part of my library and won't be there in the plugin-folder...
How could I solve the Problem of telling the type that the plugin is for? (Navigation, content.... e.g)
EDIT2: I solved the following problem. I found out that I can just ask the module to give me a dict.
My first problem still exists though
EDIT:
I managed registering and loading "normal" classes with a registry up until the following point:
from os import listdir
from os.path import isfile, join
import imp
class State:
registry = {}
def register_class(self,target_class):
self.registry[target_class.__name__] = target_class
print target_class.__name__+" registered!"
def create(self,classname):
tcls = self.registry[classname]
print self.registry[classname]
return tcls()
s = State()
mypath = """C:\metatest\plugins"""
files = [f for f in listdir(mypath) if isfile(join(mypath, f))]
for f in files:
spl = f.split(".")
if spl[1] == "py":
a = imp.load_source(spl[0], mypath + """\\""" + f)
s.register_class(a)
The problem I have at the end now is, that "a" is the loaded module, so it is a module-object. In my case there is only one class in it.
How can I get a Class object from the loaded module, so I can register the class properly??
So - let's check your problem steping back on your current proposal.
You need a way to have plug-ins for a larger system - the larger system won't know about the plug-ins at coding time - but the converse is not true: your plugins should be able to load modules, import base classes and call functions on your larger system.
Unless you really have something so plugable that plug-ins can work with more than one larger system. I doubt so, but if that is the case you need a framework that can register interfaces and retrieve classes and adapter-implementations between different classes. That framework is Zope Interface - you should read the documentation on it here: https://zopeinterface.readthedocs.io/en/latest/
Much more down to earth will be a plug-in system that sacans some preset directories for Python files and import those. As I said above, there is no problem if these files do import base classes (or metaclasses, for the record) on your main system: these are already imported by Python in the running process anyway, their import in the plug-in will just make then show up as available on the plug-in code.
You can use the exact code above, just add a short metaclass to register derived classes from State - you can maketeh convention that each base class for a different plug-in category have the registry attribute:
class RegistryMeta(type):
def __init__(cls, name, bases, namespace):
for base in cls.__mro__:
if 'registry' in base.__dict__:
if cls.__name__ in base.registry:
raise ValueError("Attempting to registrate plug-in with the name {} which is already taken".format(cls.__name__))
base.registry[cls.__name__] = cls
break
super(RegistryMeta, cls).__init__(name, base, namespace)
class State(object):
__metaclass__ = RegistryMeta
registry = {}
...
(keep the code for scanning the directory and loading modules - just switch all directory separation bars to "/" - you still are not doing it right and is subject to surprises by using "\")
and on the plug-in code include:
from mysystem import State
class YourClassCode(State):
...
And finally, as I said in the comment : you should really check the possibility of using Python 3.6 for that. Among other niceties, you could use the __init_subclass__ special method instead of needing a custom metaclass for keeping your registries.

Efficient way to pass gui variables to classes?

I'm using the program Maya to make a rather large project in python. I have numerous options that will be determined by a GUI and input by the user.
One example of an option is what dimensions to render at. However I did not make a GUI yet and am still in the testing faze.
What I ultimately want is a way to have variables be able to be looked up and used by various classes/methods within multiple modules. And also that there be a way that I can test all the code without having an actual GUI.
Should I directly pass all data to each method? My issue with this is if method foo relies on variable A, but method bar needs to call foo, it could get real annoying passing these variables to Foo from everywhere its called.
Another way I saw was passing all variables through to each class instance itself and using instance variables to access. But what if an option changes, then i'd have to put reload imports every time it runs.
For testing what I use now is a module that gets variables from a config file with the variables, and i import that module and use the instance variables throughout the script.
def __init__(self):
# Get and assign all instance variables.
options = config_section_map('Attrs', '%s\\ui_options.ini' %(data_path))
for k, v in options.items():
if v.lower() == 'none':
options[k] = None
self.check_all = int(options['check_all'])
self.control_group = options['control_group']
Does anyone have advice or can point me in the right direction dealing with getting/using ui variables?
If the options list is not overly long and won't change, you can simply set member variables in the class initializer, which makes the initialization easy for readers to understand:
class OptionData(object):
def __init___(self):
#set the options on startup
self.initial_path = "//network"
self.initial_name = "filename"
self.use_hdr = True
# ... etc
If you expect the initializations to change often you can split out the initial values into the constructor for the class:
class OptionData(object):
def __init___(self, path = "//network", name = "filename", hdr=True)
self.initial_path = path
self.initial_name = name
self.use_hdr = hdr
If you need to persist the data, you can fill out the class reading the cfg file as you're doing, or store it in some other way. Persisting makes things harder because you can't guarantee that the user won't open two Maya's at the same time, potentially changing the saved data in unpredictable ways. You can store per-file copies of the data using Maya's fileInfo.
In both of these cases I'd make the actual GUI take the data object (the OptionData or whatever you call yours) as an initializer. That way you can read and write the data from the GUI. Then have the actual functional code read the OptionData:
def perform_render(optiondata):
#.... etc
That way you can run a batch process without the gui at all and the functional code will be none the wiser. The GUI's only job is to be a custom editor for the data object and then to pass it on to the final function in a valid state.

Matplotlib Qt4 GUI programming - replace plt.figure() with OO equivalent

I have an App made using Qt4 Designer which inserts a matplotlib figure into a container widget.
The code to generate the figure comes from another module, obspy:
self.st.plot(fig = self.rawDataPlot)
https://docs.obspy.org/packages/autogen/obspy.core.stream.Stream.plot.html
Normally, this would create and show a matplotlib figure for the st object's data, which is time-series. When the fig parameter is specified this tells self.st.plot to plot to an existing matplotlib figure instance.
The code I have to generate the figure and then position it in my GUI widget is:
def addmpl(self, fig, layout, window): # code to add mpl figure to Qt4 widget
self.canvas = FigureCanvas(fig)
layout.addWidget(self.canvas)
self.canvas.draw()
self.toolbar = NavigationToolbar(self.canvas,
window, coordinates=True)
layout.addWidget(self.toolbar)
self.rawDataPlot = plt.figure() # code to create a mpl figure instance
self.st.plot(fig = self.rawDataPlot) # plot time-series data to existing matplotlib figure instance
self.addmpl(self.rawDataPlot, self.mplvl, self.mplwindow) # add mpl figure to Qt4 widget
What I want to do is instantiate a matplot figure (for use by self.st.plot) but in a way which avoids using plt.figure(), as I have read that this is bad practice when using object-oriented programming.
If I replace plt.figure() with Figure() (from matplotlib.figure.Figure()) I get an error:
AttributeError: 'NoneType' object has no attribute 'draw'
As it stands, the App runs fine if I use plt.figure(), but is there a clean way to avoid using is and is it even necessary for my case?
PS, the code snippets here are taken from a larger source, but I think it gets the point across..
In principle both methods should work.
Whether you set self.rawDataPlot = plt.figure() or self.rawDataPlot = Figure() does not make a huge difference, assuming the imports are correct.
So the error is most probably triggered within the self.st.plot() function. (In general, if you report errors, append the traceback.)
Looking at the source of obspy.core.stream.Stream.plot there is a keyword argument
:param draw: If True, the figure canvas is explicitly re-drawn, which
ensures that existing figures are fresh. It makes no difference
for figures that are not yet visible.
Defaults to True.
That means that apparently the plot function tries to draw the canvas, which in the case of providing a Figure() hasn't yet been set.
A good guess would therfore be to call
self.rawDataPlot = Figure()
self.st.plot(fig = self.rawDataPlot, draw=False)
and see if the problem persists.

How to write tests for main() function that does not return anything

I'm trying to test the code in the main() but I'm a bit unsure how to go about it since I'm not passing any arguments or even returning anything. For the purposes of the example I've shortened the tree statements in the function..
Can anyone point me in the right direction on how to test the logic below? Also I did google to see if this question had already been asked but I couldn't find it, so if it was apologies did not mean to ask again.
script.py
from . import settings
def main():
if settings.PATHS: # list containing full paths to a directory of files
paths = settings.PATHS
for path in paths:
data = read_file(path)
modified_data = do_something_with_the_data_collected(data)
write_to_new_file(modified_data)
else:
logger.warning("There are no files in {}".format(settings.FILES_DIRECTORY))
if __name__ == '__main__':
main()
tests/file_tests.py
import unittest
from module.script import main
class FileManagerTests(unittest.TestCase):
def test_main_func(self):
main() # ?? this is where I am stuck, should I just test
# that it logs correctly if certain data exists
# in settings file?
if __name__ == '__main__':
unittest.main()
Your function has inputs - but they are not arguments. For example, settings.PATHS obviously is an input. You are receiving more inputs via read_file etc. Moreover, you also have outputs, namely the data you pass to write_to_new_file. You will have to find ways to influence the input data and to observe the output data.
I recommend to read a bit about test doubles (stubs, mocks and the like) and how to use them in unit-testing. This will help you to deal with the dependencies in your code while testing.
And, you can make your life a lot easier if you also learn a bit about designing code for testability. This can help you to improve the design to remove dependencies or make them easier to handle.

Auto-save function implementation with Python and Tkinter

This might be a general question. I'm modifying a Python code wrote by former colleague. The main purpose of the code is
Read some file from local
Pop out a GUI to do some modification
Save the file to local
The GUI is wrote with Python and Tkinter. I'm not very familiar with Tkinter actually. Right now, I want to implement an auto-save function, which runs alongside Tkinter's mainloop(), and save modified files automatically for every 5 minutes. I think I will need a second thread to do this. But I'm not sure how. Any ideas or examples will be much appreciated!! Thanks
Just like the comment says, use 'after' recursion.
import Tkinter
root = Tkinter.Tk()
def autosave():
# do something you want
root.after(60000 * 5, autosave) # time in milliseconds
autosave()
root.mainloop()
Threaded solution is possible too:
import threading
import time
import Tkinter
root = Tkinter.Tk()
def autosave():
while True:
# do something you want
time.sleep(60 * 5)
saver = threading.Thread(target=autosave)
saver.start()
root.mainloop()
before leaving I use sys.exit() to kill all running threads and gui. Not sure is it proper way to do it or not.