815 lines
19 KiB
Python
815 lines
19 KiB
Python
|
#!BPY
|
||
|
|
||
|
"""
|
||
|
Name: 'Scripts Help Browser'
|
||
|
Blender: 234
|
||
|
Group: 'Help'
|
||
|
Tooltip: 'Show help information about a chosen installed script.'
|
||
|
"""
|
||
|
|
||
|
__author__ = "Willian P. Germano"
|
||
|
__version__ = "0.3 01/21/09"
|
||
|
__email__ = ('scripts', 'Author, wgermano:ig*com*br')
|
||
|
__url__ = ('blender', 'blenderartists.org')
|
||
|
|
||
|
__bpydoc__ ="""\
|
||
|
This script shows help information for scripts registered in the menus.
|
||
|
|
||
|
Usage:
|
||
|
|
||
|
- Start Screen:
|
||
|
|
||
|
To read any script's "user manual" select a script from one of the
|
||
|
available category menus. If the script has help information in the format
|
||
|
expected by this Help Browser, it will be displayed in the Script Help
|
||
|
Screen. Otherwise you'll be offered the possibility of loading the chosen
|
||
|
script's source file in Blender's Text Editor. The programmer(s) may have
|
||
|
written useful comments there for users.
|
||
|
|
||
|
Hotkeys:<br>
|
||
|
ESC or Q: [Q]uit
|
||
|
|
||
|
- Script Help Screen:
|
||
|
|
||
|
This screen shows the user manual page for the chosen script. If the text
|
||
|
doesn't fit completely on the screen, you can scroll it up or down with
|
||
|
arrow keys or a mouse wheel. There may be link and email buttons that if
|
||
|
clicked should open your default web browser and email client programs for
|
||
|
further information or support.
|
||
|
|
||
|
Hotkeys:<br>
|
||
|
ESC: back to Start Screen<br>
|
||
|
Q: [Q]uit<br>
|
||
|
S: view script's [S]ource code in Text Editor<br>
|
||
|
UP, DOWN Arrows and mouse wheel: scroll text up / down
|
||
|
"""
|
||
|
|
||
|
# $Id: help_browser.py 18607 2009-01-21 15:45:31Z ianwill $
|
||
|
#
|
||
|
# --------------------------------------------------------------------------
|
||
|
# ***** BEGIN GPL LICENSE BLOCK *****
|
||
|
#
|
||
|
# Copyright (C) 2004: Willian P. Germano, wgermano _at_ ig.com.br
|
||
|
#
|
||
|
# 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 2
|
||
|
# 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, write to the Free Software Foundation,
|
||
|
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||
|
#
|
||
|
# ***** END GPL LICENCE BLOCK *****
|
||
|
# --------------------------------------------------------------------------
|
||
|
# Thanks: Brendon Murphy (suggestion) and Kevin Morgan (implementation)
|
||
|
# for the "run" button; Jean-Michel Soler for pointing a parsing error
|
||
|
# with multilines using triple single quotes.
|
||
|
|
||
|
import Blender
|
||
|
from Blender import sys as bsys, Draw, Window, Registry
|
||
|
|
||
|
WEBBROWSER = True
|
||
|
try:
|
||
|
import webbrowser
|
||
|
except:
|
||
|
WEBBROWSER = False
|
||
|
|
||
|
DEFAULT_EMAILS = {
|
||
|
'scripts': ['Bf-scripts-dev', 'bf-scripts-dev@blender.org']
|
||
|
}
|
||
|
|
||
|
DEFAULT_LINKS = {
|
||
|
'blender': ["blender.org\'s Python forum", "http://www.blender.org/modules.php?op=modload&name=phpBB2&file=viewforum&f=9"]
|
||
|
}
|
||
|
|
||
|
PADDING = 15
|
||
|
COLUMNS = 1
|
||
|
TEXT_WRAP = 100
|
||
|
WIN_W = WIN_H = 200
|
||
|
SCROLL_DOWN = 0
|
||
|
|
||
|
def screen_was_resized():
|
||
|
global WIN_W, WIN_H
|
||
|
|
||
|
w, h = Window.GetAreaSize()
|
||
|
if WIN_W != w or WIN_H != h:
|
||
|
WIN_W = w
|
||
|
WIN_H = h
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
def fit_on_screen():
|
||
|
global TEXT_WRAP, PADDING, WIN_W, WIN_H, COLUMNS
|
||
|
|
||
|
COLUMNS = 1
|
||
|
WIN_W, WIN_H = Window.GetAreaSize()
|
||
|
TEXT_WRAP = int((WIN_W - PADDING) / 6)
|
||
|
if TEXT_WRAP < 40:
|
||
|
TEXT_WRAP = 40
|
||
|
elif TEXT_WRAP > 100:
|
||
|
if TEXT_WRAP > 110:
|
||
|
COLUMNS = 2
|
||
|
TEXT_WRAP /= 2
|
||
|
else: TEXT_WRAP = 100
|
||
|
|
||
|
def cut_point(text, length):
|
||
|
"Returns position of the last space found before 'length' chars"
|
||
|
l = length
|
||
|
c = text[l]
|
||
|
while c != ' ':
|
||
|
l -= 1
|
||
|
if l == 0: return length # no space found
|
||
|
c = text[l]
|
||
|
return l
|
||
|
|
||
|
def text_wrap(text, length = None):
|
||
|
global TEXT_WRAP
|
||
|
|
||
|
wrapped = []
|
||
|
lines = text.split('<br>')
|
||
|
llen = len(lines)
|
||
|
if llen > 1:
|
||
|
if lines[-1] == '': llen -= 1
|
||
|
for i in range(llen - 1):
|
||
|
lines[i] = lines[i].rstrip() + '<br>'
|
||
|
lines[llen-1] = lines[llen-1].rstrip()
|
||
|
|
||
|
if not length: length = TEXT_WRAP
|
||
|
|
||
|
for l in lines:
|
||
|
while len(l) > length:
|
||
|
cpt = cut_point(l, length)
|
||
|
line, l = l[:cpt], l[cpt + 1:]
|
||
|
wrapped.append(line)
|
||
|
wrapped.append(l)
|
||
|
return wrapped
|
||
|
|
||
|
def load_script_text(script):
|
||
|
global PATHS, SCRIPT_INFO
|
||
|
|
||
|
if script.userdir:
|
||
|
path = PATHS['uscripts']
|
||
|
else:
|
||
|
path = PATHS['scripts']
|
||
|
|
||
|
fname = bsys.join(path, script.fname)
|
||
|
|
||
|
source = Blender.Text.Load(fname)
|
||
|
if source:
|
||
|
Draw.PupMenu("File loaded%%t|Please check the file \"%s\" in the Text Editor window" % source.name)
|
||
|
|
||
|
|
||
|
# for theme colors:
|
||
|
def float_colors(cols):
|
||
|
return map(lambda x: x / 255.0, cols)
|
||
|
|
||
|
# globals
|
||
|
|
||
|
SCRIPT_INFO = None
|
||
|
|
||
|
PATHS = {
|
||
|
'home': Blender.Get('homedir'),
|
||
|
'scripts': Blender.Get('scriptsdir'),
|
||
|
'uscripts': Blender.Get('uscriptsdir')
|
||
|
}
|
||
|
|
||
|
if not PATHS['home']:
|
||
|
errmsg = """
|
||
|
Can't find Blender's home dir and so can't find the
|
||
|
Bpymenus file automatically stored inside it, which
|
||
|
is needed by this script. Please run the
|
||
|
Help -> System -> System Information script to get
|
||
|
information about how to fix this.
|
||
|
"""
|
||
|
raise SystemError, errmsg
|
||
|
|
||
|
BPYMENUS_FILE = bsys.join(PATHS['home'], 'Bpymenus')
|
||
|
|
||
|
f = file(BPYMENUS_FILE, 'r')
|
||
|
lines = f.readlines()
|
||
|
f.close()
|
||
|
|
||
|
AllGroups = []
|
||
|
|
||
|
class Script:
|
||
|
|
||
|
def __init__(self, data):
|
||
|
self.name = data[0]
|
||
|
self.version = data[1]
|
||
|
self.fname = data[2]
|
||
|
self.userdir = data[3]
|
||
|
self.tip = data[4]
|
||
|
|
||
|
# End of class Script
|
||
|
|
||
|
|
||
|
class Group:
|
||
|
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
self.scripts = []
|
||
|
|
||
|
def add_script(self, script):
|
||
|
self.scripts.append(script)
|
||
|
|
||
|
def get_name(self):
|
||
|
return self.name
|
||
|
|
||
|
def get_scripts(self):
|
||
|
return self.scripts
|
||
|
|
||
|
# End of class Group
|
||
|
|
||
|
|
||
|
class BPy_Info:
|
||
|
|
||
|
def __init__(self, script, dict):
|
||
|
|
||
|
self.script = script
|
||
|
|
||
|
self.d = dict
|
||
|
|
||
|
self.header = []
|
||
|
self.len_header = 0
|
||
|
self.content = []
|
||
|
self.len_content = 0
|
||
|
self.spaces = 0
|
||
|
self.fix_urls()
|
||
|
self.make_header()
|
||
|
self.wrap_lines()
|
||
|
|
||
|
def make_header(self):
|
||
|
|
||
|
sc = self.script
|
||
|
d = self.d
|
||
|
|
||
|
header = self.header
|
||
|
|
||
|
title = "Script: %s" % sc.name
|
||
|
version = "Version: %s for Blender %1.2f or newer" % (d['__version__'],
|
||
|
sc.version / 100.0)
|
||
|
|
||
|
if len(d['__author__']) == 1:
|
||
|
asuffix = ':'
|
||
|
else: asuffix = 's:'
|
||
|
|
||
|
authors = "%s%s %s" % ("Author", asuffix, ", ".join(d['__author__']))
|
||
|
|
||
|
header.append(title)
|
||
|
header.append(version)
|
||
|
header.append(authors)
|
||
|
self.len_header = len(header)
|
||
|
|
||
|
|
||
|
def fix_urls(self):
|
||
|
|
||
|
emails = self.d['__email__']
|
||
|
fixed = []
|
||
|
for a in emails:
|
||
|
if a in DEFAULT_EMAILS.keys():
|
||
|
fixed.append(DEFAULT_EMAILS[a])
|
||
|
else:
|
||
|
a = a.replace('*','.').replace(':','@')
|
||
|
ltmp = a.split(',')
|
||
|
if len(ltmp) != 2:
|
||
|
ltmp = [ltmp[0], ltmp[0]]
|
||
|
fixed.append(ltmp)
|
||
|
|
||
|
self.d['__email__'] = fixed
|
||
|
|
||
|
links = self.d['__url__']
|
||
|
fixed = []
|
||
|
for a in links:
|
||
|
if a in DEFAULT_LINKS.keys():
|
||
|
fixed.append(DEFAULT_LINKS[a])
|
||
|
else:
|
||
|
ltmp = a.split(',')
|
||
|
if len(ltmp) != 2:
|
||
|
ltmp = [ltmp[0], ltmp[0]]
|
||
|
fixed.append([ltmp[0].strip(), ltmp[1].strip()])
|
||
|
|
||
|
self.d['__url__'] = fixed
|
||
|
|
||
|
|
||
|
def wrap_lines(self, reset = 0):
|
||
|
|
||
|
lines = self.d['__bpydoc__'].split('\n')
|
||
|
self.content = []
|
||
|
newlines = []
|
||
|
newline = []
|
||
|
|
||
|
if reset:
|
||
|
self.len_content = 0
|
||
|
self.spaces = 0
|
||
|
|
||
|
for l in lines:
|
||
|
if l == '' and newline:
|
||
|
newlines.append(newline)
|
||
|
newline = []
|
||
|
newlines.append('')
|
||
|
else: newline.append(l)
|
||
|
if newline: newlines.append(newline)
|
||
|
|
||
|
for lst in newlines:
|
||
|
wrapped = text_wrap(" ".join(lst))
|
||
|
for l in wrapped:
|
||
|
self.content.append(l)
|
||
|
if l: self.len_content += 1
|
||
|
else: self.spaces += 1
|
||
|
|
||
|
if not self.content[-1]:
|
||
|
self.len_content -= 1
|
||
|
|
||
|
|
||
|
# End of class BPy_Info
|
||
|
|
||
|
def parse_pyobj_close(closetag, lines, i):
|
||
|
i += 1
|
||
|
l = lines[i]
|
||
|
while l.find(closetag) < 0:
|
||
|
i += 1
|
||
|
l = "%s%s" % (l, lines[i])
|
||
|
return [l, i]
|
||
|
|
||
|
def parse_pyobj(var, lines, i):
|
||
|
"Bad code, was in a hurry for release"
|
||
|
|
||
|
l = lines[i].replace(var, '').replace('=','',1).strip()
|
||
|
|
||
|
i0 = i - 1
|
||
|
|
||
|
if l[0] == '"':
|
||
|
if l[1:3] == '""': # """
|
||
|
if l.find('"""', 3) < 0: # multiline
|
||
|
l2, i = parse_pyobj_close('"""', lines, i)
|
||
|
if l[-1] == '\\': l = l[:-1]
|
||
|
l = "%s%s" % (l, l2)
|
||
|
elif l[-1] == '"' and l[-2] != '\\': # single line: "..."
|
||
|
pass
|
||
|
else:
|
||
|
l = "ERROR"
|
||
|
|
||
|
elif l[0] == "'":
|
||
|
if l[1:3] == "''": # '''
|
||
|
if l.find("'''", 3) < 0: # multiline
|
||
|
l2, i = parse_pyobj_close("'''", lines, i)
|
||
|
if l[-1] == '\\': l = l[:-1]
|
||
|
l = "%s%s" % (l, l2)
|
||
|
elif l[-1] == '\\':
|
||
|
l2, i = parse_pyobj_close("'", lines, i)
|
||
|
l = "%s%s" % (l, l2)
|
||
|
elif l[-1] == "'" and l[-2] != '\\': # single line: '...'
|
||
|
pass
|
||
|
else:
|
||
|
l = "ERROR"
|
||
|
|
||
|
elif l[0] == '(':
|
||
|
if l[-1] != ')':
|
||
|
l2, i = parse_pyobj_close(')', lines, i)
|
||
|
l = "%s%s" % (l, l2)
|
||
|
|
||
|
elif l[0] == '[':
|
||
|
if l[-1] != ']':
|
||
|
l2, i = parse_pyobj_close(']', lines, i)
|
||
|
l = "%s%s" % (l, l2)
|
||
|
|
||
|
return [l, i - i0]
|
||
|
|
||
|
# helper functions:
|
||
|
|
||
|
def parse_help_info(script):
|
||
|
|
||
|
global PATHS, SCRIPT_INFO
|
||
|
|
||
|
if script.userdir:
|
||
|
path = PATHS['uscripts']
|
||
|
else:
|
||
|
path = PATHS['scripts']
|
||
|
|
||
|
fname = bsys.join(path, script.fname)
|
||
|
|
||
|
if not bsys.exists(fname):
|
||
|
Draw.PupMenu('IO Error: couldn\'t find script %s' % fname)
|
||
|
return None
|
||
|
|
||
|
f = file(fname, 'r')
|
||
|
lines = f.readlines()
|
||
|
f.close()
|
||
|
|
||
|
# fix line endings:
|
||
|
if lines[0].find('\r'):
|
||
|
unixlines = []
|
||
|
for l in lines:
|
||
|
unixlines.append(l.replace('\r',''))
|
||
|
lines = unixlines
|
||
|
|
||
|
llen = len(lines)
|
||
|
has_doc = 0
|
||
|
|
||
|
doc_data = {
|
||
|
'__author__': '',
|
||
|
'__version__': '',
|
||
|
'__url__': '',
|
||
|
'__email__': '',
|
||
|
'__bpydoc__': '',
|
||
|
'__doc__': ''
|
||
|
}
|
||
|
|
||
|
i = 0
|
||
|
while i < llen:
|
||
|
l = lines[i]
|
||
|
incr = 1
|
||
|
for k in doc_data.keys():
|
||
|
if l.find(k, 0, 20) == 0:
|
||
|
value, incr = parse_pyobj(k, lines, i)
|
||
|
exec("doc_data['%s'] = %s" % (k, value))
|
||
|
has_doc = 1
|
||
|
break
|
||
|
i += incr
|
||
|
|
||
|
# fix these to seqs, simplifies coding elsewhere
|
||
|
for w in ['__author__', '__url__', '__email__']:
|
||
|
val = doc_data[w]
|
||
|
if val and type(val) == str:
|
||
|
doc_data[w] = [doc_data[w]]
|
||
|
|
||
|
if not doc_data['__bpydoc__']:
|
||
|
if doc_data['__doc__']:
|
||
|
doc_data['__bpydoc__'] = doc_data['__doc__']
|
||
|
|
||
|
if has_doc: # any data, maybe should confirm at least doc/bpydoc
|
||
|
info = BPy_Info(script, doc_data)
|
||
|
SCRIPT_INFO = info
|
||
|
return True
|
||
|
|
||
|
else:
|
||
|
return False
|
||
|
|
||
|
|
||
|
def parse_script_line(l):
|
||
|
|
||
|
tip = 'No tooltip'
|
||
|
try:
|
||
|
pieces = l.split("'")
|
||
|
name = pieces[1].replace('...','')
|
||
|
data = pieces[2].strip().split()
|
||
|
version = data[0]
|
||
|
userdir = data[-1]
|
||
|
fname = data[1]
|
||
|
i = 1
|
||
|
while not fname.endswith('.py'):
|
||
|
i += 1
|
||
|
fname = '%s %s' % (fname, data[i])
|
||
|
if len(pieces) > 3: tip = pieces[3]
|
||
|
except:
|
||
|
return None
|
||
|
|
||
|
return [name, int(version), fname, int(userdir), tip]
|
||
|
|
||
|
|
||
|
def parse_bpymenus(lines):
|
||
|
|
||
|
global AllGroups
|
||
|
|
||
|
llen = len(lines)
|
||
|
|
||
|
for i in range(llen):
|
||
|
l = lines[i].strip()
|
||
|
if not l: continue
|
||
|
if l[-1] == '{':
|
||
|
group = Group(l[:-2])
|
||
|
AllGroups.append(group)
|
||
|
i += 1
|
||
|
l = lines[i].strip()
|
||
|
while l != '}':
|
||
|
if l[0] != '|':
|
||
|
data = parse_script_line(l)
|
||
|
if data:
|
||
|
script = Script(data)
|
||
|
group.add_script(script)
|
||
|
i += 1
|
||
|
l = lines[i].strip()
|
||
|
|
||
|
# AllGroups.reverse()
|
||
|
|
||
|
|
||
|
def create_group_menus():
|
||
|
|
||
|
global AllGroups
|
||
|
menus = []
|
||
|
|
||
|
for group in AllGroups:
|
||
|
|
||
|
name = group.get_name()
|
||
|
menu = []
|
||
|
scripts = group.get_scripts()
|
||
|
for s in scripts: menu.append(s.name)
|
||
|
menu = "|".join(menu)
|
||
|
menu = "%s%%t|%s" % (name, menu)
|
||
|
menus.append([name, menu])
|
||
|
|
||
|
return menus
|
||
|
|
||
|
|
||
|
# Collecting data:
|
||
|
fit_on_screen()
|
||
|
parse_bpymenus(lines)
|
||
|
GROUP_MENUS = create_group_menus()
|
||
|
|
||
|
|
||
|
# GUI:
|
||
|
|
||
|
from Blender import BGL
|
||
|
from Blender.Window import Theme
|
||
|
|
||
|
# globals:
|
||
|
|
||
|
START_SCREEN = 0
|
||
|
SCRIPT_SCREEN = 1
|
||
|
|
||
|
SCREEN = START_SCREEN
|
||
|
|
||
|
# gui buttons:
|
||
|
len_gmenus = len(GROUP_MENUS)
|
||
|
|
||
|
BUT_GMENU = range(len_gmenus)
|
||
|
for i in range(len_gmenus):
|
||
|
BUT_GMENU[i] = Draw.Create(0)
|
||
|
|
||
|
# events:
|
||
|
BEVT_LINK = None # range(len(SCRIPT_INFO.links))
|
||
|
BEVT_EMAIL = None # range(len(SCRIPT_INFO.emails))
|
||
|
BEVT_GMENU = range(100, len_gmenus + 100)
|
||
|
BEVT_VIEWSOURCE = 1
|
||
|
BEVT_EXIT = 2
|
||
|
BEVT_BACK = 3
|
||
|
BEVT_EXEC = 4 # Executes Script
|
||
|
|
||
|
# gui callbacks:
|
||
|
|
||
|
def gui(): # drawing the screen
|
||
|
|
||
|
global SCREEN, START_SCREEN, SCRIPT_SCREEN
|
||
|
global SCRIPT_INFO, AllGroups, GROUP_MENUS
|
||
|
global BEVT_EMAIL, BEVT_LINK
|
||
|
global BEVT_VIEWSOURCE, BEVT_EXIT, BEVT_BACK, BEVT_GMENU, BUT_GMENU, BEVT_EXEC
|
||
|
global PADDING, WIN_W, WIN_H, SCROLL_DOWN, COLUMNS, FMODE
|
||
|
|
||
|
theme = Theme.Get()[0]
|
||
|
tui = theme.get('ui')
|
||
|
ttxt = theme.get('text')
|
||
|
|
||
|
COL_BG = float_colors(ttxt.back)
|
||
|
COL_TXT = ttxt.text
|
||
|
COL_TXTHI = ttxt.text_hi
|
||
|
|
||
|
BGL.glClearColor(COL_BG[0],COL_BG[1],COL_BG[2],COL_BG[3])
|
||
|
BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
|
||
|
BGL.glColor3ub(COL_TXT[0],COL_TXT[1], COL_TXT[2])
|
||
|
|
||
|
resize = screen_was_resized()
|
||
|
if resize: fit_on_screen()
|
||
|
|
||
|
if SCREEN == START_SCREEN:
|
||
|
x = PADDING
|
||
|
bw = 85
|
||
|
bh = 25
|
||
|
hincr = 50
|
||
|
|
||
|
butcolumns = (WIN_W - 2*x)/ bw
|
||
|
if butcolumns < 2: butcolumns = 2
|
||
|
elif butcolumns > 7: butcolumns = 7
|
||
|
|
||
|
len_gm = len(GROUP_MENUS)
|
||
|
butlines = len_gm / butcolumns
|
||
|
if len_gm % butcolumns: butlines += 1
|
||
|
|
||
|
h = hincr * butlines + 20
|
||
|
y = h + bh
|
||
|
|
||
|
BGL.glColor3ub(COL_TXTHI[0],COL_TXTHI[1], COL_TXTHI[2])
|
||
|
BGL.glRasterPos2i(x, y)
|
||
|
Draw.Text('Scripts Help Browser')
|
||
|
|
||
|
y -= bh
|
||
|
|
||
|
BGL.glColor3ub(COL_TXT[0],COL_TXT[1], COL_TXT[2])
|
||
|
|
||
|
i = 0
|
||
|
j = 0
|
||
|
for group_menu in GROUP_MENUS:
|
||
|
BGL.glRasterPos2i(x, y)
|
||
|
Draw.Text(group_menu[0]+':')
|
||
|
BUT_GMENU[j] = Draw.Menu(group_menu[1], BEVT_GMENU[j],
|
||
|
x, y-bh-5, bw, bh, 0,
|
||
|
'Choose a script to read its help information')
|
||
|
if i == butcolumns - 1:
|
||
|
x = PADDING
|
||
|
i = 0
|
||
|
y -= hincr
|
||
|
else:
|
||
|
i += 1
|
||
|
x += bw + 3
|
||
|
j += 1
|
||
|
|
||
|
x = PADDING
|
||
|
y = 10
|
||
|
BGL.glRasterPos2i(x, y)
|
||
|
Draw.Text('Select script for its help. Press Q or ESC to leave.')
|
||
|
|
||
|
elif SCREEN == SCRIPT_SCREEN:
|
||
|
if SCRIPT_INFO:
|
||
|
|
||
|
if resize:
|
||
|
SCRIPT_INFO.wrap_lines(1)
|
||
|
SCROLL_DOWN = 0
|
||
|
|
||
|
h = 18 * SCRIPT_INFO.len_content + 12 * SCRIPT_INFO.spaces
|
||
|
x = PADDING
|
||
|
y = WIN_H
|
||
|
bw = 38
|
||
|
bh = 16
|
||
|
|
||
|
BGL.glColor3ub(COL_TXTHI[0],COL_TXTHI[1], COL_TXTHI[2])
|
||
|
for line in SCRIPT_INFO.header:
|
||
|
y -= 18
|
||
|
BGL.glRasterPos2i(x, y)
|
||
|
size = Draw.Text(line)
|
||
|
|
||
|
for line in text_wrap('Tooltip: %s' % SCRIPT_INFO.script.tip):
|
||
|
y -= 18
|
||
|
BGL.glRasterPos2i(x, y)
|
||
|
size = Draw.Text(line)
|
||
|
|
||
|
i = 0
|
||
|
y -= 28
|
||
|
for data in SCRIPT_INFO.d['__url__']:
|
||
|
Draw.PushButton('link %d' % (i + 1), BEVT_LINK[i],
|
||
|
x + i*bw, y, bw, bh, data[0])
|
||
|
i += 1
|
||
|
y -= bh + 1
|
||
|
|
||
|
i = 0
|
||
|
for data in SCRIPT_INFO.d['__email__']:
|
||
|
Draw.PushButton('email', BEVT_EMAIL[i], x + i*bw, y, bw, bh, data[0])
|
||
|
i += 1
|
||
|
y -= 18
|
||
|
|
||
|
y0 = y
|
||
|
BGL.glColor3ub(COL_TXT[0],COL_TXT[1], COL_TXT[2])
|
||
|
for line in SCRIPT_INFO.content[SCROLL_DOWN:]:
|
||
|
if line:
|
||
|
line = line.replace('<br>', '')
|
||
|
BGL.glRasterPos2i(x, y)
|
||
|
Draw.Text(line)
|
||
|
y -= 18
|
||
|
else: y -= 12
|
||
|
if y < PADDING + 20: # reached end, either stop or go to 2nd column
|
||
|
if COLUMNS == 1: break
|
||
|
elif x == PADDING: # make sure we're still in column 1
|
||
|
x = 6*TEXT_WRAP + PADDING / 2
|
||
|
y = y0
|
||
|
|
||
|
x = PADDING
|
||
|
Draw.PushButton('source', BEVT_VIEWSOURCE, x, 17, 45, bh,
|
||
|
'View this script\'s source code in the Text Editor (hotkey: S)')
|
||
|
Draw.PushButton('exit', BEVT_EXIT, x + 45, 17, 45, bh,
|
||
|
'Exit from Scripts Help Browser (hotkey: Q)')
|
||
|
if not FMODE:
|
||
|
Draw.PushButton('back', BEVT_BACK, x + 2*45, 17, 45, bh,
|
||
|
'Back to scripts selection screen (hotkey: ESC)')
|
||
|
Draw.PushButton('run script', BEVT_EXEC, x + 3*45, 17, 60, bh, 'Run this script')
|
||
|
|
||
|
BGL.glColor3ub(COL_TXTHI[0],COL_TXTHI[1], COL_TXTHI[2])
|
||
|
BGL.glRasterPos2i(x, 5)
|
||
|
Draw.Text('use the arrow keys or the mouse wheel to scroll text', 'small')
|
||
|
|
||
|
def fit_scroll():
|
||
|
global SCROLL_DOWN
|
||
|
if not SCRIPT_INFO:
|
||
|
SCROLL_DOWN = 0
|
||
|
return
|
||
|
max = SCRIPT_INFO.len_content + SCRIPT_INFO.spaces - 1
|
||
|
if SCROLL_DOWN > max: SCROLL_DOWN = max
|
||
|
if SCROLL_DOWN < 0: SCROLL_DOWN = 0
|
||
|
|
||
|
|
||
|
def event(evt, val): # input events
|
||
|
|
||
|
global SCREEN, START_SCREEN, SCRIPT_SCREEN
|
||
|
global SCROLL_DOWN, FMODE
|
||
|
|
||
|
if not val: return
|
||
|
|
||
|
if evt == Draw.ESCKEY:
|
||
|
if SCREEN == START_SCREEN or FMODE: Draw.Exit()
|
||
|
else:
|
||
|
SCREEN = START_SCREEN
|
||
|
SCROLL_DOWN = 0
|
||
|
Draw.Redraw()
|
||
|
return
|
||
|
elif evt == Draw.QKEY:
|
||
|
Draw.Exit()
|
||
|
return
|
||
|
elif evt in [Draw.DOWNARROWKEY, Draw.WHEELDOWNMOUSE] and SCREEN == SCRIPT_SCREEN:
|
||
|
SCROLL_DOWN += 1
|
||
|
fit_scroll()
|
||
|
Draw.Redraw()
|
||
|
return
|
||
|
elif evt in [Draw.UPARROWKEY, Draw.WHEELUPMOUSE] and SCREEN == SCRIPT_SCREEN:
|
||
|
SCROLL_DOWN -= 1
|
||
|
fit_scroll()
|
||
|
Draw.Redraw()
|
||
|
return
|
||
|
elif evt == Draw.SKEY:
|
||
|
if SCREEN == SCRIPT_SCREEN and SCRIPT_INFO:
|
||
|
load_script_text(SCRIPT_INFO.script)
|
||
|
return
|
||
|
|
||
|
def button_event(evt): # gui button events
|
||
|
|
||
|
global SCREEN, START_SCREEN, SCRIPT_SCREEN
|
||
|
global BEVT_LINK, BEVT_EMAIL, BEVT_GMENU, BUT_GMENU, SCRIPT_INFO
|
||
|
global SCROLL_DOWN, FMODE
|
||
|
|
||
|
if evt >= 100: # group menus
|
||
|
for i in range(len(BUT_GMENU)):
|
||
|
if evt == BEVT_GMENU[i]:
|
||
|
group = AllGroups[i]
|
||
|
index = BUT_GMENU[i].val - 1
|
||
|
if index < 0: return # user didn't pick a menu entry
|
||
|
script = group.get_scripts()[BUT_GMENU[i].val - 1]
|
||
|
if parse_help_info(script):
|
||
|
SCREEN = SCRIPT_SCREEN
|
||
|
BEVT_LINK = range(20, len(SCRIPT_INFO.d['__url__']) + 20)
|
||
|
BEVT_EMAIL = range(50, len(SCRIPT_INFO.d['__email__']) + 50)
|
||
|
Draw.Redraw()
|
||
|
else:
|
||
|
res = Draw.PupMenu("No help available%t|View Source|Cancel")
|
||
|
if res == 1:
|
||
|
load_script_text(script)
|
||
|
elif evt >= 20:
|
||
|
if not WEBBROWSER:
|
||
|
Draw.PupMenu('Missing standard Python module%t|You need module "webbrowser" to access the web')
|
||
|
return
|
||
|
|
||
|
if evt >= 50: # script screen email buttons
|
||
|
email = SCRIPT_INFO.d['__email__'][evt - 50][1]
|
||
|
webbrowser.open("mailto:%s" % email)
|
||
|
else: # >= 20: script screen link buttons
|
||
|
link = SCRIPT_INFO.d['__url__'][evt - 20][1]
|
||
|
webbrowser.open(link)
|
||
|
elif evt == BEVT_VIEWSOURCE:
|
||
|
if SCREEN == SCRIPT_SCREEN: load_script_text(SCRIPT_INFO.script)
|
||
|
elif evt == BEVT_EXIT:
|
||
|
Draw.Exit()
|
||
|
return
|
||
|
elif evt == BEVT_BACK:
|
||
|
if SCREEN == SCRIPT_SCREEN and not FMODE:
|
||
|
SCREEN = START_SCREEN
|
||
|
SCRIPT_INFO = None
|
||
|
SCROLL_DOWN = 0
|
||
|
Draw.Redraw()
|
||
|
elif evt == BEVT_EXEC: # Execute script
|
||
|
exec_line = ''
|
||
|
if SCRIPT_INFO.script.userdir:
|
||
|
exec_line = bsys.join(Blender.Get('uscriptsdir'), SCRIPT_INFO.script.fname)
|
||
|
else:
|
||
|
exec_line = bsys.join(Blender.Get('scriptsdir'), SCRIPT_INFO.script.fname)
|
||
|
|
||
|
Blender.Run(exec_line)
|
||
|
|
||
|
keepon = True
|
||
|
FMODE = False # called by Blender.ShowHelp(name) API function ?
|
||
|
|
||
|
KEYNAME = '__help_browser'
|
||
|
rd = Registry.GetKey(KEYNAME)
|
||
|
if rd:
|
||
|
rdscript = rd['script']
|
||
|
keepon = False
|
||
|
Registry.RemoveKey(KEYNAME)
|
||
|
for group in AllGroups:
|
||
|
for script in group.get_scripts():
|
||
|
if rdscript == script.fname:
|
||
|
parseit = parse_help_info(script)
|
||
|
if parseit == True:
|
||
|
keepon = True
|
||
|
SCREEN = SCRIPT_SCREEN
|
||
|
BEVT_LINK = range(20, len(SCRIPT_INFO.d['__url__']) + 20)
|
||
|
BEVT_EMAIL = range(50, len(SCRIPT_INFO.d['__email__']) + 50)
|
||
|
FMODE = True
|
||
|
elif parseit == False:
|
||
|
Draw.PupMenu("ERROR: script doesn't have proper help data")
|
||
|
break
|
||
|
|
||
|
if not keepon:
|
||
|
Draw.PupMenu("ERROR: couldn't find script")
|
||
|
else:
|
||
|
Draw.Register(gui, event, button_event)
|