256
265
return tuple(int(value[i:i+lv/3], 16) for i in range(0, lv, lv/3))
258
################## AppleScript for Mac bundle ##################
260
terminal_close_server_script = """tell application "Terminal"
264
terminal_close_server_script = convert_line_endings(terminal_close_server_script, 1)
265
terminal_close_server_script_path = os.path.join(TEMP_PATH, "terminal_close_server_script.scpt")
267
terminal_server_script = """tell application "Terminal"
269
set a to get id of front window
270
set custom title of window id a to "E-Pyo Output"
271
set custom title of tab 1 of window id a to "E-Pyo Output"
272
set current settings of first window to settings set "Homebrew"
273
set the number of columns of window 1 to 80
274
set the number of rows of window 1 to 30
275
set the position of window 1 to {810, 25}
278
terminal_server_script = convert_line_endings(terminal_server_script, 1)
279
terminal_server_script_path = os.path.join(TEMP_PATH, "terminal_server_script.scpt")
280
f = open(terminal_server_script_path, "w")
281
f.write(terminal_server_script)
283
pid = subprocess.Popen(["osascript", terminal_server_script_path]).pid
285
terminal_client_script = """set my_path to quoted form of POSIX path of "%s"
286
set my_file to quoted form of POSIX path of "%s"
287
set which_python to quoted form of POSIX path of "%s"
288
tell application "System Events"
289
tell application process "Terminal"
290
set frontmost to true
294
keystroke "cd " & my_path
297
keystroke "%s" & which_python & " " & my_file & " &"
301
tell application process "E-Pyo"
302
set frontmost to true
306
terminal_client_script_path = os.path.join(TEMP_PATH, "terminal_client_script.scpt")
308
267
################## TEMPLATES ##################
309
268
HEADER_TEMPLATE = """#!/usr/bin/env python
310
269
# encoding: utf-8
744
703
snip_faces = {'face': DEFAULT_FONT_FACE, 'size': FONT_SIZE}
705
class RunningThread(threading.Thread):
706
def __init__(self, path, cwd, outputlog, removeProcess):
707
threading.Thread.__init__(self)
710
self.outputlog = outputlog
711
self.removeProcess = removeProcess
712
self.terminated = False
715
def setFileName(self, filename):
716
self.filename = filename
718
def setPID(self, pid):
722
self.terminated = True
723
if PLATFORM == "win32":
725
os.system("tskill %d" % self.proc.pid)
727
print "'tskill' doesn't seem to be installed on the system. It is needed to be able to kill a process."
729
self.proc.terminate()
730
if self.proc.poll() == None:
735
vars_to_remove = "PYTHONHOME PYTHONPATH EXECUTABLEPATH RESOURCEPATH ARGVZERO PYTHONOPTIMIZE"
736
prelude = "export -n %s;export PATH=/usr/local/bin:/usr/local/lib:$PATH;" % vars_to_remove
737
if CALLER_NEED_TO_INVOKE_32_BIT:
738
self.proc = subprocess.Popen(['%s%s%s "%s"' % (prelude, SET_32_BIT_ARCH, WHICH_PYTHON, self.path)],
739
shell=True, cwd=self.cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
741
self.proc = subprocess.Popen(['%s%s "%s"' % (prelude, WHICH_PYTHON, self.path)], cwd=self.cwd,
742
shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
743
elif PLATFORM == "darwin":
744
if CALLER_NEED_TO_INVOKE_32_BIT:
745
self.proc = subprocess.Popen(['%s%s "%s"' % (SET_32_BIT_ARCH, WHICH_PYTHON, self.path)],
746
shell=True, cwd=self.cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
748
self.proc = subprocess.Popen(['%s "%s"' % (WHICH_PYTHON, self.path)], cwd=self.cwd,
749
shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
750
elif PLATFORM == "win32":
751
self.proc = subprocess.Popen([WHICH_PYTHON, self.path], cwd=self.cwd, shell=False,
752
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
754
self.proc = subprocess.Popen([WHICH_PYTHON, self.path], cwd=self.cwd,
755
shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
757
while self.proc.poll() == None and not self.terminated:
759
stdout, stderr = self.proc.communicate()
760
header = '=== Output log of process "%s", launched: %s ===\n' % (self.filename, time.strftime('"%d %b %Y %H:%M:%S"', time.localtime()))
761
output = header + stdout + stderr
762
if "StartNotification name = default" in output:
763
output = output.replace("StartNotification name = default", "")
764
if "epyo_tempfile.py" in output:
766
findpos = output.find("epyo_tempfile.py")
768
while (output[pos] != '"'):
772
while (output[pos] != '"'):
775
output = output[:startpos] + self.filename + output[endpos:]
776
pos = startpos + len(self.filename)
779
while (output[pos] != ',' and output[pos] != '\n'):
782
linenum = int(output[slinepos:elinepos].strip())
783
output = output[:slinepos] + str(linenum-2) + output[elinepos:]
787
output = output + "\n=== Process killed. ==="
788
self.outputlog(output)
789
self.removeProcess(self.pid, self.filename)
746
791
class KeyCommandsFrame(wx.Frame):
747
792
def __init__(self, parent):
748
793
wx.Frame.__init__(self, parent, wx.ID_ANY, title="Editor Key Commands List", size=(650,550))
2443
2523
newtext += line + "\n"
2526
def appendLog(self, text):
2527
self.panel.outputlog.setLog(text)
2446
2529
def run(self, path):
2447
2530
cwd = self.getCurrentWorkingDirectory()
2449
if CALLER_NEED_TO_INVOKE_32_BIT:
2450
script = terminal_client_script % (cwd, path, WHICH_PYTHON, SET_32_BIT_ARCH)
2452
script = terminal_client_script % (cwd, path, WHICH_PYTHON, "")
2453
script = convert_line_endings(script, 1)
2454
with codecs.open(terminal_client_script_path, "w", encoding="utf-8") as f:
2456
pid = subprocess.Popen(["osascript", terminal_client_script_path]).pid
2457
elif PLATFORM == "darwin":
2458
if CALLER_NEED_TO_INVOKE_32_BIT:
2459
pid = subprocess.Popen(["%s%s %s" % (SET_32_BIT_ARCH, WHICH_PYTHON, path)], cwd=cwd, shell=True).pid
2461
pid = subprocess.Popen([WHICH_PYTHON, path], cwd=cwd).pid
2462
elif PLATFORM == "linux2":
2463
pid = subprocess.Popen([WHICH_PYTHON, path], cwd=cwd).pid
2464
elif PLATFORM == "win32":
2465
pid = subprocess.Popen([WHICH_PYTHON, path], cwd=cwd).pid
2531
th = RunningThread(path, cwd, self.appendLog, self.panel.outputlog.removeProcess)
2532
if "Untitled-" in self.panel.editor.path:
2533
filename = self.panel.editor.path
2535
filename = os.path.split(self.panel.editor.path)[1]
2536
th.setFileName(filename)
2537
th.setPID(self.processID)
2538
self.processes[self.processID] = [th, filename]
2539
self.panel.outputlog.addProcess(self.processID, filename)
2467
2543
def runner(self, event):
2468
2544
text = self.panel.editor.GetTextUTF8()
3766
3846
line = line + 1
3849
class SimpleEditor(stc.StyledTextCtrl):
3850
def __init__(self, parent, ID=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style= wx.NO_BORDER):
3851
stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
3855
self.alphaStr = string.lowercase + string.uppercase + '0123456789'
3857
self.Colourise(0, -1)
3858
self.SetCurrentPos(0)
3860
self.SetBackSpaceUnIndents(True)
3861
self.SetTabIndents(True)
3863
self.SetUseTabs(False)
3864
self.SetEOLMode(wx.stc.STC_EOL_LF)
3865
self.SetPasteConvertEndings(True)
3866
self.SetControlCharSymbol(32)
3867
self.SetLayoutCache(True)
3869
self.SetViewWhiteSpace(0)
3872
self.SetWrapMode(stc.STC_WRAP_WORD)
3874
self.SetUseAntiAliasing(True)
3875
self.SetEdgeColour(STYLES["lineedge"]['colour'])
3876
self.SetEdgeColumn(78)
3877
self.SetReadOnly(True)
3881
def buildStyle(forekey, backkey=None, smallsize=False):
3883
st = "face:%s,fore:%s,size:%s" % (STYLES['face'], STYLES[forekey]['colour'], STYLES['size2'])
3885
st = "face:%s,fore:%s,size:%s" % (STYLES['face'], STYLES[forekey]['colour'], STYLES['size'])
3887
st += ",back:%s" % STYLES[backkey]['colour']
3888
if STYLES[forekey].has_key('bold'):
3889
if STYLES[forekey]['bold']:
3891
if STYLES[forekey]['italic']:
3893
if STYLES[forekey]['underline']:
3896
self.StyleSetSpec(stc.STC_STYLE_DEFAULT, buildStyle('default', 'background'))
3897
self.StyleClearAll() # Reset all to be like the default
3899
self.StyleSetSpec(stc.STC_STYLE_DEFAULT, buildStyle('default', 'background'))
3900
self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, buildStyle('linenumber', 'marginback', True))
3901
self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, buildStyle('default') + ",size:5")
3902
self.SetEdgeColour(STYLES["lineedge"]['colour'])
3903
self.SetCaretForeground(STYLES['caret']['colour'])
3904
self.SetSelBackground(1, STYLES['selback']['colour'])
3905
self.SetFoldMarginColour(True, STYLES['foldmarginback']['colour'])
3906
self.SetFoldMarginHiColour(True, STYLES['foldmarginback']['colour'])
3908
class OutputLogEditor(SimpleEditor):
3909
def __init__(self, parent, ID=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style= wx.NO_BORDER):
3910
SimpleEditor.__init__(self, parent=parent, ID=ID, pos=pos, size=size, style=style)
3912
def setLog(self, text):
3913
self.SetReadOnly(False)
3915
self.SetReadOnly(True)
3917
class OutputLogPanel(wx.Panel):
3918
def __init__(self, parent, mainPanel, size=(175,400)):
3919
wx.Panel.__init__(self, parent, wx.ID_ANY, size=size, style=wx.SUNKEN_BORDER)
3920
self.mainPanel = mainPanel
3924
close_panel_bmp = catalog['close_panel_icon.png'].GetBitmap()
3926
self.sizer = wx.BoxSizer(wx.VERTICAL)
3928
toolbarbox = wx.BoxSizer(wx.HORIZONTAL)
3929
self.toolbar = wx.ToolBar(self, -1, size=(-1,36))
3930
self.toolbar.SetMargins((5, 0))
3931
font, psize = self.toolbar.GetFont(), self.toolbar.GetFont().GetPointSize()
3932
if PLATFORM == "darwin":
3933
font.SetPointSize(psize-1)
3934
self.toolbar.SetToolBitmapSize(tsize)
3936
if PLATFORM == "win32":
3937
self.toolbar.AddSeparator()
3938
title = wx.StaticText(self.toolbar, -1, "Output panel")
3940
self.toolbar.AddControl(title)
3941
self.toolbar.AddSeparator()
3942
if PLATFORM == "win32":
3943
self.toolbar.AddSeparator()
3944
self.processPopup = wx.Choice(self.toolbar, -1, choices=[])
3945
self.processPopup.SetFont(font)
3946
self.toolbar.AddControl(self.processPopup)
3947
if PLATFORM == "win32":
3948
self.toolbar.AddSeparator()
3949
self.processKill = wx.Button(self.toolbar, -1, label="Kill", size=(40,-1))
3950
self.processKill.SetFont(font)
3951
self.toolbar.AddControl(self.processKill)
3952
self.processKill.Bind(wx.EVT_BUTTON, self.killProcess)
3953
if PLATFORM == "win32":
3954
self.toolbar.AddSeparator()
3955
self.runningLabel = wx.StaticText(self.toolbar, -1, "Running: 0")
3956
self.runningLabel.SetFont(font)
3957
self.toolbar.AddControl(self.runningLabel)
3958
self.toolbar.AddSeparator()
3959
self.copyLog = wx.Button(self.toolbar, -1, label="Copy log", size=(70,-1))
3960
self.copyLog.SetFont(font)
3961
self.toolbar.AddControl(self.copyLog)
3962
self.copyLog.Bind(wx.EVT_BUTTON, self.onCopy)
3963
self.toolbar.AddSeparator()
3964
zoomLabel = wx.StaticText(self.toolbar, -1, "Zoom:")
3965
zoomLabel.SetFont(font)
3966
self.toolbar.AddControl(zoomLabel)
3967
if PLATFORM == "win32":
3968
self.toolbar.AddSeparator()
3969
self.zoomer = wx.SpinCtrl(self.toolbar, -1, "0", size=(60, -1))
3970
self.zoomer.SetRange(-10,10)
3971
self.toolbar.AddControl(self.zoomer)
3972
self.zoomer.Bind(wx.EVT_SPINCTRL, self.onZoom)
3973
self.toolbar.Realize()
3974
toolbarbox.Add(self.toolbar, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 0)
3976
tb2 = wx.ToolBar(self, -1, size=(-1,36))
3977
tb2.SetToolBitmapSize(tsize)
3978
tb2.AddLabelTool(17, "Close Panel", close_panel_bmp, shortHelp="Close Panel")
3980
toolbarbox.Add(tb2, 0, wx.ALIGN_RIGHT, 0)
3982
wx.EVT_TOOL(self, 17, self.onCloseOutputPanel)
3984
self.sizer.Add(toolbarbox, 0, wx.EXPAND)
3986
self.editor = OutputLogEditor(self, size=(-1, -1))
3987
self.sizer.Add(self.editor, 1, wx.EXPAND|wx.ALL, 0)
3989
self.SetSizer(self.sizer)
3991
def onZoom(self, evt):
3992
self.editor.SetZoom(self.zoomer.GetValue())
3994
def onCopy(self, evt):
3995
self.editor.SelectAll()
3997
self.editor.SetAnchor(0)
3999
def addProcess(self, procID, filename):
4000
self.processPopup.Append("%d :: %s" % (procID, filename))
4001
self.processPopup.SetStringSelection("%d :: %s" % (procID, filename))
4003
self.runningLabel.SetLabel("Running: %d" % self.running)
4005
def removeProcess(self, procID, filename):
4006
str = "%d :: %s" % (procID, filename)
4007
del self.mainPanel.mainFrame.processes[procID]
4008
self.processPopup.Delete(self.processPopup.GetItems().index(str))
4010
self.runningLabel.SetLabel("Running: %d" % self.running)
4012
def killProcess(self, evt):
4013
str = self.processPopup.GetStringSelection()
4015
procID = int(str.split("::")[0].strip())
4016
thread = self.mainPanel.mainFrame.processes[procID][0]
4019
def setLog(self, text):
4020
self.editor.setLog(text)
4022
def onCloseOutputPanel(self, evt):
4023
self.mainPanel.mainFrame.showOutputPanel(False)
4025
class PastingListEditorFrame(wx.Frame):
4026
def __init__(self, parent, pastingList):
4027
wx.Frame.__init__(self, parent, wx.ID_ANY, title="Pasting List Editor ", size=(700,500))
4028
self.parent = parent
4029
self.menuBar = wx.MenuBar()
4031
menu1.Append(351, "Close\tCtrl+W")
4032
self.menuBar.Append(menu1, 'File')
4033
self.SetMenuBar(self.menuBar)
4035
self.Bind(wx.EVT_MENU, self.close, id=351)
4036
self.Bind(wx.EVT_CLOSE, self.close)
4038
mainSizer = wx.BoxSizer(wx.VERTICAL)
4039
panel = scrolled.ScrolledPanel(self)
4041
if PLATFORM == "darwin":
4045
if pastingList != []:
4046
for line in pastingList:
4047
editor = SimpleEditor(panel, style=wx.SUNKEN_BORDER)
4048
editor.SetReadOnly(False)
4049
height = editor.TextHeight(0) * len(line.splitlines()) + editor.TextHeight(0) * 2
4051
editor.SetMinSize((-1, height))
4052
editor.SetMaxSize((-1, height))
4053
mainSizer.Add(editor, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 0)
4054
self.editors.append(editor)
4055
if not line.endswith("\n"):
4058
editor.AddTextUTF8(line)
4060
editor.AddText(line)
4062
Y = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)
4063
if heightSum > Y - 100:
4064
self.SetSize((-1, Y - 100))
4066
self.SetSize((-1, heightSum))
4067
panel.SetSizer(mainSizer)
4068
panel.SetAutoLayout(1)
4069
panel.SetupScrolling()
4071
def close(self, evt):
4073
for editor in self.editors:
4074
text = editor.GetTextUTF8()
4075
if text.replace("\n", "").strip() != "":
4076
pastingList.append(text)
4077
self.parent.pastingList = pastingList
3769
4080
TOOL_ADD_FILE_ID = 10
3770
4081
TOOL_ADD_FOLDER_ID = 11
3771
4082
class ProjectTree(wx.Panel):