Showing posts with label Instant Cocoa. Show all posts
Showing posts with label Instant Cocoa. Show all posts

Sunday, March 13, 2011

Cocoa: where to start?

Recommended resources for beginning Cocoa with Objective-C
NSOrderedDescending

  • do the temperature converter in the Cocoa Application Tutorial
  • short articles at Cocoa Dev (here much more here; C review)
  • Aaron Hillegass's book
  • Cocoa Fundamentals Guide (here)

    Specific to PyObjC:

  • Will Larson's tutorials (here here here here here)
  • Apple's page including a version of the temperature converter
  • Read the official introduction carefully (my biggest problem)

    A page of links to old material with simple demos of specific Cocoa features that mostly still work here.

    Code a simple game like TicTacToe, Fifteen, or Color Sudoku.

    After that, I've got tons of projects here and here. Get started with bindings (here), then move on to Vlad the Impaler (here).

    Learn specific topics by reading the Apple docs (slowly and repeatedly, it can take a while to get it, by building a simple demo project that does only that one thing. Like NSPredicate which we've done in six posts (here here here here here & one to come).

    And if you want the PyObjC templates for Xcode see here.
  • NSPredicate: problem solved (almost)

    It only took me about four hours, but with big help from this post, I solved the problem of filtering a string containing an array of (possibly invalid) filepaths. And, in the meantime, Dave DeLong himself had responded to my question on StackOverflow from yesterday, which if I'd just waited a bit more, would have saved me some time. Thanks, Dave.

    As I mentioned (here), we're just wrapping a filtering routine up in a category on NSString. Here is the first part of the code file including the category:


    #import <Foundation/Foundation.h>

    @interface NSString (ValidatingFilepathArray)
    - (NSNumber *) validate;
    @end

    @implementation NSString (ValidatingFilepathArray)

    - (NSNumber *) validate {
    NSArray *A = [self componentsSeparatedByString:@":"];
    NSString *s, *p;
    NSFileManager *fm = [NSFileManager defaultManager];
    for (p in A) {
    s = [p stringByExpandingTildeInPath];
    if ([fm fileExistsAtPath:s]) {
    NSLog(@"passed: %@", p);
    }
    else {
    NSLog(@"failed: %@", p);
    return [NSNumber numberWithBool:NO];
    }
    }
    return [NSNumber numberWithBool:YES];
    }
    @end


    In the code at the bottom of the post, we construct a "function expression" from the new NSString method. Two things really confused me. First, in the method


    expressionForFunction:selectorName:arguments:


    the "function" is the object itself. And the second thing was figuring out the format string for the predicate that wraps up our expression:


    @"FUNCTION(SELF, 'validate') isEqual:YES"


    This is the output:


    > gcc -o test test.m -framework Foundation
    > ./test
    2011-03-13 16:53:42.974 test[3494:903] passed: ~
    2011-03-13 16:53:42.976 test[3494:903] passed: ~/Desktop
    2011-03-13 16:53:42.977 test[3494:903] expression for ~:~/Desktop: YES
    2011-03-13 16:53:42.978 test[3494:903] failed: xyz
    2011-03-13 16:53:42.978 test[3494:903] expression for xyz: NO
    -------------------------------------------------
    2011-03-13 16:53:42.979 test[3494:903] passed: ~
    2011-03-13 16:53:42.980 test[3494:903] passed: ~/Desktop
    2011-03-13 16:53:42.980 test[3494:903] failed: xyz
    2011-03-13 16:53:42.981 test[3494:903] filtered: ~:~/Desktop
    2011-03-13 16:53:42.981 test[3494:903] passed: ~/Desktop
    2011-03-13 16:53:42.982 test[3494:903] evaluate: YES


    First we construct the expression and evaluate it. In the second part, we construct the predicate and use it to filter an array or just "evaluate" a string. Still to do: test it in an app with bindings and an NSTextField or NSTableView.

    Code:


    int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    NSString *validPathArray = @"~:~/Desktop";
    NSExpression *f = [NSExpression expressionForConstantValue:validPathArray];
    NSExpression *e = [NSExpression expressionForFunction:f
    selectorName:@"validate" arguments:nil];
    NSNumber *result = [e expressionValueWithObject:nil context:nil];
    NSArray *responses = [NSArray arrayWithObjects:@"NO",@"YES",nil];
    NSLog(@"expression for %@: %@", validPathArray,
    [responses objectAtIndex:[result intValue]]);

    NSString *invalidPathArray = @"xyz";
    f = [NSExpression expressionForConstantValue:invalidPathArray];
    e = [NSExpression expressionForFunction:f
    selectorName:@"validate" arguments:nil];
    result = [e expressionValueWithObject:nil context:nil];
    NSLog(@"expression for %@: %@", invalidPathArray,
    [responses objectAtIndex:[result intValue]]);

    printf("-------------------------------------------------\n");

    NSPredicate *p;
    p = [NSPredicate predicateWithFormat:@"FUNCTION(SELF, 'validate') isEqual:YES"];
    NSArray *A = [NSArray arrayWithObjects: validPathArray, invalidPathArray, nil];
    NSArray *fA = [A filteredArrayUsingPredicate:p];
    for (id obj in fA) {
    NSLog(@"filtered: %@", obj);
    }
    BOOL yesorno = [p evaluateWithObject:@"~/Desktop"];
    NSLog(@"evaluate: %@", [responses objectAtIndex:(int) yesorno]);
    [pool drain];
    return 0;
    }

    NSExpression: simple examples

    The example from last time (here) introduced the NSExpression class. That one was rather complex in code, though what it does is just a simple filtering of values.

    Here is another example which looks less forbidding: it combines two expressions in a single predicate. These can then be used to construct a comparison predicate that grabs the object for key='value' from an array, and checks it against the number 10, like this:

    As before the code block is inside a standard main:


    #import <Foundation/Foundation.h>
    
    int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    code here..    
    [pool drain];
    return 0;
    }


    and compiled like this:


    > gcc -o test test.m -framework Foundation


    code:


    NSExpression *lhs = [NSExpression expressionForKeyPath:@"value"];
    NSNumber *ten = [NSNumber numberWithInt:10];
    NSExpression *rhs = [NSExpression expressionForConstantValue:ten];
    
    NSPredicate *p = [NSComparisonPredicate 
    predicateWithLeftExpression:lhs
    rightExpression:rhs
    modifier:NSDirectPredicateModifier 
    type:NSGreaterThanOrEqualToPredicateOperatorType 
    options:0];
    
    NSArray *A = [NSArray arrayWithObjects:
    [NSMutableDictionary dictionaryWithObject:
    [NSNumber numberWithInt:3] forKey:@"value"],
    [NSMutableDictionary dictionaryWithObject:
    [NSNumber numberWithInt:15] forKey:@"value"], nil];
    NSArray *fA = [A filteredArrayUsingPredicate:p];
    for (id obj in fA) {
    NSLog(@"%@", [obj description]);
    }



    > ./test
    2011-03-13 10:58:53.885 test[218:903] {
    value = 15;
    }


    Here is a second example, taken from the docs, of how to construct an expression that uses a built-in function (they call this a function expression):


    NSArray *A = [NSArray arrayWithObjects:
    [NSNumber numberWithInt:3],
    [NSNumber numberWithInt:6],
    nil];
    NSExpression *eA = [NSExpression 
    expressionForConstantValue:A];
    NSArray *args = [NSArray arrayWithObject:eA];
    
    NSExpression *e = [NSExpression 
    expressionForFunction:@"average:" arguments:args];
    id result = [e expressionValueWithObject:nil context:nil];
    NSLog(@"%@ %@", [result description], [result class]);
    float f = [result floatValue];
    printf("result = %3.2f\n", f);



    2011-03-13 11:01:00.337 test[236:903] 4.5 NSCFNumber
    result = 4.50


    There are lots of built-in functions available (here).

    The third example is based on the first part (the simple part) of this post from Dave DeLong. It defines a category on NSNumber


    @interface NSNumber (FactorialExpression)
    - (NSNumber *) factorial;
    @end
    
    @implementation NSNumber (FactorialExpression)
    - (NSNumber *) factorial {
    double baseValue = [self doubleValue];
    double result = tgamma(baseValue+1);
    return [NSNumber numberWithDouble:result];
    }
    @end


    and the code block is:


    NSNumber *n = [NSNumber numberWithDouble:4.2];
    NSLog(@"%@ %@", n, [n factorial]);
    NSLog(@"%p %d", n, [n respondsToSelector:@selector(factorial)]);
    
    NSExpression *f = [NSExpression expressionForConstantValue:n];
    NSExpression *e = [NSExpression expressionForFunction:f
    selectorName:@"factorial" 
    arguments:nil];
    NSLog(@"operand %@ %@", [e operand], [[e operand] class]);
    NSLog(@"operand %@", [e function]);
    
    id result = [e expressionValueWithObject:nil context:nil];
    NSLog(@"%@ %@", [result description], [result class]);



    > ./test
    2011-03-13 11:02:46.798 test[251:903] 4.2 32.57809605033135
    2011-03-13 11:02:46.800 test[251:903] 0x100108d20 1
    2011-03-13 11:02:46.801 test[251:903] operand 4.2 NSConstantValueExpression
    2011-03-13 11:02:46.801 test[251:903] operand factorial
    2011-03-13 11:02:46.802 test[251:903] 32.57809605033135 NSCFNumber


    And now, the way to solve my initial question seems clear:

    Define a category on NSString that does what I want.
    Wrap the call up in an NSExpression and then an NSPredicate. Next time, if I succeed.

    Saturday, March 12, 2011

    NSPredicate: compound Cocoa example in code

    I'm resisting the temptation to call this simple---it's not! All we're doing is filtering an array of dicts for those whose 'value' is neither too large nor too small. I have no idea why it has to be this complicated. The example is from the NSPredicate docs (as reformatted and with renamed vars by me):


    #import <Foundation/Foundation.h>

    int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    NSArray *A = [NSArray arrayWithObjects:
    [NSDictionary dictionaryWithObject:[
    NSNumber numberWithInt:5] forKey:@"value"],
    [NSDictionary dictionaryWithObject:[
    NSNumber numberWithInt:50] forKey:@"value"],
    [NSDictionary dictionaryWithObject:[
    NSNumber numberWithInt:500] forKey:@"value"],
    nil];

    NSExpression *lhs = [NSExpression
    expressionForKeyPath:@"value"];
    NSExpression *gtrhs = [NSExpression
    expressionForConstantValue:[NSNumber numberWithInt:10]];
    NSExpression *ltrhs = [NSExpression
    expressionForConstantValue:[NSNumber numberWithInt:100]];

    NSPredicate *gtpred;
    gtpred = [NSComparisonPredicate
    predicateWithLeftExpression:lhs
    rightExpression:gtrhs
    modifier:NSDirectPredicateModifier
    type:NSGreaterThanOrEqualToPredicateOperatorType
    options:0];

    NSPredicate *ltpred;
    ltpred = [NSComparisonPredicate
    predicateWithLeftExpression:lhs
    rightExpression:ltrhs
    modifier:NSDirectPredicateModifier
    type:NSLessThanOrEqualToPredicateOperatorType
    options:0];

    NSPredicate *pred;
    pred = [NSCompoundPredicate andPredicateWithSubpredicates:
    [NSArray arrayWithObjects:gtpred, ltpred, nil] ];

    NSArray *fA = [A filteredArrayUsingPredicate:pred];
    for (id obj in fA) {
    NSLog(@"%@", [obj description]);
    }
    return 0;
    }

    > gcc -o test pred.m -framework Foundation
    > ./test
    2011-03-12 11:48:31.640 test[70403:903] {
    value = 50;
    }

    NSPredicate: simple Cocoa examples

    I'm exploring predicates and expressions in Cocoa using Objective-C. Actually, I'd like to use PyObjC, but this is tricky enough that I think it's better to start with Objective-C.

    Ultimately I'd like to write complex predicates, for example, to filter using either a custom function or a block. The reason I need this is to validate edits to an NSTableView using bindings. According to the docs:


    A predicate is a logical operator that returns a Boolean value (true or false). There are two types of predicate; comparison predicates, and compound predicates:

    ● A comparison predicate compares two expressions using an operator. The expressions are referred to as the left hand side and the right hand side of the predicate (with the operator in the middle). A comparison predicate returns the result of invoking the operator with the results of evaluating the expressions.

    ● A compound predicate compares the results of evaluating two or more other other predicates, or negates another predicate.


    All the code is like this:


    #import <Foundation/Foundation.h>
    
    int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    .. specific code here ..
    [pool drain];
    return 0;
    }


    compiled and run like this:


    > gcc -o test pred.m -framework Foundation
    > ./test

    Example 1. Predicate with a format string using "like"


    (the c is a case-insensitive compare, the * is a wild card):

    NSArray *A = [NSArray arrayWithObjects:
    @"John",@"Paul",@"George",@"Ringo",nil];
    NSPredicate *p = [NSPredicate predicateWithFormat:
    @"SELF like [c] 'g*' "];    
    NSLog(@"%@", [[A filteredArrayUsingPredicate:p] objectAtIndex:0]);
    
    > ./test
    2011-03-12 09:38:55.379 test[67230:903] George

    Example 2. Format string with a mathematical operator



    NSArray *A = [NSArray arrayWithObjects:
    [NSNumber numberWithInt:3],
    [NSNumber numberWithInt:5],nil];
    NSPredicate *p = [NSPredicate predicateWithFormat:
    @"SELF > 4 "];    
    NSLog(@"%@", [[A filteredArrayUsingPredicate:p] objectAtIndex:0]);
    
    > ./test
    2011-03-12 09:38:22.313 test[67217:903] 5


    Note: also works with the usual kind of string formatting :@"SELF > %d ", 4

    Example 3: Format string using a key path



    NSArray *A = [NSArray arrayWithObjects:
    [NSMutableDictionary dictionaryWithObject:@"x" forKey:@"name"],
    [NSMutableDictionary dictionaryWithObject:@"y" forKey:@"name"], nil];
    
    NSPredicate *p = [NSPredicate predicateWithFormat:@"name like %@", @"x"];    
    NSLog(@"%@", [[A filteredArrayUsingPredicate:p] objectAtIndex:0]);
    
    > ./test
    2011-03-12 10:01:43.963 test[67885:903] {
    name = x;
    }

    Example 4: Format string substituting a dynamic key path



    NSArray *A = [NSArray arrayWithObjects:
    [NSMutableDictionary dictionaryWithObject:@"x" forKey:@"name"],
    [NSMutableDictionary dictionaryWithObject:@"y" forKey:@"name"], nil];
    //NSLog(@"%@", [[A objectAtIndex:0] description]);
    
    NSString *name = @"name";
    NSString *value = @"x";
    NSPredicate *p = [NSPredicate predicateWithFormat:@"%K like %@", name, value];    
    NSLog(@"%@", [[A filteredArrayUsingPredicate:p] objectAtIndex:0]);
    
    > ./test
    2011-03-12 09:29:59.220 test[67013:903] {
    name = x;
    }

    Example 5. Compound predicate



    NSArray *theKeys = [NSArray arrayWithObjects:@"name",@"value",nil];
    NSArray *o1 = [NSArray arrayWithObjects:@"x",[NSNumber numberWithInt:5],nil];
    NSArray *o2 = [NSArray arrayWithObjects:@"y",[NSNumber numberWithInt:7],nil];
    NSArray *o3 = [NSArray arrayWithObjects:@"z",[NSNumber numberWithInt:9],nil];
    
    NSArray *A = [NSArray arrayWithObjects:
    [NSMutableDictionary dictionaryWithObjects:o1 forKeys:theKeys],
    [NSMutableDictionary dictionaryWithObjects:o2 forKeys:theKeys], nil];
    NSLog(@"%d", [A count]);
    
    NSPredicate *p = [NSPredicate predicateWithFormat:
    @"(value < %@) OR (name == %@)", [NSNumber numberWithInt:6], @"y"]; 
    NSArray *fA = [A filteredArrayUsingPredicate:p];
    NSLog(@"%d", [fA count]);
    for (id obj in fA) {
    NSLog(@"%@", [obj description]);
    }
    
    > ./test
    2011-03-12 10:34:11.921 test[68778:903] 2
    2011-03-12 10:34:11.924 test[68778:903] 2
    2011-03-12 10:34:11.925 test[68778:903] {
    name = x;
    value = 5;
    }
    2011-03-12 10:34:11.925 test[68778:903] {
    name = y;
    value = 7;
    }

    Tuesday, February 15, 2011

    A permutations puzzle



    wikimedia

    I wrote a very simple game in PyObjC---it's a "fifteen" puzzle. The goal of the game is to get all 15 tiles in order with the empty space in the lower right hand corner. This can be viewed as the permutation:


    1, 2, 3 .. 13, 14, 15, 0


    I had a vague recollection that some such puzzles are unsolvable, and it's fairly easy to see by just playing that one cannot achieve this order using the standard layout:


    1, 2, 3 .. 13, 15, 14, 0


    And conversely, if you were presented with the modified layout, the puzzle would not be solvable. Since it seems really important not to give the user an unsolvable puzzle, I looked into it some more and found that permutations (which is what this problem is about) have parity.

    Suppose we take the following permutation of 1..5:


    3  4  5  2  1


    How many swaps (u,v = v,u) does it take to put the numbers into ascending order? This sequence of three works:


    (13)(35)(24)


    So we can think of this permutation of the sequence as having an odd parity. One question I don't know the answer to is how you would prove that there does not exist some longer sequence of swaps that also reaches the "correct" order, but has even parity. Probably, if I studied the article it is explained there.

    A second measure I saw for parity is to take each member of the sequence in turn and compare it with all the members at a higher index:


    for i in range(len(L)-1):
    for j in range(i,len(L)):
    if L[i] > L[j]:
    count += 1


    We count +1 if the ith element is out of (ascending) order with respect to the jth element, and define the total count of such mismatched pairs as a different measure of parity.

    This is the output from such an analysis on all permutations of the sequence 0, 1, 2, 3, showing the permutation, then the results of the swap test, followed by the order test:


    0 1 2 3     0  0
    0 1 3 2 1 1
    0 2 1 3 1 1
    0 2 3 1 2 2
    0 3 1 2 2 2
    0 3 2 1 1 3
    1 0 2 3 1 1
    1 0 3 2 2 2
    1 2 0 3 2 2
    1 2 3 0 3 3
    1 3 0 2 3 3
    1 3 2 0 2 4
    2 0 1 3 2 2
    2 0 3 1 3 3
    2 1 0 3 1 3
    2 1 3 0 2 4
    2 3 0 1 2 4
    2 3 1 0 3 5
    3 0 1 2 3 3
    3 0 2 1 2 4
    3 1 0 2 2 4
    3 1 2 0 1 5
    3 2 0 1 3 5
    3 2 1 0 2 6


    The number of swaps required to achieve sorted order is never more than 3, while the reverse order has a maximum of 6 comparisons giving an incorrect order. I notice that these two measures seem to be either both odd or both even, and confirmed that result by testing all permutations of range(10) and random samples from range(25).

    Looking ahead, the idea will be that the moves in the puzzle, and particularly those which really matter (vertical ones), don't change the parity, but so far I am not getting the expected answer with that.


    from itertools import permutations as perms
    import random

    def format(pL):
    m = len(str(max(pL)))
    pL = [str(n).rjust(m) for n in pL]
    return ' '.join(pL)

    def swaps_to_sort(L,refL=None,verbose=False):
    if not refL:
    refL = sorted(L)
    count = 0
    for i in range(len(L)-1):
    u = L[i]
    v = refL[i]
    if verbose:
    print 'check:', u,
    if u == v:
    if verbose: print
    continue
    count += 1
    j = L.index(v)
    if verbose:
    print 'switch ', ':', u, v
    L[i],L[j] = L[j],L[i]
    if verbose:
    list_print(L)
    return count

    def out_of_order(L):
    count = 0
    for i,c1 in enumerate(L):
    for c2 in L[i+1:]:
    if c1 > c2: count += 1
    return count

    def test1(n):
    L = perms(range(n))
    for p in L:
    pL = list(p)
    s = swaps_to_sort(pL[:])
    o = out_of_order(pL)
    print '%s %s %s' % (format(p), s, o)
    assert s % 2 == o % 2

    def test2():
    pL = range(25)
    for i in range(100):
    random.shuffle(pL)
    print format(pL)
    s = swaps_to_sort(pL[:])
    o = out_of_order(pL)
    assert s % 2 == o % 2

    test1(4)
    #test2()

    Saturday, January 29, 2011

    Distributed Objects on OS X (2)

    I struggled for a while to get Distributed Objects working between different machines (first post here for same-machine example). According to the docs and this, you should just use


    NSSocketPortNameServer *name_server;
    name_server = [NSSocketPortNameServer sharedInstance];
    NSLog(@"%@", [name_server description]);
    BOOL result;
    result = [conn registerName:@"my_server"
    withNameServer:name_server];


    But it didn't work for me. So I asked a question on SO (here), and asked the Google. The latter approach finally worked. I got a working example with code from here. The discussion in the docs for the NSConnection class helped too (see initWithReceivePort:sendPort:. If you set it up with nil for the sendPort, it uses the same port for both sending and receiving, which is what you want.)

    [UPDATE: On reflection, it does seem a bit weird, because a Mach port is supposed to be a one-way channel, yet these are clearly two way. I guess they are probably complex objects built from multiple NSMacPorts. ]

    Server:


    // gcc talk3.m -o test -framework Foundation
    // http://web.archiveorange.com/archive/v/SEb6a6s543xreqfXtOCQ
    #import <Foundation/Foundation.h>

    @interface VendedObject:NSObject {}
    -(NSString *) speak;
    @end

    @implementation VendedObject

    -(NSString *) speak {
    return @"woof";
    }
    @end

    @interface NSConnection (NetworkServiceAdditions)

    + (id) networkServiceConnectionWithName:(NSString *) inName
    rootObject:(id) inRootObject;

    @end

    @implementation NSConnection (NetworkServiceAdditions)

    + (id) networkServiceConnectionWithName:(NSString *) inName
    rootObject:(id) inRootObject;
    {
    NSSocketPort *port = [[[NSSocketPort alloc] init] autorelease];
    NSConnection *connection = [NSConnection connectionWithReceivePort: port
    sendPort: nil];

    [[NSSocketPortNameServer sharedInstance] registerPort:port
    name:inName];
    [connection setRootObject: inRootObject];
    return connection;
    }

    @end

    int main(){
    NSAutoreleasePool *pool;
    pool = [[NSAutoreleasePool alloc] init];
    VendedObject *obj;
    obj = [[[VendedObject alloc ] init] autorelease];
    NSLog(@"%@", [obj description]);
    NSConnection *conn;
    conn = [NSConnection networkServiceConnectionWithName:@"my_server"
    rootObject:obj ];
    NSSocketPortNameServer *name_server;
    name_server = [NSSocketPortNameServer sharedInstance];
    NSLog(@"%@", [name_server description]);
    NSLog(@"%@", [name_server portForName:@"my_server"]);
    [[NSRunLoop mainRunLoop] run];
    [pool drain];
    return 0;
    }


    Note: if you're not just killing the Server (as I did) you should do invalidate on the connection (and I guess the ports too?).

    Client listen.py is on my laptop:


    from Foundation import *

    proxy_obj = NSConnection.rootProxyForConnectionWithRegisteredName_host_usingNameServer_(
    "my_server", "osxserver.local", NSSocketPortNameServer.sharedInstance())

    if not proxy_obj:
    print 'Did not get an object from the server.'
    else:
    print proxy_obj.description()
    print type(proxy_obj)
    print proxy_obj.speak()


    Output:


    > python listen.py 
    <VendedObject: 0x10010ced0>
    <objective-c class NSDistantObject at 0x7fff70a64868>
    woof


    It works!

    NSTask again

    I have a bit more to explore about NSTask (previous post here). There is some discussion on SO about the difference between an NSTask and an NSThread (here).

    The script task.py uses the #! thing and is set to be executable (by the user only). The code looks for an int in argv and a second arg that tells whether to examine the environment variable dictionary.


    #! /usr/bin/python
    import sys, os

    try:
    n = int(sys.argv[1])
    except:
    n = 2
    print 'woof ' * n
    if len(sys.argv) == 3:
    D = os.environ
    k = 'MYKEY'
    if k in D: print k, D[k]
    else: print k, 'not present'


    The first attempt just launches the task without setting it up first.

    The second example does the setup. The path p2 (to Python directly) isn't used in these versions but I left it in. For a while, I was getting errors when I tried to setup the task and then launch it unless I fed it to Python first and gave the script path as an argument. Then, the errors just stopped. I can't recreate the problem. This kind of bug that "just disappeared" happened with the environment dictionary as well. No clue as to why.


    from Foundation import *

    p1 = NSHomeDirectory() + '/Desktop/task.py'
    p2 = '/usr/bin/python'

    def f1():
    t = NSTask.launchedTaskWithLaunchPath_arguments_(
    p1, [str(1)])

    def doit(t):
    t.launch()
    t.waitUntilExit()

    def f2():
    t = NSTask.alloc().init()
    t.setLaunchPath_(p1)
    doit(t)

    f1()
    f2()


    Of note here is that the arguments must be in a list or array, and that the int argment must be converted to a string. The error if not is weird (but at least we're pointed to the correct line):


    2011-01-29 03:18:00.109 Python[19298:60f] -[OC_PythonNumber fileSystemRepresentation]: unrecognized selector sent to instance 0x101a6d950
    Traceback (most recent call last):
    File "parent.py", line 48, in <module>
    f()
    File "parent.py", line 15, in f1
    p1, [1])
    ValueError: NSInvalidArgumentException - -[OC_PythonNumber fileSystemRepresentation]: unrecognized selector sent to instance 0x101a6d950


    Otherwise we get this:


    > python parent.py 
    woof
    woof woof


    Next, we instantiate the task and then set its arguments and environment, having modified the dict to have an extra key/value pair:


    D = NSProcessInfo.processInfo().environment()
    D['MYKEY'] = "MYVALUE"

    def f3():
    t = NSTask.alloc().init()
    t.setLaunchPath_(p2)
    n = str(3)
    t.setArguments_([p1,n,'withenviron'])
    t.setEnvironment_(D)
    print 'MYKEY', t.environment()['MYKEY']
    doit(t)

    f3()



    > python parent.py 
    MYKEY MYVALUE
    woof woof woof
    MYKEY MYVALUE


    The ultimate line of output is from the task and shows that we did get the modified environment.

    And in the last example, we use an NSFileHandle and redirect standard output. This could be useful in the case where you don't have full control over the task that is being executed. I found that NSFileHandle.fileHandleForWritingAtPath_ apparently requires the file to already exist.


    p3 = NSHomeDirectory() + '/Desktop/results.txt'

    def f4():
    t = NSTask.alloc().init()
    t.setLaunchPath_(p1)
    n = NSNumber.numberWithInt_(4).stringValue()
    t.setArguments_([n])
    # file must exist or this fails!
    f = NSFileHandle.fileHandleForWritingAtPath_(p3)
    t.setStandardOutput_(f)
    doit()
    data = f.readToEndOfFileInBackgroundAndNotify()
    print data

    f4()



    > python parent.py 
    None
    > cat results.txt
    woof woof woof woof


    The redirect is good, but I couldn't get the file read to work. I tried before and after launch, and I tried re-grabbing the file handle, and I also tried readDataToEndOfFile. Not sure what's up with that yet.

    [UPDATE: Perhaps the answer has something to do with NSPipe (here). ]

    Thursday, January 27, 2011

    A peek into the Objective-C "runtime"

    I'm playing with Cocoa today. I ran across a post (here), reminding me that all our fancy "messages" get changed by the Objective-C "runtime." The real call is objc_msgSend and you can do that yourself if you want:


    // gcc test.m -o test -framework Foundation
    #import <Foundation/Foundation.h>

    @interface MyClass:NSObject {}
    -(NSString *) speak:(int) n;
    @end

    @implementation MyClass
    -(NSString *) speak:(int) n {
    int i;
    NSMutableArray *mA;
    mA = [NSMutableArray arrayWithCapacity:n];
    for (i=0; i<n; i++) {
    [mA addObject:@"woof"];
    }
    return [mA componentsJoinedByString:@"-"];
    }
    @end

    int main(){
    NSAutoreleasePool *pool ;
    pool = [[NSAutoreleasePool alloc] init];
    MyClass *obj;
    obj = [[[MyClass alloc ] init] autorelease];
    NSLog(@"%@", [obj description]);
    NSLog(@"%@", [obj speak:2]);

    NSString *s;
    int count = 3;
    SEL sel = @selector(speak:);
    s = objc_msgSend(obj,sel,count);
    NSLog(@"%@", s);
    [pool drain];
    return 0;
    }


    Note: you have to use an int variable, you can't just pass an int directly to objc_msgSend.
    Output:


    > ./test
    2011-01-27 13:21:37.890 test[38774:903] <MyClass: 0x10010cd20>
    2011-01-27 13:21:37.892 test[38774:903] woof-woof
    2011-01-27 13:21:37.893 test[38774:903] woof-woof-woof

    Distributed Objects on OS X

    I got a simple example with Distributed Objects working. This is supposed to be the standard way to communicate between processes. I tried it first with both the Server and the Client in PyObjC. Eventually I rewrote the Server in Objective-C and then it worked.

    [UPDATE: Found a great working example (old, but from an Apple engineer here). ]

    The output is:


    > python listen.py 
    <VendedObject: 0x10010cce0>
    <objective-c class NSDistantObject at 0x7fff70a64868>
    woof


    I'm not sure what the problem was but the error I got was


    [OC_PythonString initWithBytes:length:encoding:]: unrecognized selector sent to instance 0x3635130


    Server:


    // gcc talk.m -o test -framework Foundation
    #import <Foundation/Foundation.h>

    @interface VendedObject:NSObject {}
    -(NSString *) speak;
    @end

    @implementation VendedObject

    -(NSString *) speak {
    return @"woof";
    }
    @end

    int main(){
    NSAutoreleasePool *pool;
    pool = [[NSAutoreleasePool alloc] init];
    VendedObject *obj;
    obj = [[[VendedObject alloc ] init] autorelease];
    NSLog(@"%@", [obj description]);

    NSConnection *conn;
    conn = [[[NSConnection alloc] init] autorelease];
    [conn setRootObject:obj];
    BOOL result;
    result = [conn registerName:@"my_server"];
    if (!result) {
    NSLog(@"Failed to register Name");
    }
    else {
    NSLog(@"%@", [conn description]);
    }
    [[NSRunLoop mainRunLoop] run];
    [pool drain];
    return 0;
    }


    Run with:


    > ./test
    2011-01-27 12:09:39.001 test[36995:903] <VendedObject: 0x10010cce0>
    2011-01-27 12:09:39.005 test[36995:903] (** NSConnection 0x100112050 receivePort <NSMachPort: 0x100112530> sendPort <NSMachPort: 0x100112530> refCount 1 **)


    You'll have to kill the process when you're done. If you have tried once already in Terminal, you will need to Quit Terminal and Re-launch. Otherwise registerName: will fail.

    Client:


    from Foundation import *

    proxy_obj = NSConnection.rootProxyForConnectionWithRegisteredName_host_(
    #"my_server", "osxserver.local")
    "my_server", None)
    if not proxy_obj:
    print 'Did not get an object from the server.'
    else:
    print proxy_obj.description()
    print type(proxy_obj)
    print proxy_obj.speak()


    I also tried to connect to another machine on my local network, but I couldn't get that to work either. The proxy_obj was nil/None.

    Wednesday, January 26, 2011

    NSNotifications

    I want to send information between two running applications and I mistakenly thought I could use an NSNotification to do this. [See the UPDATE].

    So this post is about the first step, getting an NSNotification to work within a running app. The following is from an Xcode Cocoa-Python app, and in AppDelegate.py we have:


    from Foundation import *
    from AppKit import *
    import objc

    class SpeakAppDelegate(NSObject):
    def applicationDidFinishLaunching_(self, sender):
    NSLog("Application really did finish launching.")
    nc = NSNotificationCenter.defaultCenter()
    nc.addObserver_selector_name_object_(
    self, "mycallback:", 'love_note', None)

    @objc.signature('v@0:@8')
    def mycallback_(self,note):
    print 'note'
    print note.description()

    @objc.IBAction
    def button_(self,sender):
    print sender, 'button'
    nc = NSNotificationCenter.defaultCenter()
    nc.postNotificationName_object_userInfo_(
    'love_note', None, {'path':'xyz'})


    There's a button in the window hooked up to the IBAction. I push the button and get:


    <NSButton: 0x61c7b0> button
    note
    NSConcreteNotification 0x6337a0 {name = love_note; userInfo = {
    path = xyz;
    }}


    But I couldn't get it to work between apps, for example with this one compiled in Terminal:


    // gcc tell.m -o test -framework Foundation
    #import <Foundation/Foundation.h>

    int main() {
    NSNotificationCenter *nc;
    nc = [NSNotificationCenter defaultCenter];
    [nc postNotificationName:@"love_note"
    object:nil
    userInfo:nil ];
    return 0;
    }


    I asked on Stack Overflow (here), and they say I should be using Distributed Objects (here). So I have to look into that.

    [ UPDATE: Just substitute NSDistributedNotificationCenter in the code above. It works! ]

    Wednesday, January 12, 2011

    Matt Gallagher, artist


    I hope you find this as entertaining as I did. A Cocoa App (with a window, even) but not using Xcode). The App itself is a curiosity---though not so much of one as Amit Singh's even shorter program (see the link for details)---but Matt Gallagher obviously knows his stuff, and I can see it serving as a harness for various simple tests.

    Maybe I should make a sidebar populated with blogs like his. Check it out.

    Friday, December 17, 2010

    NSTask

    I investigated the use of NSTask (docs). According to the reference:
    Using the NSTask class, your program can run another program as a subprocess and can monitor that program’s execution. An NSTask object creates a separate executable entity; it differs from NSThread in that it does not share memory space with the process that creates it.

    I think it is possible to get info from the running process (beyond just the fact that it's still running) through stdout, but I haven't tried that yet. Here we just launch a task in three different ways, and use Objective-C for a change of pace. The question is more about permissions, and paths and such.

    First, pass /usr/bin/python as the launchPath and an array with two arguments: script0.py (on my Desktop), and the string "to me". Notably, the script file is not executable or even readable by anyone but the user (mode is -rwx------). We set up the task and launch it in one line.

    script0.py

    import sys
    try:
    print 'Hello world ' + sys.argv[1] + '!'
    except IndexError:
    print 'Hello world, everybody!'

    task0.m

    #import <Foundation/Foundation.h>

    int main (int argc, const char* argv[]) {
    NSString *path = @"/usr/bin/python";
    NSString *home = NSHomeDirectory();
    NSString *script = @"/Desktop/script0.py";
    NSString *arg1 = [NSString stringWithFormat:@"%@%@", home, script];

    NSArray *args = [NSArray arrayWithObjects:arg1,@"to me",nil];
    NSTask *t = [NSTask launchedTaskWithLaunchPath:path arguments:args];
    NSLog(@"%@",[t description]);
    [t waitUntilExit];
    return 0;
    }


    $ gcc -o test task0.m -framework Foundation -fobjc-gc-only
    $ ./test
    2010-12-17 12:51:41.684 test[10281:903] <NSConcreteTask: 0x20000fa80>
    Hello world to me!

    Notice that the argument 'to me' came in as a single value. We didn't get this:

    $ python script0.py to me
    Hello world to!


    In the second method we do the shebang or hashbang thing, script1.py is the same as script0.py except we add this as the first line: #! /usr/bin/env python and we make it executable first..

    $ chmod +xu script1.py

    The code for task1.m

    #import <Foundation/Foundation.h>

    int main (int argc, const char* argv[]) {
    NSString *home = NSHomeDirectory();
    NSString *script = @"/Desktop/script1.py";
    NSString *path = [NSString stringWithFormat:@"%@%@", home, script];

    NSArray *args = [NSArray arrayWithObject:@"to me"];
    NSTask *t = [NSTask launchedTaskWithLaunchPath:path arguments:args];
    NSLog(@"%@",[t description]);
    [t waitUntilExit];
    return 0;
    }


    $ gcc -o test task1.m -framework Foundation -fobjc-gc-only
    $ ./test
    2010-12-17 12:59:29.594 test[10447:903] <NSConcreteTask: 0x20000cb60>
    Hello world to me!


    The third method is similar to the first, except we set up the task before launching. This could be useful to set the environment, for example, or change StandardError, etc. We grab the user's environment (post here) and use that. You could change the working directory with setCurrentDirectoryPath, but I didn't try it yet. We re-use script0.py

    #import <Foundation/Foundation.h>

    int main (int argc, const char* argv[]) {
    NSDictionary *eD = [[NSProcessInfo processInfo] environment];
    NSTask *t = [[NSTask alloc] init];
    [t setLaunchPath:@"/usr/bin/python"];
    NSArray *args = [NSArray arrayWithObjects:@"script0.py",@"to me",nil];
    [t setArguments:args];
    [t setEnvironment:eD];
    [t launch];
    NSLog(@"%@",[t description]);
    [t waitUntilExit];
    return 0;
    }


    $ gcc -o test task2.m -framework Foundation -fobjc-gc-only
    $ ./test2010-12-17 13:06:00.543 test[10592:903] <NSConcreteTask: 0x20000f1e0>
    Hello world to me!

    I see I posted about this before (here). It's so long ago I forgot! (Thanks, Google). In that case, we were in PyObjC and called a C program, so it's a little different. I haven't checked to see that the old code still runs with the current PyObjC. I'll try to do that.

    Tuesday, November 3, 2009

    Update for HeatMapper

    When I posted my recent projects, I put up a link to an application called HeatMapper2 (blog, download page). I should have looked it over more carefully first. There is a revised version of the project files up now at the same link.

    This was the first Cocoa project I wrote in a new phase starting in September. Among other howlers, the original version shows a confusion between class and instance variables, which doesn't matter much for a non-document app, but aesthetically it grates, just the same. There was also a lot of duct tape in the program, necessitated by resources going away for reasons I didn't understand. Using @property and @synthesize and the maxim DRY, that is fixed now, I think. Please let me know about any bugs you find. I know, it still isn't really MVC code. The view class is also a controller.

    The current version has a cheap version of customization. Known column names are colored according to the user's preference. These preferences are saved in the bundle in "textColors.txt." This file has the format:


    categoryName1 colorKey1
    categoryName2 colorKey2

    categoryName1
    nameA
    nameB

    categoryName2
    nameX
    nameY

    Sunday, November 1, 2009

    NSError

    In Cocoa, objects are almost always "instantiated" in the same way. We get back a pointer to the object instead of the thing itself:

    NSArray *A = [NSArray arrayWithObject:@"x"];

    NSError is a little different. For example here we did this:

    NSError *e;
    s = [NSString
    stringWithContentsOfFile:fn
    encoding:NSASCIIStringEncoding
    error:&e];


    In the NSString Class Reference it looks like this:

    + (id)
    stringWithContentsOfFile:(NSString *)path
    encoding:(NSStringEncoding)enc
    error:(NSError **)error


    So, what's with NSError **, and what's with &e ? And what does the NSError object actually contain when an error occurs?

    NSError inherits from NSObject so it can't be that complicated. Almost invariably you do not actually create an NSError object. Instead, you declare the variable, and then pass it into a method which fills it out for you if an error occurs. The NSError object is a member of a real Cocoa class, with methods and everything.

    In the first function above: NSError *e; looks normal.

    In error:&e the "address-of operator" & means that we are passing in the address of our pointer to the NSError object, rather than the pointer itself , as we would usually do. (On second thought, when would we ever do anything but send a message to an object?)

    So, this is a pointer-to-a-pointer kind of thing, but constructed using "&". I learned about this in C++, but it does actually exist in C.

    In the class reference code, the variable is an NSError **, that is, a pointer-to-a-pointer.

    I'm not sure why this it's done this way. It kind of reminds me of "Handles"---old time Apple stuff that I remember from the book "Inside Macintosh."

    As for what messages NSError object responds to, it may respond to:

    • domain (returns a string)
    • code (returns an integer that may be used as an enumerated type)
    • userInfo (returns a dictionary that is often empty)

    So, the sky's the limit for what can be there.


    // gcc -o test test.m -framework Foundation -fobjc-gc-only
    // ./test
    #import <Foundation/Foundation.h>

    int main (int argc, const char * argv[]) {
    NSString *fn = NSHomeDirectory();
    fn = [fn stringByAppendingString:
    @"/Desktop/x.txt"];
    NSError *e;
    NSString *s;
    s = [NSString stringWithContentsOfFile:fn
    encoding:NSASCIIStringEncoding
    error:&e];
    NSLog(@"domain = %@", [e domain]);
    NSLog(@"code = %u", [e code]);
    NSLog(@"userInfo = %@", [e userInfo]);
    }



    $ gcc -o test test.m -framework Foundation -fobjc-gc-only
    $ ./test
    domain = NSCocoaErrorDomain
    code = 260
    userInfo = {
    NSFilePath = "/Users/te/Desktop/x.txt";
    NSUnderlyingError = "Error
    Domain=NSPOSIXErrorDomain Code=2
    \"The operation couldn\U2019t be completed.
    No such file or directory\"";
    }

    Tuesday, October 27, 2009

    Heat Mapper rises from the ashes (2)



    Here is the latest incarnation of HeatMapper. When I installed Snow Leopard almost two months ago the previous version, written using PyObjC, didn't work any more. I took Bill Bumgarner's advice and have been practicing to speak Cocoa and Objective-C since then.

    The app makes heat maps, such as what you might find in R. It has an inspector window that allows customization of colors. The standard ones:




    Here are topo colors:



    For reasons that escape me, it was not written as a document-based application. But it does allow saving of UserPrefs. It also has some stuff that is probably only useful to me. It will color the row names according to a dictionary, but I haven't yet generalized this to allow the User to specify her/his own values. It can load csv data from a file.



    This is the app I think is most functional and I would really appreciate any feedback. As before, the source and the app (64-bit for Snow Leopard) is on my download page hosted at .mac. If you want a different build, or whatever, just let me know. You can get a hold of me here.

    [UPDATE: The app seems broken (1.4 years later). That says more about my lack of comprehensive testing than anything else, I guess. Sorry. There is a link in comments about how to use R for this purpose, and I've posted about it too. For my part, now that I have matplotlib, I use that now. When I get time I'll look into this. ]

    NSPredicate

    Here are two examples of elementary usage of NSPredicate. This is good stuff to know: how to do in Cocoa something that is second-nature in Python.

    In the first one, we search an array of strings for @"SELF like [c] 'g*'", that is the object is "like" (equals in a stringy way) the string that starts with g and has zero or more letters following, in a [c] case-insensitive comparison.


        NSArray *A = [NSArray arrayWithObjects:
    @"Tom",@"Sam",@"George",nil];
    NSPredicate *p = [NSPredicate
    predicateWithFormat:
    @"SELF like [c] 'g*' "];
    NSLog(@"%@",
    [A filteredArrayUsingPredicate:p]);


     (
    George
    )


    In the second example, we have an array of dictionaries and test them for any that have an object for the key "firstName" that is contained in an array (which is derived from same array we used above). Since we eliminated the last dictionary (range from 0 up to but not including 2), only the first two dictionaries pass the filter, as shown in the output:


        NSMutableArray *mA;
    mA = [NSMutableArray arrayWithCapacity:5];
    NSMutableDictionary* mD;
    for (id obj in A) {
    mD = [NSMutableDictionary
    dictionaryWithObject:obj forKey:@"firstName"];
    [mA addObject:mD];
    }

    NSIndexSet *is = [NSIndexSet
    indexSetWithIndexesInRange:
    NSMakeRange(0,2) ];
    NSArray *A2 = [A objectsAtIndexes:is];

    p = [NSPredicate
    predicateWithFormat: @"firstName in %@", A2];
    NSLog(@"%@",
    [mA filteredArrayUsingPredicate:p]);


    (
    {
    firstName = Tom;
    },
    {
    firstName = Sam;
    }
    )

    More sophistication with NSArray

    This stackoverflow post hightlights another NSArray method I haven't used:

    - (id)valueForKey:(NSString *)key

    This method
    "Returns an array containing the results of invoking valueForKey: using key on each of the receiver's objects."
    The example is to make an array containing all the values obtained for a given key. The objects could be custom objects, dictionaries or whatever. Another answer says to do it by using the function below on an NSArray of objects:

    - (id)valueForKeyPath:

    as in:

    [people valueForKeyPath:@"@distinctUnionOfObjects.state"]

    It wasn't clear to me how one would know that. It's not listed under the NSArray documentation. And what is this @distinctUnionOfObjects thingie? Ah...it is added under KVC.

    "Array operators allow you to specify operations to be performed on a collection's items using a key path of the form keyPathToArray.@operator.keyPathToProperty."

    Next time I want the object in an array with the maximum value for a particular key, I'll have to remember to use @max.

    C arrays of id objects!



    Stackoverflow seems like a very nice site for Cocoa questions. I found this today, which helped me understand that in the middle of a function doing Cocoa stuff, you can still make a standard array of objects of type id.

    id arrayObjects[arrayCount]

    I never thought about that. In fact, one of my weaknesses is a failure to integrate barely remembered C++ and C stuff with Objective-C. The example also uses an instance method of NSArray that I hadn't noticed:

    getObjects:range:

    which, according to the class reference,
    "copies the objects contained in the receiver that fall within the specified range to aBuffer… the size of the buffer must therefore be at least the length of the range multiplied by the size of an object reference."

    I think the example is probably overkill since we want all of the objects. Also, is it really better to construct two arrays and then use

    NSDictionary dictionaryWithObjects:forKeys:count:

    or simply add objects and keys one by one. More efficient? I don't know. I also saw a reference to Google Toolbox for Mac. That should be an awesome resource. Thanks guys.

    Monday, October 26, 2009

    Sliders come unstuck



    I have a design issue for HeatMapper. We want to pick intervals like say, these for integers

    0-1
    2-3
    4-5
    6-8
    9-12

    and so on, and assign a color to each interval. The current method is to use a table view, but I thought it would be interesting to design a kind of "equalizer" like you see in iTunes or a music app.

    So I set up two sliders in the nib and bound their values to instance variables in my app that have been set up with @property and @synthesize as usual. I also put in a stepper (also bound to the instance variable #1) to change the value of slider #1. (The behavior is the same if you move the slider by hand).

    The idea was to bind the minimum value of slider #2 to the current value of the instance variable that is bound to slider #1. Unfortunately, it doesn't work. In fact, what it does is so weird that I can't explain it.



    We start with slider #1 at 0 and slider #2 at 3.
    Now move slider #1 up in steps to 1, 2, 3, 4, 5. The reported value of the variable n2 never changes. And what the slider does is just bizarre:








    Here is the output:


    2009-10-26 14:59:20.362 Equalizer[3291:80f] n1 = 1
    2009-10-26 14:59:20.366 Equalizer[3291:80f] n2 = 3
    2009-10-26 14:59:28.898 Equalizer[3291:80f] n1 = 2
    2009-10-26 14:59:28.899 Equalizer[3291:80f] n2 = 3
    2009-10-26 14:59:37.971 Equalizer[3291:80f] n1 = 3
    2009-10-26 14:59:37.971 Equalizer[3291:80f] n2 = 3
    2009-10-26 14:59:43.691 Equalizer[3291:80f] n1 = 4
    2009-10-26 14:59:43.692 Equalizer[3291:80f] n2 = 3
    2009-10-26 14:59:49.371 Equalizer[3291:80f] n1 = 5
    2009-10-26 14:59:49.372 Equalizer[3291:80f] n2 = 3