# **************************************************************************
# *
# * Authors: J.M. De la Rosa Trevin (delarosatrevin@scilifelab.se) [1]
# *
# * [1] SciLifeLab, Stockholm University
# *
# * This program is free software: you can redistribute it and/or modify
# * it under the terms of the GNU General Public License as published by
# * the Free Software Foundation, either version 3 of the License, or
# * (at your option) any later version.
# *
# * This program is distributed in the hope that it will be useful,
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# * GNU General Public License for more details.
# *
# * You should have received a copy of the GNU General Public License
# * along with this program. If not, see <https://www.gnu.org/licenses/>.
# *
# * All comments concerning this program package may be sent to the
# * e-mail address 'scipion@cnb.csic.es'
# *
# **************************************************************************
"""
Some basic GUI widgets are implemented in this module.
The widgets here are suppose to be used to build more complex
elements.
"""
import tkinter as tk
import tkinter.ttk as ttk
from math import ceil
import pyworkflow
from pyworkflow import TK_GRAY_DEFAULT, Config
from . import gui
from .tooltip import ToolTip
[docs]class ExplanationText(tk.Text):
"""Create an explanation text box"""
def __init__(self, frame, text='', bg=TK_GRAY_DEFAULT, border=0, wrap='word'):
self.text = tk.Text(frame, bg=bg, wrap=wrap, border=border)
self.updateExpText(text)
[docs] def updateExpText(self, text, width=50):
# Adapt textbox height to text length (width is in characters)
n_lines = ceil(len(text) / width)
self.text.config(state='normal', height=n_lines) # Make it editable
self.text.insert(tk.END, text)
self.text.config(state='disabled') # Disable text edit
[docs]class LabelSlider(ttk.Frame):
""" Create a personalized frame that contains label, slider and label value
it also keeps a variable with the value """
def __init__(self, master, label, from_=0, to=100, value=50, callback=None, step=0.01, length=None,
labelWidth=None, tickinterval=None, showvalue=None):
self.selectedLabelText = '=> {}'.format(label)
self.labelText = ' {}'.format(label)
self.var = tk.DoubleVar()
self.var.set(float(value))
ttk.Frame.__init__(self, master)
self.labelWidget = ttk.Label(self, text=self.labelText, width=labelWidth)
self.slider = tk.Scale(self, from_=from_, to=to, variable=self.var,
bigincrement=step, resolution=step, orient=tk.HORIZONTAL, length=length,
tickinterval=tickinterval, showvalue=showvalue)
if callback:
self.var.trace('w', callback)
self.labelWidget.grid(row=0, column=0, sticky='nes', padx=5, pady=5)
self.slider.grid(row=0, column=1, sticky='news', padx=5, pady=5)
self.columnconfigure(1, weight=3)
[docs] def get(self):
return self.var.get()
[docs] def highlightLabel(self):
self.labelWidget.config(text=self.selectedLabelText)
[docs] def removeHighlightFromLabel(self):
self.labelWidget.config(text=self.labelText)
[docs]class ComboBox(ttk.Combobox):
""" Extension of ttk.ComboBox to allow having different display text and values.
Also adding some utils to getSelected index and value (same for set)
"""
def __init__(self, parent, choices, values=None, initial=None, onChange=None, **kwargs):
""" Create a combobox from a list of choices.
Params:
parent: the parent widget (required by Tkinter)
choices: a list with the options to be shown.
values: if None, will enumerate from 0 to len(choices)-1
if a list is provided, should have the same length as choices.
initial: if None, take the first choice
onChange: provide a callback function to be used when change the selected value
**kwargs: extra arguments passed to ttk.Combobox constructor.
"""
indexes = range(len(choices))
if values is None:
values = indexes
choices = [str(c) for c in choices] # Convert to a list of strings
if initial is None:
initial = choices[0]
self._valuesDict = dict(zip(choices, values))
self._indexDict = dict(zip(choices, indexes))
self._var = tk.StringVar()
self._var.set(initial)
self._changeCallback = onChange
self._var.trace('w', self._onChanged)
ttk.Combobox.__init__(self, parent, textvariable=self._var, state='readonly', **kwargs)
self['values'] = choices
[docs] def getValue(self):
""" Return the selected value. """
return self._valuesDict[self._var.get()]
[docs] def getIndex(self):
""" Return the selected value. """
return self._indexDict[self._var.get()]
[docs] def getText(self):
""" Return the selected option text. """
return self._var.get()
[docs] def setChangeCallback(self, callback):
self._changeCallback = callback
def _onChanged(self, *args):
if self._changeCallback:
self._changeCallback(self)
[docs]class GradientFrame(tk.Canvas):
"""A gradient frame which uses a canvas to draw the background
Taken from:
http://stackoverflow.com/questions/11892521/tkinter-custom-window
"""
def __init__(self, parent, **args):
tk.Canvas.__init__(self, parent, **args)
self._color1 = Config.SCIPION_BG_COLOR #"#d2a7a7"
self._color2 = Config.SCIPION_MAIN_COLOR #"#820808"
self.bind("<Configure>", self._draw_gradient)
def _draw_gradient(self, event=None):
self.delete("gradient")
width = self.winfo_width()
height = self.winfo_height()
limit = width // 2
r1, g1, b1 = self.winfo_rgb(self._color1)
r2, g2, b2 = self.winfo_rgb(self._color2)
r_ratio = (r2 - r1) / limit
g_ratio = (g2 - g1) / limit
b_ratio = (b2 - b1) / limit
for i in range(limit + 1):
nr = int(r1 + (r_ratio * i))
ng = int(g1 + (g_ratio * i))
nb = int(b1 + (b_ratio * i))
color = "#%4.4x%4.4x%4.4x" % (nr, ng, nb)
self.create_line(i, 0, i, height, tags=("gradient",), fill=color)
self.create_line(width - i, 0, width - i, height,
tags=("gradient",), fill=color)
self.lower("gradient")