Virgil Dupras avatar Virgil Dupras committed 8e07075

Added the "xibless" subproject.

Comments (0)

Files changed (15)

 .DS_Store
 *.xcodeproj/xcuserdata
 *.xcodeproj/project.xcworkspace/xcuserdata
+.waf*
+.lock-waf*
 run.py
 build
 dist
 cocoa/build
 cocoa/autogen
 cocoa/guiskel.xcodeproj/*.mode1v3
-cocoa/guiskel.xcodeproj/*.pbxuser
+cocoa/guiskel.xcodeproj/*.pbxuser
+cocoa/guiskel.xcodeproj/project.xcworkspace
+xibless/autogen
 cocoa:
  - XCode 4
  - ObjP from http://bitbucket.org/hsoft/objp
+ - pluginbuilder http://bitbucket.org/hsoft/pluginbuilder
+xibless:
+ - Apple's Command line tools (or XCode) at http://developer.apple.com/downloads
+ - ObjP from http://bitbucket.org/hsoft/objp
+ - pluginbuilder http://bitbucket.org/hsoft/pluginbuilder
+ - xibless from http://bitbucket.org/hsoft/xibless
 tk:
  - None
 gtk:
 import os.path as op
 import shutil
 
-PROJNAMES = ['qt', 'cocoa', 'tk', 'gtk']
+PROJNAMES = ['qt', 'cocoa', 'tk', 'gtk', 'xibless']
 
-def build_cocoa():
+def build_cocoa_base(cocoa_path):
     import objp.o2p
     import objp.p2o
-    sys.path.insert(0, 'cocoa')
+    sys.path.insert(0, cocoa_path)
     import pyplugin
-    objp.o2p.generate_objc_code(pyplugin.PyMainWindow, 'cocoa/autogen')
-    objp.o2p.generate_objc_code(pyplugin.PyTextHolder, 'cocoa/autogen')
+    objp.o2p.generate_objc_code(pyplugin.PyMainWindow, op.join(cocoa_path, 'autogen'))
+    objp.o2p.generate_objc_code(pyplugin.PyTextHolder, op.join(cocoa_path, 'autogen'))
     textholder_spec = objp.o2p.spec_from_python_class(pyplugin.TextHolderView)
     objp.p2o.generate_python_proxy_code_from_clsspec([textholder_spec], 'build/TextHolderView.m')
     from setuptools import setup, Extension
     if not op.exists(pydest):
         os.mkdir(pydest)
     shutil.copy('TextHolderView.so', pydest)
-    shutil.copy('cocoa/pyplugin.py', pydest)
+    shutil.copy(op.join(cocoa_path, 'pyplugin.py'), pydest)
     # For some strange reason, a "site.py" file is required at pydest.
     with open(op.join(pydest, 'site.py'), 'w'):
         pass
-    from pluginbuilder import copy_embeddable_python_dylib, get_python_header_folder, collect_dependencies
+    from pluginbuilder import copy_embeddable_python_dylib, collect_dependencies
     copy_embeddable_python_dylib('build')
+    collect_dependencies(op.join(cocoa_path, 'pyplugin.py'), pydest)
+
+def build_cocoa():
+    build_cocoa_base('cocoa')
+    from pluginbuilder import get_python_header_folder
     if not op.exists('build/PythonHeaders'):
         os.symlink(get_python_header_folder(), 'build/PythonHeaders')
-    collect_dependencies('cocoa/pyplugin.py', pydest)
+    os.chdir('cocoa')
+    os.system('xcodebuild')
+    os.chdir('..')
+
+def build_xibless():
+    build_cocoa_base('xibless')
+    os.chdir('xibless')
+    os.system('%s waf configure' % sys.executable)
+    os.system('%s waf' % sys.executable)
+    os.chdir('..')
 
 def main(projname):
     if projname == 'cocoa':
         build_cocoa()
+    elif projname == 'xibless':
+        build_xibless()
     runtemplate_path = op.join(projname, 'runtemplate.py')
     shutil.copy(runtemplate_path, 'run.py')
 

xibless/Info.plist

+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleExecutable</key>
+	<string>guiskel</string>
+	<key>CFBundleIconFile</key>
+	<string></string>
+	<key>CFBundleIdentifier</key>
+	<string>com.hardcoded-software.guiskel</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>guiskel</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+</dict>
+</plist>

xibless/MainWindow.h

+#import <Cocoa/Cocoa.h>
+#import "TextHolder.h"
+#import "PyMainWindow.h"
+
+@interface MainWindow : NSWindowController
+{
+    PyMainWindow *py;
+    NSTextField *nameTextField;
+    NSTextField *msgTextField;
+    TextHolder *nameTextHolder;
+    TextHolder *msgTextHolder;
+}
+
+@property (readwrite, retain) NSTextField *nameTextField;
+@property (readwrite, retain) NSTextField *msgTextField;
+
+- (id)init;
+- (void)randomName;
+- (void)sayHello;
+@end

xibless/MainWindow.m

+#import "MainWindow.h"
+#import "MainWindow_UI.h"
+#import "PyMainWindow.h"
+
+@implementation MainWindow
+
+@synthesize nameTextField;
+@synthesize msgTextField;
+
+- (id)init
+{
+    self = [super initWithWindow:nil];
+    NSWindow *window = createMainWindow_UI(self);
+    [self setWindow:window];
+    py = [[PyMainWindow alloc] init];
+    nameTextHolder = [[TextHolder alloc] initWithTextField:nameTextField];
+    msgTextHolder = [[TextHolder alloc] initWithTextField:msgTextField];
+    [py setNameHolder:[[nameTextHolder py] pyRef] andMsgHolder:[[msgTextHolder py] pyRef]];
+    return self;
+}
+
+- (void)dealloc
+{
+    [nameTextHolder release];
+    [nameTextHolder release];
+    [py release];
+    [super dealloc];
+}
+- (void)randomName
+{
+    [py selectRandomName];
+}
+
+- (void)sayHello
+{
+    [py sayHello];
+}
+@end

xibless/TextHolder.h

+#import <Cocoa/Cocoa.h>
+#import "PyTextHolder.h"
+
+@interface TextHolder : NSObject
+{
+    NSTextField *textField;
+    PyTextHolder *py;
+}
+- (id)initWithTextField:(NSTextField *)aTextField;
+- (PyTextHolder *)py;
+@end

xibless/TextHolder.m

+#import "TextHolder.h"
+#import "ObjP.h"
+
+@implementation TextHolder
+- (id)initWithTextField:(NSTextField *)aTextField
+{
+    self = [super init];
+    PyObject *pView = ObjP_classInstanceWithRef(@"TextHolderView", @"TextHolderView", self);
+    py = [[PyTextHolder alloc] initWithView:pView];
+    textField = [aTextField retain];
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChange:)
+        name:NSControlTextDidChangeNotification object:nil];
+    return self;
+}
+
+- (void)dealloc
+{
+    [py release];
+    [textField release];
+    [super dealloc];
+}
+
+- (PyTextHolder *)py
+{
+    return py;
+}
+
+- (void)textDidChange:(NSNotification *)aNotification
+{
+    [py setText:[textField stringValue]];
+}
+
+// model --> view
+- (void)updateText
+{
+    [textField setStringValue:[py text]];
+}
+@end
+#import <Cocoa/Cocoa.h>
+#import <Python.h>
+#import <wchar.h>
+#import "MainMenu_UI.h"
+#import "MainWindow.h"
+
+int main(int argc, char *argv[])
+{
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSString *respath = [[NSBundle mainBundle] resourcePath];
+    NSString *pypath = [respath stringByAppendingPathComponent:@"py"];
+    NSString *mainpy = [pypath stringByAppendingPathComponent:@"pyplugin.py"];
+    wchar_t wPythonPath[PATH_MAX+1];
+    mbstowcs(wPythonPath, [pypath fileSystemRepresentation], PATH_MAX+1);
+    Py_SetPath(wPythonPath);
+    Py_Initialize();
+    FILE* fp = fopen([mainpy UTF8String], "r");
+    PyRun_SimpleFile(fp, "pyplugin.py");
+    fclose(fp);
+    [NSApplication sharedApplication];
+    NSMenu *mainMenu = createMainMenu_UI(nil);
+    [NSApp setMainMenu:mainMenu];
+    MainWindow *mainWindow = [[MainWindow alloc] init];
+    [mainWindow showWindow:nil];
+    [NSApp run];
+    Py_Finalize();
+    [pool release];
+    return 0;
+}

xibless/pyplugin.py

+from objp.util import pyref
+
+from core.mainwindow import MainWindow
+from core.textholder import TextHolder
+
+class PyMainWindow:
+    def __init__(self):
+        self.model = MainWindow()
+    
+    def setNameHolder_andMsgHolder_(self, nameHolder: pyref, msgHolder: pyref):
+        self.model.set_children(nameHolder.model, msgHolder.model)
+    
+    def selectRandomName(self):
+        self.model.select_random_name()
+    
+    def sayHello(self):
+        self.model.say_hello()
+    
+
+class TextHolderView:
+    def updateText(self): pass
+
+class PyTextHolder:
+    def __init__(self, view: pyref):
+        self.view = view
+        self.model = TextHolder(view=self)
+    
+    def text(self) -> str:
+        return self.model.text
+    
+    def setText_(self, newtext: str):
+        self.model.text = newtext
+    
+    def update_text(self):
+        self.view.updateText()
+    

xibless/runtemplate.py

+import sys
+import os
+
+def main():
+    return os.system('open xibless/build/guiskel.app')
+
+if __name__ == '__main__':
+    sys.exit(main())

xibless/ui/MainMenu.py

+result = MainMenu("MyApp")
+
+# Although the main menu contains a bunch of menu items, it can be tweaked afterwards.
+result.editMenu.addItem("foo", index=3)
+result.windowMenu.removeItem(index=1)

xibless/ui/MainWindow.py

+ownerclass = 'MainWindow'
+ownerimport = 'MainWindow.h'
+
+# Init
+result = Window(400, 400, 318, 115, "guiskel")
+nameLabel = Label(result, text="Name:")
+nameLabel.width = 45
+nameField = TextField(result, text="")
+helloLabel = Label(result, text="")
+helloButton = Button(result, title="Say Hello", action=Action(owner, 'sayHello'))
+randomButton = Button(result, title="Random Name", action=Action(owner, 'randomName'))
+randomButton.width = 119
+
+# Owner Assignments
+owner.nameTextField = nameField
+owner.msgTextField = helloLabel
+
+# Layout
+nameLabel.packToCorner(Pack.UpperLeft)
+nameField.packRelativeTo(nameLabel, Pack.Right, Pack.Middle)
+nameField.fill(Pack.Right)
+helloLabel.packRelativeTo(nameLabel, Pack.Below, Pack.Left)
+helloLabel.fill(Pack.Right)
+helloButton.packRelativeTo(helloLabel, Pack.Below, Pack.Right)
+randomButton.packRelativeTo(helloButton, Pack.Left, Pack.Above)
+nameField.setAnchor(Pack.UpperLeft, growX=True)
+helloLabel.setAnchor(Pack.UpperLeft, growX=True)
+helloButton.setAnchor(Pack.UpperRight)
+randomButton.setAnchor(Pack.UpperRight)

Binary file added.

+#! /usr/bin/env python
+
+import plistlib
+import xibless
+import os
+import os.path as op
+
+# This WAF script is a bit complicated, but we have to keep in mind that it replaces XCode
+# entirely... See line-by-line comments for details.
+
+top = '.'
+out = 'build'
+
+def options(opt):
+    opt.load('compiler_c python')
+
+def configure(conf):
+    # We use clang to compile our app
+    conf.env.CC = 'clang'
+    # WAF has a "pyembed" feature allowing us to automatically find Python and compile by linking
+    # to it. The problem is that because we made a copy of the Python library to mangle with its
+    # "install name", we don't actually want to link to our installed python, but to our mangled
+    # Python. The line below tells the "pyembed" WAF feature to look in ../build for Python.
+    conf.env.LIBPATH_PYEMBED = op.abspath('../build')
+    # I did a lot of fiddling-around, but I didn't find how to tell WAF the Python library name
+    # to look for without making the whole compilation process fail, so I just create a symlink
+    # with the name WAF is looking for. 
+    if not op.exists('../build/libpython3.2.dylib'):
+        os.symlink('../build/Python', '../build/libpython3.2.dylib')
+    # The rest is standard WAF code that you can find the the python and macapp demos.
+    conf.load('compiler_c python')
+    conf.check_python_version((3,2,0))
+    conf.check_python_headers()
+    conf.env.FRAMEWORK_COCOA = 'Cocoa'
+    conf.env.ARCH_COCOA = ['i386', 'x86_64']
+
+def build(ctx):
+    # xibless
+    ctx.srcnode.make_node('autogen').mkdir()
+    xibless.generate('ui/MainMenu.py', 'autogen/MainMenu_UI.h')
+    xibless.generate('ui/MainWindow.py', 'autogen/MainWindow_UI.h')
+    
+    # Here. we generate our app structure. We start by creating a base skeleton, and then we
+    # copy our "py" dependencies in "Resources" and our Python library in "Frameworks"
+    infoplist = ctx.srcnode.find_node("Info.plist")
+    info = plistlib.readPlist(infoplist.abspath())
+    appname = info['CFBundleName']
+    executablename = info['CFBundleExecutable']
+    appfilename = appname + ".app"
+    appnode = ctx.bldnode.make_node(appfilename)
+    create_app_skeleton(ctx, appnode, infoplist)
+    pyres = ctx.srcnode.find_dir('../build/py')
+    appres = appnode.find_node('Contents/Resources')
+    # I couldn't find out how to copy a folder with WAF... If you use the typical SRC and TGT, you
+    # get an error.
+    ctx(rule="cp -R \"%s\" \"%s\"" % (pyres.abspath(), appres.abspath()))
+    appfw = appnode.find_node('Contents/Frameworks')
+    pylib = ctx.srcnode.find_node('../build/Python')
+    ctx(rule="cp ${SRC} ${TGT}", source=pylib, target=appfw)
+    
+    # Compile
+    ctx.program(
+        # "pyembed" takes care of the include and linking stuff to compile an app that embed Python.
+        features      = 'c cprogram pyembed',
+        target        = appnode.find_node("Contents").find_node("MacOS").make_node(executablename),
+        source        = ctx.path.ant_glob('*.m') + ctx.path.ant_glob('autogen/*.m'),
+        includes      = '. autogen',
+        use           = 'COCOA',
+        # Because our python lib's install name is "@rpath/Python", we need to set the executable's
+        # rpath. Fortunately, WAF supports it and we just need to supply the "rpath" argument.
+        rpath         = '@executable_path/../Frameworks'
+    )
+
+def create_app_skeleton(ctx, dest, info_plist_path):
+    contents = dest.make_node("Contents")
+    contents.make_node("MacOS").mkdir()
+    contents.make_node("Resources").mkdir()
+    contents.make_node("Frameworks").mkdir()
+    ctx(rule="cp ${SRC} ${TGT}", source=info_plist_path, target=contents.make_node("Info.plist"))
+    open(contents.make_node("PkgInfo").abspath(), 'wt').write("APPLxxxx")
+
+from waflib import TaskGen
+@TaskGen.extension('.m')
+def m_hook(self, node):
+    """Alias .m files to be compiled the same as .c files, gcc will do the right thing."""
+    return self.create_compiled_task('c', node)
+
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.