Files
lazy-dsi-file-downloader/tkmacosx/variables.py
2020-12-19 11:31:25 +00:00

367 lines
14 KiB
Python

# Copyright 2020 Saad Mairaj
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import re
import sys
import ast
import pickle as pkl
if sys.version_info.major == 2:
import Tkinter as _tk
elif sys.version_info.major == 3:
import tkinter as _tk
# Modified Misc._options(...) to make ColorVar work with tkinter
# {(self, option, tag): (var, cbname)}
_all_traces_colorvar = {}
def _colorvar_patch_cget(fn):
"""Internal function."""
def _patch(self, key):
"""Return the resource value for a KEY given as string."""
if (self, key) in _all_traces_colorvar:
return _all_traces_colorvar[(self, key)][0]
return fn(self, key)
return _patch
def _colorvar_patch_destroy(fn):
"""Internal function.\n
Deletes the traces if any when widget is destroy."""
def _patch(self):
"""Interanl function."""
if self._tclCommands is not None:
# Deletes the widget from the _all_traces_colorvar
# and deletes the traces too.
for key, value in dict(_all_traces_colorvar).items():
if self == key[0]:
var, cbname = value
try: var.trace_vdelete('w', cbname)
except: pass
_all_traces_colorvar.pop(key)
return fn(self)
return _patch
def _colorvar_patch_configure(fn):
"""Internal function.\n
Patch for Canvas items to support ColorVar functionality."""
def _patch(self, cmd, cnf, kw):
"""Internal function."""
cnf = _tk._cnfmerge((cnf, kw))
cnf_copy = dict(cnf)
# -------------------- Added the below block --------------------
# Add the resources to the list to have ColorVar functionality.
if isinstance(cmd, tuple) and isinstance(self, _tk.Canvas):
tags = self.find('withtag', cmd[1])
for tag in tags:
for i in ('activefill', 'activeoutline', 'disabledfill',
'disabledoutline', 'fill', 'outline', 'background',
'activebackground', 'activeforeground',
'disabledbackground', 'disabledforeground',
'foreground'):
if isinstance(cnf_copy.get(i), _tk.Variable):
var = cnf_copy[i]
cbname = var.trace_variable('w', lambda a, b, c,
cls=self, opt=i,
tagId=tag, var=var:
cls._configure(('itemconfigure',tagId),
{opt: var.get()}, None))
if (self, (i, tag)) in _all_traces_colorvar:
v, cb = _all_traces_colorvar.get((self, (i, tag)))
v.trace_vdelete('w', cb)
_all_traces_colorvar[(self, (i, tag))] = (var, cbname)
else:
_all_traces_colorvar[(self, (i, tag))] = (var, cbname)
cnf[i] = var.get()
return fn(self, cmd, cnf, None)
return _patch
def _colorvar_patch_options(fn):
"""Internal function.\n
Patch for ColorVar to work with tkinter widgets."""
def _patch(self, cnf, kw=None):
"""Internal function."""
if kw:
cnf = _tk._cnfmerge((cnf, kw))
else:
cnf = _tk._cnfmerge(cnf)
for i in ('fg', 'foreground', 'bg', 'background',
'activebackground', 'activeforeground', 'disabledforeground',
'highlightbackground', 'highlightcolor', 'selectforeground',
'readonlybackground', 'selectbackground', 'insertbackground',
'disabledbackground'):
if isinstance(cnf.get(i), _tk.Variable):
var = cnf[i]
cbname = var.trace_variable('w', lambda a, b, c, i=i, var=var,
cls=self: cls.config({i: var.get()}))
if (self, i) in _all_traces_colorvar:
v, cb = _all_traces_colorvar.get((self, i))
v.trace_vdelete('w', cb)
_all_traces_colorvar[(self, i)] = (var, cbname)
else:
_all_traces_colorvar[(self, i)] = (var, cbname)
cnf[i] = var.get()
# [issue-1] once a ColorVar is assigned, it cannot be removed
# untill the widget is destroyed or give another ColorVar
# [issue-1] (trial) the below doesn't work as excepted.
# elif (self, i) in _all_traces_colorvar:
# if self[i] != _all_traces_colorvar[(self, i)][0].get():
# print( self, i, self[i])
# v, cb = _all_traces_colorvar.pop((self, i))
# v.trace_vdelete('w', cb)
return fn(self, cnf, None)
return _patch
def _create(self, itemType, args, kw): # Args: (val, val, ..., cnf={})
"""Internal function."""
args = _tk._flatten(args)
cnf = args[-1]
if isinstance(cnf, (dict, tuple)):
args = args[:-1]
else:
cnf = {}
# -------------------- Added the below block --------------------
# Add the resources to the list to have ColorVar functionality.
ckw = _tk._cnfmerge((cnf, kw))
var = None
for i in ('activefill', 'activeoutline', 'disabledfill',
'disabledoutline', 'fill', 'outline', 'background',
'activebackground', 'activeforeground', 'foreground',
'disabledbackground', 'disabledforeground'):
if isinstance(ckw.get(i), _tk.Variable):
var = ckw[i]
_all_traces_colorvar[(self, (i, None))] = (var, None)
if isinstance(cnf, dict) and i in cnf:
cnf[i] = var.get()
elif i in kw:
kw[i] = var.get()
# ---------------------------------------------------------------
tagId = self.tk.getint(self.tk.call(
self._w, 'create', itemType,
*(args + self._options(cnf, kw))))
for key, value in dict(_all_traces_colorvar).items():
if isinstance(key[1], (tuple, list)):
wid, (opt, tag_id) = key
var, cbname = value
if tag_id is None and cbname is None:
cbname = var.trace_variable('w', lambda a, b, c,
cls=self, opt=opt,
tagId=tagId, var=var:
cls._configure(('itemconfigure',tagId),
{opt: var.get()}, None))
_all_traces_colorvar[(self, (opt, tagId))] = (var, cbname)
_all_traces_colorvar.pop((self, (opt, None)))
return tagId
_tk.Misc.destroy = _colorvar_patch_destroy(_tk.Misc.destroy)
_tk.Misc._options = _colorvar_patch_options(_tk.Misc._options)
_tk.Misc._configure = _colorvar_patch_configure(_tk.Misc._configure)
_tk.Canvas._create = _create
class ColorVar(_tk.Variable):
"""Value holder for HEX color. Default is white"""
_default = "white"
_rgbstring = re.compile(r'#[a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?$')
def __init__(self, master=None, value=None, name=None):
"""Construct a color variable. (bg, fg, ..)
MASTER can be given as master widget.
VALUE is an optional value (defaults to "")
NAME is an optional Tcl name (defaults to PY_VARnum).
If NAME matches an existing variable and VALUE is omitted
then the existing value is retained.
"""
_tk.Variable.__init__(self, master, value, name)
def set(self, value=''):
"""Set the variable to VALUE."""
if value.startswith('#'):
if not bool(self._rgbstring.match(value)):
raise ValueError('"{}" is not a valid HEX.'.format(value))
elif isinstance(value, str):
try:
r, g, b = self._root.winfo_rgb(value)
c = (r/257, g/257, b/257)
value = '#%02x%02x%02x' % (int(c[0]), int(c[1]), int(c[2]))
except:
raise ValueError(
'Could not find right HEX for "{}".'.format(value))
return self._tk.globalsetvar(self._name, value)
initialize = set
def get(self):
"""Return value of variable color."""
value = self._tk.globalgetvar(self._name)
if isinstance(value, str):
return value
return str(value)
class DictVar(_tk.Variable):
"""
#### Value holder for Dictionaries.
Get a specific value by getting the key from this \
`get(self, key=None, d=None)` method if exists in the dictionary. \n
if `key=None` it will return the complete dictionary.
"""
_default = {}
def __init__(self, master=None, value=None, name=None):
"""Construct a string variable.
MASTER can be given as master widget.
VALUE is an optional value (defaults to {})
NAME is an optional Tcl name (defaults to PY_VARnum).
If NAME matches an existing variable and VALUE is omitted
then the existing value is retained.
"""
_tk.Variable.__init__(self, master, value, name)
def get(self, key=None, d=None):
"""Return value of variable as string."""
value = self._tk.globalgetvar(self._name)
if not isinstance(value, dict):
value = ast.literal_eval(value)
if key:
return value.get(key, d)
else:
return value
def SaveVar(var, master=None, value=None, name=None, filename='data.pkl'):
"""Save tkinter variable data in a pickle file and load the
same value when the program is executed next time.
#### If the content of the file changes, it might not load correct values \
to the assigned variables. To avoid this issue use `name` to \
refer to the exact assigned values later.
### Args:
- `var`: Give the `tkinter.Variable` class like (`tk.StringVar`, `tk.IntVar`).
- `master`: Parent widget.
- `value`: Set value.
- `name`: Set a name to group variables or to refer to assigned value when loaded.
- `filename`: Set the name of the save file. (To make the file invisible in the \
directory start the name of the file with "." like ".cache-savevar")
### Return:
- returns the tk.Variable instance passed to `var` argument.
### Example:
root = tk.Tk()
var1 = SaveVar(tk.StringVar,'Enter Username','Var1','.cache-savevar')
var2 = SaveVar(tk.StringVar,'Enter Password','Var2','.cache-savevar')
tk.Entry(root,textvariable=var1).pack()
tk.Entry(root,textvariable=var2).pack()
root.mainloop()"""
def update_val(*args):
"""Internal function for updating the value for variable"""
try: # try/except , if the file doesn't exists.
open1 = open(filename, 'rb')
tmpdict = pkl.load(open1) # load saved dictionary data.
# Block of code to check for the right value.
if tmpdict.get(str(var)):
old, default = tmpdict.get(str(var))
new = var.get()
if new != default:
var.set(new)
elif new == default and not startup[0]:
var.set(default)
else:
var.set(old)
tmpdict.update({str(var): (var.get(), defaultval)})
open1.close()
except Exception as e:
tmpdict = {}
tmpdict[str(var)] = (var.get(), defaultval)
open2 = open(filename, 'wb')
pkl.dump(tmpdict, open2)
startup[0] = False
open2.close()
startup = [True]
if not(filename.endswith('.pickle') or filename.endswith('.pkl')) \
and not filename.startswith('.'):
filename = filename+'.pkl'
var = var(master=master, value=value, name=name)
defaultval = var.get() # get a default value of the variable
update_val()
for mode, cbname in (var.trace_vinfo()):
if mode[0] == 'w' and update_val.__name__ in cbname:
try:
var.trace_vdelete('w', cbname)
except:
pass
res = var.trace_variable('w', update_val)
return var
# ------------------ Testing ------------------
def demo_colorvar():
import tkmacosx.colors as colors
root = _tk.Tk()
root.geometry('100x100')
color = ColorVar()
color_list = list(colors.OrderedHex)
L = _tk.Label(root, textvariable=color, bg=color)
L.place(relx=0.5, rely=0.5, anchor='center')
def change_color(c=0):
if root.winfo_exists():
if c >= len(color_list):
c = 0
color.set(color_list[c])
root.after(100, change_color, c+1)
change_color()
root.mainloop()
def demo_savevar():
root = _tk.Tk()
var1 = SaveVar(_tk.StringVar, root, 'Enter Username',
'Var1', '.cache-savevar')
var2 = SaveVar(_tk.StringVar, root, 'Enter Password',
'Var2', '.cache-savevar')
_tk.Entry(root, textvariable=var1).pack()
_tk.Entry(root, textvariable=var2).pack()
root.mainloop()
if __name__ == "__main__":
demo_colorvar()
demo_savevar()