1# Copyright 2000 by Katharine Lindner (Cayte).
2# Copyright 2016 by Markus Piotrowski.
3# All rights reserved.
4# This code is part of the Biopython distribution and governed by its
5# license.  Please see the LICENSE file that should have been included
6# as part of this package.
7#
8# The original SeqGui was written by Katharine Lindner (Cayte) in 2000 using
9# the wxPython library for the GUI. It was rewritten by Markus Piotrowski in
10# 2016 using tkinter and tkinter's themed widgets (ttk).
11"""A small GUI tool to demonstrate some basic sequence operations.
12
13SeqGui (sequence graphical user interface) is a little tool that allows
14transcription, translation and back translation of a sequence that the
15user can type or copy into a text field. For translation the user can select
16from several codon tables which are implemented in Biopython.
17It runs as a standalone application.
18
19"""
20import tkinter as tk
21import tkinter.ttk as ttk
22
23from Bio.Seq import translate, transcribe, back_transcribe
24from Bio.Data import CodonTable
25
26
27main_window = tk.Tk()
28main_window.title("Greetings from Biopython")
29
30menue = tk.Menu(main_window)
31menue_single = tk.Menu(menue, tearoff=0)
32menue.add_cascade(menu=menue_single, label="File")
33menue_single.add_command(label="About")
34menue_single.add_separator()
35menue_single.add_command(label="Exit", command=main_window.destroy)
36main_window.config(menu=menue)
37
38# Left panel with parameters
39param_panel = ttk.Frame(main_window, relief=tk.GROOVE, padding=5)
40
41codon_panel = ttk.LabelFrame(param_panel, text="Codon Tables")
42codon_scroller = ttk.Scrollbar(codon_panel, orient=tk.VERTICAL)
43codon_list = tk.Listbox(
44    codon_panel, height=5, width=25, yscrollcommand=codon_scroller.set
45)
46
47# Import actual codon tables from Biopython and sort alphabetically
48codon_table_list = sorted(
49    table.names[0] for n, table in CodonTable.generic_by_id.items()
50)
51
52# 'Standard' table should be first in the list
53del codon_table_list[codon_table_list.index("Standard")]
54codon_table_list.insert(0, "Standard")
55
56for codon_table in codon_table_list:
57    codon_list.insert(tk.END, codon_table)
58codon_list.selection_set(0)
59codon_list.configure(exportselection=False)
60codon_scroller.config(command=codon_list.yview)
61
62# Radiobuttons are more appropriate than another listbox here:
63transform_panel = ttk.LabelFrame(param_panel, text="Transformation")
64
65transform_var = tk.StringVar()
66transform_transcribe = ttk.Radiobutton(
67    transform_panel, text="Transcribe", variable=transform_var, value="transcribe"
68)
69transform_translate = ttk.Radiobutton(
70    transform_panel, text="Translate", variable=transform_var, value="translate"
71)
72transform_backtranscribe = ttk.Radiobutton(
73    transform_panel,
74    text="Back transcribe",
75    variable=transform_var,
76    value="back transcribe",
77)
78transform_translate.invoke()
79
80# Right panel with sequence in- and output
81seq_panel = ttk.Frame(main_window, relief=tk.GROOVE, padding=5)
82
83input_panel = ttk.LabelFrame(seq_panel, text="Original Sequence")
84input_scroller = ttk.Scrollbar(input_panel, orient=tk.VERTICAL)
85input_text = tk.Text(input_panel, width=39, height=5, yscrollcommand=input_scroller.set)
86input_scroller.config(command=input_text.yview)
87
88output_panel = ttk.LabelFrame(seq_panel, text="Transformed Sequence")
89output_scroller = ttk.Scrollbar(output_panel, orient=tk.VERTICAL)
90output_text = tk.Text(
91    output_panel, width=39, height=5, yscrollcommand=output_scroller.set
92)
93output_scroller.config(command=output_text.yview)
94
95# Buttons
96apply_button = ttk.Button(seq_panel, text="Apply")
97clear_button = ttk.Button(seq_panel, text="Clear")
98close_button = ttk.Button(seq_panel, text="Close", command=main_window.destroy)
99
100
101# Statusbar
102statustext = tk.StringVar()
103statusbar = ttk.Label(main_window, textvariable=statustext, relief=tk.GROOVE, padding=5)
104statustext.set("This is the statusbar")
105sizegrip = ttk.Sizegrip(statusbar)
106
107
108# Event methods
109def clear_output():
110    """Clear the output window."""
111    input_text.delete(1.0, tk.END)
112    output_text.delete(1.0, tk.END)
113
114
115def apply_operation():
116    """Do the selected operation."""
117    codon_table = codon_list.get(codon_list.curselection())
118    print(f"Code: {codon_table}")
119
120    seq = "".join(input_text.get(1.0, tk.END).split())
121    print(f"Input sequence: {seq}")
122
123    operation = transform_var.get()
124    print(f"Operation: {operation}")
125
126    if operation == "transcribe":
127        result = transcribe(seq)
128    elif operation == "translate":
129        result = translate(seq, table=codon_table, to_stop=True)
130    elif operation == "back transcribe":
131        result = back_transcribe(seq)
132    else:
133        result = ""
134
135    output_text.delete(1.0, tk.END)
136    output_text.insert(tk.END, result)
137    print(f"Result: {result}")
138
139
140def set_statusbar(event):
141    """Show statusbar comments from menu selection."""
142    index = main_window.call(event.widget, "index", "active")
143    if index == 0:
144        statustext.set("More information about this program")
145    elif index == 2:
146        statustext.set("Terminate the program")
147    else:
148        statustext.set("This is the statusbar")
149
150
151# Set commands and bind events
152menue_single.bind("<<MenuSelect>>", set_statusbar)
153apply_button.config(command=apply_operation)
154clear_button.config(command=clear_output)
155
156# Build GUI
157statusbar.pack(side=tk.BOTTOM, padx=1, fill=tk.X)
158sizegrip.pack(side=tk.RIGHT, padx=3, pady=4)
159
160param_panel.pack(side=tk.LEFT, anchor=tk.N, padx=5, pady=10, fill=tk.Y)
161codon_panel.pack(fill=tk.Y, expand=True)
162codon_scroller.pack(side=tk.RIGHT, fill=tk.Y)
163codon_list.pack(fill=tk.Y, expand=True)
164transform_panel.pack(pady=10, fill=tk.X)
165transform_transcribe.pack(anchor=tk.W)
166transform_translate.pack(anchor=tk.W)
167transform_backtranscribe.pack(anchor=tk.W)
168
169seq_panel.pack(anchor=tk.N, padx=5, pady=10, fill=tk.BOTH, expand=True)
170input_panel.pack(fill=tk.BOTH, expand=True)
171input_scroller.pack(side=tk.RIGHT, fill=tk.Y)
172input_text.pack(fill=tk.BOTH, expand=True)
173output_panel.pack(pady=10, fill=tk.BOTH, expand=True)
174output_scroller.pack(side=tk.RIGHT, fill=tk.Y)
175output_text.pack(fill=tk.BOTH, expand=True)
176apply_button.pack(side=tk.LEFT)
177clear_button.pack(side=tk.LEFT, padx=10)
178close_button.pack(side=tk.LEFT)
179
180main_window.mainloop()
181