4
Name: 'Scripts Config Editor'
7
Tooltip: 'View and edit available scripts configuration data'
10
__author__ = "Willian P. Germano"
11
__version__ = "0.1 2005/04/14"
12
__email__ = ('scripts', 'Author, wgermano:ig*com*br')
13
__url__ = ('blender', 'elysiun')
16
This script can be used to view and edit configuration data stored
19
Technical: this data is saved as dictionary keys with the
20
Blender.Registry module functions. It is persistent while Blender is
21
running and, if the script's author chose to, is also saved to a file
22
in the scripts config data dir.
28
To access any available key, select it from (one of) the menu(s).
36
This screen exposes the configuration data for the chosen script key. If the
37
buttons don't fit completely on the screen, you can scroll up or down with
38
arrow keys or a mouse wheel. Leave the mouse pointer over any button to get
39
a tooltip about that option.
41
Any change can be reverted -- unless you have already applied it.
43
If the key is already stored in a config file, there will be a toggle button
44
(called 'file') that controls whether the changes will be written back to
45
the file or not. If you just want to change the configuration for the current
46
session, simply unset that button. Note, though, that data from files has
47
precedence over those keys already loaded in Blender, so if you re-run this
48
config editor, unsaved changes will not be seen.
51
ESC: back to Start Screen<br>
54
ENTER: apply changes (can't be reverted, then)<br>
55
UP, DOWN Arrows and mouse wheel: scroll text up / down
59
a) Available keys are determined by which scripts you use. If the key you
60
expect isn't available (or maybe there are none or too few keys), either the
61
related script doesn't need or still doesn't support this feature or the key
62
has not been stored yet, in which case you just need to run that script once
63
to make its config data available.
65
b) There are two places where config data files can be saved: the
66
bpydata/config/ dir (1) inside the default scripts dir or (2) inside the user
67
defined Python scripts dir
68
(User Preferences window -> File Paths tab -> Python path). If available,
69
(2) is the default and also the recommended option, because then fresh Blender
70
installations won't delete your config data. To use this option, simply set a
71
dir for Python scripts at the User Preferences window and make sure this dir
72
has the subdirs bpydata/ and bpydata/config/ inside it.
74
c) The key called "General" in the "Other" menu has general config options.
75
All scripts where that data is relevant are recommended to access it and set
76
behaviors accordingly.
79
# $Id: config.py,v 1.1 2005/04/16 05:25:41 ianwill Exp $
81
# --------------------------------------------------------------------------
82
# config.py version 0.1 2005/04/08
83
# --------------------------------------------------------------------------
84
# ***** BEGIN GPL LICENSE BLOCK *****
86
# Copyright (C) 2004: Willian P. Germano, wgermano _at_ ig.com.br
88
# This program is free software; you can redistribute it and/or
89
# modify it under the terms of the GNU General Public License
90
# as published by the Free Software Foundation; either version 2
91
# of the License, or (at your option) any later version.
93
# This program is distributed in the hope that it will be useful,
94
# but WITHOUT ANY WARRANTY; without even the implied warranty of
95
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
96
# GNU General Public License for more details.
98
# You should have received a copy of the GNU General Public License
99
# along with this program; if not, write to the Free Software Foundation,
100
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
102
# ***** END GPL LICENCE BLOCK *****
103
# --------------------------------------------------------------------------
106
from Blender import Draw, BGL, Registry, Window, sys as bsys
107
from Blender.Window import Theme
108
from BPyRegistry import LoadConfigData, SaveConfigData, HasConfigData,\
109
BPY_KEY_IN_FILE, MAX_STR_LEN, MAX_ITEMS_NUM
112
# The "General" configure options key is managed from this script.
114
confirm_overwrite = True
117
'verbose': 'print script messages (info, warnings, errors) to the console',
118
'confirm_overwrite': 'scripts should always confirm before overwriting files'
121
CFG_LIST = ['verbose', 'confirm_overwrite', 'tooltips']
124
def update_registry():
127
exec("rd['%s']=%s" % (var, var))
128
Registry.SetKey(KEY_NAME, rd, True)
130
rd = Registry.GetKey('General', True)
133
for var in CFG_LIST[:-1]: # no need to update tooltips
134
exec("%s=rd['%s']" % (var, var))
135
except: update_registry()
144
GD = {} # groups dict (includes "Other" for unmapped keys)
145
INDEX = 0 # to pass button indices to fs callbacks
146
FREEKEY_IDX = 0 # index of set of keys not mapped to a script name
152
DISK_UPDATE = True # write changed data to its config file
154
ACCEPTED_TYPES = [bool, int, float, str, unicode]
156
SCREEN = START_SCREEN
162
BEVT_EXIT = 0 + BEVT_START
163
BEVT_BACK = 1 + BEVT_START
164
BEVT_DISK = 2 + BEVT_START
165
BEVT_CANCEL = 3 + BEVT_START
166
BEVT_APPLY = 4 + BEVT_START
167
BEVT_HELP = 5 + BEVT_START
168
BEVT_DEL = 6 + BEVT_START
172
BEVT_INT = BEVT_BOOL + MAX_ITEMS_NUM
173
BEVT_FLOAT = BEVT_BOOL + 2*MAX_ITEMS_NUM
174
BEVT_STR = BEVT_BOOL + 3*MAX_ITEMS_NUM
175
BEVT_BROWSEDIR = BEVT_BOOL + 4*MAX_ITEMS_NUM
176
BEVT_BROWSEFILE = BEVT_BOOL + 5*MAX_ITEMS_NUM
184
# Function definitions:
187
LoadConfigData() # loads all data from files in (u)scripts/bpydata/config/
188
return [k for k in Registry.Keys() if k[0] != "_"]
191
def show_help(script = 'config.py'):
192
Blender.ShowHelp(script)
195
def fs_dir_callback(pathname):
198
pathname = bsys.dirname(pathname)
199
datatypes = CFGKEY.sorteddata
200
datatypes[str][INDEX][1] = pathname
203
def fs_file_callback(pathname):
206
datatypes = CFGKEY.sorteddata
207
datatypes[str][INDEX][1] = pathname
210
# parse Bpymenus file to get all script filenames
211
# (used to show help for a given key)
212
def fill_scripts_dict():
213
global ALL_SCRIPTS, ALL_GROUPS
218
home = Blender.Get('homedir')
221
Can't find Blender's home dir and so can't find the
222
Bpymenus file automatically stored inside it, which
223
is needed by this script. Please run the
224
Help -> System -> System Information script to get
225
information about how to fix this.
227
raise SystemError, errmsg
228
fname = bsys.join(home, 'Bpymenus')
229
if not bsys.exists(fname): return False
231
lines = f.readlines()
236
ALL_GROUPS.append(group)
239
elif l[0] != "'": continue
240
fields = l.split("'")
242
menuname = fields[1].replace('...','')
243
fields = fields[2].split()
245
fname = fields[1].split(sep)[-1]
246
ALL_SCRIPTS[fname] = (menuname, group_len - 1)
250
def map_to_registered_script(name):
253
if not name.endswith('.py'):
254
name = "%s.py" % name
255
if ALL_SCRIPTS.has_key(name):
256
return ALL_SCRIPTS[name] # == (menuname, group index)
261
global LABELS, GD, KEYMENUS, KEYS
263
# init_data is recalled when a key is deleted, so:
270
# gather all script info, fill gui menus
272
global KEYS, GD, ALL_GROUPS, ALL_SCRIPTS, KEYMENUS, LABELS
273
global BUT_KEYMENU, BEVT_KEYMENU, FREEKEY_IDX
280
res = map_to_registered_script(k)
282
GD[ALL_GROUPS[res[1]]].append((k, res[0]))
283
else: GD[None].append((k, k))
286
if not GD[k]: GD.pop(k)
289
GD['Other'] = GD[None]
293
BUT_KEYMENU = range(len(GD))
296
kmenu = ['Configuration Keys: %s%%t' % k]
299
kmenu = "|".join(kmenu)
300
KEYMENUS.append(kmenu)
304
FREEKEY_IDX = LABELS.index('Other')
306
length = len(KEYMENUS)
307
BEVT_KEYMENU = range(1, length + 1)
308
BUT_KEYMENU = range(length)
312
def float_colors(cols):
313
return map(lambda x: x / 255.0, cols)
319
def __init__(self, key, has_group = True):
323
self.has_group = has_group
325
self.fromdisk = HasConfigData(key) & BPY_KEY_IN_FILE
326
if not self.fromdisk: DISK_UPDATE = False
327
else: DISK_UPDATE = True
329
self.origdata = Registry.GetKey(key, True)
330
data = self.data = self.origdata.copy()
333
Draw.PupMenu('ERROR: couldn\'t find requested data')
342
if nd.has_key('tooltips'):
343
ndval = nd['tooltips']
344
self.tips = data[ndval]
348
if nd.has_key('limits'):
350
self.limits = data[ndval]
352
else: self.limits = 0
356
if not scriptname.endswith('.py'):
357
scriptname = "%s.py" % scriptname
358
elif nd.has_key('script'):
360
scriptname = data[ndval]
362
if not scriptname.endswith('.py'):
363
scriptname = "%s.py" % scriptname
364
else: scriptname = None
366
self.scriptname = scriptname
371
def needs_update(self): # check if user changed data
373
new = self.sorteddata
375
for vartype in new.keys():
376
for i in new[vartype]:
377
if data[i[0]] != i[1]: return 1
379
return 0 # no changes
382
def update(self): # update original key
386
odata = self.origdata
387
new = self.sorteddata
388
for vartype in new.keys():
389
for i in new[vartype]:
390
if data[i[0]] != i[1]: data[i[0]] = i[1]
391
if odata[i[0]] != i[1]: odata[i[0]] = i[1]
393
if DISK_UPDATE: Registry.SetKey(self.key, odata, True)
398
delmsg = 'OK?%t|Delete key from memory'
400
delmsg = "%s and from disk" % delmsg
401
if Draw.PupMenu(delmsg) == 1:
402
Registry.RemoveKey(self.key, DISK_UPDATE)
408
def revert(self): # revert to original key
410
new = self.sorteddata
411
for vartype in new.keys():
412
for i in new[vartype]:
413
if data[i[0]] != i[1]: i[1] = data[i[0]]
416
def sort(self): # create a new dict with types as keys
417
global ACCEPTED_TYPES, BUT_TYPES
421
keys = [k for k in data.keys() if k[0] != '_']
425
if tval not in ACCEPTED_TYPES: continue
426
if not datatypes.has_key(tval):
428
datatypes[type(val)].append([k, val])
429
if datatypes.has_key(unicode):
430
if not datatypes.has_key(str): datatypes[str] = datatypes[unicode]
432
for i in datatypes[unicode]: datatypes[str].append(i)
433
datatypes.pop(unicode)
434
for k in datatypes.keys():
438
BUT_TYPES[k] = range(len(dk))
439
self.sorteddata = datatypes
446
def gui(): # drawing the screen
448
global SCREEN, START_SCREEN, CONFIG_SCREEN, KEYMENUS, LABELS
449
global BEVT_KEYMENU, BUT_KEYMENU, CFGKEY
450
global BUT_TYPES, SCROLL_DOWN, VARS_NUM
452
WIDTH, HEIGHT = Window.GetAreaSize()
454
theme = Theme.Get()[0]
455
tui = theme.get('ui')
456
ttxt = theme.get('text')
458
COL_BG = float_colors(ttxt.back)
460
COL_TXTHI = ttxt.text_hi
462
BGL.glClearColor(COL_BG[0],COL_BG[1],COL_BG[2],COL_BG[3])
463
BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
464
BGL.glColor3ub(COL_TXT[0],COL_TXT[1], COL_TXT[2])
466
if SCREEN == START_SCREEN:
471
BGL.glRasterPos2i(x, y)
472
Draw.Text('Select a configuration key to access it. Press Q or ESC to leave.')
473
km_len = len(KEYMENUS)
474
km_columns = (WIDTH - x) / w
475
if km_columns == 0: km_rows = km_len
477
km_rows = km_len / km_columns
478
if (km_len % km_columns): km_rows += 1
479
if km_rows == 0: km_rows = 1
480
ystart = y + 2*h*km_rows
481
if ystart > (HEIGHT - 70): ystart = HEIGHT - 70
484
for i, km in enumerate(KEYMENUS):
486
BGL.glRasterPos2i(x + 2, y + h + 5)
488
BUT_KEYMENU[i] = Draw.Menu(km, BEVT_KEYMENU[i],
489
x, y, w - 10, h, 0, 'Choose a key to access its configuration data')
490
if column > km_columns:
498
BGL.glColor3ub(COL_TXTHI[0], COL_TXTHI[1], COL_TXTHI[2])
499
BGL.glRasterPos2i(x, y)
500
Draw.Text('Scripts Configuration Editor')
501
Draw.PushButton('help', BEVT_HELP, x, 22, 45, 16,
502
'View help information about this script (hotkey: H)')
504
elif SCREEN == CONFIG_SCREEN:
507
data = CFGKEY.sorteddata
509
fromdisk = CFGKEY.fromdisk
510
limits = CFGKEY.limits
512
for k in data.keys():
513
VARS_NUM += len(data[k])
514
lines = VARS_NUM + 5 # to account for header and footer
516
if y > HEIGHT - 20: y = HEIGHT - 20
517
BGL.glColor3ub(COL_TXTHI[0],COL_TXTHI[1], COL_TXTHI[2])
518
BGL.glRasterPos2i(x, y)
519
Draw.Text('Scripts Configuration Editor')
521
BGL.glColor3ub(COL_TXT[0],COL_TXT[1], COL_TXT[2])
524
BGL.glRasterPos2i(10, 5)
525
txtsize += Draw.Text('Arrow keys or mouse wheel to scroll, ')
526
BGL.glRasterPos2i(txtsize, 5)
527
Draw.Text('Q or ESC to return.')
528
BGL.glRasterPos2i(x, y)
529
Draw.Text('Key: "%s"' % CFGKEY.name)
534
if CFGKEY.scriptname:
536
Draw.PushButton('help', BEVT_HELP, x, by, bw, bh,
537
'Show documentation for the script that owns this key (hotkey: H)')
538
Draw.PushButton('back', BEVT_BACK, x + (1+i)*bw, by, bw, bh,
539
'Back to config keys selection screen (hotkey: ESC)')
540
Draw.PushButton('exit', BEVT_EXIT, x + (2+i)*bw, by, bw, bh,
541
'Exit from Scripts Config Editor (hotkey: Q)')
542
Draw.PushButton('revert', BEVT_CANCEL, x + (3+i)*bw, by, bw, bh,
543
'Revert data to original values (hotkey: U)')
544
Draw.PushButton('apply', BEVT_APPLY, x + (4+i)*bw, by, bw, bh,
545
'Apply changes, if any (hotkey: ENTER)')
546
delmsg = 'Delete this data key from memory'
547
if fromdisk: delmsg = "%s and from disk" % delmsg
548
Draw.PushButton('delete', BEVT_DEL, x + (5+i)*bw, by, bw, bh,
549
'%s (hotkey: DELETE)' % delmsg)
551
Draw.Toggle("file", BEVT_DISK, x + 3 + (6+i)*bw, by, bw, bh, DISK_UPDATE,
552
'Update also the file where this config key is stored')
557
if data.has_key(bool) and y > 0:
562
if top < SCROLL_DOWN: continue
566
tog = data[bool][i][1]
567
if tips and tips.has_key(l[0]): tooltip = tips[l[0]]
568
else: tooltip = "click to toggle"
569
BUT_TYPES[bool][i] = Draw.Toggle("", BEVT_BOOL + i,
570
x, y, w, h, tog, tooltip)
571
BGL.glRasterPos2i(x + w + 3, y + 5)
572
Draw.Text(l[0].lower().replace('_', ' '))
575
if data.has_key(int) and y > 0:
581
if top < SCROLL_DOWN: continue
584
val = data[int][i][1]
585
if limits: min, max = limits[l[0]]
586
else: min, max = 0, 10
587
if tips and tips.has_key(l[0]): tooltip = tips[l[0]]
588
else: tooltip = "click / drag to change"
589
BUT_TYPES[int][i] = Draw.Number("", BEVT_INT + i,
590
x, y, w, h, val, min, max, tooltip)
591
BGL.glRasterPos2i(x + w + 3, y + 3)
592
Draw.Text(l[0].lower().replace('_', ' '))
595
if data.has_key(float) and y > 0:
601
if top < SCROLL_DOWN: continue
604
val = data[float][i][1]
605
if limits: min, max = limits[l[0]]
606
else: min, max = 0.0, 1.0
607
if tips and tips.has_key(l[0]): tooltip = tips[l[0]]
608
else: tooltip = "click and drag to change"
609
BUT_TYPES[float][i] = Draw.Number("", BEVT_FLOAT + i,
610
x, y, w, h, val, min, max, tooltip)
611
BGL.glRasterPos2i(x + w + 3, y + 3)
612
Draw.Text(l[0].lower().replace('_', ' '))
615
if data.has_key(str) and y > 0:
620
if top < SCROLL_DOWN: continue
624
is_dir = is_file = False
625
if name.find('_dir', -4) > 0: is_dir = True
626
elif name.find('_file', -5) > 0: is_file = True
629
if is_dir and w > wbrowse: w -= wbrowse
630
if tips and tips.has_key(l[0]): tooltip = tips[l[0]]
631
else: tooltip = "click to write a new string"
632
name = name.replace('_',' ') + ': '
633
BUT_TYPES[str][i] = Draw.String(name, BEVT_STR + i,
634
x, y, w, h, l[1], MAX_STR_LEN, tooltip)
636
Draw.PushButton('browse', BEVT_BROWSEDIR + i, x+w+1, y, wbrowse, h,
637
'click to open a file selector (pick any file in the desired dir)')
639
Draw.PushButton('browse', BEVT_BROWSEFILE + i, x + w + 1, y, 50, h,
640
'click to open a file selector')
644
global SCROLL_DOWN, VARS_NUM
645
max = VARS_NUM - 1 # so last item is always visible
646
if SCROLL_DOWN > max:
648
elif SCROLL_DOWN < 0:
652
def event(evt, val): # input events
654
global SCREEN, START_SCREEN, CONFIG_SCREEN
655
global SCROLL_DOWN, CFGKEY
659
if evt == Draw.ESCKEY:
660
if SCREEN == START_SCREEN: Draw.Exit()
662
if CFGKEY.needs_update():
663
if Draw.PupMenu('UPDATE?%t|Data was changed') == 1:
665
SCREEN = START_SCREEN
669
elif evt == Draw.QKEY:
670
if SCREEN == CONFIG_SCREEN and CFGKEY.needs_update():
671
if Draw.PupMenu('UPDATE?%t|Data was changed') == 1:
675
elif evt == Draw.HKEY:
676
if SCREEN == START_SCREEN: show_help()
677
elif CFGKEY.scriptname: show_help(CFGKEY.scriptname)
680
elif SCREEN == CONFIG_SCREEN:
681
if evt in [Draw.DOWNARROWKEY, Draw.WHEELDOWNMOUSE]:
684
elif evt in [Draw.UPARROWKEY, Draw.WHEELUPMOUSE]:
687
elif evt == Draw.UKEY:
688
if CFGKEY.needs_update():
690
elif evt == Draw.RETKEY or evt == Draw.PADENTER:
691
if CFGKEY.needs_update():
693
elif evt == Draw.DELKEY:
697
SCREEN = START_SCREEN
703
def button_event(evt): # gui button events
705
global SCREEN, START_SCREEN, CONFIG_SCREEN, CFGKEY, DISK_UPDATE
706
global BEVT_KEYMENU, BUT_KEYMENU, BUT_TYPES, SCROLL_DOWN, GD, INDEX
707
global BEVT_EXIT, BEVT_BACK, BEVT_APPLY, BEVT_CANCEL, BEVT_HELP, FREEKEY_IDX
709
if SCREEN == START_SCREEN:
710
for e in BEVT_KEYMENU:
713
k = BUT_KEYMENU[index].val - 1
714
CFGKEY = Config(GD[LABELS[index]][k][0], index != FREEKEY_IDX)
716
SCREEN = CONFIG_SCREEN
721
elif evt == BEVT_HELP:
725
elif SCREEN == CONFIG_SCREEN:
726
datatypes = CFGKEY.sorteddata
727
if evt >= BEVT_BROWSEFILE:
728
INDEX = evt - BEVT_BROWSEFILE
729
Window.FileSelector(fs_file_callback, 'Choose file')
730
elif evt >= BEVT_BROWSEDIR:
731
INDEX = evt - BEVT_BROWSEDIR
732
Window.FileSelector(fs_dir_callback, 'Choose any file')
733
elif evt >= BEVT_STR:
734
var = BUT_TYPES[str][evt - BEVT_STR].val
735
datatypes[str][evt - BEVT_STR][1] = var
736
elif evt >= BEVT_FLOAT:
737
var = BUT_TYPES[float][evt - BEVT_FLOAT].val
738
datatypes[float][evt - BEVT_FLOAT][1] = var
739
elif evt >= BEVT_INT:
740
var = BUT_TYPES[int][evt - BEVT_INT].val
741
datatypes[int][evt - BEVT_INT][1] = var
742
elif evt >= BEVT_BOOL:
743
var = datatypes[bool][evt - BEVT_BOOL][1]
744
if var == True: var = False
746
datatypes[bool][evt - BEVT_BOOL][1] = var
748
elif evt == BEVT_BACK:
749
if SCREEN == CONFIG_SCREEN:
750
SCREEN = START_SCREEN
753
elif evt == BEVT_EXIT:
754
if CFGKEY.needs_update():
755
if Draw.PupMenu('UPDATE?%t|Data was changed') == 1:
759
elif evt == BEVT_APPLY:
760
if CFGKEY.needs_update():
762
elif evt == BEVT_CANCEL:
763
if CFGKEY.needs_update():
765
elif evt == BEVT_DEL:
769
SCREEN = START_SCREEN
771
elif evt == BEVT_DISK:
772
if DISK_UPDATE: DISK_UPDATE = False
773
else: DISK_UPDATE = True
774
elif evt == BEVT_HELP:
775
show_help(CFGKEY.scriptname)
787
Draw.PupMenu("NO DATA: please read this help screen")
788
Blender.ShowHelp('config.py')
792
Draw.Register(gui, event, button_event)