9.1. Scripting
Scripting lets you replay a sequence of IQcad commands from a text file — useful for repetitive drawing setup, batch operations, demos, and recording sequences of inputs for later reuse.
This chapter documents the .scr text-script support and the
embedded Python scripting host that ship with IQcad today. A third
backend — Pascal — is still planned and will gain its own section
when it lands.
For full programmability (variables, loops, functions, and a real
drawing API) jump to Python scripting below. The .scr engine
documented first is the simpler input-replay mechanism.
Note
The SCRIPT (.scr) engine replays user inputs — command names,
coordinates, and prompt responses — exactly as if you had typed
them at the command line. It has no concept of variables, loops,
or conditionals. For those, use the Python backend
(Python scripting).
9.1.1. Overview
A script is a UTF-8 text file with a .scr extension (.txt also
works) containing IQcad command-line inputs, one operation per logical
step. Each script line — or each whitespace-separated token within a
line — is fed to the dispatcher in turn, exactly as if you had typed
it manually and pressed Enter. This means anything you can do
interactively can be scripted, with a few documented exceptions in
Limitations below.
9.1.2. Running scripts
Three ways to invoke a script:
Command — type
SCRIPTat the command line with no arguments to open a file picker filtered to*.scr/*.txt. Or typeSCRIPT <path>to run a specific file directly. Relative paths resolve against the working directory; quote the path if it contains spaces (SCRIPT "C:\Project Setup\boilerplate.scr").Alias —
SCRis the short form ofSCRIPT.Menu — Tools → Run Script… opens the file picker.
While the script runs, each executed line is echoed to the command
line as [script] <line> in grey so you can follow what’s happening.
A successful run ends with Script complete. in green.
Stopping a script. Pressing Esc cancels both the currently running CAD command and the script. The script also stops automatically on the first unknown command or unhandled exception, reporting the line number:
Script stopped at line 9 (dispatch failed).
9.1.3. File format
Plain UTF-8 text:
Lines beginning with
;are comments (leading whitespace allowed).Spaces inside a line act like Enter — the line is split into whitespace-separated tokens and each token is fed to the dispatcher in turn.
LINE 0,0 10,10is three inputs at once. This matches AutoCAD’s script convention.A blank line acts like a right-click — it finishes the active drawing tool (POLYLINE, LINE chain, SPLINE, etc.). At the empty command prompt with no tool running, a blank line is a no-op.
A double-quoted string (
"…") is one token even if it contains spaces — useful for file paths. The surrounding quotes are stripped.
9.1.3.1. Coordinate forms
A line of typed coordinates is fed to whatever drawing tool is currently active and treated as a synthetic mouse click at the parsed world point. Five forms are accepted; see Entering Coordinates in chapter 10 for the full reference. Briefly:
X,Y— absolute 2D, Z inherits from the last point.X,Y,Z— absolute 3D.@dX,dY[,dZ]— relative to the last point.@dist<angle— polar relative, angle in degrees.#N— survey point number from the active drawing’s database.
9.1.4. Examples by category
The samples below ship under samples\scripts\ and are intended as
copy-and-modify starting points. Each runs end-to-end on a fresh
drawing (01_setup.scr first if the file mentions named layers).
9.1.4.1. Setup
Create layers, set the active layer, reset the view. This is the boilerplate every other script in the folder assumes.
File: samples\scripts\01_setup.scr
MAKELAYER Existing 3
MAKELAYER Design 5
MAKELAYER Annotations 7
MAKELAYER Construction 1
MAKELAYER Centerlines 2
MAKELAYER Hatching 6
CURRENTLAYER Design
PLANVIEW
VIEWTWIST 0
ZE
REGEN
Layer colours use the AutoCAD Colour Index (1=red, 2=yellow, 3=green,
4=cyan, 5=blue, 6=magenta, 7=white). Named shortcuts (RED /
YELLOW / …) are also accepted by MAKELAYER.
9.1.4.2. Drawing primitives
LINE / CIRCLE / ARC / RECTANGLE / ELLIPSE / POLYLINE / SPLINE / POINT all use the same point-input convention.
File: samples\scripts\02_primitives.scr
LINE 0,0 10,0
LINE 0,5 10,5
LINE 0,10 @10,0
CIRCLE 20,0 25,0
CIRCLE 30,0 @5<0
ARC 40,0 45,2.5 50,0
RECTANGLE 0,20 10,30
RECTANGLE 15,20 @10,10
ELLIPSE 30,25 35,25 32.5,28
; Polyline finished by the blank line below.
PLINE 0,40 5,45 10,40 15,45 20,40
A few specifics that catch newcomers:
CIRCLE wants two world points (centre + rim point); a bare scalar radius like
CIRCLE 0,0 10is not accepted — use@10,0for the rim point.RECTANGLE wants two opposite corners.
ELLIPSE wants centre + major-axis endpoint + minor-axis endpoint.
POLYLINE / SPLINE chain-mode: each point adds a vertex; a blank line (or another command name) terminates the entity.
9.1.4.3. Annotation
TEXT and MTEXT take their content via a prompt and then a placement point. Use the prompted form (one line for the command, one for the content, one for the position) so multi-word text isn’t confused with multi-token tokenisation:
File: samples\scripts\03_annotation.scr
TEXT
Hello, IQcad scripting!
0,0
MTEXT
First paragraph of MText.
Second paragraph continues on the next line.
0,5
9.1.4.4. Hatching
HATCH is a pick-points tool: each typed coordinate adds a vertex to the
hatch boundary, and the blank line terminates the boundary, at which
point the hatch fills the closed region. Pattern and scale come from
HP* system variables set via SETVAR.
File: samples\scripts\04_hatching.scr
SETVAR HPName SOLID
HATCH 0,0 10,0 5,8
SETVAR HPName ANSI31
SETVAR HPScale 1
HATCH 15,0 25,0 20,8
9.1.4.5. Dimensions
Linear, aligned, radius, diameter, and angular dimensions all take points the same way the drawing tools do. Each command lists the specific input order in Command Reference.
File: samples\scripts\05_dimensions.scr
LINE 0,0 10,0
DIMLINEAR 0,0 10,0 5,-3
CIRCLE 30,5 35,5
DIMRADIUS 30,5 33,7
; Angle between two lines sharing a vertex at (0,20)
LINE 0,20 10,20
LINE 0,20 7,27
DIMANGULAR 0,20 10,20 7,27
9.1.4.6. Civil entities
Civil flows that take input as coordinates (SURVEYPOINT chain) or run single-shot (CREATESURFACE) script cleanly. Flows that need to click an existing entity (e.g. CREATEPARCEL picks a closed polyline) are better driven from the GUI for now.
File: samples\scripts\06_civil.scr
SURVEYPOINT 0,0,100.0
0,10,100.5
0,20,101.0
10,0,99.8
10,10,100.2
; Build a TIN surface from the survey points just placed
CREATESURFACE
9.1.4.7. Guided tour
A short walkthrough combining several entity types — useful as a quick starting template.
File: samples\scripts\07_tour.scr
This sample creates layers, draws a building footprint as a closed polyline, dimensions it, hatches the interior, and adds a label — everything in one self-contained script.
9.1.5. Patterns
Setup boilerplate. Start every script with a call to
01_setupor its equivalent so layer names and view defaults are predictable.One operation, one logical block. Group the lines for each drawing operation together with a comment line above; keep blank lines reserved for “finish current tool” right-clicks.
Mix manual and scripted input. A script can leave you with an active tool — handy for “set things up, then take it from here”.
Reuse via copy-paste. Scripts are linear; for now, the way to reuse logic is to copy it. Variables and loops will arrive with the Python / Pascal backends.
9.1.6. Limitations
Scalar radius / distance shortcuts. AutoCAD’s
CIRCLE 0,0 10(bare scalar radius) is not supported — IQcad expects two world points. Use a rim point at the desired distance instead.Modal dialogs opened by a scripted command (e.g.
LAYERS,MAPSETTINGS) block until you close them manually. The script pauses, then continues. Avoid such commands in unattended scripts.Pick-an-existing-entity flows. Commands that need to click on an entity already in the drawing (CREATEPARCEL, ALIGNFROMPOLYLINE, etc.) work only if the script’s synthetic click happens to fall on the right entity’s nearest vertex. More reliable to drive from the GUI.
9.1.7. Python scripting
IQcad now embeds a complete Python 3.12 interpreter — a private,
bundled CPython that ships in the python\ folder next to
IQcad.exe. There is nothing to install and no system Python is
required or used.
Python is the recommended choice for anything beyond a linear replay of
typed inputs. Unlike .scr, Python gives you variables, loops,
functions, conditionals, the full Python standard library, and a proper
drawing API through the built-in iqcad module.
9.1.7.1. Running Python
PYTHON drawing_setup.py ; run a .py file (alias: PY)
PYEVAL 2 + 2 ; evaluate a single expression, print result
PYLOAD extensions.py ; load a file and keep it resident
PYSCRIPTS ; list registered Python commands
PYPIP install requests ; run pip in the bundled interpreter
PYTHON file (alias PY) runs a
.pyfile once.PYEVAL expr evaluates a single Python expression and prints the result at the command line — handy as a calculator or for quick API probing (
PYEVAL iqcad.active_drawing.name).PYLOAD file runs a file and leaves it session-resident: any custom commands or event handlers it registers stay active until you close IQcad. This is how you load an extension by hand.
PYSCRIPTS lists the Python-defined commands currently registered.
PYPIP args forwards its arguments to
piprunning inside the bundled interpreter, so you can add third-party packages.
Note
PYPIP install <pkg> downloads from PyPI and therefore needs
network access. Behind a TLS-intercepting proxy or antivirus, the
SSL handshake may fail. In that case install from an offline wheel
(PYPIP install C:\wheels\somepkg.whl) or pass pip’s proxy / trust
options (PYPIP install --proxy http://host:port pkg or
--trusted-host pypi.org --trusted-host files.pythonhosted.org).
9.1.7.2. The iqcad module
Every script runs with a built-in iqcad module already imported. It
exposes an object-oriented drawing API rooted at
iqcad.active_drawing (a Drawing), plus a set of flat convenience
helpers.
import iqcad
dwg = iqcad.active_drawing # the current Drawing
dwg.create_layer("Design", color=5)
dwg.active_layer = "Design"
h = dwg.add_line((0, 0), (10, 0)) # returns an Entity
c = dwg.add_circle((5, 0), 2.5)
h.color = 1 # AutoCAD colour index (1 = red)
h.layer = "Design"
print(h.handle, h.type_name, h.points)
print(h.get_property("length"))
dwg.zoom_extents()
dwg.invalidate()
Drawing offers add_line, add_circle, add_arc,
add_ellipse, add_rectangle, add_polyline, add_text,
add_hatch, add_survey_point, add_tunnel_section_line,
create_layer, find_by_handle, entities, selection,
zoom_extents, invalidate, and the properties active_layer,
name, layer_names.
Creatable entities: line, circle, arc, ellipse, rectangle, polyline, text, hatch, survey point, tunnel-section-line.
Each Entity exposes the properties handle, type_name,
layer, color, points and the methods delete,
get_property, set_property.
The same operations are also available as flat helpers for quick
one-liners — iqcad.cmd, iqcad.write_line, iqcad.point,
iqcad.add_line / add_circle / add_arc / add_ellipse /
add_rectangle / add_text / add_hatch /
add_tunnel_section_line (each returns a handle string),
iqcad.create_layer, iqcad.set_active_layer,
iqcad.zoom_extents:
import iqcad
iqcad.create_layer("Grid", color=8)
iqcad.set_active_layer("Grid")
for x in range(0, 101, 10):
iqcad.add_line((x, 0), (x, 100))
iqcad.zoom_extents()
9.1.7.3. Surfaces
Drawing exposes three surface methods: create_surface builds a new
TIN from a list of 3-D seed points, find_surface looks up an existing
surface by name (returns None if not found), and surfaces enumerates
all surfaces in the drawing.
Each Surface has read-only name and handle properties and four
methods:
elevation_at(x, y)— interpolates the TIN at the given plan coordinates; returnsNonewhen the point lies outside the triangulation.add_point(x, y, z)— inserts a new vertex and rebuilds the TIN.profile_along(pts)— samples the surface along a polyline defined by 2-D plan points; returns a list of(station, z)tuples.volume_to(other, cell=1.0)— computes cut and fill volumes between this surface and other using a grid cell size of cell metres; returns(cut, fill)as a 2-tuple of floats.
import iqcad
d = iqcad.active_drawing
# Build an existing-ground surface from survey points
eg = d.create_surface("EG", [(0, 0, 10), (10, 0, 11), (5, 8, 10.5)])
z = eg.elevation_at(4, 2) # float, or None if outside the TIN
prof = eg.profile_along([(0, 0), (50, 0)]) # [(station, z), ...]
# Compare against a finished-grade surface already in the drawing
fg = d.find_surface("FG")
if fg is not None:
cut, fill = eg.volume_to(fg)
iqcad.write_line(f"cut={cut:.1f} fill={fill:.1f}")
9.1.7.4. Alignments
Drawing exposes three alignment methods: create_alignment_from_polyline
builds a new alignment from an existing polyline entity (located by handle),
find_alignment looks up an alignment by name (returns None if not
found), and alignments enumerates every alignment in the drawing.
Each Alignment has read-only name, handle, length,
start_station, and end_station properties and five methods:
point_at_station(s)— returns the(x, y, z)world point at chainage s.direction_at_station(s)— bearing at chainage s, in radians.elevation_at_station(s)— profile elevation at chainage s (from the attached vertical alignment, if any).station_offset_at(x, y)— nearest chainage and signed offset for a plan point; offset is negative to the left and positive to the right of the alignment direction.elements()— list of dicts describing the horizontal geometry elements; each dict has keyskind("line"or"arc"),length,start_station, andradius(0for tangent runs).
import iqcad
d = iqcad.active_drawing
# Create an alignment from a polyline already in the drawing.
# radius=0 builds straight tangents through every vertex; a non-zero
# radius fillets the interior corners with curves instead.
al = d.create_alignment_from_polyline(poly_handle, radius=25.0, name="CL-1")
s, off = al.station_offset_at(x, y) # chainage + offset (-left / +right)
x, y, z = al.point_at_station(120.0)
z = al.elevation_at_station(120.0) # from the profile
for el in al.elements():
print(el["kind"], el["length"])
# Iterate all alignments in the drawing
for a in d.alignments():
print(a.name, f"{a.length:.1f} m")
Note
create_alignment_from_polyline with radius=0 (the default) builds
straight tangents through every vertex, including polylines with 3 or
more vertices. Pass a non-zero radius (≥ 0.01) only if you want the
interior corners filleted into curves.
Full signature:
create_alignment_from_polyline(handle, radius=0, name="", use_z=False, vcurve_len=0)
use_z=True builds the vertical profile from the polyline’s per-vertex
elevations — the scripted equivalent of accepting the AFP command’s
Create vertical profile from vertex elevations? prompt. vcurve_len
sets the vertical curve length applied to every interior PVI (0, the
default, keeps an angular profile). The PascalScript equivalent is
CreateAlignmentFromPolylineZ(polyHandle, radius, name, vcurveLen) (see
Pascal Scripting), which always builds the profile from vertex
elevations.
9.1.7.5. Custom commands
Decorate a function with @iqcad.command to register a new IQcad
command. Once registered (load it with PYLOAD or drop it in the
autoload folder) it appears in HELP and can be typed at the command
prompt like any built-in. Command arguments arrive as strings — cast
them yourself.
import iqcad
@iqcad.command("GRID", aliases="GR", category="Drafting",
description="Draw an N x N construction grid")
def grid(n, spacing):
n = int(n) # args are strings — cast them
step = float(spacing)
for i in range(n + 1):
iqcad.add_line((0, i * step), (n * step, i * step))
iqcad.add_line((i * step, 0), (i * step, n * step))
iqcad.zoom_extents()
Then at the prompt: GRID 5 10.
9.1.7.6. Events
Register handlers that fire when the drawing changes. Handlers run synchronously on the main thread; an exception in a handler is logged to the command line, not fatal.
import iqcad
@iqcad.on_entity_created
def created(entity):
iqcad.write_line(f"created {entity.type_name} {entity.handle}")
@iqcad.on_entity_deleted
def deleted(entity):
iqcad.write_line(f"deleted {entity.handle}")
@iqcad.on_selection_changed
def selected(entities):
iqcad.write_line(f"selection: {len(entities)} object(s)")
Handlers can also be attached and detached explicitly with
iqcad.on(event, func) and iqcad.off(event, func).
9.1.7.7. Long-running scripts
A tight Python loop blocks IQcad’s UI for its whole duration. In any
loop that may run for a while, call iqcad.yield_() periodically: it
pumps the message queue so the window stays responsive, and lets
Esc cancel the script (which raises KeyboardInterrupt in your
code).
import iqcad
try:
for i in range(100000):
iqcad.add_circle((i, 0), 1)
if i % 100 == 0:
iqcad.yield_() # note the trailing underscore
except KeyboardInterrupt:
iqcad.write_line("cancelled by user")
Note
The method is spelled iqcad.yield_() with a trailing underscore,
because yield is a reserved keyword in Python.
9.1.7.8. Installed extensions (autoload) and trust
Any .py file dropped in %APPDATA%\IQcad\scripts\ is loaded
automatically at startup — the place to keep your personal commands and
event handlers.
Because autoloaded code runs without prompting, IQcad shows a one-time
content-trust dialog the first time it sees a script (and again
whenever its contents change). The dialog shows the file’s SHA-256
hash and offers Always, This session, or Never; your choice
is remembered in %APPDATA%\IQcad\scripts\script_trust.json. A
script you have never trusted is not executed.
9.1.7.9. Type stubs and IDE support
For autocomplete and type checking in your editor, point it at the type
stub samples\python\iqcad.pyi (or at the bundled interpreter in
python\). Worked examples ship alongside it:
samples\python\01_primitives.py— drawing geometry with the API.samples\python\02_command.py— registering a custom command.samples\python\03_events.py— wiring up event handlers.
9.1.7.10. Known limitations (Python)
Command arguments are strings.
@iqcad.commandpasses every argument as astr; cast toint/floatin your function.Limited event coverage. Only entity-created, entity-deleted, and selection-changed events exist today. There is no
on_entity_modifiedand no drawing-lifecycle or command events yet.No background / async execution. Scripts run on the UI thread; there is no
iqcad.background()worker API yet. Useiqcad.yield_()to keep long loops responsive.
Note
The Greek edition of this manual does not yet include the Python scripting section; its translation is a follow-up.
9.1.8. Future scripting languages
The SCR engine is intentionally minimal — it’s a faithful replay of typed inputs. For richer scripting:
Python — available now. A Python 3.12 interpreter embedded in IQcad with bindings to the drawing API, command dispatcher, and entity model, supporting full control flow, computations, user-defined functions, and external libraries. See Python scripting above.
Pascal — planned. A Free Pascal script host using the same compiler that builds IQcad, for users who already write extensions in Pascal and want the same syntax for scripting. When it lands, this chapter will gain a Pascal scripting section.