first commit

This commit is contained in:
Ayxan
2022-05-23 00:16:32 +04:00
commit d660f2a4ca
24786 changed files with 4428337 additions and 0 deletions

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,30 @@
Unless stated in the specfic source file, this work is
Copyright (c) 1994-2008, Mark Hammond
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
Neither name of Mark Hammond nor the name of contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,258 @@
# basictimerapp - a really simple timer application.
# This should be run using the command line:
# pythonwin /app demos\basictimerapp.py
import win32ui
import win32api
import win32con
import sys
from pywin.framework import app, cmdline, dlgappcore, cmdline
import timer
import time
import string
class TimerAppDialog(dlgappcore.AppDialog):
softspace = 1
def __init__(self, appName=""):
dlgappcore.AppDialog.__init__(self, win32ui.IDD_GENERAL_STATUS)
self.timerAppName = appName
self.argOff = 0
if len(self.timerAppName) == 0:
if len(sys.argv) > 1 and sys.argv[1][0] != "/":
self.timerAppName = sys.argv[1]
self.argOff = 1
def PreDoModal(self):
# sys.stderr = sys.stdout
pass
def ProcessArgs(self, args):
for arg in args:
if arg == "/now":
self.OnOK()
def OnInitDialog(self):
win32ui.SetProfileFileName("pytimer.ini")
self.title = win32ui.GetProfileVal(
self.timerAppName, "Title", "Remote System Timer"
)
self.buildTimer = win32ui.GetProfileVal(
self.timerAppName, "Timer", "EachMinuteIntervaler()"
)
self.doWork = win32ui.GetProfileVal(self.timerAppName, "Work", "DoDemoWork()")
# replace "\n" with real \n.
self.doWork = self.doWork.replace("\\n", "\n")
dlgappcore.AppDialog.OnInitDialog(self)
self.SetWindowText(self.title)
self.prompt1 = self.GetDlgItem(win32ui.IDC_PROMPT1)
self.prompt2 = self.GetDlgItem(win32ui.IDC_PROMPT2)
self.prompt3 = self.GetDlgItem(win32ui.IDC_PROMPT3)
self.butOK = self.GetDlgItem(win32con.IDOK)
self.butCancel = self.GetDlgItem(win32con.IDCANCEL)
self.prompt1.SetWindowText("Python Timer App")
self.prompt2.SetWindowText("")
self.prompt3.SetWindowText("")
self.butOK.SetWindowText("Do it now")
self.butCancel.SetWindowText("Close")
self.timerManager = TimerManager(self)
self.ProcessArgs(sys.argv[self.argOff :])
self.timerManager.go()
return 1
def OnDestroy(self, msg):
dlgappcore.AppDialog.OnDestroy(self, msg)
self.timerManager.stop()
def OnOK(self):
# stop the timer, then restart after setting special boolean
self.timerManager.stop()
self.timerManager.bConnectNow = 1
self.timerManager.go()
return
# def OnCancel(self): default behaviour - cancel == close.
# return
class TimerManager:
def __init__(self, dlg):
self.dlg = dlg
self.timerId = None
self.intervaler = eval(self.dlg.buildTimer)
self.bConnectNow = 0
self.bHaveSetPrompt1 = 0
def CaptureOutput(self):
self.oldOut = sys.stdout
self.oldErr = sys.stderr
sys.stdout = sys.stderr = self
self.bHaveSetPrompt1 = 0
def ReleaseOutput(self):
sys.stdout = self.oldOut
sys.stderr = self.oldErr
def write(self, str):
s = str.strip()
if len(s):
if self.bHaveSetPrompt1:
dest = self.dlg.prompt3
else:
dest = self.dlg.prompt1
self.bHaveSetPrompt1 = 1
dest.SetWindowText(s)
def go(self):
self.OnTimer(None, None)
def stop(self):
if self.timerId:
timer.kill_timer(self.timerId)
self.timerId = None
def OnTimer(self, id, timeVal):
if id:
timer.kill_timer(id)
if self.intervaler.IsTime() or self.bConnectNow:
# do the work.
try:
self.dlg.SetWindowText(self.dlg.title + " - Working...")
self.dlg.butOK.EnableWindow(0)
self.dlg.butCancel.EnableWindow(0)
self.CaptureOutput()
try:
exec(self.dlg.doWork)
print("The last operation completed successfully.")
except:
t, v, tb = sys.exc_info()
str = "Failed: %s: %s" % (t, repr(v))
print(str)
self.oldErr.write(str)
tb = None # Prevent cycle
finally:
self.ReleaseOutput()
self.dlg.butOK.EnableWindow()
self.dlg.butCancel.EnableWindow()
self.dlg.SetWindowText(self.dlg.title)
else:
now = time.time()
nextTime = self.intervaler.GetNextTime()
if nextTime:
timeDiffSeconds = nextTime - now
timeDiffMinutes = int(timeDiffSeconds / 60)
timeDiffSeconds = timeDiffSeconds % 60
timeDiffHours = int(timeDiffMinutes / 60)
timeDiffMinutes = timeDiffMinutes % 60
self.dlg.prompt1.SetWindowText(
"Next connection due in %02d:%02d:%02d"
% (timeDiffHours, timeDiffMinutes, timeDiffSeconds)
)
self.timerId = timer.set_timer(
self.intervaler.GetWakeupInterval(), self.OnTimer
)
self.bConnectNow = 0
class TimerIntervaler:
def __init__(self):
self.nextTime = None
self.wakeUpInterval = 2000
def GetWakeupInterval(self):
return self.wakeUpInterval
def GetNextTime(self):
return self.nextTime
def IsTime(self):
now = time.time()
if self.nextTime is None:
self.nextTime = self.SetFirstTime(now)
ret = 0
if now >= self.nextTime:
ret = 1
self.nextTime = self.SetNextTime(self.nextTime, now)
# do the work.
return ret
class EachAnyIntervaler(TimerIntervaler):
def __init__(self, timeAt, timePos, timeAdd, wakeUpInterval=None):
TimerIntervaler.__init__(self)
self.timeAt = timeAt
self.timePos = timePos
self.timeAdd = timeAdd
if wakeUpInterval:
self.wakeUpInterval = wakeUpInterval
def SetFirstTime(self, now):
timeTup = time.localtime(now)
lst = []
for item in timeTup:
lst.append(item)
bAdd = timeTup[self.timePos] > self.timeAt
lst[self.timePos] = self.timeAt
for pos in range(self.timePos + 1, 6):
lst[pos] = 0
ret = time.mktime(tuple(lst))
if bAdd:
ret = ret + self.timeAdd
return ret
def SetNextTime(self, lastTime, now):
return lastTime + self.timeAdd
class EachMinuteIntervaler(EachAnyIntervaler):
def __init__(self, at=0):
EachAnyIntervaler.__init__(self, at, 5, 60, 2000)
class EachHourIntervaler(EachAnyIntervaler):
def __init__(self, at=0):
EachAnyIntervaler.__init__(self, at, 4, 3600, 10000)
class EachDayIntervaler(EachAnyIntervaler):
def __init__(self, at=0):
EachAnyIntervaler.__init__(self, at, 3, 86400, 10000)
class TimerDialogApp(dlgappcore.DialogApp):
def CreateDialog(self):
return TimerAppDialog()
def DoDemoWork():
print("Doing the work...")
print("About to connect")
win32api.MessageBeep(win32con.MB_ICONASTERISK)
win32api.Sleep(2000)
print("Doing something else...")
win32api.MessageBeep(win32con.MB_ICONEXCLAMATION)
win32api.Sleep(2000)
print("More work.")
win32api.MessageBeep(win32con.MB_ICONHAND)
win32api.Sleep(2000)
print("The last bit.")
win32api.MessageBeep(win32con.MB_OK)
win32api.Sleep(2000)
app = TimerDialogApp()
def t():
t = TimerAppDialog("Test Dialog")
t.DoModal()
return t
if __name__ == "__main__":
import demoutils
demoutils.NeedApp()

View File

@ -0,0 +1,188 @@
# A demo of an Application object that has some custom print functionality.
# If you desire, you can also run this from inside Pythonwin, in which
# case it will do the demo inside the Pythonwin environment.
# This sample was contributed by Roger Burnham.
from pywin.mfc import docview, dialog, afxres
from pywin.framework import app
import win32con
import win32ui
import win32api
PRINTDLGORD = 1538
IDC_PRINT_MAG_EDIT = 1010
class PrintDemoTemplate(docview.DocTemplate):
def _SetupSharedMenu_(self):
pass
class PrintDemoView(docview.ScrollView):
def OnInitialUpdate(self):
ret = self._obj_.OnInitialUpdate()
self.colors = {
"Black": (0x00 << 0) + (0x00 << 8) + (0x00 << 16),
"Red": (0xFF << 0) + (0x00 << 8) + (0x00 << 16),
"Green": (0x00 << 0) + (0xFF << 8) + (0x00 << 16),
"Blue": (0x00 << 0) + (0x00 << 8) + (0xFF << 16),
"Cyan": (0x00 << 0) + (0xFF << 8) + (0xFF << 16),
"Magenta": (0xFF << 0) + (0x00 << 8) + (0xFF << 16),
"Yellow": (0xFF << 0) + (0xFF << 8) + (0x00 << 16),
}
self.pens = {}
for name, color in self.colors.items():
self.pens[name] = win32ui.CreatePen(win32con.PS_SOLID, 5, color)
self.pen = None
self.size = (128, 128)
self.SetScaleToFitSize(self.size)
self.HookCommand(self.OnFilePrint, afxres.ID_FILE_PRINT)
self.HookCommand(self.OnFilePrintPreview, win32ui.ID_FILE_PRINT_PREVIEW)
return ret
def OnDraw(self, dc):
oldPen = None
x, y = self.size
delta = 2
colors = list(self.colors.keys())
colors.sort()
colors = colors * 2
for color in colors:
if oldPen is None:
oldPen = dc.SelectObject(self.pens[color])
else:
dc.SelectObject(self.pens[color])
dc.MoveTo((delta, delta))
dc.LineTo((x - delta, delta))
dc.LineTo((x - delta, y - delta))
dc.LineTo((delta, y - delta))
dc.LineTo((delta, delta))
delta = delta + 4
if x - delta <= 0 or y - delta <= 0:
break
dc.SelectObject(oldPen)
def OnPrepareDC(self, dc, pInfo):
if dc.IsPrinting():
mag = self.prtDlg["mag"]
dc.SetMapMode(win32con.MM_ANISOTROPIC)
dc.SetWindowOrg((0, 0))
dc.SetWindowExt((1, 1))
dc.SetViewportOrg((0, 0))
dc.SetViewportExt((mag, mag))
def OnPreparePrinting(self, pInfo):
flags = (
win32ui.PD_USEDEVMODECOPIES
| win32ui.PD_PAGENUMS
| win32ui.PD_NOPAGENUMS
| win32ui.PD_NOSELECTION
)
self.prtDlg = ImagePrintDialog(pInfo, PRINTDLGORD, flags)
pInfo.SetPrintDialog(self.prtDlg)
pInfo.SetMinPage(1)
pInfo.SetMaxPage(1)
pInfo.SetFromPage(1)
pInfo.SetToPage(1)
ret = self.DoPreparePrinting(pInfo)
return ret
def OnBeginPrinting(self, dc, pInfo):
return self._obj_.OnBeginPrinting(dc, pInfo)
def OnEndPrinting(self, dc, pInfo):
del self.prtDlg
return self._obj_.OnEndPrinting(dc, pInfo)
def OnFilePrintPreview(self, *arg):
self._obj_.OnFilePrintPreview()
def OnFilePrint(self, *arg):
self._obj_.OnFilePrint()
def OnPrint(self, dc, pInfo):
doc = self.GetDocument()
metrics = dc.GetTextMetrics()
cxChar = metrics["tmAveCharWidth"]
cyChar = metrics["tmHeight"]
left, top, right, bottom = pInfo.GetDraw()
dc.TextOut(0, 2 * cyChar, doc.GetTitle())
top = top + (7 * cyChar) / 2
dc.MoveTo(left, top)
dc.LineTo(right, top)
top = top + cyChar
# this seems to have not effect...
# get what I want with the dc.SetWindowOrg calls
pInfo.SetDraw((left, top, right, bottom))
dc.SetWindowOrg((0, -top))
self.OnDraw(dc)
dc.SetTextAlign(win32con.TA_LEFT | win32con.TA_BOTTOM)
rect = self.GetWindowRect()
rect = self.ScreenToClient(rect)
height = rect[3] - rect[1]
dc.SetWindowOrg((0, -(top + height + cyChar)))
dc.MoveTo(left, 0)
dc.LineTo(right, 0)
x = 0
y = (3 * cyChar) / 2
dc.TextOut(x, y, doc.GetTitle())
y = y + cyChar
class PrintDemoApp(app.CApp):
def __init__(self):
app.CApp.__init__(self)
def InitInstance(self):
template = PrintDemoTemplate(None, None, None, PrintDemoView)
self.AddDocTemplate(template)
self._obj_.InitMDIInstance()
self.LoadMainFrame()
doc = template.OpenDocumentFile(None)
doc.SetTitle("Custom Print Document")
class ImagePrintDialog(dialog.PrintDialog):
sectionPos = "Image Print Demo"
def __init__(self, pInfo, dlgID, flags=win32ui.PD_USEDEVMODECOPIES):
dialog.PrintDialog.__init__(self, pInfo, dlgID, flags=flags)
mag = win32ui.GetProfileVal(self.sectionPos, "Document Magnification", 0)
if mag <= 0:
mag = 2
win32ui.WriteProfileVal(self.sectionPos, "Document Magnification", mag)
self["mag"] = mag
def OnInitDialog(self):
self.magCtl = self.GetDlgItem(IDC_PRINT_MAG_EDIT)
self.magCtl.SetWindowText(repr(self["mag"]))
return dialog.PrintDialog.OnInitDialog(self)
def OnOK(self):
dialog.PrintDialog.OnOK(self)
strMag = self.magCtl.GetWindowText()
try:
self["mag"] = int(strMag)
except:
pass
win32ui.WriteProfileVal(self.sectionPos, "Document Magnification", self["mag"])
if __name__ == "__main__":
# Running under Pythonwin
def test():
template = PrintDemoTemplate(None, None, None, PrintDemoView)
template.OpenDocumentFile(None)
test()
else:
app = PrintDemoApp()

View File

@ -0,0 +1,61 @@
# Utilities for the demos
import sys, win32api, win32con, win32ui
NotScriptMsg = """\
This demo program is not designed to be run as a Script, but is
probably used by some other test program. Please try another demo.
"""
NeedGUIMsg = """\
This demo program can only be run from inside of Pythonwin
You must start Pythonwin, and select 'Run' from the toolbar or File menu
"""
NeedAppMsg = """\
This demo program is a 'Pythonwin Application'.
It is more demo code than an example of Pythonwin's capabilities.
To run it, you must execute the command:
pythonwin.exe /app "%s"
Would you like to execute it now?
"""
def NotAScript():
import win32ui
win32ui.MessageBox(NotScriptMsg, "Demos")
def NeedGoodGUI():
from pywin.framework.app import HaveGoodGUI
rc = HaveGoodGUI()
if not rc:
win32ui.MessageBox(NeedGUIMsg, "Demos")
return rc
def NeedApp():
import win32ui
rc = win32ui.MessageBox(NeedAppMsg % sys.argv[0], "Demos", win32con.MB_YESNO)
if rc == win32con.IDYES:
try:
parent = win32ui.GetMainFrame().GetSafeHwnd()
win32api.ShellExecute(
parent, None, "pythonwin.exe", '/app "%s"' % sys.argv[0], None, 1
)
except win32api.error as details:
win32ui.MessageBox("Error executing command - %s" % (details), "Demos")
if __name__ == "__main__":
import demoutils
demoutils.NotAScript()

View File

@ -0,0 +1,50 @@
# dlgappdemo - a demo of a dialog application.
# This is a demonstration of both a custom "application" module,
# and a Python program in a dialog box.
#
# NOTE: You CAN NOT import this module from either PythonWin or Python.
# This module must be specified on the commandline to PythonWin only.
# eg, PythonWin /app dlgappdemo.py
from pywin.framework import dlgappcore, app
import win32ui
import sys
class TestDialogApp(dlgappcore.DialogApp):
def CreateDialog(self):
return TestAppDialog()
class TestAppDialog(dlgappcore.AppDialog):
def __init__(self):
self.edit = None
dlgappcore.AppDialog.__init__(self, win32ui.IDD_LARGE_EDIT)
def OnInitDialog(self):
self.SetWindowText("Test dialog application")
self.edit = self.GetDlgItem(win32ui.IDC_EDIT1)
print("Hello from Python")
print("args are:", end=" ")
for arg in sys.argv:
print(arg)
return 1
def PreDoModal(self):
sys.stdout = sys.stderr = self
def write(self, str):
if self.edit:
self.edit.SetSel(-2)
# translate \n to \n\r
self.edit.ReplaceSel(str.replace("\n", "\r\n"))
else:
win32ui.OutputDebug("dlgapp - no edit control! >>\n%s\n<<\n" % str)
app.AppBuilder = TestDialogApp
if __name__ == "__main__":
import demoutils
demoutils.NeedApp()

View File

@ -0,0 +1,73 @@
# dojobapp - do a job, show the result in a dialog, and exit.
#
# Very simple - faily minimal dialog based app.
#
# This should be run using the command line:
# pythonwin /app demos\dojobapp.py
import win32ui
import win32api
import win32con
import sys
from pywin.framework import app, dlgappcore
import string
class DoJobAppDialog(dlgappcore.AppDialog):
softspace = 1
def __init__(self, appName=""):
self.appName = appName
dlgappcore.AppDialog.__init__(self, win32ui.IDD_GENERAL_STATUS)
def PreDoModal(self):
pass
def ProcessArgs(self, args):
pass
def OnInitDialog(self):
self.SetWindowText(self.appName)
butCancel = self.GetDlgItem(win32con.IDCANCEL)
butCancel.ShowWindow(win32con.SW_HIDE)
p1 = self.GetDlgItem(win32ui.IDC_PROMPT1)
p2 = self.GetDlgItem(win32ui.IDC_PROMPT2)
# Do something here!
p1.SetWindowText("Hello there")
p2.SetWindowText("from the demo")
def OnDestroy(self, msg):
pass
# def OnOK(self):
# pass
# def OnCancel(self): default behaviour - cancel == close.
# return
class DoJobDialogApp(dlgappcore.DialogApp):
def CreateDialog(self):
return DoJobAppDialog("Do Something")
class CopyToDialogApp(DoJobDialogApp):
def __init__(self):
DoJobDialogApp.__init__(self)
app.AppBuilder = DoJobDialogApp
def t():
t = DoJobAppDialog("Copy To")
t.DoModal()
return t
if __name__ == "__main__":
import demoutils
demoutils.NeedApp()

View File

@ -0,0 +1,52 @@
##
## helloapp.py
##
##
## A nice, small 'hello world' Pythonwin application.
## NOT an MDI application - just a single, normal, top-level window.
##
## MUST be run with the command line "pythonwin.exe /app helloapp.py"
## (or if you are really keen, rename "pythonwin.exe" to something else, then
## using MSVC or similar, edit the string section in the .EXE to name this file)
##
## Originally by Willy Heineman <wheineman@uconect.net>
import win32con
import win32ui
from pywin.mfc import window, dialog, afxres
from pywin.mfc.thread import WinApp
# The main frame.
# Does almost nothing at all - doesnt even create a child window!
class HelloWindow(window.Wnd):
def __init__(self):
# The window.Wnd ctor creates a Window object, and places it in
# self._obj_. Note the window object exists, but the window itself
# does not!
window.Wnd.__init__(self, win32ui.CreateWnd())
# Now we ask the window object to create the window itself.
self._obj_.CreateWindowEx(
win32con.WS_EX_CLIENTEDGE,
win32ui.RegisterWndClass(0, 0, win32con.COLOR_WINDOW + 1),
"Hello World!",
win32con.WS_OVERLAPPEDWINDOW,
(100, 100, 400, 300),
None,
0,
None,
)
# The application object itself.
class HelloApp(WinApp):
def InitInstance(self):
self.frame = HelloWindow()
self.frame.ShowWindow(win32con.SW_SHOWNORMAL)
# We need to tell MFC what our main frame is.
self.SetMainFrame(self.frame)
# Now create the application object itself!
app = HelloApp()

View File

@ -0,0 +1,117 @@
# cmdserver.py
# Demo code that is not Pythonwin related, but too good to throw away...
import win32api
import sys
from pywin.framework import winout
import _thread, sys
import traceback
class ThreadWriter:
"Assign an instance to sys.stdout for per-thread printing objects - Courtesy Guido!"
def __init__(self):
"Constructor -- initialize the table of writers"
self.writers = {}
self.origStdOut = None
def register(self, writer):
"Register the writer for the current thread"
self.writers[_thread.get_ident()] = writer
if self.origStdOut is None:
self.origStdOut = sys.stdout
sys.stdout = self
def unregister(self):
"Remove the writer for the current thread, if any"
try:
del self.writers[_thread.get_ident()]
except KeyError:
pass
if len(self.writers) == 0:
sys.stdout = self.origStdOut
self.origStdOut = None
def getwriter(self):
"Return the current thread's writer, default sys.stdout"
try:
return self.writers[_thread.get_ident()]
except KeyError:
return self.origStdOut
def write(self, str):
"Write to the current thread's writer, default sys.stdout"
self.getwriter().write(str)
def Test():
num = 1
while num < 1000:
print("Hello there no " + str(num))
win32api.Sleep(50)
num = num + 1
class flags:
SERVER_BEST = 0
SERVER_IMMEDIATE = 1
SERVER_THREAD = 2
SERVER_PROCESS = 3
def StartServer(cmd, title=None, bCloseOnEnd=0, serverFlags=flags.SERVER_BEST):
out = winout.WindowOutput(title, None, winout.flags.WQ_IDLE)
if not title:
title = cmd
out.Create(title)
# ServerThread((out, cmd, title, bCloseOnEnd))
# out = sys.stdout
_thread.start_new_thread(ServerThread, (out, cmd, title, bCloseOnEnd))
def ServerThread(myout, cmd, title, bCloseOnEnd):
try:
writer.register(myout)
print('Executing "%s"\n' % cmd)
bOK = 1
try:
import __main__
exec(cmd + "\n", __main__.__dict__)
except:
bOK = 0
if bOK:
print("Command terminated without errors.")
else:
t, v, tb = sys.exc_info()
print(t, ": ", v)
traceback.print_tb(tb)
tb = None # prevent a cycle
print("Command terminated with an unhandled exception")
writer.unregister()
if bOK and bCloseOnEnd:
myout.frame.DestroyWindow()
# Unhandled exception of any kind in a thread kills the gui!
except:
t, v, tb = sys.exc_info()
print(t, ": ", v)
traceback.print_tb(tb)
tb = None
print("Thread failed")
# assist for reloading (when debugging) - use only 1 tracer object,
# else a large chain of tracer objects will exist.
# try:
# writer
# except NameError:
# writer=ThreadWriter()
if __name__ == "__main__":
import demoutils
demoutils.NotAScript()

View File

@ -0,0 +1,114 @@
#
# Window creation example
#
# This example creates a minimal "control" that just fills in its
# window with red. To make your own control, subclass Control and
# write your own OnPaint() method. See PyCWnd.HookMessage for what
# the parameters to OnPaint are.
#
from pywin.mfc import dialog, window
import win32ui
import win32con
import win32api
class Control(window.Wnd):
"""Generic control class"""
def __init__(self):
window.Wnd.__init__(self, win32ui.CreateWnd())
def OnPaint(self):
dc, paintStruct = self.BeginPaint()
self.DoPaint(dc)
self.EndPaint(paintStruct)
def DoPaint(self, dc): # Override this!
pass
class RedBox(Control):
def DoPaint(self, dc):
dc.FillSolidRect(self.GetClientRect(), win32api.RGB(255, 0, 0))
class RedBoxWithPie(RedBox):
def DoPaint(self, dc):
RedBox.DoPaint(self, dc)
r = self.GetClientRect()
dc.Pie(r[0], r[1], r[2], r[3], 0, 0, r[2], r[3] // 2)
def MakeDlgTemplate():
style = (
win32con.DS_MODALFRAME
| win32con.WS_POPUP
| win32con.WS_VISIBLE
| win32con.WS_CAPTION
| win32con.WS_SYSMENU
| win32con.DS_SETFONT
)
cs = win32con.WS_CHILD | win32con.WS_VISIBLE
w = 64
h = 64
dlg = [
["Red box", (0, 0, w, h), style, None, (8, "MS Sans Serif")],
]
s = win32con.WS_TABSTOP | cs
dlg.append(
[
128,
"Cancel",
win32con.IDCANCEL,
(7, h - 18, 50, 14),
s | win32con.BS_PUSHBUTTON,
]
)
return dlg
class TestDialog(dialog.Dialog):
def OnInitDialog(self):
rc = dialog.Dialog.OnInitDialog(self)
self.redbox = RedBox()
self.redbox.CreateWindow(
None,
"RedBox",
win32con.WS_CHILD | win32con.WS_VISIBLE,
(5, 5, 90, 68),
self,
1003,
)
return rc
class TestPieDialog(dialog.Dialog):
def OnInitDialog(self):
rc = dialog.Dialog.OnInitDialog(self)
self.control = RedBoxWithPie()
self.control.CreateWindow(
None,
"RedBox with Pie",
win32con.WS_CHILD | win32con.WS_VISIBLE,
(5, 5, 90, 68),
self,
1003,
)
def demo(modal=0):
d = TestPieDialog(MakeDlgTemplate())
if modal:
d.DoModal()
else:
d.CreateWindow()
if __name__ == "__main__":
demo(1)

View File

@ -0,0 +1,63 @@
# Utilities for the demos
import sys, win32api, win32con, win32ui
NotScriptMsg = """\
This demo program is not designed to be run as a Script, but is
probably used by some other test program. Please try another demo.
"""
NeedGUIMsg = """\
This demo program can only be run from inside of Pythonwin
You must start Pythonwin, and select 'Run' from the toolbar or File menu
"""
NeedAppMsg = """\
This demo program is a 'Pythonwin Application'.
It is more demo code than an example of Pythonwin's capabilities.
To run it, you must execute the command:
pythonwin.exe /app "%s"
Would you like to execute it now?
"""
def NotAScript():
import win32ui
win32ui.MessageBox(NotScriptMsg, "Demos")
def NeedGoodGUI():
from pywin.framework.app import HaveGoodGUI
rc = HaveGoodGUI()
if not rc:
win32ui.MessageBox(NeedGUIMsg, "Demos")
return rc
def NeedApp():
import win32ui
rc = win32ui.MessageBox(NeedAppMsg % sys.argv[0], "Demos", win32con.MB_YESNO)
if rc == win32con.IDYES:
try:
parent = win32ui.GetMainFrame().GetSafeHwnd()
win32api.ShellExecute(
parent, None, "pythonwin.exe", '/app "%s"' % sys.argv[0], None, 1
)
except win32api.error as details:
win32ui.MessageBox("Error executing command - %s" % (details), "Demos")
from pywin.framework.app import HaveGoodGUI
if __name__ == "__main__":
import demoutils
demoutils.NotAScript()

View File

@ -0,0 +1,73 @@
# A demo which creates a view and a frame which displays a PPM format bitmap
#
# This hasnnt been run in a while, as I dont have many of that format around!
import win32ui
import win32con
import win32api
import string
class DIBView:
def __init__(self, doc, dib):
self.dib = dib
self.view = win32ui.CreateView(doc)
self.width = self.height = 0
# set up message handlers
# self.view.OnPrepareDC = self.OnPrepareDC
self.view.HookMessage(self.OnSize, win32con.WM_SIZE)
def OnSize(self, params):
lParam = params[3]
self.width = win32api.LOWORD(lParam)
self.height = win32api.HIWORD(lParam)
def OnDraw(self, ob, dc):
# set sizes used for "non strecth" mode.
self.view.SetScrollSizes(win32con.MM_TEXT, self.dib.GetSize())
dibSize = self.dib.GetSize()
dibRect = (0, 0, dibSize[0], dibSize[1])
# stretch BMP.
# self.dib.Paint(dc, (0,0,self.width, self.height),dibRect)
# non stretch.
self.dib.Paint(dc)
class DIBDemo:
def __init__(self, filename, *bPBM):
# init data members
f = open(filename, "rb")
dib = win32ui.CreateDIBitmap()
if len(bPBM) > 0:
magic = f.readline()
if magic != "P6\n":
print("The file is not a PBM format file")
raise ValueError("Failed - The file is not a PBM format file")
# check magic?
rowcollist = f.readline().split()
cols = int(rowcollist[0])
rows = int(rowcollist[1])
f.readline() # whats this one?
dib.LoadPBMData(f, (cols, rows))
else:
dib.LoadWindowsFormatFile(f)
f.close()
# create doc/view
self.doc = win32ui.CreateDoc()
self.dibView = DIBView(self.doc, dib)
self.frame = win32ui.CreateMDIFrame()
self.frame.LoadFrame() # this will force OnCreateClient
self.doc.SetTitle("DIB Demo")
self.frame.ShowWindow()
# display the sucka
self.frame.ActivateFrame()
def OnCreateClient(self, createparams, context):
self.dibView.view.CreateWindow(self.frame)
return 1
if __name__ == "__main__":
import demoutils
demoutils.NotAScript()

View File

@ -0,0 +1,151 @@
# A Demo of Pythonwin's Dialog and Property Page support.
###################
#
# First demo - use the built-in to Pythonwin "Tab Stop" dialog, but
# customise it heavily.
#
# ID's for the tabstop dialog - out test.
#
from win32ui import IDD_SET_TABSTOPS
from win32ui import IDC_EDIT_TABS
from win32ui import IDC_PROMPT_TABS
from win32con import IDOK
from win32con import IDCANCEL
import win32ui
import win32con
from pywin.mfc import dialog
class TestDialog(dialog.Dialog):
def __init__(self, modal=1):
dialog.Dialog.__init__(self, IDD_SET_TABSTOPS)
self.counter = 0
if modal:
self.DoModal()
else:
self.CreateWindow()
def OnInitDialog(self):
# Set the caption of the dialog itself.
self.SetWindowText("Used to be Tab Stops!")
# Get a child control, remember it, and change its text.
self.edit = self.GetDlgItem(IDC_EDIT_TABS) # the text box.
self.edit.SetWindowText("Test")
# Hook a Windows message for the dialog.
self.edit.HookMessage(self.KillFocus, win32con.WM_KILLFOCUS)
# Get the prompt control, and change its next.
prompt = self.GetDlgItem(IDC_PROMPT_TABS) # the prompt box.
prompt.SetWindowText("Prompt")
# And the same for the button..
cancel = self.GetDlgItem(IDCANCEL) # the cancel button
cancel.SetWindowText("&Kill me")
# And just for demonstration purposes, we hook the notify message for the dialog.
# This allows us to be notified when the Edit Control text changes.
self.HookCommand(self.OnNotify, IDC_EDIT_TABS)
def OnNotify(self, controlid, code):
if code == win32con.EN_CHANGE:
print("Edit text changed!")
return 1 # I handled this, so no need to call defaults!
# kill focus for the edit box.
# Simply increment the value in the text box.
def KillFocus(self, msg):
self.counter = self.counter + 1
if self.edit != None:
self.edit.SetWindowText(str(self.counter))
# Called when the dialog box is terminating...
def OnDestroy(self, msg):
del self.edit
del self.counter
# A very simply Property Sheet.
# We only make a new class for demonstration purposes.
class TestSheet(dialog.PropertySheet):
def __init__(self, title):
dialog.PropertySheet.__init__(self, title)
self.HookMessage(self.OnActivate, win32con.WM_ACTIVATE)
def OnActivate(self, msg):
pass
# A very simply Property Page, which will be "owned" by the above
# Property Sheet.
# We create a new class, just so we can hook a control notification.
class TestPage(dialog.PropertyPage):
def OnInitDialog(self):
# We use the HookNotify function to allow Python to respond to
# Windows WM_NOTIFY messages.
# In this case, we are interested in BN_CLICKED messages.
self.HookNotify(self.OnNotify, win32con.BN_CLICKED)
def OnNotify(self, std, extra):
print("OnNotify", std, extra)
# Some code that actually uses these objects.
def demo(modal=0):
TestDialog(modal)
# property sheet/page demo
ps = win32ui.CreatePropertySheet("Property Sheet/Page Demo")
# Create a completely standard PropertyPage.
page1 = win32ui.CreatePropertyPage(win32ui.IDD_PROPDEMO1)
# Create our custom property page.
page2 = TestPage(win32ui.IDD_PROPDEMO2)
ps.AddPage(page1)
ps.AddPage(page2)
if modal:
ps.DoModal()
else:
style = (
win32con.WS_SYSMENU
| win32con.WS_POPUP
| win32con.WS_CAPTION
| win32con.DS_MODALFRAME
| win32con.WS_VISIBLE
)
styleex = win32con.WS_EX_DLGMODALFRAME | win32con.WS_EX_PALETTEWINDOW
ps.CreateWindow(win32ui.GetMainFrame(), style, styleex)
def test(modal=1):
# dlg=dialog.Dialog(1010)
# dlg.CreateWindow()
# dlg.EndDialog(0)
# del dlg
# return
# property sheet/page demo
ps = TestSheet("Property Sheet/Page Demo")
page1 = win32ui.CreatePropertyPage(win32ui.IDD_PROPDEMO1)
page2 = win32ui.CreatePropertyPage(win32ui.IDD_PROPDEMO2)
ps.AddPage(page1)
ps.AddPage(page2)
del page1
del page2
if modal:
ps.DoModal()
else:
ps.CreateWindow(win32ui.GetMainFrame())
return ps
def d():
dlg = win32ui.CreateDialog(win32ui.IDD_DEBUGGER)
dlg.datalist.append((win32ui.IDC_DBG_RADIOSTACK, "radio"))
print("data list is ", dlg.datalist)
dlg.data["radio"] = 1
dlg.DoModal()
print(dlg.data["radio"])
if __name__ == "__main__":
demo(1)

View File

@ -0,0 +1,104 @@
# dyndlg.py
# contributed by Curt Hagenlocher <chi@earthlink.net>
# Dialog Template params:
# Parameter 0 - Window caption
# Parameter 1 - Bounds (rect tuple)
# Parameter 2 - Window style
# Parameter 3 - Extended style
# Parameter 4 - Font tuple
# Parameter 5 - Menu name
# Parameter 6 - Window class
# Dialog item params:
# Parameter 0 - Window class
# Parameter 1 - Text
# Parameter 2 - ID
# Parameter 3 - Bounds
# Parameter 4 - Style
# Parameter 5 - Extended style
# Parameter 6 - Extra data
import win32ui
import win32con
from pywin.mfc import dialog, window
def MakeDlgTemplate():
style = (
win32con.DS_MODALFRAME
| win32con.WS_POPUP
| win32con.WS_VISIBLE
| win32con.WS_CAPTION
| win32con.WS_SYSMENU
| win32con.DS_SETFONT
)
cs = win32con.WS_CHILD | win32con.WS_VISIBLE
dlg = [
["Select Warehouse", (0, 0, 177, 93), style, None, (8, "MS Sans Serif")],
]
dlg.append([130, "Current Warehouse:", -1, (7, 7, 69, 9), cs | win32con.SS_LEFT])
dlg.append([130, "ASTORIA", 128, (16, 17, 99, 7), cs | win32con.SS_LEFT])
dlg.append([130, "New &Warehouse:", -1, (7, 29, 69, 9), cs | win32con.SS_LEFT])
s = win32con.WS_TABSTOP | cs
# dlg.append([131, None, 130, (5, 40, 110, 48),
# s | win32con.LBS_NOTIFY | win32con.LBS_SORT | win32con.LBS_NOINTEGRALHEIGHT | win32con.WS_VSCROLL | win32con.WS_BORDER])
dlg.append(
[
"{8E27C92B-1264-101C-8A2F-040224009C02}",
None,
131,
(5, 40, 110, 48),
win32con.WS_TABSTOP,
]
)
dlg.append(
[128, "OK", win32con.IDOK, (124, 5, 50, 14), s | win32con.BS_DEFPUSHBUTTON]
)
s = win32con.BS_PUSHBUTTON | s
dlg.append([128, "Cancel", win32con.IDCANCEL, (124, 22, 50, 14), s])
dlg.append([128, "&Help", 100, (124, 74, 50, 14), s])
return dlg
def test1():
win32ui.CreateDialogIndirect(MakeDlgTemplate()).DoModal()
def test2():
dialog.Dialog(MakeDlgTemplate()).DoModal()
def test3():
dlg = win32ui.LoadDialogResource(win32ui.IDD_SET_TABSTOPS)
dlg[0][0] = "New Dialog Title"
dlg[0][1] = (80, 20, 161, 60)
dlg[1][1] = "&Confusion:"
cs = (
win32con.WS_CHILD
| win32con.WS_VISIBLE
| win32con.WS_TABSTOP
| win32con.BS_PUSHBUTTON
)
dlg.append([128, "&Help", 100, (111, 41, 40, 14), cs])
dialog.Dialog(dlg).DoModal()
def test4():
page1 = dialog.PropertyPage(win32ui.LoadDialogResource(win32ui.IDD_PROPDEMO1))
page2 = dialog.PropertyPage(win32ui.LoadDialogResource(win32ui.IDD_PROPDEMO2))
ps = dialog.PropertySheet("Property Sheet/Page Demo", None, [page1, page2])
ps.DoModal()
def testall():
test1()
test2()
test3()
test4()
if __name__ == "__main__":
testall()

View File

@ -0,0 +1,88 @@
# Demo of Generic document windows, DC, and Font usage
# by Dave Brennan (brennan@hal.com)
# usage examples:
# >>> from fontdemo import *
# >>> d = FontDemo('Hello, Python')
# >>> f1 = { 'name':'Arial', 'height':36, 'weight':win32con.FW_BOLD}
# >>> d.SetFont(f1)
# >>> f2 = {'name':'Courier New', 'height':24, 'italic':1}
# >>> d.SetFont (f2)
import win32ui
import win32con
import win32api
from pywin.mfc import docview
# font is a dictionary in which the following elements matter:
# (the best matching font to supplied parameters is returned)
# name string name of the font as known by Windows
# size point size of font in logical units
# weight weight of font (win32con.FW_NORMAL, win32con.FW_BOLD)
# italic boolean; true if set to anything but None
# underline boolean; true if set to anything but None
class FontView(docview.ScrollView):
def __init__(
self, doc, text="Python Rules!", font_spec={"name": "Arial", "height": 42}
):
docview.ScrollView.__init__(self, doc)
self.font = win32ui.CreateFont(font_spec)
self.text = text
self.width = self.height = 0
# set up message handlers
self.HookMessage(self.OnSize, win32con.WM_SIZE)
def OnAttachedObjectDeath(self):
docview.ScrollView.OnAttachedObjectDeath(self)
del self.font
def SetFont(self, new_font):
# Change font on the fly
self.font = win32ui.CreateFont(new_font)
# redraw the entire client window
selfInvalidateRect(None)
def OnSize(self, params):
lParam = params[3]
self.width = win32api.LOWORD(lParam)
self.height = win32api.HIWORD(lParam)
def OnPrepareDC(self, dc, printinfo):
# Set up the DC for forthcoming OnDraw call
self.SetScrollSizes(win32con.MM_TEXT, (100, 100))
dc.SetTextColor(win32api.RGB(0, 0, 255))
dc.SetBkColor(win32api.GetSysColor(win32con.COLOR_WINDOW))
dc.SelectObject(self.font)
dc.SetTextAlign(win32con.TA_CENTER | win32con.TA_BASELINE)
def OnDraw(self, dc):
if self.width == 0 and self.height == 0:
left, top, right, bottom = self.GetClientRect()
self.width = right - left
self.height = bottom - top
x, y = self.width // 2, self.height // 2
dc.TextOut(x, y, self.text)
def FontDemo():
# create doc/view
template = docview.DocTemplate(win32ui.IDR_PYTHONTYPE, None, None, FontView)
doc = template.OpenDocumentFile(None)
doc.SetTitle("Font Demo")
# print "template is ", template, "obj is", template._obj_
template.close()
# print "closed"
# del template
if __name__ == "__main__":
import demoutils
if demoutils.NeedGoodGUI():
FontDemo()

View File

@ -0,0 +1,85 @@
# GUI Demo - just a worker script to invoke all the other demo/test scripts.
import win32ui
import __main__
import sys
import regutil
import win32api
demos = [ # ('Font', 'import fontdemo;fontdemo.FontDemo()'),
("Open GL Demo", "import openGLDemo;openGLDemo.test()"),
("Threaded GUI", "import threadedgui;threadedgui.ThreadedDemo()"),
("Tree View Demo", "import hiertest;hiertest.demoboth()"),
("3-Way Splitter Window", "import splittst;splittst.demo()"),
("Custom Toolbars and Tooltips", "import toolbar;toolbar.test()"),
("Progress Bar", "import progressbar;progressbar.demo()"),
("Slider Control", "import sliderdemo;sliderdemo.demo()"),
("Dynamic window creation", "import createwin;createwin.demo()"),
("Various Dialog demos", "import dlgtest;dlgtest.demo()"),
("OCX Control Demo", "from ocx import ocxtest;ocxtest.demo()"),
("OCX Serial Port Demo", "from ocx import ocxserialtest; ocxserialtest.test()"),
(
"IE4 Control Demo",
'from ocx import webbrowser; webbrowser.Demo("http://www.python.org")',
),
]
def demo():
try:
# seeif I can locate the demo files.
import fontdemo
except ImportError:
# else put the demos direectory on the path (if not already)
try:
instPath = regutil.GetRegistryDefaultValue(
regutil.BuildDefaultPythonKey() + "\\InstallPath"
)
except win32api.error:
print(
"The InstallPath can not be located, and the Demos directory is not on the path"
)
instPath = "."
demosDir = win32ui.FullPath(instPath + "\\Demos")
for path in sys.path:
if win32ui.FullPath(path) == demosDir:
break
else:
sys.path.append(demosDir)
import fontdemo
import sys
if "/go" in sys.argv:
for name, cmd in demos:
try:
exec(cmd)
except:
print(
"Demo of %s failed - %s:%s"
% (cmd, sys.exc_info()[0], sys.exc_info()[1])
)
return
# Otherwise allow the user to select the demo to run
import pywin.dialogs.list
while 1:
rc = pywin.dialogs.list.SelectFromLists("Select a Demo", demos, ["Demo Title"])
if rc is None:
break
title, cmd = demos[rc]
try:
exec(cmd)
except:
print(
"Demo of %s failed - %s:%s"
% (title, sys.exc_info()[0], sys.exc_info()[1])
)
if __name__ == __main__.__name__:
import demoutils
if demoutils.NeedGoodGUI():
demo()

View File

@ -0,0 +1,137 @@
import win32ui
import os
import commctrl
from pywin.tools import hierlist
from pywin.mfc import docview, window
# directory listbox
# This has obvious limitations - doesnt track subdirs, etc. Demonstrates
# simple use of Python code for querying the tree as needed.
# Only use strings, and lists of strings (from curdir())
class DirHierList(hierlist.HierList):
def __init__(self, root, listBoxID=win32ui.IDC_LIST1):
hierlist.HierList.__init__(self, root, win32ui.IDB_HIERFOLDERS, listBoxID)
def GetText(self, item):
return os.path.basename(item)
def GetSubList(self, item):
if os.path.isdir(item):
ret = [os.path.join(item, fname) for fname in os.listdir(item)]
else:
ret = None
return ret
# if the item is a dir, it is expandable.
def IsExpandable(self, item):
return os.path.isdir(item)
def GetSelectedBitmapColumn(self, item):
return self.GetBitmapColumn(item) + 6 # Use different color for selection
class TestDocument(docview.Document):
def __init__(self, template):
docview.Document.__init__(self, template)
self.hierlist = hierlist.HierListWithItems(
HLIFileDir("\\"), win32ui.IDB_HIERFOLDERS, win32ui.AFX_IDW_PANE_FIRST
)
class HierListView(docview.TreeView):
def OnInitialUpdate(self):
rc = self._obj_.OnInitialUpdate()
self.hierList = self.GetDocument().hierlist
self.hierList.HierInit(self.GetParent())
self.hierList.SetStyle(
commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS
)
return rc
class HierListFrame(window.MDIChildWnd):
pass
def GetTestRoot():
tree1 = ("Tree 1", [("Item 1", "Item 1 data"), "Item 2", 3])
tree2 = ("Tree 2", [("Item 2.1", "Item 2 data"), "Item 2.2", 2.3])
return ("Root", [tree1, tree2, "Item 3"])
def demoboth():
template = docview.DocTemplate(
win32ui.IDR_PYTHONTYPE, TestDocument, HierListFrame, HierListView
)
template.OpenDocumentFile(None).SetTitle("Hierlist demo")
demomodeless()
def demomodeless():
testList2 = DirHierList("\\")
dlg = hierlist.HierDialog("hier list test", testList2)
dlg.CreateWindow()
def demodlg():
testList2 = DirHierList("\\")
dlg = hierlist.HierDialog("hier list test", testList2)
dlg.DoModal()
def demo():
template = docview.DocTemplate(
win32ui.IDR_PYTHONTYPE, TestDocument, HierListFrame, HierListView
)
template.OpenDocumentFile(None).SetTitle("Hierlist demo")
#
# Demo/Test for HierList items.
#
# Easy to make a better directory program.
#
class HLIFileDir(hierlist.HierListItem):
def __init__(self, filename):
self.filename = filename
hierlist.HierListItem.__init__(self)
def GetText(self):
try:
return "%-20s %d bytes" % (
os.path.basename(self.filename),
os.stat(self.filename)[6],
)
except os.error as details:
return "%-20s - %s" % (self.filename, details[1])
def IsExpandable(self):
return os.path.isdir(self.filename)
def GetSubList(self):
ret = []
for newname in os.listdir(self.filename):
if newname not in [".", ".."]:
ret.append(HLIFileDir(os.path.join(self.filename, newname)))
return ret
def demohli():
template = docview.DocTemplate(
win32ui.IDR_PYTHONTYPE,
TestDocument,
hierlist.HierListFrame,
hierlist.HierListView,
)
template.OpenDocumentFile(None).SetTitle("Hierlist demo")
if __name__ == "__main__":
import demoutils
if demoutils.HaveGoodGUI():
demoboth()
else:
demodlg()

View File

@ -0,0 +1,13 @@
# Run this as a python script, to gray "close" off the edit window system menu.
from pywin.framework import interact
import win32con
if __name__ == "__main__":
import demoutils
if demoutils.NeedGoodGUI():
win = interact.edit.currentView.GetParent()
menu = win.GetSystemMenu()
id = menu.GetMenuItemID(6)
menu.EnableMenuItem(id, win32con.MF_BYCOMMAND | win32con.MF_GRAYED)
print("The interactive window's 'Close' menu item is now disabled.")

View File

@ -0,0 +1,57 @@
# This is a sample file, and shows the basic framework for using an "Object" based
# document, rather than a "filename" based document.
# This is referenced by the Pythonwin .html documentation.
# In the example below, the OpenObject() method is used instead of OpenDocumentFile,
# and all the core MFC document open functionality is retained.
import win32ui
from pywin.mfc import docview
class object_template(docview.DocTemplate):
def __init__(self):
docview.DocTemplate.__init__(self, None, None, None, object_view)
def OpenObject(self, object): # Use this instead of OpenDocumentFile.
# Look for existing open document
for doc in self.GetDocumentList():
print("document is ", doc)
if doc.object is object:
doc.GetFirstView().ActivateFrame()
return doc
# not found - new one.
doc = object_document(self, object)
frame = self.CreateNewFrame(doc)
doc.OnNewDocument()
doc.SetTitle(str(object))
self.InitialUpdateFrame(frame, doc)
return doc
class object_document(docview.Document):
def __init__(self, template, object):
docview.Document.__init__(self, template)
self.object = object
def OnOpenDocument(self, name):
raise RuntimeError("Should not be called if template strings set up correctly")
return 0
class object_view(docview.EditView):
def OnInitialUpdate(self):
self.ReplaceSel("Object is %s" % repr(self.GetDocument().object))
def demo():
t = object_template()
d = t.OpenObject(win32ui)
return (t, d)
if __name__ == "__main__":
import demoutils
if demoutils.NeedGoodGUI():
demo()

View File

@ -0,0 +1,63 @@
# Utilities for the demos
import sys, win32api, win32con, win32ui
NotScriptMsg = """\
This demo program is not designed to be run as a Script, but is
probably used by some other test program. Please try another demo.
"""
NeedGUIMsg = """\
This demo program can only be run from inside of Pythonwin
You must start Pythonwin, and select 'Run' from the toolbar or File menu
"""
NeedAppMsg = """\
This demo program is a 'Pythonwin Application'.
It is more demo code than an example of Pythonwin's capabilities.
To run it, you must execute the command:
pythonwin.exe /app "%s"
Would you like to execute it now?
"""
def NotAScript():
import win32ui
win32ui.MessageBox(NotScriptMsg, "Demos")
def NeedGoodGUI():
from pywin.framework.app import HaveGoodGUI
rc = HaveGoodGUI()
if not rc:
win32ui.MessageBox(NeedGUIMsg, "Demos")
return rc
def NeedApp():
import win32ui
rc = win32ui.MessageBox(NeedAppMsg % sys.argv[0], "Demos", win32con.MB_YESNO)
if rc == win32con.IDYES:
try:
parent = win32ui.GetMainFrame().GetSafeHwnd()
win32api.ShellExecute(
parent, None, "pythonwin.exe", '/app "%s"' % sys.argv[0], None, 1
)
except win32api.error as details:
win32ui.MessageBox("Error executing command - %s" % (details), "Demos")
from pywin.framework.app import HaveGoodGUI
if __name__ == "__main__":
from . import demoutils
demoutils.NotAScript()

View File

@ -0,0 +1,91 @@
# By Bradley Schatz
# simple flash/python application demonstrating bidirectional
# communicaion between flash and python. Click the sphere to see
# behavior. Uses Bounce.swf from FlashBounce.zip, available from
# http://pages.cpsc.ucalgary.ca/~saul/vb_examples/tutorial12/
# Update to the path of the .swf file (note it could be a true URL)
flash_url = "c:\\bounce.swf"
import win32ui, win32con, win32api, regutil
from pywin.mfc import window, activex
from win32com.client import gencache
import sys
FlashModule = gencache.EnsureModule("{D27CDB6B-AE6D-11CF-96B8-444553540000}", 0, 1, 0)
if FlashModule is None:
raise ImportError("Flash does not appear to be installed.")
class MyFlashComponent(activex.Control, FlashModule.ShockwaveFlash):
def __init__(self):
activex.Control.__init__(self)
FlashModule.ShockwaveFlash.__init__(self)
self.x = 50
self.y = 50
self.angle = 30
self.started = 0
def OnFSCommand(self, command, args):
print("FSCommend", command, args)
self.x = self.x + 20
self.y = self.y + 20
self.angle = self.angle + 20
if self.x > 200 or self.y > 200:
self.x = 0
self.y = 0
if self.angle > 360:
self.angle = 0
self.SetVariable("xVal", self.x)
self.SetVariable("yVal", self.y)
self.SetVariable("angle", self.angle)
self.TPlay("_root.mikeBall")
def OnProgress(self, percentDone):
print("PercentDone", percentDone)
def OnReadyStateChange(self, newState):
# 0=Loading, 1=Uninitialized, 2=Loaded, 3=Interactive, 4=Complete
print("State", newState)
class BrowserFrame(window.MDIChildWnd):
def __init__(self, url=None):
if url is None:
self.url = regutil.GetRegisteredHelpFile("Main Python Documentation")
else:
self.url = url
pass # Dont call base class doc/view version...
def Create(self, title, rect=None, parent=None):
style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW
self._obj_ = win32ui.CreateMDIChild()
self._obj_.AttachObject(self)
self._obj_.CreateWindow(None, title, style, rect, parent)
rect = self.GetClientRect()
rect = (0, 0, rect[2] - rect[0], rect[3] - rect[1])
self.ocx = MyFlashComponent()
self.ocx.CreateControl(
"Flash Player", win32con.WS_VISIBLE | win32con.WS_CHILD, rect, self, 1000
)
self.ocx.LoadMovie(0, flash_url)
self.ocx.Play()
self.HookMessage(self.OnSize, win32con.WM_SIZE)
def OnSize(self, params):
rect = self.GetClientRect()
rect = (0, 0, rect[2] - rect[0], rect[3] - rect[1])
self.ocx.SetWindowPos(0, rect, 0)
def Demo():
url = None
if len(sys.argv) > 1:
url = win32api.GetFullPathName(sys.argv[1])
f = BrowserFrame(url)
f.Create("Flash Player")
if __name__ == "__main__":
Demo()

View File

@ -0,0 +1,147 @@
# This demo uses some of the Microsoft Office components.
#
# It was taken from an MSDN article showing how to embed excel.
# It is not comlpete yet, but it _does_ show an Excel spreadsheet in a frame!
#
import win32ui, win32uiole, win32con, regutil
from pywin.mfc import window, activex, object, docview
from win32com.client import gencache
# WordModule = gencache.EnsureModule('{00020905-0000-0000-C000-000000000046}', 1033, 8, 0)
# if WordModule is None:
# raise ImportError, "Microsoft Word version 8 does not appear to be installed."
class OleClientItem(object.CmdTarget):
def __init__(self, doc):
object.CmdTarget.__init__(self, win32uiole.CreateOleClientItem(doc))
def OnGetItemPosition(self):
# For now return a hard-coded rect.
return (10, 10, 210, 210)
def OnActivate(self):
# Allow only one inplace activate item per frame
view = self.GetActiveView()
item = self.GetDocument().GetInPlaceActiveItem(view)
if item is not None and item._obj_ != self._obj_:
item.Close()
self._obj_.OnActivate()
def OnChange(self, oleNotification, dwParam):
self._obj_.OnChange(oleNotification, dwParam)
self.GetDocument().UpdateAllViews(None)
def OnChangeItemPosition(self, rect):
# During in-place activation CEmbed_ExcelCntrItem::OnChangeItemPosition
# is called by the server to change the position of the in-place
# window. Usually, this is a result of the data in the server
# document changing such that the extent has changed or as a result
# of in-place resizing.
#
# The default here is to call the base class, which will call
# COleClientItem::SetItemRects to move the item
# to the new position.
if not self._obj_.OnChangeItemPosition(self, rect):
return 0
# TODO: update any cache you may have of the item's rectangle/extent
return 1
class OleDocument(object.CmdTarget):
def __init__(self, template):
object.CmdTarget.__init__(self, win32uiole.CreateOleDocument(template))
self.EnableCompoundFile()
class ExcelView(docview.ScrollView):
def OnInitialUpdate(self):
self.HookMessage(self.OnSetFocus, win32con.WM_SETFOCUS)
self.HookMessage(self.OnSize, win32con.WM_SIZE)
self.SetScrollSizes(win32con.MM_TEXT, (100, 100))
rc = self._obj_.OnInitialUpdate()
self.EmbedExcel()
return rc
def EmbedExcel(self):
doc = self.GetDocument()
self.clientItem = OleClientItem(doc)
self.clientItem.CreateNewItem("Excel.Sheet")
self.clientItem.DoVerb(-1, self)
doc.UpdateAllViews(None)
def OnDraw(self, dc):
doc = self.GetDocument()
pos = doc.GetStartPosition()
clientItem, pos = doc.GetNextItem(pos)
clientItem.Draw(dc, (10, 10, 210, 210))
# Special handling of OnSetFocus and OnSize are required for a container
# when an object is being edited in-place.
def OnSetFocus(self, msg):
item = self.GetDocument().GetInPlaceActiveItem(self)
if (
item is not None
and item.GetItemState() == win32uiole.COleClientItem_activeUIState
):
wnd = item.GetInPlaceWindow()
if wnd is not None:
wnd.SetFocus()
return 0 # Dont get the base version called.
return 1 # Call the base version.
def OnSize(self, params):
item = self.GetDocument().GetInPlaceActiveItem(self)
if item is not None:
item.SetItemRects()
return 1 # do call the base!
class OleTemplate(docview.DocTemplate):
def __init__(
self, resourceId=None, MakeDocument=None, MakeFrame=None, MakeView=None
):
if MakeDocument is None:
MakeDocument = OleDocument
if MakeView is None:
MakeView = ExcelView
docview.DocTemplate.__init__(
self, resourceId, MakeDocument, MakeFrame, MakeView
)
class WordFrame(window.MDIChildWnd):
def __init__(self, doc=None):
self._obj_ = win32ui.CreateMDIChild()
self._obj_.AttachObject(self)
# Dont call base class doc/view version...
def Create(self, title, rect=None, parent=None):
style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW
self._obj_.CreateWindow(None, title, style, rect, parent)
rect = self.GetClientRect()
rect = (0, 0, rect[2] - rect[0], rect[3] - rect[1])
self.ocx = MyWordControl()
self.ocx.CreateControl(
"Microsoft Word", win32con.WS_VISIBLE | win32con.WS_CHILD, rect, self, 20000
)
def Demo():
import sys, win32api
docName = None
if len(sys.argv) > 1:
docName = win32api.GetFullPathName(sys.argv[1])
OleTemplate().OpenDocumentFile(None)
# f = WordFrame(docName)
# f.Create("Microsoft Office")
if __name__ == "__main__":
Demo()

View File

@ -0,0 +1,132 @@
# ocxserialtest.py
#
# Sample that uses the mscomm OCX to talk to a serial
# device.
# Very simple - queries a modem for ATI responses
import win32ui, win32uiole
import win32con
from pywin.mfc import dialog, activex
from win32com.client import gencache
import pythoncom
SERIAL_SETTINGS = "19200,n,8,1"
SERIAL_PORT = 2
win32ui.DoWaitCursor(1)
serialModule = gencache.EnsureModule("{648A5603-2C6E-101B-82B6-000000000014}", 0, 1, 1)
win32ui.DoWaitCursor(0)
if serialModule is None:
raise ImportError("MS COMM Control does not appear to be installed on the PC")
def MakeDlgTemplate():
style = (
win32con.DS_MODALFRAME
| win32con.WS_POPUP
| win32con.WS_VISIBLE
| win32con.WS_CAPTION
| win32con.WS_SYSMENU
| win32con.DS_SETFONT
)
cs = win32con.WS_CHILD | win32con.WS_VISIBLE
dlg = [
["Very Basic Terminal", (0, 0, 350, 180), style, None, (8, "MS Sans Serif")],
]
s = win32con.WS_TABSTOP | cs
dlg.append(
[
"RICHEDIT",
None,
132,
(5, 5, 340, 170),
s
| win32con.ES_WANTRETURN
| win32con.ES_MULTILINE
| win32con.ES_AUTOVSCROLL
| win32con.WS_VSCROLL,
]
)
return dlg
####################################
#
# Serial Control
#
class MySerialControl(activex.Control, serialModule.MSComm):
def __init__(self, parent):
activex.Control.__init__(self)
serialModule.MSComm.__init__(self)
self.parent = parent
def OnComm(self):
self.parent.OnComm()
class TestSerDialog(dialog.Dialog):
def __init__(self, *args):
dialog.Dialog.__init__(*(self,) + args)
self.olectl = None
def OnComm(self):
event = self.olectl.CommEvent
if event == serialModule.OnCommConstants.comEvReceive:
self.editwindow.ReplaceSel(self.olectl.Input)
def OnKey(self, key):
if self.olectl:
self.olectl.Output = chr(key)
def OnInitDialog(self):
rc = dialog.Dialog.OnInitDialog(self)
self.editwindow = self.GetDlgItem(132)
self.editwindow.HookAllKeyStrokes(self.OnKey)
self.olectl = MySerialControl(self)
try:
self.olectl.CreateControl(
"OCX",
win32con.WS_TABSTOP | win32con.WS_VISIBLE,
(7, 43, 500, 300),
self._obj_,
131,
)
except win32ui.error:
self.MessageBox("The Serial Control could not be created")
self.olectl = None
self.EndDialog(win32con.IDCANCEL)
if self.olectl:
self.olectl.Settings = SERIAL_SETTINGS
self.olectl.CommPort = SERIAL_PORT
self.olectl.RThreshold = 1
try:
self.olectl.PortOpen = 1
except pythoncom.com_error as details:
print(
"Could not open the specified serial port - %s"
% (details.excepinfo[2])
)
self.EndDialog(win32con.IDCANCEL)
return rc
def OnDestroy(self, msg):
if self.olectl:
try:
self.olectl.PortOpen = 0
except pythoncom.com_error as details:
print("Error closing port - %s" % (details.excepinfo[2]))
return dialog.Dialog.OnDestroy(self, msg)
def test():
d = TestSerDialog(MakeDlgTemplate())
d.DoModal()
if __name__ == "__main__":
from . import demoutils
if demoutils.NeedGoodGUI():
test()

View File

@ -0,0 +1,246 @@
# OCX Tester for Pythonwin
#
# This file _is_ ready to run. All that is required is that the OCXs being tested
# are installed on your machine.
#
# The .py files behind the OCXs will be automatically generated and imported.
from pywin.mfc import dialog, window, activex
import win32ui, win32uiole
import win32con
import os, sys, win32api, glob
from win32com.client import gencache
def MakeDlgTemplate():
style = (
win32con.DS_MODALFRAME
| win32con.WS_POPUP
| win32con.WS_VISIBLE
| win32con.WS_CAPTION
| win32con.WS_SYSMENU
| win32con.DS_SETFONT
)
cs = win32con.WS_CHILD | win32con.WS_VISIBLE
dlg = [
["OCX Demos", (0, 0, 350, 350), style, None, (8, "MS Sans Serif")],
]
s = win32con.WS_TABSTOP | cs
# dlg.append([131, None, 130, (5, 40, 110, 48),
# s | win32con.LBS_NOTIFY | win32con.LBS_SORT | win32con.LBS_NOINTEGRALHEIGHT | win32con.WS_VSCROLL | win32con.WS_BORDER])
# dlg.append(["{8E27C92B-1264-101C-8A2F-040224009C02}", None, 131, (5, 40, 110, 48),win32con.WS_TABSTOP])
dlg.append(
[128, "About", win32con.IDOK, (124, 5, 50, 14), s | win32con.BS_DEFPUSHBUTTON]
)
s = win32con.BS_PUSHBUTTON | s
dlg.append([128, "Close", win32con.IDCANCEL, (124, 22, 50, 14), s])
return dlg
####################################
#
# Calendar test code
#
def GetTestCalendarClass():
global calendarParentModule
win32ui.DoWaitCursor(1)
calendarParentModule = gencache.EnsureModule(
"{8E27C92E-1264-101C-8A2F-040224009C02}", 0, 7, 0
)
win32ui.DoWaitCursor(0)
if calendarParentModule is None:
return None
class TestCalDialog(dialog.Dialog):
def OnInitDialog(self):
class MyCal(activex.Control, calendarParentModule.Calendar):
def OnAfterUpdate(self):
print("OnAfterUpdate")
def OnClick(self):
print("OnClick")
def OnDblClick(self):
print("OnDblClick")
def OnKeyDown(self, KeyCode, Shift):
print("OnKeyDown", KeyCode, Shift)
def OnKeyPress(self, KeyAscii):
print("OnKeyPress", KeyAscii)
def OnKeyUp(self, KeyCode, Shift):
print("OnKeyUp", KeyCode, Shift)
def OnBeforeUpdate(self, Cancel):
print("OnBeforeUpdate", Cancel)
def OnNewMonth(self):
print("OnNewMonth")
def OnNewYear(self):
print("OnNewYear")
rc = dialog.Dialog.OnInitDialog(self)
self.olectl = MyCal()
try:
self.olectl.CreateControl(
"OCX",
win32con.WS_TABSTOP | win32con.WS_VISIBLE,
(7, 43, 500, 300),
self._obj_,
131,
)
except win32ui.error:
self.MessageBox("The Calendar Control could not be created")
self.olectl = None
self.EndDialog(win32con.IDCANCEL)
return rc
def OnOK(self):
self.olectl.AboutBox()
return TestCalDialog
####################################
#
# Video Control
#
def GetTestVideoModule():
global videoControlModule, videoControlFileName
win32ui.DoWaitCursor(1)
videoControlModule = gencache.EnsureModule(
"{05589FA0-C356-11CE-BF01-00AA0055595A}", 0, 2, 0
)
win32ui.DoWaitCursor(0)
if videoControlModule is None:
return None
fnames = glob.glob(os.path.join(win32api.GetWindowsDirectory(), "*.avi"))
if not fnames:
print("No AVI files available in system directory")
return None
videoControlFileName = fnames[0]
return videoControlModule
def GetTestVideoDialogClass():
if GetTestVideoModule() is None:
return None
class TestVideoDialog(dialog.Dialog):
def OnInitDialog(self):
rc = dialog.Dialog.OnInitDialog(self)
try:
self.olectl = activex.MakeControlInstance(
videoControlModule.ActiveMovie
)
self.olectl.CreateControl(
"",
win32con.WS_TABSTOP | win32con.WS_VISIBLE,
(7, 43, 500, 300),
self._obj_,
131,
)
except win32ui.error:
self.MessageBox("The Video Control could not be created")
self.olectl = None
self.EndDialog(win32con.IDCANCEL)
return
self.olectl.FileName = videoControlFileName
# self.olectl.Run()
return rc
def OnOK(self):
self.olectl.AboutBox()
return TestVideoDialog
###############
#
# An OCX in an MDI Frame
#
class OCXFrame(window.MDIChildWnd):
def __init__(self):
pass # Dont call base class doc/view version...
def Create(self, controlClass, title, rect=None, parent=None):
style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW
self._obj_ = win32ui.CreateMDIChild()
self._obj_.AttachObject(self)
self._obj_.CreateWindow(None, title, style, rect, parent)
rect = self.GetClientRect()
rect = (0, 0, rect[2] - rect[0], rect[3] - rect[1])
self.ocx = controlClass()
self.ocx.CreateControl(
"", win32con.WS_VISIBLE | win32con.WS_CHILD, rect, self, 1000
)
def MDITest():
calendarParentModule = gencache.EnsureModule(
"{8E27C92E-1264-101C-8A2F-040224009C02}", 0, 7, 0
)
class MyCal(activex.Control, calendarParentModule.Calendar):
def OnAfterUpdate(self):
print("OnAfterUpdate")
def OnClick(self):
print("OnClick")
f = OCXFrame()
f.Create(MyCal, "Calendar Test")
def test1():
klass = GetTestCalendarClass()
if klass is None:
print(
"Can not test the MSAccess Calendar control - it does not appear to be installed"
)
return
d = klass(MakeDlgTemplate())
d.DoModal()
def test2():
klass = GetTestVideoDialogClass()
if klass is None:
print("Can not test the Video OCX - it does not appear to be installed,")
print("or no AVI files can be found.")
return
d = klass(MakeDlgTemplate())
d.DoModal()
d = None
def test3():
d = TestCOMMDialog(MakeDlgTemplate())
d.DoModal()
d = None
def testall():
test1()
test2()
def demo():
testall()
if __name__ == "__main__":
from . import demoutils
if demoutils.NeedGoodGUI():
testall()

View File

@ -0,0 +1,68 @@
# This demo uses the IE4 Web Browser control.
# It catches an "OnNavigate" event, and updates the frame title.
# (event stuff by Neil Hodgson)
import win32ui, win32con, win32api, regutil
from pywin.mfc import window, activex
from win32com.client import gencache
import sys
WebBrowserModule = gencache.EnsureModule(
"{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}", 0, 1, 1
)
if WebBrowserModule is None:
raise ImportError("IE4 does not appear to be installed.")
class MyWebBrowser(activex.Control, WebBrowserModule.WebBrowser):
def OnBeforeNavigate2(
self, pDisp, URL, Flags, TargetFrameName, PostData, Headers, Cancel
):
self.GetParent().OnNavigate(URL)
# print "BeforeNavigate2", pDisp, URL, Flags, TargetFrameName, PostData, Headers, Cancel
class BrowserFrame(window.MDIChildWnd):
def __init__(self, url=None):
if url is None:
self.url = regutil.GetRegisteredHelpFile("Main Python Documentation")
if self.url is None:
self.url = "http://www.python.org"
else:
self.url = url
pass # Dont call base class doc/view version...
def Create(self, title, rect=None, parent=None):
style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW
self._obj_ = win32ui.CreateMDIChild()
self._obj_.AttachObject(self)
self._obj_.CreateWindow(None, title, style, rect, parent)
rect = self.GetClientRect()
rect = (0, 0, rect[2] - rect[0], rect[3] - rect[1])
self.ocx = MyWebBrowser()
self.ocx.CreateControl(
"Web Browser", win32con.WS_VISIBLE | win32con.WS_CHILD, rect, self, 1000
)
self.ocx.Navigate(self.url)
self.HookMessage(self.OnSize, win32con.WM_SIZE)
def OnSize(self, params):
rect = self.GetClientRect()
rect = (0, 0, rect[2] - rect[0], rect[3] - rect[1])
self.ocx.SetWindowPos(0, rect, 0)
def OnNavigate(self, url):
title = "Web Browser - %s" % (url,)
self.SetWindowText(title)
def Demo(url=None):
if url is None and len(sys.argv) > 1:
url = win32api.GetFullPathName(sys.argv[1])
f = BrowserFrame(url)
f.Create("Web Browser")
if __name__ == "__main__":
Demo()

View File

@ -0,0 +1,369 @@
# Ported from the win32 and MFC OpenGL Samples.
from pywin.mfc import docview
import sys
try:
from OpenGL.GL import *
from OpenGL.GLU import *
except ImportError:
print("The OpenGL extensions do not appear to be installed.")
print("This Pythonwin demo can not run")
sys.exit(1)
import win32con
import win32ui
import win32api
import timer
PFD_TYPE_RGBA = 0
PFD_TYPE_COLORINDEX = 1
PFD_MAIN_PLANE = 0
PFD_OVERLAY_PLANE = 1
PFD_UNDERLAY_PLANE = -1
PFD_DOUBLEBUFFER = 0x00000001
PFD_STEREO = 0x00000002
PFD_DRAW_TO_WINDOW = 0x00000004
PFD_DRAW_TO_BITMAP = 0x00000008
PFD_SUPPORT_GDI = 0x00000010
PFD_SUPPORT_OPENGL = 0x00000020
PFD_GENERIC_FORMAT = 0x00000040
PFD_NEED_PALETTE = 0x00000080
PFD_NEED_SYSTEM_PALETTE = 0x00000100
PFD_SWAP_EXCHANGE = 0x00000200
PFD_SWAP_COPY = 0x00000400
PFD_SWAP_LAYER_BUFFERS = 0x00000800
PFD_GENERIC_ACCELERATED = 0x00001000
PFD_DEPTH_DONTCARE = 0x20000000
PFD_DOUBLEBUFFER_DONTCARE = 0x40000000
PFD_STEREO_DONTCARE = 0x80000000
# threeto8 = [0, 0o111>>1, 0o222>>1, 0o333>>1, 0o444>>1, 0o555>>1, 0o666>>1, 0o377]
threeto8 = [0, 73 >> 1, 146 >> 1, 219 >> 1, 292 >> 1, 365 >> 1, 438 >> 1, 255]
twoto8 = [0, 0x55, 0xAA, 0xFF]
oneto8 = [0, 255]
def ComponentFromIndex(i, nbits, shift):
# val = (unsigned char) (i >> shift);
val = (i >> shift) & 0xF
if nbits == 1:
val = val & 0x1
return oneto8[val]
elif nbits == 2:
val = val & 0x3
return twoto8[val]
elif nbits == 3:
val = val & 0x7
return threeto8[val]
else:
return 0
OpenGLViewParent = docview.ScrollView
class OpenGLView(OpenGLViewParent):
def PreCreateWindow(self, cc):
self.HookMessage(self.OnSize, win32con.WM_SIZE)
# An OpenGL window must be created with the following flags and must not
# include CS_PARENTDC for the class style. Refer to SetPixelFormat
# documentation in the "Comments" section for further information.
style = cc[5]
style = style | win32con.WS_CLIPSIBLINGS | win32con.WS_CLIPCHILDREN
cc = cc[0], cc[1], cc[2], cc[3], cc[4], style, cc[6], cc[7], cc[8]
cc = self._obj_.PreCreateWindow(cc)
return cc
def OnSize(self, params):
lParam = params[3]
cx = win32api.LOWORD(lParam)
cy = win32api.HIWORD(lParam)
glViewport(0, 0, cx, cy)
if self.oldrect[2] > cx or self.oldrect[3] > cy:
self.RedrawWindow()
self.OnSizeChange(cx, cy)
self.oldrect = self.oldrect[0], self.oldrect[1], cx, cy
def OnInitialUpdate(self):
self.SetScaleToFitSize(
(100, 100)
) # or SetScrollSizes() - A Pythonwin requirement
return self._obj_.OnInitialUpdate()
# return rc
def OnCreate(self, cs):
self.oldrect = self.GetClientRect()
self._InitContexts()
self.Init()
def OnDestroy(self, msg):
self.Term()
self._DestroyContexts()
return OpenGLViewParent.OnDestroy(self, msg)
def OnDraw(self, dc):
self.DrawScene()
def OnEraseBkgnd(self, dc):
return 1
# The OpenGL helpers
def _SetupPixelFormat(self):
dc = self.dc.GetSafeHdc()
pfd = CreatePIXELFORMATDESCRIPTOR()
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER
pfd.iPixelType = PFD_TYPE_RGBA
pfd.cColorBits = 24
pfd.cDepthBits = 32
pfd.iLayerType = PFD_MAIN_PLANE
pixelformat = ChoosePixelFormat(dc, pfd)
SetPixelFormat(dc, pixelformat, pfd)
self._CreateRGBPalette()
def _CreateRGBPalette(self):
dc = self.dc.GetSafeHdc()
n = GetPixelFormat(dc)
pfd = DescribePixelFormat(dc, n)
if pfd.dwFlags & PFD_NEED_PALETTE:
n = 1 << pfd.cColorBits
pal = []
for i in range(n):
this = (
ComponentFromIndex(i, pfd.cRedBits, pfd.cRedShift),
ComponentFromIndex(i, pfd.cGreenBits, pfd.cGreenShift),
ComponentFromIndex(i, pfd.cBlueBits, pfd.cBlueShift),
0,
)
pal.append(this)
hpal = win32ui.CreatePalette(pal)
self.dc.SelectPalette(hpal, 0)
self.dc.RealizePalette()
def _InitContexts(self):
self.dc = self.GetDC()
self._SetupPixelFormat()
hrc = wglCreateContext(self.dc.GetSafeHdc())
wglMakeCurrent(self.dc.GetSafeHdc(), hrc)
def _DestroyContexts(self):
hrc = wglGetCurrentContext()
wglMakeCurrent(0, 0)
if hrc:
wglDeleteContext(hrc)
# The methods to support OpenGL
def DrawScene(self):
assert 0, "You must override this method"
def Init(self):
assert 0, "You must override this method"
def OnSizeChange(self, cx, cy):
pass
def Term(self):
pass
class TestView(OpenGLView):
def OnSizeChange(self, right, bottom):
glClearColor(0.0, 0.0, 0.0, 1.0)
glClearDepth(1.0)
glEnable(GL_DEPTH_TEST)
glMatrixMode(GL_PROJECTION)
if bottom:
aspect = right / bottom
else:
aspect = 0 # When window created!
glLoadIdentity()
gluPerspective(45.0, aspect, 3.0, 7.0)
glMatrixMode(GL_MODELVIEW)
near_plane = 3.0
far_plane = 7.0
maxObjectSize = 3.0
self.radius = near_plane + maxObjectSize / 2.0
def Init(self):
pass
def DrawScene(self):
glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glPushMatrix()
glTranslatef(0.0, 0.0, -self.radius)
self._DrawCone()
self._DrawPyramid()
glPopMatrix()
glFinish()
SwapBuffers(wglGetCurrentDC())
def _DrawCone(self):
glColor3f(0.0, 1.0, 0.0)
glPushMatrix()
glTranslatef(-1.0, 0.0, 0.0)
quadObj = gluNewQuadric()
gluQuadricDrawStyle(quadObj, GLU_FILL)
gluQuadricNormals(quadObj, GLU_SMOOTH)
gluCylinder(quadObj, 1.0, 0.0, 1.0, 20, 10)
# gluDeleteQuadric(quadObj);
glPopMatrix()
def _DrawPyramid(self):
glPushMatrix()
glTranslatef(1.0, 0.0, 0.0)
glBegin(GL_TRIANGLE_FAN)
glColor3f(1.0, 0.0, 0.0)
glVertex3f(0.0, 1.0, 0.0)
glColor3f(0.0, 1.0, 0.0)
glVertex3f(-1.0, 0.0, 0.0)
glColor3f(0.0, 0.0, 1.0)
glVertex3f(0.0, 0.0, 1.0)
glColor3f(0.0, 1.0, 0.0)
glVertex3f(1.0, 0.0, 0.0)
glEnd()
glPopMatrix()
class CubeView(OpenGLView):
def OnSizeChange(self, right, bottom):
glClearColor(0.0, 0.0, 0.0, 1.0)
glClearDepth(1.0)
glEnable(GL_DEPTH_TEST)
glMatrixMode(GL_PROJECTION)
if bottom:
aspect = right / bottom
else:
aspect = 0 # When window created!
glLoadIdentity()
gluPerspective(45.0, aspect, 3.0, 7.0)
glMatrixMode(GL_MODELVIEW)
near_plane = 3.0
far_plane = 7.0
maxObjectSize = 3.0
self.radius = near_plane + maxObjectSize / 2.0
def Init(self):
self.busy = 0
self.wAngleY = 10.0
self.wAngleX = 1.0
self.wAngleZ = 5.0
self.timerid = timer.set_timer(150, self.OnTimer)
def OnTimer(self, id, timeVal):
self.DrawScene()
def Term(self):
timer.kill_timer(self.timerid)
def DrawScene(self):
if self.busy:
return
self.busy = 1
glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glPushMatrix()
glTranslatef(0.0, 0.0, -self.radius)
glRotatef(self.wAngleX, 1.0, 0.0, 0.0)
glRotatef(self.wAngleY, 0.0, 1.0, 0.0)
glRotatef(self.wAngleZ, 0.0, 0.0, 1.0)
self.wAngleX = self.wAngleX + 1.0
self.wAngleY = self.wAngleY + 10.0
self.wAngleZ = self.wAngleZ + 5.0
glBegin(GL_QUAD_STRIP)
glColor3f(1.0, 0.0, 1.0)
glVertex3f(-0.5, 0.5, 0.5)
glColor3f(1.0, 0.0, 0.0)
glVertex3f(-0.5, -0.5, 0.5)
glColor3f(1.0, 1.0, 1.0)
glVertex3f(0.5, 0.5, 0.5)
glColor3f(1.0, 1.0, 0.0)
glVertex3f(0.5, -0.5, 0.5)
glColor3f(0.0, 1.0, 1.0)
glVertex3f(0.5, 0.5, -0.5)
glColor3f(0.0, 1.0, 0.0)
glVertex3f(0.5, -0.5, -0.5)
glColor3f(0.0, 0.0, 1.0)
glVertex3f(-0.5, 0.5, -0.5)
glColor3f(0.0, 0.0, 0.0)
glVertex3f(-0.5, -0.5, -0.5)
glColor3f(1.0, 0.0, 1.0)
glVertex3f(-0.5, 0.5, 0.5)
glColor3f(1.0, 0.0, 0.0)
glVertex3f(-0.5, -0.5, 0.5)
glEnd()
glBegin(GL_QUADS)
glColor3f(1.0, 0.0, 1.0)
glVertex3f(-0.5, 0.5, 0.5)
glColor3f(1.0, 1.0, 1.0)
glVertex3f(0.5, 0.5, 0.5)
glColor3f(0.0, 1.0, 1.0)
glVertex3f(0.5, 0.5, -0.5)
glColor3f(0.0, 0.0, 1.0)
glVertex3f(-0.5, 0.5, -0.5)
glEnd()
glBegin(GL_QUADS)
glColor3f(1.0, 0.0, 0.0)
glVertex3f(-0.5, -0.5, 0.5)
glColor3f(1.0, 1.0, 0.0)
glVertex3f(0.5, -0.5, 0.5)
glColor3f(0.0, 1.0, 0.0)
glVertex3f(0.5, -0.5, -0.5)
glColor3f(0.0, 0.0, 0.0)
glVertex3f(-0.5, -0.5, -0.5)
glEnd()
glPopMatrix()
glFinish()
SwapBuffers(wglGetCurrentDC())
self.busy = 0
def test():
template = docview.DocTemplate(None, None, None, CubeView)
# template = docview.DocTemplate(None, None, None, TestView )
template.OpenDocumentFile(None)
if __name__ == "__main__":
test()

View File

@ -0,0 +1,105 @@
#
# Progress bar control example
#
# PyCProgressCtrl encapsulates the MFC CProgressCtrl class. To use it,
# you:
#
# - Create the control with win32ui.CreateProgressCtrl()
# - Create the control window with PyCProgressCtrl.CreateWindow()
# - Initialize the range if you want it to be other than (0, 100) using
# PyCProgressCtrl.SetRange()
# - Either:
# - Set the step size with PyCProgressCtrl.SetStep(), and
# - Increment using PyCProgressCtrl.StepIt()
# or:
# - Set the amount completed using PyCProgressCtrl.SetPos()
#
# Example and progress bar code courtesy of KDL Technologies, Ltd., Hong Kong SAR, China.
#
from pywin.mfc import dialog
import win32ui
import win32con
def MakeDlgTemplate():
style = (
win32con.DS_MODALFRAME
| win32con.WS_POPUP
| win32con.WS_VISIBLE
| win32con.WS_CAPTION
| win32con.WS_SYSMENU
| win32con.DS_SETFONT
)
cs = win32con.WS_CHILD | win32con.WS_VISIBLE
w = 215
h = 36
dlg = [
[
"Progress bar control example",
(0, 0, w, h),
style,
None,
(8, "MS Sans Serif"),
],
]
s = win32con.WS_TABSTOP | cs
dlg.append(
[
128,
"Tick",
win32con.IDOK,
(10, h - 18, 50, 14),
s | win32con.BS_DEFPUSHBUTTON,
]
)
dlg.append(
[
128,
"Cancel",
win32con.IDCANCEL,
(w - 60, h - 18, 50, 14),
s | win32con.BS_PUSHBUTTON,
]
)
return dlg
class TestDialog(dialog.Dialog):
def OnInitDialog(self):
rc = dialog.Dialog.OnInitDialog(self)
self.pbar = win32ui.CreateProgressCtrl()
self.pbar.CreateWindow(
win32con.WS_CHILD | win32con.WS_VISIBLE, (10, 10, 310, 24), self, 1001
)
# self.pbar.SetStep (5)
self.progress = 0
self.pincr = 5
return rc
def OnOK(self):
# NB: StepIt wraps at the end if you increment past the upper limit!
# self.pbar.StepIt()
self.progress = self.progress + self.pincr
if self.progress > 100:
self.progress = 100
if self.progress <= 100:
self.pbar.SetPos(self.progress)
def demo(modal=0):
d = TestDialog(MakeDlgTemplate())
if modal:
d.DoModal()
else:
d.CreateWindow()
if __name__ == "__main__":
demo(1)

View File

@ -0,0 +1,75 @@
# sliderdemo.py
# Demo of the slider control courtesy of Mike Fletcher.
import win32con, win32ui
from pywin.mfc import dialog
class MyDialog(dialog.Dialog):
"""
Example using simple controls
"""
_dialogstyle = (
win32con.WS_MINIMIZEBOX
| win32con.WS_DLGFRAME
| win32con.DS_MODALFRAME
| win32con.WS_POPUP
| win32con.WS_VISIBLE
| win32con.WS_CAPTION
| win32con.WS_SYSMENU
| win32con.DS_SETFONT
)
_buttonstyle = (
win32con.BS_PUSHBUTTON
| win32con.WS_TABSTOP
| win32con.WS_CHILD
| win32con.WS_VISIBLE
)
### The static template, contains all "normal" dialog items
DIALOGTEMPLATE = [
# the dialog itself is the first element in the template
["Example slider", (0, 0, 50, 43), _dialogstyle, None, (8, "MS SansSerif")],
# rest of elements are the controls within the dialog
# standard "Close" button
[128, "Close", win32con.IDCANCEL, (0, 30, 50, 13), _buttonstyle],
]
### ID of the control to be created during dialog initialisation
IDC_SLIDER = 9500
def __init__(self):
dialog.Dialog.__init__(self, self.DIALOGTEMPLATE)
def OnInitDialog(self):
rc = dialog.Dialog.OnInitDialog(self)
# now initialise your controls that you want to create
# programmatically, including those which are OLE controls
# those created directly by win32ui.Create*
# and your "custom controls" which are subclasses/whatever
win32ui.EnableControlContainer()
self.slider = win32ui.CreateSliderCtrl()
self.slider.CreateWindow(
win32con.WS_TABSTOP | win32con.WS_VISIBLE,
(0, 0, 100, 30),
self._obj_,
self.IDC_SLIDER,
)
self.HookMessage(self.OnSliderMove, win32con.WM_HSCROLL)
return rc
def OnSliderMove(self, params):
print("Slider moved")
def OnCancel(self):
print("The slider control is at position", self.slider.GetPos())
self._obj_.OnCancel()
###
def demo():
dia = MyDialog()
dia.DoModal()
if __name__ == "__main__":
demo()

View File

@ -0,0 +1,80 @@
import win32ui
import win32con
import fontdemo
from pywin.mfc import window, docview
import commctrl
# derive from CMDIChild. This does much work for us.
class SplitterFrame(window.MDIChildWnd):
def __init__(self):
# call base CreateFrame
self.images = None
window.MDIChildWnd.__init__(self)
def OnCreateClient(self, cp, context):
splitter = win32ui.CreateSplitter()
doc = context.doc
frame_rect = self.GetWindowRect()
size = ((frame_rect[2] - frame_rect[0]), (frame_rect[3] - frame_rect[1]) // 2)
sub_size = (size[0] // 2, size[1])
splitter.CreateStatic(self, 2, 1)
self.v1 = win32ui.CreateEditView(doc)
self.v2 = fontdemo.FontView(doc)
# CListControl view
self.v3 = win32ui.CreateListView(doc)
sub_splitter = win32ui.CreateSplitter()
# pass "splitter" so each view knows how to get to the others
sub_splitter.CreateStatic(splitter, 1, 2)
sub_splitter.CreateView(self.v1, 0, 0, (sub_size))
sub_splitter.CreateView(self.v2, 0, 1, (0, 0)) # size ignored.
splitter.SetRowInfo(0, size[1], 0)
splitter.CreateView(self.v3, 1, 0, (0, 0)) # size ignored.
# Setup items in the imagelist
self.images = win32ui.CreateImageList(32, 32, 1, 5, 5)
self.images.Add(win32ui.GetApp().LoadIcon(win32ui.IDR_MAINFRAME))
self.images.Add(win32ui.GetApp().LoadIcon(win32ui.IDR_PYTHONCONTYPE))
self.images.Add(win32ui.GetApp().LoadIcon(win32ui.IDR_TEXTTYPE))
self.v3.SetImageList(self.images, commctrl.LVSIL_NORMAL)
self.v3.InsertItem(0, "Icon 1", 0)
self.v3.InsertItem(0, "Icon 2", 1)
self.v3.InsertItem(0, "Icon 3", 2)
# self.v3.Arrange(commctrl.LVA_DEFAULT) Hmmm - win95 aligns left always???
return 1
def OnDestroy(self, msg):
window.MDIChildWnd.OnDestroy(self, msg)
if self.images:
self.images.DeleteImageList()
self.images = None
def InitialUpdateFrame(self, doc, makeVisible):
self.v1.ReplaceSel("Hello from Edit Window 1")
self.v1.SetModifiedFlag(0)
class SampleTemplate(docview.DocTemplate):
def __init__(self):
docview.DocTemplate.__init__(
self, win32ui.IDR_PYTHONTYPE, None, SplitterFrame, None
)
def InitialUpdateFrame(self, frame, doc, makeVisible):
# print "frame is ", frame, frame._obj_
# print "doc is ", doc, doc._obj_
self._obj_.InitialUpdateFrame(frame, doc, makeVisible) # call default handler.
frame.InitialUpdateFrame(doc, makeVisible)
def demo():
template = SampleTemplate()
doc = template.OpenDocumentFile(None)
doc.SetTitle("Splitter Demo")
if __name__ == "__main__":
import demoutils
if demoutils.NeedGoodGUI():
demo()

View File

@ -0,0 +1,190 @@
# Demo of using just windows, without documents and views.
# Also demo of a GUI thread, pretty much direct from the MFC C++ sample MTMDI.
import win32ui
import win32con
import win32api
import timer
from pywin.mfc import window, docview, thread
from pywin.mfc.thread import WinThread
WM_USER_PREPARE_TO_CLOSE = win32con.WM_USER + 32
# font is a dictionary in which the following elements matter:
# (the best matching font to supplied parameters is returned)
# name string name of the font as known by Windows
# size point size of font in logical units
# weight weight of font (win32con.FW_NORMAL, win32con.FW_BOLD)
# italic boolean; true if set to anything but None
# underline boolean; true if set to anything but None
# This window is a child window of a frame. It is not the frame window itself.
class FontWindow(window.Wnd):
def __init__(self, text="Python Rules!"):
window.Wnd.__init__(self)
self.text = text
self.index = 0
self.incr = 1
self.width = self.height = 0
self.ChangeAttributes()
# set up message handlers
def Create(self, title, style, rect, parent):
classStyle = win32con.CS_HREDRAW | win32con.CS_VREDRAW
className = win32ui.RegisterWndClass(
classStyle, 0, win32con.COLOR_WINDOW + 1, 0
)
self._obj_ = win32ui.CreateWnd()
self._obj_.AttachObject(self)
self._obj_.CreateWindow(
className, title, style, rect, parent, win32ui.AFX_IDW_PANE_FIRST
)
self.HookMessage(self.OnSize, win32con.WM_SIZE)
self.HookMessage(self.OnPrepareToClose, WM_USER_PREPARE_TO_CLOSE)
self.HookMessage(self.OnDestroy, win32con.WM_DESTROY)
self.timerid = timer.set_timer(100, self.OnTimer)
self.InvalidateRect()
def OnDestroy(self, msg):
timer.kill_timer(self.timerid)
def OnTimer(self, id, timeVal):
self.index = self.index + self.incr
if self.index > len(self.text):
self.incr = -1
self.index = len(self.text)
elif self.index < 0:
self.incr = 1
self.index = 0
self.InvalidateRect()
def OnPaint(self):
# print "Paint message from thread", win32api.GetCurrentThreadId()
dc, paintStruct = self.BeginPaint()
self.OnPrepareDC(dc, None)
if self.width == 0 and self.height == 0:
left, top, right, bottom = self.GetClientRect()
self.width = right - left
self.height = bottom - top
x, y = self.width // 2, self.height // 2
dc.TextOut(x, y, self.text[: self.index])
self.EndPaint(paintStruct)
def ChangeAttributes(self):
font_spec = {"name": "Arial", "height": 42}
self.font = win32ui.CreateFont(font_spec)
def OnPrepareToClose(self, params):
self.DestroyWindow()
def OnSize(self, params):
lParam = params[3]
self.width = win32api.LOWORD(lParam)
self.height = win32api.HIWORD(lParam)
def OnPrepareDC(self, dc, printinfo):
# Set up the DC for forthcoming OnDraw call
dc.SetTextColor(win32api.RGB(0, 0, 255))
dc.SetBkColor(win32api.GetSysColor(win32con.COLOR_WINDOW))
dc.SelectObject(self.font)
dc.SetTextAlign(win32con.TA_CENTER | win32con.TA_BASELINE)
class FontFrame(window.MDIChildWnd):
def __init__(self):
pass # Dont call base class doc/view version...
def Create(self, title, rect=None, parent=None):
style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW
self._obj_ = win32ui.CreateMDIChild()
self._obj_.AttachObject(self)
self._obj_.CreateWindow(None, title, style, rect, parent)
rect = self.GetClientRect()
rect = (0, 0, rect[2] - rect[0], rect[3] - rect[1])
self.child = FontWindow("Not threaded")
self.child.Create(
"FontDemo", win32con.WS_CHILD | win32con.WS_VISIBLE, rect, self
)
class TestThread(WinThread):
def __init__(self, parentWindow):
self.parentWindow = parentWindow
self.child = None
WinThread.__init__(self)
def InitInstance(self):
rect = self.parentWindow.GetClientRect()
rect = (0, 0, rect[2] - rect[0], rect[3] - rect[1])
self.child = FontWindow()
self.child.Create(
"FontDemo", win32con.WS_CHILD | win32con.WS_VISIBLE, rect, self.parentWindow
)
self.SetMainFrame(self.child)
return WinThread.InitInstance(self)
def ExitInstance(self):
return 0
class ThreadedFontFrame(window.MDIChildWnd):
def __init__(self):
pass # Dont call base class doc/view version...
self.thread = None
def Create(self, title, rect=None, parent=None):
style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW
self._obj_ = win32ui.CreateMDIChild()
self._obj_.CreateWindow(None, title, style, rect, parent)
self._obj_.HookMessage(self.OnDestroy, win32con.WM_DESTROY)
self._obj_.HookMessage(self.OnSize, win32con.WM_SIZE)
self.thread = TestThread(self)
self.thread.CreateThread()
def OnSize(self, msg):
pass
def OnDestroy(self, msg):
win32ui.OutputDebugString("OnDestroy\n")
if self.thread and self.thread.child:
child = self.thread.child
child.SendMessage(WM_USER_PREPARE_TO_CLOSE, 0, 0)
win32ui.OutputDebugString("Destroyed\n")
def Demo():
f = FontFrame()
f.Create("Font Demo")
def ThreadedDemo():
rect = win32ui.GetMainFrame().GetMDIClient().GetClientRect()
rect = rect[0], int(rect[3] * 3 / 4), int(rect[2] / 4), rect[3]
incr = rect[2]
for i in range(4):
if i == 0:
f = FontFrame()
title = "Not threaded"
else:
f = ThreadedFontFrame()
title = "Threaded GUI Demo"
f.Create(title, rect)
rect = rect[0] + incr, rect[1], rect[2] + incr, rect[3]
# Givem a chance to start
win32api.Sleep(100)
win32ui.PumpWaitingMessages()
if __name__ == "__main__":
import demoutils
if demoutils.NeedGoodGUI():
ThreadedDemo()
# Demo()

View File

@ -0,0 +1,106 @@
# Demo of ToolBars
# Shows the toolbar control.
# Demos how to make custom tooltips, etc.
import win32ui
import win32con
import win32api
from pywin.mfc import docview, window, afxres
import commctrl
class GenericFrame(window.MDIChildWnd):
def OnCreateClient(self, cp, context):
# handlers for toolbar buttons
self.HookCommand(self.OnPrevious, 401)
self.HookCommand(self.OnNext, 402)
# Its not necessary for us to hook both of these - the
# common controls should fall-back all by themselves.
# Indeed, given we hook TTN_NEEDTEXTW, commctrl.TTN_NEEDTEXTA
# will not be called.
self.HookNotify(self.GetTTText, commctrl.TTN_NEEDTEXT)
self.HookNotify(self.GetTTText, commctrl.TTN_NEEDTEXTW)
# parent = win32ui.GetMainFrame()
parent = self
style = (
win32con.WS_CHILD
| win32con.WS_VISIBLE
| afxres.CBRS_SIZE_DYNAMIC
| afxres.CBRS_TOP
| afxres.CBRS_TOOLTIPS
| afxres.CBRS_FLYBY
)
buttons = (win32ui.ID_APP_ABOUT, win32ui.ID_VIEW_INTERACTIVE)
bitmap = win32ui.IDB_BROWSER_HIER
tbid = 0xE840
self.toolbar = tb = win32ui.CreateToolBar(parent, style, tbid)
tb.LoadBitmap(bitmap)
tb.SetButtons(buttons)
tb.EnableDocking(afxres.CBRS_ALIGN_ANY)
tb.SetWindowText("Test")
parent.EnableDocking(afxres.CBRS_ALIGN_ANY)
parent.DockControlBar(tb)
parent.LoadBarState("ToolbarTest")
window.MDIChildWnd.OnCreateClient(self, cp, context)
return 1
def OnDestroy(self, msg):
self.SaveBarState("ToolbarTest")
def GetTTText(self, std, extra):
(hwndFrom, idFrom, code) = std
text, hinst, flags = extra
if flags & commctrl.TTF_IDISHWND:
return # Not handled
if idFrom == win32ui.ID_APP_ABOUT:
# our 'extra' return value needs to be the following
# entries from a NMTTDISPINFO[W] struct:
# (szText, hinst, uFlags). None means 'don't change
# the value'
return 0, ("It works!", None, None)
return None # not handled.
def GetMessageString(self, id):
if id == win32ui.ID_APP_ABOUT:
return "Dialog Test\nTest"
else:
return self._obj_.GetMessageString(id)
def OnSize(self, params):
print("OnSize called with ", params)
def OnNext(self, id, cmd):
print("OnNext called")
def OnPrevious(self, id, cmd):
print("OnPrevious called")
msg = """\
This toolbar was dynamically created.\r
\r
The first item's tooltips is provided by Python code.\r
\r
(Dont close the window with the toolbar in a floating state - it may not re-appear!)\r
"""
def test():
template = docview.DocTemplate(
win32ui.IDR_PYTHONTYPE, None, GenericFrame, docview.EditView
)
doc = template.OpenDocumentFile(None)
doc.SetTitle("Toolbar Test")
view = doc.GetFirstView()
view.SetWindowText(msg)
if __name__ == "__main__":
import demoutils
if demoutils.NeedGoodGUI():
test()

View File

@ -0,0 +1,29 @@
[General]
# We base this configuration on the default config.
# You can list "Based On" as many times as you like
Based On = default
[Keys]
# Only list keys different to default.
# Note you may wish to rebind some of the default
# Pythonwin keys to "Beep" or "DoNothing"
Alt+L = LocateSelectedFile
Ctrl+Q = AppExit
# Other non-default Pythonwin keys
Alt+A = EditSelectAll
Alt+M = LocateModule
# Movement
Ctrl+D = GotoEndOfFile
# Tabs and other indent features
Alt+T = <<toggle-tabs>>
Ctrl+[ = <<indent-region>>
Ctrl+] = <<dedent-region>>
[Keys:Interactive]
Alt+P = <<history-previous>>
Alt+N = <<history-next>>

View File

@ -0,0 +1,10 @@
# is_platform_unicode is an old variable that was never correctly used and
# is no longer referenced in pywin32. It is staying for a few releases incase
# others are looking at it, but it will go away soon!
is_platform_unicode = 0
# Ditto default_platform_encoding - not referenced and will die.
default_platform_encoding = "mbcs"
# This one *is* real and used - but in practice can't be changed.
default_scintilla_encoding = "utf-8" # Scintilla _only_ supports this ATM

View File

@ -0,0 +1,137 @@
import sys
# Some cruft to deal with the Pythonwin GUI booting up from a non GUI app.
def _MakeDebuggerGUI():
app.InitInstance()
isInprocApp = -1
def _CheckNeedGUI():
global isInprocApp
if isInprocApp == -1:
import win32ui
isInprocApp = win32ui.GetApp().IsInproc()
if isInprocApp:
# MAY Need it - may already have one
need = "pywin.debugger.dbgpyapp" not in sys.modules
else:
need = 0
if need:
import pywin.framework.app
from . import dbgpyapp
pywin.framework.app.CreateDefaultGUI(dbgpyapp.DebuggerPythonApp)
else:
# Check we have the appropriate editor
# No longer necessary!
pass
return need
# Inject some methods in the top level name-space.
currentDebugger = None # Wipe out any old one on reload.
def _GetCurrentDebugger():
global currentDebugger
if currentDebugger is None:
_CheckNeedGUI()
from . import debugger
currentDebugger = debugger.Debugger()
return currentDebugger
def GetDebugger():
# An error here is not nice - as we are probably trying to
# break into the debugger on a Python error, any
# error raised by this is usually silent, and causes
# big problems later!
try:
rc = _GetCurrentDebugger()
rc.GUICheckInit()
return rc
except:
print("Could not create the debugger!")
import traceback
traceback.print_exc()
return None
def close():
if currentDebugger is not None:
currentDebugger.close()
def run(cmd, globals=None, locals=None, start_stepping=1):
_GetCurrentDebugger().run(cmd, globals, locals, start_stepping)
def runeval(expression, globals=None, locals=None):
return _GetCurrentDebugger().runeval(expression, globals, locals)
def runcall(*args):
return _GetCurrentDebugger().runcall(*args)
def set_trace():
import sys
d = _GetCurrentDebugger()
if d.frameShutdown:
return # App closing
if d.stopframe != d.botframe:
# If im not "running"
return
sys.settrace(None) # May be hooked
d.reset()
d.set_trace()
# "brk" is an alias for "set_trace" ("break" is a reserved word :-(
brk = set_trace
# Post-Mortem interface
def post_mortem(t=None):
if t is None:
t = sys.exc_info()[2] # Will be valid if we are called from an except handler.
if t is None:
try:
t = sys.last_traceback
except AttributeError:
print(
"No traceback can be found from which to perform post-mortem debugging!"
)
print("No debugging can continue")
return
p = _GetCurrentDebugger()
if p.frameShutdown:
return # App closing
# No idea why I need to settrace to None - it should have been reset by now?
sys.settrace(None)
p.reset()
while t.tb_next != None:
t = t.tb_next
p.bAtPostMortem = 1
p.prep_run(None)
try:
p.interaction(t.tb_frame, t)
finally:
t = None
p.bAtPostMortem = 0
p.done_run()
def pm(t=None):
post_mortem(t)

View File

@ -0,0 +1,33 @@
from . import dbgcon
from pywin.mfc import dialog
import win32ui
class DebuggerOptionsPropPage(dialog.PropertyPage):
def __init__(self):
dialog.PropertyPage.__init__(self, win32ui.IDD_PP_DEBUGGER)
def OnInitDialog(self):
options = self.options = dbgcon.LoadDebuggerOptions()
self.AddDDX(win32ui.IDC_CHECK1, dbgcon.OPT_HIDE)
self[dbgcon.OPT_STOP_EXCEPTIONS] = options[dbgcon.OPT_STOP_EXCEPTIONS]
self.AddDDX(win32ui.IDC_CHECK2, dbgcon.OPT_STOP_EXCEPTIONS)
self[dbgcon.OPT_HIDE] = options[dbgcon.OPT_HIDE]
return dialog.PropertyPage.OnInitDialog(self)
def OnOK(self):
self.UpdateData()
dirty = 0
for key, val in list(self.items()):
if key in self.options:
if self.options[key] != val:
self.options[key] = val
dirty = 1
if dirty:
dbgcon.SaveDebuggerOptions(self.options)
# If there is a debugger open, then set its options.
import pywin.debugger
if pywin.debugger.currentDebugger is not None:
pywin.debugger.currentDebugger.options = self.options
return 1

View File

@ -0,0 +1,31 @@
# General constants for the debugger
DBGSTATE_NOT_DEBUGGING = 0
DBGSTATE_RUNNING = 1
DBGSTATE_BREAK = 2
DBGSTATE_QUITTING = 3 # Attempting to back out of the debug session.
LINESTATE_CURRENT = 0x1 # This line is where we are stopped
LINESTATE_BREAKPOINT = 0x2 # This line is a breakpoint
LINESTATE_CALLSTACK = 0x4 # This line is in the callstack.
OPT_HIDE = "hide"
OPT_STOP_EXCEPTIONS = "stopatexceptions"
import win32api, win32ui
def DoGetOption(optsDict, optName, default):
optsDict[optName] = win32ui.GetProfileVal("Debugger Options", optName, default)
def LoadDebuggerOptions():
opts = {}
DoGetOption(opts, OPT_HIDE, 0)
DoGetOption(opts, OPT_STOP_EXCEPTIONS, 1)
return opts
def SaveDebuggerOptions(opts):
for key, val in opts.items():
win32ui.WriteProfileVal("Debugger Options", key, val)

View File

@ -0,0 +1,49 @@
# dbgpyapp.py - Debugger Python application class
#
import win32con
import win32ui
import sys
import string
import os
from pywin.framework import intpyapp
version = "0.3.0"
class DebuggerPythonApp(intpyapp.InteractivePythonApp):
def LoadMainFrame(self):
"Create the main applications frame"
self.frame = self.CreateMainFrame()
self.SetMainFrame(self.frame)
self.frame.LoadFrame(win32ui.IDR_DEBUGGER, win32con.WS_OVERLAPPEDWINDOW)
self.frame.DragAcceptFiles() # we can accept these.
self.frame.ShowWindow(win32con.SW_HIDE)
self.frame.UpdateWindow()
# but we do rehook, hooking the new code objects.
self.HookCommands()
def InitInstance(self):
# Use a registry path of "Python\Pythonwin Debugger
win32ui.SetAppName(win32ui.LoadString(win32ui.IDR_DEBUGGER))
win32ui.SetRegistryKey("Python %s" % (sys.winver,))
# We _need_ the Scintilla color editor.
# (and we _always_ get it now :-)
numMRU = win32ui.GetProfileVal("Settings", "Recent File List Size", 10)
win32ui.LoadStdProfileSettings(numMRU)
self.LoadMainFrame()
# Display the interactive window if the user wants it.
from pywin.framework import interact
interact.CreateInteractiveWindowUserPreference()
# Load the modules we use internally.
self.LoadSystemModules()
# Load additional module the user may want.
self.LoadUserModules()
# win32ui.CreateDebuggerThread()
win32ui.EnableControlContainer()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,54 @@
# NOTE NOTE - This module is designed to fail!
#
# The ONLY purpose for this script is testing/demoing the
# Pythonwin debugger package.
# It does nothing useful, and it even doesnt do that!
import pywin.debugger, sys, time
import traceback
def a():
a = 1
try:
b()
except:
# Break into the debugger with the exception information.
pywin.debugger.post_mortem(sys.exc_info()[2])
a = 1
a = 2
a = 3
a = 4
pass
def b():
b = 1
pywin.debugger.set_trace()
# After importing or running this module, you are likely to be
# sitting at the next line. This is because we explicitely
# broke into the debugger using the "set_trace() function
# "pywin.debugger.brk()" is a shorter alias for this.
c()
pass
def c():
c = 1
d()
def d():
d = 1
e(d)
raise ValueError("Hi")
def e(arg):
e = 1
time.sleep(1)
return e
a()

View File

@ -0,0 +1,215 @@
# The default keyboard etc configuration file for Pythonwin.
#
# The format of this file is very similar to a Windows INI file.
# Sections are identified with [Section] lines, but comments
# use the standatd Python # character. Depending on the section,
# lines may not be in the standard "key=value" format.
# NOTE: You should not need to modify this file.
# Simply create a new .CFG file, and add an entry:
# [General]
# BasedOn = Default
#
# and add your customisations. Then select your new configuration
# from the Pythonwin View/Options/Editor dialog.
# This way you get to add your own customisations,
# but still take advantage of changes to the default
# configuration in new releases.
# See IDLE.cfg for an example extension configuration.
#
##########################################################################
[IDLE Extensions]
# The list of IDLE extensions to load. The extensions
# AutoIndent, AutoFormat and possibly others are
# "built-in", so do not need specifying.
FormatParagraph
CallTips
[Keys]
# The list of _default_ key definitions.
# See [Keys:Interactive] and [Keys:Editor] below for further defs.
#Events of the format <<event-name>>
# are events defined in IDLE extensions.
Alt+Q = <<format-paragraph>>
Ctrl+W = ViewWhitespace
Ctrl+Shift+8 = ViewWhitespace # The MSVC default key def.
Ctrl+Shift+F = ViewFixedFont
# Auto-complete, call-tips, etc.
Alt+/ = <<expand-word>>
Ctrl+Space = <<expand-word>>
( = <<paren-open>>
) = <<paren-close>>
Up = <<check-calltip-cancel>>
Down = <<check-calltip-cancel>>
Left = <<check-calltip-cancel>>
Right = <<check-calltip-cancel>>
. = KeyDot
# Debugger - These are the MSVC default keys, for want of a better choice.
F9 = DbgBreakpointToggle
F5 = DbgGo
Shift+F5 = DbgClose
F11 = DbgStep
F10 = DbgStepOver
Shift+F11 = DbgStepOut
Ctrl+F3 = AutoFindNext
[Keys:Editor]
# Key bindings specific to the editor
F2 = GotoNextBookmark
Ctrl+F2 = ToggleBookmark
Ctrl+G = GotoLine
Alt+I = ShowInteractiveWindow
Alt-B = AddBanner # A sample Event defined in this file.
# Block operations
Alt+3 = <<comment-region>>
Shift+Alt+3 = <<uncomment-region>>
Alt+4 = <<uncomment-region>> # IDLE default.
Alt+5 = <<tabify-region>>
Alt+6 = <<untabify-region>>
# Tabs and other indent features
Back = <<smart-backspace>>
Ctrl+T = <<toggle-tabs>>
Alt+U = <<change-indentwidth>>
Enter = EnterKey
Tab = TabKey
Shift-Tab = <<dedent-region>>
# Folding
Add = FoldExpand
Alt+Add = FoldExpandAll
Shift+Add = FoldExpandSecondLevel
Subtract = FoldCollapse
Alt+Subtract = FoldCollapseAll
Shift+Subtract = FoldCollapseSecondLevel
Multiply = FoldTopLevel
[Keys:Interactive]
# Key bindings specific to the interactive window.
# History for the interactive window
Ctrl+Up = <<history-previous>>
Ctrl+Down = <<history-next>>
Enter = ProcessEnter
Ctrl+Enter = ProcessEnter
Shift+Enter = ProcessEnter
Esc = ProcessEsc
Alt+I = WindowBack # Toggle back to previous window.
Home = InteractiveHome # A sample Event defined in this file.
Shift+Home = InteractiveHomeExtend # A sample Event defined in this file.
# When docked, the Ctrl+Tab and Shift+Ctrl+Tab keys dont work as expected.
Ctrl+Tab = MDINext
Ctrl+Shift+Tab = MDIPrev
[Extensions]
# Python event handlers specific to this config file.
# All functions not starting with an "_" are assumed
# to be events, and take 2 params:
# * editor_window is the same object passed to IDLE
# extensions. editor_window.text is a text widget
# that conforms to the Tk text widget interface.
# * event is the event being fired. Will always be None
# in the current implementation.
# Simply by defining these functions, they are available as
# events.
# Note that we bind keystrokes to these events in the various
# [Keys] sections.
# Add a simple file/class/function simple banner
def AddBanner(editor_window, event):
text = editor_window.text
big_line = "#" * 70
banner = "%s\n## \n## \n## \n%s\n" % (big_line, big_line)
# Insert at the start of the current line.
pos = text.index("insert linestart")
text.undo_block_start() # Allow action to be undone as a single unit.
text.insert(pos, banner)
text.undo_block_stop()
# Now set the insert point to the middle of the banner.
line, col = [int(s) for s in pos.split(".")]
text.mark_set("insert", "%d.1 lineend" % (line+2, ) )
# Here is a sample event bound to the "Home" key in the
# interactive window
def InteractiveHome(editor_window, event):
return _DoInteractiveHome(editor_window.text, 0)
def InteractiveHomeExtend(editor_window, event):
return _DoInteractiveHome(editor_window.text, 1)
def _DoInteractiveHome(text, extend):
import sys
# If Scintilla has an autocomplete window open, then let Scintilla handle it.
if text.edit.SCIAutoCActive():
return 1
of_interest = "insert linestart + %d c" % len(sys.ps1)
if not text.compare("insert", "==", of_interest) and \
text.get("insert linestart", of_interest) in [sys.ps1, sys.ps2]: # Not sys.ps? line
end = of_interest
else:
end = "insert linestart"
if extend: start = "insert"
else: start = end
text.tag_add("sel", start, end)
# From Niki Spahie
def AutoFindNext(editor_window, event):
"find selected text or word under cursor"
from pywin.scintilla import find
from pywin.scintilla import scintillacon
try:
sci = editor_window.edit
word = sci.GetSelText()
if word:
find.lastSearch.findText = word
find.lastSearch.sel = sci.GetSel()
else:
pos = sci.SendScintilla( scintillacon.SCI_GETCURRENTPOS )
start = sci.SendScintilla( scintillacon.SCI_WORDSTARTPOSITION, pos, 1 )
end = sci.SendScintilla( scintillacon.SCI_WORDENDPOSITION, pos, 1 )
word = sci.GetTextRange( start, end )
if word:
find.lastSearch.findText = word
find.lastSearch.sel = (start,end)
except Exception:
import traceback
traceback.print_exc()
find.FindNext()
# A couple of generic events.
def Beep(editor_window, event):
editor_window.text.beep()
def DoNothing(editor_window, event):
pass
def ContinueEvent(editor_window, event):
# Almost an "unbind" - allows Pythonwin/MFC to handle the keystroke
return 1

View File

@ -0,0 +1,140 @@
# The property page to define generic IDE options for Pythonwin
from pywin.mfc import dialog
from pywin.framework import interact
import win32ui
import win32con
buttonControlMap = {
win32ui.IDC_BUTTON1: win32ui.IDC_EDIT1,
win32ui.IDC_BUTTON2: win32ui.IDC_EDIT2,
win32ui.IDC_BUTTON3: win32ui.IDC_EDIT3,
}
class OptionsPropPage(dialog.PropertyPage):
def __init__(self):
dialog.PropertyPage.__init__(self, win32ui.IDD_PP_IDE)
self.AddDDX(win32ui.IDC_CHECK1, "bShowAtStartup")
self.AddDDX(win32ui.IDC_CHECK2, "bDocking")
self.AddDDX(win32ui.IDC_EDIT4, "MRUSize", "i")
def OnInitDialog(self):
edit = self.GetDlgItem(win32ui.IDC_EDIT1)
format = eval(
win32ui.GetProfileVal(
interact.sectionProfile,
interact.STYLE_INTERACTIVE_PROMPT,
str(interact.formatInput),
)
)
edit.SetDefaultCharFormat(format)
edit.SetWindowText("Input Text")
edit = self.GetDlgItem(win32ui.IDC_EDIT2)
format = eval(
win32ui.GetProfileVal(
interact.sectionProfile,
interact.STYLE_INTERACTIVE_OUTPUT,
str(interact.formatOutput),
)
)
edit.SetDefaultCharFormat(format)
edit.SetWindowText("Output Text")
edit = self.GetDlgItem(win32ui.IDC_EDIT3)
format = eval(
win32ui.GetProfileVal(
interact.sectionProfile,
interact.STYLE_INTERACTIVE_ERROR,
str(interact.formatOutputError),
)
)
edit.SetDefaultCharFormat(format)
edit.SetWindowText("Error Text")
self["bShowAtStartup"] = interact.LoadPreference("Show at startup", 1)
self["bDocking"] = interact.LoadPreference("Docking", 0)
self["MRUSize"] = win32ui.GetProfileVal("Settings", "Recent File List Size", 10)
# Hook the button clicks.
self.HookCommand(self.HandleCharFormatChange, win32ui.IDC_BUTTON1)
self.HookCommand(self.HandleCharFormatChange, win32ui.IDC_BUTTON2)
self.HookCommand(self.HandleCharFormatChange, win32ui.IDC_BUTTON3)
# Ensure the spin control remains in range.
spinner = self.GetDlgItem(win32ui.IDC_SPIN1)
spinner.SetRange(1, 16)
return dialog.PropertyPage.OnInitDialog(self)
# Called to save away the new format tuple for the specified item.
def HandleCharFormatChange(self, id, code):
if code == win32con.BN_CLICKED:
editId = buttonControlMap.get(id)
assert editId is not None, "Format button has no associated edit control"
editControl = self.GetDlgItem(editId)
existingFormat = editControl.GetDefaultCharFormat()
flags = win32con.CF_SCREENFONTS
d = win32ui.CreateFontDialog(existingFormat, flags, None, self)
if d.DoModal() == win32con.IDOK:
cf = d.GetCharFormat()
editControl.SetDefaultCharFormat(cf)
self.SetModified(1)
return 0 # We handled this fully!
def OnOK(self):
# Handle the edit controls - get all the fonts, put them back into interact, then
# get interact to save its stuff!
controlAttrs = [
(win32ui.IDC_EDIT1, interact.STYLE_INTERACTIVE_PROMPT),
(win32ui.IDC_EDIT2, interact.STYLE_INTERACTIVE_OUTPUT),
(win32ui.IDC_EDIT3, interact.STYLE_INTERACTIVE_ERROR),
]
for id, key in controlAttrs:
control = self.GetDlgItem(id)
fmt = control.GetDefaultCharFormat()
win32ui.WriteProfileVal(interact.sectionProfile, key, str(fmt))
# Save the other interactive window options.
interact.SavePreference("Show at startup", self["bShowAtStartup"])
interact.SavePreference("Docking", self["bDocking"])
# And the other options.
win32ui.WriteProfileVal("Settings", "Recent File List Size", self["MRUSize"])
return 1
def ChangeFormat(self, fmtAttribute, fmt):
dlg = win32ui.CreateFontDialog(fmt)
if dlg.DoModal() != win32con.IDOK:
return None
return dlg.GetCharFormat()
def OnFormatTitle(self, command, code):
fmt = self.GetFormat(interact.formatTitle)
if fmt:
formatTitle = fmt
SaveFontPreferences()
def OnFormatInput(self, command, code):
global formatInput
fmt = self.GetFormat(formatInput)
if fmt:
formatInput = fmt
SaveFontPreferences()
def OnFormatOutput(self, command, code):
global formatOutput
fmt = self.GetFormat(formatOutput)
if fmt:
formatOutput = fmt
SaveFontPreferences()
def OnFormatError(self, command, code):
global formatOutputError
fmt = self.GetFormat(formatOutputError)
if fmt:
formatOutputError = fmt
SaveFontPreferences()

View File

@ -0,0 +1,140 @@
from pywin.mfc import dialog
import win32ui, win32con, commctrl, win32api
class ListDialog(dialog.Dialog):
def __init__(self, title, list):
dialog.Dialog.__init__(self, self._maketemplate(title))
self.HookMessage(self.on_size, win32con.WM_SIZE)
self.HookNotify(self.OnListItemChange, commctrl.LVN_ITEMCHANGED)
self.HookCommand(self.OnListClick, win32ui.IDC_LIST1)
self.items = list
def _maketemplate(self, title):
style = win32con.WS_DLGFRAME | win32con.WS_SYSMENU | win32con.WS_VISIBLE
ls = (
win32con.WS_CHILD
| win32con.WS_VISIBLE
| commctrl.LVS_ALIGNLEFT
| commctrl.LVS_REPORT
)
bs = win32con.WS_CHILD | win32con.WS_VISIBLE
return [
[title, (0, 0, 200, 200), style, None, (8, "MS Sans Serif")],
["SysListView32", None, win32ui.IDC_LIST1, (0, 0, 200, 200), ls],
[128, "OK", win32con.IDOK, (10, 0, 50, 14), bs | win32con.BS_DEFPUSHBUTTON],
[128, "Cancel", win32con.IDCANCEL, (0, 0, 50, 14), bs],
]
def FillList(self):
size = self.GetWindowRect()
width = size[2] - size[0] - (10)
itemDetails = (commctrl.LVCFMT_LEFT, width, "Item", 0)
self.itemsControl.InsertColumn(0, itemDetails)
index = 0
for item in self.items:
index = self.itemsControl.InsertItem(index + 1, str(item), 0)
def OnListClick(self, id, code):
if code == commctrl.NM_DBLCLK:
self.EndDialog(win32con.IDOK)
return 1
def OnListItemChange(self, std, extra):
(hwndFrom, idFrom, code), (
itemNotify,
sub,
newState,
oldState,
change,
point,
lparam,
) = (std, extra)
oldSel = (oldState & commctrl.LVIS_SELECTED) != 0
newSel = (newState & commctrl.LVIS_SELECTED) != 0
if oldSel != newSel:
try:
self.selecteditem = itemNotify
self.butOK.EnableWindow(1)
except win32ui.error:
self.selecteditem = None
def OnInitDialog(self):
rc = dialog.Dialog.OnInitDialog(self)
self.itemsControl = self.GetDlgItem(win32ui.IDC_LIST1)
self.butOK = self.GetDlgItem(win32con.IDOK)
self.butCancel = self.GetDlgItem(win32con.IDCANCEL)
self.FillList()
size = self.GetWindowRect()
self.LayoutControls(size[2] - size[0], size[3] - size[1])
self.butOK.EnableWindow(0) # wait for first selection
return rc
def LayoutControls(self, w, h):
self.itemsControl.MoveWindow((0, 0, w, h - 30))
self.butCancel.MoveWindow((10, h - 24, 60, h - 4))
self.butOK.MoveWindow((w - 60, h - 24, w - 10, h - 4))
def on_size(self, params):
lparam = params[3]
w = win32api.LOWORD(lparam)
h = win32api.HIWORD(lparam)
self.LayoutControls(w, h)
class ListsDialog(ListDialog):
def __init__(self, title, list, colHeadings=["Item"]):
ListDialog.__init__(self, title, list)
self.colHeadings = colHeadings
def FillList(self):
index = 0
size = self.GetWindowRect()
width = (
size[2] - size[0] - (10) - win32api.GetSystemMetrics(win32con.SM_CXVSCROLL)
)
numCols = len(self.colHeadings)
for col in self.colHeadings:
itemDetails = (commctrl.LVCFMT_LEFT, int(width / numCols), col, 0)
self.itemsControl.InsertColumn(index, itemDetails)
index = index + 1
index = 0
for items in self.items:
index = self.itemsControl.InsertItem(index + 1, str(items[0]), 0)
for itemno in range(1, numCols):
item = items[itemno]
self.itemsControl.SetItemText(index, itemno, str(item))
def SelectFromList(title, lst):
dlg = ListDialog(title, lst)
if dlg.DoModal() == win32con.IDOK:
return dlg.selecteditem
else:
return None
def SelectFromLists(title, lists, headings):
dlg = ListsDialog(title, lists, headings)
if dlg.DoModal() == win32con.IDOK:
return dlg.selecteditem
else:
return None
def test():
# print SelectFromList('Single list', [1,2,3])
print(
SelectFromLists(
"Multi-List",
[("1", 1, "a"), ("2", 2, "b"), ("3", 3, "c")],
["Col 1", "Col 2"],
)
)
if __name__ == "__main__":
test()

View File

@ -0,0 +1,156 @@
"""login -- PythonWin user ID and password dialog box
(Adapted from originally distributed with Mark Hammond's PythonWin -
this now replaces it!)
login.GetLogin() displays a modal "OK/Cancel" dialog box with input
fields for a user ID and password. The password field input is masked
with *'s. GetLogin takes two optional parameters, a window title, and a
default user ID. If these parameters are omitted, the title defaults to
"Login", and the user ID is left blank. GetLogin returns a (userid, password)
tuple. GetLogin can be called from scripts running on the console - i.e. you
don't need to write a full-blown GUI app to use it.
login.GetPassword() is similar, except there is no username field.
Example:
import pywin.dialogs.login
title = "FTP Login"
def_user = "fred"
userid, password = pywin.dialogs.login.GetLogin(title, def_user)
Jim Eggleston, 28 August 1996
Merged with dlgpass and moved to pywin.dialogs by Mark Hammond Jan 1998.
"""
import win32ui
import win32api
import win32con
from pywin.mfc import dialog
def MakeLoginDlgTemplate(title):
style = (
win32con.DS_MODALFRAME
| win32con.WS_POPUP
| win32con.WS_VISIBLE
| win32con.WS_CAPTION
| win32con.WS_SYSMENU
| win32con.DS_SETFONT
)
cs = win32con.WS_CHILD | win32con.WS_VISIBLE
# Window frame and title
dlg = [
[title, (0, 0, 184, 40), style, None, (8, "MS Sans Serif")],
]
# ID label and text box
dlg.append([130, "User ID:", -1, (7, 9, 69, 9), cs | win32con.SS_LEFT])
s = cs | win32con.WS_TABSTOP | win32con.WS_BORDER
dlg.append(["EDIT", None, win32ui.IDC_EDIT1, (50, 7, 60, 12), s])
# Password label and text box
dlg.append([130, "Password:", -1, (7, 22, 69, 9), cs | win32con.SS_LEFT])
s = cs | win32con.WS_TABSTOP | win32con.WS_BORDER
dlg.append(
["EDIT", None, win32ui.IDC_EDIT2, (50, 20, 60, 12), s | win32con.ES_PASSWORD]
)
# OK/Cancel Buttons
s = cs | win32con.WS_TABSTOP
dlg.append(
[128, "OK", win32con.IDOK, (124, 5, 50, 14), s | win32con.BS_DEFPUSHBUTTON]
)
s = win32con.BS_PUSHBUTTON | s
dlg.append([128, "Cancel", win32con.IDCANCEL, (124, 20, 50, 14), s])
return dlg
def MakePasswordDlgTemplate(title):
style = (
win32con.DS_MODALFRAME
| win32con.WS_POPUP
| win32con.WS_VISIBLE
| win32con.WS_CAPTION
| win32con.WS_SYSMENU
| win32con.DS_SETFONT
)
cs = win32con.WS_CHILD | win32con.WS_VISIBLE
# Window frame and title
dlg = [
[title, (0, 0, 177, 45), style, None, (8, "MS Sans Serif")],
]
# Password label and text box
dlg.append([130, "Password:", -1, (7, 7, 69, 9), cs | win32con.SS_LEFT])
s = cs | win32con.WS_TABSTOP | win32con.WS_BORDER
dlg.append(
["EDIT", None, win32ui.IDC_EDIT1, (50, 7, 60, 12), s | win32con.ES_PASSWORD]
)
# OK/Cancel Buttons
s = cs | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON
dlg.append(
[128, "OK", win32con.IDOK, (124, 5, 50, 14), s | win32con.BS_DEFPUSHBUTTON]
)
dlg.append([128, "Cancel", win32con.IDCANCEL, (124, 22, 50, 14), s])
return dlg
class LoginDlg(dialog.Dialog):
Cancel = 0
def __init__(self, title):
dialog.Dialog.__init__(self, MakeLoginDlgTemplate(title))
self.AddDDX(win32ui.IDC_EDIT1, "userid")
self.AddDDX(win32ui.IDC_EDIT2, "password")
def GetLogin(title="Login", userid="", password=""):
d = LoginDlg(title)
d["userid"] = userid
d["password"] = password
if d.DoModal() != win32con.IDOK:
return (None, None)
else:
return (d["userid"], d["password"])
class PasswordDlg(dialog.Dialog):
def __init__(self, title):
dialog.Dialog.__init__(self, MakePasswordDlgTemplate(title))
self.AddDDX(win32ui.IDC_EDIT1, "password")
def GetPassword(title="Password", password=""):
d = PasswordDlg(title)
d["password"] = password
if d.DoModal() != win32con.IDOK:
return None
return d["password"]
if __name__ == "__main__":
import sys
title = "Login"
def_user = ""
if len(sys.argv) > 1:
title = sys.argv[1]
if len(sys.argv) > 2:
def_userid = sys.argv[2]
userid, password = GetLogin(title, def_user)
if userid == password == None:
print("User pressed Cancel")
else:
print("User ID: ", userid)
print("Password:", password)
newpassword = GetPassword("Reenter just for fun", password)
if newpassword is None:
print("User cancelled")
else:
what = ""
if newpassword != password:
what = "not "
print("The passwords did %smatch" % (what))

View File

@ -0,0 +1,241 @@
# No cancel button.
from pywin.mfc import dialog
from pywin.mfc.thread import WinThread
import threading
import win32ui
import win32con
import win32api
import time
def MakeProgressDlgTemplate(caption, staticText=""):
style = (
win32con.DS_MODALFRAME
| win32con.WS_POPUP
| win32con.WS_VISIBLE
| win32con.WS_CAPTION
| win32con.WS_SYSMENU
| win32con.DS_SETFONT
)
cs = win32con.WS_CHILD | win32con.WS_VISIBLE
w = 215
h = 36 # With button
h = 40
dlg = [
[caption, (0, 0, w, h), style, None, (8, "MS Sans Serif")],
]
s = win32con.WS_TABSTOP | cs
dlg.append([130, staticText, 1000, (7, 7, w - 7, h - 32), cs | win32con.SS_LEFT])
# dlg.append([128,
# "Cancel",
# win32con.IDCANCEL,
# (w - 60, h - 18, 50, 14), s | win32con.BS_PUSHBUTTON])
return dlg
class CStatusProgressDialog(dialog.Dialog):
def __init__(self, title, msg="", maxticks=100, tickincr=1):
self.initMsg = msg
templ = MakeProgressDlgTemplate(title, msg)
dialog.Dialog.__init__(self, templ)
self.maxticks = maxticks
self.tickincr = tickincr
self.pbar = None
def OnInitDialog(self):
rc = dialog.Dialog.OnInitDialog(self)
self.static = self.GetDlgItem(1000)
self.pbar = win32ui.CreateProgressCtrl()
self.pbar.CreateWindow(
win32con.WS_CHILD | win32con.WS_VISIBLE, (10, 30, 310, 44), self, 1001
)
self.pbar.SetRange(0, self.maxticks)
self.pbar.SetStep(self.tickincr)
self.progress = 0
self.pincr = 5
return rc
def Close(self):
self.EndDialog(0)
def SetMaxTicks(self, maxticks):
if self.pbar is not None:
self.pbar.SetRange(0, maxticks)
def Tick(self):
if self.pbar is not None:
self.pbar.StepIt()
def SetTitle(self, text):
self.SetWindowText(text)
def SetText(self, text):
self.SetDlgItemText(1000, text)
def Set(self, pos, max=None):
if self.pbar is not None:
self.pbar.SetPos(pos)
if max is not None:
self.pbar.SetRange(0, max)
# a progress dialog created in a new thread - especially suitable for
# console apps with no message loop.
MYWM_SETTITLE = win32con.WM_USER + 10
MYWM_SETMSG = win32con.WM_USER + 11
MYWM_TICK = win32con.WM_USER + 12
MYWM_SETMAXTICKS = win32con.WM_USER + 13
MYWM_SET = win32con.WM_USER + 14
class CThreadedStatusProcessDialog(CStatusProgressDialog):
def __init__(self, title, msg="", maxticks=100, tickincr=1):
self.title = title
self.msg = msg
self.threadid = win32api.GetCurrentThreadId()
CStatusProgressDialog.__init__(self, title, msg, maxticks, tickincr)
def OnInitDialog(self):
rc = CStatusProgressDialog.OnInitDialog(self)
self.HookMessage(self.OnTitle, MYWM_SETTITLE)
self.HookMessage(self.OnMsg, MYWM_SETMSG)
self.HookMessage(self.OnTick, MYWM_TICK)
self.HookMessage(self.OnMaxTicks, MYWM_SETMAXTICKS)
self.HookMessage(self.OnSet, MYWM_SET)
return rc
def _Send(self, msg):
try:
self.PostMessage(msg)
except win32ui.error:
# the user closed the window - but this does not cancel the
# process - so just ignore it.
pass
def OnTitle(self, msg):
CStatusProgressDialog.SetTitle(self, self.title)
def OnMsg(self, msg):
CStatusProgressDialog.SetText(self, self.msg)
def OnTick(self, msg):
CStatusProgressDialog.Tick(self)
def OnMaxTicks(self, msg):
CStatusProgressDialog.SetMaxTicks(self, self.maxticks)
def OnSet(self, msg):
CStatusProgressDialog.Set(self, self.pos, self.max)
def Close(self):
assert self.threadid, "No thread!"
win32api.PostThreadMessage(self.threadid, win32con.WM_QUIT, 0, 0)
def SetMaxTicks(self, maxticks):
self.maxticks = maxticks
self._Send(MYWM_SETMAXTICKS)
def SetTitle(self, title):
self.title = title
self._Send(MYWM_SETTITLE)
def SetText(self, text):
self.msg = text
self._Send(MYWM_SETMSG)
def Tick(self):
self._Send(MYWM_TICK)
def Set(self, pos, max=None):
self.pos = pos
self.max = max
self._Send(MYWM_SET)
class ProgressThread(WinThread):
def __init__(self, title, msg="", maxticks=100, tickincr=1):
self.title = title
self.msg = msg
self.maxticks = maxticks
self.tickincr = tickincr
self.dialog = None
WinThread.__init__(self)
self.createdEvent = threading.Event()
def InitInstance(self):
self.dialog = CThreadedStatusProcessDialog(
self.title, self.msg, self.maxticks, self.tickincr
)
self.dialog.CreateWindow()
try:
self.dialog.SetForegroundWindow()
except win32ui.error:
pass
self.createdEvent.set()
return WinThread.InitInstance(self)
def ExitInstance(self):
return 0
def StatusProgressDialog(title, msg="", maxticks=100, parent=None):
d = CStatusProgressDialog(title, msg, maxticks)
d.CreateWindow(parent)
return d
def ThreadedStatusProgressDialog(title, msg="", maxticks=100):
t = ProgressThread(title, msg, maxticks)
t.CreateThread()
# Need to run a basic "PumpWaitingMessages" loop just incase we are
# running inside Pythonwin.
# Basic timeout incase things go terribly wrong. Ideally we should use
# win32event.MsgWaitForMultipleObjects(), but we use a threading module
# event - so use a dumb strategy
end_time = time.time() + 10
while time.time() < end_time:
if t.createdEvent.isSet():
break
win32ui.PumpWaitingMessages()
time.sleep(0.1)
return t.dialog
def demo():
d = StatusProgressDialog("A Demo", "Doing something...")
import win32api
for i in range(100):
if i == 50:
d.SetText("Getting there...")
if i == 90:
d.SetText("Nearly done...")
win32api.Sleep(20)
d.Tick()
d.Close()
def thread_demo():
d = ThreadedStatusProgressDialog("A threaded demo", "Doing something")
import win32api
for i in range(100):
if i == 50:
d.SetText("Getting there...")
if i == 90:
d.SetText("Nearly done...")
win32api.Sleep(20)
d.Tick()
d.Close()
if __name__ == "__main__":
thread_demo()
# demo()

View File

@ -0,0 +1,676 @@
# DockingBar.py
# Ported directly (comments and all) from the samples at www.codeguru.com
# WARNING: Use at your own risk, as this interface is highly likely to change.
# Currently we support only one child per DockingBar. Later we need to add
# support for multiple children.
import win32api, win32con, win32ui
from pywin.mfc import afxres, window
import struct
clrBtnHilight = win32api.GetSysColor(win32con.COLOR_BTNHILIGHT)
clrBtnShadow = win32api.GetSysColor(win32con.COLOR_BTNSHADOW)
def CenterPoint(rect):
width = rect[2] - rect[0]
height = rect[3] - rect[1]
return rect[0] + width // 2, rect[1] + height // 2
def OffsetRect(rect, point):
(x, y) = point
return rect[0] + x, rect[1] + y, rect[2] + x, rect[3] + y
def DeflateRect(rect, point):
(x, y) = point
return rect[0] + x, rect[1] + y, rect[2] - x, rect[3] - y
def PtInRect(rect, pt):
return rect[0] <= pt[0] < rect[2] and rect[1] <= pt[1] < rect[3]
class DockingBar(window.Wnd):
def __init__(self, obj=None):
if obj is None:
obj = win32ui.CreateControlBar()
window.Wnd.__init__(self, obj)
self.dialog = None
self.nDockBarID = 0
self.sizeMin = 32, 32
self.sizeHorz = 200, 200
self.sizeVert = 200, 200
self.sizeFloat = 200, 200
self.bTracking = 0
self.bInRecalcNC = 0
self.cxEdge = 6
self.cxBorder = 3
self.cxGripper = 20
self.brushBkgd = win32ui.CreateBrush()
self.brushBkgd.CreateSolidBrush(win32api.GetSysColor(win32con.COLOR_BTNFACE))
# Support for diagonal resizing
self.cyBorder = 3
self.cCaptionSize = win32api.GetSystemMetrics(win32con.SM_CYSMCAPTION)
self.cMinWidth = win32api.GetSystemMetrics(win32con.SM_CXMIN)
self.cMinHeight = win32api.GetSystemMetrics(win32con.SM_CYMIN)
self.rectUndock = (0, 0, 0, 0)
def OnUpdateCmdUI(self, target, bDisableIfNoHndler):
return self.UpdateDialogControls(target, bDisableIfNoHndler)
def CreateWindow(
self,
parent,
childCreator,
title,
id,
style=win32con.WS_CHILD | win32con.WS_VISIBLE | afxres.CBRS_LEFT,
childCreatorArgs=(),
):
assert not (
(style & afxres.CBRS_SIZE_FIXED) and (style & afxres.CBRS_SIZE_DYNAMIC)
), "Invalid style"
self.rectClose = self.rectBorder = self.rectGripper = self.rectTracker = (
0,
0,
0,
0,
)
# save the style
self._obj_.dwStyle = style & afxres.CBRS_ALL
cursor = win32api.LoadCursor(0, win32con.IDC_ARROW)
wndClass = win32ui.RegisterWndClass(
win32con.CS_DBLCLKS, cursor, self.brushBkgd.GetSafeHandle(), 0
)
self._obj_.CreateWindow(wndClass, title, style, (0, 0, 0, 0), parent, id)
# Create the child dialog
self.dialog = childCreator(*(self,) + childCreatorArgs)
# use the dialog dimensions as default base dimensions
assert self.dialog.IsWindow(), (
"The childCreator function %s did not create a window!" % childCreator
)
rect = self.dialog.GetWindowRect()
self.sizeHorz = self.sizeVert = self.sizeFloat = (
rect[2] - rect[0],
rect[3] - rect[1],
)
self.sizeHorz = self.sizeHorz[0], self.sizeHorz[1] + self.cxEdge + self.cxBorder
self.sizeVert = self.sizeVert[0] + self.cxEdge + self.cxBorder, self.sizeVert[1]
self.HookMessages()
def CalcFixedLayout(self, bStretch, bHorz):
rectTop = self.dockSite.GetControlBar(
afxres.AFX_IDW_DOCKBAR_TOP
).GetWindowRect()
rectLeft = self.dockSite.GetControlBar(
afxres.AFX_IDW_DOCKBAR_LEFT
).GetWindowRect()
if bStretch:
nHorzDockBarWidth = 32767
nVertDockBarHeight = 32767
else:
nHorzDockBarWidth = rectTop[2] - rectTop[0] + 4
nVertDockBarHeight = rectLeft[3] - rectLeft[1] + 4
if self.IsFloating():
return self.sizeFloat
if bHorz:
return nHorzDockBarWidth, self.sizeHorz[1]
return self.sizeVert[0], nVertDockBarHeight
def CalcDynamicLayout(self, length, mode):
# Support for diagonal sizing.
if self.IsFloating():
self.GetParent().GetParent().ModifyStyle(win32ui.MFS_4THICKFRAME, 0)
if mode & (win32ui.LM_HORZDOCK | win32ui.LM_VERTDOCK):
flags = (
win32con.SWP_NOSIZE
| win32con.SWP_NOMOVE
| win32con.SWP_NOZORDER
| win32con.SWP_NOACTIVATE
| win32con.SWP_FRAMECHANGED
)
self.SetWindowPos(
0,
(
0,
0,
0,
0,
),
flags,
)
self.dockSite.RecalcLayout()
return self._obj_.CalcDynamicLayout(length, mode)
if mode & win32ui.LM_MRUWIDTH:
return self.sizeFloat
if mode & win32ui.LM_COMMIT:
self.sizeFloat = length, self.sizeFloat[1]
return self.sizeFloat
# More diagonal sizing.
if self.IsFloating():
dc = self.dockContext
pt = win32api.GetCursorPos()
windowRect = self.GetParent().GetParent().GetWindowRect()
hittest = dc.nHitTest
if hittest == win32con.HTTOPLEFT:
cx = max(windowRect[2] - pt[0], self.cMinWidth) - self.cxBorder
cy = max(windowRect[3] - self.cCaptionSize - pt[1], self.cMinHeight) - 1
self.sizeFloat = cx, cy
top = (
min(pt[1], windowRect[3] - self.cCaptionSize - self.cMinHeight)
- self.cyBorder
)
left = min(pt[0], windowRect[2] - self.cMinWidth) - 1
dc.rectFrameDragHorz = (
left,
top,
dc.rectFrameDragHorz[2],
dc.rectFrameDragHorz[3],
)
return self.sizeFloat
if hittest == win32con.HTTOPRIGHT:
cx = max(pt[0] - windowRect[0], self.cMinWidth)
cy = max(windowRect[3] - self.cCaptionSize - pt[1], self.cMinHeight) - 1
self.sizeFloat = cx, cy
top = (
min(pt[1], windowRect[3] - self.cCaptionSize - self.cMinHeight)
- self.cyBorder
)
dc.rectFrameDragHorz = (
dc.rectFrameDragHorz[0],
top,
dc.rectFrameDragHorz[2],
dc.rectFrameDragHorz[3],
)
return self.sizeFloat
if hittest == win32con.HTBOTTOMLEFT:
cx = max(windowRect[2] - pt[0], self.cMinWidth) - self.cxBorder
cy = max(pt[1] - windowRect[1] - self.cCaptionSize, self.cMinHeight)
self.sizeFloat = cx, cy
left = min(pt[0], windowRect[2] - self.cMinWidth) - 1
dc.rectFrameDragHorz = (
left,
dc.rectFrameDragHorz[1],
dc.rectFrameDragHorz[2],
dc.rectFrameDragHorz[3],
)
return self.sizeFloat
if hittest == win32con.HTBOTTOMRIGHT:
cx = max(pt[0] - windowRect[0], self.cMinWidth)
cy = max(pt[1] - windowRect[1] - self.cCaptionSize, self.cMinHeight)
self.sizeFloat = cx, cy
return self.sizeFloat
if mode & win32ui.LM_LENGTHY:
self.sizeFloat = self.sizeFloat[0], max(self.sizeMin[1], length)
return self.sizeFloat
else:
return max(self.sizeMin[0], length), self.sizeFloat[1]
def OnWindowPosChanged(self, msg):
if self.GetSafeHwnd() == 0 or self.dialog is None:
return 0
lparam = msg[3]
""" LPARAM used with WM_WINDOWPOSCHANGED:
typedef struct {
HWND hwnd;
HWND hwndInsertAfter;
int x;
int y;
int cx;
int cy;
UINT flags;} WINDOWPOS;
"""
format = "PPiiiii"
bytes = win32ui.GetBytes(lparam, struct.calcsize(format))
hwnd, hwndAfter, x, y, cx, cy, flags = struct.unpack(format, bytes)
if self.bInRecalcNC:
rc = self.GetClientRect()
self.dialog.MoveWindow(rc)
return 0
# Find on which side are we docked
nDockBarID = self.GetParent().GetDlgCtrlID()
# Return if dropped at same location
# no docking side change and no size change
if (
(nDockBarID == self.nDockBarID)
and (flags & win32con.SWP_NOSIZE)
and (
(self._obj_.dwStyle & afxres.CBRS_BORDER_ANY) != afxres.CBRS_BORDER_ANY
)
):
return
self.nDockBarID = nDockBarID
# Force recalc the non-client area
self.bInRecalcNC = 1
try:
swpflags = (
win32con.SWP_NOSIZE
| win32con.SWP_NOMOVE
| win32con.SWP_NOZORDER
| win32con.SWP_FRAMECHANGED
)
self.SetWindowPos(0, (0, 0, 0, 0), swpflags)
finally:
self.bInRecalcNC = 0
return 0
# This is a virtual and not a message hook.
def OnSetCursor(self, window, nHitTest, wMouseMsg):
if nHitTest != win32con.HTSIZE or self.bTracking:
return self._obj_.OnSetCursor(window, nHitTest, wMouseMsg)
if self.IsHorz():
win32api.SetCursor(win32api.LoadCursor(0, win32con.IDC_SIZENS))
else:
win32api.SetCursor(win32api.LoadCursor(0, win32con.IDC_SIZEWE))
return 1
# Mouse Handling
def OnLButtonUp(self, msg):
if not self.bTracking:
return 1 # pass it on.
self.StopTracking(1)
return 0 # Dont pass on
def OnLButtonDown(self, msg):
# UINT nFlags, CPoint point)
# only start dragging if clicked in "void" space
if self.dockBar is not None:
# start the drag
pt = msg[5]
pt = self.ClientToScreen(pt)
self.dockContext.StartDrag(pt)
return 0
return 1
def OnNcLButtonDown(self, msg):
if self.bTracking:
return 0
nHitTest = wparam = msg[2]
pt = msg[5]
if nHitTest == win32con.HTSYSMENU and not self.IsFloating():
self.GetDockingFrame().ShowControlBar(self, 0, 0)
elif nHitTest == win32con.HTMINBUTTON and not self.IsFloating():
self.dockContext.ToggleDocking()
elif (
nHitTest == win32con.HTCAPTION
and not self.IsFloating()
and self.dockBar is not None
):
self.dockContext.StartDrag(pt)
elif nHitTest == win32con.HTSIZE and not self.IsFloating():
self.StartTracking()
else:
return 1
return 0
def OnLButtonDblClk(self, msg):
# only toggle docking if clicked in "void" space
if self.dockBar is not None:
# toggle docking
self.dockContext.ToggleDocking()
return 0
return 1
def OnNcLButtonDblClk(self, msg):
nHitTest = wparam = msg[2]
# UINT nHitTest, CPoint point)
if self.dockBar is not None and nHitTest == win32con.HTCAPTION:
# toggle docking
self.dockContext.ToggleDocking()
return 0
return 1
def OnMouseMove(self, msg):
flags = wparam = msg[2]
lparam = msg[3]
if self.IsFloating() or not self.bTracking:
return 1
# Convert unsigned 16 bit to signed 32 bit.
x = win32api.LOWORD(lparam)
if x & 32768:
x = x | -65536
y = win32api.HIWORD(lparam)
if y & 32768:
y = y | -65536
pt = x, y
cpt = CenterPoint(self.rectTracker)
pt = self.ClientToWnd(pt)
if self.IsHorz():
if cpt[1] != pt[1]:
self.OnInvertTracker(self.rectTracker)
self.rectTracker = OffsetRect(self.rectTracker, (0, pt[1] - cpt[1]))
self.OnInvertTracker(self.rectTracker)
else:
if cpt[0] != pt[0]:
self.OnInvertTracker(self.rectTracker)
self.rectTracker = OffsetRect(self.rectTracker, (pt[0] - cpt[0], 0))
self.OnInvertTracker(self.rectTracker)
return 0 # Dont pass it on.
# def OnBarStyleChange(self, old, new):
def OnNcCalcSize(self, bCalcValid, size_info):
(rc0, rc1, rc2, pos) = size_info
self.rectBorder = self.GetWindowRect()
self.rectBorder = OffsetRect(
self.rectBorder, (-self.rectBorder[0], -self.rectBorder[1])
)
dwBorderStyle = self._obj_.dwStyle | afxres.CBRS_BORDER_ANY
if self.nDockBarID == afxres.AFX_IDW_DOCKBAR_TOP:
dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_BOTTOM
rc0.left = rc0.left + self.cxGripper
rc0.bottom = rc0.bottom - self.cxEdge
rc0.top = rc0.top + self.cxBorder
rc0.right = rc0.right - self.cxBorder
self.rectBorder = (
self.rectBorder[0],
self.rectBorder[3] - self.cxEdge,
self.rectBorder[2],
self.rectBorder[3],
)
elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM:
dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_TOP
rc0.left = rc0.left + self.cxGripper
rc0.top = rc0.top + self.cxEdge
rc0.bottom = rc0.bottom - self.cxBorder
rc0.right = rc0.right - self.cxBorder
self.rectBorder = (
self.rectBorder[0],
self.rectBorder[1],
self.rectBorder[2],
self.rectBorder[1] + self.cxEdge,
)
elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_LEFT:
dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_RIGHT
rc0.right = rc0.right - self.cxEdge
rc0.left = rc0.left + self.cxBorder
rc0.bottom = rc0.bottom - self.cxBorder
rc0.top = rc0.top + self.cxGripper
self.rectBorder = (
self.rectBorder[2] - self.cxEdge,
self.rectBorder[1],
self.rectBorder[2],
self.rectBorder[3],
)
elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_RIGHT:
dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_LEFT
rc0.left = rc0.left + self.cxEdge
rc0.right = rc0.right - self.cxBorder
rc0.bottom = rc0.bottom - self.cxBorder
rc0.top = rc0.top + self.cxGripper
self.rectBorder = (
self.rectBorder[0],
self.rectBorder[1],
self.rectBorder[0] + self.cxEdge,
self.rectBorder[3],
)
else:
self.rectBorder = 0, 0, 0, 0
self.SetBarStyle(dwBorderStyle)
return 0
def OnNcPaint(self, msg):
self.EraseNonClient()
dc = self.GetWindowDC()
ctl = win32api.GetSysColor(win32con.COLOR_BTNHIGHLIGHT)
cbr = win32api.GetSysColor(win32con.COLOR_BTNSHADOW)
dc.Draw3dRect(self.rectBorder, ctl, cbr)
self.DrawGripper(dc)
rect = self.GetClientRect()
self.InvalidateRect(rect, 1)
return 0
def OnNcHitTest(self, pt): # A virtual, not a hooked message.
if self.IsFloating():
return 1
ptOrig = pt
rect = self.GetWindowRect()
pt = pt[0] - rect[0], pt[1] - rect[1]
if PtInRect(self.rectClose, pt):
return win32con.HTSYSMENU
elif PtInRect(self.rectUndock, pt):
return win32con.HTMINBUTTON
elif PtInRect(self.rectGripper, pt):
return win32con.HTCAPTION
elif PtInRect(self.rectBorder, pt):
return win32con.HTSIZE
else:
return self._obj_.OnNcHitTest(ptOrig)
def StartTracking(self):
self.SetCapture()
# make sure no updates are pending
self.RedrawWindow(None, None, win32con.RDW_ALLCHILDREN | win32con.RDW_UPDATENOW)
self.dockSite.LockWindowUpdate()
self.ptOld = CenterPoint(self.rectBorder)
self.bTracking = 1
self.rectTracker = self.rectBorder
if not self.IsHorz():
l, t, r, b = self.rectTracker
b = b - 4
self.rectTracker = l, t, r, b
self.OnInvertTracker(self.rectTracker)
def OnCaptureChanged(self, msg):
hwnd = lparam = msg[3]
if self.bTracking and hwnd != self.GetSafeHwnd():
self.StopTracking(0) # cancel tracking
return 1
def StopTracking(self, bAccept):
self.OnInvertTracker(self.rectTracker)
self.dockSite.UnlockWindowUpdate()
self.bTracking = 0
self.ReleaseCapture()
if not bAccept:
return
rcc = self.dockSite.GetWindowRect()
if self.IsHorz():
newsize = self.sizeHorz[1]
maxsize = newsize + (rcc[3] - rcc[1])
minsize = self.sizeMin[1]
else:
newsize = self.sizeVert[0]
maxsize = newsize + (rcc[2] - rcc[0])
minsize = self.sizeMin[0]
pt = CenterPoint(self.rectTracker)
if self.nDockBarID == afxres.AFX_IDW_DOCKBAR_TOP:
newsize = newsize + (pt[1] - self.ptOld[1])
elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM:
newsize = newsize + (-pt[1] + self.ptOld[1])
elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_LEFT:
newsize = newsize + (pt[0] - self.ptOld[0])
elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_RIGHT:
newsize = newsize + (-pt[0] + self.ptOld[0])
newsize = max(minsize, min(maxsize, newsize))
if self.IsHorz():
self.sizeHorz = self.sizeHorz[0], newsize
else:
self.sizeVert = newsize, self.sizeVert[1]
self.dockSite.RecalcLayout()
return 0
def OnInvertTracker(self, rect):
assert rect[2] - rect[0] > 0 and rect[3] - rect[1] > 0, "rect is empty"
assert self.bTracking
rcc = self.GetWindowRect()
rcf = self.dockSite.GetWindowRect()
rect = OffsetRect(rect, (rcc[0] - rcf[0], rcc[1] - rcf[1]))
rect = DeflateRect(rect, (1, 1))
flags = win32con.DCX_WINDOW | win32con.DCX_CACHE | win32con.DCX_LOCKWINDOWUPDATE
dc = self.dockSite.GetDCEx(None, flags)
try:
brush = win32ui.GetHalftoneBrush()
oldBrush = dc.SelectObject(brush)
dc.PatBlt(
(rect[0], rect[1]),
(rect[2] - rect[0], rect[3] - rect[1]),
win32con.PATINVERT,
)
dc.SelectObject(oldBrush)
finally:
self.dockSite.ReleaseDC(dc)
def IsHorz(self):
return (
self.nDockBarID == afxres.AFX_IDW_DOCKBAR_TOP
or self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM
)
def ClientToWnd(self, pt):
x, y = pt
if self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM:
y = y + self.cxEdge
elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_RIGHT:
x = x + self.cxEdge
return x, y
def DrawGripper(self, dc):
# no gripper if floating
if self._obj_.dwStyle & afxres.CBRS_FLOATING:
return
# -==HACK==-
# in order to calculate the client area properly after docking,
# the client area must be recalculated twice (I have no idea why)
self.dockSite.RecalcLayout()
# -==END HACK==-
gripper = self.GetWindowRect()
gripper = self.ScreenToClient(gripper)
gripper = OffsetRect(gripper, (-gripper[0], -gripper[1]))
gl, gt, gr, gb = gripper
if self._obj_.dwStyle & afxres.CBRS_ORIENT_HORZ:
# gripper at left
self.rectGripper = gl, gt + 40, gl + 20, gb
# draw close box
self.rectClose = gl + 7, gt + 10, gl + 19, gt + 22
dc.DrawFrameControl(
self.rectClose, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONCLOSE
)
# draw docking toggle box
self.rectUndock = OffsetRect(self.rectClose, (0, 13))
dc.DrawFrameControl(
self.rectUndock, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONMAX
)
gt = gt + 38
gb = gb - 10
gl = gl + 10
gr = gl + 3
gripper = gl, gt, gr, gb
dc.Draw3dRect(gripper, clrBtnHilight, clrBtnShadow)
dc.Draw3dRect(OffsetRect(gripper, (4, 0)), clrBtnHilight, clrBtnShadow)
else:
# gripper at top
self.rectGripper = gl, gt, gr - 40, gt + 20
# draw close box
self.rectClose = gr - 21, gt + 7, gr - 10, gt + 18
dc.DrawFrameControl(
self.rectClose, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONCLOSE
)
# draw docking toggle box
self.rectUndock = OffsetRect(self.rectClose, (-13, 0))
dc.DrawFrameControl(
self.rectUndock, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONMAX
)
gr = gr - 38
gl = gl + 10
gt = gt + 10
gb = gt + 3
gripper = gl, gt, gr, gb
dc.Draw3dRect(gripper, clrBtnHilight, clrBtnShadow)
dc.Draw3dRect(OffsetRect(gripper, (0, 4)), clrBtnHilight, clrBtnShadow)
def HookMessages(self):
self.HookMessage(self.OnLButtonUp, win32con.WM_LBUTTONUP)
self.HookMessage(self.OnLButtonDown, win32con.WM_LBUTTONDOWN)
self.HookMessage(self.OnLButtonDblClk, win32con.WM_LBUTTONDBLCLK)
self.HookMessage(self.OnNcLButtonDown, win32con.WM_NCLBUTTONDOWN)
self.HookMessage(self.OnNcLButtonDblClk, win32con.WM_NCLBUTTONDBLCLK)
self.HookMessage(self.OnMouseMove, win32con.WM_MOUSEMOVE)
self.HookMessage(self.OnNcPaint, win32con.WM_NCPAINT)
self.HookMessage(self.OnCaptureChanged, win32con.WM_CAPTURECHANGED)
self.HookMessage(self.OnWindowPosChanged, win32con.WM_WINDOWPOSCHANGED)
# self.HookMessage(self.OnSize, win32con.WM_SIZE)
def EditCreator(parent):
d = win32ui.CreateEdit()
es = (
win32con.WS_CHILD
| win32con.WS_VISIBLE
| win32con.WS_BORDER
| win32con.ES_MULTILINE
| win32con.ES_WANTRETURN
)
d.CreateWindow(es, (0, 0, 150, 150), parent, 1000)
return d
def test():
import pywin.mfc.dialog
global bar
bar = DockingBar()
creator = EditCreator
bar.CreateWindow(win32ui.GetMainFrame(), creator, "Coolbar Demo", 0xFFFFF)
# win32ui.GetMainFrame().ShowControlBar(bar, 1, 0)
bar.SetBarStyle(
bar.GetBarStyle()
| afxres.CBRS_TOOLTIPS
| afxres.CBRS_FLYBY
| afxres.CBRS_SIZE_DYNAMIC
)
bar.EnableDocking(afxres.CBRS_ALIGN_ANY)
win32ui.GetMainFrame().DockControlBar(bar, afxres.AFX_IDW_DOCKBAR_BOTTOM)
if __name__ == "__main__":
test()

View File

@ -0,0 +1,454 @@
# App.py
# Application stuff.
# The application is responsible for managing the main frame window.
#
# We also grab the FileOpen command, to invoke our Python editor
" The PythonWin application code. Manages most aspects of MDI, etc "
import win32con
import win32api
import win32ui
import sys
import string
import os
from pywin.mfc import window, dialog, afxres
from pywin.mfc.thread import WinApp
import traceback
import regutil
from . import scriptutils
## NOTE: App and AppBuild should NOT be used - instead, you should contruct your
## APP class manually whenever you like (just ensure you leave these 2 params None!)
## Whoever wants the generic "Application" should get it via win32iu.GetApp()
# These are "legacy"
AppBuilder = None
App = None # default - if used, must end up a CApp derived class.
# Helpers that should one day be removed!
def AddIdleHandler(handler):
print(
"app.AddIdleHandler is deprecated - please use win32ui.GetApp().AddIdleHandler() instead."
)
return win32ui.GetApp().AddIdleHandler(handler)
def DeleteIdleHandler(handler):
print(
"app.DeleteIdleHandler is deprecated - please use win32ui.GetApp().DeleteIdleHandler() instead."
)
return win32ui.GetApp().DeleteIdleHandler(handler)
# Helper for writing a Window position by name, and later loading it.
def SaveWindowSize(section, rect, state=""):
"""Writes a rectangle to an INI file
Args: section = section name in the applications INI file
rect = a rectangle in a (cy, cx, y, x) tuple
(same format as CREATESTRUCT position tuples)."""
left, top, right, bottom = rect
if state:
state = state + " "
win32ui.WriteProfileVal(section, state + "left", left)
win32ui.WriteProfileVal(section, state + "top", top)
win32ui.WriteProfileVal(section, state + "right", right)
win32ui.WriteProfileVal(section, state + "bottom", bottom)
def LoadWindowSize(section, state=""):
"""Loads a section from an INI file, and returns a rect in a tuple (see SaveWindowSize)"""
if state:
state = state + " "
left = win32ui.GetProfileVal(section, state + "left", 0)
top = win32ui.GetProfileVal(section, state + "top", 0)
right = win32ui.GetProfileVal(section, state + "right", 0)
bottom = win32ui.GetProfileVal(section, state + "bottom", 0)
return (left, top, right, bottom)
def RectToCreateStructRect(rect):
return (rect[3] - rect[1], rect[2] - rect[0], rect[1], rect[0])
# Define FrameWindow and Application objects
#
# The Main Frame of the application.
class MainFrame(window.MDIFrameWnd):
sectionPos = "Main Window"
statusBarIndicators = (
afxres.ID_SEPARATOR, # // status line indicator
afxres.ID_INDICATOR_CAPS,
afxres.ID_INDICATOR_NUM,
afxres.ID_INDICATOR_SCRL,
win32ui.ID_INDICATOR_LINENUM,
win32ui.ID_INDICATOR_COLNUM,
)
def OnCreate(self, cs):
self._CreateStatusBar()
return 0
def _CreateStatusBar(self):
self.statusBar = win32ui.CreateStatusBar(self)
self.statusBar.SetIndicators(self.statusBarIndicators)
self.HookCommandUpdate(self.OnUpdatePosIndicator, win32ui.ID_INDICATOR_LINENUM)
self.HookCommandUpdate(self.OnUpdatePosIndicator, win32ui.ID_INDICATOR_COLNUM)
def OnUpdatePosIndicator(self, cmdui):
editControl = scriptutils.GetActiveEditControl()
value = " " * 5
if editControl is not None:
try:
startChar, endChar = editControl.GetSel()
lineNo = editControl.LineFromChar(startChar)
colNo = endChar - editControl.LineIndex(lineNo)
if cmdui.m_nID == win32ui.ID_INDICATOR_LINENUM:
value = "%0*d" % (5, lineNo + 1)
else:
value = "%0*d" % (3, colNo + 1)
except win32ui.error:
pass
cmdui.SetText(value)
cmdui.Enable()
def PreCreateWindow(self, cc):
cc = self._obj_.PreCreateWindow(cc)
pos = LoadWindowSize(self.sectionPos)
self.startRect = pos
if pos[2] - pos[0]:
rect = RectToCreateStructRect(pos)
cc = cc[0], cc[1], cc[2], cc[3], rect, cc[5], cc[6], cc[7], cc[8]
return cc
def OnDestroy(self, msg):
# use GetWindowPlacement(), as it works even when min'd or max'd
rectNow = self.GetWindowPlacement()[4]
if rectNow != self.startRect:
SaveWindowSize(self.sectionPos, rectNow)
return 0
class CApp(WinApp):
"A class for the application"
def __init__(self):
self.oldCallbackCaller = None
WinApp.__init__(self, win32ui.GetApp())
self.idleHandlers = []
def InitInstance(self):
"Called to crank up the app"
HookInput()
numMRU = win32ui.GetProfileVal("Settings", "Recent File List Size", 10)
win32ui.LoadStdProfileSettings(numMRU)
# self._obj_.InitMDIInstance()
if win32api.GetVersionEx()[0] < 4:
win32ui.SetDialogBkColor()
win32ui.Enable3dControls()
# install a "callback caller" - a manager for the callbacks
# self.oldCallbackCaller = win32ui.InstallCallbackCaller(self.CallbackManager)
self.LoadMainFrame()
self.SetApplicationPaths()
def ExitInstance(self):
"Called as the app dies - too late to prevent it here!"
win32ui.OutputDebug("Application shutdown\n")
# Restore the callback manager, if any.
try:
win32ui.InstallCallbackCaller(self.oldCallbackCaller)
except AttributeError:
pass
if self.oldCallbackCaller:
del self.oldCallbackCaller
self.frame = None # clean Python references to the now destroyed window object.
self.idleHandlers = []
# Attempt cleanup if not already done!
if self._obj_:
self._obj_.AttachObject(None)
self._obj_ = None
global App
global AppBuilder
App = None
AppBuilder = None
return 0
def HaveIdleHandler(self, handler):
return handler in self.idleHandlers
def AddIdleHandler(self, handler):
self.idleHandlers.append(handler)
def DeleteIdleHandler(self, handler):
self.idleHandlers.remove(handler)
def OnIdle(self, count):
try:
ret = 0
handlers = self.idleHandlers[:] # copy list, as may be modified during loop
for handler in handlers:
try:
thisRet = handler(handler, count)
except:
print("Idle handler %s failed" % (repr(handler)))
traceback.print_exc()
print("Idle handler removed from list")
try:
self.DeleteIdleHandler(handler)
except ValueError: # Item not in list.
pass
thisRet = 0
ret = ret or thisRet
return ret
except KeyboardInterrupt:
pass
def CreateMainFrame(self):
return MainFrame()
def LoadMainFrame(self):
"Create the main applications frame"
self.frame = self.CreateMainFrame()
self.SetMainFrame(self.frame)
self.frame.LoadFrame(win32ui.IDR_MAINFRAME, win32con.WS_OVERLAPPEDWINDOW)
self.frame.DragAcceptFiles() # we can accept these.
self.frame.ShowWindow(win32ui.GetInitialStateRequest())
self.frame.UpdateWindow()
self.HookCommands()
def OnHelp(self, id, code):
try:
if id == win32ui.ID_HELP_GUI_REF:
helpFile = regutil.GetRegisteredHelpFile("Pythonwin Reference")
helpCmd = win32con.HELP_CONTENTS
else:
helpFile = regutil.GetRegisteredHelpFile("Main Python Documentation")
helpCmd = win32con.HELP_FINDER
if helpFile is None:
win32ui.MessageBox("The help file is not registered!")
else:
from . import help
help.OpenHelpFile(helpFile, helpCmd)
except:
t, v, tb = sys.exc_info()
win32ui.MessageBox(
"Internal error in help file processing\r\n%s: %s" % (t, v)
)
tb = None # Prevent a cycle
def DoLoadModules(self, modules):
# XXX - this should go, but the debugger uses it :-(
# dont do much checking!
for module in modules:
__import__(module)
def HookCommands(self):
self.frame.HookMessage(self.OnDropFiles, win32con.WM_DROPFILES)
self.HookCommand(self.HandleOnFileOpen, win32ui.ID_FILE_OPEN)
self.HookCommand(self.HandleOnFileNew, win32ui.ID_FILE_NEW)
self.HookCommand(self.OnFileMRU, win32ui.ID_FILE_MRU_FILE1)
self.HookCommand(self.OnHelpAbout, win32ui.ID_APP_ABOUT)
self.HookCommand(self.OnHelp, win32ui.ID_HELP_PYTHON)
self.HookCommand(self.OnHelp, win32ui.ID_HELP_GUI_REF)
# Hook for the right-click menu.
self.frame.GetWindow(win32con.GW_CHILD).HookMessage(
self.OnRClick, win32con.WM_RBUTTONDOWN
)
def SetApplicationPaths(self):
# Load the users/application paths
new_path = []
apppath = win32ui.GetProfileVal("Python", "Application Path", "").split(";")
for path in apppath:
if len(path) > 0:
new_path.append(win32ui.FullPath(path))
for extra_num in range(1, 11):
apppath = win32ui.GetProfileVal(
"Python", "Application Path %d" % extra_num, ""
).split(";")
if len(apppath) == 0:
break
for path in apppath:
if len(path) > 0:
new_path.append(win32ui.FullPath(path))
sys.path = new_path + sys.path
def OnRClick(self, params):
"Handle right click message"
# put up the entire FILE menu!
menu = win32ui.LoadMenu(win32ui.IDR_TEXTTYPE).GetSubMenu(0)
menu.TrackPopupMenu(params[5]) # track at mouse position.
return 0
def OnDropFiles(self, msg):
"Handle a file being dropped from file manager"
hDropInfo = msg[2]
self.frame.SetActiveWindow() # active us
nFiles = win32api.DragQueryFile(hDropInfo)
try:
for iFile in range(0, nFiles):
fileName = win32api.DragQueryFile(hDropInfo, iFile)
win32ui.GetApp().OpenDocumentFile(fileName)
finally:
win32api.DragFinish(hDropInfo)
return 0
# No longer used by Pythonwin, as the C++ code has this same basic functionality
# but handles errors slightly better.
# It all still works, tho, so if you need similar functionality, you can use it.
# Therefore I havent deleted this code completely!
# def CallbackManager( self, ob, args = () ):
# """Manage win32 callbacks. Trap exceptions, report on them, then return 'All OK'
# to the frame-work. """
# import traceback
# try:
# ret = apply(ob, args)
# return ret
# except:
# # take copies of the exception values, else other (handled) exceptions may get
# # copied over by the other fns called.
# win32ui.SetStatusText('An exception occured in a windows command handler.')
# t, v, tb = sys.exc_info()
# traceback.print_exception(t, v, tb.tb_next)
# try:
# sys.stdout.flush()
# except (NameError, AttributeError):
# pass
# Command handlers.
def OnFileMRU(self, id, code):
"Called when a File 1-n message is recieved"
fileName = win32ui.GetRecentFileList()[id - win32ui.ID_FILE_MRU_FILE1]
win32ui.GetApp().OpenDocumentFile(fileName)
def HandleOnFileOpen(self, id, code):
"Called when FileOpen message is received"
win32ui.GetApp().OnFileOpen()
def HandleOnFileNew(self, id, code):
"Called when FileNew message is received"
win32ui.GetApp().OnFileNew()
def OnHelpAbout(self, id, code):
"Called when HelpAbout message is received. Displays the About dialog."
win32ui.InitRichEdit()
dlg = AboutBox()
dlg.DoModal()
def _GetRegistryValue(key, val, default=None):
# val is registry value - None for default val.
try:
hkey = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, key)
return win32api.RegQueryValueEx(hkey, val)[0]
except win32api.error:
try:
hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, key)
return win32api.RegQueryValueEx(hkey, val)[0]
except win32api.error:
return default
scintilla = "Scintilla is Copyright 1998-2008 Neil Hodgson (http://www.scintilla.org)"
idle = "This program uses IDLE extensions by Guido van Rossum, Tim Peters and others."
contributors = "Thanks to the following people for making significant contributions: Roger Upole, Sidnei da Silva, Sam Rushing, Curt Hagenlocher, Dave Brennan, Roger Burnham, Gordon McMillan, Neil Hodgson, Laramie Leavitt. (let me know if I have forgotten you!)"
# The About Box
class AboutBox(dialog.Dialog):
def __init__(self, idd=win32ui.IDD_ABOUTBOX):
dialog.Dialog.__init__(self, idd)
def OnInitDialog(self):
text = (
"Pythonwin - Python IDE and GUI Framework for Windows.\n\n%s\n\nPython is %s\n\n%s\n\n%s\n\n%s"
% (win32ui.copyright, sys.copyright, scintilla, idle, contributors)
)
self.SetDlgItemText(win32ui.IDC_EDIT1, text)
# Get the build number - written by installers.
# For distutils build, read pywin32.version.txt
import sysconfig
site_packages = sysconfig.get_paths()["platlib"]
try:
build_no = (
open(os.path.join(site_packages, "pywin32.version.txt")).read().strip()
)
ver = "pywin32 build %s" % build_no
except EnvironmentError:
ver = None
if ver is None:
# See if we are Part of Active Python
ver = _GetRegistryValue(
"SOFTWARE\\ActiveState\\ActivePython", "CurrentVersion"
)
if ver is not None:
ver = "ActivePython build %s" % (ver,)
if ver is None:
ver = ""
self.SetDlgItemText(win32ui.IDC_ABOUT_VERSION, ver)
self.HookCommand(self.OnButHomePage, win32ui.IDC_BUTTON1)
def OnButHomePage(self, id, code):
if code == win32con.BN_CLICKED:
win32api.ShellExecute(
0, "open", "https://github.com/mhammond/pywin32", None, "", 1
)
def Win32RawInput(prompt=None):
"Provide raw_input() for gui apps"
# flush stderr/out first.
try:
sys.stdout.flush()
sys.stderr.flush()
except:
pass
if prompt is None:
prompt = ""
ret = dialog.GetSimpleInput(prompt)
if ret == None:
raise KeyboardInterrupt("operation cancelled")
return ret
def Win32Input(prompt=None):
"Provide input() for gui apps"
return eval(input(prompt))
def HookInput():
try:
raw_input
# must be py2x...
sys.modules["__builtin__"].raw_input = Win32RawInput
sys.modules["__builtin__"].input = Win32Input
except NameError:
# must be py3k
import code
sys.modules["builtins"].input = Win32RawInput
def HaveGoodGUI():
"""Returns true if we currently have a good gui available."""
return "pywin.framework.startup" in sys.modules
def CreateDefaultGUI(appClass=None):
"""Creates a default GUI environment"""
if appClass is None:
from . import intpyapp # Bring in the default app - could be param'd later.
appClass = intpyapp.InteractivePythonApp
# Create and init the app.
appClass().InitInstance()
def CheckCreateDefaultGUI():
"""Checks and creates if necessary a default GUI environment."""
rc = HaveGoodGUI()
if not rc:
CreateDefaultGUI()
return rc

View File

@ -0,0 +1,165 @@
import win32ui
import win32con
import win32api
import string
import os
from . import app
import sys
from pywin.mfc import docview, window
bStretch = 1
class BitmapDocument(docview.Document):
"A bitmap document. Holds the bitmap data itself."
def __init__(self, template):
docview.Document.__init__(self, template)
self.bitmap = None
def OnNewDocument(self):
# I can not create new bitmaps.
win32ui.MessageBox("Bitmaps can not be created.")
def OnOpenDocument(self, filename):
self.bitmap = win32ui.CreateBitmap()
# init data members
f = open(filename, "rb")
try:
try:
self.bitmap.LoadBitmapFile(f)
except IOError:
win32ui.MessageBox("Could not load the bitmap from %s" % filename)
return 0
finally:
f.close()
self.size = self.bitmap.GetSize()
return 1
def DeleteContents(self):
self.bitmap = None
class BitmapView(docview.ScrollView):
"A view of a bitmap. Obtains data from document."
def __init__(self, doc):
docview.ScrollView.__init__(self, doc)
self.width = self.height = 0
# set up message handlers
self.HookMessage(self.OnSize, win32con.WM_SIZE)
def OnInitialUpdate(self):
doc = self.GetDocument()
if doc.bitmap:
bitmapSize = doc.bitmap.GetSize()
self.SetScrollSizes(win32con.MM_TEXT, bitmapSize)
def OnSize(self, params):
lParam = params[3]
self.width = win32api.LOWORD(lParam)
self.height = win32api.HIWORD(lParam)
def OnDraw(self, dc):
# set sizes used for "non stretch" mode.
doc = self.GetDocument()
if doc.bitmap is None:
return
bitmapSize = doc.bitmap.GetSize()
if bStretch:
# stretch BMP.
viewRect = (0, 0, self.width, self.height)
bitmapRect = (0, 0, bitmapSize[0], bitmapSize[1])
doc.bitmap.Paint(dc, viewRect, bitmapRect)
else:
# non stretch.
doc.bitmap.Paint(dc)
class BitmapFrame(window.MDIChildWnd):
def OnCreateClient(self, createparams, context):
borderX = win32api.GetSystemMetrics(win32con.SM_CXFRAME)
borderY = win32api.GetSystemMetrics(win32con.SM_CYFRAME)
titleY = win32api.GetSystemMetrics(win32con.SM_CYCAPTION) # includes border
# try and maintain default window pos, else adjust if cant fit
# get the main client window dimensions.
mdiClient = win32ui.GetMainFrame().GetWindow(win32con.GW_CHILD)
clientWindowRect = mdiClient.ScreenToClient(mdiClient.GetWindowRect())
clientWindowSize = (
clientWindowRect[2] - clientWindowRect[0],
clientWindowRect[3] - clientWindowRect[1],
)
left, top, right, bottom = mdiClient.ScreenToClient(self.GetWindowRect())
# width, height=context.doc.size[0], context.doc.size[1]
# width = width+borderX*2
# height= height+titleY+borderY*2-1
# if (left+width)>clientWindowSize[0]:
# left = clientWindowSize[0] - width
# if left<0:
# left = 0
# width = clientWindowSize[0]
# if (top+height)>clientWindowSize[1]:
# top = clientWindowSize[1] - height
# if top<0:
# top = 0
# height = clientWindowSize[1]
# self.frame.MoveWindow((left, top, left+width, top+height),0)
window.MDIChildWnd.OnCreateClient(self, createparams, context)
return 1
class BitmapTemplate(docview.DocTemplate):
def __init__(self):
docview.DocTemplate.__init__(
self, win32ui.IDR_PYTHONTYPE, BitmapDocument, BitmapFrame, BitmapView
)
def MatchDocType(self, fileName, fileType):
doc = self.FindOpenDocument(fileName)
if doc:
return doc
ext = os.path.splitext(fileName)[1].lower()
if ext == ".bmp": # removed due to PIL! or ext=='.ppm':
return win32ui.CDocTemplate_Confidence_yesAttemptNative
return win32ui.CDocTemplate_Confidence_maybeAttemptForeign
# return win32ui.CDocTemplate_Confidence_noAttempt
# For debugging purposes, when this module may be reloaded many times.
try:
win32ui.GetApp().RemoveDocTemplate(bitmapTemplate)
except NameError:
pass
bitmapTemplate = BitmapTemplate()
bitmapTemplate.SetDocStrings(
"\nBitmap\nBitmap\nBitmap (*.bmp)\n.bmp\nPythonBitmapFileType\nPython Bitmap File"
)
win32ui.GetApp().AddDocTemplate(bitmapTemplate)
# This works, but just didnt make it through the code reorg.
# class PPMBitmap(Bitmap):
# def LoadBitmapFile(self, file ):
# magic=file.readline()
# if magic <> "P6\n":
# raise TypeError, "The file is not a PPM format file"
# rowcollist=string.split(file.readline())
# cols=string.atoi(rowcollist[0])
# rows=string.atoi(rowcollist[1])
# file.readline() # whats this one?
# self.bitmap.LoadPPMFile(file,(cols,rows))
def t():
bitmapTemplate.OpenDocumentFile("d:\\winnt\\arcade.bmp")
# OpenBMPFile( 'd:\\winnt\\arcade.bmp')
def demo():
import glob
winDir = win32api.GetWindowsDirectory()
for fileName in glob.glob1(winDir, "*.bmp")[:2]:
bitmapTemplate.OpenDocumentFile(os.path.join(winDir, fileName))

View File

@ -0,0 +1,55 @@
# cmdline - command line utilities.
import sys
import win32ui
import string
def ParseArgs(str):
import string
ret = []
pos = 0
length = len(str)
while pos < length:
try:
while str[pos] in string.whitespace:
pos = pos + 1
except IndexError:
break
if pos >= length:
break
if str[pos] == '"':
pos = pos + 1
try:
endPos = str.index('"', pos) - 1
nextPos = endPos + 2
except ValueError:
endPos = length
nextPos = endPos + 1
else:
endPos = pos
while endPos < length and not str[endPos] in string.whitespace:
endPos = endPos + 1
nextPos = endPos + 1
ret.append(str[pos : endPos + 1].strip())
pos = nextPos
return ret
def FixArgFileName(fileName):
"""Convert a filename on the commandline to something useful.
Given an automatic filename on the commandline, turn it a python module name,
with the path added to sys.path."""
import os
path, fname = os.path.split(fileName)
if len(path) == 0:
path = os.curdir
path = os.path.abspath(path)
# must check that the command line arg's path is in sys.path
for syspath in sys.path:
if os.path.abspath(syspath) == path:
break
else:
sys.path.append(path)
return os.path.splitext(fname)[0]

View File

@ -0,0 +1,187 @@
# Command Handlers for the debugger.
# Not in the debugger package, as I always want these interfaces to be
# available, even if the debugger has not yet been (or can not be)
# imported
import win32ui, win32con
from . import scriptutils
import warnings
from pywin.scintilla.control import CScintillaEditInterface
IdToBarNames = {
win32ui.IDC_DBG_STACK: ("Stack", 0),
win32ui.IDC_DBG_BREAKPOINTS: ("Breakpoints", 0),
win32ui.IDC_DBG_WATCH: ("Watch", 1),
}
class DebuggerCommandHandler:
def HookCommands(self):
commands = (
(self.OnStep, None, win32ui.IDC_DBG_STEP),
(self.OnStepOut, self.OnUpdateOnlyBreak, win32ui.IDC_DBG_STEPOUT),
(self.OnStepOver, None, win32ui.IDC_DBG_STEPOVER),
(self.OnGo, None, win32ui.IDC_DBG_GO),
(self.OnClose, self.OnUpdateClose, win32ui.IDC_DBG_CLOSE),
(self.OnAdd, self.OnUpdateAddBreakpoints, win32ui.IDC_DBG_ADD),
(self.OnClearAll, self.OnUpdateClearAllBreakpoints, win32ui.IDC_DBG_CLEAR),
# (self.OnDebuggerToolbar, self.OnUpdateDebuggerToolbar, win32ui.ID_DEBUGGER_TOOLBAR),
)
frame = win32ui.GetMainFrame()
for methHandler, methUpdate, id in commands:
frame.HookCommand(methHandler, id)
if not methUpdate is None:
frame.HookCommandUpdate(methUpdate, id)
for id in list(IdToBarNames.keys()):
frame.HookCommand(self.OnDebuggerBar, id)
frame.HookCommandUpdate(self.OnUpdateDebuggerBar, id)
def OnDebuggerToolbar(self, id, code):
if code == 0:
return not win32ui.GetMainFrame().OnBarCheck(id)
def OnUpdateDebuggerToolbar(self, cmdui):
win32ui.GetMainFrame().OnUpdateControlBarMenu(cmdui)
cmdui.Enable(1)
def _GetDebugger(self):
try:
import pywin.debugger
return pywin.debugger.currentDebugger
except ImportError:
return None
def _DoOrStart(self, doMethod, startFlag):
d = self._GetDebugger()
if d is not None and d.IsDebugging():
method = getattr(d, doMethod)
method()
else:
scriptutils.RunScript(
defName=None, defArgs=None, bShowDialog=0, debuggingType=startFlag
)
def OnStep(self, msg, code):
self._DoOrStart("do_set_step", scriptutils.RS_DEBUGGER_STEP)
def OnStepOver(self, msg, code):
self._DoOrStart("do_set_next", scriptutils.RS_DEBUGGER_STEP)
def OnStepOut(self, msg, code):
d = self._GetDebugger()
if d is not None and d.IsDebugging():
d.do_set_return()
def OnGo(self, msg, code):
self._DoOrStart("do_set_continue", scriptutils.RS_DEBUGGER_GO)
def OnClose(self, msg, code):
d = self._GetDebugger()
if d is not None:
if d.IsDebugging():
d.set_quit()
else:
d.close()
def OnUpdateClose(self, cmdui):
d = self._GetDebugger()
if d is not None and d.inited:
cmdui.Enable(1)
else:
cmdui.Enable(0)
def OnAdd(self, msg, code):
doc, view = scriptutils.GetActiveEditorDocument()
if doc is None:
## Don't do a messagebox, as this could be triggered from the app's
## idle loop whenever the debug toolbar is visible, giving a never-ending
## series of dialogs. This can happen when the OnUpdate handler
## for the toolbar button IDC_DBG_ADD fails, since MFC falls back to
## sending a normal command if the UI update command fails.
## win32ui.MessageBox('There is no active window - no breakpoint can be added')
warnings.warn("There is no active window - no breakpoint can be added")
return None
pathName = doc.GetPathName()
lineNo = view.LineFromChar(view.GetSel()[0]) + 1
# If I have a debugger, then tell it, otherwise just add a marker
d = self._GetDebugger()
if d is None:
import pywin.framework.editor.color.coloreditor
doc.MarkerToggle(
lineNo, pywin.framework.editor.color.coloreditor.MARKER_BREAKPOINT
)
else:
if d.get_break(pathName, lineNo):
win32ui.SetStatusText("Clearing breakpoint", 1)
rc = d.clear_break(pathName, lineNo)
else:
win32ui.SetStatusText("Setting breakpoint", 1)
rc = d.set_break(pathName, lineNo)
if rc:
win32ui.MessageBox(rc)
d.GUIRespondDebuggerData()
def OnClearAll(self, msg, code):
win32ui.SetStatusText("Clearing all breakpoints")
d = self._GetDebugger()
if d is None:
import pywin.framework.editor
import pywin.framework.editor.color.coloreditor
for doc in pywin.framework.editor.editorTemplate.GetDocumentList():
doc.MarkerDeleteAll(
pywin.framework.editor.color.coloreditor.MARKER_BREAKPOINT
)
else:
d.clear_all_breaks()
d.UpdateAllLineStates()
d.GUIRespondDebuggerData()
def OnUpdateOnlyBreak(self, cmdui):
d = self._GetDebugger()
ok = d is not None and d.IsBreak()
cmdui.Enable(ok)
def OnUpdateAddBreakpoints(self, cmdui):
doc, view = scriptutils.GetActiveEditorDocument()
if doc is None or not isinstance(view, CScintillaEditInterface):
enabled = 0
else:
enabled = 1
lineNo = view.LineFromChar(view.GetSel()[0]) + 1
import pywin.framework.editor.color.coloreditor
cmdui.SetCheck(
doc.MarkerAtLine(
lineNo, pywin.framework.editor.color.coloreditor.MARKER_BREAKPOINT
)
!= 0
)
cmdui.Enable(enabled)
def OnUpdateClearAllBreakpoints(self, cmdui):
d = self._GetDebugger()
cmdui.Enable(d is None or len(d.breaks) != 0)
def OnUpdateDebuggerBar(self, cmdui):
name, always = IdToBarNames.get(cmdui.m_nID)
enabled = always
d = self._GetDebugger()
if d is not None and d.IsDebugging() and name is not None:
enabled = 1
bar = d.GetDebuggerBar(name)
cmdui.SetCheck(bar.IsWindowVisible())
cmdui.Enable(enabled)
def OnDebuggerBar(self, id, code):
name = IdToBarNames.get(id)[0]
d = self._GetDebugger()
if d is not None and name is not None:
bar = d.GetDebuggerBar(name)
newState = not bar.IsWindowVisible()
win32ui.GetMainFrame().ShowControlBar(bar, newState, 1)

View File

@ -0,0 +1,74 @@
# dlgappcore.
#
# base classes for dialog based apps.
from . import app
import win32ui
import win32con
import win32api
import sys
from pywin.mfc import dialog
error = "Dialog Application Error"
class AppDialog(dialog.Dialog):
"The dialog box for the application"
def __init__(self, id, dll=None):
self.iconId = win32ui.IDR_MAINFRAME
dialog.Dialog.__init__(self, id, dll)
def OnInitDialog(self):
return dialog.Dialog.OnInitDialog(self)
# Provide support for a dlg app using an icon
def OnPaint(self):
if not self.IsIconic():
return self._obj_.OnPaint()
self.DefWindowProc(win32con.WM_ICONERASEBKGND, dc.GetHandleOutput(), 0)
left, top, right, bottom = self.GetClientRect()
left = (right - win32api.GetSystemMetrics(win32con.SM_CXICON)) >> 1
top = (bottom - win32api.GetSystemMetrics(win32con.SM_CYICON)) >> 1
hIcon = win32ui.GetApp().LoadIcon(self.iconId)
self.GetDC().DrawIcon((left, top), hIcon)
# Only needed to provide a minimized icon (and this seems
# less important under win95/NT4
def OnEraseBkgnd(self, dc):
if self.IsIconic():
return 1
else:
return self._obj_.OnEraseBkgnd(dc)
def OnQueryDragIcon(self):
return win32ui.GetApp().LoadIcon(self.iconId)
def PreDoModal(self):
pass
class DialogApp(app.CApp):
"An application class, for an app with main dialog box"
def InitInstance(self):
# win32ui.SetProfileFileName('dlgapp.ini')
win32ui.LoadStdProfileSettings()
win32ui.EnableControlContainer()
win32ui.Enable3dControls()
self.dlg = self.frame = self.CreateDialog()
if self.frame is None:
raise error("No dialog was created by CreateDialog()")
return
self._obj_.InitDlgInstance(self.dlg)
self.PreDoModal()
self.dlg.PreDoModal()
self.dlg.DoModal()
def CreateDialog(self):
pass
def PreDoModal(self):
pass

View File

@ -0,0 +1,235 @@
# ModuleBrowser.py - A view that provides a module browser for an editor document.
import pywin.mfc.docview
import win32ui
import win32con
import commctrl
import win32api
from pywin.tools import hierlist, browser
import pywin.framework.scriptutils
import afxres
import pyclbr
class HierListCLBRModule(hierlist.HierListItem):
def __init__(self, modName, clbrdata):
self.modName = modName
self.clbrdata = clbrdata
def GetText(self):
return self.modName
def GetSubList(self):
ret = []
for item in self.clbrdata.values():
if (
item.__class__ != pyclbr.Class
): # ie, it is a pyclbr Function instance (only introduced post 1.5.2)
ret.append(HierListCLBRFunction(item))
else:
ret.append(HierListCLBRClass(item))
ret.sort()
return ret
def IsExpandable(self):
return 1
class HierListCLBRItem(hierlist.HierListItem):
def __init__(self, name, file, lineno, suffix=""):
self.name = str(name)
self.file = file
self.lineno = lineno
self.suffix = suffix
def __lt__(self, other):
return self.name < other.name
def __eq__(self, other):
return self.name == other.name
def GetText(self):
return self.name + self.suffix
def TakeDefaultAction(self):
if self.file:
pywin.framework.scriptutils.JumpToDocument(
self.file, self.lineno, bScrollToTop=1
)
else:
win32ui.SetStatusText("Can not locate the source code for this object.")
def PerformItemSelected(self):
if self.file is None:
msg = "%s - source can not be located." % (self.name,)
else:
msg = "%s defined at line %d of %s" % (self.name, self.lineno, self.file)
win32ui.SetStatusText(msg)
class HierListCLBRClass(HierListCLBRItem):
def __init__(self, clbrclass, suffix=""):
try:
name = clbrclass.name
file = clbrclass.file
lineno = clbrclass.lineno
self.super = clbrclass.super
self.methods = clbrclass.methods
except AttributeError:
name = clbrclass
file = lineno = None
self.super = []
self.methods = {}
HierListCLBRItem.__init__(self, name, file, lineno, suffix)
def GetSubList(self):
r1 = []
for c in self.super:
r1.append(HierListCLBRClass(c, " (Parent class)"))
r1.sort()
r2 = []
for meth, lineno in self.methods.items():
r2.append(HierListCLBRMethod(meth, self.file, lineno))
r2.sort()
return r1 + r2
def IsExpandable(self):
return len(self.methods) + len(self.super)
def GetBitmapColumn(self):
return 21
class HierListCLBRFunction(HierListCLBRItem):
def __init__(self, clbrfunc, suffix=""):
name = clbrfunc.name
file = clbrfunc.file
lineno = clbrfunc.lineno
HierListCLBRItem.__init__(self, name, file, lineno, suffix)
def GetBitmapColumn(self):
return 22
class HierListCLBRMethod(HierListCLBRItem):
def GetBitmapColumn(self):
return 22
class HierListCLBRErrorItem(hierlist.HierListItem):
def __init__(self, text):
self.text = text
def GetText(self):
return self.text
def GetSubList(self):
return [HierListCLBRErrorItem(self.text)]
def IsExpandable(self):
return 0
class HierListCLBRErrorRoot(HierListCLBRErrorItem):
def IsExpandable(self):
return 1
class BrowserView(pywin.mfc.docview.TreeView):
def OnInitialUpdate(self):
self.list = None
rc = self._obj_.OnInitialUpdate()
self.HookMessage(self.OnSize, win32con.WM_SIZE)
self.bDirty = 0
self.destroying = 0
return rc
def DestroyBrowser(self):
self.DestroyList()
def OnActivateView(self, activate, av, dv):
# print "AV", self.bDirty, activate
if activate:
self.CheckRefreshList()
return self._obj_.OnActivateView(activate, av, dv)
def _MakeRoot(self):
path = self.GetDocument().GetPathName()
if not path:
return HierListCLBRErrorRoot(
"Error: Can not browse a file until it is saved"
)
else:
mod, path = pywin.framework.scriptutils.GetPackageModuleName(path)
if self.bDirty:
what = "Refreshing"
# Hack for pyclbr being too smart
try:
del pyclbr._modules[mod]
except (KeyError, AttributeError):
pass
else:
what = "Building"
win32ui.SetStatusText("%s class list - please wait..." % (what,), 1)
win32ui.DoWaitCursor(1)
try:
reader = pyclbr.readmodule_ex # new version post 1.5.2
except AttributeError:
reader = pyclbr.readmodule
try:
data = reader(mod, [path])
if data:
return HierListCLBRModule(mod, data)
else:
return HierListCLBRErrorRoot("No Python classes in module.")
finally:
win32ui.DoWaitCursor(0)
win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE))
def DestroyList(self):
self.destroying = 1
list = getattr(
self, "list", None
) # If the document was not successfully opened, we may not have a list.
self.list = None
if list is not None:
list.HierTerm()
self.destroying = 0
def CheckMadeList(self):
if self.list is not None or self.destroying:
return
self.rootitem = root = self._MakeRoot()
self.list = list = hierlist.HierListWithItems(root, win32ui.IDB_BROWSER_HIER)
list.HierInit(self.GetParentFrame(), self)
list.SetStyle(
commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS
)
def CheckRefreshList(self):
if self.bDirty:
if self.list is None:
self.CheckMadeList()
else:
new_root = self._MakeRoot()
if self.rootitem.__class__ == new_root.__class__ == HierListCLBRModule:
self.rootitem.modName = new_root.modName
self.rootitem.clbrdata = new_root.clbrdata
self.list.Refresh()
else:
self.list.AcceptRoot(self._MakeRoot())
self.bDirty = 0
def OnSize(self, params):
lparam = params[3]
w = win32api.LOWORD(lparam)
h = win32api.HIWORD(lparam)
if w != 0:
self.CheckMadeList()
elif w == 0:
self.DestroyList()
return 1
def _UpdateUIForState(self):
self.bDirty = 1

View File

@ -0,0 +1,103 @@
# __init__ for the Pythonwin editor package.
#
# We used to support optional editors - eg, color or non-color.
#
# This really isnt necessary with Scintilla, and scintilla
# is getting so deeply embedded that it was too much work.
import win32ui, sys, win32con
defaultCharacterFormat = (-402653169, 0, 200, 0, 0, 0, 49, "Courier New")
##def GetDefaultEditorModuleName():
## import pywin
## # If someone has set pywin.editormodulename, then this is what we use
## try:
## prefModule = pywin.editormodulename
## except AttributeError:
## prefModule = win32ui.GetProfileVal("Editor","Module", "")
## return prefModule
##
##def WriteDefaultEditorModule(module):
## try:
## module = module.__name__
## except:
## pass
## win32ui.WriteProfileVal("Editor", "Module", module)
def LoadDefaultEditor():
pass
## prefModule = GetDefaultEditorModuleName()
## restorePrefModule = None
## mod = None
## if prefModule:
## try:
## mod = __import__(prefModule)
## except 'xx':
## msg = "Importing your preferred editor ('%s') failed.\n\nError %s: %s\n\nAn attempt will be made to load the default editor.\n\nWould you like this editor disabled in the future?" % (prefModule, sys.exc_info()[0], sys.exc_info()[1])
## rc = win32ui.MessageBox(msg, "Error importing editor", win32con.MB_YESNO)
## if rc == win32con.IDNO:
## restorePrefModule = prefModule
## WriteDefaultEditorModule("")
## del rc
##
## try:
## # Try and load the default one - dont catch errors here.
## if mod is None:
## prefModule = "pywin.framework.editor.color.coloreditor"
## mod = __import__(prefModule)
##
## # Get at the real module.
## mod = sys.modules[prefModule]
##
## # Do a "from mod import *"
## globals().update(mod.__dict__)
##
## finally:
## # Restore the users default editor if it failed and they requested not to disable it.
## if restorePrefModule:
## WriteDefaultEditorModule(restorePrefModule)
def GetEditorOption(option, defaultValue, min=None, max=None):
rc = win32ui.GetProfileVal("Editor", option, defaultValue)
if min is not None and rc < min:
rc = defaultValue
if max is not None and rc > max:
rc = defaultValue
return rc
def SetEditorOption(option, newValue):
win32ui.WriteProfileVal("Editor", option, newValue)
def DeleteEditorOption(option):
try:
win32ui.WriteProfileVal("Editor", option, None)
except win32ui.error:
pass
# Load and save font tuples
def GetEditorFontOption(option, default=None):
if default is None:
default = defaultCharacterFormat
fmt = GetEditorOption(option, "")
if fmt == "":
return default
try:
return eval(fmt)
except:
print("WARNING: Invalid font setting in registry - setting ignored")
return default
def SetEditorFontOption(option, newValue):
SetEditorOption(option, str(newValue))
from pywin.framework.editor.color.coloreditor import editorTemplate

View File

@ -0,0 +1,667 @@
# Color Editor originally by Neil Hodgson, but restructured by mh to integrate
# even tighter into Pythonwin.
import win32ui
import win32con
import win32api
import sys
import pywin.scintilla.keycodes
from pywin.scintilla import bindings
from pywin.framework.editor import (
GetEditorOption,
SetEditorOption,
GetEditorFontOption,
SetEditorFontOption,
defaultCharacterFormat,
)
# from pywin.framework.editor import EditorPropertyPage
MSG_CHECK_EXTERNAL_FILE = (
win32con.WM_USER + 1999
) ## WARNING: Duplicated in document.py and editor.py
# Define a few common markers
MARKER_BOOKMARK = 0
MARKER_BREAKPOINT = 1
MARKER_CURRENT = 2
from pywin.debugger import dbgcon
from pywin.scintilla.document import CScintillaDocument
from pywin.framework.editor.document import EditorDocumentBase
from pywin.scintilla import scintillacon # For the marker definitions
import pywin.scintilla.view
class SyntEditDocument(EditorDocumentBase):
"A SyntEdit document."
def OnDebuggerStateChange(self, state):
self._ApplyOptionalToViews("OnDebuggerStateChange", state)
def HookViewNotifications(self, view):
EditorDocumentBase.HookViewNotifications(self, view)
view.SCISetUndoCollection(1)
def FinalizeViewCreation(self, view):
EditorDocumentBase.FinalizeViewCreation(self, view)
if view == self.GetFirstView():
self.GetDocTemplate().CheckIDLEMenus(view.idle)
SyntEditViewParent = pywin.scintilla.view.CScintillaView
class SyntEditView(SyntEditViewParent):
"A view of a SyntEdit. Obtains data from document."
def __init__(self, doc):
SyntEditViewParent.__init__(self, doc)
self.bCheckingFile = 0
def OnInitialUpdate(self):
SyntEditViewParent.OnInitialUpdate(self)
self.HookMessage(self.OnRClick, win32con.WM_RBUTTONDOWN)
for id in [
win32ui.ID_VIEW_FOLD_COLLAPSE,
win32ui.ID_VIEW_FOLD_COLLAPSE_ALL,
win32ui.ID_VIEW_FOLD_EXPAND,
win32ui.ID_VIEW_FOLD_EXPAND_ALL,
]:
self.HookCommand(self.OnCmdViewFold, id)
self.HookCommandUpdate(self.OnUpdateViewFold, id)
self.HookCommand(self.OnCmdViewFoldTopLevel, win32ui.ID_VIEW_FOLD_TOPLEVEL)
# Define the markers
# self.SCIMarkerDeleteAll()
self.SCIMarkerDefineAll(
MARKER_BOOKMARK,
scintillacon.SC_MARK_ROUNDRECT,
win32api.RGB(0x0, 0x0, 0x0),
win32api.RGB(0, 0xFF, 0xFF),
)
self.SCIMarkerDefine(MARKER_CURRENT, scintillacon.SC_MARK_ARROW)
self.SCIMarkerSetBack(MARKER_CURRENT, win32api.RGB(0xFF, 0xFF, 0x00))
# Define the folding markers
if 1: # traditional markers
self.SCIMarkerDefineAll(
scintillacon.SC_MARKNUM_FOLDEROPEN,
scintillacon.SC_MARK_MINUS,
win32api.RGB(0xFF, 0xFF, 0xFF),
win32api.RGB(0, 0, 0),
)
self.SCIMarkerDefineAll(
scintillacon.SC_MARKNUM_FOLDER,
scintillacon.SC_MARK_PLUS,
win32api.RGB(0xFF, 0xFF, 0xFF),
win32api.RGB(0, 0, 0),
)
self.SCIMarkerDefineAll(
scintillacon.SC_MARKNUM_FOLDERSUB,
scintillacon.SC_MARK_EMPTY,
win32api.RGB(0xFF, 0xFF, 0xFF),
win32api.RGB(0, 0, 0),
)
self.SCIMarkerDefineAll(
scintillacon.SC_MARKNUM_FOLDERTAIL,
scintillacon.SC_MARK_EMPTY,
win32api.RGB(0xFF, 0xFF, 0xFF),
win32api.RGB(0, 0, 0),
)
self.SCIMarkerDefineAll(
scintillacon.SC_MARKNUM_FOLDEREND,
scintillacon.SC_MARK_EMPTY,
win32api.RGB(0xFF, 0xFF, 0xFF),
win32api.RGB(0, 0, 0),
)
self.SCIMarkerDefineAll(
scintillacon.SC_MARKNUM_FOLDEROPENMID,
scintillacon.SC_MARK_EMPTY,
win32api.RGB(0xFF, 0xFF, 0xFF),
win32api.RGB(0, 0, 0),
)
self.SCIMarkerDefineAll(
scintillacon.SC_MARKNUM_FOLDERMIDTAIL,
scintillacon.SC_MARK_EMPTY,
win32api.RGB(0xFF, 0xFF, 0xFF),
win32api.RGB(0, 0, 0),
)
else: # curved markers
self.SCIMarkerDefineAll(
scintillacon.SC_MARKNUM_FOLDEROPEN,
scintillacon.SC_MARK_CIRCLEMINUS,
win32api.RGB(0xFF, 0xFF, 0xFF),
win32api.RGB(0, 0, 0),
)
self.SCIMarkerDefineAll(
scintillacon.SC_MARKNUM_FOLDER,
scintillacon.SC_MARK_CIRCLEPLUS,
win32api.RGB(0xFF, 0xFF, 0xFF),
win32api.RGB(0, 0, 0),
)
self.SCIMarkerDefineAll(
scintillacon.SC_MARKNUM_FOLDERSUB,
scintillacon.SC_MARK_VLINE,
win32api.RGB(0xFF, 0xFF, 0xFF),
win32api.RGB(0, 0, 0),
)
self.SCIMarkerDefineAll(
scintillacon.SC_MARKNUM_FOLDERTAIL,
scintillacon.SC_MARK_LCORNERCURVE,
win32api.RGB(0xFF, 0xFF, 0xFF),
win32api.RGB(0, 0, 0),
)
self.SCIMarkerDefineAll(
scintillacon.SC_MARKNUM_FOLDEREND,
scintillacon.SC_MARK_CIRCLEPLUSCONNECTED,
win32api.RGB(0xFF, 0xFF, 0xFF),
win32api.RGB(0, 0, 0),
)
self.SCIMarkerDefineAll(
scintillacon.SC_MARKNUM_FOLDEROPENMID,
scintillacon.SC_MARK_CIRCLEMINUSCONNECTED,
win32api.RGB(0xFF, 0xFF, 0xFF),
win32api.RGB(0, 0, 0),
)
self.SCIMarkerDefineAll(
scintillacon.SC_MARKNUM_FOLDERMIDTAIL,
scintillacon.SC_MARK_TCORNERCURVE,
win32api.RGB(0xFF, 0xFF, 0xFF),
win32api.RGB(0, 0, 0),
)
self.SCIMarkerDefine(MARKER_BREAKPOINT, scintillacon.SC_MARK_CIRCLE)
# Marker background depends on debugger state
self.SCIMarkerSetFore(MARKER_BREAKPOINT, win32api.RGB(0x0, 0, 0))
# Get the current debugger state.
try:
import pywin.debugger
if pywin.debugger.currentDebugger is None:
state = dbgcon.DBGSTATE_NOT_DEBUGGING
else:
state = pywin.debugger.currentDebugger.debuggerState
except ImportError:
state = dbgcon.DBGSTATE_NOT_DEBUGGING
self.OnDebuggerStateChange(state)
def _GetSubConfigNames(self):
return ["editor"] # Allow [Keys:Editor] sections to be specific to us
def DoConfigChange(self):
SyntEditViewParent.DoConfigChange(self)
tabSize = GetEditorOption("Tab Size", 4, 2)
indentSize = GetEditorOption("Indent Size", 4, 2)
bUseTabs = GetEditorOption("Use Tabs", 0)
bSmartTabs = GetEditorOption("Smart Tabs", 1)
ext = self.idle.IDLEExtension("AutoIndent") # Required extension.
self.SCISetViewWS(GetEditorOption("View Whitespace", 0))
self.SCISetViewEOL(GetEditorOption("View EOL", 0))
self.SCISetIndentationGuides(GetEditorOption("View Indentation Guides", 0))
if GetEditorOption("Right Edge Enabled", 0):
mode = scintillacon.EDGE_BACKGROUND
else:
mode = scintillacon.EDGE_NONE
self.SCISetEdgeMode(mode)
self.SCISetEdgeColumn(GetEditorOption("Right Edge Column", 75))
self.SCISetEdgeColor(
GetEditorOption("Right Edge Color", win32api.RGB(0xEF, 0xEF, 0xEF))
)
width = GetEditorOption("Marker Margin Width", 16)
self.SCISetMarginWidthN(1, width)
width = GetEditorOption("Fold Margin Width", 12)
self.SCISetMarginWidthN(2, width)
width = GetEditorOption("Line Number Margin Width", 0)
self.SCISetMarginWidthN(0, width)
self.bFolding = GetEditorOption("Enable Folding", 1)
fold_flags = 0
self.SendScintilla(
scintillacon.SCI_SETMODEVENTMASK, scintillacon.SC_MOD_CHANGEFOLD
)
if self.bFolding:
if GetEditorOption("Fold Lines", 1):
fold_flags = 16
self.SCISetProperty("fold", self.bFolding)
self.SCISetFoldFlags(fold_flags)
tt_color = GetEditorOption("Tab Timmy Color", win32api.RGB(0xFF, 0, 0))
self.SendScintilla(scintillacon.SCI_INDICSETFORE, 1, tt_color)
tt_use = GetEditorOption("Use Tab Timmy", 1)
if tt_use:
self.SCISetProperty("tab.timmy.whinge.level", "1")
# Auto-indent has very complicated behaviour. In a nutshell, the only
# way to get sensible behaviour from it is to ensure tabwidth != indentsize.
# Further, usetabs will only ever go from 1->0, never 0->1.
# This is _not_ the behaviour Pythonwin wants:
# * Tab width is arbitary, so should have no impact on smarts.
# * bUseTabs setting should reflect how new files are created, and
# if Smart Tabs disabled, existing files are edited
# * If "Smart Tabs" is enabled, bUseTabs should have no bearing
# for existing files (unless of course no context can be determined)
#
# So for smart tabs we configure the widget with completely dummy
# values (ensuring tabwidth != indentwidth), ask it to guess, then
# look at the values it has guessed, and re-configure
if bSmartTabs:
ext.config(usetabs=1, tabwidth=5, indentwidth=4)
ext.set_indentation_params(1)
if ext.indentwidth == 5:
# Either 5 literal spaces, or a single tab character. Assume a tab
usetabs = 1
indentwidth = tabSize
else:
# Either Indented with spaces, and indent size has been guessed or
# an empty file (or no context found - tough!)
if self.GetTextLength() == 0: # emtpy
usetabs = bUseTabs
indentwidth = indentSize
else: # guessed.
indentwidth = ext.indentwidth
usetabs = 0
# Tab size can never be guessed - set at user preference.
ext.config(usetabs=usetabs, indentwidth=indentwidth, tabwidth=tabSize)
else:
# Dont want smart-tabs - just set the options!
ext.config(usetabs=bUseTabs, tabwidth=tabSize, indentwidth=indentSize)
self.SCISetIndent(indentSize)
self.SCISetTabWidth(tabSize)
def OnDebuggerStateChange(self, state):
if state == dbgcon.DBGSTATE_NOT_DEBUGGING:
# Indicate breakpoints arent really usable.
# Not quite white - useful when no marker margin, so set as background color.
self.SCIMarkerSetBack(MARKER_BREAKPOINT, win32api.RGB(0xEF, 0xEF, 0xEF))
else:
# A light-red, so still readable when no marker margin.
self.SCIMarkerSetBack(MARKER_BREAKPOINT, win32api.RGB(0xFF, 0x80, 0x80))
def HookDocumentHandlers(self):
SyntEditViewParent.HookDocumentHandlers(self)
self.HookMessage(self.OnCheckExternalDocumentUpdated, MSG_CHECK_EXTERNAL_FILE)
def HookHandlers(self):
SyntEditViewParent.HookHandlers(self)
self.HookMessage(self.OnSetFocus, win32con.WM_SETFOCUS)
def _PrepareUserStateChange(self):
return self.GetSel(), self.GetFirstVisibleLine()
def _EndUserStateChange(self, info):
scrollOff = info[1] - self.GetFirstVisibleLine()
if scrollOff:
self.LineScroll(scrollOff)
# Make sure we dont reset the cursor beyond the buffer.
max = self.GetTextLength()
newPos = min(info[0][0], max), min(info[0][1], max)
self.SetSel(newPos)
#######################################
# The Windows Message or Notify handlers.
#######################################
def OnMarginClick(self, std, extra):
notify = self.SCIUnpackNotifyMessage(extra)
if notify.margin == 2: # Our fold margin
line_click = self.LineFromChar(notify.position)
# max_line = self.GetLineCount()
if self.SCIGetFoldLevel(line_click) & scintillacon.SC_FOLDLEVELHEADERFLAG:
# If a fold point.
self.SCIToggleFold(line_click)
return 1
def OnSetFocus(self, msg):
# Even though we use file change notifications, we should be very sure about it here.
self.OnCheckExternalDocumentUpdated(msg)
return 1
def OnCheckExternalDocumentUpdated(self, msg):
if self.bCheckingFile:
return
self.bCheckingFile = 1
self.GetDocument().CheckExternalDocumentUpdated()
self.bCheckingFile = 0
def OnRClick(self, params):
menu = win32ui.CreatePopupMenu()
self.AppendMenu(menu, "&Locate module", "LocateModule")
self.AppendMenu(menu, flags=win32con.MF_SEPARATOR)
self.AppendMenu(menu, "&Undo", "EditUndo")
self.AppendMenu(menu, "&Redo", "EditRedo")
self.AppendMenu(menu, flags=win32con.MF_SEPARATOR)
self.AppendMenu(menu, "Cu&t", "EditCut")
self.AppendMenu(menu, "&Copy", "EditCopy")
self.AppendMenu(menu, "&Paste", "EditPaste")
self.AppendMenu(menu, flags=win32con.MF_SEPARATOR)
self.AppendMenu(menu, "&Select all", "EditSelectAll")
self.AppendMenu(
menu, "View &Whitespace", "ViewWhitespace", checked=self.SCIGetViewWS()
)
self.AppendMenu(
menu, "&Fixed Font", "ViewFixedFont", checked=self._GetColorizer().bUseFixed
)
self.AppendMenu(menu, flags=win32con.MF_SEPARATOR)
self.AppendMenu(menu, "&Goto line...", "GotoLine")
submenu = win32ui.CreatePopupMenu()
newitems = self.idle.GetMenuItems("edit")
for text, event in newitems:
self.AppendMenu(submenu, text, event)
flags = win32con.MF_STRING | win32con.MF_ENABLED | win32con.MF_POPUP
menu.AppendMenu(flags, submenu.GetHandle(), "&Source code")
flags = (
win32con.TPM_LEFTALIGN | win32con.TPM_LEFTBUTTON | win32con.TPM_RIGHTBUTTON
)
menu.TrackPopupMenu(params[5], flags, self)
return 0
def OnCmdViewFold(self, cid, code): # Handle the menu command
if cid == win32ui.ID_VIEW_FOLD_EXPAND_ALL:
self.FoldExpandAllEvent(None)
elif cid == win32ui.ID_VIEW_FOLD_EXPAND:
self.FoldExpandEvent(None)
elif cid == win32ui.ID_VIEW_FOLD_COLLAPSE_ALL:
self.FoldCollapseAllEvent(None)
elif cid == win32ui.ID_VIEW_FOLD_COLLAPSE:
self.FoldCollapseEvent(None)
else:
print("Unknown collapse/expand ID")
def OnUpdateViewFold(self, cmdui): # Update the tick on the UI.
if not self.bFolding:
cmdui.Enable(0)
return
id = cmdui.m_nID
if id in [win32ui.ID_VIEW_FOLD_EXPAND_ALL, win32ui.ID_VIEW_FOLD_COLLAPSE_ALL]:
cmdui.Enable()
else:
enable = 0
lineno = self.LineFromChar(self.GetSel()[0])
foldable = (
self.SCIGetFoldLevel(lineno) & scintillacon.SC_FOLDLEVELHEADERFLAG
)
is_expanded = self.SCIGetFoldExpanded(lineno)
if id == win32ui.ID_VIEW_FOLD_EXPAND:
if foldable and not is_expanded:
enable = 1
elif id == win32ui.ID_VIEW_FOLD_COLLAPSE:
if foldable and is_expanded:
enable = 1
cmdui.Enable(enable)
def OnCmdViewFoldTopLevel(self, cid, code): # Handle the menu command
self.FoldTopLevelEvent(None)
#######################################
# The Events
#######################################
def ToggleBookmarkEvent(self, event, pos=-1):
"""Toggle a bookmark at the specified or current position"""
if pos == -1:
pos, end = self.GetSel()
startLine = self.LineFromChar(pos)
self.GetDocument().MarkerToggle(startLine + 1, MARKER_BOOKMARK)
return 0
def GotoNextBookmarkEvent(self, event, fromPos=-1):
"""Move to the next bookmark"""
if fromPos == -1:
fromPos, end = self.GetSel()
startLine = self.LineFromChar(fromPos) + 1 # Zero based line to start
nextLine = self.GetDocument().MarkerGetNext(startLine + 1, MARKER_BOOKMARK) - 1
if nextLine < 0:
nextLine = self.GetDocument().MarkerGetNext(0, MARKER_BOOKMARK) - 1
if nextLine < 0 or nextLine == startLine - 1:
win32api.MessageBeep()
else:
self.SCIEnsureVisible(nextLine)
self.SCIGotoLine(nextLine)
return 0
def TabKeyEvent(self, event):
"""Insert an indent. If no selection, a single indent, otherwise a block indent"""
# Handle auto-complete first.
if self.SCIAutoCActive():
self.SCIAutoCComplete()
return 0
# Call the IDLE event.
return self.bindings.fire("<<smart-indent>>", event)
def EnterKeyEvent(self, event):
"""Handle the enter key with special handling for auto-complete"""
# Handle auto-complete first.
if self.SCIAutoCActive():
self.SCIAutoCComplete()
self.SCIAutoCCancel()
# Call the IDLE event.
return self.bindings.fire("<<newline-and-indent>>", event)
def ShowInteractiveWindowEvent(self, event):
import pywin.framework.interact
pywin.framework.interact.ShowInteractiveWindow()
def FoldTopLevelEvent(self, event=None):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
try:
self.Colorize()
maxLine = self.GetLineCount()
# Find the first line, and check out its state.
for lineSeek in range(maxLine):
if self.SCIGetFoldLevel(lineSeek) & scintillacon.SC_FOLDLEVELHEADERFLAG:
expanding = not self.SCIGetFoldExpanded(lineSeek)
break
else:
# no folds here!
return
for lineSeek in range(lineSeek, maxLine):
level = self.SCIGetFoldLevel(lineSeek)
level_no = (
level
& scintillacon.SC_FOLDLEVELNUMBERMASK
- scintillacon.SC_FOLDLEVELBASE
)
is_header = level & scintillacon.SC_FOLDLEVELHEADERFLAG
# print lineSeek, level_no, is_header
if level_no == 0 and is_header:
if (expanding and not self.SCIGetFoldExpanded(lineSeek)) or (
not expanding and self.SCIGetFoldExpanded(lineSeek)
):
self.SCIToggleFold(lineSeek)
finally:
win32ui.DoWaitCursor(-1)
def FoldExpandSecondLevelEvent(self, event):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
## I think this is needed since Scintilla may not have
## already formatted parts of file outside visible window.
self.Colorize()
levels = [scintillacon.SC_FOLDLEVELBASE]
## Scintilla's level number is based on amount of whitespace indentation
for lineno in range(self.GetLineCount()):
level = self.SCIGetFoldLevel(lineno)
if not level & scintillacon.SC_FOLDLEVELHEADERFLAG:
continue
curr_level = level & scintillacon.SC_FOLDLEVELNUMBERMASK
if curr_level > levels[-1]:
levels.append(curr_level)
try:
level_ind = levels.index(curr_level)
except ValueError:
## probably syntax error in source file, bail
break
levels = levels[: level_ind + 1]
if level_ind == 1 and not self.SCIGetFoldExpanded(lineno):
self.SCIToggleFold(lineno)
win32ui.DoWaitCursor(-1)
def FoldCollapseSecondLevelEvent(self, event):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
## I think this is needed since Scintilla may not have
## already formatted parts of file outside visible window.
self.Colorize()
levels = [scintillacon.SC_FOLDLEVELBASE]
## Scintilla's level number is based on amount of whitespace indentation
for lineno in range(self.GetLineCount()):
level = self.SCIGetFoldLevel(lineno)
if not level & scintillacon.SC_FOLDLEVELHEADERFLAG:
continue
curr_level = level & scintillacon.SC_FOLDLEVELNUMBERMASK
if curr_level > levels[-1]:
levels.append(curr_level)
try:
level_ind = levels.index(curr_level)
except ValueError:
## probably syntax error in source file, bail
break
levels = levels[: level_ind + 1]
if level_ind == 1 and self.SCIGetFoldExpanded(lineno):
self.SCIToggleFold(lineno)
win32ui.DoWaitCursor(-1)
def FoldExpandEvent(self, event):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
lineno = self.LineFromChar(self.GetSel()[0])
if self.SCIGetFoldLevel(
lineno
) & scintillacon.SC_FOLDLEVELHEADERFLAG and not self.SCIGetFoldExpanded(lineno):
self.SCIToggleFold(lineno)
win32ui.DoWaitCursor(-1)
def FoldExpandAllEvent(self, event):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
for lineno in range(0, self.GetLineCount()):
if self.SCIGetFoldLevel(
lineno
) & scintillacon.SC_FOLDLEVELHEADERFLAG and not self.SCIGetFoldExpanded(
lineno
):
self.SCIToggleFold(lineno)
win32ui.DoWaitCursor(-1)
def FoldCollapseEvent(self, event):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
lineno = self.LineFromChar(self.GetSel()[0])
if self.SCIGetFoldLevel(
lineno
) & scintillacon.SC_FOLDLEVELHEADERFLAG and self.SCIGetFoldExpanded(lineno):
self.SCIToggleFold(lineno)
win32ui.DoWaitCursor(-1)
def FoldCollapseAllEvent(self, event):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
self.Colorize()
for lineno in range(0, self.GetLineCount()):
if self.SCIGetFoldLevel(
lineno
) & scintillacon.SC_FOLDLEVELHEADERFLAG and self.SCIGetFoldExpanded(lineno):
self.SCIToggleFold(lineno)
win32ui.DoWaitCursor(-1)
from pywin.framework.editor.frame import EditorFrame
class SplitterFrame(EditorFrame):
def OnCreate(self, cs):
self.HookCommand(self.OnWindowSplit, win32ui.ID_WINDOW_SPLIT)
return 1
def OnWindowSplit(self, id, code):
self.GetDlgItem(win32ui.AFX_IDW_PANE_FIRST).DoKeyboardSplit()
return 1
from pywin.framework.editor.template import EditorTemplateBase
class SyntEditTemplate(EditorTemplateBase):
def __init__(
self, res=win32ui.IDR_TEXTTYPE, makeDoc=None, makeFrame=None, makeView=None
):
if makeDoc is None:
makeDoc = SyntEditDocument
if makeView is None:
makeView = SyntEditView
if makeFrame is None:
makeFrame = SplitterFrame
self.bSetMenus = 0
EditorTemplateBase.__init__(self, res, makeDoc, makeFrame, makeView)
def CheckIDLEMenus(self, idle):
if self.bSetMenus:
return
self.bSetMenus = 1
submenu = win32ui.CreatePopupMenu()
newitems = idle.GetMenuItems("edit")
flags = win32con.MF_STRING | win32con.MF_ENABLED
for text, event in newitems:
id = bindings.event_to_commands.get(event)
if id is not None:
keyname = pywin.scintilla.view.configManager.get_key_binding(
event, ["editor"]
)
if keyname is not None:
text = text + "\t" + keyname
submenu.AppendMenu(flags, id, text)
mainMenu = self.GetSharedMenu()
editMenu = mainMenu.GetSubMenu(1)
editMenu.AppendMenu(win32con.MF_SEPARATOR, 0, "")
editMenu.AppendMenu(
win32con.MF_STRING | win32con.MF_POPUP | win32con.MF_ENABLED,
submenu.GetHandle(),
"&Source Code",
)
def _CreateDocTemplate(self, resourceId):
return win32ui.CreateDocTemplate(resourceId)
def CreateWin32uiDocument(self):
return self.DoCreateDoc()
def GetPythonPropertyPages(self):
"""Returns a list of property pages"""
from pywin.scintilla import configui
return EditorTemplateBase.GetPythonPropertyPages(self) + [
configui.ScintillaFormatPropertyPage()
]
# For debugging purposes, when this module may be reloaded many times.
try:
win32ui.GetApp().RemoveDocTemplate(editorTemplate)
except NameError:
pass
editorTemplate = SyntEditTemplate()
win32ui.GetApp().AddDocTemplate(editorTemplate)

View File

@ -0,0 +1,307 @@
from pywin.mfc import dialog
from . import document
import win32ui
import win32con
import win32api
from pywin.framework.editor import (
GetEditorOption,
SetEditorOption,
DeleteEditorOption,
GetEditorFontOption,
SetEditorFontOption,
defaultCharacterFormat,
editorTemplate,
)
import pywin.scintilla.config
# The standard 16 color VGA palette should always be possible
paletteVGA = (
("Black", 0, 0, 0),
("Navy", 0, 0, 128),
("Green", 0, 128, 0),
("Cyan", 0, 128, 128),
("Maroon", 128, 0, 0),
("Purple", 128, 0, 128),
("Olive", 128, 128, 0),
("Gray", 128, 128, 128),
("Silver", 192, 192, 192),
("Blue", 0, 0, 255),
("Lime", 0, 255, 0),
("Aqua", 0, 255, 255),
("Red", 255, 0, 0),
("Fuchsia", 255, 0, 255),
("Yellow", 255, 255, 0),
("White", 255, 255, 255),
)
######################################################
#
# Property Page for editor options
#
class EditorPropertyPage(dialog.PropertyPage):
def __init__(self):
dialog.PropertyPage.__init__(self, win32ui.IDD_PP_EDITOR)
self.autooptions = []
self._AddEditorOption(win32ui.IDC_AUTO_RELOAD, "i", "Auto Reload", 1)
self._AddEditorOption(
win32ui.IDC_COMBO1, "i", "Backup Type", document.BAK_DOT_BAK_BAK_DIR
)
self._AddEditorOption(
win32ui.IDC_AUTOCOMPLETE, "i", "Autocomplete Attributes", 1
)
self._AddEditorOption(win32ui.IDC_CALLTIPS, "i", "Show Call Tips", 1)
self._AddEditorOption(
win32ui.IDC_MARGIN_LINENUMBER, "i", "Line Number Margin Width", 0
)
self._AddEditorOption(win32ui.IDC_RADIO1, "i", "MarkersInMargin", None)
self._AddEditorOption(
win32ui.IDC_MARGIN_MARKER, "i", "Marker Margin Width", None
)
self["Marker Margin Width"] = GetEditorOption("Marker Margin Width", 16)
# Folding
self._AddEditorOption(win32ui.IDC_MARGIN_FOLD, "i", "Fold Margin Width", 12)
self._AddEditorOption(win32ui.IDC_FOLD_ENABLE, "i", "Enable Folding", 1)
self._AddEditorOption(win32ui.IDC_FOLD_ON_OPEN, "i", "Fold On Open", 0)
self._AddEditorOption(win32ui.IDC_FOLD_SHOW_LINES, "i", "Fold Lines", 1)
# Right edge.
self._AddEditorOption(
win32ui.IDC_RIGHTEDGE_ENABLE, "i", "Right Edge Enabled", 0
)
self._AddEditorOption(
win32ui.IDC_RIGHTEDGE_COLUMN, "i", "Right Edge Column", 75
)
# Source control, etc
self.AddDDX(win32ui.IDC_VSS_INTEGRATE, "bVSS")
self.AddDDX(win32ui.IDC_KEYBOARD_CONFIG, "Configs", "l")
self["Configs"] = pywin.scintilla.config.find_config_files()
def _AddEditorOption(self, idd, typ, optionName, defaultVal):
self.AddDDX(idd, optionName, typ)
# some options are "derived" - ie, can be implied from others
# (eg, "view markers in background" is implied from "markerMarginWidth==0"
# So we don't actually store these values, but they do still get DDX support.
if defaultVal is not None:
self[optionName] = GetEditorOption(optionName, defaultVal)
self.autooptions.append((optionName, defaultVal))
def OnInitDialog(self):
for name, val in self.autooptions:
self[name] = GetEditorOption(name, val)
# Note that these MUST be in the same order as the BAK constants.
cbo = self.GetDlgItem(win32ui.IDC_COMBO1)
cbo.AddString("None")
cbo.AddString(".BAK File")
cbo.AddString("TEMP dir")
cbo.AddString("Own dir")
# Source Safe
bVSS = (
GetEditorOption("Source Control Module", "") == "pywin.framework.editor.vss"
)
self["bVSS"] = bVSS
edit = self.GetDlgItem(win32ui.IDC_RIGHTEDGE_SAMPLE)
edit.SetWindowText("Sample Color")
rc = dialog.PropertyPage.OnInitDialog(self)
try:
self.GetDlgItem(win32ui.IDC_KEYBOARD_CONFIG).SelectString(
-1, GetEditorOption("Keyboard Config", "default")
)
except win32ui.error:
import traceback
traceback.print_exc()
pass
self.HookCommand(self.OnButSimple, win32ui.IDC_FOLD_ENABLE)
self.HookCommand(self.OnButSimple, win32ui.IDC_RADIO1)
self.HookCommand(self.OnButSimple, win32ui.IDC_RADIO2)
self.HookCommand(self.OnButSimple, win32ui.IDC_RIGHTEDGE_ENABLE)
self.HookCommand(self.OnButEdgeColor, win32ui.IDC_RIGHTEDGE_DEFINE)
butMarginEnabled = self["Marker Margin Width"] > 0
self.GetDlgItem(win32ui.IDC_RADIO1).SetCheck(butMarginEnabled)
self.GetDlgItem(win32ui.IDC_RADIO2).SetCheck(not butMarginEnabled)
self.edgeColor = self.initialEdgeColor = GetEditorOption(
"Right Edge Color", win32api.RGB(0xEF, 0xEF, 0xEF)
)
for spinner_id in (win32ui.IDC_SPIN1, win32ui.IDC_SPIN2, win32ui.IDC_SPIN3):
spinner = self.GetDlgItem(spinner_id)
spinner.SetRange(0, 100)
self.UpdateUIForState()
return rc
def OnButSimple(self, id, code):
if code == win32con.BN_CLICKED:
self.UpdateUIForState()
def OnButEdgeColor(self, id, code):
if code == win32con.BN_CLICKED:
d = win32ui.CreateColorDialog(self.edgeColor, 0, self)
# Ensure the current color is a custom color (as it may not be in the swatch)
# plus some other nice gray scales.
ccs = [self.edgeColor]
for c in range(0xEF, 0x4F, -0x10):
ccs.append(win32api.RGB(c, c, c))
d.SetCustomColors(ccs)
if d.DoModal() == win32con.IDOK:
self.edgeColor = d.GetColor()
self.UpdateUIForState()
def UpdateUIForState(self):
folding = self.GetDlgItem(win32ui.IDC_FOLD_ENABLE).GetCheck()
self.GetDlgItem(win32ui.IDC_FOLD_ON_OPEN).EnableWindow(folding)
self.GetDlgItem(win32ui.IDC_FOLD_SHOW_LINES).EnableWindow(folding)
widthEnabled = self.GetDlgItem(win32ui.IDC_RADIO1).GetCheck()
self.GetDlgItem(win32ui.IDC_MARGIN_MARKER).EnableWindow(widthEnabled)
self.UpdateData() # Ensure self[] is up to date with the control data.
if widthEnabled and self["Marker Margin Width"] == 0:
self["Marker Margin Width"] = 16
self.UpdateData(0) # Ensure control up to date with self[]
# Right edge
edgeEnabled = self.GetDlgItem(win32ui.IDC_RIGHTEDGE_ENABLE).GetCheck()
self.GetDlgItem(win32ui.IDC_RIGHTEDGE_COLUMN).EnableWindow(edgeEnabled)
self.GetDlgItem(win32ui.IDC_RIGHTEDGE_SAMPLE).EnableWindow(edgeEnabled)
self.GetDlgItem(win32ui.IDC_RIGHTEDGE_DEFINE).EnableWindow(edgeEnabled)
edit = self.GetDlgItem(win32ui.IDC_RIGHTEDGE_SAMPLE)
edit.SetBackgroundColor(0, self.edgeColor)
def OnOK(self):
for name, defVal in self.autooptions:
SetEditorOption(name, self[name])
# Margin width gets handled differently.
if self["MarkersInMargin"] == 0:
SetEditorOption("Marker Margin Width", self["Marker Margin Width"])
else:
SetEditorOption("Marker Margin Width", 0)
if self.edgeColor != self.initialEdgeColor:
SetEditorOption("Right Edge Color", self.edgeColor)
if self["bVSS"]:
SetEditorOption("Source Control Module", "pywin.framework.editor.vss")
else:
if (
GetEditorOption("Source Control Module", "")
== "pywin.framework.editor.vss"
):
SetEditorOption("Source Control Module", "")
# Keyboard config
configname = self.GetDlgItem(win32ui.IDC_KEYBOARD_CONFIG).GetWindowText()
if configname:
if configname == "default":
DeleteEditorOption("Keyboard Config")
else:
SetEditorOption("Keyboard Config", configname)
import pywin.scintilla.view
pywin.scintilla.view.LoadConfiguration()
# Now tell all views we have changed.
## for doc in editorTemplate.GetDocumentList():
## for view in doc.GetAllViews():
## try:
## fn = view.OnConfigChange
## except AttributeError:
## continue
## fn()
return 1
class EditorWhitespacePropertyPage(dialog.PropertyPage):
def __init__(self):
dialog.PropertyPage.__init__(self, win32ui.IDD_PP_TABS)
self.autooptions = []
self._AddEditorOption(win32ui.IDC_TAB_SIZE, "i", "Tab Size", 4)
self._AddEditorOption(win32ui.IDC_INDENT_SIZE, "i", "Indent Size", 4)
self._AddEditorOption(win32ui.IDC_USE_SMART_TABS, "i", "Smart Tabs", 1)
self._AddEditorOption(win32ui.IDC_VIEW_WHITESPACE, "i", "View Whitespace", 0)
self._AddEditorOption(win32ui.IDC_VIEW_EOL, "i", "View EOL", 0)
self._AddEditorOption(
win32ui.IDC_VIEW_INDENTATIONGUIDES, "i", "View Indentation Guides", 0
)
def _AddEditorOption(self, idd, typ, optionName, defaultVal):
self.AddDDX(idd, optionName, typ)
self[optionName] = GetEditorOption(optionName, defaultVal)
self.autooptions.append((optionName, defaultVal))
def OnInitDialog(self):
for name, val in self.autooptions:
self[name] = GetEditorOption(name, val)
rc = dialog.PropertyPage.OnInitDialog(self)
idc = win32ui.IDC_TABTIMMY_NONE
if GetEditorOption("Use Tab Timmy", 1):
idc = win32ui.IDC_TABTIMMY_IND
self.GetDlgItem(idc).SetCheck(1)
idc = win32ui.IDC_RADIO1
if GetEditorOption("Use Tabs", 0):
idc = win32ui.IDC_USE_TABS
self.GetDlgItem(idc).SetCheck(1)
tt_color = GetEditorOption("Tab Timmy Color", win32api.RGB(0xFF, 0, 0))
self.cbo = self.GetDlgItem(win32ui.IDC_COMBO1)
for c in paletteVGA:
self.cbo.AddString(c[0])
sel = 0
for c in paletteVGA:
if tt_color == win32api.RGB(c[1], c[2], c[3]):
break
sel = sel + 1
else:
sel = -1
self.cbo.SetCurSel(sel)
self.HookCommand(self.OnButSimple, win32ui.IDC_TABTIMMY_NONE)
self.HookCommand(self.OnButSimple, win32ui.IDC_TABTIMMY_IND)
self.HookCommand(self.OnButSimple, win32ui.IDC_TABTIMMY_BG)
# Set ranges for the spinners.
for spinner_id in [win32ui.IDC_SPIN1, win32ui.IDC_SPIN2]:
spinner = self.GetDlgItem(spinner_id)
spinner.SetRange(1, 16)
return rc
def OnButSimple(self, id, code):
if code == win32con.BN_CLICKED:
self.UpdateUIForState()
def UpdateUIForState(self):
timmy = self.GetDlgItem(win32ui.IDC_TABTIMMY_NONE).GetCheck()
self.GetDlgItem(win32ui.IDC_COMBO1).EnableWindow(not timmy)
def OnOK(self):
for name, defVal in self.autooptions:
SetEditorOption(name, self[name])
SetEditorOption("Use Tabs", self.GetDlgItem(win32ui.IDC_USE_TABS).GetCheck())
SetEditorOption(
"Use Tab Timmy", self.GetDlgItem(win32ui.IDC_TABTIMMY_IND).GetCheck()
)
c = paletteVGA[self.cbo.GetCurSel()]
SetEditorOption("Tab Timmy Color", win32api.RGB(c[1], c[2], c[3]))
return 1
def testpp():
ps = dialog.PropertySheet("Editor Options")
ps.AddPage(EditorWhitespacePropertyPage())
ps.DoModal()
if __name__ == "__main__":
testpp()

View File

@ -0,0 +1,378 @@
# We no longer support the old, non-colour editor!
from pywin.mfc import docview, object
from pywin.framework.editor import GetEditorOption
import win32ui
import os
import win32con
import string
import traceback
import win32api
import shutil
BAK_NONE = 0
BAK_DOT_BAK = 1
BAK_DOT_BAK_TEMP_DIR = 2
BAK_DOT_BAK_BAK_DIR = 3
MSG_CHECK_EXTERNAL_FILE = (
win32con.WM_USER + 1999
) ## WARNING: Duplicated in editor.py and coloreditor.py
import pywin.scintilla.document
ParentEditorDocument = pywin.scintilla.document.CScintillaDocument
class EditorDocumentBase(ParentEditorDocument):
def __init__(self, template):
self.bAutoReload = GetEditorOption("Auto Reload", 1)
self.bDeclinedReload = 0 # Has the user declined to reload.
self.fileStat = None
self.bReportedFileNotFound = 0
# what sort of bak file should I create.
# default to write to %temp%/bak/filename.ext
self.bakFileType = GetEditorOption("Backup Type", BAK_DOT_BAK_BAK_DIR)
self.watcherThread = FileWatchingThread(self)
self.watcherThread.CreateThread()
# Should I try and use VSS integration?
self.scModuleName = GetEditorOption("Source Control Module", "")
self.scModule = None # Loaded when first used.
ParentEditorDocument.__init__(self, template, template.CreateWin32uiDocument())
def OnCloseDocument(self):
self.watcherThread.SignalStop()
return self._obj_.OnCloseDocument()
# def OnOpenDocument(self, name):
# rc = ParentEditorDocument.OnOpenDocument(self, name)
# self.GetFirstView()._SetLoadedText(self.text)
# self._DocumentStateChanged()
# return rc
def OnSaveDocument(self, fileName):
win32ui.SetStatusText("Saving file...", 1)
# rename to bak if required.
dir, basename = os.path.split(fileName)
if self.bakFileType == BAK_DOT_BAK:
bakFileName = dir + "\\" + os.path.splitext(basename)[0] + ".bak"
elif self.bakFileType == BAK_DOT_BAK_TEMP_DIR:
bakFileName = (
win32api.GetTempPath() + "\\" + os.path.splitext(basename)[0] + ".bak"
)
elif self.bakFileType == BAK_DOT_BAK_BAK_DIR:
tempPath = os.path.join(win32api.GetTempPath(), "bak")
try:
os.mkdir(tempPath, 0)
except os.error:
pass
bakFileName = os.path.join(tempPath, basename)
try:
os.unlink(bakFileName) # raise NameError if no bakups wanted.
except (os.error, NameError):
pass
try:
# Do a copy as it might be on different volumes,
# and the file may be a hard-link, causing the link
# to follow the backup.
shutil.copy2(fileName, bakFileName)
except (os.error, NameError, IOError):
pass
try:
self.SaveFile(fileName)
except IOError as details:
win32ui.MessageBox("Error - could not save file\r\n\r\n%s" % details)
return 0
except (UnicodeEncodeError, LookupError) as details:
rc = win32ui.MessageBox(
"Encoding failed: \r\n%s" % details
+ "\r\nPlease add desired source encoding as first line of file, eg \r\n"
+ "# -*- coding: mbcs -*-\r\n\r\n"
+ "If you continue, the file will be saved as binary and will\r\n"
+ "not be valid in the declared encoding.\r\n\r\n"
+ "Save the file as binary with an invalid encoding?",
"File save failed",
win32con.MB_YESNO | win32con.MB_DEFBUTTON2,
)
if rc == win32con.IDYES:
try:
self.SaveFile(fileName, encoding="latin-1")
except IOError as details:
win32ui.MessageBox(
"Error - could not save file\r\n\r\n%s" % details
)
return 0
else:
return 0
self.SetModifiedFlag(0) # No longer dirty
self.bDeclinedReload = 0 # They probably want to know if it changes again!
win32ui.AddToRecentFileList(fileName)
self.SetPathName(fileName)
win32ui.SetStatusText("Ready")
self._DocumentStateChanged()
return 1
def FinalizeViewCreation(self, view):
ParentEditorDocument.FinalizeViewCreation(self, view)
if view == self.GetFirstView():
self._DocumentStateChanged()
if view.bFolding and GetEditorOption("Fold On Open", 0):
view.FoldTopLevelEvent()
def HookViewNotifications(self, view):
ParentEditorDocument.HookViewNotifications(self, view)
# Support for reloading the document from disk - presumably after some
# external application has modified it (or possibly source control has
# checked it out.
def ReloadDocument(self):
"""Reloads the document from disk. Assumes the file has
been saved and user has been asked if necessary - it just does it!
"""
win32ui.SetStatusText("Reloading document. Please wait...", 1)
self.SetModifiedFlag(0)
# Loop over all views, saving their state, then reload the document
views = self.GetAllViews()
states = []
for view in views:
try:
info = view._PrepareUserStateChange()
except AttributeError: # Not our editor view?
info = None
states.append(info)
self.OnOpenDocument(self.GetPathName())
for view, info in zip(views, states):
if info is not None:
view._EndUserStateChange(info)
self._DocumentStateChanged()
win32ui.SetStatusText("Document reloaded.")
# Reloading the file
def CheckExternalDocumentUpdated(self):
if self.bDeclinedReload or not self.GetPathName():
return
try:
newstat = os.stat(self.GetPathName())
except os.error as exc:
if not self.bReportedFileNotFound:
print(
"The file '%s' is open for editing, but\nchecking it for changes caused the error: %s"
% (self.GetPathName(), exc.strerror)
)
self.bReportedFileNotFound = 1
return
if self.bReportedFileNotFound:
print(
"The file '%s' has re-appeared - continuing to watch for changes..."
% (self.GetPathName(),)
)
self.bReportedFileNotFound = (
0 # Once found again we want to start complaining.
)
changed = (
(self.fileStat is None)
or self.fileStat[0] != newstat[0]
or self.fileStat[6] != newstat[6]
or self.fileStat[8] != newstat[8]
or self.fileStat[9] != newstat[9]
)
if changed:
question = None
if self.IsModified():
question = (
"%s\r\n\r\nThis file has been modified outside of the source editor.\r\nDo you want to reload it and LOSE THE CHANGES in the source editor?"
% self.GetPathName()
)
mbStyle = win32con.MB_YESNO | win32con.MB_DEFBUTTON2 # Default to "No"
else:
if not self.bAutoReload:
question = (
"%s\r\n\r\nThis file has been modified outside of the source editor.\r\nDo you want to reload it?"
% self.GetPathName()
)
mbStyle = win32con.MB_YESNO # Default to "Yes"
if question:
rc = win32ui.MessageBox(question, None, mbStyle)
if rc != win32con.IDYES:
self.bDeclinedReload = 1
return
self.ReloadDocument()
def _DocumentStateChanged(self):
"""Called whenever the documents state (on disk etc) has been changed
by the editor (eg, as the result of a save operation)
"""
if self.GetPathName():
try:
self.fileStat = os.stat(self.GetPathName())
except os.error:
self.fileStat = None
else:
self.fileStat = None
self.watcherThread._DocumentStateChanged()
self._UpdateUIForState()
self._ApplyOptionalToViews("_UpdateUIForState")
self._ApplyOptionalToViews("SetReadOnly", self._IsReadOnly())
self._ApplyOptionalToViews("SCISetSavePoint")
# Allow the debugger to reset us too.
import pywin.debugger
if pywin.debugger.currentDebugger is not None:
pywin.debugger.currentDebugger.UpdateDocumentLineStates(self)
# Read-only document support - make it obvious to the user
# that the file is read-only.
def _IsReadOnly(self):
return self.fileStat is not None and (self.fileStat[0] & 128) == 0
def _UpdateUIForState(self):
"""Change the title to reflect the state of the document -
eg ReadOnly, Dirty, etc
"""
filename = self.GetPathName()
if not filename:
return # New file - nothing to do
try:
# This seems necessary so the internal state of the window becomes
# "visible". without it, it is still shown, but certain functions
# (such as updating the title) dont immediately work?
self.GetFirstView().ShowWindow(win32con.SW_SHOW)
title = win32ui.GetFileTitle(filename)
except win32ui.error:
title = filename
if self._IsReadOnly():
title = title + " (read-only)"
self.SetTitle(title)
def MakeDocumentWritable(self):
pretend_ss = 0 # Set to 1 to test this without source safe :-)
if not self.scModuleName and not pretend_ss: # No Source Control support.
win32ui.SetStatusText(
"Document is read-only, and no source-control system is configured"
)
win32api.MessageBeep()
return 0
# We have source control support - check if the user wants to use it.
msg = "Would you like to check this file out?"
defButton = win32con.MB_YESNO
if self.IsModified():
msg = msg + "\r\n\r\nALL CHANGES IN THE EDITOR WILL BE LOST"
defButton = win32con.MB_YESNO
if win32ui.MessageBox(msg, None, defButton) != win32con.IDYES:
return 0
if pretend_ss:
print("We are only pretending to check it out!")
win32api.SetFileAttributes(
self.GetPathName(), win32con.FILE_ATTRIBUTE_NORMAL
)
self.ReloadDocument()
return 1
# Now call on the module to do it.
if self.scModule is None:
try:
self.scModule = __import__(self.scModuleName)
for part in self.scModuleName.split(".")[1:]:
self.scModule = getattr(self.scModule, part)
except:
traceback.print_exc()
print("Error loading source control module.")
return 0
if self.scModule.CheckoutFile(self.GetPathName()):
self.ReloadDocument()
return 1
return 0
def CheckMakeDocumentWritable(self):
if self._IsReadOnly():
return self.MakeDocumentWritable()
return 1
def SaveModified(self):
# Called as the document is closed. If we are about
# to prompt for a save, bring the document to the foreground.
if self.IsModified():
frame = self.GetFirstView().GetParentFrame()
try:
frame.MDIActivate()
frame.AutoRestore()
except:
print("Could not bring document to foreground")
return self._obj_.SaveModified()
# NOTE - I DONT use the standard threading module,
# as this waits for all threads to terminate at shutdown.
# When using the debugger, it is possible shutdown will
# occur without Pythonwin getting a complete shutdown,
# so we deadlock at the end - threading is waiting for
import pywin.mfc.thread
import win32event
class FileWatchingThread(pywin.mfc.thread.WinThread):
def __init__(self, doc):
self.doc = doc
self.adminEvent = win32event.CreateEvent(None, 0, 0, None)
self.stopEvent = win32event.CreateEvent(None, 0, 0, None)
self.watchEvent = None
pywin.mfc.thread.WinThread.__init__(self)
def _DocumentStateChanged(self):
win32event.SetEvent(self.adminEvent)
def RefreshEvent(self):
self.hwnd = self.doc.GetFirstView().GetSafeHwnd()
if self.watchEvent is not None:
win32api.FindCloseChangeNotification(self.watchEvent)
self.watchEvent = None
path = self.doc.GetPathName()
if path:
path = os.path.dirname(path)
if path:
filter = (
win32con.FILE_NOTIFY_CHANGE_FILE_NAME
| win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES
| win32con.FILE_NOTIFY_CHANGE_LAST_WRITE
)
try:
self.watchEvent = win32api.FindFirstChangeNotification(path, 0, filter)
except win32api.error as exc:
print("Can not watch file", path, "for changes -", exc.strerror)
def SignalStop(self):
win32event.SetEvent(self.stopEvent)
def Run(self):
while 1:
handles = [self.stopEvent, self.adminEvent]
if self.watchEvent is not None:
handles.append(self.watchEvent)
rc = win32event.WaitForMultipleObjects(handles, 0, win32event.INFINITE)
if rc == win32event.WAIT_OBJECT_0:
break
elif rc == win32event.WAIT_OBJECT_0 + 1:
self.RefreshEvent()
else:
win32api.PostMessage(self.hwnd, MSG_CHECK_EXTERNAL_FILE, 0, 0)
try:
# If the directory has been removed underneath us, we get this error.
win32api.FindNextChangeNotification(self.watchEvent)
except win32api.error as exc:
print(
"Can not watch file",
self.doc.GetPathName(),
"for changes -",
exc.strerror,
)
break
# close a circular reference
self.doc = None
if self.watchEvent:
win32api.FindCloseChangeNotification(self.watchEvent)

View File

@ -0,0 +1,526 @@
#####################################################################
#
# editor.py
#
# A general purpose text editor, built on top of the win32ui edit
# type, which is built on an MFC CEditView
#
#
# We now support reloading of externally modified documented
# (eg, presumably by some other process, such as source control or
# another editor.
# We also suport auto-loading of externally modified files.
# - if the current document has not been modified in this
# editor, but has been modified on disk, then the file
# can be automatically reloaded.
#
# Note that it will _always_ prompt you if the file in the editor has been modified.
import win32ui
import win32api
import win32con
import regex
import re
import string
import sys, os
import traceback
from pywin.mfc import docview, dialog, afxres
from pywin.framework.editor import (
GetEditorOption,
SetEditorOption,
GetEditorFontOption,
SetEditorFontOption,
defaultCharacterFormat,
)
patImport = regex.symcomp("import \(<name>.*\)")
patIndent = regex.compile("^\\([ \t]*[~ \t]\\)")
ID_LOCATE_FILE = 0xE200
ID_GOTO_LINE = 0xE2001
MSG_CHECK_EXTERNAL_FILE = (
win32con.WM_USER + 1999
) ## WARNING: Duplicated in document.py and coloreditor.py
# Key Codes that modify the bufffer when Ctrl or Alt are NOT pressed.
MODIFYING_VK_KEYS = [
win32con.VK_BACK,
win32con.VK_TAB,
win32con.VK_RETURN,
win32con.VK_SPACE,
win32con.VK_DELETE,
]
for k in range(48, 91):
MODIFYING_VK_KEYS.append(k)
# Key Codes that modify the bufffer when Ctrl is pressed.
MODIFYING_VK_KEYS_CTRL = [
win32con.VK_BACK,
win32con.VK_RETURN,
win32con.VK_SPACE,
win32con.VK_DELETE,
]
# Key Codes that modify the bufffer when Alt is pressed.
MODIFYING_VK_KEYS_ALT = [
win32con.VK_BACK,
win32con.VK_RETURN,
win32con.VK_SPACE,
win32con.VK_DELETE,
]
# The editor itself starts here.
# Using the MFC Document/View model, we have an EditorDocument, which is responsible for
# managing the contents of the file, and a view which is responsible for rendering it.
#
# Due to a limitation in the Windows edit controls, we are limited to one view
# per document, although nothing in this code assumes this (I hope!)
isRichText = 1 # We are using the Rich Text control. This has not been tested with value "0" for quite some time!
# ParentEditorDocument=docview.Document
from .document import EditorDocumentBase
ParentEditorDocument = EditorDocumentBase
class EditorDocument(ParentEditorDocument):
#
# File loading and saving operations
#
def OnOpenDocument(self, filename):
#
# handle Unix and PC text file format.
#
# Get the "long name" of the file name, as it may have been translated
# to short names by the shell.
self.SetPathName(filename) # Must set this early!
# Now do the work!
self.BeginWaitCursor()
win32ui.SetStatusText("Loading file...", 1)
try:
f = open(filename, "rb")
except IOError:
win32ui.MessageBox(
filename
+ "\nCan not find this file\nPlease verify that the correct path and file name are given"
)
self.EndWaitCursor()
return 0
raw = f.read()
f.close()
contents = self.TranslateLoadedData(raw)
rc = 0
if win32ui.IsWin32s() and len(contents) > 62000: # give or take a few bytes
win32ui.MessageBox(
"This file is too big for Python on Windows 3.1\r\nPlease use another editor to view this file."
)
else:
try:
self.GetFirstView().SetWindowText(contents)
rc = 1
except TypeError: # Null byte in file.
win32ui.MessageBox(
"This file contains NULL bytes, and can not be edited"
)
rc = 0
self.EndWaitCursor()
self.SetModifiedFlag(0) # No longer dirty
self._DocumentStateChanged()
return rc
def TranslateLoadedData(self, data):
"""Given raw data read from a file, massage it suitable for the edit window"""
# if a CR in the first 250 chars, then perform the expensive translate
if data[:250].find("\r") == -1:
win32ui.SetStatusText(
"Translating from Unix file format - please wait...", 1
)
return re.sub("\r*\n", "\r\n", data)
else:
return data
def SaveFile(self, fileName, encoding=None):
if isRichText:
view = self.GetFirstView()
view.SaveTextFile(fileName, encoding=encoding)
else: # Old style edit view window.
self.GetFirstView().SaveFile(fileName)
try:
# Make sure line cache has updated info about me!
import linecache
linecache.checkcache()
except:
pass
#
# Color state stuff
#
def SetAllLineColors(self, color=None):
for view in self.GetAllViews():
view.SetAllLineColors(color)
def SetLineColor(self, lineNo, color):
"Color a line of all views"
for view in self.GetAllViews():
view.SetLineColor(lineNo, color)
# def StreamTextOut(self, data): ### This seems unreliable???
# self.saveFileHandle.write(data)
# return 1 # keep em coming!
# ParentEditorView=docview.EditView
ParentEditorView = docview.RichEditView
class EditorView(ParentEditorView):
def __init__(self, doc):
ParentEditorView.__init__(self, doc)
if isRichText:
self.SetWordWrap(win32ui.CRichEditView_WrapNone)
self.addToMRU = 1
self.HookHandlers()
self.bCheckingFile = 0
self.defCharFormat = GetEditorFontOption("Default Font", defaultCharacterFormat)
# Smart tabs override everything else if context can be worked out.
self.bSmartTabs = GetEditorOption("Smart Tabs", 1)
self.tabSize = GetEditorOption("Tab Size", 8)
self.indentSize = GetEditorOption("Indent Size", 8)
# If next indent is at a tab position, and useTabs is set, a tab will be inserted.
self.bUseTabs = GetEditorOption("Use Tabs", 1)
def OnInitialUpdate(self):
rc = self._obj_.OnInitialUpdate()
self.SetDefaultCharFormat(self.defCharFormat)
return rc
def CutCurLine(self):
curLine = self._obj_.LineFromChar()
nextLine = curLine + 1
start = self._obj_.LineIndex(curLine)
end = self._obj_.LineIndex(nextLine)
if end == 0: # must be last line.
end = start + self.end.GetLineLength(curLine)
self._obj_.SetSel(start, end)
self._obj_.Cut()
def _PrepareUserStateChange(self):
"Return selection, lineindex, etc info, so it can be restored"
self.SetRedraw(0)
return self.GetModify(), self.GetSel(), self.GetFirstVisibleLine()
def _EndUserStateChange(self, info):
scrollOff = info[2] - self.GetFirstVisibleLine()
if scrollOff:
self.LineScroll(scrollOff)
self.SetSel(info[1])
self.SetModify(info[0])
self.SetRedraw(1)
self.InvalidateRect()
self.UpdateWindow()
def _UpdateUIForState(self):
self.SetReadOnly(self.GetDocument()._IsReadOnly())
def SetAllLineColors(self, color=None):
if isRichText:
info = self._PrepareUserStateChange()
try:
if color is None:
color = self.defCharFormat[4]
self.SetSel(0, -1)
self.SetSelectionCharFormat((win32con.CFM_COLOR, 0, 0, 0, color))
finally:
self._EndUserStateChange(info)
def SetLineColor(self, lineNo, color):
"lineNo is the 1 based line number to set. If color is None, default color is used."
if isRichText:
info = self._PrepareUserStateChange()
try:
if color is None:
color = self.defCharFormat[4]
lineNo = lineNo - 1
startIndex = self.LineIndex(lineNo)
if startIndex != -1:
self.SetSel(startIndex, self.LineIndex(lineNo + 1))
self.SetSelectionCharFormat((win32con.CFM_COLOR, 0, 0, 0, color))
finally:
self._EndUserStateChange(info)
def Indent(self):
"""Insert an indent to move the cursor to the next tab position.
Honors the tab size and 'use tabs' settings. Assumes the cursor is already at the
position to be indented, and the selection is a single character (ie, not a block)
"""
start, end = self._obj_.GetSel()
startLine = self._obj_.LineFromChar(start)
line = self._obj_.GetLine(startLine)
realCol = start - self._obj_.LineIndex(startLine)
# Calulate the next tab stop.
# Expand existing tabs.
curCol = 0
for ch in line[:realCol]:
if ch == "\t":
curCol = ((curCol / self.tabSize) + 1) * self.tabSize
else:
curCol = curCol + 1
nextColumn = ((curCol / self.indentSize) + 1) * self.indentSize
# print "curCol is", curCol, "nextColumn is", nextColumn
ins = None
if self.bSmartTabs:
# Look for some context.
if realCol == 0: # Start of the line - see if the line above can tell us
lookLine = startLine - 1
while lookLine >= 0:
check = self._obj_.GetLine(lookLine)[0:1]
if check in ["\t", " "]:
ins = check
break
lookLine = lookLine - 1
else: # See if the previous char can tell us
check = line[realCol - 1]
if check in ["\t", " "]:
ins = check
# Either smart tabs off, or not smart enough!
# Use the "old style" settings.
if ins is None:
if self.bUseTabs and nextColumn % self.tabSize == 0:
ins = "\t"
else:
ins = " "
if ins == " ":
# Calc the number of spaces to take us to the next stop
ins = ins * (nextColumn - curCol)
self._obj_.ReplaceSel(ins)
def BlockDent(self, isIndent, startLine, endLine):
"Indent/Undent all lines specified"
if not self.GetDocument().CheckMakeDocumentWritable():
return 0
tabSize = self.tabSize # hard-code for now!
info = self._PrepareUserStateChange()
try:
for lineNo in range(startLine, endLine):
pos = self._obj_.LineIndex(lineNo)
self._obj_.SetSel(pos, pos)
if isIndent:
self.Indent()
else:
line = self._obj_.GetLine(lineNo)
try:
noToDel = 0
if line[0] == "\t":
noToDel = 1
elif line[0] == " ":
for noToDel in range(0, tabSize):
if line[noToDel] != " ":
break
else:
noToDel = tabSize
if noToDel:
self._obj_.SetSel(pos, pos + noToDel)
self._obj_.Clear()
except IndexError:
pass
finally:
self._EndUserStateChange(info)
self.GetDocument().SetModifiedFlag(1) # Now dirty
self._obj_.SetSel(self.LineIndex(startLine), self.LineIndex(endLine))
def GotoLine(self, lineNo=None):
try:
if lineNo is None:
lineNo = int(input("Enter Line Number"))
except (ValueError, KeyboardInterrupt):
return 0
self.GetLineCount() # Seems to be needed when file first opened???
charNo = self.LineIndex(lineNo - 1)
self.SetSel(charNo)
def HookHandlers(self): # children can override, but should still call me!
# self.HookAllKeyStrokes(self.OnKey)
self.HookMessage(self.OnCheckExternalDocumentUpdated, MSG_CHECK_EXTERNAL_FILE)
self.HookMessage(self.OnRClick, win32con.WM_RBUTTONDOWN)
self.HookMessage(self.OnSetFocus, win32con.WM_SETFOCUS)
self.HookMessage(self.OnKeyDown, win32con.WM_KEYDOWN)
self.HookKeyStroke(self.OnKeyCtrlY, 25) # ^Y
self.HookKeyStroke(self.OnKeyCtrlG, 7) # ^G
self.HookKeyStroke(self.OnKeyTab, 9) # TAB
self.HookKeyStroke(self.OnKeyEnter, 13) # Enter
self.HookCommand(self.OnCmdLocateFile, ID_LOCATE_FILE)
self.HookCommand(self.OnCmdGotoLine, ID_GOTO_LINE)
self.HookCommand(self.OnEditPaste, afxres.ID_EDIT_PASTE)
self.HookCommand(self.OnEditCut, afxres.ID_EDIT_CUT)
# Hook Handlers
def OnSetFocus(self, msg):
# Even though we use file change notifications, we should be very sure about it here.
self.OnCheckExternalDocumentUpdated(msg)
def OnRClick(self, params):
menu = win32ui.CreatePopupMenu()
# look for a module name
line = self._obj_.GetLine().strip()
flags = win32con.MF_STRING | win32con.MF_ENABLED
if patImport.match(line) == len(line):
menu.AppendMenu(
flags, ID_LOCATE_FILE, "&Locate %s.py" % patImport.group("name")
)
menu.AppendMenu(win32con.MF_SEPARATOR)
menu.AppendMenu(flags, win32ui.ID_EDIT_UNDO, "&Undo")
menu.AppendMenu(win32con.MF_SEPARATOR)
menu.AppendMenu(flags, win32ui.ID_EDIT_CUT, "Cu&t")
menu.AppendMenu(flags, win32ui.ID_EDIT_COPY, "&Copy")
menu.AppendMenu(flags, win32ui.ID_EDIT_PASTE, "&Paste")
menu.AppendMenu(flags, win32con.MF_SEPARATOR)
menu.AppendMenu(flags, win32ui.ID_EDIT_SELECT_ALL, "&Select all")
menu.AppendMenu(flags, win32con.MF_SEPARATOR)
menu.AppendMenu(flags, ID_GOTO_LINE, "&Goto line...")
menu.TrackPopupMenu(params[5])
return 0
def OnCmdGotoLine(self, cmd, code):
self.GotoLine()
return 0
def OnCmdLocateFile(self, cmd, code):
modName = patImport.group("name")
if not modName:
return 0
import pywin.framework.scriptutils
fileName = pywin.framework.scriptutils.LocatePythonFile(modName)
if fileName is None:
win32ui.SetStatusText("Can't locate module %s" % modName)
else:
win32ui.GetApp().OpenDocumentFile(fileName)
return 0
# Key handlers
def OnKeyEnter(self, key):
if not self.GetDocument().CheckMakeDocumentWritable():
return 0
curLine = self._obj_.GetLine()
self._obj_.ReplaceSel("\r\n") # insert the newline
# If the current line indicates the next should be indented,
# then copy the current indentation to this line.
res = patIndent.match(curLine, 0)
if res > 0 and curLine.strip():
curIndent = patIndent.group(1)
self._obj_.ReplaceSel(curIndent)
return 0 # dont pass on
def OnKeyCtrlY(self, key):
if not self.GetDocument().CheckMakeDocumentWritable():
return 0
self.CutCurLine()
return 0 # dont let him have it!
def OnKeyCtrlG(self, key):
self.GotoLine()
return 0 # dont let him have it!
def OnKeyTab(self, key):
if not self.GetDocument().CheckMakeDocumentWritable():
return 0
start, end = self._obj_.GetSel()
if start == end: # normal TAB key
self.Indent()
return 0 # we handled this.
# Otherwise it is a block indent/dedent.
if start > end:
start, end = end, start # swap them.
startLine = self._obj_.LineFromChar(start)
endLine = self._obj_.LineFromChar(end)
self.BlockDent(win32api.GetKeyState(win32con.VK_SHIFT) >= 0, startLine, endLine)
return 0
def OnEditPaste(self, id, code):
# Return 1 if we can make the file editable.(or it already is!)
return self.GetDocument().CheckMakeDocumentWritable()
def OnEditCut(self, id, code):
# Return 1 if we can make the file editable.(or it already is!)
return self.GetDocument().CheckMakeDocumentWritable()
def OnKeyDown(self, msg):
key = msg[2]
if win32api.GetKeyState(win32con.VK_CONTROL) & 0x8000:
modList = MODIFYING_VK_KEYS_CTRL
elif win32api.GetKeyState(win32con.VK_MENU) & 0x8000:
modList = MODIFYING_VK_KEYS_ALT
else:
modList = MODIFYING_VK_KEYS
if key in modList:
# Return 1 if we can make the file editable.(or it already is!)
return self.GetDocument().CheckMakeDocumentWritable()
return 1 # Pass it on OK
# def OnKey(self, key):
# return self.GetDocument().CheckMakeDocumentWritable()
def OnCheckExternalDocumentUpdated(self, msg):
if self._obj_ is None or self.bCheckingFile:
return
self.bCheckingFile = 1
self.GetDocument().CheckExternalDocumentUpdated()
self.bCheckingFile = 0
from .template import EditorTemplateBase
class EditorTemplate(EditorTemplateBase):
def __init__(
self, res=win32ui.IDR_TEXTTYPE, makeDoc=None, makeFrame=None, makeView=None
):
if makeDoc is None:
makeDoc = EditorDocument
if makeView is None:
makeView = EditorView
EditorTemplateBase.__init__(self, res, makeDoc, makeFrame, makeView)
def _CreateDocTemplate(self, resourceId):
return win32ui.CreateRichEditDocTemplate(resourceId)
def CreateWin32uiDocument(self):
return self.DoCreateRichEditDoc()
def Create(fileName=None, title=None, template=None):
return editorTemplate.OpenDocumentFile(fileName)
from pywin.framework.editor import GetDefaultEditorModuleName
prefModule = GetDefaultEditorModuleName()
# Initialize only if this is the "default" editor.
if __name__ == prefModule:
# For debugging purposes, when this module may be reloaded many times.
try:
win32ui.GetApp().RemoveDocTemplate(editorTemplate)
except (NameError, win32ui.error):
pass
editorTemplate = EditorTemplate()
win32ui.GetApp().AddDocTemplate(editorTemplate)

View File

@ -0,0 +1,76 @@
# frame.py - The MDI frame window for an editor.
import pywin.framework.window
import win32ui
import win32con
import afxres
from . import ModuleBrowser
class EditorFrame(pywin.framework.window.MDIChildWnd):
def OnCreateClient(self, cp, context):
# Create the default view as specified by the template (ie, the editor view)
view = context.template.MakeView(context.doc)
# Create the browser view.
browserView = ModuleBrowser.BrowserView(context.doc)
view2 = context.template.MakeView(context.doc)
splitter = win32ui.CreateSplitter()
style = win32con.WS_CHILD | win32con.WS_VISIBLE
splitter.CreateStatic(self, 1, 2, style, win32ui.AFX_IDW_PANE_FIRST)
sub_splitter = self.sub_splitter = win32ui.CreateSplitter()
sub_splitter.CreateStatic(splitter, 2, 1, style, win32ui.AFX_IDW_PANE_FIRST + 1)
# Note we must add the default view first, so that doc.GetFirstView() returns the editor view.
sub_splitter.CreateView(view, 1, 0, (0, 0))
splitter.CreateView(browserView, 0, 0, (0, 0))
sub_splitter.CreateView(view2, 0, 0, (0, 0))
## print "First view is", context.doc.GetFirstView()
## print "Views are", view, view2, browserView
## print "Parents are", view.GetParent(), view2.GetParent(), browserView.GetParent()
## print "Splitter is", splitter
## print "sub splitter is", sub_splitter
## Old
## splitter.CreateStatic (self, 1, 2)
## splitter.CreateView(view, 0, 1, (0,0)) # size ignored.
## splitter.CreateView (browserView, 0, 0, (0, 0))
# Restrict the size of the browser splitter (and we can avoid filling
# it until it is shown)
splitter.SetColumnInfo(0, 10, 20)
# And the active view is our default view (so it gets initial focus)
self.SetActiveView(view)
def GetEditorView(self):
# In a multi-view (eg, splitter) environment, get
# an editor (ie, scintilla) view
# Look for the splitter opened the most!
if self.sub_splitter is None:
return self.GetDlgItem(win32ui.AFX_IDW_PANE_FIRST)
v1 = self.sub_splitter.GetPane(0, 0)
v2 = self.sub_splitter.GetPane(1, 0)
r1 = v1.GetWindowRect()
r2 = v2.GetWindowRect()
if r1[3] - r1[1] > r2[3] - r2[1]:
return v1
return v2
def GetBrowserView(self):
# XXX - should fix this :-)
return self.GetActiveDocument().GetAllViews()[1]
def OnClose(self):
doc = self.GetActiveDocument()
if not doc.SaveModified():
## Cancel button selected from Save dialog, do not actually close
## print 'close cancelled'
return 0
## So the 'Save' dialog doesn't come up twice
doc._obj_.SetModifiedFlag(False)
# Must force the module browser to close itself here (OnDestroy for the view itself is too late!)
self.sub_splitter = None # ensure no circles!
self.GetBrowserView().DestroyBrowser()
return self._obj_.OnClose()

View File

@ -0,0 +1,59 @@
import string
import win32ui
import win32api
from pywin.mfc import docview
import pywin.framework.window
import os
from . import frame
ParentEditorTemplate = docview.DocTemplate
class EditorTemplateBase(ParentEditorTemplate):
def __init__(
self, res=win32ui.IDR_TEXTTYPE, makeDoc=None, makeFrame=None, makeView=None
):
if makeFrame is None:
makeFrame = frame.EditorFrame
ParentEditorTemplate.__init__(self, res, makeDoc, makeFrame, makeView)
def _CreateDocTemplate(self, resourceId):
assert 0, "You must override this"
def CreateWin32uiDocument(self):
assert 0, "You must override this"
def GetFileExtensions(self):
return ".txt", ".py"
def MatchDocType(self, fileName, fileType):
doc = self.FindOpenDocument(fileName)
if doc:
return doc
ext = os.path.splitext(fileName)[1].lower()
if ext in self.GetFileExtensions():
return win32ui.CDocTemplate_Confidence_yesAttemptNative
return win32ui.CDocTemplate_Confidence_maybeAttemptForeign
def InitialUpdateFrame(self, frame, doc, makeVisible=1):
self._obj_.InitialUpdateFrame(frame, doc, makeVisible) # call default handler.
doc._UpdateUIForState()
def GetPythonPropertyPages(self):
"""Returns a list of property pages"""
from . import configui
return [configui.EditorPropertyPage(), configui.EditorWhitespacePropertyPage()]
def OpenDocumentFile(self, filename, bMakeVisible=1):
if filename is not None:
try:
path = os.path.split(filename)[0]
# print "The editor is translating", `filename`,"to",
filename = win32api.FindFiles(filename)[0][8]
filename = os.path.join(path, filename)
# print `filename`
except (win32api.error, IndexError) as details:
pass
# print "Couldnt get the full filename!", details
return self._obj_.OpenDocumentFile(filename, bMakeVisible)

View File

@ -0,0 +1,100 @@
# vss.py -- Source Control using Microsoft VSS.
# Provides routines for checking files out of VSS.
#
# Uses an INI file very similar to how VB integrates with VSS - even
# as far as using the same name.
# The file must be named "Mssccprj.scc", and be in the format of
# an INI file. This file may be in a parent directory, in which
# case the project name will be built from what is specified in the
# ini file, plus the path from the INI file to the file itself.
#
# The INI file should have a [Python] section, and a
# Project=Project Name
# and optionally
# Database=??
import win32ui, win32api, win32con, os, string, sys
import traceback
g_iniName = "Mssccprj.scc" # Use the same INI name as VB!
g_sourceSafe = None
def FindVssProjectInfo(fullfname):
"""Looks up the file system for an INI file describing the project.
Looking up the tree is for ni style packages.
Returns (projectName, pathToFileName) where pathToFileName contains
the path from the ini file to the actual file.
"""
path, fnameonly = os.path.split(fullfname)
origPath = path
project = ""
retPaths = [fnameonly]
while not project:
iniName = os.path.join(path, g_iniName)
database = win32api.GetProfileVal("Python", "Database", "", iniName)
project = win32api.GetProfileVal("Python", "Project", "", iniName)
if project:
break
# No valid INI file in this directory - look up a level.
path, addpath = os.path.split(path)
if not addpath: # Root?
break
retPaths.insert(0, addpath)
if not project:
win32ui.MessageBox(
"%s\r\n\r\nThis directory is not configured for Python/VSS" % origPath
)
return
return project, "/".join(retPaths), database
def CheckoutFile(fileName):
global g_sourceSafe
import pythoncom
ok = 0
# Assumes the fileName has a complete path,
# and that the INI file can be found in that path
# (or a parent path if a ni style package)
try:
import win32com.client, win32com.client.gencache
mod = win32com.client.gencache.EnsureModule(
"{783CD4E0-9D54-11CF-B8EE-00608CC9A71F}", 0, 5, 0
)
if mod is None:
win32ui.MessageBox(
"VSS does not appear to be installed. The TypeInfo can not be created"
)
return ok
rc = FindVssProjectInfo(fileName)
if rc is None:
return
project, vssFname, database = rc
if g_sourceSafe is None:
g_sourceSafe = win32com.client.Dispatch("SourceSafe")
# SS seems a bit wierd. It defaults the arguments as empty strings, but
# then complains when they are used - so we pass "Missing"
if not database:
database = pythoncom.Missing
g_sourceSafe.Open(database, pythoncom.Missing, pythoncom.Missing)
item = g_sourceSafe.VSSItem("$/%s/%s" % (project, vssFname))
item.Checkout(None, fileName)
ok = 1
except pythoncom.com_error as exc:
win32ui.MessageBox(exc.strerror, "Error checking out file")
except:
typ, val, tb = sys.exc_info()
traceback.print_exc()
win32ui.MessageBox("%s - %s" % (str(typ), str(val)), "Error checking out file")
tb = None # Cleanup a cycle
return ok

View File

@ -0,0 +1,174 @@
# help.py - help utilities for PythonWin.
import win32api
import win32con
import win32ui
import string
import sys
import regutil
import string, os
htmlhelp_handle = None
html_help_command_translators = {
win32con.HELP_CONTENTS: 1, # HH_DISPLAY_TOC
win32con.HELP_CONTEXT: 15, # HH_HELP_CONTEXT
win32con.HELP_FINDER: 1, # HH_DISPLAY_TOC
}
def FinalizeHelp():
global htmlhelp_handle
if htmlhelp_handle is not None:
import win32help
try:
# frame = win32ui.GetMainFrame().GetSafeHwnd()
frame = 0
win32help.HtmlHelp(frame, None, win32help.HH_UNINITIALIZE, htmlhelp_handle)
except win32help.error:
print("Failed to finalize htmlhelp!")
htmlhelp_handle = None
def OpenHelpFile(fileName, helpCmd=None, helpArg=None):
"Open a help file, given a full path"
# default help arg.
win32ui.DoWaitCursor(1)
try:
if helpCmd is None:
helpCmd = win32con.HELP_CONTENTS
ext = os.path.splitext(fileName)[1].lower()
if ext == ".hlp":
win32api.WinHelp(
win32ui.GetMainFrame().GetSafeHwnd(), fileName, helpCmd, helpArg
)
# XXX - using the htmlhelp API wreaks havoc with keyboard shortcuts
# so we disable it, forcing ShellExecute, which works fine (but
# doesn't close the help file when Pythonwin is closed.
# Tom Heller also points out http://www.microsoft.com/mind/0499/faq/faq0499.asp,
# which may or may not be related.
elif 0 and ext == ".chm":
import win32help
global htmlhelp_handle
helpCmd = html_help_command_translators.get(helpCmd, helpCmd)
# frame = win32ui.GetMainFrame().GetSafeHwnd()
frame = 0 # Dont want it overlapping ours!
if htmlhelp_handle is None:
htmlhelp_hwnd, htmlhelp_handle = win32help.HtmlHelp(
frame, None, win32help.HH_INITIALIZE
)
win32help.HtmlHelp(frame, fileName, helpCmd, helpArg)
else:
# Hope that the extension is registered, and we know what to do!
win32api.ShellExecute(0, "open", fileName, None, "", win32con.SW_SHOW)
return fileName
finally:
win32ui.DoWaitCursor(-1)
def ListAllHelpFiles():
ret = []
ret = _ListAllHelpFilesInRoot(win32con.HKEY_LOCAL_MACHINE)
# Ensure we don't get dups.
for item in _ListAllHelpFilesInRoot(win32con.HKEY_CURRENT_USER):
if item not in ret:
ret.append(item)
return ret
def _ListAllHelpFilesInRoot(root):
"""Returns a list of (helpDesc, helpFname) for all registered help files"""
import regutil
retList = []
try:
key = win32api.RegOpenKey(
root, regutil.BuildDefaultPythonKey() + "\\Help", 0, win32con.KEY_READ
)
except win32api.error as exc:
import winerror
if exc.winerror != winerror.ERROR_FILE_NOT_FOUND:
raise
return retList
try:
keyNo = 0
while 1:
try:
helpDesc = win32api.RegEnumKey(key, keyNo)
helpFile = win32api.RegQueryValue(key, helpDesc)
retList.append((helpDesc, helpFile))
keyNo = keyNo + 1
except win32api.error as exc:
import winerror
if exc.winerror != winerror.ERROR_NO_MORE_ITEMS:
raise
break
finally:
win32api.RegCloseKey(key)
return retList
def SelectAndRunHelpFile():
from pywin.dialogs import list
helpFiles = ListAllHelpFiles()
if len(helpFiles) == 1:
# only 1 help file registered - probably ours - no point asking
index = 0
else:
index = list.SelectFromLists("Select Help file", helpFiles, ["Title"])
if index is not None:
OpenHelpFile(helpFiles[index][1])
helpIDMap = None
def SetHelpMenuOtherHelp(mainMenu):
"""Modifies the main Help Menu to handle all registered help files.
mainMenu -- The main menu to modify - usually from docTemplate.GetSharedMenu()
"""
# Load all help files from the registry.
global helpIDMap
if helpIDMap is None:
helpIDMap = {}
cmdID = win32ui.ID_HELP_OTHER
excludeList = ["Main Python Documentation", "Pythonwin Reference"]
firstList = ListAllHelpFiles()
# We actually want to not only exclude these entries, but
# their help file names (as many entries may share the same name)
excludeFnames = []
for desc, fname in firstList:
if desc in excludeList:
excludeFnames.append(fname)
helpDescs = []
for desc, fname in firstList:
if fname not in excludeFnames:
helpIDMap[cmdID] = (desc, fname)
win32ui.GetMainFrame().HookCommand(HandleHelpOtherCommand, cmdID)
cmdID = cmdID + 1
helpMenu = mainMenu.GetSubMenu(
mainMenu.GetMenuItemCount() - 1
) # Help menu always last.
otherHelpMenuPos = 2 # cant search for ID, as sub-menu has no ID.
otherMenu = helpMenu.GetSubMenu(otherHelpMenuPos)
while otherMenu.GetMenuItemCount():
otherMenu.DeleteMenu(0, win32con.MF_BYPOSITION)
if helpIDMap:
for id, (desc, fname) in helpIDMap.items():
otherMenu.AppendMenu(win32con.MF_ENABLED | win32con.MF_STRING, id, desc)
else:
helpMenu.EnableMenuItem(
otherHelpMenuPos, win32con.MF_BYPOSITION | win32con.MF_GRAYED
)
def HandleHelpOtherCommand(cmd, code):
OpenHelpFile(helpIDMap[cmd][1])

View File

@ -0,0 +1,985 @@
##################################################################
##
## Interactive Shell Window
##
import sys, os
import code
import string
import win32ui
import win32api
import win32clipboard
import win32con
import traceback
import afxres
import array
import __main__
import pywin.scintilla.formatter
import pywin.scintilla.control
import pywin.scintilla.IDLEenvironment
import pywin.framework.app
## sequential after ID_GOTO_LINE defined in editor.py
ID_EDIT_COPY_CODE = 0xE2002
ID_EDIT_EXEC_CLIPBOARD = 0x2003
trace = pywin.scintilla.formatter.trace
from . import winout
import re
# from IDLE.
_is_block_opener = re.compile(r":\s*(#.*)?$").search
_is_block_closer = re.compile(
r"""
\s*
( return
| break
| continue
| raise
| pass
)
\b
""",
re.VERBOSE,
).match
tracebackHeader = "Traceback (".encode("ascii")
sectionProfile = "Interactive Window"
valueFormatTitle = "FormatTitle"
valueFormatInput = "FormatInput"
valueFormatOutput = "FormatOutput"
valueFormatOutputError = "FormatOutputError"
# These are defaults only. Values are read from the registry.
formatTitle = (-536870897, 0, 220, 0, 16711680, 184, 34, "Arial")
formatInput = (-402653169, 0, 200, 0, 0, 0, 49, "Courier New")
formatOutput = (-402653169, 0, 200, 0, 8421376, 0, 49, "Courier New")
formatOutputError = (-402653169, 0, 200, 0, 255, 0, 49, "Courier New")
try:
sys.ps1
except AttributeError:
sys.ps1 = ">>> "
sys.ps2 = "... "
def LoadPreference(preference, default=""):
return win32ui.GetProfileVal(sectionProfile, preference, default)
def SavePreference(prefName, prefValue):
win32ui.WriteProfileVal(sectionProfile, prefName, prefValue)
def GetPromptPrefix(line):
ps1 = sys.ps1
if line[: len(ps1)] == ps1:
return ps1
ps2 = sys.ps2
if line[: len(ps2)] == ps2:
return ps2
#############################################################
#
# Colorizer related code.
#
#############################################################
STYLE_INTERACTIVE_EOL = "Interactive EOL"
STYLE_INTERACTIVE_OUTPUT = "Interactive Output"
STYLE_INTERACTIVE_PROMPT = "Interactive Prompt"
STYLE_INTERACTIVE_BANNER = "Interactive Banner"
STYLE_INTERACTIVE_ERROR = "Interactive Error"
STYLE_INTERACTIVE_ERROR_FINALLINE = "Interactive Error (final line)"
INTERACTIVE_STYLES = [
STYLE_INTERACTIVE_EOL,
STYLE_INTERACTIVE_OUTPUT,
STYLE_INTERACTIVE_PROMPT,
STYLE_INTERACTIVE_BANNER,
STYLE_INTERACTIVE_ERROR,
STYLE_INTERACTIVE_ERROR_FINALLINE,
]
FormatterParent = pywin.scintilla.formatter.PythonSourceFormatter
class InteractiveFormatter(FormatterParent):
def __init__(self, scintilla):
FormatterParent.__init__(self, scintilla)
self.bannerDisplayed = False
def SetStyles(self):
FormatterParent.SetStyles(self)
Style = pywin.scintilla.formatter.Style
self.RegisterStyle(Style(STYLE_INTERACTIVE_EOL, STYLE_INTERACTIVE_PROMPT))
self.RegisterStyle(Style(STYLE_INTERACTIVE_PROMPT, formatInput))
self.RegisterStyle(Style(STYLE_INTERACTIVE_OUTPUT, formatOutput))
self.RegisterStyle(Style(STYLE_INTERACTIVE_BANNER, formatTitle))
self.RegisterStyle(Style(STYLE_INTERACTIVE_ERROR, formatOutputError))
self.RegisterStyle(
Style(STYLE_INTERACTIVE_ERROR_FINALLINE, STYLE_INTERACTIVE_ERROR)
)
def LoadPreference(self, name, default):
rc = win32ui.GetProfileVal("Format", name, default)
if rc == default:
rc = win32ui.GetProfileVal(sectionProfile, name, default)
return rc
def ColorizeInteractiveCode(self, cdoc, styleStart, stylePyStart):
lengthDoc = len(cdoc)
if lengthDoc == 0:
return
state = styleStart
# As per comments in Colorize(), we work with the raw utf8
# bytes. To avoid too muych py3k pain, we treat each utf8 byte
# as a latin-1 unicode character - we only use it to compare
# against ascii chars anyway...
chNext = cdoc[0:1].decode("latin-1")
startSeg = 0
i = 0
lastState = state # debug only
while i < lengthDoc:
ch = chNext
chNext = cdoc[i + 1 : i + 2].decode("latin-1")
# trace("ch=%r, i=%d, next=%r, state=%s" % (ch, i, chNext, state))
if state == STYLE_INTERACTIVE_EOL:
if ch not in "\r\n":
self.ColorSeg(startSeg, i - 1, state)
startSeg = i
if ch in [sys.ps1[0], sys.ps2[0]]:
state = STYLE_INTERACTIVE_PROMPT
elif cdoc[i : i + len(tracebackHeader)] == tracebackHeader:
state = STYLE_INTERACTIVE_ERROR
else:
state = STYLE_INTERACTIVE_OUTPUT
elif state == STYLE_INTERACTIVE_PROMPT:
if ch not in sys.ps1 + sys.ps2 + " ":
self.ColorSeg(startSeg, i - 1, state)
startSeg = i
if ch in "\r\n":
state = STYLE_INTERACTIVE_EOL
else:
state = stylePyStart # Start coloring Python code.
elif state in [STYLE_INTERACTIVE_OUTPUT]:
if ch in "\r\n":
self.ColorSeg(startSeg, i - 1, state)
startSeg = i
state = STYLE_INTERACTIVE_EOL
elif state == STYLE_INTERACTIVE_ERROR:
if ch in "\r\n" and chNext and chNext not in string.whitespace:
# Everything including me
self.ColorSeg(startSeg, i, state)
startSeg = i + 1
state = STYLE_INTERACTIVE_ERROR_FINALLINE
elif i == 0 and ch not in string.whitespace:
# If we are coloring from the start of a line,
# we need this better check for the last line
# Color up to not including me
self.ColorSeg(startSeg, i - 1, state)
startSeg = i
state = STYLE_INTERACTIVE_ERROR_FINALLINE
elif state == STYLE_INTERACTIVE_ERROR_FINALLINE:
if ch in "\r\n":
self.ColorSeg(startSeg, i - 1, state)
startSeg = i
state = STYLE_INTERACTIVE_EOL
elif state == STYLE_INTERACTIVE_BANNER:
if ch in "\r\n" and (chNext == "" or chNext in ">["):
# Everything including me
self.ColorSeg(startSeg, i - 1, state)
startSeg = i
state = STYLE_INTERACTIVE_EOL
else:
# It is a PythonColorizer state - seek past the end of the line
# and ask the Python colorizer to color that.
end = startSeg
while end < lengthDoc and cdoc[end] not in "\r\n".encode("ascii"):
end = end + 1
self.ColorizePythonCode(cdoc[:end], startSeg, state)
stylePyStart = self.GetStringStyle(end - 1)
if stylePyStart is None:
stylePyStart = pywin.scintilla.formatter.STYLE_DEFAULT
else:
stylePyStart = stylePyStart.name
startSeg = end
i = end - 1 # ready for increment.
chNext = cdoc[end : end + 1].decode("latin-1")
state = STYLE_INTERACTIVE_EOL
if lastState != state:
lastState = state
i = i + 1
# and the rest
if startSeg < i:
self.ColorSeg(startSeg, i - 1, state)
def Colorize(self, start=0, end=-1):
# scintilla's formatting is all done in terms of utf, so
# we work with utf8 bytes instead of unicode. This magically
# works as any extended chars found in the utf8 don't change
# the semantics.
stringVal = self.scintilla.GetTextRange(start, end, decode=False)
styleStart = None
stylePyStart = None
if start > 1:
# Likely we are being asked to color from the start of the line.
# We find the last formatted character on the previous line.
# If TQString, we continue it. Otherwise, we reset.
look = start - 1
while look and self.scintilla.SCIGetCharAt(look) in "\n\r":
look = look - 1
if look and look < start - 1: # Did we find a char before the \n\r sets?
strstyle = self.GetStringStyle(look)
quote_char = None
if strstyle is not None:
if strstyle.name == pywin.scintilla.formatter.STYLE_TQSSTRING:
quote_char = "'"
elif strstyle.name == pywin.scintilla.formatter.STYLE_TQDSTRING:
quote_char = '"'
if quote_char is not None:
# It is a TQS. If the TQS is not terminated, we
# carry the style through.
if look > 2:
look_str = (
self.scintilla.SCIGetCharAt(look - 2)
+ self.scintilla.SCIGetCharAt(look - 1)
+ self.scintilla.SCIGetCharAt(look)
)
if look_str != quote_char * 3:
stylePyStart = strstyle.name
if stylePyStart is None:
stylePyStart = pywin.scintilla.formatter.STYLE_DEFAULT
if start > 0:
stylenum = self.scintilla.SCIGetStyleAt(start - 1)
styleStart = self.GetStyleByNum(stylenum).name
elif self.bannerDisplayed:
styleStart = STYLE_INTERACTIVE_EOL
else:
styleStart = STYLE_INTERACTIVE_BANNER
self.bannerDisplayed = True
self.scintilla.SCIStartStyling(start, 31)
self.style_buffer = array.array("b", (0,) * len(stringVal))
self.ColorizeInteractiveCode(stringVal, styleStart, stylePyStart)
self.scintilla.SCISetStylingEx(self.style_buffer)
self.style_buffer = None
###############################################################
#
# This class handles the Python interactive interpreter.
#
# It uses a basic EditWindow, and does all the magic.
# This is triggered by the enter key hander attached by the
# start-up code. It determines if a command is to be executed
# or continued (ie, emit "... ") by snooping around the current
# line, looking for the prompts
#
class PythonwinInteractiveInterpreter(code.InteractiveInterpreter):
def __init__(self, locals=None, globals=None):
if locals is None:
locals = __main__.__dict__
if globals is None:
globals = locals
self.globals = globals
code.InteractiveInterpreter.__init__(self, locals)
def showsyntaxerror(self, filename=None):
sys.stderr.write(
tracebackHeader.decode("ascii")
) # So the color syntaxer recognises it.
code.InteractiveInterpreter.showsyntaxerror(self, filename)
def runcode(self, code):
try:
exec(code, self.globals, self.locals)
except SystemExit:
raise
except:
self.showtraceback()
class InteractiveCore:
def __init__(self, banner=None):
self.banner = banner
# LoadFontPreferences()
def Init(self):
self.oldStdOut = self.oldStdErr = None
# self.SetWordWrap(win32ui.CRichEditView_WrapNone)
self.interp = PythonwinInteractiveInterpreter()
self.OutputGrab() # Release at cleanup.
if self.GetTextLength() == 0:
if self.banner is None:
suffix = ""
if win32ui.debug:
suffix = ", debug build"
sys.stderr.write(
"PythonWin %s on %s%s.\n" % (sys.version, sys.platform, suffix)
)
sys.stderr.write(
"Portions %s - see 'Help/About PythonWin' for further copyright information.\n"
% (win32ui.copyright,)
)
else:
sys.stderr.write(banner)
rcfile = os.environ.get("PYTHONSTARTUP")
if rcfile:
import __main__
try:
exec(
compile(
open(rcfile, "rb").read(), rcfile, "exec", dont_inherit=True
),
__main__.__dict__,
__main__.__dict__,
)
except:
sys.stderr.write(
">>> \nError executing PYTHONSTARTUP script %r\n" % (rcfile)
)
traceback.print_exc(file=sys.stderr)
self.AppendToPrompt([])
def SetContext(self, globals, locals, name="Dbg"):
oldPrompt = sys.ps1
if globals is None:
# Reset
sys.ps1 = ">>> "
sys.ps2 = "... "
locals = globals = __main__.__dict__
else:
sys.ps1 = "[%s]>>> " % name
sys.ps2 = "[%s]... " % name
self.interp.locals = locals
self.interp.globals = globals
self.AppendToPrompt([], oldPrompt)
def GetContext(self):
return self.interp.globals, self.interp.locals
def DoGetLine(self, line=-1):
if line == -1:
line = self.LineFromChar()
line = self.GetLine(line)
while line and line[-1] in ["\r", "\n"]:
line = line[:-1]
return line
def AppendToPrompt(self, bufLines, oldPrompt=None):
"Take a command and stick it at the end of the buffer (with python prompts inserted if required)."
self.flush()
lastLineNo = self.GetLineCount() - 1
line = self.DoGetLine(lastLineNo)
if oldPrompt and line == oldPrompt:
self.SetSel(self.GetTextLength() - len(oldPrompt), self.GetTextLength())
self.ReplaceSel(sys.ps1)
elif line != str(sys.ps1):
if len(line) != 0:
self.write("\n")
self.write(sys.ps1)
self.flush()
self.idle.text.mark_set("iomark", "end-1c")
if not bufLines:
return
terms = (["\n" + sys.ps2] * (len(bufLines) - 1)) + [""]
for bufLine, term in zip(bufLines, terms):
if bufLine.strip():
self.write(bufLine + term)
self.flush()
def EnsureNoPrompt(self):
# Get ready to write some text NOT at a Python prompt.
self.flush()
lastLineNo = self.GetLineCount() - 1
line = self.DoGetLine(lastLineNo)
if not line or line in [sys.ps1, sys.ps2]:
self.SetSel(self.GetTextLength() - len(line), self.GetTextLength())
self.ReplaceSel("")
else:
# Just add a new line.
self.write("\n")
def _GetSubConfigNames(self):
return ["interactive"] # Allow [Keys:Interactive] sections to be specific
def HookHandlers(self):
# Hook menu command (executed when a menu item with that ID is selected from a menu/toolbar
self.HookCommand(self.OnSelectBlock, win32ui.ID_EDIT_SELECT_BLOCK)
self.HookCommand(self.OnEditCopyCode, ID_EDIT_COPY_CODE)
self.HookCommand(self.OnEditExecClipboard, ID_EDIT_EXEC_CLIPBOARD)
mod = pywin.scintilla.IDLEenvironment.GetIDLEModule("IdleHistory")
if mod is not None:
self.history = mod.History(self.idle.text, "\n" + sys.ps2)
else:
self.history = None
# hack for now for event handling.
# GetBlockBoundary takes a line number, and will return the
# start and and line numbers of the block, and a flag indicating if the
# block is a Python code block.
# If the line specified has a Python prompt, then the lines are parsed
# backwards and forwards, and the flag is true.
# If the line does not start with a prompt, the block is searched forward
# and backward until a prompt _is_ found, and all lines in between without
# prompts are returned, and the flag is false.
def GetBlockBoundary(self, lineNo):
line = self.DoGetLine(lineNo)
maxLineNo = self.GetLineCount() - 1
prefix = GetPromptPrefix(line)
if prefix is None: # Non code block
flag = 0
startLineNo = lineNo
while startLineNo > 0:
if GetPromptPrefix(self.DoGetLine(startLineNo - 1)) is not None:
break # there _is_ a prompt
startLineNo = startLineNo - 1
endLineNo = lineNo
while endLineNo < maxLineNo:
if GetPromptPrefix(self.DoGetLine(endLineNo + 1)) is not None:
break # there _is_ a prompt
endLineNo = endLineNo + 1
else: # Code block
flag = 1
startLineNo = lineNo
while startLineNo > 0 and prefix != str(sys.ps1):
prefix = GetPromptPrefix(self.DoGetLine(startLineNo - 1))
if prefix is None:
break
# there is no prompt.
startLineNo = startLineNo - 1
endLineNo = lineNo
while endLineNo < maxLineNo:
prefix = GetPromptPrefix(self.DoGetLine(endLineNo + 1))
if prefix is None:
break # there is no prompt
if prefix == str(sys.ps1):
break # this is another command
endLineNo = endLineNo + 1
# continue until end of buffer, or no prompt
return (startLineNo, endLineNo, flag)
def ExtractCommand(self, lines):
start, end = lines
retList = []
while end >= start:
thisLine = self.DoGetLine(end)
promptLen = len(GetPromptPrefix(thisLine))
retList = [thisLine[promptLen:]] + retList
end = end - 1
return retList
def OutputGrab(self):
# import win32traceutil; return
self.oldStdOut = sys.stdout
self.oldStdErr = sys.stderr
sys.stdout = self
sys.stderr = self
self.flush()
def OutputRelease(self):
# a command may have overwritten these - only restore if not.
if self.oldStdOut is not None:
if sys.stdout == self:
sys.stdout = self.oldStdOut
if self.oldStdErr is not None:
if sys.stderr == self:
sys.stderr = self.oldStdErr
self.oldStdOut = None
self.oldStdErr = None
self.flush()
###################################
#
# Message/Command/Key Hooks.
#
# Enter key handler
#
def ProcessEnterEvent(self, event):
# If autocompletion has been triggered, complete and do not process event
if self.SCIAutoCActive():
self.SCIAutoCComplete()
self.SCICancel()
return
self.SCICancel()
# First, check for an error message
haveGrabbedOutput = 0
if self.HandleSpecialLine():
return 0
lineNo = self.LineFromChar()
start, end, isCode = self.GetBlockBoundary(lineNo)
# If we are not in a code block just go to the prompt (or create a new one)
if not isCode:
self.AppendToPrompt([])
win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE))
return
lines = self.ExtractCommand((start, end))
# If we are in a code-block, but it isnt at the end of the buffer
# then copy it to the end ready for editing and subsequent execution
if end != self.GetLineCount() - 1:
win32ui.SetStatusText("Press ENTER to execute command")
self.AppendToPrompt(lines)
self.SetSel(-2)
return
# If SHIFT held down, we want new code here and now!
bNeedIndent = (
win32api.GetKeyState(win32con.VK_SHIFT) < 0
or win32api.GetKeyState(win32con.VK_CONTROL) < 0
)
if bNeedIndent:
self.ReplaceSel("\n")
else:
self.SetSel(-2)
self.ReplaceSel("\n")
source = "\n".join(lines)
while source and source[-1] in "\t ":
source = source[:-1]
self.OutputGrab() # grab the output for the command exec.
try:
if self.interp.runsource(
source, "<interactive input>"
): # Need more input!
bNeedIndent = 1
else:
# If the last line isnt empty, append a newline
if self.history is not None:
self.history.history_store(source)
self.AppendToPrompt([])
win32ui.SetStatusText(
win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE)
)
# win32ui.SetStatusText('Successfully executed statement')
finally:
self.OutputRelease()
if bNeedIndent:
win32ui.SetStatusText("Ready to continue the command")
# Now attempt correct indentation (should use IDLE?)
curLine = self.DoGetLine(lineNo)[len(sys.ps2) :]
pos = 0
indent = ""
while len(curLine) > pos and curLine[pos] in string.whitespace:
indent = indent + curLine[pos]
pos = pos + 1
if _is_block_opener(curLine):
indent = indent + "\t"
elif _is_block_closer(curLine):
indent = indent[:-1]
# use ReplaceSel to ensure it goes at the cursor rather than end of buffer.
self.ReplaceSel(sys.ps2 + indent)
return 0
# ESC key handler
def ProcessEscEvent(self, event):
# Implement a cancel.
if self.SCIAutoCActive() or self.SCICallTipActive():
self.SCICancel()
else:
win32ui.SetStatusText("Cancelled.")
self.AppendToPrompt(("",))
return 0
def OnSelectBlock(self, command, code):
lineNo = self.LineFromChar()
start, end, isCode = self.GetBlockBoundary(lineNo)
startIndex = self.LineIndex(start)
endIndex = self.LineIndex(end + 1) - 2 # skip \r + \n
if endIndex < 0: # must be beyond end of buffer
endIndex = -2 # self.Length()
self.SetSel(startIndex, endIndex)
def OnEditCopyCode(self, command, code):
"""Sanitizes code from interactive window, removing prompts and output,
and inserts it in the clipboard."""
code = self.GetSelText()
lines = code.splitlines()
out_lines = []
for line in lines:
if line.startswith(sys.ps1):
line = line[len(sys.ps1) :]
out_lines.append(line)
elif line.startswith(sys.ps2):
line = line[len(sys.ps2) :]
out_lines.append(line)
out_code = os.linesep.join(out_lines)
win32clipboard.OpenClipboard()
try:
win32clipboard.SetClipboardData(
win32clipboard.CF_UNICODETEXT, str(out_code)
)
finally:
win32clipboard.CloseClipboard()
def OnEditExecClipboard(self, command, code):
"""Executes python code directly from the clipboard."""
win32clipboard.OpenClipboard()
try:
code = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT)
finally:
win32clipboard.CloseClipboard()
code = code.replace("\r\n", "\n") + "\n"
try:
o = compile(code, "<clipboard>", "exec")
exec(o, __main__.__dict__)
except:
traceback.print_exc()
def GetRightMenuItems(self):
# Just override parents
ret = []
flags = 0
ret.append((flags, win32ui.ID_EDIT_UNDO, "&Undo"))
ret.append(win32con.MF_SEPARATOR)
ret.append((flags, win32ui.ID_EDIT_CUT, "Cu&t"))
ret.append((flags, win32ui.ID_EDIT_COPY, "&Copy"))
start, end = self.GetSel()
if start != end:
ret.append((flags, ID_EDIT_COPY_CODE, "Copy code without prompts"))
if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_UNICODETEXT):
ret.append(
(flags, ID_EDIT_EXEC_CLIPBOARD, "Execute python code from clipboard")
)
ret.append((flags, win32ui.ID_EDIT_PASTE, "&Paste"))
ret.append(win32con.MF_SEPARATOR)
ret.append((flags, win32ui.ID_EDIT_SELECT_ALL, "&Select all"))
ret.append((flags, win32ui.ID_EDIT_SELECT_BLOCK, "Select &block"))
ret.append((flags, win32ui.ID_VIEW_WHITESPACE, "View &Whitespace"))
return ret
def MDINextEvent(self, event):
win32ui.GetMainFrame().MDINext(0)
def MDIPrevEvent(self, event):
win32ui.GetMainFrame().MDINext(0)
def WindowBackEvent(self, event):
parent = self.GetParentFrame()
if parent == win32ui.GetMainFrame():
# It is docked.
try:
wnd, isactive = parent.MDIGetActive()
wnd.SetFocus()
except win32ui.error:
# No MDI window active!
pass
else:
# Normal Window
try:
lastActive = self.GetParentFrame().lastActive
# If the window is invalid, reset it.
if lastActive is not None and (
lastActive._obj_ is None or lastActive.GetSafeHwnd() == 0
):
lastActive = self.GetParentFrame().lastActive = None
win32ui.SetStatusText("The last active Window has been closed.")
except AttributeError:
print("Can't find the last active window!")
lastActive = None
if lastActive is not None:
lastActive.MDIActivate()
class InteractiveView(InteractiveCore, winout.WindowOutputView):
def __init__(self, doc):
InteractiveCore.__init__(self)
winout.WindowOutputView.__init__(self, doc)
self.encoding = pywin.default_scintilla_encoding
def _MakeColorizer(self):
return InteractiveFormatter(self)
def OnInitialUpdate(self):
winout.WindowOutputView.OnInitialUpdate(self)
self.SetWordWrap()
self.Init()
def HookHandlers(self):
winout.WindowOutputView.HookHandlers(self)
InteractiveCore.HookHandlers(self)
class CInteractivePython(winout.WindowOutput):
def __init__(self, makeDoc=None, makeFrame=None):
self.IsFinalDestroy = 0
winout.WindowOutput.__init__(
self,
sectionProfile,
sectionProfile,
winout.flags.WQ_LINE,
1,
None,
makeDoc,
makeFrame,
InteractiveView,
)
self.Create()
def OnViewDestroy(self, view):
if self.IsFinalDestroy:
view.OutputRelease()
winout.WindowOutput.OnViewDestroy(self, view)
def Close(self):
self.IsFinalDestroy = 1
winout.WindowOutput.Close(self)
class InteractiveFrame(winout.WindowOutputFrame):
def __init__(self):
self.lastActive = None
winout.WindowOutputFrame.__init__(self)
def OnMDIActivate(self, bActive, wndActive, wndDeactive):
if bActive:
self.lastActive = wndDeactive
######################################################################
##
## Dockable Window Support
##
######################################################################
ID_DOCKED_INTERACTIVE_CONTROLBAR = 0xE802
DockedInteractiveViewParent = InteractiveView
class DockedInteractiveView(DockedInteractiveViewParent):
def HookHandlers(self):
DockedInteractiveViewParent.HookHandlers(self)
self.HookMessage(self.OnSetFocus, win32con.WM_SETFOCUS)
self.HookMessage(self.OnKillFocus, win32con.WM_KILLFOCUS)
def OnSetFocus(self, msg):
self.GetParentFrame().SetActiveView(self)
return 1
def OnKillFocus(self, msg):
# If we are losing focus to another in this app, reset the main frame's active view.
hwnd = wparam = msg[2]
try:
wnd = win32ui.CreateWindowFromHandle(hwnd)
reset = wnd.GetTopLevelFrame() == self.GetTopLevelFrame()
except win32ui.error:
reset = 0 # Not my window
if reset:
self.GetParentFrame().SetActiveView(None)
return 1
def OnDestroy(self, msg):
newSize = self.GetWindowPlacement()[4]
pywin.framework.app.SaveWindowSize("Interactive Window", newSize, "docked")
try:
if self.GetParentFrame().GetActiveView == self:
self.GetParentFrame().SetActiveView(None)
except win32ui.error:
pass
try:
if win32ui.GetMainFrame().GetActiveView() == self:
win32ui.GetMainFrame().SetActiveView(None)
except win32ui.error:
pass
return DockedInteractiveViewParent.OnDestroy(self, msg)
class CDockedInteractivePython(CInteractivePython):
def __init__(self, dockbar):
self.bFirstCreated = 0
self.dockbar = dockbar
CInteractivePython.__init__(self)
def NeedRecreateWindow(self):
if self.bCreating:
return 0
try:
frame = win32ui.GetMainFrame()
if frame.closing:
return 0 # Dieing!
except (win32ui.error, AttributeError):
return 0 # The app is dieing!
try:
cb = frame.GetControlBar(ID_DOCKED_INTERACTIVE_CONTROLBAR)
return not cb.IsWindowVisible()
except win32ui.error:
return 1 # Control bar does not exist!
def RecreateWindow(self):
try:
dockbar = win32ui.GetMainFrame().GetControlBar(
ID_DOCKED_INTERACTIVE_CONTROLBAR
)
win32ui.GetMainFrame().ShowControlBar(dockbar, 1, 1)
except win32ui.error:
CreateDockedInteractiveWindow()
def Create(self):
self.bCreating = 1
doc = InteractiveDocument(None, self.DoCreateDoc())
view = DockedInteractiveView(doc)
defRect = pywin.framework.app.LoadWindowSize("Interactive Window", "docked")
if defRect[2] - defRect[0] == 0:
defRect = 0, 0, 500, 200
style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_BORDER
id = 1050 # win32ui.AFX_IDW_PANE_FIRST
view.CreateWindow(self.dockbar, id, style, defRect)
view.OnInitialUpdate()
self.bFirstCreated = 1
self.currentView = doc.GetFirstView()
self.bCreating = 0
if self.title:
doc.SetTitle(self.title)
# The factory we pass to the dockable window support.
def InteractiveViewCreator(parent):
global edit
edit = CDockedInteractivePython(parent)
return edit.currentView
def CreateDockedInteractiveWindow():
# Later, the DockingBar should be capable of hosting multiple
# children.
from pywin.docking.DockingBar import DockingBar
bar = DockingBar()
creator = InteractiveViewCreator
bar.CreateWindow(
win32ui.GetMainFrame(),
creator,
"Interactive Window",
ID_DOCKED_INTERACTIVE_CONTROLBAR,
)
bar.SetBarStyle(
bar.GetBarStyle()
| afxres.CBRS_TOOLTIPS
| afxres.CBRS_FLYBY
| afxres.CBRS_SIZE_DYNAMIC
)
bar.EnableDocking(afxres.CBRS_ALIGN_ANY)
win32ui.GetMainFrame().DockControlBar(bar, afxres.AFX_IDW_DOCKBAR_BOTTOM)
######################################################################
#
# The public interface to this module.
#
######################################################################
# No extra functionality now, but maybe later, so
# publicize these names.
InteractiveDocument = winout.WindowOutputDocument
# We remember our one and only interactive window in the "edit" variable.
edit = None
def CreateInteractiveWindowUserPreference(makeDoc=None, makeFrame=None):
"""Create some sort of interactive window if the user's preference say we should."""
bCreate = LoadPreference("Show at startup", 1)
if bCreate:
CreateInteractiveWindow(makeDoc, makeFrame)
def CreateInteractiveWindow(makeDoc=None, makeFrame=None):
"""Create a standard or docked interactive window unconditionally"""
assert edit is None, "Creating second interactive window!"
bDocking = LoadPreference("Docking", 0)
if bDocking:
CreateDockedInteractiveWindow()
else:
CreateMDIInteractiveWindow(makeDoc, makeFrame)
assert edit is not None, "Created interactive window, but did not set the global!"
edit.currentView.SetFocus()
def CreateMDIInteractiveWindow(makeDoc=None, makeFrame=None):
"""Create a standard (non-docked) interactive window unconditionally"""
global edit
if makeDoc is None:
makeDoc = InteractiveDocument
if makeFrame is None:
makeFrame = InteractiveFrame
edit = CInteractivePython(makeDoc=makeDoc, makeFrame=makeFrame)
def DestroyInteractiveWindow():
"""Destroy the interactive window.
This is different to Closing the window,
which may automatically re-appear. Once destroyed, it can never be recreated,
and a complete new instance must be created (which the various other helper
functions will then do after making this call
"""
global edit
if edit is not None and edit.currentView is not None:
if edit.currentView.GetParentFrame() == win32ui.GetMainFrame():
# It is docked - do nothing now (this is only called at shutdown!)
pass
else:
# It is a standard window - call Close on the container.
edit.Close()
edit = None
def CloseInteractiveWindow():
"""Close the interactive window, allowing it to be re-created on demand."""
global edit
if edit is not None and edit.currentView is not None:
if edit.currentView.GetParentFrame() == win32ui.GetMainFrame():
# It is docked, just hide the dock bar.
frame = win32ui.GetMainFrame()
cb = frame.GetControlBar(ID_DOCKED_INTERACTIVE_CONTROLBAR)
frame.ShowControlBar(cb, 0, 1)
else:
# It is a standard window - destroy the frame/view, allowing the object itself to remain.
edit.currentView.GetParentFrame().DestroyWindow()
def ToggleInteractiveWindow():
"""If the interactive window is visible, hide it, otherwise show it."""
if edit is None:
CreateInteractiveWindow()
else:
if edit.NeedRecreateWindow():
edit.RecreateWindow()
else:
# Close it, allowing a reopen.
CloseInteractiveWindow()
def ShowInteractiveWindow():
"""Shows (or creates if necessary) an interactive window"""
if edit is None:
CreateInteractiveWindow()
else:
if edit.NeedRecreateWindow():
edit.RecreateWindow()
else:
parent = edit.currentView.GetParentFrame()
if parent == win32ui.GetMainFrame(): # It is docked.
edit.currentView.SetFocus()
else: # It is a "normal" window
edit.currentView.GetParentFrame().AutoRestore()
win32ui.GetMainFrame().MDIActivate(edit.currentView.GetParentFrame())
def IsInteractiveWindowVisible():
return edit is not None and not edit.NeedRecreateWindow()

View File

@ -0,0 +1,553 @@
# intpyapp.py - Interactive Python application class
#
import win32con
import win32api
import win32ui
import __main__
import sys
import os
from . import app
import traceback
from pywin.mfc import afxres, dialog
import commctrl
from . import dbgcommands
lastLocateFileName = ".py" # used in the "File/Locate" dialog...
# todo - _SetupSharedMenu should be moved to a framework class.
def _SetupSharedMenu_(self):
sharedMenu = self.GetSharedMenu()
from pywin.framework import toolmenu
toolmenu.SetToolsMenu(sharedMenu)
from pywin.framework import help
help.SetHelpMenuOtherHelp(sharedMenu)
from pywin.mfc import docview
docview.DocTemplate._SetupSharedMenu_ = _SetupSharedMenu_
class MainFrame(app.MainFrame):
def OnCreate(self, createStruct):
self.closing = 0
if app.MainFrame.OnCreate(self, createStruct) == -1:
return -1
style = (
win32con.WS_CHILD
| afxres.CBRS_SIZE_DYNAMIC
| afxres.CBRS_TOP
| afxres.CBRS_TOOLTIPS
| afxres.CBRS_FLYBY
)
self.EnableDocking(afxres.CBRS_ALIGN_ANY)
tb = win32ui.CreateToolBar(self, style | win32con.WS_VISIBLE)
tb.ModifyStyle(0, commctrl.TBSTYLE_FLAT)
tb.LoadToolBar(win32ui.IDR_MAINFRAME)
tb.EnableDocking(afxres.CBRS_ALIGN_ANY)
tb.SetWindowText("Standard")
self.DockControlBar(tb)
# Any other packages which use toolbars
from pywin.debugger.debugger import PrepareControlBars
PrepareControlBars(self)
# Note "interact" also uses dockable windows, but they already happen
# And a "Tools" menu on the main frame.
menu = self.GetMenu()
from . import toolmenu
toolmenu.SetToolsMenu(menu, 2)
# And fix the "Help" menu on the main frame
from pywin.framework import help
help.SetHelpMenuOtherHelp(menu)
def OnClose(self):
try:
import pywin.debugger
if (
pywin.debugger.currentDebugger is not None
and pywin.debugger.currentDebugger.pumping
):
try:
pywin.debugger.currentDebugger.close(1)
except:
traceback.print_exc()
return
except win32ui.error:
pass
self.closing = 1
self.SaveBarState("ToolbarDefault")
self.SetActiveView(None) # Otherwise MFC's OnClose may _not_ prompt for save.
from pywin.framework import help
help.FinalizeHelp()
self.DestroyControlBar(afxres.AFX_IDW_TOOLBAR)
self.DestroyControlBar(win32ui.ID_VIEW_TOOLBAR_DBG)
return self._obj_.OnClose()
def DestroyControlBar(self, id):
try:
bar = self.GetControlBar(id)
except win32ui.error:
return
bar.DestroyWindow()
def OnCommand(self, wparam, lparam):
# By default, the current MDI child frame will process WM_COMMAND
# messages before any docked control bars - even if the control bar
# has focus. This is a problem for the interactive window when docked.
# Therefore, we detect the situation of a view having the main frame
# as its parent, and assume it must be a docked view (which it will in an MDI app)
try:
v = (
self.GetActiveView()
) # Raise an exception if none - good - then we want default handling
# Main frame _does_ have a current view (ie, a docking view) - see if it wants it.
if v.OnCommand(wparam, lparam):
return 1
except (win32ui.error, AttributeError):
pass
return self._obj_.OnCommand(wparam, lparam)
class InteractivePythonApp(app.CApp):
# This works if necessary - just we dont need to override the Run method.
# def Run(self):
# return self._obj_.Run()
def HookCommands(self):
app.CApp.HookCommands(self)
dbgcommands.DebuggerCommandHandler().HookCommands()
self.HookCommand(self.OnViewBrowse, win32ui.ID_VIEW_BROWSE)
self.HookCommand(self.OnFileImport, win32ui.ID_FILE_IMPORT)
self.HookCommand(self.OnFileCheck, win32ui.ID_FILE_CHECK)
self.HookCommandUpdate(self.OnUpdateFileCheck, win32ui.ID_FILE_CHECK)
self.HookCommand(self.OnFileRun, win32ui.ID_FILE_RUN)
self.HookCommand(self.OnFileLocate, win32ui.ID_FILE_LOCATE)
self.HookCommand(self.OnInteractiveWindow, win32ui.ID_VIEW_INTERACTIVE)
self.HookCommandUpdate(
self.OnUpdateInteractiveWindow, win32ui.ID_VIEW_INTERACTIVE
)
self.HookCommand(self.OnViewOptions, win32ui.ID_VIEW_OPTIONS)
self.HookCommand(self.OnHelpIndex, afxres.ID_HELP_INDEX)
self.HookCommand(self.OnFileSaveAll, win32ui.ID_FILE_SAVE_ALL)
self.HookCommand(self.OnViewToolbarDbg, win32ui.ID_VIEW_TOOLBAR_DBG)
self.HookCommandUpdate(self.OnUpdateViewToolbarDbg, win32ui.ID_VIEW_TOOLBAR_DBG)
def CreateMainFrame(self):
return MainFrame()
def MakeExistingDDEConnection(self):
# Use DDE to connect to an existing instance
# Return None if no existing instance
try:
from . import intpydde
except ImportError:
# No dde support!
return None
conv = intpydde.CreateConversation(self.ddeServer)
try:
conv.ConnectTo("Pythonwin", "System")
return conv
except intpydde.error:
return None
def InitDDE(self):
# Do all the magic DDE handling.
# Returns TRUE if we have pumped the arguments to our
# remote DDE app, and we should terminate.
try:
from . import intpydde
except ImportError:
self.ddeServer = None
intpydde = None
if intpydde is not None:
self.ddeServer = intpydde.DDEServer(self)
self.ddeServer.Create("Pythonwin", intpydde.CBF_FAIL_SELFCONNECTIONS)
try:
# If there is an existing instance, pump the arguments to it.
connection = self.MakeExistingDDEConnection()
if connection is not None:
connection.Exec("self.Activate()")
if self.ProcessArgs(sys.argv, connection) is None:
return 1
except:
# It is too early to 'print' an exception - we
# don't have stdout setup yet!
win32ui.DisplayTraceback(
sys.exc_info(), " - error in DDE conversation with Pythonwin"
)
return 1
def InitInstance(self):
# Allow "/nodde" and "/new" to optimize this!
if (
"/nodde" not in sys.argv
and "/new" not in sys.argv
and "-nodde" not in sys.argv
and "-new" not in sys.argv
):
if self.InitDDE():
return 1 # A remote DDE client is doing it for us!
else:
self.ddeServer = None
win32ui.SetRegistryKey(
"Python %s" % (sys.winver,)
) # MFC automatically puts the main frame caption on!
app.CApp.InitInstance(self)
# Create the taskbar icon
win32ui.CreateDebuggerThread()
# Allow Pythonwin to host OCX controls.
win32ui.EnableControlContainer()
# Display the interactive window if the user wants it.
from . import interact
interact.CreateInteractiveWindowUserPreference()
# Load the modules we use internally.
self.LoadSystemModules()
# Load additional module the user may want.
self.LoadUserModules()
# Load the ToolBar state near the end of the init process, as
# there may be Toolbar IDs created by the user or other modules.
# By now all these modules should be loaded, so all the toolbar IDs loaded.
try:
self.frame.LoadBarState("ToolbarDefault")
except win32ui.error:
# MFC sucks. It does essentially "GetDlgItem(x)->Something", so if the
# toolbar with ID x does not exist, MFC crashes! Pythonwin has a trap for this
# but I need to investigate more how to prevent it (AFAIK, ensuring all the
# toolbars are created by now _should_ stop it!)
pass
# Finally process the command line arguments.
try:
self.ProcessArgs(sys.argv)
except:
# too early for printing anything.
win32ui.DisplayTraceback(
sys.exc_info(), " - error processing command line args"
)
def ExitInstance(self):
win32ui.DestroyDebuggerThread()
try:
from . import interact
interact.DestroyInteractiveWindow()
except:
pass
if self.ddeServer is not None:
self.ddeServer.Shutdown()
self.ddeServer = None
return app.CApp.ExitInstance(self)
def Activate(self):
# Bring to the foreground. Mainly used when another app starts up, it asks
# this one to activate itself, then it terminates.
frame = win32ui.GetMainFrame()
frame.SetForegroundWindow()
if frame.GetWindowPlacement()[1] == win32con.SW_SHOWMINIMIZED:
frame.ShowWindow(win32con.SW_RESTORE)
def ProcessArgs(self, args, dde=None):
# If we are going to talk to a remote app via DDE, then
# activate it!
if (
len(args) < 1 or not args[0]
): # argv[0]=='' when started without args, just like Python.exe!
return
i = 0
while i < len(args):
argType = args[i]
i += 1
if argType.startswith("-"):
# Support dash options. Slash options are misinterpreted by python init
# as path and not finding usually 'C:\\' ends up in sys.path[0]
argType = "/" + argType[1:]
if not argType.startswith("/"):
argType = win32ui.GetProfileVal(
"Python", "Default Arg Type", "/edit"
).lower()
i -= 1 # arg is /edit's parameter
par = i < len(args) and args[i] or "MISSING"
if argType in ["/nodde", "/new", "-nodde", "-new"]:
# Already handled
pass
elif argType.startswith("/goto:"):
gotoline = int(argType[len("/goto:") :])
if dde:
dde.Exec(
"from pywin.framework import scriptutils\n"
"ed = scriptutils.GetActiveEditControl()\n"
"if ed: ed.SetSel(ed.LineIndex(%s - 1))" % gotoline
)
else:
from . import scriptutils
ed = scriptutils.GetActiveEditControl()
if ed:
ed.SetSel(ed.LineIndex(gotoline - 1))
elif argType == "/edit":
# Load up the default application.
i += 1
fname = win32api.GetFullPathName(par)
if not os.path.isfile(fname):
# if we don't catch this, OpenDocumentFile() (actually
# PyCDocument.SetPathName() in
# pywin.scintilla.document.CScintillaDocument.OnOpenDocument)
# segfaults Pythonwin on recent PY3 builds (b228)
win32ui.MessageBox(
"No such file: %s\n\nCommand Line: %s"
% (fname, win32api.GetCommandLine()),
"Open file for edit",
win32con.MB_ICONERROR,
)
continue
if dde:
dde.Exec("win32ui.GetApp().OpenDocumentFile(%s)" % (repr(fname)))
else:
win32ui.GetApp().OpenDocumentFile(par)
elif argType == "/rundlg":
if dde:
dde.Exec(
"from pywin.framework import scriptutils;scriptutils.RunScript(%r, %r, 1)"
% (par, " ".join(args[i + 1 :]))
)
else:
from . import scriptutils
scriptutils.RunScript(par, " ".join(args[i + 1 :]))
return
elif argType == "/run":
if dde:
dde.Exec(
"from pywin.framework import scriptutils;scriptutils.RunScript(%r, %r, 0)"
% (par, " ".join(args[i + 1 :]))
)
else:
from . import scriptutils
scriptutils.RunScript(par, " ".join(args[i + 1 :]), 0)
return
elif argType == "/app":
raise RuntimeError(
"/app only supported for new instances of Pythonwin.exe"
)
elif argType == "/dde": # Send arbitary command
if dde is not None:
dde.Exec(par)
else:
win32ui.MessageBox(
"The /dde command can only be used\r\nwhen Pythonwin is already running"
)
i += 1
else:
raise ValueError("Command line argument not recognised: %s" % argType)
def LoadSystemModules(self):
self.DoLoadModules("pywin.framework.editor,pywin.framework.stdin")
def LoadUserModules(self, moduleNames=None):
# Load the users modules.
if moduleNames is None:
default = "pywin.framework.sgrepmdi,pywin.framework.mdi_pychecker"
moduleNames = win32ui.GetProfileVal("Python", "Startup Modules", default)
self.DoLoadModules(moduleNames)
def DoLoadModules(self, moduleNames): # ", sep string of module names.
if not moduleNames:
return
modules = moduleNames.split(",")
for module in modules:
try:
__import__(module)
except: # Catch em all, else the app itself dies! 'ImportError:
traceback.print_exc()
msg = 'Startup import of user module "%s" failed' % module
print(msg)
win32ui.MessageBox(msg)
#
# DDE Callback
#
def OnDDECommand(self, command):
try:
exec(command + "\n")
except:
print("ERROR executing DDE command: ", command)
traceback.print_exc()
raise
#
# General handlers
#
def OnViewBrowse(self, id, code):
"Called when ViewBrowse message is received"
from pywin.tools import browser
obName = dialog.GetSimpleInput("Object", "__builtins__", "Browse Python Object")
if obName is None:
return
try:
browser.Browse(eval(obName, __main__.__dict__, __main__.__dict__))
except NameError:
win32ui.MessageBox("This is no object with this name")
except AttributeError:
win32ui.MessageBox("The object has no attribute of that name")
except:
traceback.print_exc()
win32ui.MessageBox("This object can not be browsed")
def OnFileImport(self, id, code):
"Called when a FileImport message is received. Import the current or specified file"
from . import scriptutils
scriptutils.ImportFile()
def OnFileCheck(self, id, code):
"Called when a FileCheck message is received. Check the current file."
from . import scriptutils
scriptutils.CheckFile()
def OnUpdateFileCheck(self, cmdui):
from . import scriptutils
cmdui.Enable(scriptutils.GetActiveFileName(0) is not None)
def OnFileRun(self, id, code):
"Called when a FileRun message is received."
from . import scriptutils
showDlg = win32api.GetKeyState(win32con.VK_SHIFT) >= 0
scriptutils.RunScript(None, None, showDlg)
def OnFileLocate(self, id, code):
from . import scriptutils
global lastLocateFileName # save the new version away for next time...
name = dialog.GetSimpleInput(
"File name", lastLocateFileName, "Locate Python File"
)
if name is None: # Cancelled.
return
lastLocateFileName = name
# if ".py" supplied, rip it off!
# should also check for .pys and .pyw
if lastLocateFileName[-3:].lower() == ".py":
lastLocateFileName = lastLocateFileName[:-3]
lastLocateFileName = lastLocateFileName.replace(".", "\\")
newName = scriptutils.LocatePythonFile(lastLocateFileName)
if newName is None:
win32ui.MessageBox("The file '%s' can not be located" % lastLocateFileName)
else:
win32ui.GetApp().OpenDocumentFile(newName)
# Display all the "options" proprety pages we can find
def OnViewOptions(self, id, code):
win32ui.InitRichEdit()
sheet = dialog.PropertySheet("Pythonwin Options")
# Add property pages we know about that need manual work.
from pywin.dialogs import ideoptions
sheet.AddPage(ideoptions.OptionsPropPage())
from . import toolmenu
sheet.AddPage(toolmenu.ToolMenuPropPage())
# Get other dynamic pages from templates.
pages = []
for template in self.GetDocTemplateList():
try:
# Dont actually call the function with the exception handler.
getter = template.GetPythonPropertyPages
except AttributeError:
# Template does not provide property pages!
continue
pages = pages + getter()
# Debugger template goes at the end
try:
from pywin.debugger import configui
except ImportError:
configui = None
if configui is not None:
pages.append(configui.DebuggerOptionsPropPage())
# Now simply add the pages, and display the dialog.
for page in pages:
sheet.AddPage(page)
if sheet.DoModal() == win32con.IDOK:
win32ui.SetStatusText("Applying configuration changes...", 1)
win32ui.DoWaitCursor(1)
# Tell every Window in our app that win.ini has changed!
win32ui.GetMainFrame().SendMessageToDescendants(
win32con.WM_WININICHANGE, 0, 0
)
win32ui.DoWaitCursor(0)
def OnInteractiveWindow(self, id, code):
# toggle the existing state.
from . import interact
interact.ToggleInteractiveWindow()
def OnUpdateInteractiveWindow(self, cmdui):
try:
interact = sys.modules["pywin.framework.interact"]
state = interact.IsInteractiveWindowVisible()
except KeyError: # Interactive module hasnt ever been imported.
state = 0
cmdui.Enable()
cmdui.SetCheck(state)
def OnFileSaveAll(self, id, code):
# Only attempt to save editor documents.
from pywin.framework.editor import editorTemplate
num = 0
for doc in editorTemplate.GetDocumentList():
if doc.IsModified() and doc.GetPathName():
num = num = 1
doc.OnSaveDocument(doc.GetPathName())
win32ui.SetStatusText("%d documents saved" % num, 1)
def OnViewToolbarDbg(self, id, code):
if code == 0:
return not win32ui.GetMainFrame().OnBarCheck(id)
def OnUpdateViewToolbarDbg(self, cmdui):
win32ui.GetMainFrame().OnUpdateControlBarMenu(cmdui)
cmdui.Enable(1)
def OnHelpIndex(self, id, code):
from . import help
help.SelectAndRunHelpFile()
# As per the comments in app.py, this use is depreciated.
# app.AppBuilder = InteractivePythonApp
# Now all we do is create the application
thisApp = InteractivePythonApp()

View File

@ -0,0 +1,58 @@
# DDE support for Pythonwin
#
# Seems to work fine (in the context that IE4 seems to have broken
# DDE on _all_ NT4 machines I have tried, but only when a "Command Prompt" window
# is open. Strange, but true. If you have problems with this, close all Command Prompts!
import win32ui
import win32api, win32con
from pywin.mfc import object
from dde import *
import sys, traceback
class DDESystemTopic(object.Object):
def __init__(self, app):
self.app = app
object.Object.__init__(self, CreateServerSystemTopic())
def Exec(self, data):
try:
# print "Executing", cmd
self.app.OnDDECommand(data)
except:
t, v, tb = sys.exc_info()
# The DDE Execution failed.
print("Error executing DDE command.")
traceback.print_exception(t, v, tb)
return 0
class DDEServer(object.Object):
def __init__(self, app):
self.app = app
object.Object.__init__(self, CreateServer())
self.topic = self.item = None
def CreateSystemTopic(self):
return DDESystemTopic(self.app)
def Shutdown(self):
self._obj_.Shutdown()
self._obj_.Destroy()
if self.topic is not None:
self.topic.Destroy()
self.topic = None
if self.item is not None:
self.item.Destroy()
self.item = None
def OnCreate(self):
return 1
def Status(self, msg):
try:
win32ui.SetStatusText(msg)
except win32ui.error:
pass

View File

@ -0,0 +1,843 @@
######################################################################
##
## The Pychecker MDI Plug-In UserModule for Pythonwin
##
## contributed by Robert Kiendl
##
## Style is similar to (and inherited) from the SGrepMDI UserModule
##
## Usage:
##
## Start Pychecker on current file: Menu/File/New../Pychecker.
## Use it: Jump to Pychecker warning source lines by double-click.
## Auto-add "#$pycheck_no" / "#$pycheck_no=specific-re-pattern" tags
## to source lines by context/right-mouse-click on warning lines.
##
## It requires pychecker installed and the pychecker.bat to be on
## the PATH. Example pychecker.bat:
##
## REM pychecker.bat
## C:\bin\python.exe C:\PYTHON23\Lib\site-packages\pychecker\checker.py %1 %2 %3 %4 %5 %6 %7 %8 %9
##
## Adding it as default module in PythonWin:
##
## +++ ./intpyapp.py 2006-10-02 17:59:32.974161600 +0200
## @@ -272,7 +282,7 @@
## def LoadUserModules(self, moduleNames = None):
## # Load the users modules.
## if moduleNames is None:
## - default = "sgrepmdi"
## + default = "sgrepmdi,mdi_pychecker"
## moduleNames=win32ui.GetProfileVal('Python','Startup Modules',default)
## self.DoLoadModules(moduleNames)
##
######################################################################
import win32ui
import win32api
from pywin.mfc import docview, dialog, window
import win32con
import sys, string, re, glob, os, stat, time
from . import scriptutils
def getsubdirs(d):
dlist = []
flist = glob.glob(d + "\\*")
for f in flist:
if os.path.isdir(f):
dlist.append(f)
dlist = dlist + getsubdirs(f)
return dlist
class dirpath:
def __init__(self, str, recurse=0):
dp = str.split(";")
dirs = {}
for d in dp:
if os.path.isdir(d):
d = d.lower()
if d not in dirs:
dirs[d] = None
if recurse:
subdirs = getsubdirs(d)
for sd in subdirs:
sd = sd.lower()
if sd not in dirs:
dirs[sd] = None
elif os.path.isfile(d):
pass
else:
x = None
if d in os.environ:
x = dirpath(os.environ[d])
elif d[:5] == "HKEY_":
keystr = d.split("\\")
try:
root = eval("win32con." + keystr[0])
except:
win32ui.MessageBox(
"Can't interpret registry key name '%s'" % keystr[0]
)
try:
subkey = "\\".join(keystr[1:])
val = win32api.RegQueryValue(root, subkey)
if val:
x = dirpath(val)
else:
win32ui.MessageBox(
"Registry path '%s' did not return a path entry" % d
)
except:
win32ui.MessageBox(
"Can't interpret registry key value: %s" % keystr[1:]
)
else:
win32ui.MessageBox("Directory '%s' not found" % d)
if x:
for xd in x:
if xd not in dirs:
dirs[xd] = None
if recurse:
subdirs = getsubdirs(xd)
for sd in subdirs:
sd = sd.lower()
if sd not in dirs:
dirs[sd] = None
self.dirs = []
for d in dirs.keys():
self.dirs.append(d)
def __getitem__(self, key):
return self.dirs[key]
def __len__(self):
return len(self.dirs)
def __setitem__(self, key, value):
self.dirs[key] = value
def __delitem__(self, key):
del self.dirs[key]
def __getslice__(self, lo, hi):
return self.dirs[lo:hi]
def __setslice__(self, lo, hi, seq):
self.dirs[lo:hi] = seq
def __delslice__(self, lo, hi):
del self.dirs[lo:hi]
def __add__(self, other):
if type(other) == type(self) or type(other) == type([]):
return self.dirs + other.dirs
def __radd__(self, other):
if type(other) == type(self) or type(other) == type([]):
return other.dirs + self.dirs
# Group(1) is the filename, group(2) is the lineno.
# regexGrepResult=regex.compile("^\\([a-zA-Z]:.*\\)(\\([0-9]+\\))")
# regexGrep=re.compile(r"^([a-zA-Z]:[^(]*)\((\d+)\)")
regexGrep = re.compile(r"^(..[^\(:]+)?[\(:](\d+)[\):]:?\s*(.*)")
# these are the atom numbers defined by Windows for basic dialog controls
BUTTON = 0x80
EDIT = 0x81
STATIC = 0x82
LISTBOX = 0x83
SCROLLBAR = 0x84
COMBOBOX = 0x85
class TheTemplate(docview.RichEditDocTemplate):
def __init__(self):
docview.RichEditDocTemplate.__init__(
self, win32ui.IDR_TEXTTYPE, TheDocument, TheFrame, TheView
)
self.SetDocStrings(
"\nPychecker\nPychecker\nPychecker params (*.pychecker)\n.pychecker\n\n\n"
)
win32ui.GetApp().AddDocTemplate(self)
self.docparams = None
def MatchDocType(self, fileName, fileType):
doc = self.FindOpenDocument(fileName)
if doc:
return doc
ext = os.path.splitext(fileName)[1].lower()
if ext == ".pychecker":
return win32ui.CDocTemplate_Confidence_yesAttemptNative
return win32ui.CDocTemplate_Confidence_noAttempt
def setParams(self, params):
self.docparams = params
def readParams(self):
tmp = self.docparams
self.docparams = None
return tmp
class TheFrame(window.MDIChildWnd):
# The template and doc params will one day be removed.
def __init__(self, wnd=None):
window.MDIChildWnd.__init__(self, wnd)
class TheDocument(docview.RichEditDoc):
def __init__(self, template):
docview.RichEditDoc.__init__(self, template)
self.dirpattern = ""
self.filpattern = ""
self.greppattern = ""
self.casesensitive = 1
self.recurse = 1
self.verbose = 0
def OnOpenDocument(self, fnm):
# this bizarre stuff with params is so right clicking in a result window
# and starting a new grep can communicate the default parameters to the
# new grep.
try:
params = open(fnm, "r").read()
except:
params = None
self.setInitParams(params)
return self.OnNewDocument()
def OnCloseDocument(self):
try:
win32ui.GetApp().DeleteIdleHandler(self.idleHandler)
except:
pass
return self._obj_.OnCloseDocument()
def saveInitParams(self):
# Only save the flags, not the text boxes.
paramstr = "\t\t\t%d\t%d" % (self.casesensitive, self.recurse)
win32ui.WriteProfileVal("Pychecker", "Params", paramstr)
def setInitParams(self, paramstr):
if paramstr is None:
paramstr = win32ui.GetProfileVal("Pychecker", "Params", "\t\t\t1\t0\t0")
params = paramstr.split("\t")
if len(params) < 3:
params = params + [""] * (3 - len(params))
if len(params) < 6:
params = params + [0] * (6 - len(params))
self.dirpattern = params[0]
self.filpattern = params[1]
self.greppattern = params[2] or "-#1000 --only"
self.casesensitive = int(params[3])
self.recurse = int(params[4])
self.verbose = int(params[5])
# setup some reasonable defaults.
if not self.dirpattern:
try:
editor = win32ui.GetMainFrame().MDIGetActive()[0].GetEditorView()
self.dirpattern = os.path.abspath(
os.path.dirname(editor.GetDocument().GetPathName())
)
except (AttributeError, win32ui.error):
self.dirpattern = os.getcwd()
if not self.filpattern:
try:
editor = win32ui.GetMainFrame().MDIGetActive()[0].GetEditorView()
self.filpattern = editor.GetDocument().GetPathName()
except AttributeError:
self.filpattern = "*.py"
def OnNewDocument(self):
if self.dirpattern == "":
self.setInitParams(greptemplate.readParams())
d = TheDialog(
self.dirpattern,
self.filpattern,
self.greppattern,
self.casesensitive,
self.recurse,
self.verbose,
)
if d.DoModal() == win32con.IDOK:
self.dirpattern = d["dirpattern"]
self.filpattern = d["filpattern"]
self.greppattern = d["greppattern"]
# self.casesensitive = d['casesensitive']
# self.recurse = d['recursive']
# self.verbose = d['verbose']
self.doSearch()
self.saveInitParams()
return 1
return 0 # cancelled - return zero to stop frame creation.
def doSearch(self):
self.dp = dirpath(self.dirpattern, self.recurse)
self.SetTitle(
"Pychecker Run '%s' (options: %s)" % (self.filpattern, self.greppattern)
)
# self.text = []
self.GetFirstView().Append(
"#Pychecker Run in " + self.dirpattern + " %s\n" % time.asctime()
)
if self.verbose:
self.GetFirstView().Append("# =" + repr(self.dp.dirs) + "\n")
self.GetFirstView().Append("# Files " + self.filpattern + "\n")
self.GetFirstView().Append("# Options " + self.greppattern + "\n")
self.fplist = self.filpattern.split(";")
self.GetFirstView().Append(
"# Running... ( double click on result lines in order to jump to the source code ) \n"
)
win32ui.SetStatusText("Pychecker running. Please wait...", 0)
self.dpndx = self.fpndx = 0
self.fndx = -1
if not self.dp:
self.GetFirstView().Append(
"# ERROR: '%s' does not resolve to any search locations"
% self.dirpattern
)
self.SetModifiedFlag(0)
else:
##self.flist = glob.glob(self.dp[0]+'\\'+self.fplist[0])
import operator
self.flist = reduce(operator.add, list(map(glob.glob, self.fplist)))
# import pywin.debugger;pywin.debugger.set_trace()
self.startPycheckerRun()
def idleHandler(self, handler, count):
import time
time.sleep(0.001)
if self.result != None:
win32ui.GetApp().DeleteIdleHandler(self.idleHandler)
return 0
return 1 # more
def startPycheckerRun(self):
self.result = None
old = win32api.SetCursor(win32api.LoadCursor(0, win32con.IDC_APPSTARTING))
win32ui.GetApp().AddIdleHandler(self.idleHandler)
import _thread
_thread.start_new(self.threadPycheckerRun, ())
##win32api.SetCursor(old)
def threadPycheckerRun(self):
result = ""
rc = -1
try:
options = self.greppattern
files = " ".join(self.flist)
# Recently MarkH has failed to run pychecker without it having
# been explicitly installed - so we assume it is and locate it
# from its default location.
# Step1 - get python.exe
py = os.path.join(sys.prefix, "python.exe")
if not os.path.isfile(py):
if "64 bit" in sys.version:
py = os.path.join(sys.prefix, "PCBuild", "amd64", "python.exe")
else:
py = os.path.join(sys.prefix, "PCBuild", "python.exe")
try:
py = win32api.GetShortPathName(py)
except win32api.error:
py = ""
# Find checker.py
import sysconfig
pychecker = os.path.join(
sysconfig.get_paths()["purelib"], "pychecker", "checker.py"
)
if not os.path.isfile(py):
result = "Can't find python.exe!\n"
elif not os.path.isfile(pychecker):
result = (
"Can't find checker.py - please install pychecker "
"(or run 'setup.py install' if you have the source version)\n"
)
else:
cmd = '%s "%s" %s %s 2>&1' % (py, pychecker, options, files)
##fin,fout,ferr=os.popen3(cmd)
##result=ferr.read()+fout.read()
result = os.popen(cmd).read()
##rc=f.close()
self.GetFirstView().Append(result)
finally:
self.result = result
print("== Pychecker run finished ==")
self.GetFirstView().Append("\n" + "== Pychecker run finished ==")
self.SetModifiedFlag(0)
def _inactive_idleHandler(self, handler, count):
self.fndx = self.fndx + 1
if self.fndx < len(self.flist):
f = self.flist[self.fndx]
if self.verbose:
self.GetFirstView().Append("# .." + f + "\n")
win32ui.SetStatusText("Searching " + f, 0)
lines = open(f, "r").readlines()
for i in range(len(lines)):
line = lines[i]
if self.pat.search(line) != None:
self.GetFirstView().Append(f + "(" + repr(i + 1) + ") " + line)
else:
self.fndx = -1
self.fpndx = self.fpndx + 1
if self.fpndx < len(self.fplist):
self.flist = glob.glob(
self.dp[self.dpndx] + "\\" + self.fplist[self.fpndx]
)
else:
self.fpndx = 0
self.dpndx = self.dpndx + 1
if self.dpndx < len(self.dp):
self.flist = glob.glob(
self.dp[self.dpndx] + "\\" + self.fplist[self.fpndx]
)
else:
win32ui.SetStatusText("Search complete.", 0)
self.SetModifiedFlag(0) # default to not modified.
try:
win32ui.GetApp().DeleteIdleHandler(self.idleHandler)
except:
pass
return 0
return 1
def GetParams(self):
return (
self.dirpattern
+ "\t"
+ self.filpattern
+ "\t"
+ self.greppattern
+ "\t"
+ repr(self.casesensitive)
+ "\t"
+ repr(self.recurse)
+ "\t"
+ repr(self.verbose)
)
def OnSaveDocument(self, filename):
# print 'OnSaveDocument() filename=',filename
savefile = open(filename, "wb")
txt = self.GetParams() + "\n"
# print 'writing',txt
savefile.write(txt)
savefile.close()
self.SetModifiedFlag(0)
return 1
ID_OPEN_FILE = 0xE500
ID_PYCHECKER = 0xE501
ID_SAVERESULTS = 0x502
ID_TRYAGAIN = 0x503
ID_ADDCOMMENT = 0x504
ID_ADDPYCHECKNO2 = 0x505
class TheView(docview.RichEditView):
def __init__(self, doc):
docview.RichEditView.__init__(self, doc)
self.SetWordWrap(win32ui.CRichEditView_WrapNone)
self.HookHandlers()
def OnInitialUpdate(self):
rc = self._obj_.OnInitialUpdate()
format = (-402653169, 0, 200, 0, 0, 0, 49, "Courier New")
self.SetDefaultCharFormat(format)
return rc
def HookHandlers(self):
self.HookMessage(self.OnRClick, win32con.WM_RBUTTONDOWN)
self.HookCommand(self.OnCmdOpenFile, ID_OPEN_FILE)
self.HookCommand(self.OnCmdThe, ID_PYCHECKER)
self.HookCommand(self.OnCmdSave, ID_SAVERESULTS)
self.HookCommand(self.OnTryAgain, ID_TRYAGAIN)
self.HookCommand(self.OnAddComment, ID_ADDCOMMENT)
self.HookCommand(self.OnAddComment, ID_ADDPYCHECKNO2)
self.HookMessage(self.OnLDblClick, win32con.WM_LBUTTONDBLCLK)
def OnLDblClick(self, params):
line = self.GetLine()
regexGrepResult = regexGrep.match(line)
if regexGrepResult:
fname = regexGrepResult.group(1)
line = int(regexGrepResult.group(2))
scriptutils.JumpToDocument(fname, line)
return 0 # dont pass on
return 1 # pass it on by default.
def OnRClick(self, params):
menu = win32ui.CreatePopupMenu()
flags = win32con.MF_STRING | win32con.MF_ENABLED
lineno = self._obj_.LineFromChar(-1) # selection or current line
line = self._obj_.GetLine(lineno)
regexGrepResult = regexGrep.match(line)
charstart, charend = self._obj_.GetSel()
if regexGrepResult:
self.fnm = regexGrepResult.group(1)
self.lnnum = int(regexGrepResult.group(2))
menu.AppendMenu(flags, ID_OPEN_FILE, "&Open " + self.fnm)
menu.AppendMenu(
flags, ID_ADDCOMMENT, "&Add to source: Comment Tag/#$pycheck_no .."
)
menu.AppendMenu(
flags,
ID_ADDPYCHECKNO2,
"&Add to source: Specific #$pycheck_no=%(errtext)s ..",
)
menu.AppendMenu(win32con.MF_SEPARATOR)
menu.AppendMenu(flags, ID_TRYAGAIN, "&Try Again")
menu.AppendMenu(flags, win32ui.ID_EDIT_CUT, "Cu&t")
menu.AppendMenu(flags, win32ui.ID_EDIT_COPY, "&Copy")
menu.AppendMenu(flags, win32ui.ID_EDIT_PASTE, "&Paste")
menu.AppendMenu(flags, win32con.MF_SEPARATOR)
menu.AppendMenu(flags, win32ui.ID_EDIT_SELECT_ALL, "&Select all")
menu.AppendMenu(flags, win32con.MF_SEPARATOR)
menu.AppendMenu(flags, ID_SAVERESULTS, "Sa&ve results")
menu.TrackPopupMenu(params[5])
return 0
def OnAddComment(self, cmd, code):
addspecific = cmd == ID_ADDPYCHECKNO2
_ = list(self.GetSel())
_.sort()
start, end = _
line_start, line_end = self.LineFromChar(start), self.LineFromChar(end)
first = 1
for i in range(line_start, line_end + 1):
line = self.GetLine(i)
m = regexGrep.match(line)
if m:
if first:
first = 0
cmnt = dialog.GetSimpleInput(
"Add to %s lines" % (line_end - line_start + 1),
addspecific
and " #$pycheck_no=%(errtext)s"
or " #$pycheck_no",
)
if not cmnt:
return 0
##import pywin.debugger;pywin.debugger.set_trace()
fname = m.group(1)
line = int(m.group(2))
view = scriptutils.JumpToDocument(fname, line)
pos = view.LineIndex(line) - 1
if view.GetTextRange(pos - 1, pos) in ("\r", "\n"):
pos -= 1
view.SetSel(pos, pos)
errtext = m.group(3)
if start != end and line_start == line_end:
errtext = self.GetSelText()
errtext = repr(re.escape(errtext).replace("\ ", " "))
view.ReplaceSel(addspecific and cmnt % locals() or cmnt)
return 0
def OnCmdOpenFile(self, cmd, code):
doc = win32ui.GetApp().OpenDocumentFile(self.fnm)
if doc:
vw = doc.GetFirstView()
# hope you have an editor that implements GotoLine()!
try:
vw.GotoLine(int(self.lnnum))
except:
pass
return 0
def OnCmdThe(self, cmd, code):
curparamsstr = self.GetDocument().GetParams()
params = curparamsstr.split("\t")
params[2] = self.sel
greptemplate.setParams("\t".join(params))
greptemplate.OpenDocumentFile()
return 0
def OnTryAgain(self, cmd, code):
greptemplate.setParams(self.GetDocument().GetParams())
greptemplate.OpenDocumentFile()
return 0
def OnCmdSave(self, cmd, code):
flags = win32con.OFN_OVERWRITEPROMPT
dlg = win32ui.CreateFileDialog(
0, None, None, flags, "Text Files (*.txt)|*.txt||", self
)
dlg.SetOFNTitle("Save Results As")
if dlg.DoModal() == win32con.IDOK:
pn = dlg.GetPathName()
self._obj_.SaveFile(pn)
return 0
def Append(self, strng):
numlines = self.GetLineCount()
endpos = self.LineIndex(numlines - 1) + len(self.GetLine(numlines - 1))
self.SetSel(endpos, endpos)
self.ReplaceSel(strng)
class TheDialog(dialog.Dialog):
def __init__(self, dp, fp, gp, cs, r, v):
style = (
win32con.DS_MODALFRAME
| win32con.WS_POPUP
| win32con.WS_VISIBLE
| win32con.WS_CAPTION
| win32con.WS_SYSMENU
| win32con.DS_SETFONT
)
CS = win32con.WS_CHILD | win32con.WS_VISIBLE
tmp = [
["Pychecker Run", (0, 0, 210, 90), style, None, (8, "MS Sans Serif")],
]
tmp.append([STATIC, "Files:", -1, (7, 7, 50, 9), CS])
tmp.append(
[
EDIT,
gp,
103,
(52, 7, 144, 11),
CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER,
]
)
tmp.append([STATIC, "Directories:", -1, (7, 20, 50, 9), CS])
tmp.append(
[
EDIT,
dp,
102,
(52, 20, 128, 11),
CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER,
]
)
tmp.append(
[
BUTTON,
"...",
110,
(182, 20, 16, 11),
CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,
]
)
tmp.append([STATIC, "Options:", -1, (7, 33, 50, 9), CS])
tmp.append(
[
EDIT,
fp,
101,
(52, 33, 128, 11),
CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER,
]
)
tmp.append(
[
BUTTON,
"...",
111,
(182, 33, 16, 11),
CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,
]
)
# tmp.append([BUTTON,'Case sensitive', 104, (7, 45, 72, 9), CS | win32con.BS_AUTOCHECKBOX | win32con.BS_LEFTTEXT| win32con.WS_TABSTOP])
# tmp.append([BUTTON,'Subdirectories', 105, (7, 56, 72, 9), CS | win32con.BS_AUTOCHECKBOX | win32con.BS_LEFTTEXT| win32con.WS_TABSTOP])
# tmp.append([BUTTON,'Verbose', 106, (7, 67, 72, 9), CS | win32con.BS_AUTOCHECKBOX | win32con.BS_LEFTTEXT| win32con.WS_TABSTOP])
tmp.append(
[
BUTTON,
"OK",
win32con.IDOK,
(166, 53, 32, 12),
CS | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP,
]
)
tmp.append(
[
BUTTON,
"Cancel",
win32con.IDCANCEL,
(166, 67, 32, 12),
CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,
]
)
dialog.Dialog.__init__(self, tmp)
self.AddDDX(101, "greppattern")
self.AddDDX(102, "dirpattern")
self.AddDDX(103, "filpattern")
# self.AddDDX(104,'casesensitive')
# self.AddDDX(105,'recursive')
# self.AddDDX(106,'verbose')
self._obj_.data["greppattern"] = gp
self._obj_.data["dirpattern"] = dp
self._obj_.data["filpattern"] = fp
# self._obj_.data['casesensitive'] = cs
# self._obj_.data['recursive'] = r
# self._obj_.data['verbose'] = v
self.HookCommand(self.OnMoreDirectories, 110)
self.HookCommand(self.OnMoreFiles, 111)
def OnMoreDirectories(self, cmd, code):
self.getMore("Pychecker\\Directories", "dirpattern")
def OnMoreFiles(self, cmd, code):
self.getMore("Pychecker\\File Types", "filpattern")
def getMore(self, section, key):
self.UpdateData(1)
# get the items out of the ini file
ini = win32ui.GetProfileFileName()
secitems = win32api.GetProfileSection(section, ini)
items = []
for secitem in secitems:
items.append(secitem.split("=")[1])
dlg = TheParamsDialog(items)
if dlg.DoModal() == win32con.IDOK:
itemstr = ";".join(dlg.getItems())
self._obj_.data[key] = itemstr
# update the ini file with dlg.getNew()
i = 0
newitems = dlg.getNew()
if newitems:
items = items + newitems
for item in items:
win32api.WriteProfileVal(section, repr(i), item, ini)
i = i + 1
self.UpdateData(0)
def OnOK(self):
self.UpdateData(1)
for id, name in [
(101, "greppattern"),
(102, "dirpattern"),
(103, "filpattern"),
]:
if not self[name]:
self.GetDlgItem(id).SetFocus()
win32api.MessageBeep()
win32ui.SetStatusText("Please enter a value")
return
self._obj_.OnOK()
class TheParamsDialog(dialog.Dialog):
def __init__(self, items):
self.items = items
self.newitems = []
style = (
win32con.DS_MODALFRAME
| win32con.WS_POPUP
| win32con.WS_VISIBLE
| win32con.WS_CAPTION
| win32con.WS_SYSMENU
| win32con.DS_SETFONT
)
CS = win32con.WS_CHILD | win32con.WS_VISIBLE
tmp = [
[
"Pychecker Parameters",
(0, 0, 205, 100),
style,
None,
(8, "MS Sans Serif"),
],
]
tmp.append(
[
LISTBOX,
"",
107,
(7, 7, 150, 72),
CS
| win32con.LBS_MULTIPLESEL
| win32con.LBS_STANDARD
| win32con.LBS_HASSTRINGS
| win32con.WS_TABSTOP
| win32con.LBS_NOTIFY,
]
)
tmp.append(
[
BUTTON,
"OK",
win32con.IDOK,
(167, 7, 32, 12),
CS | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP,
]
)
tmp.append(
[
BUTTON,
"Cancel",
win32con.IDCANCEL,
(167, 23, 32, 12),
CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,
]
)
tmp.append([STATIC, "New:", -1, (2, 83, 15, 12), CS])
tmp.append(
[
EDIT,
"",
108,
(18, 83, 139, 12),
CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER,
]
)
tmp.append(
[
BUTTON,
"Add",
109,
(167, 83, 32, 12),
CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,
]
)
dialog.Dialog.__init__(self, tmp)
self.HookCommand(self.OnAddItem, 109)
self.HookCommand(self.OnListDoubleClick, 107)
def OnInitDialog(self):
lb = self.GetDlgItem(107)
for item in self.items:
lb.AddString(item)
return self._obj_.OnInitDialog()
def OnAddItem(self, cmd, code):
eb = self.GetDlgItem(108)
item = eb.GetLine(0)
self.newitems.append(item)
lb = self.GetDlgItem(107)
i = lb.AddString(item)
lb.SetSel(i, 1)
return 1
def OnListDoubleClick(self, cmd, code):
if code == win32con.LBN_DBLCLK:
self.OnOK()
return 1
def OnOK(self):
lb = self.GetDlgItem(107)
self.selections = lb.GetSelTextItems()
self._obj_.OnOK()
def getItems(self):
return self.selections
def getNew(self):
return self.newitems
try:
win32ui.GetApp().RemoveDocTemplate(greptemplate)
except NameError:
pass
greptemplate = TheTemplate()

View File

@ -0,0 +1,685 @@
"""
Various utilities for running/importing a script
"""
import sys
import win32ui
import win32api
import win32con
import __main__
from pywin.mfc import dialog
from pywin.mfc.docview import TreeView
import os
import string
import traceback
import linecache
import bdb
from .cmdline import ParseArgs
RS_DEBUGGER_NONE = 0 # Dont run under the debugger.
RS_DEBUGGER_STEP = 1 # Start stepping under the debugger
RS_DEBUGGER_GO = 2 # Just run under the debugger, stopping only at break-points.
RS_DEBUGGER_PM = 3 # Dont run under debugger, but do post-mortem analysis on exception.
debugging_options = """No debugging
Step-through in the debugger
Run in the debugger
Post-Mortem of unhandled exceptions""".split(
"\n"
)
byte_cr = "\r".encode("ascii")
byte_lf = "\n".encode("ascii")
byte_crlf = "\r\n".encode("ascii")
# A dialog box for the "Run Script" command.
class DlgRunScript(dialog.Dialog):
"A class for the 'run script' dialog"
def __init__(self, bHaveDebugger):
dialog.Dialog.__init__(self, win32ui.IDD_RUN_SCRIPT)
self.AddDDX(win32ui.IDC_EDIT1, "script")
self.AddDDX(win32ui.IDC_EDIT2, "args")
self.AddDDX(win32ui.IDC_COMBO1, "debuggingType", "i")
self.HookCommand(self.OnBrowse, win32ui.IDC_BUTTON2)
self.bHaveDebugger = bHaveDebugger
def OnInitDialog(self):
rc = dialog.Dialog.OnInitDialog(self)
cbo = self.GetDlgItem(win32ui.IDC_COMBO1)
for o in debugging_options:
cbo.AddString(o)
cbo.SetCurSel(self["debuggingType"])
if not self.bHaveDebugger:
cbo.EnableWindow(0)
def OnBrowse(self, id, cmd):
openFlags = win32con.OFN_OVERWRITEPROMPT | win32con.OFN_FILEMUSTEXIST
dlg = win32ui.CreateFileDialog(
1, None, None, openFlags, "Python Scripts (*.py)|*.py||", self
)
dlg.SetOFNTitle("Run Script")
if dlg.DoModal() != win32con.IDOK:
return 0
self["script"] = dlg.GetPathName()
self.UpdateData(0)
return 0
def GetDebugger():
"""Get the default Python debugger. Returns the debugger, or None.
It is assumed the debugger has a standard "pdb" defined interface.
Currently always returns the 'pywin.debugger' debugger, or None
(pdb is _not_ returned as it is not effective in this GUI environment)
"""
try:
import pywin.debugger
return pywin.debugger
except ImportError:
return None
def IsOnPythonPath(path):
"Given a path only, see if it is on the Pythonpath. Assumes path is a full path spec."
# must check that the command line arg's path is in sys.path
for syspath in sys.path:
try:
# Python 1.5 and later allows an empty sys.path entry.
if syspath and win32ui.FullPath(syspath) == path:
return 1
except win32ui.error as details:
print(
"Warning: The sys.path entry '%s' is invalid\n%s" % (syspath, details)
)
return 0
def GetPackageModuleName(fileName):
"""Given a filename, return (module name, new path).
eg - given "c:\a\b\c\my.py", return ("b.c.my",None) if "c:\a" is on sys.path.
If no package found, will return ("my", "c:\a\b\c")
"""
path, fname = os.path.split(fileName)
path = origPath = win32ui.FullPath(path)
fname = os.path.splitext(fname)[0]
modBits = []
newPathReturn = None
if not IsOnPythonPath(path):
# Module not directly on the search path - see if under a package.
while len(path) > 3: # ie 'C:\'
path, modBit = os.path.split(path)
modBits.append(modBit)
# If on path, _and_ existing package of that name loaded.
if (
IsOnPythonPath(path)
and modBit in sys.modules
and (
os.path.exists(os.path.join(path, modBit, "__init__.py"))
or os.path.exists(os.path.join(path, modBit, "__init__.pyc"))
or os.path.exists(os.path.join(path, modBit, "__init__.pyo"))
)
):
modBits.reverse()
return ".".join(modBits) + "." + fname, newPathReturn
# Not found - look a level higher
else:
newPathReturn = origPath
return fname, newPathReturn
def GetActiveView():
"""Gets the edit control (eg, EditView) with the focus, or None"""
try:
childFrame, bIsMaximised = win32ui.GetMainFrame().MDIGetActive()
return childFrame.GetActiveView()
except win32ui.error:
return None
def GetActiveEditControl():
view = GetActiveView()
if view is None:
return None
if hasattr(view, "SCIAddText"): # Is it a scintilla control?
return view
try:
return view.GetRichEditCtrl()
except AttributeError:
pass
try:
return view.GetEditCtrl()
except AttributeError:
pass
def GetActiveEditorDocument():
"""Returns the active editor document and view, or (None,None) if no
active document or its not an editor document.
"""
view = GetActiveView()
if view is None or isinstance(view, TreeView):
return (None, None)
doc = view.GetDocument()
if hasattr(doc, "MarkerAdd"): # Is it an Editor document?
return doc, view
return (None, None)
def GetActiveFileName(bAutoSave=1):
"""Gets the file name for the active frame, saving it if necessary.
Returns None if it cant be found, or raises KeyboardInterrupt.
"""
pathName = None
active = GetActiveView()
if active is None:
return None
try:
doc = active.GetDocument()
pathName = doc.GetPathName()
if bAutoSave and (
len(pathName) > 0
or doc.GetTitle()[:8] == "Untitled"
or doc.GetTitle()[:6] == "Script"
): # if not a special purpose window
if doc.IsModified():
try:
doc.OnSaveDocument(pathName)
pathName = doc.GetPathName()
# clear the linecache buffer
linecache.clearcache()
except win32ui.error:
raise KeyboardInterrupt
except (win32ui.error, AttributeError):
pass
if not pathName:
return None
return pathName
lastScript = ""
lastArgs = ""
lastDebuggingType = RS_DEBUGGER_NONE
def RunScript(defName=None, defArgs=None, bShowDialog=1, debuggingType=None):
global lastScript, lastArgs, lastDebuggingType
_debugger_stop_frame_ = 1 # Magic variable so the debugger will hide me!
# Get the debugger - may be None!
debugger = GetDebugger()
if defName is None:
try:
pathName = GetActiveFileName()
except KeyboardInterrupt:
return # User cancelled save.
else:
pathName = defName
if not pathName:
pathName = lastScript
if defArgs is None:
args = ""
if pathName == lastScript:
args = lastArgs
else:
args = defArgs
if debuggingType is None:
debuggingType = lastDebuggingType
if not pathName or bShowDialog:
dlg = DlgRunScript(debugger is not None)
dlg["script"] = pathName
dlg["args"] = args
dlg["debuggingType"] = debuggingType
if dlg.DoModal() != win32con.IDOK:
return
script = dlg["script"]
args = dlg["args"]
debuggingType = dlg["debuggingType"]
if not script:
return
if debuggingType == RS_DEBUGGER_GO and debugger is not None:
# This may surprise users - they select "Run under debugger", but
# it appears not to! Only warn when they pick from the dialog!
# First - ensure the debugger is activated to pickup any break-points
# set in the editor.
try:
# Create the debugger, but _dont_ init the debugger GUI.
rd = debugger._GetCurrentDebugger()
except AttributeError:
rd = None
if rd is not None and len(rd.breaks) == 0:
msg = "There are no active break-points.\r\n\r\nSelecting this debug option without any\r\nbreak-points is unlikely to have the desired effect\r\nas the debugger is unlikely to be invoked..\r\n\r\nWould you like to step-through in the debugger instead?"
rc = win32ui.MessageBox(
msg,
win32ui.LoadString(win32ui.IDR_DEBUGGER),
win32con.MB_YESNOCANCEL | win32con.MB_ICONINFORMATION,
)
if rc == win32con.IDCANCEL:
return
if rc == win32con.IDYES:
debuggingType = RS_DEBUGGER_STEP
lastDebuggingType = debuggingType
lastScript = script
lastArgs = args
else:
script = pathName
# try and open the script.
if (
len(os.path.splitext(script)[1]) == 0
): # check if no extension supplied, and give one.
script = script + ".py"
# If no path specified, try and locate the file
path, fnameonly = os.path.split(script)
if len(path) == 0:
try:
os.stat(fnameonly) # See if it is OK as is...
script = fnameonly
except os.error:
fullScript = LocatePythonFile(script)
if fullScript is None:
win32ui.MessageBox("The file '%s' can not be located" % script)
return
script = fullScript
else:
path = win32ui.FullPath(path)
if not IsOnPythonPath(path):
sys.path.append(path)
# py3k fun: If we use text mode to open the file, we get \r\n
# translated so Python allows the syntax (good!), but we get back
# text already decoded from the default encoding (bad!) and Python
# ignores any encoding decls (bad!). If we use binary mode we get
# the raw bytes and Python looks at the encoding (good!) but \r\n
# chars stay in place so Python throws a syntax error (bad!).
# So: so the binary thing and manually normalize \r\n.
try:
f = open(script, "rb")
except IOError as exc:
win32ui.MessageBox(
"The file could not be opened - %s (%d)" % (exc.strerror, exc.errno)
)
return
# Get the source-code - as above, normalize \r\n
code = f.read().replace(byte_crlf, byte_lf).replace(byte_cr, byte_lf) + byte_lf
# Remember and hack sys.argv for the script.
oldArgv = sys.argv
sys.argv = ParseArgs(args)
sys.argv.insert(0, script)
# sys.path[0] is the path of the script
oldPath0 = sys.path[0]
newPath0 = os.path.split(script)[0]
if not oldPath0: # if sys.path[0] is empty
sys.path[0] = newPath0
insertedPath0 = 0
else:
sys.path.insert(0, newPath0)
insertedPath0 = 1
bWorked = 0
win32ui.DoWaitCursor(1)
base = os.path.split(script)[1]
# Allow windows to repaint before starting.
win32ui.PumpWaitingMessages()
win32ui.SetStatusText("Running script %s..." % base, 1)
exitCode = 0
from pywin.framework import interact
# Check the debugger flags
if debugger is None and (debuggingType != RS_DEBUGGER_NONE):
win32ui.MessageBox(
"No debugger is installed. Debugging options have been ignored!"
)
debuggingType = RS_DEBUGGER_NONE
# Get a code object - ignore the debugger for this, as it is probably a syntax error
# at this point
try:
codeObject = compile(code, script, "exec")
except:
# Almost certainly a syntax error!
_HandlePythonFailure("run script", script)
# No code object which to run/debug.
return
__main__.__file__ = script
try:
if debuggingType == RS_DEBUGGER_STEP:
debugger.run(codeObject, __main__.__dict__, start_stepping=1)
elif debuggingType == RS_DEBUGGER_GO:
debugger.run(codeObject, __main__.__dict__, start_stepping=0)
else:
# Post mortem or no debugging
exec(codeObject, __main__.__dict__)
bWorked = 1
except bdb.BdbQuit:
# Dont print tracebacks when the debugger quit, but do print a message.
print("Debugging session cancelled.")
exitCode = 1
bWorked = 1
except SystemExit as code:
exitCode = code
bWorked = 1
except KeyboardInterrupt:
# Consider this successful, as we dont want the debugger.
# (but we do want a traceback!)
if interact.edit and interact.edit.currentView:
interact.edit.currentView.EnsureNoPrompt()
traceback.print_exc()
if interact.edit and interact.edit.currentView:
interact.edit.currentView.AppendToPrompt([])
bWorked = 1
except:
if interact.edit and interact.edit.currentView:
interact.edit.currentView.EnsureNoPrompt()
traceback.print_exc()
if interact.edit and interact.edit.currentView:
interact.edit.currentView.AppendToPrompt([])
if debuggingType == RS_DEBUGGER_PM:
debugger.pm()
del __main__.__file__
sys.argv = oldArgv
if insertedPath0:
del sys.path[0]
else:
sys.path[0] = oldPath0
f.close()
if bWorked:
win32ui.SetStatusText("Script '%s' returned exit code %s" % (script, exitCode))
else:
win32ui.SetStatusText("Exception raised while running script %s" % base)
try:
sys.stdout.flush()
except AttributeError:
pass
win32ui.DoWaitCursor(0)
def ImportFile():
"""This code looks for the current window, and determines if it can be imported. If not,
it will prompt for a file name, and allow it to be imported."""
try:
pathName = GetActiveFileName()
except KeyboardInterrupt:
pathName = None
if pathName is not None:
if os.path.splitext(pathName)[1].lower() not in (".py", ".pyw", ".pyx"):
pathName = None
if pathName is None:
openFlags = win32con.OFN_OVERWRITEPROMPT | win32con.OFN_FILEMUSTEXIST
dlg = win32ui.CreateFileDialog(
1, None, None, openFlags, "Python Scripts (*.py;*.pyw)|*.py;*.pyw;*.pyx||"
)
dlg.SetOFNTitle("Import Script")
if dlg.DoModal() != win32con.IDOK:
return 0
pathName = dlg.GetPathName()
# If already imported, dont look for package
path, modName = os.path.split(pathName)
modName, modExt = os.path.splitext(modName)
newPath = None
# note that some packages (*cough* email *cough*) use "lazy importers"
# meaning sys.modules can change as a side-effect of looking at
# module.__file__ - so we must take a copy (ie, items() in py2k,
# list(items()) in py3k)
for key, mod in list(sys.modules.items()):
if getattr(mod, "__file__", None):
fname = mod.__file__
base, ext = os.path.splitext(fname)
if ext.lower() in [".pyo", ".pyc"]:
ext = ".py"
fname = base + ext
if win32ui.ComparePath(fname, pathName):
modName = key
break
else: # for not broken
modName, newPath = GetPackageModuleName(pathName)
if newPath:
sys.path.append(newPath)
if modName in sys.modules:
bNeedReload = 1
what = "reload"
else:
what = "import"
bNeedReload = 0
win32ui.SetStatusText(what.capitalize() + "ing module...", 1)
win32ui.DoWaitCursor(1)
# win32ui.GetMainFrame().BeginWaitCursor()
try:
# always do an import, as it is cheap if it's already loaded. This ensures
# it is in our name space.
codeObj = compile("import " + modName, "<auto import>", "exec")
except SyntaxError:
win32ui.SetStatusText('Invalid filename for import: "' + modName + '"')
return
try:
exec(codeObj, __main__.__dict__)
mod = sys.modules.get(modName)
if bNeedReload:
from importlib import reload
mod = reload(sys.modules[modName])
win32ui.SetStatusText(
"Successfully "
+ what
+ "ed module '"
+ modName
+ "': %s" % getattr(mod, "__file__", "<unkown file>")
)
except:
_HandlePythonFailure(what)
win32ui.DoWaitCursor(0)
def CheckFile():
"""This code looks for the current window, and gets Python to check it
without actually executing any code (ie, by compiling only)
"""
try:
pathName = GetActiveFileName()
except KeyboardInterrupt:
return
what = "check"
win32ui.SetStatusText(what.capitalize() + "ing module...", 1)
win32ui.DoWaitCursor(1)
try:
f = open(pathName)
except IOError as details:
print("Cant open file '%s' - %s" % (pathName, details))
return
try:
code = f.read() + "\n"
finally:
f.close()
try:
codeObj = compile(code, pathName, "exec")
if RunTabNanny(pathName):
win32ui.SetStatusText(
"Python and the TabNanny successfully checked the file '"
+ os.path.basename(pathName)
+ "'"
)
except SyntaxError:
_HandlePythonFailure(what, pathName)
except:
traceback.print_exc()
_HandlePythonFailure(what)
win32ui.DoWaitCursor(0)
def RunTabNanny(filename):
import io as io
tabnanny = FindTabNanny()
if tabnanny is None:
win32ui.MessageBox("The TabNanny is not around, so the children can run amok!")
return
# Capture the tab-nanny output
newout = io.StringIO()
old_out = sys.stderr, sys.stdout
sys.stderr = sys.stdout = newout
try:
tabnanny.check(filename)
finally:
# Restore output
sys.stderr, sys.stdout = old_out
data = newout.getvalue()
if data:
try:
lineno = data.split()[1]
lineno = int(lineno)
_JumpToPosition(filename, lineno)
try: # Try and display whitespace
GetActiveEditControl().SCISetViewWS(1)
except:
pass
win32ui.SetStatusText("The TabNanny found trouble at line %d" % lineno)
except (IndexError, TypeError, ValueError):
print("The tab nanny complained, but I cant see where!")
print(data)
return 0
return 1
def _JumpToPosition(fileName, lineno, col=1):
JumpToDocument(fileName, lineno, col)
def JumpToDocument(fileName, lineno=0, col=1, nChars=0, bScrollToTop=0):
# Jump to the position in a file.
# If lineno is <= 0, dont move the position - just open/restore.
# if nChars > 0, select that many characters.
# if bScrollToTop, the specified line will be moved to the top of the window
# (eg, bScrollToTop should be false when jumping to an error line to retain the
# context, but true when jumping to a method defn, where we want the full body.
# Return the view which is editing the file, or None on error.
doc = win32ui.GetApp().OpenDocumentFile(fileName)
if doc is None:
return None
frame = doc.GetFirstView().GetParentFrame()
try:
view = frame.GetEditorView()
if frame.GetActiveView() != view:
frame.SetActiveView(view)
frame.AutoRestore()
except AttributeError: # Not an editor frame??
view = doc.GetFirstView()
if lineno > 0:
charNo = view.LineIndex(lineno - 1)
start = charNo + col - 1
size = view.GetTextLength()
try:
view.EnsureCharsVisible(charNo)
except AttributeError:
print("Doesnt appear to be one of our views?")
view.SetSel(min(start, size), min(start + nChars, size))
if bScrollToTop:
curTop = view.GetFirstVisibleLine()
nScroll = (lineno - 1) - curTop
view.LineScroll(nScroll, 0)
view.SetFocus()
return view
def _HandlePythonFailure(what, syntaxErrorPathName=None):
typ, details, tb = sys.exc_info()
if isinstance(details, SyntaxError):
try:
msg, (fileName, line, col, text) = details
if (not fileName or fileName == "<string>") and syntaxErrorPathName:
fileName = syntaxErrorPathName
_JumpToPosition(fileName, line, col)
except (TypeError, ValueError):
msg = str(details)
win32ui.SetStatusText("Failed to " + what + " - syntax error - %s" % msg)
else:
traceback.print_exc()
win32ui.SetStatusText("Failed to " + what + " - " + str(details))
tb = None # Clean up a cycle.
# Find the Python TabNanny in either the standard library or the Python Tools/Scripts directory.
def FindTabNanny():
try:
return __import__("tabnanny")
except ImportError:
pass
# OK - not in the standard library - go looking.
filename = "tabnanny.py"
try:
path = win32api.RegQueryValue(
win32con.HKEY_LOCAL_MACHINE,
"SOFTWARE\\Python\\PythonCore\\%s\\InstallPath" % (sys.winver),
)
except win32api.error:
print("WARNING - The Python registry does not have an 'InstallPath' setting")
print(" The file '%s' can not be located" % (filename))
return None
fname = os.path.join(path, "Tools\\Scripts\\%s" % filename)
try:
os.stat(fname)
except os.error:
print(
"WARNING - The file '%s' can not be located in path '%s'" % (filename, path)
)
return None
tabnannyhome, tabnannybase = os.path.split(fname)
tabnannybase = os.path.splitext(tabnannybase)[0]
# Put tab nanny at the top of the path.
sys.path.insert(0, tabnannyhome)
try:
return __import__(tabnannybase)
finally:
# remove the tab-nanny from the path
del sys.path[0]
def LocatePythonFile(fileName, bBrowseIfDir=1):
"Given a file name, return a fully qualified file name, or None"
# first look for the exact file as specified
if not os.path.isfile(fileName):
# Go looking!
baseName = fileName
for path in sys.path:
fileName = os.path.abspath(os.path.join(path, baseName))
if os.path.isdir(fileName):
if bBrowseIfDir:
d = win32ui.CreateFileDialog(
1, "*.py", None, 0, "Python Files (*.py)|*.py|All files|*.*"
)
d.SetOFNInitialDir(fileName)
rc = d.DoModal()
if rc == win32con.IDOK:
fileName = d.GetPathName()
break
else:
return None
else:
fileName = fileName + ".py"
if os.path.isfile(fileName):
break # Found it!
else: # for not broken out of
return None
return win32ui.FullPath(fileName)

View File

@ -0,0 +1,747 @@
# SGrepMDI is by Gordon McMillan (gmcm@hypernet.com)
# It does basically what Find In Files does in MSVC with a couple enhancements.
# - It saves any directories in the app's ini file (if you want to get rid
# of them you'll have to edit the file)
# - "Directories" can be directories,
# - semicolon separated lists of "directories",
# - environment variables that evaluate to "directories",
# - registry path names that evaluate to "directories",
# - all of which is recursive, so you can mix them all up.
# - It is MDI, so you can 'nest' greps and return to earlier ones,
# (ie, have multiple results open at the same time)
# - Like FIF, double clicking a line opens an editor and takes you to the line.
# - You can highlight text, right click and start a new grep with the selected
# text as search pattern and same directories etc as before.
# - You can save grep parameters (so you don't lose your hardearned pattern)
# from File|Save
# - You can save grep results by right clicking in the result window.
# Hats off to Mark Hammond for providing an environment where I could cobble
# something like this together in a couple evenings!
import win32ui
import win32api
from pywin.mfc import docview, dialog, window
import win32con
import string
import re
import glob
import os
import stat
import glob
from . import scriptutils
def getsubdirs(d):
dlist = []
flist = glob.glob(d + "\\*")
for f in flist:
if os.path.isdir(f):
dlist.append(f)
dlist = dlist + getsubdirs(f)
return dlist
class dirpath:
def __init__(self, str, recurse=0):
dp = str.split(";")
dirs = {}
for d in dp:
if os.path.isdir(d):
d = d.lower()
if d not in dirs:
dirs[d] = None
if recurse:
subdirs = getsubdirs(d)
for sd in subdirs:
sd = sd.lower()
if sd not in dirs:
dirs[sd] = None
elif os.path.isfile(d):
pass
else:
x = None
if d in os.environ:
x = dirpath(os.environ[d])
elif d[:5] == "HKEY_":
keystr = d.split("\\")
try:
root = eval("win32con." + keystr[0])
except:
win32ui.MessageBox(
"Can't interpret registry key name '%s'" % keystr[0]
)
try:
subkey = "\\".join(keystr[1:])
val = win32api.RegQueryValue(root, subkey)
if val:
x = dirpath(val)
else:
win32ui.MessageBox(
"Registry path '%s' did not return a path entry" % d
)
except:
win32ui.MessageBox(
"Can't interpret registry key value: %s" % keystr[1:]
)
else:
win32ui.MessageBox("Directory '%s' not found" % d)
if x:
for xd in x:
if xd not in dirs:
dirs[xd] = None
if recurse:
subdirs = getsubdirs(xd)
for sd in subdirs:
sd = sd.lower()
if sd not in dirs:
dirs[sd] = None
self.dirs = []
for d in list(dirs.keys()):
self.dirs.append(d)
def __getitem__(self, key):
return self.dirs[key]
def __len__(self):
return len(self.dirs)
def __setitem__(self, key, value):
self.dirs[key] = value
def __delitem__(self, key):
del self.dirs[key]
def __getslice__(self, lo, hi):
return self.dirs[lo:hi]
def __setslice__(self, lo, hi, seq):
self.dirs[lo:hi] = seq
def __delslice__(self, lo, hi):
del self.dirs[lo:hi]
def __add__(self, other):
if type(other) == type(self) or type(other) == type([]):
return self.dirs + other.dirs
def __radd__(self, other):
if type(other) == type(self) or type(other) == type([]):
return other.dirs + self.dirs
# Group(1) is the filename, group(2) is the lineno.
# regexGrepResult=regex.compile("^\\([a-zA-Z]:.*\\)(\\([0-9]+\\))")
regexGrep = re.compile(r"^([a-zA-Z]:[^(]*)\(([0-9]+)\)")
# these are the atom numbers defined by Windows for basic dialog controls
BUTTON = 0x80
EDIT = 0x81
STATIC = 0x82
LISTBOX = 0x83
SCROLLBAR = 0x84
COMBOBOX = 0x85
class GrepTemplate(docview.RichEditDocTemplate):
def __init__(self):
docview.RichEditDocTemplate.__init__(
self, win32ui.IDR_TEXTTYPE, GrepDocument, GrepFrame, GrepView
)
self.SetDocStrings("\nGrep\nGrep\nGrep params (*.grep)\n.grep\n\n\n")
win32ui.GetApp().AddDocTemplate(self)
self.docparams = None
def MatchDocType(self, fileName, fileType):
doc = self.FindOpenDocument(fileName)
if doc:
return doc
ext = os.path.splitext(fileName)[1].lower()
if ext == ".grep":
return win32ui.CDocTemplate_Confidence_yesAttemptNative
return win32ui.CDocTemplate_Confidence_noAttempt
def setParams(self, params):
self.docparams = params
def readParams(self):
tmp = self.docparams
self.docparams = None
return tmp
class GrepFrame(window.MDIChildWnd):
# The template and doc params will one day be removed.
def __init__(self, wnd=None):
window.MDIChildWnd.__init__(self, wnd)
class GrepDocument(docview.RichEditDoc):
def __init__(self, template):
docview.RichEditDoc.__init__(self, template)
self.dirpattern = ""
self.filpattern = ""
self.greppattern = ""
self.casesensitive = 1
self.recurse = 1
self.verbose = 0
def OnOpenDocument(self, fnm):
# this bizarre stuff with params is so right clicking in a result window
# and starting a new grep can communicate the default parameters to the
# new grep.
try:
params = open(fnm, "r").read()
except:
params = None
self.setInitParams(params)
return self.OnNewDocument()
def OnCloseDocument(self):
try:
win32ui.GetApp().DeleteIdleHandler(self.SearchFile)
except:
pass
return self._obj_.OnCloseDocument()
def saveInitParams(self):
# Only save the flags, not the text boxes.
paramstr = "\t%s\t\t%d\t%d" % (
self.filpattern,
self.casesensitive,
self.recurse,
)
win32ui.WriteProfileVal("Grep", "Params", paramstr)
def setInitParams(self, paramstr):
if paramstr is None:
paramstr = win32ui.GetProfileVal("Grep", "Params", "\t\t\t1\t0\t0")
params = paramstr.split("\t")
if len(params) < 3:
params = params + [""] * (3 - len(params))
if len(params) < 6:
params = params + [0] * (6 - len(params))
self.dirpattern = params[0]
self.filpattern = params[1]
self.greppattern = params[2]
self.casesensitive = int(params[3])
self.recurse = int(params[4])
self.verbose = int(params[5])
# setup some reasonable defaults.
if not self.dirpattern:
try:
editor = win32ui.GetMainFrame().MDIGetActive()[0].GetEditorView()
self.dirpattern = os.path.abspath(
os.path.dirname(editor.GetDocument().GetPathName())
)
except (AttributeError, win32ui.error):
self.dirpattern = os.getcwd()
if not self.filpattern:
self.filpattern = "*.py"
def OnNewDocument(self):
if self.dirpattern == "":
self.setInitParams(greptemplate.readParams())
d = GrepDialog(
self.dirpattern,
self.filpattern,
self.greppattern,
self.casesensitive,
self.recurse,
self.verbose,
)
if d.DoModal() == win32con.IDOK:
self.dirpattern = d["dirpattern"]
self.filpattern = d["filpattern"]
self.greppattern = d["greppattern"]
self.casesensitive = d["casesensitive"]
self.recurse = d["recursive"]
self.verbose = d["verbose"]
self.doSearch()
self.saveInitParams()
return 1
return 0 # cancelled - return zero to stop frame creation.
def doSearch(self):
self.dp = dirpath(self.dirpattern, self.recurse)
self.SetTitle("Grep for %s in %s" % (self.greppattern, self.filpattern))
# self.text = []
self.GetFirstView().Append("#Search " + self.dirpattern + "\n")
if self.verbose:
self.GetFirstView().Append("# =" + repr(self.dp.dirs) + "\n")
self.GetFirstView().Append("# Files " + self.filpattern + "\n")
self.GetFirstView().Append("# For " + self.greppattern + "\n")
self.fplist = self.filpattern.split(";")
if self.casesensitive:
self.pat = re.compile(self.greppattern)
else:
self.pat = re.compile(self.greppattern, re.IGNORECASE)
win32ui.SetStatusText("Searching. Please wait...", 0)
self.dpndx = self.fpndx = 0
self.fndx = -1
if not self.dp:
self.GetFirstView().Append(
"# ERROR: '%s' does not resolve to any search locations"
% self.dirpattern
)
self.SetModifiedFlag(0)
else:
self.flist = glob.glob(self.dp[0] + "\\" + self.fplist[0])
win32ui.GetApp().AddIdleHandler(self.SearchFile)
def SearchFile(self, handler, count):
self.fndx = self.fndx + 1
if self.fndx < len(self.flist):
f = self.flist[self.fndx]
if self.verbose:
self.GetFirstView().Append("# .." + f + "\n")
# Directories may match the file type pattern, and files may be removed
# while grep is running
if os.path.isfile(f):
win32ui.SetStatusText("Searching " + f, 0)
lines = open(f, "r").readlines()
for i in range(len(lines)):
line = lines[i]
if self.pat.search(line) != None:
self.GetFirstView().Append(f + "(" + repr(i + 1) + ") " + line)
else:
self.fndx = -1
self.fpndx = self.fpndx + 1
if self.fpndx < len(self.fplist):
self.flist = glob.glob(
self.dp[self.dpndx] + "\\" + self.fplist[self.fpndx]
)
else:
self.fpndx = 0
self.dpndx = self.dpndx + 1
if self.dpndx < len(self.dp):
self.flist = glob.glob(
self.dp[self.dpndx] + "\\" + self.fplist[self.fpndx]
)
else:
win32ui.SetStatusText("Search complete.", 0)
self.SetModifiedFlag(0) # default to not modified.
try:
win32ui.GetApp().DeleteIdleHandler(self.SearchFile)
except:
pass
return 0
return 1
def GetParams(self):
return (
self.dirpattern
+ "\t"
+ self.filpattern
+ "\t"
+ self.greppattern
+ "\t"
+ repr(self.casesensitive)
+ "\t"
+ repr(self.recurse)
+ "\t"
+ repr(self.verbose)
)
def OnSaveDocument(self, filename):
# print 'OnSaveDocument() filename=',filename
savefile = open(filename, "wb")
txt = self.GetParams() + "\n"
# print 'writing',txt
savefile.write(txt)
savefile.close()
self.SetModifiedFlag(0)
return 1
ID_OPEN_FILE = 0xE400
ID_GREP = 0xE401
ID_SAVERESULTS = 0x402
ID_TRYAGAIN = 0x403
class GrepView(docview.RichEditView):
def __init__(self, doc):
docview.RichEditView.__init__(self, doc)
self.SetWordWrap(win32ui.CRichEditView_WrapNone)
self.HookHandlers()
def OnInitialUpdate(self):
rc = self._obj_.OnInitialUpdate()
format = (-402653169, 0, 200, 0, 0, 0, 49, "Courier New")
self.SetDefaultCharFormat(format)
return rc
def HookHandlers(self):
self.HookMessage(self.OnRClick, win32con.WM_RBUTTONDOWN)
self.HookCommand(self.OnCmdOpenFile, ID_OPEN_FILE)
self.HookCommand(self.OnCmdGrep, ID_GREP)
self.HookCommand(self.OnCmdSave, ID_SAVERESULTS)
self.HookCommand(self.OnTryAgain, ID_TRYAGAIN)
self.HookMessage(self.OnLDblClick, win32con.WM_LBUTTONDBLCLK)
def OnLDblClick(self, params):
line = self.GetLine()
regexGrepResult = regexGrep.match(line)
if regexGrepResult:
fname = regexGrepResult.group(1)
line = int(regexGrepResult.group(2))
scriptutils.JumpToDocument(fname, line)
return 0 # dont pass on
return 1 # pass it on by default.
def OnRClick(self, params):
menu = win32ui.CreatePopupMenu()
flags = win32con.MF_STRING | win32con.MF_ENABLED
lineno = self._obj_.LineFromChar(-1) # selection or current line
line = self._obj_.GetLine(lineno)
regexGrepResult = regexGrep.match(line)
if regexGrepResult:
self.fnm = regexGrepResult.group(1)
self.lnnum = int(regexGrepResult.group(2))
menu.AppendMenu(flags, ID_OPEN_FILE, "&Open " + self.fnm)
menu.AppendMenu(win32con.MF_SEPARATOR)
menu.AppendMenu(flags, ID_TRYAGAIN, "&Try Again")
charstart, charend = self._obj_.GetSel()
if charstart != charend:
linestart = self._obj_.LineIndex(lineno)
self.sel = line[charstart - linestart : charend - linestart]
menu.AppendMenu(flags, ID_GREP, "&Grep for " + self.sel)
menu.AppendMenu(win32con.MF_SEPARATOR)
menu.AppendMenu(flags, win32ui.ID_EDIT_CUT, "Cu&t")
menu.AppendMenu(flags, win32ui.ID_EDIT_COPY, "&Copy")
menu.AppendMenu(flags, win32ui.ID_EDIT_PASTE, "&Paste")
menu.AppendMenu(flags, win32con.MF_SEPARATOR)
menu.AppendMenu(flags, win32ui.ID_EDIT_SELECT_ALL, "&Select all")
menu.AppendMenu(flags, win32con.MF_SEPARATOR)
menu.AppendMenu(flags, ID_SAVERESULTS, "Sa&ve results")
menu.TrackPopupMenu(params[5])
return 0
def OnCmdOpenFile(self, cmd, code):
doc = win32ui.GetApp().OpenDocumentFile(self.fnm)
if doc:
vw = doc.GetFirstView()
# hope you have an editor that implements GotoLine()!
try:
vw.GotoLine(int(self.lnnum))
except:
pass
return 0
def OnCmdGrep(self, cmd, code):
curparamsstr = self.GetDocument().GetParams()
params = curparamsstr.split("\t")
params[2] = self.sel
greptemplate.setParams("\t".join(params))
greptemplate.OpenDocumentFile()
return 0
def OnTryAgain(self, cmd, code):
greptemplate.setParams(self.GetDocument().GetParams())
greptemplate.OpenDocumentFile()
return 0
def OnCmdSave(self, cmd, code):
flags = win32con.OFN_OVERWRITEPROMPT
dlg = win32ui.CreateFileDialog(
0, None, None, flags, "Text Files (*.txt)|*.txt||", self
)
dlg.SetOFNTitle("Save Results As")
if dlg.DoModal() == win32con.IDOK:
pn = dlg.GetPathName()
self._obj_.SaveTextFile(pn)
return 0
def Append(self, strng):
numlines = self.GetLineCount()
endpos = self.LineIndex(numlines - 1) + len(self.GetLine(numlines - 1))
self.SetSel(endpos, endpos)
self.ReplaceSel(strng)
class GrepDialog(dialog.Dialog):
def __init__(self, dp, fp, gp, cs, r, v):
style = (
win32con.DS_MODALFRAME
| win32con.WS_POPUP
| win32con.WS_VISIBLE
| win32con.WS_CAPTION
| win32con.WS_SYSMENU
| win32con.DS_SETFONT
)
CS = win32con.WS_CHILD | win32con.WS_VISIBLE
tmp = [
["Grep", (0, 0, 210, 90), style, None, (8, "MS Sans Serif")],
]
tmp.append([STATIC, "Grep For:", -1, (7, 7, 50, 9), CS])
tmp.append(
[
EDIT,
gp,
101,
(52, 7, 144, 11),
CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER,
]
)
tmp.append([STATIC, "Directories:", -1, (7, 20, 50, 9), CS])
tmp.append(
[
EDIT,
dp,
102,
(52, 20, 128, 11),
CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER,
]
)
tmp.append(
[
BUTTON,
"...",
110,
(182, 20, 16, 11),
CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,
]
)
tmp.append([STATIC, "File types:", -1, (7, 33, 50, 9), CS])
tmp.append(
[
EDIT,
fp,
103,
(52, 33, 128, 11),
CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER,
]
)
tmp.append(
[
BUTTON,
"...",
111,
(182, 33, 16, 11),
CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,
]
)
tmp.append(
[
BUTTON,
"Case sensitive",
104,
(7, 45, 72, 9),
CS
| win32con.BS_AUTOCHECKBOX
| win32con.BS_LEFTTEXT
| win32con.WS_TABSTOP,
]
)
tmp.append(
[
BUTTON,
"Subdirectories",
105,
(7, 56, 72, 9),
CS
| win32con.BS_AUTOCHECKBOX
| win32con.BS_LEFTTEXT
| win32con.WS_TABSTOP,
]
)
tmp.append(
[
BUTTON,
"Verbose",
106,
(7, 67, 72, 9),
CS
| win32con.BS_AUTOCHECKBOX
| win32con.BS_LEFTTEXT
| win32con.WS_TABSTOP,
]
)
tmp.append(
[
BUTTON,
"OK",
win32con.IDOK,
(166, 53, 32, 12),
CS | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP,
]
)
tmp.append(
[
BUTTON,
"Cancel",
win32con.IDCANCEL,
(166, 67, 32, 12),
CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,
]
)
dialog.Dialog.__init__(self, tmp)
self.AddDDX(101, "greppattern")
self.AddDDX(102, "dirpattern")
self.AddDDX(103, "filpattern")
self.AddDDX(104, "casesensitive")
self.AddDDX(105, "recursive")
self.AddDDX(106, "verbose")
self._obj_.data["greppattern"] = gp
self._obj_.data["dirpattern"] = dp
self._obj_.data["filpattern"] = fp
self._obj_.data["casesensitive"] = cs
self._obj_.data["recursive"] = r
self._obj_.data["verbose"] = v
self.HookCommand(self.OnMoreDirectories, 110)
self.HookCommand(self.OnMoreFiles, 111)
def OnMoreDirectories(self, cmd, code):
self.getMore("Grep\\Directories", "dirpattern")
def OnMoreFiles(self, cmd, code):
self.getMore("Grep\\File Types", "filpattern")
def getMore(self, section, key):
self.UpdateData(1)
# get the items out of the ini file
ini = win32ui.GetProfileFileName()
secitems = win32api.GetProfileSection(section, ini)
items = []
for secitem in secitems:
items.append(secitem.split("=")[1])
dlg = GrepParamsDialog(items)
if dlg.DoModal() == win32con.IDOK:
itemstr = ";".join(dlg.getItems())
self._obj_.data[key] = itemstr
# update the ini file with dlg.getNew()
i = 0
newitems = dlg.getNew()
if newitems:
items = items + newitems
for item in items:
win32api.WriteProfileVal(section, repr(i), item, ini)
i = i + 1
self.UpdateData(0)
def OnOK(self):
self.UpdateData(1)
for id, name in [
(101, "greppattern"),
(102, "dirpattern"),
(103, "filpattern"),
]:
if not self[name]:
self.GetDlgItem(id).SetFocus()
win32api.MessageBeep()
win32ui.SetStatusText("Please enter a value")
return
self._obj_.OnOK()
class GrepParamsDialog(dialog.Dialog):
def __init__(self, items):
self.items = items
self.newitems = []
style = (
win32con.DS_MODALFRAME
| win32con.WS_POPUP
| win32con.WS_VISIBLE
| win32con.WS_CAPTION
| win32con.WS_SYSMENU
| win32con.DS_SETFONT
)
CS = win32con.WS_CHILD | win32con.WS_VISIBLE
tmp = [
["Grep Parameters", (0, 0, 205, 100), style, None, (8, "MS Sans Serif")],
]
tmp.append(
[
LISTBOX,
"",
107,
(7, 7, 150, 72),
CS
| win32con.LBS_MULTIPLESEL
| win32con.LBS_STANDARD
| win32con.LBS_HASSTRINGS
| win32con.WS_TABSTOP
| win32con.LBS_NOTIFY,
]
)
tmp.append(
[
BUTTON,
"OK",
win32con.IDOK,
(167, 7, 32, 12),
CS | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP,
]
)
tmp.append(
[
BUTTON,
"Cancel",
win32con.IDCANCEL,
(167, 23, 32, 12),
CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,
]
)
tmp.append([STATIC, "New:", -1, (2, 83, 15, 12), CS])
tmp.append(
[
EDIT,
"",
108,
(18, 83, 139, 12),
CS | win32con.WS_TABSTOP | win32con.ES_AUTOHSCROLL | win32con.WS_BORDER,
]
)
tmp.append(
[
BUTTON,
"Add",
109,
(167, 83, 32, 12),
CS | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP,
]
)
dialog.Dialog.__init__(self, tmp)
self.HookCommand(self.OnAddItem, 109)
self.HookCommand(self.OnListDoubleClick, 107)
def OnInitDialog(self):
lb = self.GetDlgItem(107)
for item in self.items:
lb.AddString(item)
return self._obj_.OnInitDialog()
def OnAddItem(self, cmd, code):
eb = self.GetDlgItem(108)
item = eb.GetLine(0)
self.newitems.append(item)
lb = self.GetDlgItem(107)
i = lb.AddString(item)
lb.SetSel(i, 1)
return 1
def OnListDoubleClick(self, cmd, code):
if code == win32con.LBN_DBLCLK:
self.OnOK()
return 1
def OnOK(self):
lb = self.GetDlgItem(107)
self.selections = lb.GetSelTextItems()
self._obj_.OnOK()
def getItems(self):
return self.selections
def getNew(self):
return self.newitems
try:
win32ui.GetApp().RemoveDocTemplate(greptemplate)
except NameError:
pass
greptemplate = GrepTemplate()

View File

@ -0,0 +1,78 @@
# startup.py
#
"The main application startup code for PythonWin."
#
# This does the basic command line handling.
# Keep this as short as possible, cos error output is only redirected if
# this runs OK. Errors in imported modules are much better - the messages go somewhere (not any more :-)
import sys
import os
import win32api
import win32ui
if not sys.argv:
# Initialize sys.argv from commandline. When sys.argv is empty list (
# different from [''] meaning "no cmd line arguments" ), then C
# bootstrapping or another method of invocation failed to initialize
# sys.argv and it will be done here. ( This was a workaround for a bug in
# win32ui but is retained for other situations. )
argv = win32api.CommandLineToArgv(win32api.GetCommandLine())
sys.argv = argv[1:]
if os.getcwd() not in sys.path and "." not in sys.path:
sys.path.insert(0, os.getcwd())
# You may wish to redirect error output somewhere useful if you have startup errors.
# eg, 'import win32traceutil' will do this for you.
# import win32traceutil # Just uncomment this line to see error output!
# An old class I used to use - generally only useful if Pythonwin is running under MSVC
# class DebugOutput:
# softspace=1
# def write(self,message):
# win32ui.OutputDebug(message)
# sys.stderr=sys.stdout=DebugOutput()
# To fix a problem with Pythonwin when started from the Pythonwin directory,
# we update the pywin path to ensure it is absolute.
# If it is indeed relative, it will be relative to our current directory.
# If its already absolute, then this will have no affect.
import pywin, pywin.framework
pywin.__path__[0] = win32ui.FullPath(pywin.__path__[0])
pywin.framework.__path__[0] = win32ui.FullPath(pywin.framework.__path__[0])
# make a few wierd sys values. This is so later we can clobber sys.argv to trick
# scripts when running under a GUI environment.
moduleName = "pywin.framework.intpyapp"
sys.appargvoffset = 0
sys.appargv = sys.argv[:]
# Must check for /app param here.
if len(sys.argv) >= 2 and sys.argv[0].lower() in ("/app", "-app"):
from . import cmdline
moduleName = cmdline.FixArgFileName(sys.argv[1])
sys.appargvoffset = 2
newargv = sys.argv[sys.appargvoffset :]
# newargv.insert(0, sys.argv[0])
sys.argv = newargv
# Import the application module.
__import__(moduleName)
try:
win32ui.GetApp()._obj_
# This worked - an app already exists - do nothing more
except (AttributeError, win32ui.error):
# This means either no app object exists at all, or the one
# that does exist does not have a Python class (ie, was created
# by the host .EXE). In this case, we do the "old style" init...
from . import app
if app.AppBuilder is None:
raise TypeError("No application object has been registered")
app.App = app.AppBuilder()

View File

@ -0,0 +1,176 @@
# Copyright (c) 2000 David Abrahams. Permission to copy, use, modify, sell
# and distribute this software is granted provided this copyright
# notice appears in all copies. This software is provided "as is" without
# express or implied warranty, and with no claim as to its suitability for
# any purpose.
"""Provides a class Stdin which can be used to emulate the regular old
sys.stdin for the PythonWin interactive window. Right now it just pops
up a raw_input() dialog. With luck, someone will integrate it into the
actual PythonWin interactive window someday.
WARNING: Importing this file automatically replaces sys.stdin with an
instance of Stdin (below). This is useful because you can just open
Stdin.py in PythonWin and hit the import button to get it set up right
if you don't feel like changing PythonWin's source. To put things back
the way they were, simply use this magic incantation:
import sys
sys.stdin = sys.stdin.real_file
"""
import sys
try:
get_input_line = raw_input # py2x
except NameError:
get_input_line = input # py3k
class Stdin:
def __init__(self):
self.real_file = sys.stdin # NOTE: Likely to be None in py3k
self.buffer = ""
self.closed = False
def __getattr__(self, name):
"""Forward most functions to the real sys.stdin for absolute realism."""
if self.real_file is None:
raise AttributeError(name)
return getattr(self.real_file, name)
def isatty(self):
"""Return 1 if the file is connected to a tty(-like) device, else 0."""
return 1
def read(self, size=-1):
"""Read at most size bytes from the file (less if the read
hits EOF or no more data is immediately available on a pipe,
tty or similar device). If the size argument is negative or
omitted, read all data until EOF is reached. The bytes are
returned as a string object. An empty string is returned when
EOF is encountered immediately. (For certain files, like ttys,
it makes sense to continue reading after an EOF is hit.)"""
result_size = self.__get_lines(size)
return self.__extract_from_buffer(result_size)
def readline(self, size=-1):
"""Read one entire line from the file. A trailing newline
character is kept in the string2.6 (but may be absent when a file ends
with an incomplete line). If the size argument is present and
non-negative, it is a maximum byte count (including the trailing
newline) and an incomplete line may be returned. An empty string is
returned when EOF is hit immediately. Note: unlike stdio's fgets(),
the returned string contains null characters ('\0') if they occurred
in the input.
"""
maximum_result_size = self.__get_lines(size, lambda buffer: "\n" in buffer)
if "\n" in self.buffer[:maximum_result_size]:
result_size = self.buffer.find("\n", 0, maximum_result_size) + 1
assert result_size > 0
else:
result_size = maximum_result_size
return self.__extract_from_buffer(result_size)
def __extract_from_buffer(self, character_count):
"""Remove the first character_count characters from the internal buffer and
return them.
"""
result = self.buffer[:character_count]
self.buffer = self.buffer[character_count:]
return result
def __get_lines(self, desired_size, done_reading=lambda buffer: False):
"""Keep adding lines to our internal buffer until done_reading(self.buffer)
is true or EOF has been reached or we have desired_size bytes in the buffer.
If desired_size < 0, we are never satisfied until we reach EOF. If done_reading
is not supplied, it is not consulted.
If desired_size < 0, returns the length of the internal buffer. Otherwise,
returns desired_size.
"""
while not done_reading(self.buffer) and (
desired_size < 0 or len(self.buffer) < desired_size
):
try:
self.__get_line()
except (
EOFError,
KeyboardInterrupt,
): # deal with cancellation of get_input_line dialog
desired_size = len(self.buffer) # Be satisfied!
if desired_size < 0:
return len(self.buffer)
else:
return desired_size
def __get_line(self):
"""Grab one line from get_input_line() and append it to the buffer."""
line = get_input_line()
print(">>>", line) # echo input to console
self.buffer = self.buffer + line + "\n"
def readlines(self, *sizehint):
"""Read until EOF using readline() and return a list containing the lines
thus read. If the optional sizehint argument is present, instead of
reading up to EOF, whole lines totalling approximately sizehint bytes
(possibly after rounding up to an internal buffer size) are read.
"""
result = []
total_read = 0
while sizehint == () or total_read < sizehint[0]:
line = self.readline()
if line == "":
break
total_read = total_read + len(line)
result.append(line)
return result
if __name__ == "__main__":
test_input = r"""this is some test
input that I am hoping
~
will be very instructive
and when I am done
I will have tested everything.
Twelve and twenty blackbirds
baked in a pie. Patty cake
patty cake so am I.
~
Thirty-five niggling idiots!
Sell you soul to the devil, baby
"""
def fake_raw_input(prompt=None):
"""Replacement for raw_input() which pulls lines out of global test_input.
For testing only!
"""
global test_input
if "\n" not in test_input:
end_of_line_pos = len(test_input)
else:
end_of_line_pos = test_input.find("\n")
result = test_input[:end_of_line_pos]
test_input = test_input[end_of_line_pos + 1 :]
if len(result) == 0 or result[0] == "~":
raise EOFError()
return result
get_input_line = fake_raw_input
# Some completely inadequate tests, just to make sure the code's not totally broken
try:
x = Stdin()
print(x.read())
print(x.readline())
print(x.read(12))
print(x.readline(47))
print(x.readline(3))
print(x.readlines())
finally:
get_input_line = raw_input
else:
import sys
sys.stdin = Stdin()

View File

@ -0,0 +1,280 @@
# toolmenu.py
import win32ui
import win32con
import win32api
from . import app
import sys
import string
tools = {}
idPos = 100
# The default items should no tools menu exist in the INI file.
defaultToolMenuItems = [
("Browser", "win32ui.GetApp().OnViewBrowse(0,0)"),
(
"Browse PythonPath",
"from pywin.tools import browseProjects;browseProjects.Browse()",
),
("Edit Python Path", "from pywin.tools import regedit;regedit.EditRegistry()"),
("COM Makepy utility", "from win32com.client import makepy;makepy.main()"),
("COM Browser", "from win32com.client import combrowse;combrowse.main()"),
(
"Trace Collector Debugging tool",
"from pywin.tools import TraceCollector;TraceCollector.MakeOutputWindow()",
),
]
def LoadToolMenuItems():
# Load from the registry.
items = []
lookNo = 1
while 1:
menu = win32ui.GetProfileVal("Tools Menu\\%s" % lookNo, "", "")
if menu == "":
break
cmd = win32ui.GetProfileVal("Tools Menu\\%s" % lookNo, "Command", "")
items.append((menu, cmd))
lookNo = lookNo + 1
if len(items) == 0:
items = defaultToolMenuItems
return items
def WriteToolMenuItems(items):
# Items is a list of (menu, command)
# Delete the entire registry tree.
try:
mainKey = win32ui.GetAppRegistryKey()
toolKey = win32api.RegOpenKey(mainKey, "Tools Menu")
except win32ui.error:
toolKey = None
if toolKey is not None:
while 1:
try:
subkey = win32api.RegEnumKey(toolKey, 0)
except win32api.error:
break
win32api.RegDeleteKey(toolKey, subkey)
# Keys are now removed - write the new ones.
# But first check if we have the defaults - and if so, dont write anything!
if items == defaultToolMenuItems:
return
itemNo = 1
for menu, cmd in items:
win32ui.WriteProfileVal("Tools Menu\\%s" % itemNo, "", menu)
win32ui.WriteProfileVal("Tools Menu\\%s" % itemNo, "Command", cmd)
itemNo = itemNo + 1
def SetToolsMenu(menu, menuPos=None):
global tools
global idPos
# todo - check the menu does not already exist.
# Create the new menu
toolsMenu = win32ui.CreatePopupMenu()
# Load from the ini file.
items = LoadToolMenuItems()
for menuString, cmd in items:
tools[idPos] = (menuString, cmd, menuString)
toolsMenu.AppendMenu(
win32con.MF_ENABLED | win32con.MF_STRING, idPos, menuString
)
win32ui.GetMainFrame().HookCommand(HandleToolCommand, idPos)
idPos = idPos + 1
# Find the correct spot to insert the new tools menu.
if menuPos is None:
menuPos = menu.GetMenuItemCount() - 2
if menuPos < 0:
menuPos = 0
menu.InsertMenu(
menuPos,
win32con.MF_BYPOSITION
| win32con.MF_ENABLED
| win32con.MF_STRING
| win32con.MF_POPUP,
toolsMenu.GetHandle(),
"&Tools",
)
def HandleToolCommand(cmd, code):
import traceback
import re
global tools
(menuString, pyCmd, desc) = tools[cmd]
win32ui.SetStatusText("Executing tool %s" % desc, 1)
pyCmd = re.sub("\\\\n", "\n", pyCmd)
win32ui.DoWaitCursor(1)
oldFlag = None
try:
oldFlag = sys.stdout.template.writeQueueing
sys.stdout.template.writeQueueing = 0
except (NameError, AttributeError):
pass
try:
exec("%s\n" % pyCmd)
worked = 1
except SystemExit:
# The program raised a SystemExit - ignore it.
worked = 1
except:
print("Failed to execute command:\n%s" % pyCmd)
traceback.print_exc()
worked = 0
if oldFlag is not None:
sys.stdout.template.writeQueueing = oldFlag
win32ui.DoWaitCursor(0)
if worked:
text = "Completed successfully."
else:
text = "Error executing %s." % desc
win32ui.SetStatusText(text, 1)
# The property page for maintaing the items on the Tools menu.
import commctrl
from pywin.mfc import dialog
if win32ui.UNICODE:
LVN_ENDLABELEDIT = commctrl.LVN_ENDLABELEDITW
else:
LVN_ENDLABELEDIT = commctrl.LVN_ENDLABELEDITA
class ToolMenuPropPage(dialog.PropertyPage):
def __init__(self):
self.bImChangingEditControls = 0 # Am I programatically changing the controls?
dialog.PropertyPage.__init__(self, win32ui.IDD_PP_TOOLMENU)
def OnInitDialog(self):
self.editMenuCommand = self.GetDlgItem(win32ui.IDC_EDIT2)
self.butNew = self.GetDlgItem(win32ui.IDC_BUTTON3)
# Now hook the change notification messages for the edit controls.
self.HookCommand(self.OnCommandEditControls, win32ui.IDC_EDIT1)
self.HookCommand(self.OnCommandEditControls, win32ui.IDC_EDIT2)
self.HookNotify(self.OnNotifyListControl, commctrl.LVN_ITEMCHANGED)
self.HookNotify(self.OnNotifyListControlEndLabelEdit, commctrl.LVN_ENDLABELEDIT)
# Hook the button clicks.
self.HookCommand(self.OnButtonNew, win32ui.IDC_BUTTON3) # New Item
self.HookCommand(self.OnButtonDelete, win32ui.IDC_BUTTON4) # Delete item
self.HookCommand(self.OnButtonMove, win32ui.IDC_BUTTON1) # Move up
self.HookCommand(self.OnButtonMove, win32ui.IDC_BUTTON2) # Move down
# Setup the columns in the list control
lc = self.GetDlgItem(win32ui.IDC_LIST1)
rect = lc.GetWindowRect()
cx = rect[2] - rect[0]
colSize = cx / 2 - win32api.GetSystemMetrics(win32con.SM_CXBORDER) - 1
item = commctrl.LVCFMT_LEFT, colSize, "Menu Text"
lc.InsertColumn(0, item)
item = commctrl.LVCFMT_LEFT, colSize, "Python Command"
lc.InsertColumn(1, item)
# Insert the existing tools menu
itemNo = 0
for desc, cmd in LoadToolMenuItems():
lc.InsertItem(itemNo, desc)
lc.SetItemText(itemNo, 1, cmd)
itemNo = itemNo + 1
self.listControl = lc
return dialog.PropertyPage.OnInitDialog(self)
def OnOK(self):
# Write the menu back to the registry.
items = []
itemLook = 0
while 1:
try:
text = self.listControl.GetItemText(itemLook, 0)
if not text:
break
items.append((text, self.listControl.GetItemText(itemLook, 1)))
except win32ui.error:
# no more items!
break
itemLook = itemLook + 1
WriteToolMenuItems(items)
return self._obj_.OnOK()
def OnCommandEditControls(self, id, cmd):
# print "OnEditControls", id, cmd
if cmd == win32con.EN_CHANGE and not self.bImChangingEditControls:
itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
newText = self.editMenuCommand.GetWindowText()
self.listControl.SetItemText(itemNo, 1, newText)
return 0
def OnNotifyListControlEndLabelEdit(self, id, cmd):
newText = self.listControl.GetEditControl().GetWindowText()
itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
self.listControl.SetItemText(itemNo, 0, newText)
def OnNotifyListControl(self, id, cmd):
# print id, cmd
try:
itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
except win32ui.error: # No selection!
return
self.bImChangingEditControls = 1
try:
item = self.listControl.GetItem(itemNo, 1)
self.editMenuCommand.SetWindowText(item[4])
finally:
self.bImChangingEditControls = 0
return 0 # we have handled this!
def OnButtonNew(self, id, cmd):
if cmd == win32con.BN_CLICKED:
newIndex = self.listControl.GetItemCount()
self.listControl.InsertItem(newIndex, "Click to edit the text")
self.listControl.EnsureVisible(newIndex, 0)
def OnButtonMove(self, id, cmd):
if cmd == win32con.BN_CLICKED:
try:
itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
except win32ui.error:
return
menu = self.listControl.GetItemText(itemNo, 0)
cmd = self.listControl.GetItemText(itemNo, 1)
if id == win32ui.IDC_BUTTON1:
# Move up
if itemNo > 0:
self.listControl.DeleteItem(itemNo)
# reinsert it.
self.listControl.InsertItem(itemNo - 1, menu)
self.listControl.SetItemText(itemNo - 1, 1, cmd)
else:
# Move down.
if itemNo < self.listControl.GetItemCount() - 1:
self.listControl.DeleteItem(itemNo)
# reinsert it.
self.listControl.InsertItem(itemNo + 1, menu)
self.listControl.SetItemText(itemNo + 1, 1, cmd)
def OnButtonDelete(self, id, cmd):
if cmd == win32con.BN_CLICKED:
try:
itemNo = self.listControl.GetNextItem(-1, commctrl.LVNI_SELECTED)
except win32ui.error: # No selection!
return
self.listControl.DeleteItem(itemNo)

View File

@ -0,0 +1,14 @@
# Framework Window classes.
# Most Pythonwin windows should use these classes rather than
# the raw MFC ones if they want Pythonwin specific functionality.
import pywin.mfc.window
import win32con
class MDIChildWnd(pywin.mfc.window.MDIChildWnd):
def AutoRestore(self):
"If the window is minimised or maximised, restore it."
p = self.GetWindowPlacement()
if p[1] == win32con.SW_MINIMIZE or p[1] == win32con.SW_SHOWMINIMIZED:
self.SetWindowPlacement(p[0], win32con.SW_RESTORE, p[2], p[3], p[4])

View File

@ -0,0 +1,588 @@
# winout.py
#
# generic "output window"
#
# This Window will detect itself closing, and recreate next time output is
# written to it.
# This has the option of writing output at idle time (by hooking the
# idle message, and queueing output) or writing as each
# write is executed.
# Updating the window directly gives a jerky appearance as many writes
# take place between commands, and the windows scrolls, and updates etc
# Updating at idle-time may defer all output of a long process, giving the
# appearence nothing is happening.
# There is a compromise "line" mode, which will output whenever
# a complete line is available.
# behaviour depends on self.writeQueueing
# This module is thread safe - output can originate from any thread. If any thread
# other than the main thread attempts to print, it is always queued until next idle time
import sys, string, re
from pywin.mfc import docview
from pywin.framework import app, window
import win32ui, win32api, win32con
import queue
debug = lambda msg: None
##debug=win32ui.OutputDebugString
##import win32trace;win32trace.InitWrite() # for debugging - delete me!
##debug = win32trace.write
class flags:
# queueing of output.
WQ_NONE = 0
WQ_LINE = 1
WQ_IDLE = 2
# WindowOutputDocumentParent=docview.RichEditDoc
# WindowOutputDocumentParent=docview.Document
import pywin.scintilla.document
from pywin.scintilla import scintillacon
from pywin import default_scintilla_encoding
WindowOutputDocumentParent = pywin.scintilla.document.CScintillaDocument
class WindowOutputDocument(WindowOutputDocumentParent):
def SaveModified(self):
return 1 # say it is OK to destroy my document
def OnSaveDocument(self, fileName):
win32ui.SetStatusText("Saving file...", 1)
try:
self.SaveFile(fileName)
except IOError as details:
win32ui.MessageBox("Error - could not save file\r\n\r\n%s" % details)
return 0
win32ui.SetStatusText("Ready")
return 1
class WindowOutputFrame(window.MDIChildWnd):
def __init__(self, wnd=None):
window.MDIChildWnd.__init__(self, wnd)
self.HookMessage(self.OnSizeMove, win32con.WM_SIZE)
self.HookMessage(self.OnSizeMove, win32con.WM_MOVE)
def LoadFrame(self, idResource, style, wndParent, context):
self.template = context.template
return self._obj_.LoadFrame(idResource, style, wndParent, context)
def PreCreateWindow(self, cc):
cc = self._obj_.PreCreateWindow(cc)
if (
self.template.defSize
and self.template.defSize[0] != self.template.defSize[1]
):
rect = app.RectToCreateStructRect(self.template.defSize)
cc = cc[0], cc[1], cc[2], cc[3], rect, cc[5], cc[6], cc[7], cc[8]
return cc
def OnSizeMove(self, msg):
# so recreate maintains position.
# Need to map coordinates from the
# frame windows first child.
mdiClient = self.GetParent()
self.template.defSize = mdiClient.ScreenToClient(self.GetWindowRect())
def OnDestroy(self, message):
self.template.OnFrameDestroy(self)
return 1
class WindowOutputViewImpl:
def __init__(self):
self.patErrorMessage = re.compile('\W*File "(.*)", line ([0-9]+)')
self.template = self.GetDocument().GetDocTemplate()
def HookHandlers(self):
# Hook for the right-click menu.
self.HookMessage(self.OnRClick, win32con.WM_RBUTTONDOWN)
def OnDestroy(self, msg):
self.template.OnViewDestroy(self)
def OnInitialUpdate(self):
self.RestoreKillBuffer()
self.SetSel(-2) # end of buffer
def GetRightMenuItems(self):
ret = []
flags = win32con.MF_STRING | win32con.MF_ENABLED
ret.append((flags, win32ui.ID_EDIT_COPY, "&Copy"))
ret.append((flags, win32ui.ID_EDIT_SELECT_ALL, "&Select all"))
return ret
#
# Windows command handlers, virtuals, etc.
#
def OnRClick(self, params):
paramsList = self.GetRightMenuItems()
menu = win32ui.CreatePopupMenu()
for appendParams in paramsList:
if type(appendParams) != type(()):
appendParams = (appendParams,)
menu.AppendMenu(*appendParams)
menu.TrackPopupMenu(params[5]) # track at mouse position.
return 0
# as this is often used as an output window, exeptions will often
# be printed. Therefore, we support this functionality at this level.
# Returns TRUE if the current line is an error message line, and will
# jump to it. FALSE if no error (and no action taken)
def HandleSpecialLine(self):
from . import scriptutils
line = self.GetLine()
if line[:11] == "com_error: ":
# An OLE Exception - pull apart the exception
# and try and locate a help file.
try:
import win32api, win32con
det = eval(line[line.find(":") + 1 :].strip())
win32ui.SetStatusText("Opening help file on OLE error...")
from . import help
help.OpenHelpFile(det[2][3], win32con.HELP_CONTEXT, det[2][4])
return 1
except win32api.error as details:
win32ui.SetStatusText(
"The help file could not be opened - %s" % details.strerror
)
return 1
except:
win32ui.SetStatusText(
"Line is a COM error, but no WinHelp details can be parsed"
)
# Look for a Python traceback.
matchResult = self.patErrorMessage.match(line)
if matchResult is None:
# No match - try the previous line
lineNo = self.LineFromChar()
if lineNo > 0:
line = self.GetLine(lineNo - 1)
matchResult = self.patErrorMessage.match(line)
if matchResult is not None:
# we have an error line.
fileName = matchResult.group(1)
if fileName[0] == "<":
win32ui.SetStatusText("Can not load this file")
return 1 # still was an error message.
else:
lineNoString = matchResult.group(2)
# Attempt to locate the file (in case it is a relative spec)
fileNameSpec = fileName
fileName = scriptutils.LocatePythonFile(fileName)
if fileName is None:
# Dont force update, so it replaces the idle prompt.
win32ui.SetStatusText(
"Cant locate the file '%s'" % (fileNameSpec), 0
)
return 1
win32ui.SetStatusText(
"Jumping to line " + lineNoString + " of file " + fileName, 1
)
if not scriptutils.JumpToDocument(fileName, int(lineNoString)):
win32ui.SetStatusText("Could not open %s" % fileName)
return 1 # still was an error message.
return 1
return 0 # not an error line
def write(self, msg):
return self.template.write(msg)
def writelines(self, lines):
for line in lines:
self.write(line)
def flush(self):
self.template.flush()
class WindowOutputViewRTF(docview.RichEditView, WindowOutputViewImpl):
def __init__(self, doc):
docview.RichEditView.__init__(self, doc)
WindowOutputViewImpl.__init__(self)
def OnInitialUpdate(self):
WindowOutputViewImpl.OnInitialUpdate(self)
return docview.RichEditView.OnInitialUpdate(self)
def OnDestroy(self, msg):
WindowOutputViewImpl.OnDestroy(self, msg)
docview.RichEditView.OnDestroy(self, msg)
def HookHandlers(self):
WindowOutputViewImpl.HookHandlers(self)
# Hook for finding and locating error messages
self.HookMessage(self.OnLDoubleClick, win32con.WM_LBUTTONDBLCLK)
# docview.RichEditView.HookHandlers(self)
def OnLDoubleClick(self, params):
if self.HandleSpecialLine():
return 0 # dont pass on
return 1 # pass it on by default.
def RestoreKillBuffer(self):
if len(self.template.killBuffer):
self.StreamIn(win32con.SF_RTF, self._StreamRTFIn)
self.template.killBuffer = []
def SaveKillBuffer(self):
self.StreamOut(win32con.SF_RTFNOOBJS, self._StreamRTFOut)
def _StreamRTFOut(self, data):
self.template.killBuffer.append(data)
return 1 # keep em coming!
def _StreamRTFIn(self, bytes):
try:
item = self.template.killBuffer[0]
self.template.killBuffer.remove(item)
if bytes < len(item):
print("Warning - output buffer not big enough!")
return item
except IndexError:
return None
def dowrite(self, str):
self.SetSel(-2)
self.ReplaceSel(str)
import pywin.scintilla.view
class WindowOutputViewScintilla(
pywin.scintilla.view.CScintillaView, WindowOutputViewImpl
):
def __init__(self, doc):
pywin.scintilla.view.CScintillaView.__init__(self, doc)
WindowOutputViewImpl.__init__(self)
def OnInitialUpdate(self):
pywin.scintilla.view.CScintillaView.OnInitialUpdate(self)
self.SCISetMarginWidth(3)
WindowOutputViewImpl.OnInitialUpdate(self)
def OnDestroy(self, msg):
WindowOutputViewImpl.OnDestroy(self, msg)
pywin.scintilla.view.CScintillaView.OnDestroy(self, msg)
def HookHandlers(self):
WindowOutputViewImpl.HookHandlers(self)
pywin.scintilla.view.CScintillaView.HookHandlers(self)
self.GetParent().HookNotify(
self.OnScintillaDoubleClick, scintillacon.SCN_DOUBLECLICK
)
## self.HookMessage(self.OnLDoubleClick,win32con.WM_LBUTTONDBLCLK)
def OnScintillaDoubleClick(self, std, extra):
self.HandleSpecialLine()
## def OnLDoubleClick(self,params):
## return 0 # never dont pass on
def RestoreKillBuffer(self):
assert len(self.template.killBuffer) in [0, 1], "Unexpected killbuffer contents"
if self.template.killBuffer:
self.SCIAddText(self.template.killBuffer[0])
self.template.killBuffer = []
def SaveKillBuffer(self):
self.template.killBuffer = [self.GetTextRange(0, -1)]
def dowrite(self, str):
end = self.GetTextLength()
atEnd = end == self.GetSel()[0]
self.SCIInsertText(str, end)
if atEnd:
self.SetSel(self.GetTextLength())
def SetWordWrap(self, bWrapOn=1):
if bWrapOn:
wrap_mode = scintillacon.SC_WRAP_WORD
else:
wrap_mode = scintillacon.SC_WRAP_NONE
self.SCISetWrapMode(wrap_mode)
def _MakeColorizer(self):
return None # No colorizer for me!
WindowOutputView = WindowOutputViewScintilla
# The WindowOutput class is actually an MFC template. This is a conventient way of
# making sure that my state can exist beyond the life of the windows themselves.
# This is primarily to support the functionality of a WindowOutput window automatically
# being recreated if necessary when written to.
class WindowOutput(docview.DocTemplate):
"""Looks like a general Output Window - text can be written by the 'write' method.
Will auto-create itself on first write, and also on next write after being closed"""
softspace = 1
def __init__(
self,
title=None,
defSize=None,
queueing=flags.WQ_LINE,
bAutoRestore=1,
style=None,
makeDoc=None,
makeFrame=None,
makeView=None,
):
"""init the output window -
Params
title=None -- What is the title of the window
defSize=None -- What is the default size for the window - if this
is a string, the size will be loaded from the ini file.
queueing = flags.WQ_LINE -- When should output be written
bAutoRestore=1 -- Should a minimized window be restored.
style -- Style for Window, or None for default.
makeDoc, makeFrame, makeView -- Classes for frame, view and window respectively.
"""
if makeDoc is None:
makeDoc = WindowOutputDocument
if makeFrame is None:
makeFrame = WindowOutputFrame
if makeView is None:
makeView = WindowOutputViewScintilla
docview.DocTemplate.__init__(
self, win32ui.IDR_PYTHONTYPE, makeDoc, makeFrame, makeView
)
self.SetDocStrings("\nOutput\n\nText Documents (*.txt)\n.txt\n\n\n")
win32ui.GetApp().AddDocTemplate(self)
self.writeQueueing = queueing
self.errorCantRecreate = 0
self.killBuffer = []
self.style = style
self.bAutoRestore = bAutoRestore
self.title = title
self.bCreating = 0
self.interruptCount = 0
if type(defSize) == type(""): # is a string - maintain size pos from ini file.
self.iniSizeSection = defSize
self.defSize = app.LoadWindowSize(defSize)
self.loadedSize = self.defSize
else:
self.iniSizeSection = None
self.defSize = defSize
self.currentView = None
self.outputQueue = queue.Queue(-1)
self.mainThreadId = win32api.GetCurrentThreadId()
self.idleHandlerSet = 0
self.SetIdleHandler()
def __del__(self):
self.Close()
def Create(self, title=None, style=None):
self.bCreating = 1
if title:
self.title = title
if style:
self.style = style
doc = self.OpenDocumentFile()
if doc is None:
return
self.currentView = doc.GetFirstView()
self.bCreating = 0
if self.title:
doc.SetTitle(self.title)
def Close(self):
self.RemoveIdleHandler()
try:
parent = self.currentView.GetParent()
except (AttributeError, win32ui.error): # Already closed
return
parent.DestroyWindow()
def SetTitle(self, title):
self.title = title
if self.currentView:
self.currentView.GetDocument().SetTitle(self.title)
def OnViewDestroy(self, view):
self.currentView.SaveKillBuffer()
self.currentView = None
def OnFrameDestroy(self, frame):
if self.iniSizeSection:
# use GetWindowPlacement(), as it works even when min'd or max'd
newSize = frame.GetWindowPlacement()[4]
if self.loadedSize != newSize:
app.SaveWindowSize(self.iniSizeSection, newSize)
def SetIdleHandler(self):
if not self.idleHandlerSet:
debug("Idle handler set\n")
win32ui.GetApp().AddIdleHandler(self.QueueIdleHandler)
self.idleHandlerSet = 1
def RemoveIdleHandler(self):
if self.idleHandlerSet:
debug("Idle handler reset\n")
if win32ui.GetApp().DeleteIdleHandler(self.QueueIdleHandler) == 0:
debug("Error deleting idle handler\n")
self.idleHandlerSet = 0
def RecreateWindow(self):
if self.errorCantRecreate:
debug("Error = not trying again")
return 0
try:
# This will fail if app shutting down
win32ui.GetMainFrame().GetSafeHwnd()
self.Create()
return 1
except (win32ui.error, AttributeError):
self.errorCantRecreate = 1
debug("Winout can not recreate the Window!\n")
return 0
# this handles the idle message, and does the printing.
def QueueIdleHandler(self, handler, count):
try:
bEmpty = self.QueueFlush(20)
# If the queue is empty, then we are back to idle and restart interrupt logic.
if bEmpty:
self.interruptCount = 0
except KeyboardInterrupt:
# First interrupt since idle we just pass on.
# later ones we dump the queue and give up.
self.interruptCount = self.interruptCount + 1
if self.interruptCount > 1:
# Drop the queue quickly as the user is already annoyed :-)
self.outputQueue = queue.Queue(-1)
print("Interrupted.")
bEmpty = 1
else:
raise # re-raise the error so the users exception filters up.
return not bEmpty # More to do if not empty.
# Returns true if the Window needs to be recreated.
def NeedRecreateWindow(self):
try:
if self.currentView is not None and self.currentView.IsWindow():
return 0
except (
win32ui.error,
AttributeError,
): # Attribute error if the win32ui object has died.
pass
return 1
# Returns true if the Window is OK (either cos it was, or because it was recreated
def CheckRecreateWindow(self):
if self.bCreating:
return 1
if not self.NeedRecreateWindow():
return 1
if self.bAutoRestore:
if self.RecreateWindow():
return 1
return 0
def QueueFlush(self, max=None):
# Returns true if the queue is empty after the flush
# debug("Queueflush - %d, %d\n" % (max, self.outputQueue.qsize()))
if self.bCreating:
return 1
items = []
rc = 0
while max is None or max > 0:
try:
item = self.outputQueue.get_nowait()
items.append(item)
except queue.Empty:
rc = 1
break
if max is not None:
max = max - 1
if len(items) != 0:
if not self.CheckRecreateWindow():
debug(":Recreate failed!\n")
return 1 # In trouble - so say we have nothing to do.
win32ui.PumpWaitingMessages() # Pump paint messages
self.currentView.dowrite("".join(items))
return rc
def HandleOutput(self, message):
# debug("QueueOutput on thread %d, flags %d with '%s'...\n" % (win32api.GetCurrentThreadId(), self.writeQueueing, message ))
self.outputQueue.put(message)
if win32api.GetCurrentThreadId() != self.mainThreadId:
pass
# debug("not my thread - ignoring queue options!\n")
elif self.writeQueueing == flags.WQ_LINE:
pos = message.rfind("\n")
if pos >= 0:
# debug("Line queueing - forcing flush\n")
self.QueueFlush()
return
elif self.writeQueueing == flags.WQ_NONE:
# debug("WQ_NONE - flushing!\n")
self.QueueFlush()
return
# Let our idle handler get it - wake it up
try:
win32ui.GetMainFrame().PostMessage(
win32con.WM_USER
) # Kick main thread off.
except win32ui.error:
# This can happen as the app is shutting down, so we send it to the C++ debugger
win32api.OutputDebugString(message)
# delegate certain fns to my view.
def writelines(self, lines):
for line in lines:
self.write(line)
def write(self, message):
self.HandleOutput(message)
def flush(self):
self.QueueFlush()
def HandleSpecialLine(self):
self.currentView.HandleSpecialLine()
def RTFWindowOutput(*args, **kw):
kw["makeView"] = WindowOutputViewRTF
return WindowOutput(*args, **kw)
def thread_test(o):
for i in range(5):
o.write("Hi from thread %d\n" % (win32api.GetCurrentThreadId()))
win32api.Sleep(100)
def test():
w = WindowOutput(queueing=flags.WQ_IDLE)
w.write("First bit of text\n")
import _thread
for i in range(5):
w.write("Hello from the main thread\n")
_thread.start_new(thread_test, (w,))
for i in range(2):
w.write("Hello from the main thread\n")
win32api.Sleep(50)
return w
if __name__ == "__main__":
test()

View File

@ -0,0 +1,96 @@
import string
import re
###$ event <<expand-word>>
###$ win <Alt-slash>
###$ unix <Alt-slash>
class AutoExpand:
keydefs = {
"<<expand-word>>": ["<Alt-slash>"],
}
unix_keydefs = {
"<<expand-word>>": ["<Meta-slash>"],
}
menudefs = [
(
"edit",
[
("E_xpand word", "<<expand-word>>"),
],
),
]
wordchars = string.ascii_letters + string.digits + "_"
def __init__(self, editwin):
self.text = editwin.text
self.text.wordlist = None # XXX what is this?
self.state = None
def expand_word_event(self, event):
curinsert = self.text.index("insert")
curline = self.text.get("insert linestart", "insert lineend")
if not self.state:
words = self.getwords()
index = 0
else:
words, index, insert, line = self.state
if insert != curinsert or line != curline:
words = self.getwords()
index = 0
if not words:
self.text.bell()
return "break"
word = self.getprevword()
self.text.delete("insert - %d chars" % len(word), "insert")
newword = words[index]
index = (index + 1) % len(words)
if index == 0:
self.text.bell() # Warn we cycled around
self.text.insert("insert", newword)
curinsert = self.text.index("insert")
curline = self.text.get("insert linestart", "insert lineend")
self.state = words, index, curinsert, curline
return "break"
def getwords(self):
word = self.getprevword()
if not word:
return []
before = self.text.get("1.0", "insert wordstart")
wbefore = re.findall(r"\b" + word + r"\w+\b", before)
del before
after = self.text.get("insert wordend", "end")
wafter = re.findall(r"\b" + word + r"\w+\b", after)
del after
if not wbefore and not wafter:
return []
words = []
dict = {}
# search backwards through words before
wbefore.reverse()
for w in wbefore:
if dict.get(w):
continue
words.append(w)
dict[w] = w
# search onwards through words after
for w in wafter:
if dict.get(w):
continue
words.append(w)
dict[w] = w
words.append(word)
return words
def getprevword(self):
line = self.text.get("insert linestart", "insert")
i = len(line)
while i > 0 and line[i - 1] in self.wordchars:
i = i - 1
return line[i:]

View File

@ -0,0 +1,547 @@
import sys
import string, tokenize
from . import PyParse
from pywin import default_scintilla_encoding
if sys.version_info < (3,):
# in py2k, tokenize() takes a 'token eater' callback, while
# generate_tokens is a generator that works with str objects.
token_generator = tokenize.generate_tokens
else:
# in py3k tokenize() is the generator working with 'byte' objects, and
# token_generator is the 'undocumented b/w compat' function that
# theoretically works with str objects - but actually seems to fail)
token_generator = tokenize.tokenize
class AutoIndent:
menudefs = [
(
"edit",
[
None,
("_Indent region", "<<indent-region>>"),
("_Dedent region", "<<dedent-region>>"),
("Comment _out region", "<<comment-region>>"),
("U_ncomment region", "<<uncomment-region>>"),
("Tabify region", "<<tabify-region>>"),
("Untabify region", "<<untabify-region>>"),
("Toggle tabs", "<<toggle-tabs>>"),
("New indent width", "<<change-indentwidth>>"),
],
),
]
keydefs = {
"<<smart-backspace>>": ["<Key-BackSpace>"],
"<<newline-and-indent>>": ["<Key-Return>", "<KP_Enter>"],
"<<smart-indent>>": ["<Key-Tab>"],
}
windows_keydefs = {
"<<indent-region>>": ["<Control-bracketright>"],
"<<dedent-region>>": ["<Control-bracketleft>"],
"<<comment-region>>": ["<Alt-Key-3>"],
"<<uncomment-region>>": ["<Alt-Key-4>"],
"<<tabify-region>>": ["<Alt-Key-5>"],
"<<untabify-region>>": ["<Alt-Key-6>"],
"<<toggle-tabs>>": ["<Alt-Key-t>"],
"<<change-indentwidth>>": ["<Alt-Key-u>"],
}
unix_keydefs = {
"<<indent-region>>": [
"<Alt-bracketright>",
"<Meta-bracketright>",
"<Control-bracketright>",
],
"<<dedent-region>>": [
"<Alt-bracketleft>",
"<Meta-bracketleft>",
"<Control-bracketleft>",
],
"<<comment-region>>": ["<Alt-Key-3>", "<Meta-Key-3>"],
"<<uncomment-region>>": ["<Alt-Key-4>", "<Meta-Key-4>"],
"<<tabify-region>>": ["<Alt-Key-5>", "<Meta-Key-5>"],
"<<untabify-region>>": ["<Alt-Key-6>", "<Meta-Key-6>"],
"<<toggle-tabs>>": ["<Alt-Key-t>"],
"<<change-indentwidth>>": ["<Alt-Key-u>"],
}
# usetabs true -> literal tab characters are used by indent and
# dedent cmds, possibly mixed with spaces if
# indentwidth is not a multiple of tabwidth
# false -> tab characters are converted to spaces by indent
# and dedent cmds, and ditto TAB keystrokes
# indentwidth is the number of characters per logical indent level.
# tabwidth is the display width of a literal tab character.
# CAUTION: telling Tk to use anything other than its default
# tab setting causes it to use an entirely different tabbing algorithm,
# treating tab stops as fixed distances from the left margin.
# Nobody expects this, so for now tabwidth should never be changed.
usetabs = 1
indentwidth = 4
tabwidth = 8 # for IDLE use, must remain 8 until Tk is fixed
# If context_use_ps1 is true, parsing searches back for a ps1 line;
# else searches for a popular (if, def, ...) Python stmt.
context_use_ps1 = 0
# When searching backwards for a reliable place to begin parsing,
# first start num_context_lines[0] lines back, then
# num_context_lines[1] lines back if that didn't work, and so on.
# The last value should be huge (larger than the # of lines in a
# conceivable file).
# Making the initial values larger slows things down more often.
num_context_lines = 50, 500, 5000000
def __init__(self, editwin):
self.editwin = editwin
self.text = editwin.text
def config(self, **options):
for key, value in options.items():
if key == "usetabs":
self.usetabs = value
elif key == "indentwidth":
self.indentwidth = value
elif key == "tabwidth":
self.tabwidth = value
elif key == "context_use_ps1":
self.context_use_ps1 = value
else:
raise KeyError("bad option name: %s" % repr(key))
# If ispythonsource and guess are true, guess a good value for
# indentwidth based on file content (if possible), and if
# indentwidth != tabwidth set usetabs false.
# In any case, adjust the Text widget's view of what a tab
# character means.
def set_indentation_params(self, ispythonsource, guess=1):
if guess and ispythonsource:
i = self.guess_indent()
if 2 <= i <= 8:
self.indentwidth = i
if self.indentwidth != self.tabwidth:
self.usetabs = 0
self.editwin.set_tabwidth(self.tabwidth)
def smart_backspace_event(self, event):
text = self.text
first, last = self.editwin.get_selection_indices()
if first and last:
text.delete(first, last)
text.mark_set("insert", first)
return "break"
# Delete whitespace left, until hitting a real char or closest
# preceding virtual tab stop.
chars = text.get("insert linestart", "insert")
if chars == "":
if text.compare("insert", ">", "1.0"):
# easy: delete preceding newline
text.delete("insert-1c")
else:
text.bell() # at start of buffer
return "break"
if chars[-1] not in " \t":
# easy: delete preceding real char
text.delete("insert-1c")
return "break"
# Ick. It may require *inserting* spaces if we back up over a
# tab character! This is written to be clear, not fast.
have = len(chars.expandtabs(self.tabwidth))
assert have > 0
want = int((have - 1) / self.indentwidth) * self.indentwidth
ncharsdeleted = 0
while 1:
chars = chars[:-1]
ncharsdeleted = ncharsdeleted + 1
have = len(chars.expandtabs(self.tabwidth))
if have <= want or chars[-1] not in " \t":
break
text.undo_block_start()
text.delete("insert-%dc" % ncharsdeleted, "insert")
if have < want:
text.insert("insert", " " * (want - have))
text.undo_block_stop()
return "break"
def smart_indent_event(self, event):
# if intraline selection:
# delete it
# elif multiline selection:
# do indent-region & return
# indent one level
text = self.text
first, last = self.editwin.get_selection_indices()
text.undo_block_start()
try:
if first and last:
if index2line(first) != index2line(last):
return self.indent_region_event(event)
text.delete(first, last)
text.mark_set("insert", first)
prefix = text.get("insert linestart", "insert")
raw, effective = classifyws(prefix, self.tabwidth)
if raw == len(prefix):
# only whitespace to the left
self.reindent_to(effective + self.indentwidth)
else:
if self.usetabs:
pad = "\t"
else:
effective = len(prefix.expandtabs(self.tabwidth))
n = self.indentwidth
pad = " " * (n - effective % n)
text.insert("insert", pad)
text.see("insert")
return "break"
finally:
text.undo_block_stop()
def newline_and_indent_event(self, event):
text = self.text
first, last = self.editwin.get_selection_indices()
text.undo_block_start()
try:
if first and last:
text.delete(first, last)
text.mark_set("insert", first)
line = text.get("insert linestart", "insert")
i, n = 0, len(line)
while i < n and line[i] in " \t":
i = i + 1
if i == n:
# the cursor is in or at leading indentation; just inject
# an empty line at the start and strip space from current line
text.delete("insert - %d chars" % i, "insert")
text.insert("insert linestart", "\n")
return "break"
indent = line[:i]
# strip whitespace before insert point
i = 0
while line and line[-1] in " \t":
line = line[:-1]
i = i + 1
if i:
text.delete("insert - %d chars" % i, "insert")
# strip whitespace after insert point
while text.get("insert") in " \t":
text.delete("insert")
# start new line
text.insert("insert", "\n")
# adjust indentation for continuations and block
# open/close first need to find the last stmt
lno = index2line(text.index("insert"))
y = PyParse.Parser(self.indentwidth, self.tabwidth)
for context in self.num_context_lines:
startat = max(lno - context, 1)
startatindex = repr(startat) + ".0"
rawtext = text.get(startatindex, "insert")
y.set_str(rawtext)
bod = y.find_good_parse_start(
self.context_use_ps1, self._build_char_in_string_func(startatindex)
)
if bod is not None or startat == 1:
break
y.set_lo(bod or 0)
c = y.get_continuation_type()
if c != PyParse.C_NONE:
# The current stmt hasn't ended yet.
if c == PyParse.C_STRING:
# inside a string; just mimic the current indent
text.insert("insert", indent)
elif c == PyParse.C_BRACKET:
# line up with the first (if any) element of the
# last open bracket structure; else indent one
# level beyond the indent of the line with the
# last open bracket
self.reindent_to(y.compute_bracket_indent())
elif c == PyParse.C_BACKSLASH:
# if more than one line in this stmt already, just
# mimic the current indent; else if initial line
# has a start on an assignment stmt, indent to
# beyond leftmost =; else to beyond first chunk of
# non-whitespace on initial line
if y.get_num_lines_in_stmt() > 1:
text.insert("insert", indent)
else:
self.reindent_to(y.compute_backslash_indent())
else:
assert 0, "bogus continuation type " + repr(c)
return "break"
# This line starts a brand new stmt; indent relative to
# indentation of initial line of closest preceding
# interesting stmt.
indent = y.get_base_indent_string()
text.insert("insert", indent)
if y.is_block_opener():
self.smart_indent_event(event)
elif indent and y.is_block_closer():
self.smart_backspace_event(event)
return "break"
finally:
text.see("insert")
text.undo_block_stop()
auto_indent = newline_and_indent_event
# Our editwin provides a is_char_in_string function that works
# with a Tk text index, but PyParse only knows about offsets into
# a string. This builds a function for PyParse that accepts an
# offset.
def _build_char_in_string_func(self, startindex):
def inner(offset, _startindex=startindex, _icis=self.editwin.is_char_in_string):
return _icis(_startindex + "+%dc" % offset)
return inner
def indent_region_event(self, event):
head, tail, chars, lines = self.get_region()
for pos in range(len(lines)):
line = lines[pos]
if line:
raw, effective = classifyws(line, self.tabwidth)
effective = effective + self.indentwidth
lines[pos] = self._make_blanks(effective) + line[raw:]
self.set_region(head, tail, chars, lines)
return "break"
def dedent_region_event(self, event):
head, tail, chars, lines = self.get_region()
for pos in range(len(lines)):
line = lines[pos]
if line:
raw, effective = classifyws(line, self.tabwidth)
effective = max(effective - self.indentwidth, 0)
lines[pos] = self._make_blanks(effective) + line[raw:]
self.set_region(head, tail, chars, lines)
return "break"
def comment_region_event(self, event):
head, tail, chars, lines = self.get_region()
for pos in range(len(lines) - 1):
line = lines[pos]
lines[pos] = "##" + line
self.set_region(head, tail, chars, lines)
def uncomment_region_event(self, event):
head, tail, chars, lines = self.get_region()
for pos in range(len(lines)):
line = lines[pos]
if not line:
continue
if line[:2] == "##":
line = line[2:]
elif line[:1] == "#":
line = line[1:]
lines[pos] = line
self.set_region(head, tail, chars, lines)
def tabify_region_event(self, event):
head, tail, chars, lines = self.get_region()
tabwidth = self._asktabwidth()
for pos in range(len(lines)):
line = lines[pos]
if line:
raw, effective = classifyws(line, tabwidth)
ntabs, nspaces = divmod(effective, tabwidth)
lines[pos] = "\t" * ntabs + " " * nspaces + line[raw:]
self.set_region(head, tail, chars, lines)
def untabify_region_event(self, event):
head, tail, chars, lines = self.get_region()
tabwidth = self._asktabwidth()
for pos in range(len(lines)):
lines[pos] = lines[pos].expandtabs(tabwidth)
self.set_region(head, tail, chars, lines)
def toggle_tabs_event(self, event):
if self.editwin.askyesno(
"Toggle tabs",
"Turn tabs " + ("on", "off")[self.usetabs] + "?",
parent=self.text,
):
self.usetabs = not self.usetabs
return "break"
# XXX this isn't bound to anything -- see class tabwidth comments
def change_tabwidth_event(self, event):
new = self._asktabwidth()
if new != self.tabwidth:
self.tabwidth = new
self.set_indentation_params(0, guess=0)
return "break"
def change_indentwidth_event(self, event):
new = self.editwin.askinteger(
"Indent width",
"New indent width (1-16)",
parent=self.text,
initialvalue=self.indentwidth,
minvalue=1,
maxvalue=16,
)
if new and new != self.indentwidth:
self.indentwidth = new
return "break"
def get_region(self):
text = self.text
first, last = self.editwin.get_selection_indices()
if first and last:
head = text.index(first + " linestart")
tail = text.index(last + "-1c lineend +1c")
else:
head = text.index("insert linestart")
tail = text.index("insert lineend +1c")
chars = text.get(head, tail)
lines = chars.split("\n")
return head, tail, chars, lines
def set_region(self, head, tail, chars, lines):
text = self.text
newchars = "\n".join(lines)
if newchars == chars:
text.bell()
return
text.tag_remove("sel", "1.0", "end")
text.mark_set("insert", head)
text.undo_block_start()
text.delete(head, tail)
text.insert(head, newchars)
text.undo_block_stop()
text.tag_add("sel", head, "insert")
# Make string that displays as n leading blanks.
def _make_blanks(self, n):
if self.usetabs:
ntabs, nspaces = divmod(n, self.tabwidth)
return "\t" * ntabs + " " * nspaces
else:
return " " * n
# Delete from beginning of line to insert point, then reinsert
# column logical (meaning use tabs if appropriate) spaces.
def reindent_to(self, column):
text = self.text
text.undo_block_start()
if text.compare("insert linestart", "!=", "insert"):
text.delete("insert linestart", "insert")
if column:
text.insert("insert", self._make_blanks(column))
text.undo_block_stop()
def _asktabwidth(self):
return (
self.editwin.askinteger(
"Tab width",
"Spaces per tab?",
parent=self.text,
initialvalue=self.tabwidth,
minvalue=1,
maxvalue=16,
)
or self.tabwidth
)
# Guess indentwidth from text content.
# Return guessed indentwidth. This should not be believed unless
# it's in a reasonable range (e.g., it will be 0 if no indented
# blocks are found).
def guess_indent(self):
opener, indented = IndentSearcher(self.text, self.tabwidth).run()
if opener and indented:
raw, indentsmall = classifyws(opener, self.tabwidth)
raw, indentlarge = classifyws(indented, self.tabwidth)
else:
indentsmall = indentlarge = 0
return indentlarge - indentsmall
# "line.col" -> line, as an int
def index2line(index):
return int(float(index))
# Look at the leading whitespace in s.
# Return pair (# of leading ws characters,
# effective # of leading blanks after expanding
# tabs to width tabwidth)
def classifyws(s, tabwidth):
raw = effective = 0
for ch in s:
if ch == " ":
raw = raw + 1
effective = effective + 1
elif ch == "\t":
raw = raw + 1
effective = (effective // tabwidth + 1) * tabwidth
else:
break
return raw, effective
class IndentSearcher:
# .run() chews over the Text widget, looking for a block opener
# and the stmt following it. Returns a pair,
# (line containing block opener, line containing stmt)
# Either or both may be None.
def __init__(self, text, tabwidth):
self.text = text
self.tabwidth = tabwidth
self.i = self.finished = 0
self.blkopenline = self.indentedline = None
def readline(self):
if self.finished:
val = ""
else:
i = self.i = self.i + 1
mark = repr(i) + ".0"
if self.text.compare(mark, ">=", "end"):
val = ""
else:
val = self.text.get(mark, mark + " lineend+1c")
# hrm - not sure this is correct in py3k - the source code may have
# an encoding declared, but the data will *always* be in
# default_scintilla_encoding - so if anyone looks at the encoding decl
# in the source they will be wrong. I think. Maybe. Or something...
return val.encode(default_scintilla_encoding)
def run(self):
OPENERS = ("class", "def", "for", "if", "try", "while")
INDENT = tokenize.INDENT
NAME = tokenize.NAME
save_tabsize = tokenize.tabsize
tokenize.tabsize = self.tabwidth
try:
try:
for (typ, token, start, end, line) in token_generator(self.readline):
if typ == NAME and token in OPENERS:
self.blkopenline = line
elif typ == INDENT and self.blkopenline:
self.indentedline = line
break
except (tokenize.TokenError, IndentationError):
# since we cut off the tokenizer early, we can trigger
# spurious errors
pass
finally:
tokenize.tabsize = save_tabsize
return self.blkopenline, self.indentedline

View File

@ -0,0 +1,223 @@
# CallTips.py - An IDLE extension that provides "Call Tips" - ie, a floating window that
# displays parameter information as you open parens.
import string
import sys
import inspect
import traceback
class CallTips:
menudefs = []
keydefs = {
"<<paren-open>>": ["<Key-parenleft>"],
"<<paren-close>>": ["<Key-parenright>"],
"<<check-calltip-cancel>>": ["<KeyRelease>"],
"<<calltip-cancel>>": ["<ButtonPress>", "<Key-Escape>"],
}
windows_keydefs = {}
unix_keydefs = {}
def __init__(self, editwin):
self.editwin = editwin
self.text = editwin.text
self.calltip = None
if hasattr(self.text, "make_calltip_window"):
self._make_calltip_window = self.text.make_calltip_window
else:
self._make_calltip_window = self._make_tk_calltip_window
def close(self):
self._make_calltip_window = None
# Makes a Tk based calltip window. Used by IDLE, but not Pythonwin.
# See __init__ above for how this is used.
def _make_tk_calltip_window(self):
import CallTipWindow
return CallTipWindow.CallTip(self.text)
def _remove_calltip_window(self):
if self.calltip:
self.calltip.hidetip()
self.calltip = None
def paren_open_event(self, event):
self._remove_calltip_window()
arg_text = get_arg_text(self.get_object_at_cursor())
if arg_text:
self.calltip_start = self.text.index("insert")
self.calltip = self._make_calltip_window()
self.calltip.showtip(arg_text)
return "" # so the event is handled normally.
def paren_close_event(self, event):
# Now just hides, but later we should check if other
# paren'd expressions remain open.
self._remove_calltip_window()
return "" # so the event is handled normally.
def check_calltip_cancel_event(self, event):
if self.calltip:
# If we have moved before the start of the calltip,
# or off the calltip line, then cancel the tip.
# (Later need to be smarter about multi-line, etc)
if self.text.compare(
"insert", "<=", self.calltip_start
) or self.text.compare("insert", ">", self.calltip_start + " lineend"):
self._remove_calltip_window()
return "" # so the event is handled normally.
def calltip_cancel_event(self, event):
self._remove_calltip_window()
return "" # so the event is handled normally.
def get_object_at_cursor(
self,
wordchars="._"
+ string.ascii_uppercase
+ string.ascii_lowercase
+ string.digits,
):
# XXX - This needs to be moved to a better place
# so the "." attribute lookup code can also use it.
text = self.text
chars = text.get("insert linestart", "insert")
i = len(chars)
while i and chars[i - 1] in wordchars:
i = i - 1
word = chars[i:]
if word:
# How is this for a hack!
import sys, __main__
namespace = sys.modules.copy()
namespace.update(__main__.__dict__)
try:
return eval(word, namespace)
except:
pass
return None # Can't find an object.
def _find_constructor(class_ob):
# Given a class object, return a function object used for the
# constructor (ie, __init__() ) or None if we can't find one.
try:
return class_ob.__init__
except AttributeError:
for base in class_ob.__bases__:
rc = _find_constructor(base)
if rc is not None:
return rc
return None
def get_arg_text(ob):
# Get a string describing the arguments for the given object.
argText = ""
if ob is not None:
argOffset = 0
if inspect.isclass(ob):
# Look for the highest __init__ in the class chain.
fob = _find_constructor(ob)
if fob is None:
fob = lambda: None
else:
fob = ob
if inspect.isfunction(fob) or inspect.ismethod(fob):
try:
# py3k has a 'getfullargspec' which can handle py3k specific things.
arg_getter = getattr(inspect, "getfullargspec", inspect.getargspec)
argText = inspect.formatargspec(*arg_getter(fob))
except:
print("Failed to format the args")
traceback.print_exc()
# See if we can use the docstring
if hasattr(ob, "__doc__"):
doc = ob.__doc__
try:
doc = doc.strip()
pos = doc.find("\n")
except AttributeError:
## New style classes may have __doc__ slot without actually
## having a string assigned to it
pass
else:
if pos < 0 or pos > 70:
pos = 70
if argText:
argText = argText + "\n"
argText = argText + doc[:pos]
return argText
#################################################
#
# Test code
#
if __name__ == "__main__":
def t1():
"()"
def t2(a, b=None):
"(a, b=None)"
def t3(a, *args):
"(a, *args)"
def t4(*args):
"(*args)"
def t5(a, *args):
"(a, *args)"
def t6(a, b=None, *args, **kw):
"(a, b=None, *args, **kw)"
class TC:
"(self, a=None, *b)"
def __init__(self, a=None, *b):
"(self, a=None, *b)"
def t1(self):
"(self)"
def t2(self, a, b=None):
"(self, a, b=None)"
def t3(self, a, *args):
"(self, a, *args)"
def t4(self, *args):
"(self, *args)"
def t5(self, a, *args):
"(self, a, *args)"
def t6(self, a, b=None, *args, **kw):
"(self, a, b=None, *args, **kw)"
def test(tests):
failed = []
for t in tests:
expected = t.__doc__ + "\n" + t.__doc__
if get_arg_text(t) != expected:
failed.append(t)
print(
"%s - expected %s, but got %s"
% (t, repr(expected), repr(get_arg_text(t)))
)
print("%d of %d tests failed" % (len(failed), len(tests)))
tc = TC()
tests = t1, t2, t3, t4, t5, t6, TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6
test(tests)

View File

@ -0,0 +1,168 @@
# Extension to format a paragraph
# Does basic, standard text formatting, and also understands Python
# comment blocks. Thus, for editing Python source code, this
# extension is really only suitable for reformatting these comment
# blocks or triple-quoted strings.
# Known problems with comment reformatting:
# * If there is a selection marked, and the first line of the
# selection is not complete, the block will probably not be detected
# as comments, and will have the normal "text formatting" rules
# applied.
# * If a comment block has leading whitespace that mixes tabs and
# spaces, they will not be considered part of the same block.
# * Fancy comments, like this bulleted list, arent handled :-)
import string
import re
class FormatParagraph:
menudefs = [
(
"edit",
[
("Format Paragraph", "<<format-paragraph>>"),
],
)
]
keydefs = {
"<<format-paragraph>>": ["<Alt-q>"],
}
unix_keydefs = {
"<<format-paragraph>>": ["<Meta-q>"],
}
def __init__(self, editwin):
self.editwin = editwin
def close(self):
self.editwin = None
def format_paragraph_event(self, event):
text = self.editwin.text
first, last = self.editwin.get_selection_indices()
if first and last:
data = text.get(first, last)
comment_header = ""
else:
first, last, comment_header, data = find_paragraph(
text, text.index("insert")
)
if comment_header:
# Reformat the comment lines - convert to text sans header.
lines = data.split("\n")
lines = map(lambda st, l=len(comment_header): st[l:], lines)
data = "\n".join(lines)
# Reformat to 70 chars or a 20 char width, whichever is greater.
format_width = max(70 - len(comment_header), 20)
newdata = reformat_paragraph(data, format_width)
# re-split and re-insert the comment header.
newdata = newdata.split("\n")
# If the block ends in a \n, we dont want the comment
# prefix inserted after it. (Im not sure it makes sense to
# reformat a comment block that isnt made of complete
# lines, but whatever!) Can't think of a clean soltution,
# so we hack away
block_suffix = ""
if not newdata[-1]:
block_suffix = "\n"
newdata = newdata[:-1]
builder = lambda item, prefix=comment_header: prefix + item
newdata = "\n".join([builder(d) for d in newdata]) + block_suffix
else:
# Just a normal text format
newdata = reformat_paragraph(data)
text.tag_remove("sel", "1.0", "end")
if newdata != data:
text.mark_set("insert", first)
text.undo_block_start()
text.delete(first, last)
text.insert(first, newdata)
text.undo_block_stop()
else:
text.mark_set("insert", last)
text.see("insert")
def find_paragraph(text, mark):
lineno, col = list(map(int, mark.split(".")))
line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line):
lineno = lineno + 1
line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
first_lineno = lineno
comment_header = get_comment_header(line)
comment_header_len = len(comment_header)
while get_comment_header(line) == comment_header and not is_all_white(
line[comment_header_len:]
):
lineno = lineno + 1
line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
last = "%d.0" % lineno
# Search back to beginning of paragraph
lineno = first_lineno - 1
line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
while (
lineno > 0
and get_comment_header(line) == comment_header
and not is_all_white(line[comment_header_len:])
):
lineno = lineno - 1
line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno)
first = "%d.0" % (lineno + 1)
return first, last, comment_header, text.get(first, last)
def reformat_paragraph(data, limit=70):
lines = data.split("\n")
i = 0
n = len(lines)
while i < n and is_all_white(lines[i]):
i = i + 1
if i >= n:
return data
indent1 = get_indent(lines[i])
if i + 1 < n and not is_all_white(lines[i + 1]):
indent2 = get_indent(lines[i + 1])
else:
indent2 = indent1
new = lines[:i]
partial = indent1
while i < n and not is_all_white(lines[i]):
# XXX Should take double space after period (etc.) into account
words = re.split("(\s+)", lines[i])
for j in range(0, len(words), 2):
word = words[j]
if not word:
continue # Can happen when line ends in whitespace
if len((partial + word).expandtabs()) > limit and partial != indent1:
new.append(partial.rstrip())
partial = indent2
partial = partial + word + " "
if j + 1 < len(words) and words[j + 1] != " ":
partial = partial + " "
i = i + 1
new.append(partial.rstrip())
# XXX Should reformat remaining paragraphs as well
new.extend(lines[i:])
return "\n".join(new)
def is_all_white(line):
return re.match(r"^\s*$", line) is not None
def get_indent(line):
return re.match(r"^(\s*)", line).group()
def get_comment_header(line):
m = re.match(r"^(\s*#*)", line)
if m is None:
return ""
return m.group(1)

View File

@ -0,0 +1,90 @@
import string
class History:
def __init__(self, text, output_sep="\n"):
self.text = text
self.history = []
self.history_prefix = None
self.history_pointer = None
self.output_sep = output_sep
text.bind("<<history-previous>>", self.history_prev)
text.bind("<<history-next>>", self.history_next)
def history_next(self, event):
self.history_do(0)
return "break"
def history_prev(self, event):
self.history_do(1)
return "break"
def _get_source(self, start, end):
# Get source code from start index to end index. Lines in the
# text control may be separated by sys.ps2 .
lines = self.text.get(start, end).split(self.output_sep)
return "\n".join(lines)
def _put_source(self, where, source):
output = self.output_sep.join(source.split("\n"))
self.text.insert(where, output)
def history_do(self, reverse):
nhist = len(self.history)
pointer = self.history_pointer
prefix = self.history_prefix
if pointer is not None and prefix is not None:
if (
self.text.compare("insert", "!=", "end-1c")
or self._get_source("iomark", "end-1c") != self.history[pointer]
):
pointer = prefix = None
if pointer is None or prefix is None:
prefix = self._get_source("iomark", "end-1c")
if reverse:
pointer = nhist
else:
pointer = -1
nprefix = len(prefix)
while 1:
if reverse:
pointer = pointer - 1
else:
pointer = pointer + 1
if pointer < 0 or pointer >= nhist:
self.text.bell()
if self._get_source("iomark", "end-1c") != prefix:
self.text.delete("iomark", "end-1c")
self._put_source("iomark", prefix)
pointer = prefix = None
break
item = self.history[pointer]
if item[:nprefix] == prefix and len(item) > nprefix:
self.text.delete("iomark", "end-1c")
self._put_source("iomark", item)
break
self.text.mark_set("insert", "end-1c")
self.text.see("insert")
self.text.tag_remove("sel", "1.0", "end")
self.history_pointer = pointer
self.history_prefix = prefix
def history_store(self, source):
source = source.strip()
if len(source) > 2:
# avoid duplicates
try:
self.history.remove(source)
except ValueError:
pass
self.history.append(source)
self.history_pointer = None
self.history_prefix = None
def recall(self, s):
s = s.strip()
self.text.tag_remove("sel", "1.0", "end")
self.text.delete("iomark", "end-1c")
self.text.mark_set("insert", "end-1c")
self.text.insert("insert", s)
self.text.see("insert")

View File

@ -0,0 +1,591 @@
import string
import re
import sys
# Reason last stmt is continued (or C_NONE if it's not).
C_NONE, C_BACKSLASH, C_STRING, C_BRACKET = list(range(4))
if 0: # for throwaway debugging output
def dump(*stuff):
sys.__stdout__.write(" ".join(map(str, stuff)) + "\n")
# Find what looks like the start of a popular stmt.
_synchre = re.compile(
r"""
^
[ \t]*
(?: if
| for
| while
| else
| def
| return
| assert
| break
| class
| continue
| elif
| try
| except
| raise
| import
)
\b
""",
re.VERBOSE | re.MULTILINE,
).search
# Match blank line or non-indenting comment line.
_junkre = re.compile(
r"""
[ \t]*
(?: \# \S .* )?
\n
""",
re.VERBOSE,
).match
# Match any flavor of string; the terminating quote is optional
# so that we're robust in the face of incomplete program text.
_match_stringre = re.compile(
r"""
\""" [^"\\]* (?:
(?: \\. | "(?!"") )
[^"\\]*
)*
(?: \""" )?
| " [^"\\\n]* (?: \\. [^"\\\n]* )* "?
| ''' [^'\\]* (?:
(?: \\. | '(?!'') )
[^'\\]*
)*
(?: ''' )?
| ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
""",
re.VERBOSE | re.DOTALL,
).match
# Match a line that starts with something interesting;
# used to find the first item of a bracket structure.
_itemre = re.compile(
r"""
[ \t]*
[^\s#\\] # if we match, m.end()-1 is the interesting char
""",
re.VERBOSE,
).match
# Match start of stmts that should be followed by a dedent.
_closere = re.compile(
r"""
\s*
(?: return
| break
| continue
| raise
| pass
)
\b
""",
re.VERBOSE,
).match
# Chew up non-special chars as quickly as possible. If match is
# successful, m.end() less 1 is the index of the last boring char
# matched. If match is unsuccessful, the string starts with an
# interesting char.
_chew_ordinaryre = re.compile(
r"""
[^[\](){}#'"\\]+
""",
re.VERBOSE,
).match
# Build translation table to map uninteresting chars to "x", open
# brackets to "(", and close brackets to ")".
_tran = ["x"] * 256
for ch in "({[":
_tran[ord(ch)] = "("
for ch in ")}]":
_tran[ord(ch)] = ")"
for ch in "\"'\\\n#":
_tran[ord(ch)] = ch
# We are called with unicode strings, and str.translate is one of the few
# py2k functions which can't 'do the right thing' - so take care to ensure
# _tran is full of unicode...
_tran = "".join(_tran)
del ch
class Parser:
def __init__(self, indentwidth, tabwidth):
self.indentwidth = indentwidth
self.tabwidth = tabwidth
def set_str(self, str):
assert len(str) == 0 or str[-1] == "\n", "Oops - have str %r" % (str,)
self.str = str
self.study_level = 0
# Return index of a good place to begin parsing, as close to the
# end of the string as possible. This will be the start of some
# popular stmt like "if" or "def". Return None if none found:
# the caller should pass more prior context then, if possible, or
# if not (the entire program text up until the point of interest
# has already been tried) pass 0 to set_lo.
#
# This will be reliable iff given a reliable is_char_in_string
# function, meaning that when it says "no", it's absolutely
# guaranteed that the char is not in a string.
#
# Ack, hack: in the shell window this kills us, because there's
# no way to tell the differences between output, >>> etc and
# user input. Indeed, IDLE's first output line makes the rest
# look like it's in an unclosed paren!:
# Python 1.5.2 (#0, Apr 13 1999, ...
def find_good_parse_start(self, use_ps1, is_char_in_string=None):
str, pos = self.str, None
if use_ps1:
# shell window
ps1 = "\n" + sys.ps1
i = str.rfind(ps1)
if i >= 0:
pos = i + len(ps1)
# make it look like there's a newline instead
# of ps1 at the start -- hacking here once avoids
# repeated hackery later
self.str = str[: pos - 1] + "\n" + str[pos:]
return pos
# File window -- real work.
if not is_char_in_string:
# no clue -- make the caller pass everything
return None
# Peek back from the end for a good place to start,
# but don't try too often; pos will be left None, or
# bumped to a legitimate synch point.
limit = len(str)
for tries in range(5):
i = str.rfind(":\n", 0, limit)
if i < 0:
break
i = str.rfind("\n", 0, i) + 1 # start of colon line
m = _synchre(str, i, limit)
if m and not is_char_in_string(m.start()):
pos = m.start()
break
limit = i
if pos is None:
# Nothing looks like a block-opener, or stuff does
# but is_char_in_string keeps returning true; most likely
# we're in or near a giant string, the colorizer hasn't
# caught up enough to be helpful, or there simply *aren't*
# any interesting stmts. In any of these cases we're
# going to have to parse the whole thing to be sure, so
# give it one last try from the start, but stop wasting
# time here regardless of the outcome.
m = _synchre(str)
if m and not is_char_in_string(m.start()):
pos = m.start()
return pos
# Peeking back worked; look forward until _synchre no longer
# matches.
i = pos + 1
while 1:
m = _synchre(str, i)
if m:
s, i = m.span()
if not is_char_in_string(s):
pos = s
else:
break
return pos
# Throw away the start of the string. Intended to be called with
# find_good_parse_start's result.
def set_lo(self, lo):
assert lo == 0 or self.str[lo - 1] == "\n"
if lo > 0:
self.str = self.str[lo:]
# As quickly as humanly possible <wink>, find the line numbers (0-
# based) of the non-continuation lines.
# Creates self.{goodlines, continuation}.
def _study1(self):
if self.study_level >= 1:
return
self.study_level = 1
# Map all uninteresting characters to "x", all open brackets
# to "(", all close brackets to ")", then collapse runs of
# uninteresting characters. This can cut the number of chars
# by a factor of 10-40, and so greatly speed the following loop.
str = self.str
str = str.translate(_tran)
str = str.replace("xxxxxxxx", "x")
str = str.replace("xxxx", "x")
str = str.replace("xx", "x")
str = str.replace("xx", "x")
str = str.replace("\nx", "\n")
# note that replacing x\n with \n would be incorrect, because
# x may be preceded by a backslash
# March over the squashed version of the program, accumulating
# the line numbers of non-continued stmts, and determining
# whether & why the last stmt is a continuation.
continuation = C_NONE
level = lno = 0 # level is nesting level; lno is line number
self.goodlines = goodlines = [0]
push_good = goodlines.append
i, n = 0, len(str)
while i < n:
ch = str[i]
i = i + 1
# cases are checked in decreasing order of frequency
if ch == "x":
continue
if ch == "\n":
lno = lno + 1
if level == 0:
push_good(lno)
# else we're in an unclosed bracket structure
continue
if ch == "(":
level = level + 1
continue
if ch == ")":
if level:
level = level - 1
# else the program is invalid, but we can't complain
continue
if ch == '"' or ch == "'":
# consume the string
quote = ch
if str[i - 1 : i + 2] == quote * 3:
quote = quote * 3
w = len(quote) - 1
i = i + w
while i < n:
ch = str[i]
i = i + 1
if ch == "x":
continue
if str[i - 1 : i + w] == quote:
i = i + w
break
if ch == "\n":
lno = lno + 1
if w == 0:
# unterminated single-quoted string
if level == 0:
push_good(lno)
break
continue
if ch == "\\":
assert i < n
if str[i] == "\n":
lno = lno + 1
i = i + 1
continue
# else comment char or paren inside string
else:
# didn't break out of the loop, so we're still
# inside a string
continuation = C_STRING
continue # with outer loop
if ch == "#":
# consume the comment
i = str.find("\n", i)
assert i >= 0
continue
assert ch == "\\"
assert i < n
if str[i] == "\n":
lno = lno + 1
if i + 1 == n:
continuation = C_BACKSLASH
i = i + 1
# The last stmt may be continued for all 3 reasons.
# String continuation takes precedence over bracket
# continuation, which beats backslash continuation.
if continuation != C_STRING and level > 0:
continuation = C_BRACKET
self.continuation = continuation
# Push the final line number as a sentinel value, regardless of
# whether it's continued.
assert (continuation == C_NONE) == (goodlines[-1] == lno)
if goodlines[-1] != lno:
push_good(lno)
def get_continuation_type(self):
self._study1()
return self.continuation
# study1 was sufficient to determine the continuation status,
# but doing more requires looking at every character. study2
# does this for the last interesting statement in the block.
# Creates:
# self.stmt_start, stmt_end
# slice indices of last interesting stmt
# self.lastch
# last non-whitespace character before optional trailing
# comment
# self.lastopenbracketpos
# if continuation is C_BRACKET, index of last open bracket
def _study2(self):
_ws = string.whitespace
if self.study_level >= 2:
return
self._study1()
self.study_level = 2
# Set p and q to slice indices of last interesting stmt.
str, goodlines = self.str, self.goodlines
i = len(goodlines) - 1
p = len(str) # index of newest line
while i:
assert p
# p is the index of the stmt at line number goodlines[i].
# Move p back to the stmt at line number goodlines[i-1].
q = p
for nothing in range(goodlines[i - 1], goodlines[i]):
# tricky: sets p to 0 if no preceding newline
p = str.rfind("\n", 0, p - 1) + 1
# The stmt str[p:q] isn't a continuation, but may be blank
# or a non-indenting comment line.
if _junkre(str, p):
i = i - 1
else:
break
if i == 0:
# nothing but junk!
assert p == 0
q = p
self.stmt_start, self.stmt_end = p, q
# Analyze this stmt, to find the last open bracket (if any)
# and last interesting character (if any).
lastch = ""
stack = [] # stack of open bracket indices
push_stack = stack.append
while p < q:
# suck up all except ()[]{}'"#\\
m = _chew_ordinaryre(str, p, q)
if m:
# we skipped at least one boring char
newp = m.end()
# back up over totally boring whitespace
i = newp - 1 # index of last boring char
while i >= p and str[i] in " \t\n":
i = i - 1
if i >= p:
lastch = str[i]
p = newp
if p >= q:
break
ch = str[p]
if ch in "([{":
push_stack(p)
lastch = ch
p = p + 1
continue
if ch in ")]}":
if stack:
del stack[-1]
lastch = ch
p = p + 1
continue
if ch == '"' or ch == "'":
# consume string
# Note that study1 did this with a Python loop, but
# we use a regexp here; the reason is speed in both
# cases; the string may be huge, but study1 pre-squashed
# strings to a couple of characters per line. study1
# also needed to keep track of newlines, and we don't
# have to.
lastch = ch
p = _match_stringre(str, p, q).end()
continue
if ch == "#":
# consume comment and trailing newline
p = str.find("\n", p, q) + 1
assert p > 0
continue
assert ch == "\\"
p = p + 1 # beyond backslash
assert p < q
if str[p] != "\n":
# the program is invalid, but can't complain
lastch = ch + str[p]
p = p + 1 # beyond escaped char
# end while p < q:
self.lastch = lastch
if stack:
self.lastopenbracketpos = stack[-1]
# Assuming continuation is C_BRACKET, return the number
# of spaces the next line should be indented.
def compute_bracket_indent(self):
self._study2()
assert self.continuation == C_BRACKET
j = self.lastopenbracketpos
str = self.str
n = len(str)
origi = i = str.rfind("\n", 0, j) + 1
j = j + 1 # one beyond open bracket
# find first list item; set i to start of its line
while j < n:
m = _itemre(str, j)
if m:
j = m.end() - 1 # index of first interesting char
extra = 0
break
else:
# this line is junk; advance to next line
i = j = str.find("\n", j) + 1
else:
# nothing interesting follows the bracket;
# reproduce the bracket line's indentation + a level
j = i = origi
while str[j] in " \t":
j = j + 1
extra = self.indentwidth
return len(str[i:j].expandtabs(self.tabwidth)) + extra
# Return number of physical lines in last stmt (whether or not
# it's an interesting stmt! this is intended to be called when
# continuation is C_BACKSLASH).
def get_num_lines_in_stmt(self):
self._study1()
goodlines = self.goodlines
return goodlines[-1] - goodlines[-2]
# Assuming continuation is C_BACKSLASH, return the number of spaces
# the next line should be indented. Also assuming the new line is
# the first one following the initial line of the stmt.
def compute_backslash_indent(self):
self._study2()
assert self.continuation == C_BACKSLASH
str = self.str
i = self.stmt_start
while str[i] in " \t":
i = i + 1
startpos = i
# See whether the initial line starts an assignment stmt; i.e.,
# look for an = operator
endpos = str.find("\n", startpos) + 1
found = level = 0
while i < endpos:
ch = str[i]
if ch in "([{":
level = level + 1
i = i + 1
elif ch in ")]}":
if level:
level = level - 1
i = i + 1
elif ch == '"' or ch == "'":
i = _match_stringre(str, i, endpos).end()
elif ch == "#":
break
elif (
level == 0
and ch == "="
and (i == 0 or str[i - 1] not in "=<>!")
and str[i + 1] != "="
):
found = 1
break
else:
i = i + 1
if found:
# found a legit =, but it may be the last interesting
# thing on the line
i = i + 1 # move beyond the =
found = re.match(r"\s*\\", str[i:endpos]) is None
if not found:
# oh well ... settle for moving beyond the first chunk
# of non-whitespace chars
i = startpos
while str[i] not in " \t\n":
i = i + 1
return len(str[self.stmt_start : i].expandtabs(self.tabwidth)) + 1
# Return the leading whitespace on the initial line of the last
# interesting stmt.
def get_base_indent_string(self):
self._study2()
i, n = self.stmt_start, self.stmt_end
j = i
str = self.str
while j < n and str[j] in " \t":
j = j + 1
return str[i:j]
# Did the last interesting stmt open a block?
def is_block_opener(self):
self._study2()
return self.lastch == ":"
# Did the last interesting stmt close a block?
def is_block_closer(self):
self._study2()
return _closere(self.str, self.stmt_start) is not None
# index of last open bracket ({[, or None if none
lastopenbracketpos = None
def get_last_open_bracket_pos(self):
self._study2()
return self.lastopenbracketpos

View File

@ -0,0 +1 @@
# This file denotes the directory as a Python package.

View File

@ -0,0 +1,84 @@
"""Support for ActiveX control hosting in Pythonwin.
"""
import win32ui, win32uiole
from . import window
# XXX - we are still "classic style" classes in py2x, so we need can't yet
# use 'type()' everywhere - revisit soon, as py2x will move to new-style too...
try:
from types import ClassType as new_type
except ImportError:
new_type = type # py3k
class Control(window.Wnd):
"""An ActiveX control base class. A new class must be derived from both
this class and the Events class. See the demos for more details.
"""
def __init__(self):
self.__dict__["_dispobj_"] = None
window.Wnd.__init__(self)
def _GetControlCLSID(self):
return self.CLSID
def _GetDispatchClass(self):
return self.default_interface
def _GetEventMap(self):
return self.default_source._dispid_to_func_
def CreateControl(self, windowTitle, style, rect, parent, id, lic_string=None):
clsid = str(self._GetControlCLSID())
self.__dict__["_obj_"] = win32ui.CreateControl(
clsid, windowTitle, style, rect, parent, id, None, False, lic_string
)
klass = self._GetDispatchClass()
dispobj = klass(win32uiole.GetIDispatchForWindow(self._obj_))
self.HookOleEvents()
self.__dict__["_dispobj_"] = dispobj
def HookOleEvents(self):
dict = self._GetEventMap()
for dispid, methodName in dict.items():
if hasattr(self, methodName):
self._obj_.HookOleEvent(getattr(self, methodName), dispid)
def __getattr__(self, attr):
# Delegate attributes to the windows and the Dispatch object for this class
try:
return window.Wnd.__getattr__(self, attr)
except AttributeError:
pass
return getattr(self._dispobj_, attr)
def __setattr__(self, attr, value):
if hasattr(self.__dict__, attr):
self.__dict__[attr] = value
return
try:
if self._dispobj_:
self._dispobj_.__setattr__(attr, value)
return
except AttributeError:
pass
self.__dict__[attr] = value
def MakeControlClass(controlClass, name=None):
"""Given a CoClass in a generated .py file, this function will return a Class
object which can be used as an OCX control.
This function is used when you do not want to handle any events from the OCX
control. If you need events, then you should derive a class from both the
activex.Control class and the CoClass
"""
if name is None:
name = controlClass.__name__
return new_type("OCX" + name, (Control, controlClass), {})
def MakeControlInstance(controlClass, name=None):
"""As for MakeControlClass(), but returns an instance of the class."""
return MakeControlClass(controlClass, name)()

View File

@ -0,0 +1,501 @@
# Generated by h2py from stdin
TCS_MULTILINE = 0x0200
CBRS_ALIGN_LEFT = 0x1000
CBRS_ALIGN_TOP = 0x2000
CBRS_ALIGN_RIGHT = 0x4000
CBRS_ALIGN_BOTTOM = 0x8000
CBRS_ALIGN_ANY = 0xF000
CBRS_BORDER_LEFT = 0x0100
CBRS_BORDER_TOP = 0x0200
CBRS_BORDER_RIGHT = 0x0400
CBRS_BORDER_BOTTOM = 0x0800
CBRS_BORDER_ANY = 0x0F00
CBRS_TOOLTIPS = 0x0010
CBRS_FLYBY = 0x0020
CBRS_FLOAT_MULTI = 0x0040
CBRS_BORDER_3D = 0x0080
CBRS_HIDE_INPLACE = 0x0008
CBRS_SIZE_DYNAMIC = 0x0004
CBRS_SIZE_FIXED = 0x0002
CBRS_FLOATING = 0x0001
CBRS_GRIPPER = 0x00400000
CBRS_ORIENT_HORZ = CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM
CBRS_ORIENT_VERT = CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT
CBRS_ORIENT_ANY = CBRS_ORIENT_HORZ | CBRS_ORIENT_VERT
CBRS_ALL = 0xFFFF
CBRS_NOALIGN = 0x00000000
CBRS_LEFT = CBRS_ALIGN_LEFT | CBRS_BORDER_RIGHT
CBRS_TOP = CBRS_ALIGN_TOP | CBRS_BORDER_BOTTOM
CBRS_RIGHT = CBRS_ALIGN_RIGHT | CBRS_BORDER_LEFT
CBRS_BOTTOM = CBRS_ALIGN_BOTTOM | CBRS_BORDER_TOP
SBPS_NORMAL = 0x0000
SBPS_NOBORDERS = 0x0100
SBPS_POPOUT = 0x0200
SBPS_OWNERDRAW = 0x1000
SBPS_DISABLED = 0x04000000
SBPS_STRETCH = 0x08000000
ID_INDICATOR_EXT = 0xE700
ID_INDICATOR_CAPS = 0xE701
ID_INDICATOR_NUM = 0xE702
ID_INDICATOR_SCRL = 0xE703
ID_INDICATOR_OVR = 0xE704
ID_INDICATOR_REC = 0xE705
ID_INDICATOR_KANA = 0xE706
ID_SEPARATOR = 0
AFX_IDW_CONTROLBAR_FIRST = 0xE800
AFX_IDW_CONTROLBAR_LAST = 0xE8FF
AFX_IDW_TOOLBAR = 0xE800
AFX_IDW_STATUS_BAR = 0xE801
AFX_IDW_PREVIEW_BAR = 0xE802
AFX_IDW_RESIZE_BAR = 0xE803
AFX_IDW_DOCKBAR_TOP = 0xE81B
AFX_IDW_DOCKBAR_LEFT = 0xE81C
AFX_IDW_DOCKBAR_RIGHT = 0xE81D
AFX_IDW_DOCKBAR_BOTTOM = 0xE81E
AFX_IDW_DOCKBAR_FLOAT = 0xE81F
def AFX_CONTROLBAR_MASK(nIDC):
return 1 << (nIDC - AFX_IDW_CONTROLBAR_FIRST)
AFX_IDW_PANE_FIRST = 0xE900
AFX_IDW_PANE_LAST = 0xE9FF
AFX_IDW_HSCROLL_FIRST = 0xEA00
AFX_IDW_VSCROLL_FIRST = 0xEA10
AFX_IDW_SIZE_BOX = 0xEA20
AFX_IDW_PANE_SAVE = 0xEA21
AFX_IDS_APP_TITLE = 0xE000
AFX_IDS_IDLEMESSAGE = 0xE001
AFX_IDS_HELPMODEMESSAGE = 0xE002
AFX_IDS_APP_TITLE_EMBEDDING = 0xE003
AFX_IDS_COMPANY_NAME = 0xE004
AFX_IDS_OBJ_TITLE_INPLACE = 0xE005
ID_FILE_NEW = 0xE100
ID_FILE_OPEN = 0xE101
ID_FILE_CLOSE = 0xE102
ID_FILE_SAVE = 0xE103
ID_FILE_SAVE_AS = 0xE104
ID_FILE_PAGE_SETUP = 0xE105
ID_FILE_PRINT_SETUP = 0xE106
ID_FILE_PRINT = 0xE107
ID_FILE_PRINT_DIRECT = 0xE108
ID_FILE_PRINT_PREVIEW = 0xE109
ID_FILE_UPDATE = 0xE10A
ID_FILE_SAVE_COPY_AS = 0xE10B
ID_FILE_SEND_MAIL = 0xE10C
ID_FILE_MRU_FIRST = 0xE110
ID_FILE_MRU_FILE1 = 0xE110
ID_FILE_MRU_FILE2 = 0xE111
ID_FILE_MRU_FILE3 = 0xE112
ID_FILE_MRU_FILE4 = 0xE113
ID_FILE_MRU_FILE5 = 0xE114
ID_FILE_MRU_FILE6 = 0xE115
ID_FILE_MRU_FILE7 = 0xE116
ID_FILE_MRU_FILE8 = 0xE117
ID_FILE_MRU_FILE9 = 0xE118
ID_FILE_MRU_FILE10 = 0xE119
ID_FILE_MRU_FILE11 = 0xE11A
ID_FILE_MRU_FILE12 = 0xE11B
ID_FILE_MRU_FILE13 = 0xE11C
ID_FILE_MRU_FILE14 = 0xE11D
ID_FILE_MRU_FILE15 = 0xE11E
ID_FILE_MRU_FILE16 = 0xE11F
ID_FILE_MRU_LAST = 0xE11F
ID_EDIT_CLEAR = 0xE120
ID_EDIT_CLEAR_ALL = 0xE121
ID_EDIT_COPY = 0xE122
ID_EDIT_CUT = 0xE123
ID_EDIT_FIND = 0xE124
ID_EDIT_PASTE = 0xE125
ID_EDIT_PASTE_LINK = 0xE126
ID_EDIT_PASTE_SPECIAL = 0xE127
ID_EDIT_REPEAT = 0xE128
ID_EDIT_REPLACE = 0xE129
ID_EDIT_SELECT_ALL = 0xE12A
ID_EDIT_UNDO = 0xE12B
ID_EDIT_REDO = 0xE12C
ID_WINDOW_NEW = 0xE130
ID_WINDOW_ARRANGE = 0xE131
ID_WINDOW_CASCADE = 0xE132
ID_WINDOW_TILE_HORZ = 0xE133
ID_WINDOW_TILE_VERT = 0xE134
ID_WINDOW_SPLIT = 0xE135
AFX_IDM_WINDOW_FIRST = 0xE130
AFX_IDM_WINDOW_LAST = 0xE13F
AFX_IDM_FIRST_MDICHILD = 0xFF00
ID_APP_ABOUT = 0xE140
ID_APP_EXIT = 0xE141
ID_HELP_INDEX = 0xE142
ID_HELP_FINDER = 0xE143
ID_HELP_USING = 0xE144
ID_CONTEXT_HELP = 0xE145
ID_HELP = 0xE146
ID_DEFAULT_HELP = 0xE147
ID_NEXT_PANE = 0xE150
ID_PREV_PANE = 0xE151
ID_FORMAT_FONT = 0xE160
ID_OLE_INSERT_NEW = 0xE200
ID_OLE_EDIT_LINKS = 0xE201
ID_OLE_EDIT_CONVERT = 0xE202
ID_OLE_EDIT_CHANGE_ICON = 0xE203
ID_OLE_EDIT_PROPERTIES = 0xE204
ID_OLE_VERB_FIRST = 0xE210
ID_OLE_VERB_LAST = 0xE21F
AFX_ID_PREVIEW_CLOSE = 0xE300
AFX_ID_PREVIEW_NUMPAGE = 0xE301
AFX_ID_PREVIEW_NEXT = 0xE302
AFX_ID_PREVIEW_PREV = 0xE303
AFX_ID_PREVIEW_PRINT = 0xE304
AFX_ID_PREVIEW_ZOOMIN = 0xE305
AFX_ID_PREVIEW_ZOOMOUT = 0xE306
ID_VIEW_TOOLBAR = 0xE800
ID_VIEW_STATUS_BAR = 0xE801
ID_RECORD_FIRST = 0xE900
ID_RECORD_LAST = 0xE901
ID_RECORD_NEXT = 0xE902
ID_RECORD_PREV = 0xE903
IDC_STATIC = -1
AFX_IDS_SCFIRST = 0xEF00
AFX_IDS_SCSIZE = 0xEF00
AFX_IDS_SCMOVE = 0xEF01
AFX_IDS_SCMINIMIZE = 0xEF02
AFX_IDS_SCMAXIMIZE = 0xEF03
AFX_IDS_SCNEXTWINDOW = 0xEF04
AFX_IDS_SCPREVWINDOW = 0xEF05
AFX_IDS_SCCLOSE = 0xEF06
AFX_IDS_SCRESTORE = 0xEF12
AFX_IDS_SCTASKLIST = 0xEF13
AFX_IDS_MDICHILD = 0xEF1F
AFX_IDS_DESKACCESSORY = 0xEFDA
AFX_IDS_OPENFILE = 0xF000
AFX_IDS_SAVEFILE = 0xF001
AFX_IDS_ALLFILTER = 0xF002
AFX_IDS_UNTITLED = 0xF003
AFX_IDS_SAVEFILECOPY = 0xF004
AFX_IDS_PREVIEW_CLOSE = 0xF005
AFX_IDS_UNNAMED_FILE = 0xF006
AFX_IDS_ABOUT = 0xF010
AFX_IDS_HIDE = 0xF011
AFX_IDP_NO_ERROR_AVAILABLE = 0xF020
AFX_IDS_NOT_SUPPORTED_EXCEPTION = 0xF021
AFX_IDS_RESOURCE_EXCEPTION = 0xF022
AFX_IDS_MEMORY_EXCEPTION = 0xF023
AFX_IDS_USER_EXCEPTION = 0xF024
AFX_IDS_PRINTONPORT = 0xF040
AFX_IDS_ONEPAGE = 0xF041
AFX_IDS_TWOPAGE = 0xF042
AFX_IDS_PRINTPAGENUM = 0xF043
AFX_IDS_PREVIEWPAGEDESC = 0xF044
AFX_IDS_PRINTDEFAULTEXT = 0xF045
AFX_IDS_PRINTDEFAULT = 0xF046
AFX_IDS_PRINTFILTER = 0xF047
AFX_IDS_PRINTCAPTION = 0xF048
AFX_IDS_PRINTTOFILE = 0xF049
AFX_IDS_OBJECT_MENUITEM = 0xF080
AFX_IDS_EDIT_VERB = 0xF081
AFX_IDS_ACTIVATE_VERB = 0xF082
AFX_IDS_CHANGE_LINK = 0xF083
AFX_IDS_AUTO = 0xF084
AFX_IDS_MANUAL = 0xF085
AFX_IDS_FROZEN = 0xF086
AFX_IDS_ALL_FILES = 0xF087
AFX_IDS_SAVE_MENU = 0xF088
AFX_IDS_UPDATE_MENU = 0xF089
AFX_IDS_SAVE_AS_MENU = 0xF08A
AFX_IDS_SAVE_COPY_AS_MENU = 0xF08B
AFX_IDS_EXIT_MENU = 0xF08C
AFX_IDS_UPDATING_ITEMS = 0xF08D
AFX_IDS_METAFILE_FORMAT = 0xF08E
AFX_IDS_DIB_FORMAT = 0xF08F
AFX_IDS_BITMAP_FORMAT = 0xF090
AFX_IDS_LINKSOURCE_FORMAT = 0xF091
AFX_IDS_EMBED_FORMAT = 0xF092
AFX_IDS_PASTELINKEDTYPE = 0xF094
AFX_IDS_UNKNOWNTYPE = 0xF095
AFX_IDS_RTF_FORMAT = 0xF096
AFX_IDS_TEXT_FORMAT = 0xF097
AFX_IDS_INVALID_CURRENCY = 0xF098
AFX_IDS_INVALID_DATETIME = 0xF099
AFX_IDS_INVALID_DATETIMESPAN = 0xF09A
AFX_IDP_INVALID_FILENAME = 0xF100
AFX_IDP_FAILED_TO_OPEN_DOC = 0xF101
AFX_IDP_FAILED_TO_SAVE_DOC = 0xF102
AFX_IDP_ASK_TO_SAVE = 0xF103
AFX_IDP_FAILED_TO_CREATE_DOC = 0xF104
AFX_IDP_FILE_TOO_LARGE = 0xF105
AFX_IDP_FAILED_TO_START_PRINT = 0xF106
AFX_IDP_FAILED_TO_LAUNCH_HELP = 0xF107
AFX_IDP_INTERNAL_FAILURE = 0xF108
AFX_IDP_COMMAND_FAILURE = 0xF109
AFX_IDP_FAILED_MEMORY_ALLOC = 0xF10A
AFX_IDP_PARSE_INT = 0xF110
AFX_IDP_PARSE_REAL = 0xF111
AFX_IDP_PARSE_INT_RANGE = 0xF112
AFX_IDP_PARSE_REAL_RANGE = 0xF113
AFX_IDP_PARSE_STRING_SIZE = 0xF114
AFX_IDP_PARSE_RADIO_BUTTON = 0xF115
AFX_IDP_PARSE_BYTE = 0xF116
AFX_IDP_PARSE_UINT = 0xF117
AFX_IDP_PARSE_DATETIME = 0xF118
AFX_IDP_PARSE_CURRENCY = 0xF119
AFX_IDP_FAILED_INVALID_FORMAT = 0xF120
AFX_IDP_FAILED_INVALID_PATH = 0xF121
AFX_IDP_FAILED_DISK_FULL = 0xF122
AFX_IDP_FAILED_ACCESS_READ = 0xF123
AFX_IDP_FAILED_ACCESS_WRITE = 0xF124
AFX_IDP_FAILED_IO_ERROR_READ = 0xF125
AFX_IDP_FAILED_IO_ERROR_WRITE = 0xF126
AFX_IDP_STATIC_OBJECT = 0xF180
AFX_IDP_FAILED_TO_CONNECT = 0xF181
AFX_IDP_SERVER_BUSY = 0xF182
AFX_IDP_BAD_VERB = 0xF183
AFX_IDP_FAILED_TO_NOTIFY = 0xF185
AFX_IDP_FAILED_TO_LAUNCH = 0xF186
AFX_IDP_ASK_TO_UPDATE = 0xF187
AFX_IDP_FAILED_TO_UPDATE = 0xF188
AFX_IDP_FAILED_TO_REGISTER = 0xF189
AFX_IDP_FAILED_TO_AUTO_REGISTER = 0xF18A
AFX_IDP_FAILED_TO_CONVERT = 0xF18B
AFX_IDP_GET_NOT_SUPPORTED = 0xF18C
AFX_IDP_SET_NOT_SUPPORTED = 0xF18D
AFX_IDP_ASK_TO_DISCARD = 0xF18E
AFX_IDP_FAILED_TO_CREATE = 0xF18F
AFX_IDP_FAILED_MAPI_LOAD = 0xF190
AFX_IDP_INVALID_MAPI_DLL = 0xF191
AFX_IDP_FAILED_MAPI_SEND = 0xF192
AFX_IDP_FILE_NONE = 0xF1A0
AFX_IDP_FILE_GENERIC = 0xF1A1
AFX_IDP_FILE_NOT_FOUND = 0xF1A2
AFX_IDP_FILE_BAD_PATH = 0xF1A3
AFX_IDP_FILE_TOO_MANY_OPEN = 0xF1A4
AFX_IDP_FILE_ACCESS_DENIED = 0xF1A5
AFX_IDP_FILE_INVALID_FILE = 0xF1A6
AFX_IDP_FILE_REMOVE_CURRENT = 0xF1A7
AFX_IDP_FILE_DIR_FULL = 0xF1A8
AFX_IDP_FILE_BAD_SEEK = 0xF1A9
AFX_IDP_FILE_HARD_IO = 0xF1AA
AFX_IDP_FILE_SHARING = 0xF1AB
AFX_IDP_FILE_LOCKING = 0xF1AC
AFX_IDP_FILE_DISKFULL = 0xF1AD
AFX_IDP_FILE_EOF = 0xF1AE
AFX_IDP_ARCH_NONE = 0xF1B0
AFX_IDP_ARCH_GENERIC = 0xF1B1
AFX_IDP_ARCH_READONLY = 0xF1B2
AFX_IDP_ARCH_ENDOFFILE = 0xF1B3
AFX_IDP_ARCH_WRITEONLY = 0xF1B4
AFX_IDP_ARCH_BADINDEX = 0xF1B5
AFX_IDP_ARCH_BADCLASS = 0xF1B6
AFX_IDP_ARCH_BADSCHEMA = 0xF1B7
AFX_IDS_OCC_SCALEUNITS_PIXELS = 0xF1C0
AFX_IDS_STATUS_FONT = 0xF230
AFX_IDS_TOOLTIP_FONT = 0xF231
AFX_IDS_UNICODE_FONT = 0xF232
AFX_IDS_MINI_FONT = 0xF233
AFX_IDP_SQL_FIRST = 0xF280
AFX_IDP_SQL_CONNECT_FAIL = 0xF281
AFX_IDP_SQL_RECORDSET_FORWARD_ONLY = 0xF282
AFX_IDP_SQL_EMPTY_COLUMN_LIST = 0xF283
AFX_IDP_SQL_FIELD_SCHEMA_MISMATCH = 0xF284
AFX_IDP_SQL_ILLEGAL_MODE = 0xF285
AFX_IDP_SQL_MULTIPLE_ROWS_AFFECTED = 0xF286
AFX_IDP_SQL_NO_CURRENT_RECORD = 0xF287
AFX_IDP_SQL_NO_ROWS_AFFECTED = 0xF288
AFX_IDP_SQL_RECORDSET_READONLY = 0xF289
AFX_IDP_SQL_SQL_NO_TOTAL = 0xF28A
AFX_IDP_SQL_ODBC_LOAD_FAILED = 0xF28B
AFX_IDP_SQL_DYNASET_NOT_SUPPORTED = 0xF28C
AFX_IDP_SQL_SNAPSHOT_NOT_SUPPORTED = 0xF28D
AFX_IDP_SQL_API_CONFORMANCE = 0xF28E
AFX_IDP_SQL_SQL_CONFORMANCE = 0xF28F
AFX_IDP_SQL_NO_DATA_FOUND = 0xF290
AFX_IDP_SQL_ROW_UPDATE_NOT_SUPPORTED = 0xF291
AFX_IDP_SQL_ODBC_V2_REQUIRED = 0xF292
AFX_IDP_SQL_NO_POSITIONED_UPDATES = 0xF293
AFX_IDP_SQL_LOCK_MODE_NOT_SUPPORTED = 0xF294
AFX_IDP_SQL_DATA_TRUNCATED = 0xF295
AFX_IDP_SQL_ROW_FETCH = 0xF296
AFX_IDP_SQL_INCORRECT_ODBC = 0xF297
AFX_IDP_SQL_UPDATE_DELETE_FAILED = 0xF298
AFX_IDP_SQL_DYNAMIC_CURSOR_NOT_SUPPORTED = 0xF299
AFX_IDP_DAO_FIRST = 0xF2A0
AFX_IDP_DAO_ENGINE_INITIALIZATION = 0xF2A0
AFX_IDP_DAO_DFX_BIND = 0xF2A1
AFX_IDP_DAO_OBJECT_NOT_OPEN = 0xF2A2
AFX_IDP_DAO_ROWTOOSHORT = 0xF2A3
AFX_IDP_DAO_BADBINDINFO = 0xF2A4
AFX_IDP_DAO_COLUMNUNAVAILABLE = 0xF2A5
AFX_IDC_LISTBOX = 100
AFX_IDC_CHANGE = 101
AFX_IDC_PRINT_DOCNAME = 201
AFX_IDC_PRINT_PRINTERNAME = 202
AFX_IDC_PRINT_PORTNAME = 203
AFX_IDC_PRINT_PAGENUM = 204
ID_APPLY_NOW = 0x3021
ID_WIZBACK = 0x3023
ID_WIZNEXT = 0x3024
ID_WIZFINISH = 0x3025
AFX_IDC_TAB_CONTROL = 0x3020
AFX_IDD_FILEOPEN = 28676
AFX_IDD_FILESAVE = 28677
AFX_IDD_FONT = 28678
AFX_IDD_COLOR = 28679
AFX_IDD_PRINT = 28680
AFX_IDD_PRINTSETUP = 28681
AFX_IDD_FIND = 28682
AFX_IDD_REPLACE = 28683
AFX_IDD_NEWTYPEDLG = 30721
AFX_IDD_PRINTDLG = 30722
AFX_IDD_PREVIEW_TOOLBAR = 30723
AFX_IDD_PREVIEW_SHORTTOOLBAR = 30731
AFX_IDD_INSERTOBJECT = 30724
AFX_IDD_CHANGEICON = 30725
AFX_IDD_CONVERT = 30726
AFX_IDD_PASTESPECIAL = 30727
AFX_IDD_EDITLINKS = 30728
AFX_IDD_FILEBROWSE = 30729
AFX_IDD_BUSY = 30730
AFX_IDD_OBJECTPROPERTIES = 30732
AFX_IDD_CHANGESOURCE = 30733
AFX_IDC_CONTEXTHELP = 30977
AFX_IDC_MAGNIFY = 30978
AFX_IDC_SMALLARROWS = 30979
AFX_IDC_HSPLITBAR = 30980
AFX_IDC_VSPLITBAR = 30981
AFX_IDC_NODROPCRSR = 30982
AFX_IDC_TRACKNWSE = 30983
AFX_IDC_TRACKNESW = 30984
AFX_IDC_TRACKNS = 30985
AFX_IDC_TRACKWE = 30986
AFX_IDC_TRACK4WAY = 30987
AFX_IDC_MOVE4WAY = 30988
AFX_IDB_MINIFRAME_MENU = 30994
AFX_IDB_CHECKLISTBOX_NT = 30995
AFX_IDB_CHECKLISTBOX_95 = 30996
AFX_IDR_PREVIEW_ACCEL = 30997
AFX_IDI_STD_MDIFRAME = 31233
AFX_IDI_STD_FRAME = 31234
AFX_IDC_FONTPROP = 1000
AFX_IDC_FONTNAMES = 1001
AFX_IDC_FONTSTYLES = 1002
AFX_IDC_FONTSIZES = 1003
AFX_IDC_STRIKEOUT = 1004
AFX_IDC_UNDERLINE = 1005
AFX_IDC_SAMPLEBOX = 1006
AFX_IDC_COLOR_BLACK = 1100
AFX_IDC_COLOR_WHITE = 1101
AFX_IDC_COLOR_RED = 1102
AFX_IDC_COLOR_GREEN = 1103
AFX_IDC_COLOR_BLUE = 1104
AFX_IDC_COLOR_YELLOW = 1105
AFX_IDC_COLOR_MAGENTA = 1106
AFX_IDC_COLOR_CYAN = 1107
AFX_IDC_COLOR_GRAY = 1108
AFX_IDC_COLOR_LIGHTGRAY = 1109
AFX_IDC_COLOR_DARKRED = 1110
AFX_IDC_COLOR_DARKGREEN = 1111
AFX_IDC_COLOR_DARKBLUE = 1112
AFX_IDC_COLOR_LIGHTBROWN = 1113
AFX_IDC_COLOR_DARKMAGENTA = 1114
AFX_IDC_COLOR_DARKCYAN = 1115
AFX_IDC_COLORPROP = 1116
AFX_IDC_SYSTEMCOLORS = 1117
AFX_IDC_PROPNAME = 1201
AFX_IDC_PICTURE = 1202
AFX_IDC_BROWSE = 1203
AFX_IDC_CLEAR = 1204
AFX_IDD_PROPPAGE_COLOR = 32257
AFX_IDD_PROPPAGE_FONT = 32258
AFX_IDD_PROPPAGE_PICTURE = 32259
AFX_IDB_TRUETYPE = 32384
AFX_IDS_PROPPAGE_UNKNOWN = 0xFE01
AFX_IDS_COLOR_DESKTOP = 0xFE04
AFX_IDS_COLOR_APPWORKSPACE = 0xFE05
AFX_IDS_COLOR_WNDBACKGND = 0xFE06
AFX_IDS_COLOR_WNDTEXT = 0xFE07
AFX_IDS_COLOR_MENUBAR = 0xFE08
AFX_IDS_COLOR_MENUTEXT = 0xFE09
AFX_IDS_COLOR_ACTIVEBAR = 0xFE0A
AFX_IDS_COLOR_INACTIVEBAR = 0xFE0B
AFX_IDS_COLOR_ACTIVETEXT = 0xFE0C
AFX_IDS_COLOR_INACTIVETEXT = 0xFE0D
AFX_IDS_COLOR_ACTIVEBORDER = 0xFE0E
AFX_IDS_COLOR_INACTIVEBORDER = 0xFE0F
AFX_IDS_COLOR_WNDFRAME = 0xFE10
AFX_IDS_COLOR_SCROLLBARS = 0xFE11
AFX_IDS_COLOR_BTNFACE = 0xFE12
AFX_IDS_COLOR_BTNSHADOW = 0xFE13
AFX_IDS_COLOR_BTNTEXT = 0xFE14
AFX_IDS_COLOR_BTNHIGHLIGHT = 0xFE15
AFX_IDS_COLOR_DISABLEDTEXT = 0xFE16
AFX_IDS_COLOR_HIGHLIGHT = 0xFE17
AFX_IDS_COLOR_HIGHLIGHTTEXT = 0xFE18
AFX_IDS_REGULAR = 0xFE19
AFX_IDS_BOLD = 0xFE1A
AFX_IDS_ITALIC = 0xFE1B
AFX_IDS_BOLDITALIC = 0xFE1C
AFX_IDS_SAMPLETEXT = 0xFE1D
AFX_IDS_DISPLAYSTRING_FONT = 0xFE1E
AFX_IDS_DISPLAYSTRING_COLOR = 0xFE1F
AFX_IDS_DISPLAYSTRING_PICTURE = 0xFE20
AFX_IDS_PICTUREFILTER = 0xFE21
AFX_IDS_PICTYPE_UNKNOWN = 0xFE22
AFX_IDS_PICTYPE_NONE = 0xFE23
AFX_IDS_PICTYPE_BITMAP = 0xFE24
AFX_IDS_PICTYPE_METAFILE = 0xFE25
AFX_IDS_PICTYPE_ICON = 0xFE26
AFX_IDS_COLOR_PPG = 0xFE28
AFX_IDS_COLOR_PPG_CAPTION = 0xFE29
AFX_IDS_FONT_PPG = 0xFE2A
AFX_IDS_FONT_PPG_CAPTION = 0xFE2B
AFX_IDS_PICTURE_PPG = 0xFE2C
AFX_IDS_PICTURE_PPG_CAPTION = 0xFE2D
AFX_IDS_PICTUREBROWSETITLE = 0xFE30
AFX_IDS_BORDERSTYLE_0 = 0xFE31
AFX_IDS_BORDERSTYLE_1 = 0xFE32
AFX_IDS_VERB_EDIT = 0xFE40
AFX_IDS_VERB_PROPERTIES = 0xFE41
AFX_IDP_PICTURECANTOPEN = 0xFE83
AFX_IDP_PICTURECANTLOAD = 0xFE84
AFX_IDP_PICTURETOOLARGE = 0xFE85
AFX_IDP_PICTUREREADFAILED = 0xFE86
AFX_IDP_E_ILLEGALFUNCTIONCALL = 0xFEA0
AFX_IDP_E_OVERFLOW = 0xFEA1
AFX_IDP_E_OUTOFMEMORY = 0xFEA2
AFX_IDP_E_DIVISIONBYZERO = 0xFEA3
AFX_IDP_E_OUTOFSTRINGSPACE = 0xFEA4
AFX_IDP_E_OUTOFSTACKSPACE = 0xFEA5
AFX_IDP_E_BADFILENAMEORNUMBER = 0xFEA6
AFX_IDP_E_FILENOTFOUND = 0xFEA7
AFX_IDP_E_BADFILEMODE = 0xFEA8
AFX_IDP_E_FILEALREADYOPEN = 0xFEA9
AFX_IDP_E_DEVICEIOERROR = 0xFEAA
AFX_IDP_E_FILEALREADYEXISTS = 0xFEAB
AFX_IDP_E_BADRECORDLENGTH = 0xFEAC
AFX_IDP_E_DISKFULL = 0xFEAD
AFX_IDP_E_BADRECORDNUMBER = 0xFEAE
AFX_IDP_E_BADFILENAME = 0xFEAF
AFX_IDP_E_TOOMANYFILES = 0xFEB0
AFX_IDP_E_DEVICEUNAVAILABLE = 0xFEB1
AFX_IDP_E_PERMISSIONDENIED = 0xFEB2
AFX_IDP_E_DISKNOTREADY = 0xFEB3
AFX_IDP_E_PATHFILEACCESSERROR = 0xFEB4
AFX_IDP_E_PATHNOTFOUND = 0xFEB5
AFX_IDP_E_INVALIDPATTERNSTRING = 0xFEB6
AFX_IDP_E_INVALIDUSEOFNULL = 0xFEB7
AFX_IDP_E_INVALIDFILEFORMAT = 0xFEB8
AFX_IDP_E_INVALIDPROPERTYVALUE = 0xFEB9
AFX_IDP_E_INVALIDPROPERTYARRAYINDEX = 0xFEBA
AFX_IDP_E_SETNOTSUPPORTEDATRUNTIME = 0xFEBB
AFX_IDP_E_SETNOTSUPPORTED = 0xFEBC
AFX_IDP_E_NEEDPROPERTYARRAYINDEX = 0xFEBD
AFX_IDP_E_SETNOTPERMITTED = 0xFEBE
AFX_IDP_E_GETNOTSUPPORTEDATRUNTIME = 0xFEBF
AFX_IDP_E_GETNOTSUPPORTED = 0xFEC0
AFX_IDP_E_PROPERTYNOTFOUND = 0xFEC1
AFX_IDP_E_INVALIDCLIPBOARDFORMAT = 0xFEC2
AFX_IDP_E_INVALIDPICTURE = 0xFEC3
AFX_IDP_E_PRINTERERROR = 0xFEC4
AFX_IDP_E_CANTSAVEFILETOTEMP = 0xFEC5
AFX_IDP_E_SEARCHTEXTNOTFOUND = 0xFEC6
AFX_IDP_E_REPLACEMENTSTOOLONG = 0xFEC7

View File

@ -0,0 +1,278 @@
""" \
Base class for Dialogs. Also contains a few useful utility functions
"""
# dialog.py
# Python class for Dialog Boxes in PythonWin.
import win32ui
import win32con
# sob - 2to3 doesn't see this as a relative import :(
from pywin.mfc import window
def dllFromDll(dllid):
"given a 'dll' (maybe a dll, filename, etc), return a DLL object"
if dllid == None:
return None
elif type("") == type(dllid):
return win32ui.LoadLibrary(dllid)
else:
try:
dllid.GetFileName()
except AttributeError:
raise TypeError("DLL parameter must be None, a filename or a dll object")
return dllid
class Dialog(window.Wnd):
"Base class for a dialog"
def __init__(self, id, dllid=None):
"""id is the resource ID, or a template
dllid may be None, a dll object, or a string with a dll name"""
# must take a reference to the DLL until InitDialog.
self.dll = dllFromDll(dllid)
if type(id) == type([]): # a template
dlg = win32ui.CreateDialogIndirect(id)
else:
dlg = win32ui.CreateDialog(id, self.dll)
window.Wnd.__init__(self, dlg)
self.HookCommands()
self.bHaveInit = None
def HookCommands(self):
pass
def OnAttachedObjectDeath(self):
self.data = self._obj_.data
window.Wnd.OnAttachedObjectDeath(self)
# provide virtuals.
def OnOK(self):
self._obj_.OnOK()
def OnCancel(self):
self._obj_.OnCancel()
def OnInitDialog(self):
self.bHaveInit = 1
if self._obj_.data:
self._obj_.UpdateData(0)
return 1 # I did NOT set focus to a child window.
def OnDestroy(self, msg):
self.dll = None # theoretically not needed if object destructs normally.
# DDX support
def AddDDX(self, *args):
self._obj_.datalist.append(args)
# Make a dialog object look like a dictionary for the DDX support
def __bool__(self):
return True
def __len__(self):
return len(self.data)
def __getitem__(self, key):
return self.data[key]
def __setitem__(self, key, item):
self._obj_.data[key] = item # self.UpdateData(0)
def keys(self):
return list(self.data.keys())
def items(self):
return list(self.data.items())
def values(self):
return list(self.data.values())
# XXX - needs py3k work!
def has_key(self, key):
return key in self.data
class PrintDialog(Dialog):
"Base class for a print dialog"
def __init__(
self,
pInfo,
dlgID,
printSetupOnly=0,
flags=(
win32ui.PD_ALLPAGES
| win32ui.PD_USEDEVMODECOPIES
| win32ui.PD_NOPAGENUMS
| win32ui.PD_HIDEPRINTTOFILE
| win32ui.PD_NOSELECTION
),
parent=None,
dllid=None,
):
self.dll = dllFromDll(dllid)
if type(dlgID) == type([]): # a template
raise TypeError("dlgID parameter must be an integer resource ID")
dlg = win32ui.CreatePrintDialog(dlgID, printSetupOnly, flags, parent, self.dll)
window.Wnd.__init__(self, dlg)
self.HookCommands()
self.bHaveInit = None
self.pInfo = pInfo
# init values (if PrintSetup is called, values still available)
flags = pInfo.GetFlags()
self["toFile"] = flags & win32ui.PD_PRINTTOFILE != 0
self["direct"] = pInfo.GetDirect()
self["preview"] = pInfo.GetPreview()
self["continuePrinting"] = pInfo.GetContinuePrinting()
self["curPage"] = pInfo.GetCurPage()
self["numPreviewPages"] = pInfo.GetNumPreviewPages()
self["userData"] = pInfo.GetUserData()
self["draw"] = pInfo.GetDraw()
self["pageDesc"] = pInfo.GetPageDesc()
self["minPage"] = pInfo.GetMinPage()
self["maxPage"] = pInfo.GetMaxPage()
self["offsetPage"] = pInfo.GetOffsetPage()
self["fromPage"] = pInfo.GetFromPage()
self["toPage"] = pInfo.GetToPage()
# these values updated after OnOK
self["copies"] = 0
self["deviceName"] = ""
self["driverName"] = ""
self["printAll"] = 0
self["printCollate"] = 0
self["printRange"] = 0
self["printSelection"] = 0
def OnInitDialog(self):
self.pInfo.CreatePrinterDC() # This also sets the hDC of the pInfo structure.
return self._obj_.OnInitDialog()
def OnCancel(self):
del self.pInfo
def OnOK(self):
"""DoModal has finished. Can now access the users choices"""
self._obj_.OnOK()
pInfo = self.pInfo
# user values
flags = pInfo.GetFlags()
self["toFile"] = flags & win32ui.PD_PRINTTOFILE != 0
self["direct"] = pInfo.GetDirect()
self["preview"] = pInfo.GetPreview()
self["continuePrinting"] = pInfo.GetContinuePrinting()
self["curPage"] = pInfo.GetCurPage()
self["numPreviewPages"] = pInfo.GetNumPreviewPages()
self["userData"] = pInfo.GetUserData()
self["draw"] = pInfo.GetDraw()
self["pageDesc"] = pInfo.GetPageDesc()
self["minPage"] = pInfo.GetMinPage()
self["maxPage"] = pInfo.GetMaxPage()
self["offsetPage"] = pInfo.GetOffsetPage()
self["fromPage"] = pInfo.GetFromPage()
self["toPage"] = pInfo.GetToPage()
self["copies"] = pInfo.GetCopies()
self["deviceName"] = pInfo.GetDeviceName()
self["driverName"] = pInfo.GetDriverName()
self["printAll"] = pInfo.PrintAll()
self["printCollate"] = pInfo.PrintCollate()
self["printRange"] = pInfo.PrintRange()
self["printSelection"] = pInfo.PrintSelection()
del self.pInfo
class PropertyPage(Dialog):
"Base class for a Property Page"
def __init__(self, id, dllid=None, caption=0):
"""id is the resource ID
dllid may be None, a dll object, or a string with a dll name"""
self.dll = dllFromDll(dllid)
if self.dll:
oldRes = win32ui.SetResource(self.dll)
if type(id) == type([]):
dlg = win32ui.CreatePropertyPageIndirect(id)
else:
dlg = win32ui.CreatePropertyPage(id, caption)
if self.dll:
win32ui.SetResource(oldRes)
# dont call dialog init!
window.Wnd.__init__(self, dlg)
self.HookCommands()
class PropertySheet(window.Wnd):
def __init__(self, caption, dll=None, pageList=None): # parent=None, style,etc):
"Initialize a property sheet. pageList is a list of ID's"
# must take a reference to the DLL until InitDialog.
self.dll = dllFromDll(dll)
self.sheet = win32ui.CreatePropertySheet(caption)
window.Wnd.__init__(self, self.sheet)
if not pageList is None:
self.AddPage(pageList)
def OnInitDialog(self):
return self._obj_.OnInitDialog()
def DoModal(self):
if self.dll:
oldRes = win32ui.SetResource(self.dll)
rc = self.sheet.DoModal()
if self.dll:
win32ui.SetResource(oldRes)
return rc
def AddPage(self, pages):
if self.dll:
oldRes = win32ui.SetResource(self.dll)
try: # try list style access
pages[0]
isSeq = 1
except (TypeError, KeyError):
isSeq = 0
if isSeq:
for page in pages:
self.DoAddSinglePage(page)
else:
self.DoAddSinglePage(pages)
if self.dll:
win32ui.SetResource(oldRes)
def DoAddSinglePage(self, page):
"Page may be page, or int ID. Assumes DLL setup"
if type(page) == type(0):
self.sheet.AddPage(win32ui.CreatePropertyPage(page))
else:
self.sheet.AddPage(page)
# define some app utility functions.
def GetSimpleInput(prompt, defValue="", title=None):
"""displays a dialog, and returns a string, or None if cancelled.
args prompt, defValue='', title=main frames title"""
# uses a simple dialog to return a string object.
if title is None:
title = win32ui.GetMainFrame().GetWindowText()
# 2to3 insists on converting 'Dialog.__init__' to 'tkinter.dialog...'
DlgBaseClass = Dialog
class DlgSimpleInput(DlgBaseClass):
def __init__(self, prompt, defValue, title):
self.title = title
DlgBaseClass.__init__(self, win32ui.IDD_SIMPLE_INPUT)
self.AddDDX(win32ui.IDC_EDIT1, "result")
self.AddDDX(win32ui.IDC_PROMPT1, "prompt")
self._obj_.data["result"] = defValue
self._obj_.data["prompt"] = prompt
def OnInitDialog(self):
self.SetWindowText(self.title)
return DlgBaseClass.OnInitDialog(self)
dlg = DlgSimpleInput(prompt, defValue, title)
if dlg.DoModal() != win32con.IDOK:
return None
return dlg["result"]

View File

@ -0,0 +1,152 @@
# document and view classes for MFC.
import win32ui
import win32con
from . import object
from . import window
class View(window.Wnd):
def __init__(self, initobj):
window.Wnd.__init__(self, initobj)
def OnInitialUpdate(self):
pass
# Simple control based views.
class CtrlView(View):
def __init__(self, doc, wndclass, style=0):
View.__init__(self, win32ui.CreateCtrlView(doc, wndclass, style))
class EditView(CtrlView):
def __init__(self, doc):
View.__init__(self, win32ui.CreateEditView(doc))
class RichEditView(CtrlView):
def __init__(self, doc):
View.__init__(self, win32ui.CreateRichEditView(doc))
class ListView(CtrlView):
def __init__(self, doc):
View.__init__(self, win32ui.CreateListView(doc))
class TreeView(CtrlView):
def __init__(self, doc):
View.__init__(self, win32ui.CreateTreeView(doc))
# Other more advanced views.
class ScrollView(View):
def __init__(self, doc):
View.__init__(self, win32ui.CreateView(doc))
class FormView(View):
def __init__(self, doc, id):
View.__init__(self, win32ui.CreateFormView(doc, id))
class Document(object.CmdTarget):
def __init__(self, template, docobj=None):
if docobj is None:
docobj = template.DoCreateDoc()
object.CmdTarget.__init__(self, docobj)
class RichEditDoc(object.CmdTarget):
def __init__(self, template):
object.CmdTarget.__init__(self, template.DoCreateRichEditDoc())
class CreateContext:
"A transient base class used as a CreateContext"
def __init__(self, template, doc=None):
self.template = template
self.doc = doc
def __del__(self):
self.close()
def close(self):
self.doc = None
self.template = None
class DocTemplate(object.CmdTarget):
def __init__(
self, resourceId=None, MakeDocument=None, MakeFrame=None, MakeView=None
):
if resourceId is None:
resourceId = win32ui.IDR_PYTHONTYPE
object.CmdTarget.__init__(self, self._CreateDocTemplate(resourceId))
self.MakeDocument = MakeDocument
self.MakeFrame = MakeFrame
self.MakeView = MakeView
self._SetupSharedMenu_()
def _SetupSharedMenu_(self):
pass # to be overridden by each "app"
def _CreateDocTemplate(self, resourceId):
return win32ui.CreateDocTemplate(resourceId)
def __del__(self):
object.CmdTarget.__del__(self)
def CreateCreateContext(self, doc=None):
return CreateContext(self, doc)
def CreateNewFrame(self, doc):
makeFrame = self.MakeFrame
if makeFrame is None:
makeFrame = window.MDIChildWnd
wnd = makeFrame()
context = self.CreateCreateContext(doc)
wnd.LoadFrame(
self.GetResourceID(), -1, None, context
) # triggers OnCreateClient...
return wnd
def CreateNewDocument(self):
makeDocument = self.MakeDocument
if makeDocument is None:
makeDocument = Document
return makeDocument(self)
def CreateView(self, frame, context):
makeView = self.MakeView
if makeView is None:
makeView = EditView
view = makeView(context.doc)
view.CreateWindow(frame)
class RichEditDocTemplate(DocTemplate):
def __init__(
self, resourceId=None, MakeDocument=None, MakeFrame=None, MakeView=None
):
if MakeView is None:
MakeView = RichEditView
if MakeDocument is None:
MakeDocument = RichEditDoc
DocTemplate.__init__(self, resourceId, MakeDocument, MakeFrame, MakeView)
def _CreateDocTemplate(self, resourceId):
return win32ui.CreateRichEditDocTemplate(resourceId)
def t():
class FormTemplate(DocTemplate):
def CreateView(self, frame, context):
makeView = self.MakeView
# view = FormView(context.doc, win32ui.IDD_PROPDEMO1)
view = ListView(context.doc)
view.CreateWindow(frame)
t = FormTemplate()
return t.OpenDocumentFile(None)

View File

@ -0,0 +1,66 @@
# MFC base classes.
import sys
import win32ui
class Object:
def __init__(self, initObj=None):
self.__dict__["_obj_"] = initObj
# self._obj_ = initObj
if initObj is not None:
initObj.AttachObject(self)
def __del__(self):
self.close()
def __getattr__(
self, attr
): # Make this object look like the underlying win32ui one.
# During cleanup __dict__ is not available, causing recursive death.
if not attr.startswith("__"):
try:
o = self.__dict__["_obj_"]
if o is not None:
return getattr(o, attr)
# Only raise this error for non "internal" names -
# Python may be calling __len__, __nonzero__, etc, so
# we dont want this exception
if attr[0] != "_" and attr[-1] != "_":
raise win32ui.error("The MFC object has died.")
except KeyError:
# No _obj_ at all - dont report MFC object died when there isnt one!
pass
raise AttributeError(attr)
def OnAttachedObjectDeath(self):
# print "object", self.__class__.__name__, "dieing"
self._obj_ = None
def close(self):
if "_obj_" in self.__dict__:
if self._obj_ is not None:
self._obj_.AttachObject(None)
self._obj_ = None
class CmdTarget(Object):
def __init__(self, initObj):
Object.__init__(self, initObj)
def HookNotifyRange(self, handler, firstID, lastID):
oldhandlers = []
for i in range(firstID, lastID + 1):
oldhandlers.append(self.HookNotify(handler, i))
return oldhandlers
def HookCommandRange(self, handler, firstID, lastID):
oldhandlers = []
for i in range(firstID, lastID + 1):
oldhandlers.append(self.HookCommand(handler, i))
return oldhandlers
def HookCommandUpdateRange(self, handler, firstID, lastID):
oldhandlers = []
for i in range(firstID, lastID + 1):
oldhandlers.append(self.HookCommandUpdate(handler, i))
return oldhandlers

View File

@ -0,0 +1,24 @@
# Thread and application objects
from . import object
import win32ui
class WinThread(object.CmdTarget):
def __init__(self, initObj=None):
if initObj is None:
initObj = win32ui.CreateThread()
object.CmdTarget.__init__(self, initObj)
def InitInstance(self):
pass # Default None/0 return indicates success for InitInstance()
def ExitInstance(self):
pass
class WinApp(WinThread):
def __init__(self, initApp=None):
if initApp is None:
initApp = win32ui.GetApp()
WinThread.__init__(self, initApp)

View File

@ -0,0 +1,49 @@
# The MFCish window classes.
from . import object
import win32ui
import win32con
class Wnd(object.CmdTarget):
def __init__(self, initobj=None):
object.CmdTarget.__init__(self, initobj)
if self._obj_:
self._obj_.HookMessage(self.OnDestroy, win32con.WM_DESTROY)
def OnDestroy(self, msg):
pass
# NOTE NOTE - This facility is currently disabled in Pythonwin!!!!!
# Note - to process all messages for your window, add the following method
# to a derived class. This code provides default message handling (ie, is
# identical, except presumably in speed, as if the method did not exist at
# all, so presumably will be modified to test for specific messages to be
# useful!
# def WindowProc(self, msg, wParam, lParam):
# rc, lResult = self._obj_.OnWndMsg(msg, wParam, lParam)
# if not rc: lResult = self._obj_.DefWindowProc(msg, wParam, lParam)
# return lResult
class FrameWnd(Wnd):
def __init__(self, wnd):
Wnd.__init__(self, wnd)
class MDIChildWnd(FrameWnd):
def __init__(self, wnd=None):
if wnd is None:
wnd = win32ui.CreateMDIChild()
FrameWnd.__init__(self, wnd)
def OnCreateClient(self, cp, context):
if context is not None and context.template is not None:
context.template.CreateView(self, context)
class MDIFrameWnd(FrameWnd):
def __init__(self, wnd=None):
if wnd is None:
wnd = win32ui.CreateMDIFrame()
FrameWnd.__init__(self, wnd)

View File

@ -0,0 +1,596 @@
# Code that allows Pythonwin to pretend it is IDLE
# (at least as far as most IDLE extensions are concerned)
import string
import win32api
import win32ui
import win32con
import sys
from pywin.mfc.dialog import GetSimpleInput
from pywin import default_scintilla_encoding
wordchars = string.ascii_uppercase + string.ascii_lowercase + string.digits
class TextError(Exception): # When a TclError would normally be raised.
pass
class EmptyRange(Exception): # Internally raised.
pass
def GetIDLEModule(module):
try:
# First get it from Pythonwin it is exists.
modname = "pywin.idle." + module
__import__(modname)
except ImportError as details:
msg = (
"The IDLE extension '%s' can not be located.\r\n\r\n"
"Please correct the installation and restart the"
" application.\r\n\r\n%s" % (module, details)
)
win32ui.MessageBox(msg)
return None
mod = sys.modules[modname]
mod.TclError = TextError # A hack that can go soon!
return mod
# A class that is injected into the IDLE auto-indent extension.
# It allows for decent performance when opening a new file,
# as auto-indent uses the tokenizer module to determine indents.
# The default AutoIndent readline method works OK, but it goes through
# this layer of Tk index indirection for every single line. For large files
# without indents (and even small files with indents :-) it was pretty slow!
def fast_readline(self):
if self.finished:
val = ""
else:
if "_scint_lines" not in self.__dict__:
# XXX - note - assumes this is only called once the file is loaded!
self._scint_lines = self.text.edit.GetTextRange().split("\n")
sl = self._scint_lines
i = self.i = self.i + 1
if i >= len(sl):
val = ""
else:
val = sl[i] + "\n"
return val.encode(default_scintilla_encoding)
try:
GetIDLEModule("AutoIndent").IndentSearcher.readline = fast_readline
except AttributeError: # GetIDLEModule may return None
pass
# A class that attempts to emulate an IDLE editor window.
# Construct with a Pythonwin view.
class IDLEEditorWindow:
def __init__(self, edit):
self.edit = edit
self.text = TkText(edit)
self.extensions = {}
self.extension_menus = {}
def close(self):
self.edit = self.text = None
self.extension_menus = None
try:
for ext in self.extensions.values():
closer = getattr(ext, "close", None)
if closer is not None:
closer()
finally:
self.extensions = {}
def IDLEExtension(self, extension):
ext = self.extensions.get(extension)
if ext is not None:
return ext
mod = GetIDLEModule(extension)
if mod is None:
return None
klass = getattr(mod, extension)
ext = self.extensions[extension] = klass(self)
# Find and bind all the events defined in the extension.
events = [item for item in dir(klass) if item[-6:] == "_event"]
for event in events:
name = "<<%s>>" % (event[:-6].replace("_", "-"),)
self.edit.bindings.bind(name, getattr(ext, event))
return ext
def GetMenuItems(self, menu_name):
# Get all menu items for the menu name (eg, "edit")
bindings = self.edit.bindings
ret = []
for ext in self.extensions.values():
menudefs = getattr(ext, "menudefs", [])
for name, items in menudefs:
if name == menu_name:
for text, event in [item for item in items if item is not None]:
text = text.replace("&", "&&")
text = text.replace("_", "&")
ret.append((text, event))
return ret
######################################################################
# The IDLE "Virtual UI" methods that are exposed to the IDLE extensions.
#
def askinteger(
self, caption, prompt, parent=None, initialvalue=0, minvalue=None, maxvalue=None
):
while 1:
rc = GetSimpleInput(prompt, str(initialvalue), caption)
if rc is None:
return 0 # Correct "cancel" semantics?
err = None
try:
rc = int(rc)
except ValueError:
err = "Please enter an integer"
if not err and minvalue is not None and rc < minvalue:
err = "Please enter an integer greater then or equal to %s" % (
minvalue,
)
if not err and maxvalue is not None and rc > maxvalue:
err = "Please enter an integer less then or equal to %s" % (maxvalue,)
if err:
win32ui.MessageBox(err, caption, win32con.MB_OK)
continue
return rc
def askyesno(self, caption, prompt, parent=None):
return win32ui.MessageBox(prompt, caption, win32con.MB_YESNO) == win32con.IDYES
######################################################################
# The IDLE "Virtual Text Widget" methods that are exposed to the IDLE extensions.
#
# Is character at text_index in a Python string? Return 0 for
# "guaranteed no", true for anything else.
def is_char_in_string(self, text_index):
# A helper for the code analyser - we need internal knowledge of
# the colorizer to get this information
# This assumes the colorizer has got to this point!
text_index = self.text._getoffset(text_index)
c = self.text.edit._GetColorizer()
if c and c.GetStringStyle(text_index) is None:
return 0
return 1
# If a selection is defined in the text widget, return
# (start, end) as Tkinter text indices, otherwise return
# (None, None)
def get_selection_indices(self):
try:
first = self.text.index("sel.first")
last = self.text.index("sel.last")
return first, last
except TextError:
return None, None
def set_tabwidth(self, width):
self.edit.SCISetTabWidth(width)
def get_tabwidth(self):
return self.edit.GetTabWidth()
# A class providing the generic "Call Tips" interface
class CallTips:
def __init__(self, edit):
self.edit = edit
def showtip(self, tip_text):
self.edit.SCICallTipShow(tip_text)
def hidetip(self):
self.edit.SCICallTipCancel()
########################################
#
# Helpers for the TkText emulation.
def TkOffsetToIndex(offset, edit):
lineoff = 0
# May be 1 > actual end if we pretended there was a trailing '\n'
offset = min(offset, edit.GetTextLength())
line = edit.LineFromChar(offset)
lineIndex = edit.LineIndex(line)
return "%d.%d" % (line + 1, offset - lineIndex)
def _NextTok(str, pos):
# Returns (token, endPos)
end = len(str)
if pos >= end:
return None, 0
while pos < end and str[pos] in string.whitespace:
pos = pos + 1
# Special case for +-
if str[pos] in "+-":
return str[pos], pos + 1
# Digits also a special case.
endPos = pos
while endPos < end and str[endPos] in string.digits + ".":
endPos = endPos + 1
if pos != endPos:
return str[pos:endPos], endPos
endPos = pos
while endPos < end and str[endPos] not in string.whitespace + string.digits + "+-":
endPos = endPos + 1
if pos != endPos:
return str[pos:endPos], endPos
return None, 0
def TkIndexToOffset(bm, edit, marks):
base, nextTokPos = _NextTok(bm, 0)
if base is None:
raise ValueError("Empty bookmark ID!")
if base.find(".") > 0:
try:
line, col = base.split(".", 2)
if col == "first" or col == "last":
# Tag name
if line != "sel":
raise ValueError("Tags arent here!")
sel = edit.GetSel()
if sel[0] == sel[1]:
raise EmptyRange
if col == "first":
pos = sel[0]
else:
pos = sel[1]
else:
# Lines are 1 based for tkinter
line = int(line) - 1
if line > edit.GetLineCount():
pos = edit.GetTextLength() + 1
else:
pos = edit.LineIndex(line)
if pos == -1:
pos = edit.GetTextLength()
pos = pos + int(col)
except (ValueError, IndexError):
raise ValueError("Unexpected literal in '%s'" % base)
elif base == "insert":
pos = edit.GetSel()[0]
elif base == "end":
pos = edit.GetTextLength()
# Pretend there is a trailing '\n' if necessary
if pos and edit.SCIGetCharAt(pos - 1) != "\n":
pos = pos + 1
else:
try:
pos = marks[base]
except KeyError:
raise ValueError("Unsupported base offset or undefined mark '%s'" % base)
while 1:
word, nextTokPos = _NextTok(bm, nextTokPos)
if word is None:
break
if word in ["+", "-"]:
num, nextTokPos = _NextTok(bm, nextTokPos)
if num is None:
raise ValueError("+/- operator needs 2 args")
what, nextTokPos = _NextTok(bm, nextTokPos)
if what is None:
raise ValueError("+/- operator needs 2 args")
if what[0] != "c":
raise ValueError("+/- only supports chars")
if word == "+":
pos = pos + int(num)
else:
pos = pos - int(num)
elif word == "wordstart":
while pos > 0 and edit.SCIGetCharAt(pos - 1) in wordchars:
pos = pos - 1
elif word == "wordend":
end = edit.GetTextLength()
while pos < end and edit.SCIGetCharAt(pos) in wordchars:
pos = pos + 1
elif word == "linestart":
while pos > 0 and edit.SCIGetCharAt(pos - 1) not in "\n\r":
pos = pos - 1
elif word == "lineend":
end = edit.GetTextLength()
while pos < end and edit.SCIGetCharAt(pos) not in "\n\r":
pos = pos + 1
else:
raise ValueError("Unsupported relative offset '%s'" % word)
return max(pos, 0) # Tkinter is tollerant of -ve indexes - we aren't
# A class that resembles an IDLE (ie, a Tk) text widget.
# Construct with an edit object (eg, an editor view)
class TkText:
def __init__(self, edit):
self.calltips = None
self.edit = edit
self.marks = {}
## def __getattr__(self, attr):
## if attr=="tk": return self # So text.tk.call works.
## if attr=="master": return None # ditto!
## raise AttributeError, attr
## def __getitem__(self, item):
## if item=="tabs":
## size = self.edit.GetTabWidth()
## if size==8: return "" # Tk default
## return size # correct semantics?
## elif item=="font": # Used for measurements we dont need to do!
## return "Dont know the font"
## raise IndexError, "Invalid index '%s'" % item
def make_calltip_window(self):
if self.calltips is None:
self.calltips = CallTips(self.edit)
return self.calltips
def _getoffset(self, index):
return TkIndexToOffset(index, self.edit, self.marks)
def _getindex(self, off):
return TkOffsetToIndex(off, self.edit)
def _fix_indexes(self, start, end):
# first some magic to handle skipping over utf8 extended chars.
while start > 0 and ord(self.edit.SCIGetCharAt(start)) & 0xC0 == 0x80:
start -= 1
while (
end < self.edit.GetTextLength()
and ord(self.edit.SCIGetCharAt(end)) & 0xC0 == 0x80
):
end += 1
# now handling fixing \r\n->\n disparities...
if (
start > 0
and self.edit.SCIGetCharAt(start) == "\n"
and self.edit.SCIGetCharAt(start - 1) == "\r"
):
start = start - 1
if (
end < self.edit.GetTextLength()
and self.edit.SCIGetCharAt(end - 1) == "\r"
and self.edit.SCIGetCharAt(end) == "\n"
):
end = end + 1
return start, end
## def get_tab_width(self):
## return self.edit.GetTabWidth()
## def call(self, *rest):
## # Crap to support Tk measurement hacks for tab widths
## if rest[0] != "font" or rest[1] != "measure":
## raise ValueError, "Unsupport call type"
## return len(rest[5])
## def configure(self, **kw):
## for name, val in kw.items():
## if name=="tabs":
## self.edit.SCISetTabWidth(int(val))
## else:
## raise ValueError, "Unsupported configuration item %s" % kw
def bind(self, binding, handler):
self.edit.bindings.bind(binding, handler)
def get(self, start, end=None):
try:
start = self._getoffset(start)
if end is None:
end = start + 1
else:
end = self._getoffset(end)
except EmptyRange:
return ""
# Simple semantic checks to conform to the Tk text interface
if end <= start:
return ""
max = self.edit.GetTextLength()
checkEnd = 0
if end > max:
end = max
checkEnd = 1
start, end = self._fix_indexes(start, end)
ret = self.edit.GetTextRange(start, end)
# pretend a trailing '\n' exists if necessary.
if checkEnd and (not ret or ret[-1] != "\n"):
ret = ret + "\n"
return ret.replace("\r", "")
def index(self, spec):
try:
return self._getindex(self._getoffset(spec))
except EmptyRange:
return ""
def insert(self, pos, text):
try:
pos = self._getoffset(pos)
except EmptyRange:
raise TextError("Empty range")
self.edit.SetSel((pos, pos))
# IDLE only deals with "\n" - we will be nicer
bits = text.split("\n")
self.edit.SCIAddText(bits[0])
for bit in bits[1:]:
self.edit.SCINewline()
self.edit.SCIAddText(bit)
def delete(self, start, end=None):
try:
start = self._getoffset(start)
if end is not None:
end = self._getoffset(end)
except EmptyRange:
raise TextError("Empty range")
# If end is specified and == start, then we must delete nothing.
if start == end:
return
# If end is not specified, delete one char
if end is None:
end = start + 1
else:
# Tk says not to delete in this case, but our control would.
if end < start:
return
if start == self.edit.GetTextLength():
return # Nothing to delete.
old = self.edit.GetSel()[0] # Lose a selection
# Hack for partial '\r\n' and UTF-8 char removal
start, end = self._fix_indexes(start, end)
self.edit.SetSel((start, end))
self.edit.Clear()
if old >= start and old < end:
old = start
elif old >= end:
old = old - (end - start)
self.edit.SetSel(old)
def bell(self):
win32api.MessageBeep()
def see(self, pos):
# Most commands we use in Scintilla actually force the selection
# to be seen, making this unnecessary.
pass
def mark_set(self, name, pos):
try:
pos = self._getoffset(pos)
except EmptyRange:
raise TextError("Empty range '%s'" % pos)
if name == "insert":
self.edit.SetSel(pos)
else:
self.marks[name] = pos
def tag_add(self, name, start, end):
if name != "sel":
raise ValueError("Only sel tag is supported")
try:
start = self._getoffset(start)
end = self._getoffset(end)
except EmptyRange:
raise TextError("Empty range")
self.edit.SetSel(start, end)
def tag_remove(self, name, start, end):
if name != "sel" or start != "1.0" or end != "end":
raise ValueError("Cant remove this tag")
# Turn the sel into a cursor
self.edit.SetSel(self.edit.GetSel()[0])
def compare(self, i1, op, i2):
try:
i1 = self._getoffset(i1)
except EmptyRange:
i1 = ""
try:
i2 = self._getoffset(i2)
except EmptyRange:
i2 = ""
return eval("%d%s%d" % (i1, op, i2))
def undo_block_start(self):
self.edit.SCIBeginUndoAction()
def undo_block_stop(self):
self.edit.SCIEndUndoAction()
######################################################################
#
# Test related code.
#
######################################################################
def TestCheck(index, edit, expected=None):
rc = TkIndexToOffset(index, edit, {})
if rc != expected:
print("ERROR: Index", index, ", expected", expected, "but got", rc)
def TestGet(fr, to, t, expected):
got = t.get(fr, to)
if got != expected:
print(
"ERROR: get(%s, %s) expected %s, but got %s"
% (repr(fr), repr(to), repr(expected), repr(got))
)
def test():
import pywin.framework.editor
d = pywin.framework.editor.editorTemplate.OpenDocumentFile(None)
e = d.GetFirstView()
t = TkText(e)
e.SCIAddText("hi there how\nare you today\r\nI hope you are well")
e.SetSel((4, 4))
skip = """
TestCheck("insert", e, 4)
TestCheck("insert wordstart", e, 3)
TestCheck("insert wordend", e, 8)
TestCheck("insert linestart", e, 0)
TestCheck("insert lineend", e, 12)
TestCheck("insert + 4 chars", e, 8)
TestCheck("insert +4c", e, 8)
TestCheck("insert - 2 chars", e, 2)
TestCheck("insert -2c", e, 2)
TestCheck("insert-2c", e, 2)
TestCheck("insert-2 c", e, 2)
TestCheck("insert- 2c", e, 2)
TestCheck("1.1", e, 1)
TestCheck("1.0", e, 0)
TestCheck("2.0", e, 13)
try:
TestCheck("sel.first", e, 0)
print "*** sel.first worked with an empty selection"
except TextError:
pass
e.SetSel((4,5))
TestCheck("sel.first- 2c", e, 2)
TestCheck("sel.last- 2c", e, 3)
"""
# Check EOL semantics
e.SetSel((4, 4))
TestGet("insert lineend", "insert lineend +1c", t, "\n")
e.SetSel((20, 20))
TestGet("insert lineend", "insert lineend +1c", t, "\n")
e.SetSel((35, 35))
TestGet("insert lineend", "insert lineend +1c", t, "\n")
class IDLEWrapper:
def __init__(self, control):
self.text = control
def IDLETest(extension):
import sys, os
modname = "pywin.idle." + extension
__import__(modname)
mod = sys.modules[modname]
mod.TclError = TextError
klass = getattr(mod, extension)
# Create a new Scintilla Window.
import pywin.framework.editor
d = pywin.framework.editor.editorTemplate.OpenDocumentFile(None)
v = d.GetFirstView()
fname = os.path.splitext(__file__)[0] + ".py"
v.SCIAddText(open(fname).read())
d.SetModifiedFlag(0)
r = klass(IDLEWrapper(TkText(v)))
return r
if __name__ == "__main__":
test()

View File

@ -0,0 +1 @@
# package init.

View File

@ -0,0 +1,179 @@
from . import IDLEenvironment
import string
import win32ui
import win32api
import win32con
from . import keycodes
import sys
import traceback
HANDLER_ARGS_GUESS = 0
HANDLER_ARGS_NATIVE = 1
HANDLER_ARGS_IDLE = 2
HANDLER_ARGS_EXTENSION = 3
next_id = 5000
event_to_commands = {} # dict of integer IDs to event names.
command_to_events = {} # dict of event names to int IDs
def assign_command_id(event, id=0):
global next_id
if id == 0:
id = event_to_commands.get(event, 0)
if id == 0:
id = next_id
next_id = next_id + 1
# Only map the ones we allocated - specified ones are assumed to have a handler
command_to_events[id] = event
event_to_commands[event] = id
return id
class SendCommandHandler:
def __init__(self, cmd):
self.cmd = cmd
def __call__(self, *args):
win32ui.GetMainFrame().SendMessage(win32con.WM_COMMAND, self.cmd)
class Binding:
def __init__(self, handler, handler_args_type):
self.handler = handler
self.handler_args_type = handler_args_type
class BindingsManager:
def __init__(self, parent_view):
self.parent_view = parent_view
self.bindings = {} # dict of Binding instances.
self.keymap = {}
def prepare_configure(self):
self.keymap = {}
def complete_configure(self):
for id in command_to_events.keys():
self.parent_view.HookCommand(self._OnCommand, id)
def close(self):
self.parent_view = self.bindings = self.keymap = None
def report_error(self, problem):
try:
win32ui.SetStatusText(problem, 1)
except win32ui.error:
# No status bar!
print(problem)
def update_keymap(self, keymap):
self.keymap.update(keymap)
def bind(self, event, handler, handler_args_type=HANDLER_ARGS_GUESS, cid=0):
if handler is None:
handler = SendCommandHandler(cid)
self.bindings[event] = self._new_binding(handler, handler_args_type)
self.bind_command(event, cid)
def bind_command(self, event, id=0):
"Binds an event to a Windows control/command ID"
id = assign_command_id(event, id)
return id
def get_command_id(self, event):
id = event_to_commands.get(event)
if id is None:
# See if we even have an event of that name!?
if event not in self.bindings:
return None
id = self.bind_command(event)
return id
def _OnCommand(self, id, code):
event = command_to_events.get(id)
if event is None:
self.report_error("No event associated with event ID %d" % id)
return 1
return self.fire(event)
def _new_binding(self, event, handler_args_type):
return Binding(event, handler_args_type)
def _get_IDLE_handler(self, ext, handler):
try:
instance = self.parent_view.idle.IDLEExtension(ext)
name = handler.replace("-", "_") + "_event"
return getattr(instance, name)
except (ImportError, AttributeError):
msg = "Can not find event '%s' in IDLE extension '%s'" % (handler, ext)
self.report_error(msg)
return None
def fire(self, event, event_param=None):
# Fire the specified event. Result is native Pythonwin result
# (ie, 1==pass one, 0 or None==handled)
# First look up the event directly - if there, we are set.
binding = self.bindings.get(event)
if binding is None:
# If possible, find it!
# A native method name
handler = getattr(self.parent_view, event + "Event", None)
if handler is None:
# Can't decide if I should report an error??
self.report_error("The event name '%s' can not be found." % event)
# Either way, just let the default handlers grab it.
return 1
binding = self._new_binding(handler, HANDLER_ARGS_NATIVE)
# Cache it.
self.bindings[event] = binding
handler_args_type = binding.handler_args_type
# Now actually fire it.
if handler_args_type == HANDLER_ARGS_GUESS:
# Can't be native, as natives are never added with "guess".
# Must be extension or IDLE.
if event[0] == "<":
handler_args_type = HANDLER_ARGS_IDLE
else:
handler_args_type = HANDLER_ARGS_EXTENSION
try:
if handler_args_type == HANDLER_ARGS_EXTENSION:
args = self.parent_view.idle, event_param
else:
args = (event_param,)
rc = binding.handler(*args)
if handler_args_type == HANDLER_ARGS_IDLE:
# Convert to our return code.
if rc in [None, "break"]:
rc = 0
else:
rc = 1
except:
message = "Firing event '%s' failed." % event
print(message)
traceback.print_exc()
self.report_error(message)
rc = 1 # Let any default handlers have a go!
return rc
def fire_key_event(self, msg):
key = msg[2]
keyState = 0
if win32api.GetKeyState(win32con.VK_CONTROL) & 0x8000:
keyState = (
keyState | win32con.RIGHT_CTRL_PRESSED | win32con.LEFT_CTRL_PRESSED
)
if win32api.GetKeyState(win32con.VK_SHIFT) & 0x8000:
keyState = keyState | win32con.SHIFT_PRESSED
if win32api.GetKeyState(win32con.VK_MENU) & 0x8000:
keyState = keyState | win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED
keyinfo = key, keyState
# Special hacks for the dead-char key on non-US keyboards.
# (XXX - which do not work :-(
event = self.keymap.get(keyinfo)
if event is None:
return 1
return self.fire(event, None)

View File

@ -0,0 +1,361 @@
# config.py - deals with loading configuration information.
# Loads config data from a .cfg file. Also caches the compiled
# data back into a .cfc file.
# If you are wondering how to avoid needing .cfg files (eg,
# if you are freezing Pythonwin etc) I suggest you create a
# .py file, and put the config info in a docstring. Then
# pass a CStringIO file (rather than a filename) to the
# config manager.
import sys
import string
from . import keycodes
import marshal
import stat
import os
import types
import traceback
import pywin
import glob
import importlib.util
import win32api
debugging = 0
if debugging:
import win32traceutil # Some trace statements fire before the interactive window is open.
def trace(*args):
sys.stderr.write(" ".join(map(str, args)) + "\n")
else:
trace = lambda *args: None
compiled_config_version = 3
def split_line(line, lineno):
comment_pos = line.find("#")
if comment_pos >= 0:
line = line[:comment_pos]
sep_pos = line.rfind("=")
if sep_pos == -1:
if line.strip():
print("Warning: Line %d: %s is an invalid entry" % (lineno, repr(line)))
return None, None
return "", ""
return line[:sep_pos].strip(), line[sep_pos + 1 :].strip()
def get_section_header(line):
# Returns the section if the line is a section header, else None
if line[0] == "[":
end = line.find("]")
if end == -1:
end = len(line)
rc = line[1:end].lower()
try:
i = rc.index(":")
return rc[:i], rc[i + 1 :]
except ValueError:
return rc, ""
return None, None
def find_config_file(f):
return os.path.join(pywin.__path__[0], f + ".cfg")
def find_config_files():
return [
os.path.split(x)[1]
for x in [
os.path.splitext(x)[0]
for x in glob.glob(os.path.join(pywin.__path__[0], "*.cfg"))
]
]
class ConfigManager:
def __init__(self, f):
self.filename = "unknown"
self.last_error = None
self.key_to_events = {}
if hasattr(f, "readline"):
fp = f
self.filename = "<config string>"
compiled_name = None
else:
try:
f = find_config_file(f)
src_stat = os.stat(f)
except os.error:
self.report_error("Config file '%s' not found" % f)
return
self.filename = f
self.basename = os.path.basename(f)
trace("Loading configuration", self.basename)
compiled_name = os.path.splitext(f)[0] + ".cfc"
try:
cf = open(compiled_name, "rb")
try:
ver = marshal.load(cf)
ok = compiled_config_version == ver
if ok:
kblayoutname = marshal.load(cf)
magic = marshal.load(cf)
size = marshal.load(cf)
mtime = marshal.load(cf)
if (
magic == importlib.util.MAGIC_NUMBER
and win32api.GetKeyboardLayoutName() == kblayoutname
and src_stat[stat.ST_MTIME] == mtime
and src_stat[stat.ST_SIZE] == size
):
self.cache = marshal.load(cf)
trace("Configuration loaded cached", compiled_name)
return # We are ready to roll!
finally:
cf.close()
except (os.error, IOError, EOFError):
pass
fp = open(f)
self.cache = {}
lineno = 1
line = fp.readline()
while line:
# Skip to the next section (maybe already there!)
section, subsection = get_section_header(line)
while line and section is None:
line = fp.readline()
if not line:
break
lineno = lineno + 1
section, subsection = get_section_header(line)
if not line:
break
if section == "keys":
line, lineno = self._load_keys(subsection, fp, lineno)
elif section == "extensions":
line, lineno = self._load_extensions(subsection, fp, lineno)
elif section == "idle extensions":
line, lineno = self._load_idle_extensions(subsection, fp, lineno)
elif section == "general":
line, lineno = self._load_general(subsection, fp, lineno)
else:
self.report_error(
"Unrecognised section header '%s:%s'" % (section, subsection)
)
line = fp.readline()
lineno = lineno + 1
# Check critical data.
if not self.cache.get("keys"):
self.report_error("No keyboard definitions were loaded")
if not self.last_error and compiled_name:
try:
cf = open(compiled_name, "wb")
marshal.dump(compiled_config_version, cf)
marshal.dump(win32api.GetKeyboardLayoutName(), cf)
marshal.dump(importlib.util.MAGIC_NUMBER, cf)
marshal.dump(src_stat[stat.ST_SIZE], cf)
marshal.dump(src_stat[stat.ST_MTIME], cf)
marshal.dump(self.cache, cf)
cf.close()
except (IOError, EOFError):
pass # Ignore errors - may be read only.
def configure(self, editor, subsections=None):
# Execute the extension code, and find any events.
# First, we "recursively" connect any we are based on.
if subsections is None:
subsections = []
subsections = [""] + subsections
general = self.get_data("general")
if general:
parents = general.get("based on", [])
for parent in parents:
trace("Configuration based on", parent, "- loading.")
parent = self.__class__(parent)
parent.configure(editor, subsections)
if parent.last_error:
self.report_error(parent.last_error)
bindings = editor.bindings
codeob = self.get_data("extension code")
if codeob is not None:
ns = {}
try:
exec(codeob, ns)
except:
traceback.print_exc()
self.report_error("Executing extension code failed")
ns = None
if ns:
num = 0
for name, func in list(ns.items()):
if type(func) == types.FunctionType and name[:1] != "_":
bindings.bind(name, func)
num = num + 1
trace("Configuration Extension code loaded", num, "events")
# Load the idle extensions
for subsection in subsections:
for ext in self.get_data("idle extensions", {}).get(subsection, []):
try:
editor.idle.IDLEExtension(ext)
trace("Loaded IDLE extension", ext)
except:
self.report_error("Can not load the IDLE extension '%s'" % ext)
# Now bind up the key-map (remembering a reverse map
subsection_keymap = self.get_data("keys")
num_bound = 0
for subsection in subsections:
keymap = subsection_keymap.get(subsection, {})
bindings.update_keymap(keymap)
num_bound = num_bound + len(keymap)
trace("Configuration bound", num_bound, "keys")
def get_key_binding(self, event, subsections=None):
if subsections is None:
subsections = []
subsections = [""] + subsections
subsection_keymap = self.get_data("keys")
for subsection in subsections:
map = self.key_to_events.get(subsection)
if map is None: # Build it
map = {}
keymap = subsection_keymap.get(subsection, {})
for key_info, map_event in list(keymap.items()):
map[map_event] = key_info
self.key_to_events[subsection] = map
info = map.get(event)
if info is not None:
return keycodes.make_key_name(info[0], info[1])
return None
def report_error(self, msg):
self.last_error = msg
print("Error in %s: %s" % (self.filename, msg))
def report_warning(self, msg):
print("Warning in %s: %s" % (self.filename, msg))
def _readline(self, fp, lineno, bStripComments=1):
line = fp.readline()
lineno = lineno + 1
if line:
bBreak = (
get_section_header(line)[0] is not None
) # A new section is starting
if bStripComments and not bBreak:
pos = line.find("#")
if pos >= 0:
line = line[:pos] + "\n"
else:
bBreak = 1
return line, lineno, bBreak
def get_data(self, name, default=None):
return self.cache.get(name, default)
def _save_data(self, name, data):
self.cache[name] = data
return data
def _load_general(self, sub_section, fp, lineno):
map = {}
while 1:
line, lineno, bBreak = self._readline(fp, lineno)
if bBreak:
break
key, val = split_line(line, lineno)
if not key:
continue
key = key.lower()
l = map.get(key, [])
l.append(val)
map[key] = l
self._save_data("general", map)
return line, lineno
def _load_keys(self, sub_section, fp, lineno):
# Builds a nested dictionary of
# (scancode, flags) = event_name
main_map = self.get_data("keys", {})
map = main_map.get(sub_section, {})
while 1:
line, lineno, bBreak = self._readline(fp, lineno)
if bBreak:
break
key, event = split_line(line, lineno)
if not event:
continue
sc, flag = keycodes.parse_key_name(key)
if sc is None:
self.report_warning("Line %d: Invalid key name '%s'" % (lineno, key))
else:
map[sc, flag] = event
main_map[sub_section] = map
self._save_data("keys", main_map)
return line, lineno
def _load_extensions(self, sub_section, fp, lineno):
start_lineno = lineno
lines = []
while 1:
line, lineno, bBreak = self._readline(fp, lineno, 0)
if bBreak:
break
lines.append(line)
try:
c = compile(
"\n" * start_lineno + "".join(lines), # produces correct tracebacks
self.filename,
"exec",
)
self._save_data("extension code", c)
except SyntaxError as details:
errlineno = details.lineno + start_lineno
# Should handle syntax errors better here, and offset the lineno.
self.report_error(
"Compiling extension code failed:\r\nFile: %s\r\nLine %d\r\n%s"
% (details.filename, errlineno, details.msg)
)
return line, lineno
def _load_idle_extensions(self, sub_section, fp, lineno):
extension_map = self.get_data("idle extensions")
if extension_map is None:
extension_map = {}
extensions = []
while 1:
line, lineno, bBreak = self._readline(fp, lineno)
if bBreak:
break
line = line.strip()
if line:
extensions.append(line)
extension_map[sub_section] = extensions
self._save_data("idle extensions", extension_map)
return line, lineno
def test():
import time
start = time.clock()
f = "default"
cm = ConfigManager(f)
map = cm.get_data("keys")
took = time.clock() - start
print("Loaded %s items in %.4f secs" % (len(map), took))
if __name__ == "__main__":
test()

Some files were not shown because too many files have changed in this diff Show More