9.2. Python Scripting Reference
This chapter is the complete reference for IQcad’s embedded iqcad
Python module: every class, method, property, decorator, and event,
with text-based class diagrams and worked examples.
For a getting-started introduction (running scripts, the trust model, custom commands, the big picture) see Python scripting in chapter 18. This chapter is the API reference you reach for once you know the basics.
9.2.1. Intro
IQcad embeds a private, bundled Python 3.12 interpreter that ships
in the python\ folder next to IQcad.exe. There is nothing to
install and no system Python is required or used. Every script runs
with a built-in iqcad module already imported, exposing an
object-oriented drawing API rooted at iqcad.active_drawing together
with a set of flat convenience helpers.
The reference below is generated from the type stub
samples\python\iqcad.pyi and matches it exactly. Point your IDE at
that stub (or the bundled interpreter) for autocomplete.
9.2.2. Running Python
Five command-line entry points drive the Python host:
Command |
Description |
|---|---|
|
Run a |
|
Evaluate a single Python expression and print the result at the
command line (e.g. |
|
Run a file and leave it session-resident: any
|
|
List the Python-defined commands currently registered. |
|
Forward arguments to |
Warning
PYPIP install <pkg> downloads from PyPI and therefore needs
network access. Behind a TLS-intercepting proxy or antivirus the
SSL handshake may fail. 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.2.2.1. Autoload and trust
Any .py file dropped in %APPDATA%\IQcad\scripts\ is loaded
automatically at startup. 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 SHA-256 content hash
changes). The dialog 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.2.2.2. Responsiveness and Esc
Scripts run on the GUI thread, so a tight loop blocks the 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 (raising a
catchable KeyboardInterrupt). The method is spelled with a
trailing underscore because yield is a Python keyword.
9.2.2.3. Type stubs
For autocomplete and type checking, point your editor at the stub
samples\python\iqcad.pyi or at the bundled interpreter in
python\.
9.2.3. The object model
The iqcad module is the root. It carries flat helper functions, the
command/event decorators, and the live active_drawing attribute.
active_drawing resolves fresh on every access (PEP 562) and returns
the current Drawing. The Drawing is the factory for everything
else: it creates and returns Entity, Surface, and
Alignment objects.
iqcad (module)
|
|-- flat helpers ....... cmd, write_line, point,
| add_line/add_circle/add_arc/add_ellipse/
| add_rectangle/add_hatch/
| add_tunnel_section_line (return handle str),
| create_layer, set_active_layer, zoom_extents
|-- decorators ......... command, on_entity_created,
| on_entity_deleted, on_selection_changed,
| on / off, unregister_command
|-- yield_() ........... pump UI / allow Esc
|
`-- active_drawing ---> Drawing (resolves fresh each access)
|
| create/returns
|
+---------------+----------------+
| | |
v v v
Entity Surface Alignment
(geometry) (TIN) (horizontal/vertical)
add_line/circle/arc/ellipse/rectangle/polyline/
text/hatch/survey_point/tunnel_section_line --> Entity
create_surface / find_surface / surfaces --> Surface
create_alignment_from_polyline /
find_alignment / alignments --> Alignment
Note
iqcad.add_line(...) (flat) returns a handle string, whereas
iqcad.active_drawing.add_line(...) (OO) returns an Entity.
add_polyline is OO-only: there is no flat iqcad.add_polyline.
9.2.4. Class reference
9.2.4.1. Drawing
Obtained from iqcad.active_drawing. The factory for entities,
surfaces, and alignments.
Member |
Signature |
Returns |
Description |
|---|---|---|---|
|
property (read/write) |
|
Name of the current layer. |
|
property (read-only) |
|
Drawing name. |
|
property (read-only) |
|
All layer names. |
|
|
|
Line from point a to b. |
|
|
|
Circle, centre c, radius r. |
|
|
|
Text at pos with height. |
|
|
|
Polyline through pts (OO-only). |
|
|
|
Survey point with number. |
|
|
|
Three-point arc. |
|
|
|
Ellipse with semi-axes. |
|
|
|
Rectangle from opposite corners. |
|
|
|
Hatch filling the points boundary. |
|
|
|
Tunnel section line. |
|
|
|
Create a layer; color is an ACI index. |
|
|
|
Look up an entity by handle. |
|
|
|
All entities in the drawing. |
|
|
|
Currently selected entities. |
|
|
|
Zoom to drawing extents. |
|
|
|
Force a redraw. |
|
|
|
All surfaces. |
|
|
|
Look up a surface by name. |
|
|
|
Build a TIN from |
|
|
|
All alignments. |
|
|
|
Look up an alignment by name. |
|
|
|
Build an alignment from a polyline entity. |
9.2.4.2. Entity
A single drawing object returned by the OO add_* methods.
Member |
Signature |
Returns |
Description |
|---|---|---|---|
|
property (read-only) |
|
Persistent handle. |
|
property (read-only) |
|
|
|
property (read/write) |
|
Layer name. |
|
property (read/write) |
|
ACI colour index. |
|
property (read/write) |
|
Defining points. |
|
|
|
Remove the entity. Any later access raises |
|
|
|
Read a typed property (e.g. circle |
|
|
|
Write a typed property. |
Common get_property / set_property names: circle 'Radius';
ellipse 'RadiusX' / 'RadiusY'; text 'Text' / 'Height';
survey point 'PointNumber' / 'Elevation'.
9.2.4.3. Surface
A TIN surface created by Drawing.create_surface (or looked up via
find_surface / surfaces).
Member |
Signature |
Returns |
Description |
|---|---|---|---|
|
property (read-only) |
|
Surface name. |
|
property (read-only) |
|
Persistent handle. |
|
|
|
Interpolated Z, or |
|
|
|
Insert a vertex and rebuild the TIN. |
|
|
|
Long-section: list of |
|
|
|
Grid-approx |
9.2.4.4. Alignment
A horizontal alignment created by create_alignment_from_polyline (or
looked up via find_alignment / alignments).
Member |
Signature |
Returns |
Description |
|---|---|---|---|
|
property (read-only) |
|
Alignment name. |
|
property (read-only) |
|
Persistent handle. |
|
property (read-only) |
|
Total length. |
|
property (read-only) |
|
Start chainage. |
|
property (read-only) |
|
End chainage. |
|
|
|
|
|
|
|
Bearing at s, in radians. |
|
|
|
Profile elevation at s. |
|
|
|
|
|
|
|
Geometry elements; keys |
9.2.4.5. Module-level functions
The flat helpers and the live active_drawing attribute.
Member |
Signature |
Returns |
Description |
|---|---|---|---|
|
attribute (read-only) |
|
The current drawing; resolves fresh on each access. |
|
|
|
Run a command-line string. |
|
|
|
Print a line to the command line. |
|
|
|
Build a point tuple. |
|
|
|
Flat line; returns a handle string. |
|
|
|
Flat circle; returns a handle string. |
|
|
|
Flat text; returns a handle string. |
|
|
|
Flat arc; returns a handle string. |
|
|
|
Flat ellipse; returns a handle string. |
|
|
|
Flat rectangle; returns a handle string. |
|
|
|
Flat hatch; returns a handle string. |
|
|
|
Flat tunnel section line; returns a handle string. |
|
|
|
Create a layer (ACI color). |
|
|
|
Set the current layer. |
|
|
|
Zoom to drawing extents. |
|
|
|
Remove a registered custom command. |
|
|
|
Attach an event handler by name. |
|
|
|
Detach an event handler. |
|
|
|
Pump the UI; raises |
|
|
|
Show a message box. |
|
|
|
Yes/No prompt; |
|
|
|
Text input; |
|
|
|
File chooser; |
9.2.5. Decorators and events
9.2.5.1. Custom commands
Decorate a function with @iqcad.command to register a new IQcad
command. Load it with PYLOAD (or drop it in the autoload folder)
and it can be typed at the command prompt like any built-in.
@iqcad.command("NAME", aliases="N", category="Tools",
description="What it does")
def handler(*args):
...
Warning
Command arguments arrive as strings. Cast them yourself
(int(args[0]), float(args[1])).
Remove a registration with iqcad.unregister_command("NAME").
9.2.5.2. Events
Three event decorators register handlers that fire when the drawing changes:
Decorator |
Handler receives |
Fires when |
|---|---|---|
|
|
An entity is created. |
|
|
An entity is deleted. |
|
|
The selection changes. |
Handlers can also be attached and detached explicitly with
iqcad.on(event, func) and iqcad.off(event, func).
Note
Handlers run synchronously on the GUI thread. An exception
inside a handler is logged to the command line, not fatal. There is
no on_entity_modified and no drawing-lifecycle or command events
yet.
9.2.6. Dialogs
Four module-level dialog functions prompt the user. They are shared
with the Pascal host (see Pascal Scripting). Run headlessly –
or wherever no GUI provider is installed – they return safe defaults so
an unattended script never blocks: confirm returns False,
input_string returns None, and select_file returns ''.
Function |
Signature |
Returns |
Description |
|---|---|---|---|
|
|
|
Show a message box with text. |
|
|
|
Yes/No prompt; |
|
|
|
Single-line text input; |
|
|
|
File chooser; |
import iqcad
iqcad.show_message("Starting import.")
if iqcad.confirm("Proceed?"):
name = iqcad.input_string("Layer name", "Design")
if name is not None:
path = iqcad.select_file("Open points file", "CSV files|*.csv", False)
if path:
iqcad.write_line("import %s onto %s" % (path, name))
9.2.7. Examples
Worked example scripts ship under samples\python\:
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.samples\python\04_surface.py– TIN surfaces: create, query, profile, volume.samples\python\05_alignment.py– alignments from a polyline.samples\python\06_civil_workflow.py– chainage along an alignment while sampling a surface (the highlight).
9.2.7.1. Surfaces
Create a TIN from seed points, interpolate it, take a long-section, then
compare two surfaces by volume (04_surface.py):
import iqcad
d = iqcad.active_drawing
eg = d.create_surface("EG", [
(0, 0, 10.0), (50, 0, 11.0), (50, 40, 12.5),
(0, 40, 10.5), (25, 20, 11.2),
])
z = eg.elevation_at(25, 20) # float, or None outside the TIN
eg.add_point(25, 40, 11.8) # rebuilds the TIN
for station, pz in eg.profile_along([(0, 20), (50, 20)]):
iqcad.write_line("STA %.3f Z %.3f" % (station, pz))
fg = d.create_surface("FG", [
(0, 0, 11.0), (50, 0, 12.0), (50, 40, 13.5),
(0, 40, 11.5), (25, 20, 12.2),
])
cut, fill = eg.volume_to(fg, cell=1.0) # cut where EG is above FG
iqcad.write_line("cut=%.2f fill=%.2f" % (cut, fill))
9.2.7.2. Alignments
Build an alignment from a polyline (fillet radius required for 3+
vertices), then query it (05_alignment.py):
import iqcad
d = iqcad.active_drawing
# add_polyline is OO-only; capture the handle for the alignment factory.
ph = d.add_polyline([(0, 0), (100, 0), (100, 50), (160, 50)], False).handle
al = d.create_alignment_from_polyline(ph, 20.0, "CL-1")
iqcad.write_line("len=%.3f STA %.3f..%.3f" % (
al.length, al.start_station, al.end_station))
x, y, z = al.point_at_station(50.0)
d_rad = al.direction_at_station(50.0)
s, off = al.station_offset_at(50.0, 5.0) # offset -left / +right
for el in al.elements():
iqcad.write_line("%s len=%.3f R=%.3f" % (
el["kind"], el["length"], el["radius"]))
9.2.7.3. Combined civil workflow
The highlight: step chainage along an alignment and sample the ground
surface at each station (06_civil_workflow.py):
import iqcad
d = iqcad.active_drawing
surf = d.create_surface("EG", [
(-20, -20, 10.0), (180, -20, 14.0), (180, 80, 16.0),
(-20, 80, 11.0), (80, 25, 13.0),
])
ph = d.add_polyline([(0, 0), (100, 0), (100, 50), (160, 50)], False).handle
al = d.create_alignment_from_polyline(ph, 20.0, "CL-1")
step = 10.0
s = al.start_station
while s <= al.end_station + 1e-6:
x, y, z = al.point_at_station(s)
gz = surf.elevation_at(x, y) # None outside the TIN
iqcad.write_line("STA %.1f surf=%.3f" % (
s, gz if gz is not None else float('nan')))
s += step
9.2.8. Dialogs and message boxes
Scripts can pop a parameter dialog or a message box through the same host-built UI the plugin SDK uses (so there is no Tk/Qt dependency).
iqcad.create_dialog(title) returns a dialog object; describe fields,
execute(), then read the values:
dlg = iqcad.create_dialog("Draw a shape")
dlg.add_choice("shape", "Shape", ["Rectangle", "Circle"], 0)
dlg.add_float("size", "Size (m)", 10.0, decimals=2) # min=0, max=0 → unbounded
dlg.add_text ("layer", "Layer name", "SHAPES")
dlg.add_color("color", "Color", 0x00B400) # 0x00BBGGRR
dlg.add_bool ("filled","Filled?", False)
if dlg.execute():
shape = dlg.get_choice("shape") # selected index
size = dlg.get_float("size")
layer = dlg.get_text("layer")
color = dlg.get_color("color")
filled = dlg.get_bool("filled")
Field builders: add_text, add_multiline, add_int(…, min=0,
max=0), add_float(…, min=0.0, max=0.0, decimals=3), add_bool,
add_choice(key, label, options_list, default_index=0), add_file(…,
filter='', save=False), add_folder, add_color. Getters:
get_text (also returns the selected choice’s text), get_int,
get_float, get_bool, get_choice (index), get_color.
execute() returns False if the user cancels.
iqcad.message_box(text, caption="", buttons=iqcad.MB_OK,
icon=iqcad.MB_ICON_NONE) shows a message box and returns the pressed
button. Buttons: MB_OK, MB_OKCANCEL, MB_YESNO,
MB_YESNOCANCEL; icons: MB_ICON_NONE/INFO/WARNING/ERROR/QUESTION;
results: MB_RESULT_OK/CANCEL/YES/NO (a dismissed box returns
MB_RESULT_CANCEL).
if iqcad.message_box("Delete selection?", "Confirm",
iqcad.MB_YESNO, iqcad.MB_ICON_QUESTION) == iqcad.MB_RESULT_YES:
pass # … proceed with the delete
9.2.9. Live forms
Where a dialog is modal, a live form is a non-modal, host-owned tool window that stays open: you build controls, keep the handles, mutate them at runtime, and react to the user through a handler object. It is the scripting face of the plugin SDK’s live tool windows.
iqcad.create_form(title, handler=None) returns a live form. Build it with
add_button, add_label, add_text, add_multiline, add_int,
add_float, add_bool, add_choice, add_color, add_file,
add_folder, add_list (plus begin_row / end_row,
find_control), then show(). Each add_* returns a control object
with get_* / set_* accessors, set_caption / set_enabled /
set_visible, and the list ops append_line / clear /
item_count / get_selected / set_selected. The form also offers
hide(), close(), is_visible() and set_title().
The optional handler is any object whose on_button_clicked(key),
on_value_changed(key) and on_form_closed() methods the host calls (on
the main thread) when the user interacts with the form:
class Handler:
def __init__(self, form):
self.form = form
def on_button_clicked(self, key):
if key == "add":
n = self.form.find_control("n").get_int()
self.form.find_control("log").append_line("Added: %d" % n)
self.form.find_control("status").set_text("Last added: %d" % n)
form = iqcad.create_form("Form demo")
form.add_label("status", "Click Add to append a value")
form.add_int("n", "Value", 1, 0, 1000)
form.add_button("add", "Add")
form.add_list("log", "Log")
form.handler = Handler(form) # or pass handler= to create_form
form.show()
iqcad.create_panel(title, handler=None) is identical to create_form but
docks the panel into the main window — it appears as a tab in the right-side
dock strip instead of a floating window. The builder, control objects and
handler surface are exactly the same; only the container differs:
panel = iqcad.create_panel("Form demo", handler) # docked twin of create_form
9.2.11. Limitations and tips
Command arguments are strings.
@iqcad.commandpasses every argument as astr; cast toint/floatin your function.``elevation_at`` returns None outside the TIN. Always guard the result before using it in arithmetic.
Alignment fillet radius.
create_alignment_from_polylinewithradius=0(the default) builds straight tangents through every vertex, including polylines with 3 or more vertices. Pass a non-zero radius (at least0.01) only if you want the interior corners filleted into arcs.Offset sign.
station_offset_atreturns an offset that is negative to the left and positive to the right of the alignment direction.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 GUI thread; use
iqcad.yield_()to keep long loops responsive and Esc-cancelable.