We created a very elegant script in Python that efficiently counts words in FASTA sequences and then using a basic statistical method, calculates the significance of each word and output the overrepresented ones.
Our script used a little bit less than 50 lines, and if you include the imported fasta module, it won't top 100. But the number of lines is not important. The efficiency, clarity and speed are key here. At the same time, running a script from the command line is not something everyone is used to do. In order to add more visibility to our simple script, why not including a GUI? With a visual interface, more people can use our script, in different systems. Sounds great.
Python has many options of GUI frameworks, some more cross-platform that others. In the end finding the right framework is more a matter of taste, or availability. My personal experience with wxWidgets lead me to start developing in wxPython, and for me this was a natural choice. But there are many other GUI frameworks for Python, each one providing more or less integration and portability (you can “choose” you own here).
So, let's create a skeleton for our GUI. First step is to install wxPython. Packages for Windows are available from their website, RPMs for Linux and DMG for Macs (I'm quite sure OS X Leopard comes with wxPython by default, just test importing it). After installing it, start Python and check if everything is in place
import wx
wx.__version__
On my machine, I get no errors and the version is 2.8.9.1 (you don't need the latest version to create the GUI). Everything seems to be fine. A wxPython script has the same format as any Python script, the only difference is that its output is not directed to the prompt or a file. The script's product will be the screen, so in most cases the output and program usage will depend on the user's interaction with objects on the screen. Like any other graphical interface. A very simple script would look like
#!/usr/bin/env python import wx class pymot(wx.App): def __init__(self, redirect=False): wx.App.__init__(self, redirect) class pymotGUI(wx.Frame): def __init__(self, parent, id): wx.Frame.__init__(self, parent, id, 'Python Motif Finder', style=wx.DEFAULT_FRAME_STYLE) self.__do_layout() def __do_layout(self): pass app = pymot() frame = pymotGUI(parent=None, id = -1) frame.Show() app.MainLoop()
Usually a wxPython interface has three parts in its script: a class for the window/frame/dialog, a class for the application and a initialization routine. All wxPython applications, and scripts, need to derive an wx.App class and initialize it (on OnInit or on init functions), i.e. create the window, begin the program, etc. Another class, derived from wx.Frame in this case, will build the window/frame/dialog per se and will also contain initialization for the window, objects, events, etc. The last part is the main script where the application is started, by calling the derived class, the window is also called and shown. The last line is the MainLoop, present in every wxPython script, and it is the main line of the script, the heart of the application. MainLoop processes all the events and manages how the objects interact by receiving and dispatching such events.
The script above could have been created differently, some lines of it omitted and there is also no need to derive an specific class for the frame. But this way it is easier to get a grasp of the script as it will need to be enlarged so accomodates the objects and maybe a couple of extra windows and dialogues. Running the above script will generate the window below
very simple and barebones.
Let's take a deeper look on the code we started yesterday, piece by piece
class pymot(wx.App): def __init__(self, redirect=False): wx.App.__init__(self, redirect, filename)
This is the class pymot we derived from wx.App, and this will be the main class for your application. As any other class derived it needs a OnInit or a init function that will take care of initializing things. As usual, we pass self and a redirect parameter, that will tell the application to redirect some output to the command line. We actually don't need a redirect, but it can be useful in the future to track errors. It's set to false as we don't need it now.
class pymotGUI(wx.Frame): def __init__(self, parent, id): wx.Frame.__init__(self, parent, id, 'Python Motif Finder', style=wx.DEFAULT_FRAME_STYLE) self.__do_layout() def __do_layout(self): pass
This is the pymotGUI class derived, in this case, from wx.Frame. a wx.Frame is the common window you see in most OS. As above, it needs a OnInit or init function, and here it initializes the window (but does not show it). In the first line of init we have a call to format the window we want to display. The frame method would need these <a href=“http://www.wxpython.org/docs/api/wx.Frame-class.html”>paramaters</a> to customize the window
__init__(self, parent, id, title, pos, size, style, name)
Both title and style are set by default (not that they cannot be changed) in the frame definition, and whe this is called and properly initialized, other parameters can be passed and/or changed. There is a second defined function in the pymotGUI class, do_layout. This is a personal preference of having all the layout methods for the window grouped in one function. It helps organizing a bit the code and easier to browse and correct it if needed.
Most of the main part of the script could be moved to the wx.App class derivation, but for now, we can keep it there.
app = pymot() frame = pymotGUI(parent=None, id = -1) frame.Show() app.MainLoop()
The first line initializes the application, the second calls and initializes the frame. The method Show makes the window to be displayed. MainLoop we saw last time.
The skeleton of a wxPython script and application is very simple. Now we need to populate our window, create menus, buttons, and specially events.
Looking at the previous screencap it is easy to conclude that our interface needs a lot of work to be ready. First, it has a dark gray background that does not resemble the usual window background (it looks more like a MDI frame). We need to change that. Also, there are no menu bars or menus, or tool bars. It's pretty bare bones, and not exactly good or useful.
There many ways of customizing the look of a window/frame in wxPython, and two of these methods are adding a panel to the frame or adding the so-called sizers. The latter is a difficult method to master, but powerful and very good to customize objects, look and feels of a window. Addin a panel and subsequently adding objects to it is a more laborious process, but easier to understand. We will start by adding the panel to you do_layout function (where most of our changes will happen for now).
Basically, only one line is required:
#adding the panel panel = wx.Panel(self)
That's it, the wx.Panel method only needs one parameter, where the panel is being added to. The name panel is the one that we will be using to access methods and properties associated with the wx.Panel derivation that we just created.
Adding the menu would require a little bit more code. As its predecessor wxWidgets, wxPython divides the menu in subcategories. The menubar is based on wx.Menubar method, the menu itself (File, Edit, etc) is a wx.Menu wehre each of the entries is added. At the end each menu derived from wx.Menu will be added to the menubar. In order case we have to initialize a menubar
#defines the menubar menubar = wx.MenuBar()
and then initialize a menu element, which we will call filemenu and will be labeled File
#file menu filemenu = wx.Menu()
This will only initialize a menu element with the name filemenu, it won't add anything anywhere. In our case from the start, as we didn't do any planning on how our interface would look like (no UML, no case studies, nothing!), we need at least three menu items: one to open/set the foreground sequence file, one to open/set the background sequence file and one to quit the application. So what we are going to do is append these items to the
filemenu
convertmenu = filemenu.Append(-1, 'Select foreground file') seqmenu = filemenu.Append(-1, 'Select background file') sep = filemenu.AppendSeparator() treenooutmenu = filemenu.Append(-1, 'Quit')
that simple. The first two lines and the last one append the items that open/set files. The -1 parameter is an ID, as we saw previously, when no ID is required for our code we use -1, and the second parameter is the label of that menu item. The menu item sep is a separator, keeping apart the file open/set items and the quit element. One final thing is append the derived wx.Menu to the menubar and set it. We accomplish that by
#appends the menu to the menubar and creates it menubar.Append(filemenu, 'File') self.SetMenuBar(menubar)
Line 2 initializes menubar on self, also known as pymotGUI, our main window. Putting everything together our code would look like
#!/usr/bin/env python import wx import pymot import fasta class pymot(wx.App): def __init__(self, redirect=False): wx.App.__init__(self, redirect) class pymotGUI(wx.Frame): def __init__(self, parent, id): wx.Frame.__init__(self, parent, id, 'Python Motif Finder', style=wx.DEFAULT_FRAME_STYLE) self.__do_layout() # self.__do_binding() def __do_layout(self): #adding the panel panel = wx.Panel(self) #defines the menubar menubar = wx.MenuBar() #file menu filemenu = wx.Menu() foreground_menu = filemenu.Append(-1, 'Select foreground file') background_menu = filemenu.Append(-1, 'Select background file') sep = filemenu.AppendSeparator() quit_menu = filemenu.Append(-1, 'Quit') #appends the menu to the menubar and creates it menubar.Append(filemenu, 'File') self.SetMenuBar(menubar) #if __name__ == '__main__': app = pymot() frame = pymotGUI(parent=None, id = -1) #frame.CentreOnScreen() frame.Show() app.MainLoop()
and this would look like the screencap below (on Vista).
We checked how to add a menu to our simple frame. Unfortunately, just adding it won't make the menu useful. In order to do that we need to bind some events to it. As any interface framework, wxPython is governed by events generated by the user, being these events mouse clicks on buttons and menus, objects getting/losing focus, etc. In our case, so far, we evidently need a event called menu event, which will tell the code what path to use when a menu is clicked.
My personal preference for binding an event to menu is to create a separate function to store these procedures, do_binding. But by using this route we would need to change some code in the menu declaration, and to simplify things we will add the menu binding at the end of the do_layout function.
And how we create a binding? In order to bind an object/menu to a function that will contain the executed code after the event is fired up, we need the name of the object/menu, the target function and the menu type. We already know the first and the last, we just need the function name then. Remember that we created the menu last time by using (the menu name were changed in the previous entry - it was some old code that got in the way - my mistake)
foreground_menu = filemenu.Append(-1, 'Select foreground file') background_menu = filemenu.Append(-1, 'Select background file') ... quitmenu = filemenu.Append(-1, 'Quit')
hence our menu names are foreground_menu>, background_menu and quit_menu. Basically a wx.Bind method has this structure
self.Bind(EVENT_TYPE, handler, source)
where the handler is the function and the source is the actual source of the event. Let's say then we want to use function on_foreground every time someone clicks on foreground menu, and on_background every time someone clicks on the background menu. We add a couple of lines to our layout function
self.Bind(wx.EVT_MENU, self.on_foreground, foreground_menu) self.Bind(wx.EVT_MENU, self.on_background, background_menu)
This will tell the code where to go when these items are clicked. If you start the interface, and error will be generated because we still haven't created the event handler functions. We should define them
def on_foreground(self, event): pass def on_background(self, event): pass
Note that these function receive an event parameter, which is the actual event itself. The pass line means that the function is defined but no actual code has been added, so execution can bypass it and do nothing when the function is called. Our complete code would look like
#!/usr/bin/env python import wx import pymot import fasta class pymot(wx.App): def __init__(self, redirect=False): wx.App.__init__(self, redirect) class pymotGUI(wx.Frame): def __init__(self, parent, id): wx.Frame.__init__(self, parent, id, 'Python Motif Finder', style=wx.DEFAULT_FRAME_STYLE) self.__do_layout() def __do_layout(self): #adding the panel panel = wx.Panel(self) #defines the menubar menubar = wx.MenuBar() #file menu filemenu = wx.Menu() foreground_menu = filemenu.Append(-1, 'Select foreground file') background_menu = filemenu.Append(-1, 'Select background file') sep = filemenu.AppendSeparator() quitmenu = filemenu.Append(-1, 'Quit') #appends the menu to the menubar and creates it menubar.Append(filemenu, 'File') self.SetMenuBar(menubar) self.Bind(wx.EVT_MENU, self.on_foreground, foreground_menu) self.Bind(wx.EVT_MENU, self.on_background, background_menu) def on_foreground(self, event): pass def on_background(self, event): pass app = pymot() frame = pymotGUI(parent=None, id = -1) frame.Show() app.MainLoop()
Last time we saw how to bind an interface element to a function. Now we need to make good use of it, and make the script have some actual functionality. First thing we are going to do is to include a label (or static text) on the interface. Remember that initially we added a panel to the frame, so the label should go on the panel. For a label we use a wx.StaticText and has these parameters
(self, parent, id=-1, label=EmptyString, pos=DefaultPosition, size=DefaultSize, style=0, name=StaticTextNameStr)
We don't need all of them, just a couple would be enough. Basically, parent, id, label and pos will do it, as the size would be default and based on the text length we input. We are going to work on our do_layout function and add two labels to the panel on the frame, one for each the fore and background files
self.fore_label = wx.StaticText(panel, -1, 'Select the foreground file', (10, 10)) self.back_label = wx.StaticText(panel, -1, 'Select the background file', (10, 30))
These two lines are very similar, only the label, position and name change. panel is the name of the panel we created previously, -1 is the ID, the string is the actual text that will appear on the label and the values between parentheses are the X, Y coordinates to display them on the frame. In the beginning (or when a size needs to be set) we can add pos= to the label declaration in order to make clearer what the values are setting
self.fore_label = wx.StaticText(panel, -1, 'Select the foreground file', pos=(10, 10))
If we add these two lines and run our script, both labels will be there on the frame, as can be seen in the screencap below.
Now, we need to add some functionality to the menus. The menu items set previously, basically should work by presenting a file open dialog to the user, where he/she can select a file that will be processed later (or immediately). wxPython provides an option of automatically creating a file dialog, by using the wx.FileDialog method. This method requires only one parameter, which is the style of the dialog. The dialog can be of many types, i.e. for opening (single and multiple files) and saving. the dialog call would look like
dialog = wx.FileDialog(self, style=wx.OPEN)
very simple and objective. But just declaring won't make it show up on the screen. We need to actually call the dialog's show method. Usually, most dialogs are modal, requiring some kind of interaction between the user and the dialog before returning to the application that called the dialog. Because of this behaviour we need to use an if clause when showing the dialog, to check what type of result returns from the user/dialog interaction.
if dialog.ShowModal() == wx.ID_OK:
wx.ID_OK is a internal method of wxPython that checks if the user pressed the OK button on the file open dialog. If so, the program will process the code, otherwise it will destroy the dialog and return to the main application (or do something else if we set an elif clause). So, all we need is set, we just need to put things together and add some code when the user selects a file
def on_foreground(self, event): dialog = wx.FileDialog(self, style=wx.OPEN) if dialog.ShowModal() == wx.ID_OK: fore_file = dialog.GetFilename() self.fore_label.SetLabel(forefile)
After the if clause, the script will get the name of the selected file from the dialog and then set the label of our StaticText (label!) with it. Straightforward. We do the same thing for the background file and we have some code going. One last thing, the objects fore_file and back_file are declared on the init function of the frame class, so they are available to the whole frame scope. Our script will look like
#!/usr/bin/env python import wx import pymot import fasta import os class pymot(wx.App): def __init__(self, redirect=False): wx.App.__init__(self, redirect) class pymotGUI(wx.Frame): def __init__(self, parent, id): wx.Frame.__init__(self, parent, id, 'Python Motif Finder', style=wx.DEFAULT_FRAME_STYLE) self.__do_layout() self.fore_file = '' self.back_file = '' def __do_layout(self): #adding the panel panel = wx.Panel(self) #defines the menubar menubar = wx.MenuBar() #file menu filemenu = wx.Menu() foreground_menu = filemenu.Append(-1, 'Select foreground file') background_menu = filemenu.Append(-1, 'Select background file') sep = filemenu.AppendSeparator() quitmenu = filemenu.Append(-1, 'Quit') #appends the menu to the menubar and creates it menubar.Append(filemenu, 'File') self.SetMenuBar(menubar) self.fore_label = wx.StaticText(panel, -1, 'Select the foreground file', (10, 10)) self.back_label = wx.StaticText(panel, -1, 'Select the background file', (10, 30)) self.Bind(wx.EVT_MENU, self.on_foreground, foreground_menu) self.Bind(wx.EVT_MENU, self.on_background, background_menu) def on_foreground(self, event): dialog = wx.FileDialog(self, style=wx.OPEN) if dialog.ShowModal() == wx.ID_OK: fore_file = dialog.GetFilename() self.fore_label.SetLabel(dialog.GetFilename()) def on_background(self, event): dialog = wx.FileDialog(self, style=wx.OPEN) if dialog.ShowModal() == wx.ID_OK: back_file = dialog.GetFilename() self.back_label.SetLabel(dialog.GetFilename()) #if __name__ == '__main__': app = pymot() frame = pymotGUI(parent=None, id = -1) #frame.CentreOnScreen() frame.Show() app.MainLoop()
Last entry we saw how to allow the user to open a file. Now we need to work on this file and store its path so the script can process it later on. After the file is selected on the file menu, the filename is printed on the label. Let's think for a second … If we get only the filename from the dialog, the program won't work, because the file might be located in another directory, partition, you name it. So we need tp get the file's full path. We need to change the lines
back_file = dialog.GetFilename() self.fore_label.SetLabel(dialog.GetFilename())
by
back_file = dialog.GetPath() self.fore_label.SetLabel(back_file)
(do not forget to do the same to the fore_file!).
Let's run the script and check what happens. The frame should look like the one below (with a little stretching for me).
OK, so this is part is solved. As we haven't planned our application from the start, we will spend sometime thinking of the basic functionality that we migth need. So far, we need one input box, where the user can enter the motif width to be searched, and a button to start the process. Fine, let's add the input box. For this we also need an extra label to tell the user what the box is for. Always working on our do_layout function we add two lines
self.one_label = wx.StaticText(panel, -1, 'Motif width', (10,50)) self.motif_width = wx.TextCtrl(panel, -1, '10', (95, 50), (40,18))
Simple as that we have a input box. For the button, one line will suffice
self.run_button = wx.Button(panel, -1, 'Run', (10, 80))
As we can see there is not much difference in any of the declarations, they follow a similar process and the parameters are more or less identical in some of them. Now, we need to bind the button to a function, that we will call run_finder. Remember that binding needs an event type, a target function and an object. This time the event is a button event, but the other two parameters are similar.
self.Bind(wx.EVT_BUTTON, self.run_finder, self.run_button)
and the function, for now will look like
def run_finder(self, event): wx.MessageBox('It should run, eh?')
That's all for today. Our script is growing and the full code is below
#!/usr/bin/env python import wx import pymot import fasta import os class pymot(wx.App): def __init__(self, redirect=False): wx.App.__init__(self, redirect) class pymotGUI(wx.Frame): def __init__(self, parent, id): wx.Frame.__init__(self, parent, id, 'Python Motif Finder', style=wx.DEFAULT_FRAME_STYLE) self.__do_layout() self.fore_file = '' self.back_file = '' def __do_layout(self): #adding the panel panel = wx.Panel(self) #defines the menubar menubar = wx.MenuBar() #file menu filemenu = wx.Menu() foreground_menu = filemenu.Append(-1, 'Select foreground file') background_menu = filemenu.Append(-1, 'Select background file') sep = filemenu.AppendSeparator() quitmenu = filemenu.Append(-1, 'Quit') #appends the menu to the menubar and creates it menubar.Append(filemenu, 'File') self.SetMenuBar(menubar) #input box for motif width, and label self.one_label = wx.StaticText(panel, -1, 'Motif width', (10,50)) self.motif_width = wx.TextCtrl(panel, -1, '10', (95, 50), (40,18)) #run bbutton self.run_button = wx.Button(panel, -1, 'Run', (10, 80)) #labels self.fore_label = wx.StaticText(panel, -1, 'Select the foreground file', (10, 10)) self.back_label = wx.StaticText(panel, -1, 'Select the background file', (10, 30)) #binding the menus to functions self.Bind(wx.EVT_MENU, self.on_foreground, foreground_menu) self.Bind(wx.EVT_MENU, self.on_background, background_menu) self.Bind(wx.EVT_BUTTON, self.run_finder, self.run_button) def on_foreground(self, event): dialog = wx.FileDialog(self, style=wx.OPEN) if dialog.ShowModal() == wx.ID_OK: fore_file = dialog.GetPath() self.fore_label.SetLabel(fore_file) def on_background(self, event): dialog = wx.FileDialog(self, style=wx.OPEN) if dialog.ShowModal() == wx.ID_OK: back_file = dialog.GetPath() self.back_label.SetLabel(back_file) def run_finder(self, event): wx.MessageBox('It should run, eh?') #if __name__ == '__main__': app = pymot() frame = pymotGUI(parent=None, id = -1) #frame.CentreOnScreen() frame.Show() app.MainLoop()
Let's check one line we entered
self.motif_width = wx.TextCtrl(panel, -1, '10', (95, 50), (40,18))
There is something in this line that I did not explain. The third parameter in the test box declaration is '10'. How does this affect our box? That's the default text that will be displayed inside the box as soon as it is created. In our case, 10 is the motif width, and it's the value we consider to be the most common search width.
Another aspect not explained is the run_finder. We added a line
wx.MessageBox('It should run, eh?')
where we declare a wx.MessageBox. What is it? A message box is the usual error/information dialog that you see in most programs. In our case it is very simple, just a warning/reminder that we need to include some code there.
Let's see now how do we connect our GUI to the the pymotif file (I changed the name because of some conflicts with the app name, my bad!). And also how to display the results, in a simpler manner.
Ok, first to connecting the script to the function file, pymotif.py. The file is already imported in our script and we have used it before. We need to find the exact point and which parameters to pass. pytmotif.py is a slightly modified version of your command line script, and the code is below.
#!/usr/bin/env python import fasta import sys from collections import defaultdict def choose(n, k): if 0 <= k <= n: ntok = 1 ktok = 1 for t in xrange(1, min(k, n - k) + 1): ntok *= n ktok *= t n -= 1 return ntok // ktok else: return 0 def get_quorums(seqs, mlen): """ add seq id_no to a set use explicit counter to create seq_no """ quorum = defaultdict(int) for seq in seqs: for n in range(len(seq) - mlen): quorum[seq[n:n + mlen]] += 1 return quorum def calculate_motifs(input_seqs, input_seqs2): input_seqs = fasta.read_seqs(open('celladhesion1000.fa').readlines()) input_seqs2 = fasta.read_seqs(open('celladhesion1000C.fa').readlines()) foreground = get_quorums(input_seqs, 10) background = get_quorums(input_seqs2, 10) N = len(input_seqs) + len(input_seqs2) res_motifs = [] for i in foreground: term1 = choose(background[i], foreground[i]) term2 = choose((N - background[i]), len(input_seqs) - 1) term3 = choose(N, len(input_seqs)) p = (float(term1) * float(term2)) / term3 if 0 < p <= 0.0001: res_motifs.append(i + 't' + str(foreground[i]) + 't' + str(background[i]) + 't' + str(p)) res_motifs.sort() return res_motifs
So, basically the line we are interested is this one
def calculate_motifs(input_seqs, input_seqs2):
We replace the wx.MessageBox line in our run_finder function and use the input files selected by the user as parameters for calculate_motifs, and we are done
def run_finder(self, event): result = pymotif.calculate_motifs(self.fore_file, self.back_file)
Very simple and direct. This should take care of everything except the motif width, what we will see in the next section. We still need a place to write the overrepresented motifs. We can add a text box to the frame, and we do that by adding an extra declaration in our do_layout function. This time we need to add some extra style to the box, so it can show multiple lines and has a scroll bar.
self.results = wx.TextCtrl(panel, -1, '', (150, 50), (200, 100), wx.TE_MULTILINE | wx.TE_AUTO_SCROLL | wx.HSCROLL)
Notice the wx. flags added. MULTILINE allows the box to have multiple lines and the other two turn on the auto scroll and horizontal scroll. Great. And how do we write the results. Notice above that the function that calculates the motifs, returns a list where each item has the motif sequence and the p value, sorted. So the only thing we need to do is to iterate over the list and print each line to the result box. That simple, and we accomplish it by using the WriteText method, that receives as a parameter a string, either literal or a string object. Our run_finder function will have a couple of extra lines
def run_finder(self, event): result = pymotif.calculate_motifs(self.fore_file, self.back_file) for motif in result: self.results.WriteText(motif + 'n')
That will present in a very simplistic way the resulting overrepresented motifs, but it's enough for now. Our GUI script will be
#!/usr/bin/env python import wx import pymot import pymotif import fasta import os class pymot(wx.App): def __init__(self, redirect=False): wx.App.__init__(self, redirect) class pymotGUI(wx.Frame): def __init__(self, parent, id): wx.Frame.__init__(self, parent, id, 'Python Motif Finder', style=wx.DEFAULT_FRAME_STYLE) self.__do_layout() self.fore_file = '' self.back_file = '' def __do_layout(self): #adding the panel panel = wx.Panel(self) #defines the menubar menubar = wx.MenuBar() #file menu filemenu = wx.Menu() foreground_menu = filemenu.Append(-1, 'Select foreground file') background_menu = filemenu.Append(-1, 'Select background file') sep = filemenu.AppendSeparator() quitmenu = filemenu.Append(-1, 'Quit') #appends the menu to the menubar and creates it menubar.Append(filemenu, 'File') self.SetMenuBar(menubar) #input box for motif width, and label self.one_label = wx.StaticText(panel, -1, 'Motif width', (10,50)) self.motif_width = wx.TextCtrl(panel, -1, '10', (95, 50), (40,18)) #result textbox self.results = wx.TextCtrl(panel, -1, '', (150, 50), (200, 100), wx.TE_MULTILINE | wx.TE_AUTO_SCROLL | wx.HSCROLL) #run bbutton self.run_button = wx.Button(panel, -1, 'Run', (10, 80)) #labels self.fore_label = wx.StaticText(panel, -1, 'Select the foreground file', (10, 10)) self.back_label = wx.StaticText(panel, -1, 'Select the background file', (10, 30)) #binding the menus to functions self.Bind(wx.EVT_MENU, self.on_foreground, foreground_menu) self.Bind(wx.EVT_MENU, self.on_background, background_menu) self.Bind(wx.EVT_BUTTON, self.run_finder, self.run_button) def on_foreground(self, event): dialog = wx.FileDialog(self, style=wx.OPEN) if dialog.ShowModal() == wx.ID_OK: fore_file = dialog.GetPath() self.fore_label.SetLabel(fore_file) def on_background(self, event): dialog = wx.FileDialog(self, style=wx.OPEN) if dialog.ShowModal() == wx.ID_OK: back_file = dialog.GetPath() self.back_label.SetLabel(back_file) def run_finder(self, event): result = pymotif.calculate_motifs(self.fore_file, self.back_file) for motif in result: self.results.WriteText(motif + 'n') #wx.MessageBox('It should run, eh?') #if __name__ == '__main__': app = pymot() frame = pymotGUI(parent=None, id = -1) #frame.CentreOnScreen() frame.Show() app.MainLoop()
We can say that this would be our final version of the script. There are many nice wxPython programming resources, and one is a very good book called wxPython in Action, which is co-written by Robin Dunn, the wxPython maintainer. Go check it out.
So for the last entry in this series, we just need to add a couple of changes to our interface and motif finding scripts. Basically on the interface script we need to add a line that gets the value entered (or the default one, if not changed) in the motif width input box. And we can do that by including the line below in the
run_finder
function.
width = self.motif_width.GetValue()
This line tells the script to get the value of the box and assign to the variable width. This method will get whatever is inside the input box and save as a string to the variable assigned. Now, we need to create the structure to actually send this value to the motif finder functions. Last version of our function calculate_motifs received two parameters, we need to add an extra one, and also change the lines that call the function that get the quorums. Basically the first lines of the function will be
def calculate_motifs(input_seqs, input_seqs2, width): print input_seqs, input_seqs2 input_seqs = fasta.read_seqs(open(input_seqs).readlines()) input_seqs2 = fasta.read_seqs(open(input_seqs2).readlines()) foreground = get_quorums(input_seqs, width) background = get_quorums(input_seqs2, width)
And that's it. Our simple interface is ready to prime time. OK, not prime prime time, we didn't add a series of features that will make it useful by everyone. For instance, there is no error control, so someone could enter 'ABC' in the width input box and that value would be sent and an error will occur. Also you can click the run button without any file selected. And we could go on and on. But this is just a primer, and we can build from it.
The code is on Github, so get it there and have fun.