INPUT /  OUTPUT /  LANGUAGE





Hosting Other Programs

An ioL console can contain other ioL consoles. Your program can set aside an area of its screen, and run a completely separate program inside it. That program doesn't need to know it's inside another one: it prints its output and reads its input exactly as usual.

This is how you can build a program made of several smaller ones: a settings screen where each tab is its own program, a dashboard of independent panels, or a launcher that runs other programs in a window.


The clientFrame tag

A clientFrame tag is an area of the screen, like a box, that another program's console can be connected to:

<left:clientFrame width=300 height=150 border=1>

When the frame is created, the console gives it a unique key, and puts it in the frame's primary field. To start a program inside the frame, your program does three things:

  1. reads the key, by asking for it with <putLn left>;
  2. starts the other program with the iol command, passing the key with -k;
  3. carries on with its own work. The other program now runs on its own, inside the frame.

In Python, the subprocess module starts other programs:

import subprocess

def start_in_frame(frame, command):
    print('<putLn ' + frame + '>')          # ask the console for the frame's key
    key = input()
    return subprocess.Popen(['iol', '-q', '-k', key, '--'] + command)

start_in_frame('left', ['python3', 'clock.py'])

-q stops iol from printing its welcome message. The -- marks the end of the options for iol, so that options meant for the other program (like Python's -c) aren't mistaken for iol's own. Always include it.

Each key can only be used once. When a program in a frame finishes, the frame is given a new key, so you can start another program in the same frame.

Keeping track of hosted programs

The program inside a frame runs independently. Its input and output go to its own console, not yours. But a frame does tell you a few things about it:

FieldTells you
titlethe hosted program's title (its console.title)
connectedtrue while a program is running in the frame
onConnectcode to carry out when a program connects to the frame
onDisconnectcode to carry out when it finishes
onTitlecode to carry out when its title changes

You can read these fields, but not change them: they belong to the hosted program.

Example: two programs side by side

First, a tiny program to host. It sets its title, says hello, and has a button to close it. There's nothing special about it at all; you could run it on its own too:

import sys

name = sys.argv[1]
print('<console.title {' + name + '}>')
print('<span size=16,pt bold=true {Hello from ' + name + '}></p>')
print('<button {Close me} onClick=<putLn {/quit}>>')
while input() != '/quit':
    pass

Save it as child.py. Now the host program, which runs two copies of it in two frames, and shows each one's title underneath:

import subprocess

def start_in_frame(frame, command):
    print('<putLn ' + frame + '>')
    key = input()
    return subprocess.Popen(['iol', '-q', '-k', key, '--'] + command)

print('''
<console.title {Host}>
<span bold=true {Two programs, side by side:}></n>
<left:clientFrame width=300 height=150 border=1 onTitle=<leftTitle left.title>
    onDisconnect=<putLn {/left-finished}>>
<right:clientFrame width=300 height=150 border=1 onTitle=<rightTitle right.title>></n>
<leftTitle:span {}> | <rightTitle:span {}>
''')

start_in_frame('left', ['python3', 'child.py', 'Alpha'])
start_in_frame('right', ['python3', 'child.py', 'Beta'])

while True:
    line = input()
    if line == '/left-finished':
        print('<leftTitle {(closed)}>')
Two programs, side by side:
Hello from Alpha

Close me
Hello from Beta

Close me

Alpha | Beta

Click "Close me" in the left frame, and that program finishes: its frame empties, and the host is told.

A bigger example of the same idea is a program with tabs, where each tab shows a different program in its own frame, and only one frame is visible at a time. The program sets up one frame per tab, starts a program in each, and shows or hides the frames (with their visible field) when a tab is clicked. Each tab can be labelled with its program's title, using onTitle.

Programs you start are your program's responsibility. When your program finishes, make sure it also tells the programs it started to finish (or ends them), so they don't carry on running unseen.

More about the iol command

Starting other programs is a good moment to look at the rest of the iol command's options:

OptionDoes
-qquiet: don't print the welcome message
-k keyrun the program inside the clientFrame with this key
-t titleset the console's starting title
-i fileset the console's starting icon, from a PNG file
-r textpass some text to the program, which it can read as console.rescom
-instead of a command: you are the program (see the chapter on printing text)
--everything after this is the command to run

-r is a way to tell a program what to do when it starts, separately from its own command-line arguments. What the text means is up to the program: a file to open, perhaps, or which screen to show first.


Try it

  1. Add a "Restart" button to the host that starts a new child.py in the left frame after it's been closed.
  2. Run two of your programs from earlier chapters (say, the stopwatch and the guessing game) side by side.
  3. Build a host with three tab buttons and three frames, where clicking a tab shows only that tab's frame.