'''
This software is licensed under the GPL (GNU General Public License) version 2
as it appears here: http://www.gnu.org/copyleft/gpl.html
It is also included with this archive as `gpl.txt <gpl.txt>`_.
'''
import os
import sys
import todo
import imp
import time
import random
import mylistmix
import keydialog
import thread
import threading
__main__ = _pype
import wx
macropath = os.path.join(_pype.runpath, 'macros')
columns = (
(0, "Macro Name", 150, 0),
(1, "Hotkey", 50, 0),
)
class macroList(todo.vTodo, mylistmix.ListSelect):
def __init__(self, parent, columns):
todo.vTodo.__init__(self, parent, columns)
self.parent = parent
def Refresh(self):
self.SetItemCount(0)
## try:
## wx.Yield()
## except:
## pass
self.SetItemCount(len(self.data))
todo.vTodo.Refresh(self)
def OnGetItemText(self, item, col):
if col == 0:
return "%s" % (self.data[item][col],)
elif col == 1:
x = self.parent.d.get(self.data[item][1], (None, None))[1]
if hasattr(x, "hotkeydisplay"):
return "%s"%(x.hotkeydisplay,)
elif hasattr(x, "hotkeyaccept"):
return "%s"%(x.hotkeyaccept,)
return ""
def OnGetItemAttr(self, item):
if not self.parent.d[self.data[item][1]][1]:
return red
return None
template = '''
creation_date = %r
name = %r
hotkeydisplay = ""
hotkeyaccept = ""
%s
'''
hlp = '''\
#Copy and paste the following code into the
#source of the macro that you would like to
#have this hotkey bound to.
%(h)s
#for example:
#...
%(d)s
#...
#def macro(self):
# ...
'''
def kill_main_thread_in(condition, seconds=6):
a = threading.Thread(target=_kill_main_thread_in, args=(condition, seconds))
a.setDaemon(1)
a.start()
def _kill_main_thread_in(condition, seconds):
time.sleep(seconds)
if condition:
thread.interrupt_main()
red = wx.ListItemAttr()
red.SetTextColour(wx.Colour(200, 0, 0))
timeout = None
def macro_timeout(a,b,c):
del a, b, c
if time.time() > timeout:
raise KeyboardInterrupt, "Macro took more than 5 seconds!"
try:
wx.Yield()
except:
pass
return macro_timeout
def start_macro():
global timeout
timeout = time.time() + 5
sys.settrace(macro_timeout)
def end_macro():
sys.settrace(None)
def rpartition(str, sep):
if not sep in str:
return str, ''
i = str.rfind(sep)
return str[:i], str[i+len(sep):]
nostart = '''\
Couldn't start macro because some other long-term action is
already being performed within PyPE. Please wait a few
moments until it is complete, and try again.'''
def load_module(name, fname):
x = imp.new_module(name)
x.__file__ = fname
x.__name__ = name
x.__builtins__ = __builtins__
execfile(fname, x.__dict__)
return x
def Button1(parent, id, which, help, extra):
bitmap = wx.ArtProvider_GetBitmap(which, wx.ART_TOOLBAR, (16,16))
z = wx.BitmapButton(parent, id, bitmap, (16,16), (26,26))
z.SetToolTipString(help)
return z
def Button2(parent, id, which, help, extra):
z = wx.Button(parent, id, extra)
z.SetToolTipString(help)
return z
class macroPanel(wx.Panel):
def __init__(self, parent, root):
wx.Panel.__init__(self, parent)
self.root = root
if __main__.macro_images:
bmButton = Button1
else:
bmButton = Button2
self.rec1 = bmButton(self, -1, wx.ART_CDROM, "Start Recording", "Record")
self.rec2 = bmButton(self, -1, wx.ART_ERROR, "Stop Recording", "Stop!"); self.rec2.Hide()
self.edit = bmButton(self, -1, wx.ART_FOLDER_OPEN, "Edit Macro", "Edit")
self.empty = bmButton(self, -1, wx.ART_NEW, "New Empty Macro", "New")
self.hotkey = bmButton(self, -1, wx.ART_ADD_BOOKMARK, "Create Hotkey", "Hotkey")
self.play = bmButton(self, -1, wx.ART_REDO, "Run Macro", "Run")
self.de1 = bmButton(self, -1, wx.ART_DELETE, "Delete Macro", "Delete")
sz = wx.BoxSizer(wx.HORIZONTAL)
a = (1, wx.EXPAND|wx.ALL, 2)
sz.Add(self.rec1, *a)
sz.Add(self.rec2, *a)
sz.Add(self.edit, *a)
sz.Add(self.empty, *a)
sz.Add(self.hotkey, *a)
sz.Add(self.play, *a)
sz.Add(self.de1, *a)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(sz, 0, wx.EXPAND)
self.macros = macroList(self, columns)
sizer.Add(self.macros, 1, wx.EXPAND|wx.ALL, 3)
self.SetSizer(sizer)
self.m = []
self.d = {}
self.hotkeys = {}
self.macros.setData(self.m, copy=0)
self.accelerator = ''
self.acceleratork = ''
self.macros.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated)
self.rec1.Bind(wx.EVT_BUTTON, self.OnRec)
self.rec2.Bind(wx.EVT_BUTTON, self.OnRec)
self.edit.Bind(wx.EVT_BUTTON, self.OnEdit)
self.empty.Bind(wx.EVT_BUTTON, self.OnEmpty)
self.hotkey.Bind(wx.EVT_BUTTON, self.GetHotkey)
self.play.Bind(wx.EVT_BUTTON, self.OnPlay)
self.de1.Bind(wx.EVT_BUTTON, self.OnDel)
## self.t = wx.Timer(self)
## self.Bind(wx.EVT_TIMER, self.CheckMacros)
## self.t.Start(1000, wx.TIMER_CONTINUOUS)
## self.t = Timer(self.CheckMacros)
## self.t.Start(1000, oneShot=False)
__main__.CheckMacros = self.CheckMacros
wx.CallAfter(self.CheckMacros, None)
def RunMacro(self, hotkey):
if hotkey not in self.hotkeys:
return 0
which = self.hotkeys[hotkey]
self.macros.SelectI(which)
wx.CallAfter(self.OnPlay, None)
return 1
def update_button(self, stc=None):
if not stc:
stc = self.root.getNumWin(None)[1]
if not isinstance(stc, _pype.PythonSTC):
return
if stc.recording:
self.rec1.Hide()
self.rec2.Show()
self.Layout()
else:
self.rec1.Show()
self.rec2.Hide()
self.Layout()
def OnItemActivated(self, e):
dc = __main__.macro_doubleclick
if dc == 0:
return
elif dc == 1:
self.OnEdit(e)
elif dc == 2:
self.OnPlay(e)
def CheckMacros(self, e=None):
try:
a = os.listdir(macropath)
except:
return
changed = 0
## self.m = [] #[(name, file), (name, file), ...]
## self.d = {} #file : (mtime, module)
notseen = dict.fromkeys(self.d)
for name in a:
if not (name.endswith('.py') or name.endswith('.pyw')):
if name.endswith('.pyc') or name.endswith('.pyo'):
try:
os.remove(os.path.join(macropath, name))
except:
pass
continue
file = os.path.join(macropath, name)
name, ext = rpartition(name, '.')
try:
mtime = os.stat(file).st_mtime
except:
continue
_ = notseen.pop(file, None)
if file in self.d and self.d[file][0] == mtime:
#the file hasn't changed
continue
module = None
try:
module = load_module('_'+name, file)
except:
#module load failure...
#should probably report the exception...
pass
if hasattr(module, 'name'):
name = module.name
_ = notseen.pop(file, None)
if file not in self.d:
self.m.append((name, file))
self.d[file] = (mtime, module)
## print "new", file
else:
for i, f in enumerate(self.m):
if f[1] == file:
self.m[i] = (name, file)
break
self.d[file] = (mtime, module)
## print "updated", file
changed = 1
for f in notseen:
i = 0
while i < len(self.m):
j = self.m[i]
if j[1] == f:
del self.m[i]
del self.d[j[1]]
## print "deleted", f
changed = 1
break
else:
i += 1
else:
## print "what?", f
#not in either list?
pass
if changed:
self.hotkeys.clear()
for i, (n,f) in enumerate(self.m):
x = self.d[f][1]
hk = None
if hasattr(x, "hotkeyaccept"):
hk = "%s"%(x.hotkeyaccept,)
elif hasattr(x, "hotkeydisplay"):
hk = "%s"%(x.hotkeydisplay,)
if hk and hk not in self.hotkeys:
self.hotkeys[hk] = i
self.macros.Refresh()
def OnRec(self, e):
num, stc = self.root.getNumWin(None)
if not isinstance(stc, _pype.PythonSTC):
return
stc.MacroToggle(None)
self.update_button()
if not stc.recording:
if stc.macro:
source = stc._macro_to_source()
self.OnEmpty(e, source)
def OnEdit(self, e):
x = self.macros.GetFirstSelected()
if x == -1:
return
self.root.OnDrop([self.m[x][1]])
def OnEmpty(self, e, source=None):
if source == None:
source = 'def macro(self):\n pass'
fname = 'macro_%i_%i.py'%(int(time.time()), random.randrange(65536))
ctime = time.asctime()
name = 'macro: %s'%ctime
open(os.path.join(macropath, fname), 'w').write(template%(ctime, name, source))
self.CheckMacros(None)
def OnPlay(self, e):
## if self.play.GetLabel() == "Stop\nMacro":
## global timeout
## timeout = None
## return
x = self.macros.GetFirstSelected()
if x == -1:
return
module = self.d[self.m[x][1]][1]
if not module:
return
if not hasattr(module, 'macro'):
return
stc = self.root.getNumWin(None)[1]
if not isinstance(stc, _pype.PythonSTC):
return
if stc.recording:
self.root.dialog("You must stop recording a macro\nin order to play a macro.", 'Sorry')
return
try:
wx.Yield()
except:
self.root.dialog(nostart, 'Sorry')
return
## self.play.SetLabel("Stop\nMacro")
finished = 0
start_macro()
condition = [None]
kill_main_thread_in(condition)
try:
try:
try:
getattr(module, 'macro')(stc)
except Exception, why:
_ = condition.pop()
end_macro()
finished = 1
self.root.exceptDialog("Failure in macro!")
finally:
if condition:
_ = condition.pop()
if not finished:
end_macro()
finished = 1
finally:
#We try to cleanup twice to guarantee that the cleanup happens;
#the kill_main_thread_in() function only tries to kill us once.
if condition:
_ = condition.pop()
if not finished:
end_macro()
finished = 1
def OnDel(self, e):
x = self.macros.GetFirstSelected()
if x == -1:
return
dlg = wx.MessageDialog(self,
('Are you sure you want to delete the\n'
'the macro with name: %s\n'
'with location: %s'
)%self.m[x],
'Are you sure?',
wx.YES_NO | wx.ICON_INFORMATION | wx.NO_DEFAULT, pos=(0,0)
)
yesno = dlg.ShowModal()
dlg.Destroy()
if yesno != wx.ID_YES:
return
try:
os.remove(self.m[x][1])
except:
self.root.exceptDialog("Macro deletion failed")
self.CheckMacros(None)
def GetHotkey(self, e):
self.accelerator = ''
self.acceleratork = ''
dlg = keydialog.GetKeyDialog(self, '', '', '')
dlg.ShowModal()
if not (self.accelerator or self.acceleratork):
return
ha = 'hotkeyaccept = %r'%self.accelerator
hd = 'hotkeydisplay = %r'%self.acceleratork
if self.accelerator == self.acceleratork:
h = ha
d = '#'+h
else:
h = '\n'.join((ha,hd))
d = '\n'.join(('#'+ha,'#'+hd))
dlg = wx.lib.dialogs.ScrolledMessageDialog(self, hlp%locals(), "Your hotkey", pos=(0,0))
dlg.ShowModal()
dlg.Destroy()
|