# Copyright (C) 2014-2019, Ariel Vina Rodriguez ( arielvina@yahoo.es )
# distributed under the GNU General Public License, see <http://www.gnu.org/licenses/>.
#
# author Ariel Vina-Rodriguez (qPCR4vir)
# 2014-2019
"""
GUI
===
Implement a GUI that automatically detect available protocols.
"""
__author__ = 'qPCR4vir'
import tkinter
from tkinter.filedialog import askopenfilename
from pathlib import Path
from protocols.registre_protocols import available
GUI4parameters = {} # map { 'protocol class name' : GUI_init_parameters class to use }
av_prot_names = [] # list of "protocol names+: + run names" with the same index as available executables
[docs]class App(tkinter.Frame):
"""
GUI orgaization:
app = App(master = tkinter.Tk()), - tkinter.Frame.__init__(self, master). : new window
- Logo,
- DropBox (OptionMenu) protocol_selection -> protocol_selected(self, value): for the selected create:
GUI_protocol(protocol), - tkinter.Frame.__init__(self, tkinter.Tk()) : new window
- protocol.GUI = self: this is the GUI protocols refer.
- OptionMenu Versions ->
- Buttons: run: "Initialize protocol" -> command=self.run_selected,
quit:"Synthetize script" -> command=self.quit
- Listbox Comments
- self.GUI_init = GUI4parameters[protocol.name](protocol):
populate and update GUI_parameters to check protocol initialization parameters
- self.GUI_parameters = tkinter.Frame(self): (between Versions and buttons), for file IO, #samples, first tip, ect.
- self.GUI_CheckList = tkinter.Frame(self): Headers, reagent_frames, ReplicaFrame . self.master.mainloop()
- self.mainloop()
GUI_init (and derived GUI_init_parameters, GUI_init_pipeline):
populate and update GUI_parameters to check protocol initialization parameters
GUI_init_parameters:
- WorkTable and Output file, First tip
GUI_init_RNAext_parameters: add num_of_samples
GUI_init_pipeline:
- self.ProtcolFrames = list of ProtocolFrame(prot)
- ProtocolFrame(tkinter.Frame) - tkinter.Frame.__init__(self, prot.pipeline.GUI.GUI_CheckList)
- Label protocol.name: entry run_name
- Button run: run.mainloop() -> run_prot: App.GUI_protocol(self.protocol) in a new window
"""
# See: http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/minimal-app.html
# GUI classes to handle initialization parameters of protocol objects
[docs] class GUI_init_pipeline:
""" Show the parameters of the pipeline for review: a list of the protocols to run with run names
"""
[docs] class ProtocolFrame(tkinter.Frame):
[docs] def __init__(self, prot):
self.protocol = prot
if prot.run_name is None: prot.run_name = ""
print ('protocol: ' + prot.name)
tkinter.Frame.__init__(self, prot.pipeline.GUI.GUI_CheckList)
self.grid(sticky=tkinter.N + tkinter.S and tkinter.E)
self.protocol_label = tkinter.Label(self, text=self.protocol.name)
self.protocol_label.grid(row=0, column=0, sticky=tkinter.W + tkinter.E)
self.ProtName = tkinter.StringVar(self)
self.ProtName.set(prot.run_name)
self.RackNameEntry = tkinter.Entry(self, textvariable=self.ProtName)
self.RackNameEntry.grid(row=0, column=1, sticky=tkinter.W)
[docs] def Run(self):
self.runb = tkinter.Button(self, # state=tkinter.DISABLED,
text='run', command=self.run_prot)
self.runb.grid( row=0, column=2)
# self.runb.configure(state='normal')
self.runb.mainloop()
[docs] def run_prot(self):
self.runb.configure(state='disable')
self.protocol.run_name = self.ProtName.get()
print('For pipeline run GUI for protocol: ' + self.protocol.name +' run-named '+ self.protocol.run_name)
App.GUI_protocol(self.protocol)
[docs] def __init__(self, pipeline):
self.pipeline = pipeline # todo ??
print('run GUI_init_pipeline for: ')
self.ProtcolFrames = [App.GUI_init_pipeline.ProtocolFrame(prot) for prot in pipeline.protocols]
[docs] def update_parameters(self):
pass # ??
from EvoScriPy.protocol_steps import Pipeline
GUI4parameters[Pipeline.name]=GUI_init_pipeline
[docs] class GUI_init_parameters: # (uses the tkinter.Frame protocol.GUI.GUI_parameters)
[docs] def __init__(self, protocol):
self.protocol = protocol
# worktable_template_filename
tkinter.Label(self.protocol.GUI.GUI_parameters, text='WorkTable:').grid(row=0, column=0, columnspan=1,
sticky=tkinter.N + tkinter.W)
self.worktable_filename_v = tkinter.StringVar(self.protocol.GUI.GUI_parameters)
self.worktable_filename_v.set(protocol.worktable_template_filename)
tkinter.Entry(self.protocol.GUI.GUI_parameters, textvariable=self.worktable_filename_v).grid(row=0, column=1, columnspan=9,
sticky=tkinter.N + tkinter.E + tkinter.W)
tkinter.Button(self.protocol.GUI.GUI_parameters, text='...', command=self.selet_WT_FN).grid(row=0, column=10)
self.worktable_filename_v.trace("w", self.set_WT_FN)
# output_filename
tkinter.Label(self.protocol.GUI.GUI_parameters, text='Output filename:').grid(row=1, column=0, columnspan=1,
sticky=tkinter.N + tkinter.W)
self.output_filename_v = tkinter.StringVar(self.protocol.GUI.GUI_parameters)
self.output_filename_v.set(protocol.output_filename)
self.output_filename_Entry = tkinter.Entry(self.protocol.GUI.GUI_parameters, textvariable=self.output_filename_v).grid(row=1,
column=1,
columnspan=9,
sticky=tkinter.N + tkinter.E + tkinter.W)
tkinter.Button(self.protocol.GUI.GUI_parameters, text='...', command=self.selet_O_FN).grid(row=1,
column=10)
self.output_filename_v.trace("w", self.set_O_FN)
# First tip (in the first tip rack)
tkinter.Label(self.protocol.GUI.GUI_parameters, text='First tip:').grid(row=2, column=0, columnspan=1,
sticky=tkinter.N + tkinter.W)
self.first_tip_v = tkinter.StringVar(self.protocol.GUI.GUI_parameters)
self.first_tip_v.set(protocol.first_tip)
tkinter.Entry(self.protocol.GUI.GUI_parameters, textvariable=self.first_tip_v).grid(row=2, column=1, columnspan=1,
sticky=tkinter.N + tkinter.E + tkinter.W)
self.first_tip_v.trace("w", self.set_first_tip)
[docs] def set_first_tip(self, *args):
self.protocol.first_tip = self.first_tip_v.get()
if self.protocol.first_tip == "None":
self.protocol.first_tip = None
[docs] def set_WT_FN(self, *args):
self.protocol.worktable_template_filename = self.worktable_filename_v.get()
[docs] def selet_WT_FN(self):
self.worktable_filename_v.set( tkinter.filedialog.askopenfilename(title='Select the WorkTable template') )
[docs] def set_O_FN(self, *args):
self.protocol.output_filename = Path(self.output_filename_v.get())
[docs] def change_O_FN(self, new_O_FN):
print("\nChanging prot name from: " + self.output_filename_v.get())
self.output_filename_v.set(new_O_FN)
print("\nto : " + self.output_filename_v.get())
[docs] def selet_O_FN(self):
self.output_filename_v.set( tkinter.filedialog.asksaveasfilename(title='Select the output filename') )
[docs] def update_parameters(self):
self.set_first_tip()
self.set_WT_FN()
self.set_O_FN()
from EvoScriPy.protocol_steps import Protocol
GUI4parameters[Protocol.name]=GUI_init_parameters
[docs] class GUI_init_RNAext_parameters(GUI_init_parameters):
[docs] def __init__(self, protocol):
App.GUI_init_parameters.__init__(self, protocol)
# Number of Samples
label = "Number of Samples: ({}-{}) ".format(protocol.min_s, protocol.max_s)
tkinter.Label(self.protocol.GUI.GUI_parameters, text=label).grid(row=2, column=3, columnspan=2,
sticky=tkinter.N + tkinter.W)
self.num_of_samples = tkinter.IntVar(self.protocol.GUI.GUI_parameters)
self.num_of_samples.set(self.protocol.num_of_samples)
self.sample_num = tkinter.Spinbox(self.protocol.GUI.GUI_parameters, textvariable=self.num_of_samples,
from_=protocol.min_s, to=protocol.max_s, increment=1,
command=self.read_num_of_samples)
self.sample_num.grid(row=2, column=5, columnspan=1)
[docs] def read_num_of_samples(self, *args):
self.protocol.num_of_samples = self.num_of_samples.get()
print(" --- num_of_samples set to: %d" % (self.protocol.num_of_samples))
[docs] def update_parameters(self):
App.GUI_init_parameters.update_parameters(self)
self.read_num_of_samples()
from protocols.demos.demo_two_mixes.demo_two_mixes import DemoTwoMixes
GUI4parameters[DemoTwoMixes.name] = GUI_init_RNAext_parameters
from protocols.evo100_f.RNAextractionMN_Mag_Vet.RNAextractionMN_Mag_Vet import RNAextr_MN_Vet_Kit
GUI4parameters[RNAextr_MN_Vet_Kit.name]=GUI_init_RNAext_parameters
from protocols.evo100_f.PreKingFisher_RNAextNucleoMag_EtOH80p.PreKingFisher_RNAextNucleoMag_EtOH80p import PreKingFisher_RNAextNucleoMag_EtOH80p
GUI4parameters[PreKingFisher_RNAextNucleoMag_EtOH80p.name]=GUI_init_RNAext_parameters
from protocols.evo100_f.Prefill_plates_VEW1_ElutionBuffer_VEW2.Prefill_plates_VEW1_ElutionBuffer_VEW2 import Prefill_plates_VEW1_ElutionBuffer_VEW2
GUI4parameters[Prefill_plates_VEW1_ElutionBuffer_VEW2.name]=GUI_init_RNAext_parameters
from protocols.evo100_f.Prefill_plates_LysisBuffer.Prefill_plates_LysisBuffer import Prefill_plates_LysisBuffer
GUI4parameters[Prefill_plates_LysisBuffer.name]=GUI_init_RNAext_parameters
from protocols.evo100_f.Prefill_plates_LysisBuffer.Prefill_plates_LysisBuffer_and_ProtKpreMix import Prefill_plates_LysisBuffer_and_ProtKpreMix
GUI4parameters[Prefill_plates_LysisBuffer_and_ProtKpreMix.name]=GUI_init_RNAext_parameters
from protocols.demos.hello_world.hello_world import HelloWorld
GUI4parameters[HelloWorld.name]=GUI_init_RNAext_parameters
[docs] class GUI_protocol(tkinter.Frame):
"""
Implements a GUI for the selected protocol. Each protocol receive a reference to it in .GUI
Opens in a new window. Has:
- GUI_init, to review creation parameters,
- GUI_parameters for check_list prior to actually running the protocol,
- Comments that shows the run.
Alternatively, for Pipelines shows the list of protocols for sequential running
"""
[docs] def __init__(self, protocol):
self.protocol = protocol
self.output_filename = protocol.output_filename
protocol.GUI = self
tkinter.Frame.__init__(self, tkinter.Tk())
self.master.title(protocol.name)
self.grid()
self.selected_version_StrVar = tkinter.StringVar(self ) # variable ? command=lambda v=self: v.setVariant(l)
self.selected_version_StrVar.set(next(iter(self.protocol.versions))) # variable def value
self.version_selection_Menu = tkinter.OptionMenu(self, self.selected_version_StrVar, *self.protocol.versions)
self.version_selection_Menu.grid(row=1, column=0, rowspan=1, columnspan=4, sticky=tkinter.W + tkinter.E)
# initialize parameters
self.GUI_parameters = tkinter.Frame(self)
self.GUI_parameters.grid(row=0, column=4, columnspan=11, rowspan=3)
# run / quit_bt ---------------------
self.run = tkinter.Button(self, text="Initialize the selected protocol",
command=self.run_selected)
self.run.grid(row=0, column=15, columnspan=2)
self.quit_bt = tkinter.Button(self, text="Synthetize the TECAN script", command=self.quit,
state=tkinter.DISABLED)
self.quit_bt.grid(row=1, column=15, columnspan=2)
if isinstance(protocol, App.Pipeline):
pass
else:
# comments: visualize the synthesized script -----------------------
self.yScroll = tkinter.Scrollbar(self, orient=tkinter.VERTICAL)
self.yScroll.grid(row=3, column=16, rowspan=20, sticky=tkinter.N + tkinter.S)
self.xScroll = tkinter.Scrollbar(self, orient=tkinter.HORIZONTAL)
self.xScroll.grid(row=23, column=8, columnspan=8, sticky=tkinter.E + tkinter.W)
self.comments = tkinter.Listbox(self, height=30, width=150,
xscrollcommand=self.xScroll.set,
yscrollcommand=self.yScroll.set)
self.comments.grid(row=3, column=8, rowspan=20, columnspan=8,
sticky=tkinter.N + tkinter.S + tkinter.E + tkinter.W)
self.xScroll['command'] = self.comments.xview
self.yScroll['command'] = self.comments.yview
self.GUI_CheckList = tkinter.Frame(self)
self.GUI_CheckList.grid(row=3, column=0, columnspan=8, rowspan=15)
# show the Parameters with the corresponding GUI_init_parameter
self.GUI_init = GUI4parameters[protocol.name](protocol)
self.mainloop()
[docs] def setVariant(self, variant):
self.selected_version_StrVar.set(variant)
self.protocol.output_filename = Path(self.output_filename + "_" + variant)
self.GUI_init.change_O_FN(self.protocol.output_filename)
[docs] def update_parameters(self):
self.protocol.version = self.selected_version_StrVar.get()
self.protocol.versions[self.protocol.version]()
self.GUI_init.update_parameters() # ??
[docs] class ReplicaFrame(tkinter.Frame):
[docs] def __init__(self, master, reply, num):
tkinter.Frame.__init__(self, master)
self.grid(sticky=tkinter.N + tkinter.S, row=num, column=6, columnspan=3)
self.reply = reply
self.num = num
self.Vol = tkinter.DoubleVar(self)
self.Vol.set(reply.vol)
self.Well = tkinter.IntVar(self)
self.Well.set(reply.offset + 1)
self.CheckB = tkinter.Checkbutton(self, text="Reply" + str(num + 1), justify=tkinter.LEFT,
state=tkinter.DISABLED) # width=15,
self.CheckB.grid(column=0, row=0, )
tkinter.Entry(self, textvariable=self.Well, width=2).grid(row=0, column=1) # , state=tkinter.DISABLED
tkinter.Spinbox(self,
textvariable=self.Vol, state=tkinter.DISABLED,
increment=1,
from_=0.0,
to=100000,
width=7).grid(column=2, row=0)
[docs] class ReagentFrame(tkinter.Frame):
[docs] def __init__(self, check_list, reagent):
tkinter.Frame.__init__(self, check_list.GUI_CheckList)
self.grid(sticky=tkinter.N + tkinter.S and tkinter.E)
self.columnconfigure(0, minsize=140)
self.reagent = reagent
self.RackName = tkinter.StringVar(self)
self.RackName.set(reagent.labware.label)
self.RackGrid = tkinter.IntVar(self)
self.RackGrid.set(reagent.labware.location.grid)
self.RackSite = tkinter.IntVar(self)
self.RackSite.set(reagent.labware.location.site + 1)
self.check_list = check_list
tkinter.Label(self, text=reagent.name, justify=tkinter.RIGHT).grid(row=0, column=0, sticky=tkinter.E)
dis = tkinter.DISABLED if reagent.components else tkinter.NORMAL
self.Vol = tkinter.DoubleVar(self)
self.Vol.set(reagent.volpersample)
tkinter.Spinbox(self,
textvariable=self.Vol, state=dis, command=self.setVol,
increment=1, from_=0.0, to=100000, width=5).grid(row=0, column=1, sticky=tkinter.W)
self.RackNameEntry = tkinter.Entry(self, state=tkinter.DISABLED,
textvariable=self.RackName, width=10).grid(row=0, column=2, padx=5,
sticky=tkinter.W)
self.RackGridEntry = tkinter.Entry(self, state=tkinter.DISABLED,
textvariable=self.RackGrid, width=2).grid(row=0, column=3, padx=5,
sticky=tkinter.W)
self.RackSiteEntry = tkinter.Entry(self, state=tkinter.DISABLED,
textvariable=self.RackSite, width=2).grid(row=0, column=4, padx=5,
sticky=tkinter.W)
self.ReplicaFrames = [App.GUI_protocol.ReplicaFrame(self, reply, rn) for rn, reply in enumerate(reagent.aliquots)]
[docs] def setVol(self, *args):
print("changing volumen of '{0}' from {1} to {2}".format(
self.reagent.name, self.reagent.volpersample, self.Vol.get()))
self.reagent.volpersample = self.Vol.get()
for rf in self.check_list.ReactFrames: # change possibles mix.
for c in rf.react.components:
if self.reagent is c:
rf.react.init_vol()
rf.Vol.set(rf.react.volpersample)
self.reagent.init_vol()
for rf in self.ReplicaFrames: # change num_of_aliquots
rf.Vol.set(rf.reply.vol)
[docs] def check_list(self):
self.GUI_init.update_parameters()
Header=tkinter.Frame(self.GUI_CheckList)
Header.grid( sticky=tkinter.E)
Header.columnconfigure(1, minsize=120)
tkinter.Label (Header, text='Reagent', justify=tkinter.RIGHT).grid(row=0, column=0, sticky=tkinter.E)
tkinter.Label (Header, text=" µL/sample ", ).grid(row=0, column=1 ) # , sticky=tkinter.CENTER
tkinter.Label (Header, text="Rack ", ).grid(row=0, column=2, sticky=tkinter.E)
tkinter.Label (Header, text="Grid", ).grid(row=0, column=3, sticky=tkinter.E)
tkinter.Label (Header, text="Site ", ).grid(row=0, column=4, sticky=tkinter.E)
tkinter.Label (Header, text=' ', ).grid(row=0, column=5, sticky=tkinter.E)
tkinter.Label (Header, text="Well ", ).grid(row=0, column=6, sticky=tkinter.E)
tkinter.Label (Header, text="µL/total", ).grid(row=0, column=7, sticky=tkinter.E)
# todo: add "global protocol variables" like number of samples, worktable template and output files
self.reagent_frames = [App.GUI_protocol.ReagentFrame(self, reagent)
for reagent in self.protocol.worktable.reagents.values()]
# self.GUI_parameters.destroy() # ['state'] = 'disabled'
for child in self.GUI_parameters.winfo_children():
child.configure(state='disable')
self.quit_bt['state'] = 'normal'
self.run['state'] = 'disabled'
self.master.mainloop()
self.quit_bt['text'] = 'Quit'
[docs] def CheckPipeline(self, pipeline):
#for prot, run_name in pipeline.
print ('checking pipeline ' + self.protocol.name)
for GUI_init_prot in self.GUI_init.ProtcolFrames:
GUI_init_prot.run()
[docs] def run_selected(self):
# create and run the protocol
self.GUI_init.update_parameters()
self.protocol.run()
if isinstance(self.protocol, App.Pipeline):
pass
else:
for line in self.protocol.comments():
self.comments.insert(tkinter.END, line)
[docs] def __init__(self, master=None):
tkinter.Frame.__init__(self, master)
self.master.title('RobotEvo')
tkinter.Label(self, padx=10, text='Please, select the protocol for which you want to generate an Evoware script:').grid(row=1, columnspan=3)
# Protocol selection ------------------
global av_prot_names
av_prot_names = [prot.name + (': ' + prot.run_name if prot.run_name else '') for prot in available]
self.selected_protocol = tkinter.StringVar(master) # variable
self.selected_protocol.set(av_prot_names[0]) # variable initial value
self.protocol_selection = tkinter.OptionMenu(self, self.selected_protocol, *av_prot_names, command=self.protocol_selected)
self.protocol_selection.grid(row=2, column=1, rowspan=1, columnspan=3, sticky=tkinter.W + tkinter.E)
self.grid()
#self.protocol_selected(None)
[docs] def protocol_selected(self, value):
selected = self.selected_protocol.get()
print('Selected protocol: ' + value)
App.GUI_protocol(available[ av_prot_names.index(value)])
if __name__ == "__main__":
app = App(tkinter.Tk())
app.master.mainloop()