Under ipl/gprogs/ is a program randweav.icn. This program presents a small GUI, allowing the user to generate various kinds of weaving patterns. It is a typical example of a small GUI program with some vidgets for inputting data / providing information and a main panel with a drawn display.
Here, I have rewritten the program to construct the GUI using hand-coded vidgets in place of vsetup, and tried to explain how I understand the important graphical/GUI elements.
The original display of randweav is:
The basic interface consists of some input text and toggle buttons at the top of the screen, and buttons to render, save or quit around the central area. The central area is a pane onto which is drawn the pattern - drawing the pattern is discussed in the next section.
Setting up the interface is straightforward: for each input vidget, we save the vidget into a global variable, for later access.
win := Window ! put(["size=380,492", "bg=pale gray", "label=weaver"], args) # open window root := Vroot_frame(win) VSetFont(win) # top-left are three input values VInsert(root, colours_text := Vtext(win, "Colours: \\=6", , "colours", 3), # default value of 6 10, 9, 87, 19) VInsert(root, vcyclic_toggle := Vtoggle(win, "cycle warp", , "vcyclic", "checkno", 97, 17), 5, 36) VInsert(root, hcyclic_toggle := Vtoggle(win, "cycle weft", , "hcyclic", "checkno", 97, 20), 5, 56) # top-centre is a button to redraw the display VInsert(root, Vbutton(win, "RENDER", render, "render", "regular", 72, 36), 159, 24) # top-right are three input values VInsert(root, side_text := Vtext(win, "Side: \\=90", , "side", 3), 285, 8, 87, 19) VInsert(root, bias_text := Vtext(win, "Bias: \\=60", , "bias", 3), 285, 37, 87, 19) VInsert(root, perfect_toggle := Vtoggle(win, "perfect", , "perfect", "checkno", 76, 20), 281, 57) # two buttons at the base of the display VInsert(root, Vbutton(win, "save @S", save_image, "save", "regular", 78, 20), 8, 462) VInsert(root, Vbutton(win, "quit @Q", quit, "quit", "regular", 78, 20), 293, 462) # draw an outline around the RENDER button VInsert(root, Vpane(win, , "outline", "sunken"), 153, 18, 84, 48) # drawn an outline for the drawing region VInsert(root, region := Vpane(win, , "region", "grooved"), 8, 84, 364, 364) VResize(root)
Some notes:
-
VSetFont
can be left out, but sets a smaller font on the screen -
default values are provided for the text vidgets, such as
colours_text
- callbacks are left out of the text/toggle vidgets, as their values will be retrieved directly - an alternative design could trigger a redraw when the values are changed using callbacks
- and yes, I changed "color" to "colour"!
The design of the interface can be seen in the following screenshot, which does not draw the pattern.
Events are handled in the all
procedure which captures some key events, and
by buttons directly calling the render/save_image/quit procedures. It's worth
also noting the save_image
procedure, where a file dialog is opened and an
image saved.
OpenDialog TODO
Hidden Window
Drawing the Image
Showing the Image on main Window
Saving the Image to a file
link random link vsetup global root # root vidget global hcyclic_toggle global vcyclic_toggle global perfect_toggle global colours_text, side_text, bias_text global region # pattern region global hidwin # hidden window for saving to file global allcolours # string of all palette colours global maxsiz # maximum pattern size global patsize # pattern size selected $define PALETTE "c1" # colour palette $define PREFCOLOURS "06NBCDFsHIJM?!" # preferred colours procedure main(args) local win randomize() allcolours := PREFCOLOURS || (PaletteChars(PALETTE) -- PREFCOLOURS) win := Window ! put(["size=380,492", "bg=pale gray", "label=weaver"], args) # open window root := Vroot_frame(win) VSetFont(win) VInsert(root, colours_text := Vtext(win, "Colours: \\=6", , "colours", 3), 10, 9, 87, 19) VInsert(root, vcyclic_toggle := Vtoggle(win, "cycle warp", , "vcyclic", "checkno", 97, 17), 5, 36) VInsert(root, hcyclic_toggle := Vtoggle(win, "cycle weft", , "hcyclic", "checkno", 97, 20), 5, 56) VInsert(root, Vbutton(win, "RENDER", render, "render", "regular", 72, 36), 159, 24) VInsert(root, side_text := Vtext(win, "Side: \\=90", , "side", 3), 285, 8, 87, 19) VInsert(root, bias_text := Vtext(win, "Bias: \\=60", , "bias", 3), 285, 37, 87, 19) VInsert(root, perfect_toggle := Vtoggle(win, "perfect", , "perfect", "checkno", 76, 20), 281, 57) VInsert(root, Vbutton(win, "save @S", save_image, "save", "regular", 78, 20), 8, 462) VInsert(root, Vbutton(win, "quit @Q", quit, "quit", "regular", 78, 20), 293, 462) VInsert(root, Vpane(win, , "outline", "sunken"), 153, 18, 84, 48) VInsert(root, region := Vpane(win, , "region", "grooved"), 8, 84, 364, 364) VResize(root) VSetState(vcyclic_toggle, 1) # default "cycle warp" on VSetState(hcyclic_toggle, 1) # default "cycle weft" on hidwin := WOpen("canvas=hidden", # open hidden window "width=" || region.uw, "height=" || region.uh) maxsiz := region.uw # set maximum size maxsiz >:= region.uh render() # draw once without prompting GetEvents(root, , all) # then wait for events end # all(a, x, y) -- process all events, checking for keyboard shortcuts procedure all(a, x, y) if a === !" \n\r" then render() # draw new pattern for SPACE, CR, LF else if &meta then case a of { !"qQ": exit() # exit for @Q !"sS": save_image() # save image for @S } return end # render() -- draw a new pattern according to current parameters procedure render() local ncolours, bias local s, x, y, w, h, z, k static prevsize ncolours := txtval(colours_text, 1, *allcolours) # retrieve "Colours" setting patsize := txtval(side_text, 1, maxsiz) # retrieve "Side" setting bias := txtval(bias_text, 0, 100) # retrieve "Bias" setting k := (shuffle(PREFCOLOURS) | allcolours)[1+:ncolours] # pick a colour set s := genpatt(patsize, k, bias / 100.0) # generate a pattern DrawImage(hidwin, 0, 0, s) # draw on hidden win z := maxsiz / patsize # calculate scaling x := region.ux + (region.uw - z * patsize) / 2 y := region.uy + (region.uh - z * patsize) / 2 # copy to main window with enlargement if prevsize ~===:= patsize then EraseArea(region.ux, region.uy, region.uw, region.uh) # erase old pattern Zoom(hidwin, &window, 0, 0, patsize, patsize, x, y, z * patsize, z * patsize) return end # genpatt(size, colours, bias) -- generate a new pattern as DrawImage() string procedure genpatt(size, colours, bias) local warp, weft, perfect, s, x, y, w # choose thread colours warp := genthreads(size, colours, VGetState(vcyclic_toggle)) weft := genthreads(size, colours, VGetState(hcyclic_toggle)) # initialize output string (including first row) s := size || "," || PALETTE || "," || warp perfect := VGetState(perfect_toggle) # fill in remaining rows every y := 2 to size do { w := ?weft[y] # get weft colour s ||:= w # put in first column if \perfect then every x := 2 to size do # fill the rest (perfect case) s ||:= if ((x + y) % 2) = 0 then w else warp[x] else every x := 2 to size do # fill the rest (random case) s ||:= if ?0 > bias then w else warp[x] } return s end # genthreads(n, colours, cyclic) -- generate a set of warp or weft threads procedure genthreads(n, colours, cyclic) local s if \cyclic then return repl(shuffle(colours), 1 + n / *colours)[1+:n] s := "" every 1 to n do s ||:= ?colours return s end # txtval(s, min, max) -- get numeric value from named vidget and clamp to range procedure txtval(v, min, max) local n VEvent(v, "\r", v.ax, v.ay) # set RETURN event to update state n := integer(VGetState(v)) | min # retrieve int value, else use minimum n <:= min # limit value by min and max n >:= max VSetState(v, n) # update vidget with validated value return n # return value end # save_image() -- present dialog box and save pattern as image file procedure save_image() local g g := WAttrib("gamma") # save old gamma value WAttrib("gamma=1.0") # don't gamma-correct on write repeat case OpenDialog("Save pattern as:") of { "Cancel": { WAttrib("gamma=" || g) fail } "Okay": { if WriteImage(hidwin, dialog_value, 0, 0, patsize, patsize) then break else Notice("cannot write file:", dialog_value) } } WAttrib("gamma=" || g) # restore gamma value return end procedure quit() exit() end