<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Python Archives | DMC, Inc.</title>
	<atom:link href="https://static.dmcinfo.com/blog/tag/python/feed/index.xml" rel="self" type="application/rss+xml" />
	<link></link>
	<description></description>
	<lastBuildDate>Fri, 10 Jul 2026 20:23:50 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.2</generator>

<image>
	<url>https://static.dmcinfo.com/wp-content/uploads/2025/04/site-icon-150x150.png</url>
	<title>Python Archives | DMC, Inc.</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>LabVIEW + Python Over TCP: A Reusable Architecture Pattern for Industrial Software</title>
		<link>https://static.dmcinfo.com/blog/47156/labview-python-tcp-integration-architecture/</link>
		
		<dc:creator><![CDATA[Fadil Eledath]]></dc:creator>
		<pubDate>Mon, 13 Jul 2026 11:00:00 +0000</pubDate>
				<category><![CDATA[LabVIEW]]></category>
		<category><![CDATA[Test and Measurement Automation]]></category>
		<category><![CDATA[LabVIEW Programming]]></category>
		<category><![CDATA[Modbus TCP]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Systems Integration]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/?p=47156</guid>

					<description><![CDATA[<p>LabVIEW is still one of the best environments for operator interfaces, machine state logic, and deterministic control workflows. However, many teams now need to ship features that evolve faster than traditional LabVIEW development cycles can accommodate, including computer vision, advanced analytics, AI-assisted decision support, and custom tooling. The practical answer is not to rewrite everything. [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/47156/labview-python-tcp-integration-architecture/">LabVIEW + Python Over TCP: A Reusable Architecture Pattern for Industrial Software</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">LabVIEW is still one of the best environments for operator interfaces, machine state logic, and deterministic control workflows. However, many teams now need to ship features that evolve faster than traditional LabVIEW development cycles can accommodate, including computer vision, advanced analytics, AI-assisted decision support, and custom tooling.</p>



<p class="wp-block-paragraph">The practical answer is not to rewrite everything. Instead, a split-runtime architecture allows teams to keep LabVIEW where it shines and move high-change computation into Python services connected over TCP.</p>



<p class="wp-block-paragraph">This Python integration approach supports a wide range of industrial use cases, including:</p>



<ul class="wp-block-list">
<li>Vision inference and image analysis.</li>



<li>Statistical quality calculations.</li>



<li>Optimization and scheduling support.</li>



<li>Report generation and data enrichment.</li>



<li>AI-assisted engineering tools.</li>
</ul>



<p class="wp-block-paragraph">The end result is an architecture in which LabVIEW remains the orchestration and operator-layer interface for complex hardware systems, while Python hosts features that benefit from rapid iteration and rich libraries. One particularly salient reason to integrate Python in this way is that it integrates cleanly with the latest AI coding agents and tooling, supporting accelerated development.</p>



<h2 id="h-why-tcp-is-the-right-approach" class="wp-block-heading">Why TCP Is the Right Approach</h2>



<p class="wp-block-paragraph">There are multiple ways to connect runtimes, many of which DMC has explored across numerous domains. For example, we’ve worked with MQTT, RabbitMQ, NATS, Redis, as well as other messaging tools and protocols. In this tutorial, we use a simple request-reply pattern. Here, TCP is the best fit since it is:</p>



<ul class="wp-block-list">
<li style="padding-bottom:var(--wp--preset--spacing--30)"><strong>Language neutral:</strong>&nbsp;Both LabVIEW and Python support sockets natively.</li>



<li style="padding-bottom:var(--wp--preset--spacing--30)"><strong>Low overhead:</strong>&nbsp;Efficient for frequent request/response cycles without added protocol information.</li>



<li style="padding-bottom:var(--wp--preset--spacing--30)"><strong>Process isolated:</strong>&nbsp;UI/control failures and compute-service failures are easier to contain. No need to manage the lifecycle of a binary outside of two processes communicating with each other.</li>



<li><strong>Flexible in deployment: </strong>Run components on the same machine or distribute later without changing the contract.</li>
</ul>



<h2 id="h-labview-python-tcp-reference-architecture" class="wp-block-heading">LabVIEW Python TCP Reference Architecture</h2>



<p class="wp-block-paragraph">A reusable version of this request-reply pattern looks like this:</p>



<ol class="wp-block-list">
<li>LabVIEW gathers the request parameters and sends a typed request.</li>



<li>The Python service receives the request and executes the requested logic.</li>



<li>Python returns typed results plus optional status and timing metadata.</li>



<li>LabVIEW processes the reply body as needed.</li>
</ol>



<p class="wp-block-paragraph">This same structural loop applies whether the compute workload is vision, forecasting, anomaly scoring, or rule evaluation.</p>



<h2 id="h-socket-wrappers-for-labview-and-python-communication" class="wp-block-heading">Socket Wrappers for LabVIEW and Python Communication</h2>



<p class="wp-block-paragraph">This is a minimal implementation of a socket wrapper that serves as a skeleton you can use to add type safety, error checking, and other safeguards to make your code more robust. The wire format is a fixed header to ensure that reads are deterministic:</p>



<ul class="wp-block-list">
<li style="padding-bottom:var(--wp--preset--spacing--30)"><strong>LabVIEW to Python:</strong> 1 byte message type + 4 byte big-endian payload size + payload bytes.</li>



<li><strong>Python to LabVIEW:</strong> 4-byte big-endian payload size + payload bytes, since the message type defines the response.</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import json
from enum import Enum


class Lv2PyMessageType(Enum):
    ANALYZE_ANIMAL = 0
    FETCH_WEATHER = 1
    EXIT = 2


class LVSocket:
    def __init__(self, sock):
        self.sock = sock

    def await_message(self) -&gt; tuple&#91;Lv2PyMessageType, bytes&#93;:
        msg_type = Lv2PyMessageType(int.from_bytes(self._recv_exact(1), "big"))
        size = int.from_bytes(self._recv_exact(4), "big")
        return msg_type, self._recv_exact(size)

    def send_message(self, payload: bytes):
        self.sock.sendall(len(payload).to_bytes(4, "big"))
        self.sock.sendall(payload)

    def _recv_exact(self, n: int) -&gt; bytes:
        out = b""
        while len(out) &lt; n:
            chunk = self.sock.recv(n - len(out))
            if not chunk:
                raise ConnectionError("Socket closed")
            out += chunk
        return out</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> json</span></span>
<span class="line"><span style="color: #C586C0">from</span><span style="color: #D4D4D4"> enum </span><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> Enum</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Lv2PyMessageType</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">Enum</span><span style="color: #D4D4D4">):</span></span>
<span class="line"><span style="color: #D4D4D4">    ANALYZE_ANIMAL = </span><span style="color: #B5CEA8">0</span></span>
<span class="line"><span style="color: #D4D4D4">    FETCH_WEATHER = </span><span style="color: #B5CEA8">1</span></span>
<span class="line"><span style="color: #D4D4D4">    EXIT = </span><span style="color: #B5CEA8">2</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">LVSocket</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">__init__</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">sock</span><span style="color: #D4D4D4">):</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">.sock = sock</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">await_message</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">) -&gt; tuple&#91;Lv2PyMessageType, </span><span style="color: #4EC9B0">bytes</span><span style="color: #D4D4D4">&#93;:</span></span>
<span class="line"><span style="color: #D4D4D4">        msg_type = Lv2PyMessageType(</span><span style="color: #4EC9B0">int</span><span style="color: #D4D4D4">.from_bytes(</span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">._recv_exact(</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">), </span><span style="color: #CE9178">&quot;big&quot;</span><span style="color: #D4D4D4">))</span></span>
<span class="line"><span style="color: #D4D4D4">        size = </span><span style="color: #4EC9B0">int</span><span style="color: #D4D4D4">.from_bytes(</span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">._recv_exact(</span><span style="color: #B5CEA8">4</span><span style="color: #D4D4D4">), </span><span style="color: #CE9178">&quot;big&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> msg_type, </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">._recv_exact(size)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">send_message</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">payload</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">bytes</span><span style="color: #D4D4D4">):</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">.sock.sendall(</span><span style="color: #DCDCAA">len</span><span style="color: #D4D4D4">(payload).to_bytes(</span><span style="color: #B5CEA8">4</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;big&quot;</span><span style="color: #D4D4D4">))</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">.sock.sendall(payload)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">_recv_exact</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">n</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">int</span><span style="color: #D4D4D4">) -&gt; </span><span style="color: #4EC9B0">bytes</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">        out = </span><span style="color: #569CD6">b</span><span style="color: #CE9178">&quot;&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">while</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">len</span><span style="color: #D4D4D4">(out) &lt; n:</span></span>
<span class="line"><span style="color: #D4D4D4">            chunk = </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">.sock.recv(n - </span><span style="color: #DCDCAA">len</span><span style="color: #D4D4D4">(out))</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">not</span><span style="color: #D4D4D4"> chunk:</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #C586C0">raise</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">ConnectionError</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Socket closed&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">            out += chunk</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> out</span></span></code></pre></div>



<figure class="wp-block-image size-full is-resized"><img fetchpriority="high" decoding="async" width="577" height="220" src="https://static.dmcinfo.com/wp-content/uploads/2026/07/labview-python-over-tcp-1.png" alt="LabVIEW block diagram showing TCP/IP communication using TCP Read and Write functions, including payload handling, connection management, and error handling clusters." class="wp-image-47162" style="aspect-ratio:2.6228373702422147;width:839px;height:auto" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/07/labview-python-over-tcp-1.png 577w, https://static.dmcinfo.com/wp-content/uploads/2026/07/labview-python-over-tcp-1-300x114.png 300w" sizes="(max-width: 577px) 100vw, 577px" /></figure>



<h2 id="h-service-loops" class="wp-block-heading">Service Loops</h2>



<p class="wp-block-paragraph">The service itself is just a&nbsp;<code>while True</code>&nbsp;loop: wait for a typed message, branch on it, compute, and send the reply. This pattern keeps the service logic straightforward and maintainable.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import socket

PORT = 12345

def analyze_animal(payload):
    # parse the payload for this message and generate your response here
    ...

def fetch_weather(payload):
    # parse the payload for this message and generate your response here
    ...

def serve(sock):
    lv = LVSocket(sock)
    while True:
        msg_type, payload = lv.await_message()
        if msg_type == Lv2PyMessageType.ANALYZE_ANIMAL:
            result = analyze_animal(payload)
            lv.send_message(json.dumps(result).encode("utf-8"))
        elif msg_type == Lv2PyMessageType.FETCH_WEATHER:
            result = fetch_weather(payload)
            lv.send_message(json.dumps(result).encode("utf-8"))
        elif msg_type == Lv2PyMessageType.EXIT:
            break

if __name__ == "__main__":
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind(("localhost", PORT))
        s.listen()
        print("Waiting for LabVIEW to connect...")
        conn, addr = s.accept()
        print(f"Connected by {addr}")
        with conn:
            serve(conn)</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> socket</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">PORT = </span><span style="color: #B5CEA8">12345</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">analyze_animal</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">payload</span><span style="color: #D4D4D4">):</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955"># parse the payload for this message and generate your response here</span></span>
<span class="line"><span style="color: #D4D4D4">    ...</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">fetch_weather</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">payload</span><span style="color: #D4D4D4">):</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955"># parse the payload for this message and generate your response here</span></span>
<span class="line"><span style="color: #D4D4D4">    ...</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">serve</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">sock</span><span style="color: #D4D4D4">):</span></span>
<span class="line"><span style="color: #D4D4D4">    lv = LVSocket(sock)</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">while</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">True</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">        msg_type, payload = lv.await_message()</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> msg_type == Lv2PyMessageType.ANALYZE_ANIMAL:</span></span>
<span class="line"><span style="color: #D4D4D4">            result = analyze_animal(payload)</span></span>
<span class="line"><span style="color: #D4D4D4">            lv.send_message(json.dumps(result).encode(</span><span style="color: #CE9178">&quot;utf-8&quot;</span><span style="color: #D4D4D4">))</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">elif</span><span style="color: #D4D4D4"> msg_type == Lv2PyMessageType.FETCH_WEATHER:</span></span>
<span class="line"><span style="color: #D4D4D4">            result = fetch_weather(payload)</span></span>
<span class="line"><span style="color: #D4D4D4">            lv.send_message(json.dumps(result).encode(</span><span style="color: #CE9178">&quot;utf-8&quot;</span><span style="color: #D4D4D4">))</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">elif</span><span style="color: #D4D4D4"> msg_type == Lv2PyMessageType.EXIT:</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">break</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">__name__</span><span style="color: #D4D4D4"> == </span><span style="color: #CE9178">&quot;__main__&quot;</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">with</span><span style="color: #D4D4D4"> socket.socket(socket.AF_INET, socket.SOCK_STREAM) </span><span style="color: #C586C0">as</span><span style="color: #D4D4D4"> s:</span></span>
<span class="line"><span style="color: #D4D4D4">        s.bind((</span><span style="color: #CE9178">&quot;localhost&quot;</span><span style="color: #D4D4D4">, PORT))</span></span>
<span class="line"><span style="color: #D4D4D4">        s.listen()</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #DCDCAA">print</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Waiting for LabVIEW to connect...&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">        conn, addr = s.accept()</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #DCDCAA">print</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">f</span><span style="color: #CE9178">&quot;Connected by </span><span style="color: #569CD6">{</span><span style="color: #D4D4D4">addr</span><span style="color: #569CD6">}</span><span style="color: #CE9178">&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">with</span><span style="color: #D4D4D4"> conn:</span></span>
<span class="line"><span style="color: #D4D4D4">            serve(conn)</span></span></code></pre></div>



<p class="wp-block-paragraph">We can handle these messages from LabVIEW using the built-in TCP functions and pass a wide range of data in the message body.</p>



<h2 id="h-lifecycle-management" class="wp-block-heading">Lifecycle Management</h2>



<p class="wp-block-paragraph">The next step is to start and stop our Python script along with our LabVIEW code while gracefully handling errors and exits. We can use the&nbsp;<code>System Exec.vi</code>&nbsp;function to accomplish this, as shown in the snippet below.</p>



<p class="wp-block-paragraph">We can also safely manage the lifecycle of the Python script by, in addition to the EXIT message type, adding handling for socket disconnections by gracefully exiting the Python script if the socket is closed from the LabVIEW side. With this approach, operators can restart Python service, for any reason, by simply stopping and restarting the LabVIEW application, without needing to manage the Python process separately.</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img decoding="async" width="1174" height="541" src="https://static.dmcinfo.com/wp-content/uploads/2026/07/labview-python-over-tcp-2.png" alt="LabVIEW block diagram showing TCP/IP data reception for an Analyze Animal function, including external script execution via command line, resource management, and error handling using case structures." class="wp-image-47163" style="aspect-ratio:2.172079421271356;width:1200px;height:auto" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/07/labview-python-over-tcp-2.png 1174w, https://static.dmcinfo.com/wp-content/uploads/2026/07/labview-python-over-tcp-2-300x138.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/07/labview-python-over-tcp-2-1024x472.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/07/labview-python-over-tcp-2-768x354.png 768w" sizes="(max-width: 1174px) 100vw, 1174px" /></figure>



<h2 id="h-packaging-for-deployment" class="wp-block-heading">Packaging for Deployment</h2>



<p class="wp-block-paragraph">Finally, to deploy this architecture, package the Python script so it can be distributed alongside the LabVIEW build for client systems. This can be done with tools like PyInstaller, which creates a standalone executable from your Python script. You can then conditionally call this executable from LabVIEW using the&nbsp;<code>System Exec.vi</code>&nbsp;as shown below. This approach simplifies testing in development and deployment to production without needing to manage Python environments on the target machines.</p>



<h2 id="h-reliability-requirements-for-production" class="wp-block-heading">Reliability Requirements for Production</h2>



<p class="wp-block-paragraph">Industrial systems need graceful behavior under real-world conditions, including dropped connections, overloaded services, and operator restarts.</p>



<p class="wp-block-paragraph">Baseline reliability checklist includes:</p>



<ul class="wp-block-list">
<li>Request timeout and retry policy.</li>



<li>Reconnect strategy with back-off.</li>



<li>Health check or heartbeat message.</li>



<li>Structured logs on both sides.</li>



<li>Fail-safe startup and shutdown behavior.</li>



<li>Logging of exceptions and edge cases for postmortem analysis.</li>
</ul>



<p class="wp-block-paragraph">These patterns are essential for production readiness, and they also build trust with operations teams by ensuring the system won&#8217;t fail silently or require manual intervention.</p>



<h2 id="h-why-integrating-python-matters-for-ai-adoption-in-labview-teams" class="wp-block-heading">Why Integrating Python Matters for AI Adoption in LabVIEW Teams</h2>



<p class="wp-block-paragraph">This architecture also enables AI adoption. It allows teams to build key software components outside LabVIEW, where AI-assisted development can be faster due to:</p>



<ul class="wp-block-list">
<li>Rapid prototyping of analysis components.</li>



<li>Quick generation of helper tools and scripts.</li>



<li>Faster experimentation with algorithms and thresholds.</li>



<li>Simplified profiling and optimization workflows.</li>
</ul>



<p class="wp-block-paragraph">LabVIEW still governs system behavior; Python becomes the iteration engine. This division allows teams to leverage AI productively while preserving control-system rigor.</p>



<h2 id="h-key-takeaways" class="wp-block-heading">Key Takeaways</h2>



<ul class="wp-block-list">
<li>LabVIEW + Python over TCP is a reusable and extensible architecture pattern across many industrial use cases.</li>



<li>Keep LabVIEW for orchestration and UI; externalize high-change compute to Python.</li>



<li>The pattern creates a practical path for AI-assisted software development in industrial systems.</li>
</ul>



<p class="wp-block-paragraph">If you want to modernize an existing LabVIEW application, TCP-connected Python services are one of the highest-leverage places to start.</p>



<div class="wp-block-group alignwide has-custom-light-blue-background-color has-background is-layout-flow wp-container-core-group-is-layout-dbd34961 wp-block-group-is-layout-flow" style="border-radius:20px;margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50);padding-top:var(--wp--preset--spacing--50);padding-right:0;padding-bottom:var(--wp--preset--spacing--50);padding-left:0">
<div class="wp-block-columns alignwide are-vertically-aligned-center is-layout-flex wp-container-core-columns-is-layout-43efaee5 wp-block-columns-is-layout-flex" style="padding-right:var(--wp--preset--spacing--60);padding-left:var(--wp--preset--spacing--60)">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:85%">
<h3 class="wp-block-heading has-text-align-left" id="h-have-an-upcoming-project-dmc-can-help-you-take-the-next-step">Partner with DMC to accelerate your next LabVIEW project.</h3>



<p class="has-text-align-left wp-block-paragraph" id="h-need-help-turning-ideas-into-outcomes-automation-project-to-the-next-level-contact-us-today-to-learn-more-about-our-solutions-and-how-we-can-help-you-achieve-your-goals">DMC’s <a href="https://static.dmcinfo.com/services/test-and-measurement-automation/">Test &amp; Measurement </a>experts can help you design and deploy robust TCP-based architectures tailored to your application.</p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:15%">
<div class="wp-block-buttons is-horizontal is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-2236275c wp-block-buttons-is-layout-flex">
<div class="wp-block-button is-style-fill"><a class="wp-block-button__link has-base-contrast-color has-text-color has-link-color wp-element-button" href="https://static.dmcinfo.com/contact/">Contact Us</a></div>
</div>
</div>
</div>
</div>
<p>The post <a href="https://static.dmcinfo.com/blog/47156/labview-python-tcp-integration-architecture/">LabVIEW + Python Over TCP: A Reusable Architecture Pattern for Industrial Software</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Use Raspberry Pi as a DAQ Device in LabVIEW</title>
		<link>https://static.dmcinfo.com/blog/46615/raspberry-pi-labview-data-acquisition/</link>
		
		<dc:creator><![CDATA[Fadil Eledath]]></dc:creator>
		<pubDate>Wed, 08 Jul 2026 11:00:00 +0000</pubDate>
				<category><![CDATA[LabVIEW]]></category>
		<category><![CDATA[Test and Measurement Automation]]></category>
		<category><![CDATA[Automation]]></category>
		<category><![CDATA[Data Acquisition]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/?p=46615</guid>

					<description><![CDATA[<p>LabVIEW simplifies the process of quickly acquiring data from hardware and processing it into an output for application users. Compared to text-based programming languages, LabVIEW’s treatment of data flow as code makes tasks like parallel processing and hardware resource management much simpler. This is especially true when using hardware created by the group behind it, [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/46615/raspberry-pi-labview-data-acquisition/">How to Use Raspberry Pi as a DAQ Device in LabVIEW</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">LabVIEW simplifies the process of quickly acquiring data from hardware and processing it into an output for application users. Compared to text-based programming languages, LabVIEW’s treatment of data flow as code makes tasks like parallel processing and hardware resource management much simpler. This is especially true when using hardware created by the group behind it, National Instruments. Their DAQ (data acquisition) devices are designed for ease of use in the LabVIEW programming environment and can be configured and used in code to effectively replace typical workbench equipment.</p>



<p class="wp-block-paragraph">There are, of course, other types of hardware that someone might want to acquire data from. Our clients often need us to integrate all kinds of equipment based on their technical and budget needs from a specialized hipot meter to a general-purpose DMM. One such client needed us to integrate a Raspberry Pi, which was collecting data from a set of sensors, with a LabVIEW app that was already collecting data from NI hardware.</p>



<p class="wp-block-paragraph">Raspberry Pis are pretty popular among hobbyists and hardware engineers alike for prototyping and actual production use since they’re inexpensive, fairly robust, and have a broad ecosystem of products that work nicely with them. Their GPIO (General Purpose Input/Output) pins make it easy to interface with hardware over different low-level protocols like SPI or I2C. The boards also come with WiFi, Bluetooth, Ethernet, and USB if you need to connect to something over a higher-level interface. This brings us to the question of how to use a Raspberry Pi in a way that is fast and reliable from a LabVIEW application running on a separate PC—effectively using it as a DAQ device.</p>



<h2 id="h-the-easy-way-fastapi" class="wp-block-heading">The Easy Way: FastAPI</h2>



<p class="wp-block-paragraph">Let&#8217;s begin with a simple approach—we can set up a minimal REST API service on the Raspberry Pi using FastAPI that, when queried, creates and returns your measurement.</p>



<h3 id="h-setting-up-the-api" class="wp-block-heading">Setting Up the API</h3>



<p class="wp-block-paragraph">FastAPI is a modern Python web framework that makes it simple to create REST APIs. On your Raspberry Pi, you can install it along with a production server like <code>uvicorn</code>:</p>



<p class="wp-block-paragraph"><code>pip install fastapi uvicorn</code></p>



<p class="wp-block-paragraph">Then create a simple API endpoint that reads from your sensor:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>from fastapi import FastAPI
import time

app = FastAPI()

@app.get("/sensor/read")
def read_sensor():
    # Your sensor reading code here
    # For example, reading from an I2C device
    value = read_i2c_sensor()
    timestamp = time.time()

    return {
        "value": value,
        "timestamp": timestamp
    }

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">from</span><span style="color: #D4D4D4"> fastapi </span><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> FastAPI</span></span>
<span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> time</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">app = FastAPI()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #DCDCAA">@app.get</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;/sensor/read&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">read_sensor</span><span style="color: #D4D4D4">():</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955"># Your sensor reading code here</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955"># For example, reading from an I2C device</span></span>
<span class="line"><span style="color: #D4D4D4">    value = read_i2c_sensor()</span></span>
<span class="line"><span style="color: #D4D4D4">    timestamp = time.time()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #CE9178">&quot;value&quot;</span><span style="color: #D4D4D4">: value,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #CE9178">&quot;timestamp&quot;</span><span style="color: #D4D4D4">: timestamp</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">__name__</span><span style="color: #D4D4D4"> == </span><span style="color: #CE9178">&quot;__main__&quot;</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> uvicorn</span></span>
<span class="line"><span style="color: #D4D4D4">    uvicorn.run(app, </span><span style="color: #9CDCFE">host</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;0.0.0.0&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">port</span><span style="color: #D4D4D4">=</span><span style="color: #B5CEA8">8000</span><span style="color: #D4D4D4">)</span></span></code></pre></div>



<p class="wp-block-paragraph">From LabVIEW, you can use the HTTP Client VIs to make GET requests to <code>&lt;http://your-pi-ip:8000/sensor/read&gt;</code> and parse the JSON response to extract your sensor data.</p>



<figure class="wp-block-image size-full"><img decoding="async" width="620" height="269" src="https://static.dmcinfo.com/wp-content/uploads/2026/07/raspberry-pi-with-labview-application-1.png" alt="LabVIEW block diagram performing an HTTP GET request to a Raspberry Pi, parsing JSON sensor data, timestamping results, and processing output with built-in error handling." class="wp-image-46842" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/07/raspberry-pi-with-labview-application-1.png 620w, https://static.dmcinfo.com/wp-content/uploads/2026/07/raspberry-pi-with-labview-application-1-300x130.png 300w" sizes="(max-width: 620px) 100vw, 620px" /></figure>



<h3 id="h-the-issues" class="wp-block-heading">The Issues</h3>



<p class="wp-block-paragraph">While this approach is straightforward and gets you up and running quickly, it has some significant limitations for serious data acquisition:</p>



<p class="wp-block-paragraph"><strong>Not Truly Time-Series</strong>: Each request creates a new measurement on demand. If you&#8217;re trying to capture a continuous stream of data, you&#8217;ll miss all the samples between requests. This is fine for slow-changing values like temperature readings every few seconds, but inadequate for high-speed data acquisition.</p>



<p class="wp-block-paragraph"><strong>Request Overhead</strong>: Every HTTP request involves substantial overhead, TCP handshaking, HTTP headers, JSON serialization/deserialization, and network latency. If you need to sample at high rates (say, 1000 samples per second), making 1000 individual HTTP requests per second is inefficient and will likely introduce timing jitter and missed samples.</p>



<p class="wp-block-paragraph">For applications where you need occasional readings or the data changes slowly, this approach works great. But for continuous, high-speed data acquisition, we need something better.</p>



<h2 id="h-the-high-speed-way-redis-streams" class="wp-block-heading">The High-Speed Way: Redis Streams</h2>



<p class="wp-block-paragraph">Redis is an in-memory data structure store that&#8217;s fast and supports various data types, including streams &#8211; perfect for time-series data collection. The architecture here is more sophisticated but provides much better performance:</p>



<p class="wp-block-paragraph">In production, you may implement the sensor loop in C or another lower-level language for tighter timing, but Python is a good way to understand and prototype the architecture.</p>



<ol class="wp-block-list">
<li>A Python process on the Raspberry Pi continuously reads sensors and writes to a Redis stream.</li>



<li>Redis stores the data in memory as a time-ordered stream.</li>



<li>LabVIEW periodically reads from the stream, getting batches of new data.</li>



<li>Webdis provides an HTTP interface to Redis, making it accessible from LabVIEW.</li>
</ol>



<h3 id="h-setting-up-redis" class="wp-block-heading">Setting Up Redis</h3>



<p class="wp-block-paragraph">First, install Redis on your Raspberry Pi:</p>



<p class="wp-block-paragraph"><code>sudo apt-get install redis-server</code></p>



<p class="wp-block-paragraph">Install the Python Redis client as well:</p>



<p class="wp-block-paragraph"><code>pip install redis</code></p>



<p class="wp-block-paragraph">Configure Redis to start on boot and ensure it&#8217;s listening on the network if your LabVIEW application is on a different machine:</p>



<p class="wp-block-paragraph"><code>sudo systemctl enable redis-server<br>sudo systemctl start redis-server</code></p>



<h3 id="h-the-data-collection-process" class="wp-block-heading">The Data Collection Process</h3>



<p class="wp-block-paragraph">Create a Python script that continuously reads your sensors and writes to a Redis stream:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import time
import json
import redis

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

def collect_data():
    interval = 0.001  # 1ms = 1000 Hz sampling
    cycle_time = time.time()
    while True:

        # Read your sensor
        sensor_value = read_i2c_sensor()

        # Add to Redis stream
        r.xadd('sensor_stream', {
            'value': sensor_value,
            'timestamp': cycle_time
        })

        # Delay enough to start next loop at the interval rate
        time_to_next_cycle = cycle_time + interval - time.time()
        sleep_time = max(0, time_to_next_cycle)
        time.sleep(sleep_time)
        cycle_time += interval

if __name__ == "__main__":
    collect_data()</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> time</span></span>
<span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> json</span></span>
<span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> redis</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">r = redis.Redis(</span><span style="color: #9CDCFE">host</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&apos;localhost&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">port</span><span style="color: #D4D4D4">=</span><span style="color: #B5CEA8">6379</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">decode_responses</span><span style="color: #D4D4D4">=</span><span style="color: #569CD6">True</span><span style="color: #D4D4D4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">collect_data</span><span style="color: #D4D4D4">():</span></span>
<span class="line"><span style="color: #D4D4D4">    interval = </span><span style="color: #B5CEA8">0.001</span><span style="color: #D4D4D4">  </span><span style="color: #6A9955"># 1ms = 1000 Hz sampling</span></span>
<span class="line"><span style="color: #D4D4D4">    cycle_time = time.time()</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">while</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">True</span><span style="color: #D4D4D4">:</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955"># Read your sensor</span></span>
<span class="line"><span style="color: #D4D4D4">        sensor_value = read_i2c_sensor()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955"># Add to Redis stream</span></span>
<span class="line"><span style="color: #D4D4D4">        r.xadd(</span><span style="color: #CE9178">&apos;sensor_stream&apos;</span><span style="color: #D4D4D4">, {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #CE9178">&apos;value&apos;</span><span style="color: #D4D4D4">: sensor_value,</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #CE9178">&apos;timestamp&apos;</span><span style="color: #D4D4D4">: cycle_time</span></span>
<span class="line"><span style="color: #D4D4D4">        })</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #6A9955"># Delay enough to start next loop at the interval rate</span></span>
<span class="line"><span style="color: #D4D4D4">        time_to_next_cycle = cycle_time + interval - time.time()</span></span>
<span class="line"><span style="color: #D4D4D4">        sleep_time = </span><span style="color: #DCDCAA">max</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">, time_to_next_cycle)</span></span>
<span class="line"><span style="color: #D4D4D4">        time.sleep(sleep_time)</span></span>
<span class="line"><span style="color: #D4D4D4">        cycle_time += interval</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">__name__</span><span style="color: #D4D4D4"> == </span><span style="color: #CE9178">&quot;__main__&quot;</span><span style="color: #D4D4D4">:</span></span>
<span class="line"><span style="color: #D4D4D4">    collect_data()</span></span></code></pre></div>



<p class="wp-block-paragraph">This process runs independently, continuously writing data to the stream regardless of whether anyone is reading it. Redis handles the buffering and ensures data isn&#8217;t lost.</p>



<h3 id="h-accessing-redis-from-labview-with-webdis" class="wp-block-heading">Accessing Redis from LabVIEW with Webdis</h3>



<p class="wp-block-paragraph">Webdis is a simple web server that provides an HTTP interface to Redis. Install it on your Raspberry Pi:</p>



<p class="wp-block-paragraph"><code>git clone https://github.com/nicolasff/webdis.git<br>cd webdis<br>make<br>./webdis &amp;</code></p>



<p class="wp-block-paragraph">Now you can access Redis commands via HTTP. From LabVIEW, you can read from the stream using the HTTP Client VIs to make requests like:</p>



<p class="wp-block-paragraph"><code>http://your-pi-ip:7379/XREAD/COUNT/&lt;count>/STREAMS/sensor_stream/$</code></p>



<p class="wp-block-paragraph">The <code>$</code> special ID means &#8220;only entries added after this request begins&#8221; &#8211; it&#8217;s useful for your initial read when you want to ignore old buffered samples and start with future data. For subsequent reads to get all new data since your last read, you&#8217;ll need to use the actual stream ID you received from the previous read instead of <code>$</code>.</p>



<p class="wp-block-paragraph">The <code>&lt;count&gt;</code> value represents the maximum number of entries to return. Based on your sample rate and how often you want to read buffered data from the device, you will want to modify this value to keep up with data production on the Raspberry Pi.</p>



<h3 id="h-labview-implementation" class="wp-block-heading">LabVIEW Implementation</h3>



<p class="wp-block-paragraph">In your LabVIEW VI:</p>



<ol class="wp-block-list">
<li>Store the last stream ID you read (starting with &#8220;$&#8221;).</li>



<li>Periodically poll the stream using XREAD with your last ID.</li>



<li>Parse the JSON response to extract the sensor values.</li>



<li>Update your last stream ID for the next request.</li>



<li>Process the batch of samples.</li>
</ol>



<figure class="wp-block-image size-full"><img decoding="async" width="926" height="404" src="https://static.dmcinfo.com/wp-content/uploads/2026/07/raspberry-pi-with-labview-application-2.png" alt="LabVIEW block diagram showing an HTTP GET request to a Raspberry Pi, with JSON parsing and a loop for processing response data, including error handling clusters." class="wp-image-46843" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/07/raspberry-pi-with-labview-application-2.png 926w, https://static.dmcinfo.com/wp-content/uploads/2026/07/raspberry-pi-with-labview-application-2-300x131.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/07/raspberry-pi-with-labview-application-2-768x335.png 768w" sizes="(max-width: 926px) 100vw, 926px" /></figure>



<p class="wp-block-paragraph">This approach dramatically reduces overhead; instead of 1000 requests per second for 1000 samples, you might make 10 requests per second and get 100 samples each time.</p>



<h3 id="h-advantages" class="wp-block-heading">Advantages</h3>



<ul class="wp-block-list">
<li><strong>Continuous Collection: </strong>The Python process collects data continuously without gaps.</li>



<li><strong>Buffering:</strong> Redis buffers recent data in memory, so if LabVIEW is briefly busy, samples remain available.</li>



<li><strong>Batch Processing: </strong>LabVIEW can read multiple samples per request, reducing overhead.</li>
</ul>



<h3 id="h-adding-time-synchronization" class="wp-block-heading">Adding Time Synchronization</h3>



<p class="wp-block-paragraph">When combining data from multiple sources (like your NI DAQ and Raspberry Pi), timestamp synchronization becomes critical. The Raspberry Pi&#8217;s clock and your Windows PC&#8217;s clock will drift apart over time, making it difficult to properly align and correlate data.</p>



<p class="wp-block-paragraph">There are several approaches to tackle this problem:</p>



<p class="wp-block-paragraph"><strong>Measuring Clock Offset:</strong> Create a calibration routine in which LabVIEW requests the current time from the Pi and measures the round-trip time to calculate the clock offset. Apply this offset to align timestamps. Keep in mind that different hardware clocks will experience drift and you will need to regularly re-calibrate to account for this.</p>



<p class="wp-block-paragraph"><strong>Network Time Protocol (NTP):</strong> Configure both systems to sync with the same NTP server. This gets you in the ballpark but won&#8217;t give you perfect alignment due to network delays and update intervals.</p>



<h2 id="h-conclusion" class="wp-block-heading">Conclusion</h2>



<p class="wp-block-paragraph">Integrating a Raspberry Pi as a data acquisition device in your LabVIEW application enables cost-effective, flexible hardware integration. Your chosen approach depends on your requirements.</p>



<p class="wp-block-paragraph" style="padding-bottom:var(--wp--preset--spacing--40)">For our client&#8217;s application, we used the Redis approach with time synchronization to integrate their Raspberry Pi sensors with their LabVIEW system, achieving reliable 1 kHz data collection aligned with their NI DAQ data.</p>



<div class="wp-block-group alignwide has-custom-light-blue-background-color has-background is-layout-flow wp-container-core-group-is-layout-dbd34961 wp-block-group-is-layout-flow" style="border-radius:20px;margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50);padding-top:var(--wp--preset--spacing--50);padding-right:0;padding-bottom:var(--wp--preset--spacing--50);padding-left:0">
<div class="wp-block-columns alignwide are-vertically-aligned-center is-layout-flex wp-container-core-columns-is-layout-43efaee5 wp-block-columns-is-layout-flex" style="padding-right:var(--wp--preset--spacing--60);padding-left:var(--wp--preset--spacing--60)">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:85%">
<h3 class="wp-block-heading has-text-align-left" id="h-have-an-upcoming-project-dmc-can-help-you-take-the-next-step">Have an upcoming project? DMC can help you take the next step.</h3>



<p class="has-text-align-left wp-block-paragraph" id="h-need-help-turning-ideas-into-outcomes-automation-project-to-the-next-level-contact-us-today-to-learn-more-about-our-solutions-and-how-we-can-help-you-achieve-your-goals">Whether integrating Raspberry Pis, NI DAQs, mixed or specialized hardware, DMC can help build robust <a href="https://static.dmcinfo.com/services/test-and-measurement-automation/">test and measurement systems</a>.</p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:15%">
<div class="wp-block-buttons is-horizontal is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-2236275c wp-block-buttons-is-layout-flex">
<div class="wp-block-button is-style-fill"><a class="wp-block-button__link has-base-contrast-color has-text-color has-link-color wp-element-button" href="https://static.dmcinfo.com/contact/">Contact Us</a></div>
</div>
</div>
</div>
</div>
<p>The post <a href="https://static.dmcinfo.com/blog/46615/raspberry-pi-labview-data-acquisition/">How to Use Raspberry Pi as a DAQ Device in LabVIEW</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>3 Ways AI Improves Real-Time Vision Inspection with Python</title>
		<link>https://static.dmcinfo.com/blog/46368/ai-real-time-vision-inspection-python-optimization/</link>
		
		<dc:creator><![CDATA[Fadil Eledath]]></dc:creator>
		<pubDate>Thu, 02 Jul 2026 11:00:00 +0000</pubDate>
				<category><![CDATA[Test and Measurement Automation]]></category>
		<category><![CDATA[AI]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Vision Inspection]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/?p=46368</guid>

					<description><![CDATA[<p>Real-time vision inspection has an unforgiving constraint: every product on the conveyor has to be analyzed before the next set of frames arrives. A pipeline that&#8217;s accurate but too slow is just as unusable as one that&#8217;s fast but wrong. Getting both requires deliberate optimization, which has historically been difficult, but using AI and Python [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/46368/ai-real-time-vision-inspection-python-optimization/">3 Ways AI Improves Real-Time Vision Inspection with Python</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Real-time vision inspection has an unforgiving constraint: every product on the conveyor has to be analyzed before the next set of frames arrives. A pipeline that&#8217;s accurate but too slow is just as unusable as one that&#8217;s fast but wrong. Getting both requires deliberate optimization, which has historically been difficult, but using AI and Python together can make it much easier.</p>



<p class="wp-block-paragraph">Here are three practical ways DMC has used AI and Python to optimize a real-time inspection system and deliver real results to our clients.</p>



<h2 id="h-1-profile-your-pipeline-to-find-the-bottlenecks" class="wp-block-heading">1. Profile Your Pipeline to Find the Bottlenecks</h2>



<p class="wp-block-paragraph">As you move from implementation to production, the slower parts of your code can become painful while remaining hidden. In fact, slow lines of code can show up in surprising places and, depending on how your pipeline is structured, have an outsized effect on your speed. The solution is to bring a magnifying glass to your code by profiling it.</p>



<figure class="wp-block-image alignleft size-full is-resized has-custom-border" style="margin-bottom:var(--wp--preset--spacing--50)"><img decoding="async" width="458" height="250" src="https://static.dmcinfo.com/wp-content/uploads/2026/06/optimizing-python-vision-inspection-system-1.png" alt="Generated image of a conveyor belt with a robotic sensor and an HMI screen showing defects on a tortilla on the conveyor belt." class="wp-image-46382" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px;width:500px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/06/optimizing-python-vision-inspection-system-1.png 458w, https://static.dmcinfo.com/wp-content/uploads/2026/06/optimizing-python-vision-inspection-system-1-300x164.png 300w" sizes="(max-width: 458px) 100vw, 458px" /></figure>



<p class="wp-block-paragraph">I’ve been a heavy user of Jupyter notebooks inside VS Code, which allows me to use the AI coding agent of my choice to easily set up and run experiments against my inspection pipeline to understand how it runs in practice against specific examples and, more relevantly, how long each section of the pipeline takes to run.</p>



<p class="wp-block-paragraph">For example, one recent system DMC worked on involved belt masking. A client needed to inspect yellow tortillas moving down a blue conveyor belt, so we had to isolate each tortilla on each frame, separating it from the background for later steps in our analysis. The naive-but-useful approach we started with computed a full-resolution color distance, each pixel relative to the average color of the belt. This gave us a reference for what a functionally correct approach looks like, but profiling the code in conversation with AI showed us that it took up the majority of its time analyzing each frame.</p>



<h2 id="h-2-redesign-expensive-metrics-to-be-computationally-cheaper" class="wp-block-heading">2. Redesign Expensive Metrics to be Computationally Cheaper</h2>



<p class="wp-block-paragraph">Once you know which stage is slow, the next lever is the algorithms inside it. Many vision metrics have a naive implementation that&#8217;s accurate but expensive, and a smarter implementation that&#8217;s nearly as accurate and far faster.</p>



<figure class="wp-block-image alignright size-full is-resized has-custom-border" style="margin-bottom:var(--wp--preset--spacing--60)"><img decoding="async" width="458" height="250" src="https://static.dmcinfo.com/wp-content/uploads/2026/06/optimizing-python-vision-inspection-system-2.png" alt="Generated image of a blue conveyor belt with with tortillas." class="wp-image-46384" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px;aspect-ratio:1.8320261063720158;width:500px;height:auto" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/06/optimizing-python-vision-inspection-system-2.png 458w, https://static.dmcinfo.com/wp-content/uploads/2026/06/optimizing-python-vision-inspection-system-2-300x164.png 300w" sizes="(max-width: 458px) 100vw, 458px" /></figure>



<p class="wp-block-paragraph">After profiling the inspection system, we looked for ways to optimize it. A faster approach involved shrinking the frame by 50% on each side to reduce the number of computations by 75%. Comparing against the full-frame approach, we saw minimal reduction in accuracy. Additionally, by computing distances, we were performing a square root operation before comparing the distance to a threshold parameter; we could ditch that operation by squaring the threshold parameter and then comparing the squared distance, which sped up the process even further with no reduction in accuracy.</p>



<p class="wp-block-paragraph">Eventually, we switched from checking RGB color distance to an axis-aligned range, which had similar accuracy while drastically reducing the computational load per frame. AI was vital here for quickly brainstorming and drafting these alternative formulations so we could compare their performance and accuracy.</p>



<h2 id="h-3-use-synthetic-data-to-stress-test-thresholds-and-edge-cases" class="wp-block-heading">3. Use Synthetic Data to Stress-Test Thresholds and Edge Cases</h2>



<p class="wp-block-paragraph">Optimization isn&#8217;t only about speed; it&#8217;s also about ensuring the system remains correct under pressure and across the full range of inputs. It&#8217;s hard to verify with only a handful of real images, which is often the case at the pre-deployment stage of a project.</p>



<figure class="wp-block-image alignleft size-full is-resized has-custom-border" style="margin-bottom:var(--wp--preset--spacing--50)"><img decoding="async" width="608" height="250" src="https://static.dmcinfo.com/wp-content/uploads/2026/06/optimizing-python-vision-inspection-system-3.png" alt="" class="wp-image-46385" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px;width:500px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/06/optimizing-python-vision-inspection-system-3.png 608w, https://static.dmcinfo.com/wp-content/uploads/2026/06/optimizing-python-vision-inspection-system-3-300x123.png 300w" sizes="(max-width: 608px) 100vw, 608px" /></figure>



<p class="wp-block-paragraph">Synthetic data can fill this gap. By generating test subjects across the entire pass/fail spectrum, including rare failure modes, you can stress-test thresholds and confirm the optimized pipeline still behaves correctly on edge cases.</p>



<p class="wp-block-paragraph">In our example of the tortilla inspection project, we used a Python script to programmatically generate AI images of tortillas with different levels and types of defects by varying the prompts. For example, each tortilla prompt was given a probability of having a prompt addition specifying a torn edge or a burn mark. After generating 100 tortillas, we used another script to create simulated footage of these tortillas moving down a looping belt texture, with added random noise and motion blur, which we then used to verify our pipeline&#8217;s capabilities before we had any real footage to work with. These efforts allowed us to deploy our code into production quickly for our client.</p>



<h2 id="h-conclusion" class="wp-block-heading">Conclusion</h2>



<p class="wp-block-paragraph">These threThese three practices work together to create a more efficient and reliable system:</p>



<ul class="wp-block-list">
<li><strong>Profiling</strong> identifies where to focus your optimization efforts.</li>



<li><strong>Metric redesign</strong> speeds up slow stages exposed by your profiling.</li>



<li><strong>Synthetic data</strong> ensures your system remains accurate across edge cases.</li>
</ul>



<p class="wp-block-paragraph">Each of these stages has always been essential to vision inspection, but AI and modern coding tools have made them faster and easier to implement. Additionally, unlike certain uses of AI that have greater exposure to risk, like code implementation, using AI here is less likely to cause major faults in your system. That said, AI should be an accelerator, not a replacement for engineering judgment. It is essential to verify that AI-generated code is correct and aligned with system requirements.</p>



<p class="wp-block-paragraph">Ultimately, the impact of these practices is clear. By combining these approaches, you can confidently improve the speed and reliability of your real-time vision inspection pipeline.</p>



<div class="wp-block-group alignwide has-custom-light-blue-background-color has-background is-layout-flow wp-container-core-group-is-layout-dbd34961 wp-block-group-is-layout-flow" style="border-radius:20px;margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50);padding-top:var(--wp--preset--spacing--50);padding-right:0;padding-bottom:var(--wp--preset--spacing--50);padding-left:0">
<div class="wp-block-columns alignwide are-vertically-aligned-center is-layout-flex wp-container-core-columns-is-layout-43efaee5 wp-block-columns-is-layout-flex" style="padding-right:var(--wp--preset--spacing--60);padding-left:var(--wp--preset--spacing--60)">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:85%">
<h3 class="wp-block-heading has-text-align-left" id="h-have-an-upcoming-project-dmc-can-help-you-take-the-next-step">Kicking off a Vision Inspection project? DMC can help you take the next step!</h3>



<p class="has-text-align-left wp-block-paragraph" id="h-need-help-turning-ideas-into-outcomes-automation-project-to-the-next-level-contact-us-today-to-learn-more-about-our-solutions-and-how-we-can-help-you-achieve-your-goals">Contact DMC&#8217;s <a href="https://static.dmcinfo.com/services/test-and-measurement-automation/">Test &amp; Measurement</a> team today to learn more about our experience in <a href="https://static.dmcinfo.com/services/manufacturing-automation-and-intelligence/vision-inspection/" id="494">vision inspection systems</a> and how we can help you achieve your goals.</p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:15%">
<div class="wp-block-buttons is-horizontal is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-2236275c wp-block-buttons-is-layout-flex">
<div class="wp-block-button is-style-fill"><a class="wp-block-button__link has-base-contrast-color has-text-color has-link-color wp-element-button" href="https://static.dmcinfo.com/contact/">Contact Us</a></div>
</div>
</div>
</div>
</div>
<p>The post <a href="https://static.dmcinfo.com/blog/46368/ai-real-time-vision-inspection-python-optimization/">3 Ways AI Improves Real-Time Vision Inspection with Python</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Using a QAbstractListModel in QML</title>
		<link>https://static.dmcinfo.com/blog/17671/using-a-qabstractlistmodel-in-qml/</link>
		
		<dc:creator><![CDATA[Mark Locascio]]></dc:creator>
		<pubDate>Mon, 27 Mar 2023 09:36:10 +0000</pubDate>
				<category><![CDATA[Application Development]]></category>
		<category><![CDATA[PC Application Development]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Qt]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/17671/using-a-qabstractlistmodel-in-qml/</guid>

					<description><![CDATA[<p>The QAbstractListModel class provided by Qt can be used to organize data that will be presented visually as a list or table. Standardizing the interface with an abstract class like QAbstractListModel makes it easy to keep your model data completely isolated from your view (a software design principle known as &#8220;separation of concerns&#8220;). That abstraction [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/17671/using-a-qabstractlistmodel-in-qml/">Using a QAbstractListModel in QML</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">The <code><a href="https://doc.qt.io/qt-6/qabstractlistmodel.html">QAbstractListModel</a></code> class provided by Qt can be used to organize data that will be presented visually as a list or table. Standardizing the interface with an abstract class like <code>QAbstractListModel</code> makes it easy to keep your model data completely isolated from your view (a software design principle known as &#8220;<a href="https://csrc.nist.gov/glossary/term/separation_of_concerns">separation of concerns</a>&#8220;). That abstraction makes it a powerful and flexible tool, but it also makes the learning curve steep.</p>



<p class="wp-block-paragraph">The goal of this post is to provide concrete examples, explanations, and definitions of terms so you can more easily make use of the <code>QAbstractListModel</code> class. For your reference, you can see the complete <a href="https://github.com/marklocascio/qml-listmodel-example">example code on GitHub</a>.</p>



<h2 class="wp-block-heading" id="h-example-gui">Example GUI</h2>



<p class="wp-block-paragraph">Let&#8217;s say we&#8217;ve got a list of devices with which our software interacts. The data we&#8217;ve got for each device is:</p>



<ul class="wp-block-list">
<li>A human-readable name (a string)</li>



<li>A serial number (an integer)</li>



<li>Whether or not the device is currently connected (a Boolean)</li>
</ul>



<p class="wp-block-paragraph">Our example GUI will look like this:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/app.png" alt="Example GUI"/></figure>



<p class="wp-block-paragraph">Part of the appeal of Qt is that you can make extremely slick UIs. We will not be doing that here in order to keep the focus on listmodel concepts. I&#8217;ve resisted the urge to add eye candy for the sake of clarity, and I have crafted the example to make it clear how you <strong><em>could</em></strong> stylize the list if you wanted to.</p>



<p class="wp-block-paragraph">Additionally, the example uses Qt&#8217;s Python bindings (<a href="https://pypi.org/project/PySide6/">PySide6</a>). Everything here is equally applicable to C++, but again, for the sake of simplicity, it is presented as a Python application. The QML is identical in both cases.</p>



<h2 class="wp-block-heading" id="h-the-qml-description">The QML Description</h2>



<p class="wp-block-paragraph">First, we&#8217;ll describe the visualization of our list of devices in QML. The <a href="https://doc.qt.io/qt-6/qml-qtquick-listview.html">QML <code>ListView</code> class</a> is a great start. We&#8217;ll set three properties:</p>



<ul class="wp-block-list">
<li>
<p class="wp-block-paragraph"><code><span style="color:#c00000">model</span></code>: this is what we&#8217;ll use to bind the QML <code>ListView</code> to a <code>QAbstractListModel</code> class defined in C++ or Python</p>
</li>



<li><code><span style="color:#2f5496">delegate</span></code>: this is used to define how each item in the list is rendered as a QML object</li>



<li><code><span style="color:#bf8f00">highlight</span></code>: this is not necessary to use a <code>ListView</code>, but it is generally useful to visualize a selected item in the list</li>
</ul>



<h2 class="wp-block-heading" id="h-basic-example">Basic Example</h2>



<p class="wp-block-paragraph">A rough first draft of the QML might look like this (for the final version, <a href="https://github.com/marklocascio/qml-listmodel-example/blob/main/sample/main.qml">see here</a>):</p>



<p class="wp-block-paragraph"><code><span style="white-space: nowrap;">ListView {<br>
&nbsp;&nbsp;&nbsp;&nbsp;id: deviceList<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#c00000">model: controller.listmodel</span><br>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#2f5496">delegate: Item {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;width: deviceList.width<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Text {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text: "Placeholder"<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MouseArea {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;anchors.fill: parent<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>
&nbsp;&nbsp;&nbsp;&nbsp;}</span>&nbsp;&nbsp;<span style="color:#538135">// Item delegate</span><br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#bf8f00">highlight: Rectangle { color: "lightBlue" }</span><br>
}&nbsp;&nbsp;<span style="color:#538135">// ListView</span></span></code></p>



<h2 class="wp-block-heading" id="h-using-the-model-and-the-delegate">Using the Model and the Delegate</h2>



<p class="wp-block-paragraph">In my <code>main()</code> function, I <a href="https://github.com/marklocascio/qml-listmodel-example/blob/main/sample/__main__.py#L18">set a context property</a> called <code>controller</code> that refers to an instance of my <code>Controller</code> class:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>qml_app_engine = QQmlApplicationEngine()
qml_context = qml_app_engine.rootContext()
controller = Controller(parent=app)
qml_context.setContextProperty("controller", controller)</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">qml_app_engine = QQmlApplicationEngine()</span></span>
<span class="line"><span style="color: #D4D4D4">qml_context = qml_app_engine.rootContext()</span></span>
<span class="line"><span style="color: #D4D4D4">controller = Controller(</span><span style="color: #9CDCFE">parent</span><span style="color: #D4D4D4">=app)</span></span>
<span class="line"><span style="color: #D4D4D4">qml_context.setContextProperty(</span><span style="color: #CE9178">&quot;controller&quot;</span><span style="color: #D4D4D4">, controller)</span></span></code></pre></div>



<p class="wp-block-paragraph">The <code>Controller</code> class <a href="https://github.com/marklocascio/qml-listmodel-example/blob/main/sample/controller.py#L12">exposes a Qt property</a> called <code>listmodel</code>. Note that that property is declared as a <code>QObject</code> in my Python code, and that it does not need a property change signal (i.e., I use <code>constant=True</code>). In the QML above, I bind the <code>ListView</code>&#8216;s <code>model</code> property to the <code>listmodel</code> property of my <code>controller</code> object:</p>



<p class="wp-block-paragraph"><code><span style="color:#c00000">model: controller.listmodel</span></code></p>



<p class="wp-block-paragraph">The <code><span style="color:#2f5496">delegate</span></code> property of <code>ListView</code> is like a template that defines how each item in the list is rendered as a QML object. For the sake of demonstration, we&#8217;ll keep it simple here. I made it a QML <code><a href="https://doc.qt.io/qt-6/qml-qtquick-item.html">Item</a></code> that is as wide as the <code>ListView</code> itself and contains a <code><a href="https://doc.qt.io/qt-6/qml-qtquick-text.html">Text</a></code> object and a <code><a href="https://doc.qt.io/qt-6/qml-qtquick-mousearea.html">MouseArea</a></code>, but you can make it anything you like (you&#8217;ll generally make it much fancier)! For example, you might instead have something like a <code><a href="https://doc.qt.io/qt-6/qml-qtquick-layouts-rowlayout.html">RowLayout</a></code> containing a <code><a href="https://doc.qt.io/qt-6/qml-qtquick-controls2-checkbox.html">Checkbox</a></code>, an <code><a href="https://doc.qt.io/qt-6/qml-qtquick-image.html">Image</a></code>, and a <code><a href="https://doc.qt.io/qt-6/qml-qtquick-text.html">Text</a></code>. (Haven&#8217;t used layouts yet? <a href="https://static.dmcinfo.com/latest-thinking/blog/id/10393/resizing-uis-with-qml-layouts">Start here!</a>) However you want each item in your list to be visualized, you can define it in your <code><span style="color:#2f5496">delegate</span></code>. For simplicity, I often start by just rendering it all in a <code>Text</code> item. We&#8217;ll look at how to access each item of data (name, serial number, and connection status) in the next section.</p>



<p class="wp-block-paragraph">Note also that the <code>MouseArea</code> in my <code><span style="color:#2f5496">delegate</span></code> is used to select an item in the list. Each item in the list is instantiated as a <code><span style="color:#2f5496">delegate</span></code> object, so each item in the list has a <code>MouseArea</code> that can handle click events. We&#8217;ll look at this in more detail later also.</p>



<h2 class="wp-block-heading" id="h-the-qabstractlistmodel-class">The QAbstractListModel Class</h2>



<p class="wp-block-paragraph">If you are managing a large quantity of data and you want to visualize it on your QML GUI, you have a few options. For simple cases, a <a href="https://doc.qt.io/qt-6/qml-qtquick-repeater.html"><code>Repeater</code></a> can usually get the job done just fine and is conceptually very easy to grasp. However, for very large lists, <a href="https://doc.qt.io/qt-6/qml-qtquick-repeater.html#considerations-when-using-repeater">a <code>Repeater</code> is not recommended</a> because it instantiates all visual items at once. In cases where you have a lot of data, you often only want to view or update a small section of it. For these cases, QML provides the <a href="https://doc.qt.io/qt-6/qml-qtquick-listview.html"><code>ListView</code></a> object, which expects to be bound to a <a href="https://doc.qt.io/qt-6/qabstractlistmodel.html">QAbstractListModel</a> object in your C++ or Python application.</p>



<p class="wp-block-paragraph"><code>QAbstractListModel</code> is an abstract class that cannot be instantiated itself, so you need to create a new class that inherits from it and is specialized for your needs. I would first suggest reading the section of its documentation titled <a href="https://doc.qt.io/qt-6/qabstractlistmodel.html#subclassing">&#8220;Subclassing,&#8221;</a> which states that:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">When subclassing QAbstractListModel, you must provide implementations of the <code>rowCount()</code> and <code>data()</code> functions. Well behaved models also provide a <code>headerData()</code> implementation.</p>



<p class="wp-block-paragraph">If your model is used within QML and requires roles other than the default ones provided by the <code>roleNames()</code> function, you must override it.</p>



<p class="wp-block-paragraph">For editable list models, you must also provide an implementation of <code>setData()</code>&nbsp;and implement the <code>flags()</code> function so that it returns a value containing Qt::ItemIsEditable.</p>
</blockquote>



<p class="wp-block-paragraph">It&#8217;s unlikely that those few sentences made it immediately obvious what you need to do. Let&#8217;s start with what confused me most when I got started: the concept of a &#8220;role.&#8221;</p>



<h2 class="wp-block-heading" id="h-roles">Roles</h2>



<p class="wp-block-paragraph">Think about the data we&#8217;re presenting. We have a list of devices, and each device in the list has three pieces of data (name, serial number, and connection status). You might think of each device&#8217;s data as a row in a table:</p>


<table border="0" cellpadding="1" cellspacing="1">
<thead>
<tr>
<th scope="col" style="text-align: left;">Serial Number</th>
<th scope="col" style="text-align: left;">Human-readable name</th>
<th scope="col" style="text-align: left;">Connected?</th>
</tr>
</thead>
<tbody>
<tr>
<td>123</td>
<td>name1</td>
<td>No</td>
</tr>
<tr>
<td>456</td>
<td>name2</td>
<td>Yes</td>
</tr>
<tr>
<td>789</td>
<td>name3</td>
<td>No</td>
</tr>
</tbody>
</table>


<p class="wp-block-paragraph">The simplest analogy is that the &#8220;role&#8221; is the piece of data that goes in each column. You might also think of it as identifying each piece of data in each object in the list. So, we will define our &#8220;roles&#8221; as <code>name</code>, <code>serial</code>, and <code>connected</code>.</p>



<p class="wp-block-paragraph">Notice also that Qt provides a <a href="https://doc.qt.io/qt-6/qt.html#ItemDataRole-enum">built-in <code>ItemDataRole</code> enum</a>. I initially found this very confusing, because it provides roles with names like <code>Qt::DisplayRole</code> and <code>Qt::EditRole</code>, which don&#8217;t really sound like individual data items to me. The built-in roles are intended for use with built-in classes like <code>QString</code> and <code>QIcon</code>, and they don&#8217;t necessarily make sense for this particular custom class, so don&#8217;t let it throw you off. Consider though, that you might have roles (items of data in your class) that aren&#8217;t pieces of data that you&#8217;d want to render as text but are instead pieces of data that determine how the display of that data behaves (like a background color or an icon).</p>



<p class="wp-block-paragraph">Roles that you want to define yourself for your own custom class can use enum values starting with <code>Qt::UserRole</code>, which has value 0x0100 = 256.</p>



<h2 class="wp-block-heading" id="h-an-aside-on-tables">An Aside on Tables</h2>



<p class="wp-block-paragraph">It&#8217;s worth mentioning that there is indeed a <a href="https://doc.qt.io/qt-6/qabstracttablemodel.html"><code>QAbstractTableModel</code></a> class as well. As shown above, we can use the role as the &#8220;second dimension&#8221; of our one-dimensional list, making it look like a table. So, when would you use <code>QAbstract<u>Table</u>Model</code>? You might use it when you have a 2D array of objects, where each object has a set of properties that you identify as &#8220;roles.&#8221;</p>



<p class="wp-block-paragraph">What makes the most sense as a data model will depend on your specific data, and it may be confusing to think about a list in terms of &#8220;rows&#8221; if your data doesn&#8217;t really seem like a table (you may not even arrange items vertically on your UI, which makes the terminology much worse!). We&#8217;re stuck with the &#8220;row&#8221; and &#8220;column&#8221; terminology used by Qt here, but the models can be used in whatever way makes the most sense for the data you need to represent.</p>



<p class="wp-block-paragraph">In most real-life applications (as well as in the example code here), I use a 1D array with multiple roles, because I find that to be the simplest and most natural data structure. However, both <code>QAbstract<u>Table</u>Model</code> and <code>QAbstract<u>Item</u>Model</code> are available to you if you need a more complex visualization of more complex data. Once you get a handle on <code>QAbstract<u>List</u>Model</code>, the more general classes will make more sense.</p>



<h2 class="wp-block-heading" id="h-how-are-roles-used-in-qml">How are Roles Used in QML?</h2>



<p class="wp-block-paragraph">As shown above, you will use the QML <code>ListView</code>&#8216;s model property to specify an object in your C++ or Python code that inherits from <code>QAbstractListModel</code>. The <code><span style="color:#2f5496">delegate</span></code> property is then used to define the QML object that will visualize that data. In this case, each item in the list has three roles (<code>name</code>, <code>serial</code>, and <code>connected</code>), and we&#8217;ll want to access each of those data items in QML independently.</p>



<p class="wp-block-paragraph">Looking at the GUI again:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/app-1.png" alt="GUI example"/></figure>



<p class="wp-block-paragraph">Each item in the list is visualized as a <code>Text</code> item where the content follows this pattern:</p>



<p class="wp-block-paragraph"><code><span style="white-space: nowrap;">[<span style="color:#00b050">index</span> of item]: [<span style="color:#00b050">name</span> role] ([<span style="color:#00b050">serial</span> role]) - [<span style="color:#00b050">connection</span> role]</span></code></p>



<p class="wp-block-paragraph">In our <span style="color:#2f5496"><code>delegate</code></span>, we can access each item of data using the name of the role:</p>



<p class="wp-block-paragraph"><span style="white-space: nowrap;"><code>ListView {<br>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#c00000">model: controller.listmodel</span><br>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#2f5496">delegate: Text {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text: `${<span style="color:#00b050">index</span>}: ${<span style="color:#00b050">name</span>} (${<span style="color:#00b050">serial</span>}) - ${<span style="color:#00b050">connected</span> ? "OK" : "NOT FOUND"}`<br>
&nbsp;&nbsp;&nbsp;&nbsp;}</span><br>
}</code></span></p>



<p class="wp-block-paragraph">Inside the <span style="color:#2f5496"><code>delegate</code></span> object we can simply use <span style="color:#00b050"><code>index</code></span>, <span style="color:#00b050"><code>name</code></span>, <span style="color:#00b050"><code>serial</code></span> and <span style="color:#00b050"><code>connected</code></span> as if they are bound to the individual data items inside that element of the list. <span style="color:#00b050"><code>index</code></span> is provided out-of-the-box by <code>ListView</code>, but <span style="color:#00b050"><code>name</code></span>, <span style="color:#00b050"><code>serial</code></span>, and <span style="color:#00b050"><code>connected</code></span> are the <strong><em>names of roles we define ourselves</em></strong>. Within the <span style="color:#2f5496"><code>delegate</code></span> object, we can refer to those names, and our child class of <code>QAbstractListModel</code> will provide methods that QML can use to link those names to specific pieces of data.</p>



<p class="wp-block-paragraph">The <span style="color:#00b050"><code>index</code></span> value is also useful in our <code>MouseArea</code>. We added the <code>MouseArea</code> so that the user could click an item in the list and manipulate it. Since the <code>MouseArea</code> is inside the <span style="color:#2f5496"><code>delegate</code></span>, we have access to the index value. In the <code>MouseArea</code>&#8216;s signal handler <code>onClicked</code>, we will want to set <a href="https://doc.qt.io/qt-6/qml-qtquick-listview.html#currentIndex-prop">the <code>currentIndex</code> property</a> of the <code>ListView</code> to the index of the item in the list that was clicked:</p>



<p class="wp-block-paragraph"><code><span style="white-space: nowrap;">ListView {<br>
&nbsp;&nbsp;&nbsp;&nbsp;id: deviceList<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#2f5496">delegate: Item {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MouseArea {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;onClicked: deviceList.currentIndex = index<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp; // MouseArea<br>
&nbsp;&nbsp;&nbsp;&nbsp;}</span>&nbsp;&nbsp;<span style="color:#538135">// Item delegate</span><br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#bf8f00">highlight: Rectangle { color: "lightBlue" }</span><br>
}&nbsp;&nbsp;<span style="color:#538135">// ListView</span></span></code></p>



<p class="wp-block-paragraph">Setting the <code>currentIndex</code> of the <code>ListView</code> enables the <code>ListView</code> to automatically animate the <span style="color:#bf8f00"><code>highlight</code></span> object that we defined. When the user clicks an item in the list, it will move the <code>Rectangle</code> to highlight the selected list item.</p>



<p class="wp-block-paragraph">As an exercise for the reader, try making the <span style="color:#2f5496"><code>delegate</code></span> more interesting. Instead of indicating the state of the connected role with just a <code>Text</code>, try using the <code>connected</code> role to set the text color of the <span style="color:#2f5496"><code>delegate</code></span>, or add an icon to each row that indicates whether or not the device is connected.</p>



<h2 class="wp-block-heading" id="h-setting-up-the-roles">Setting Up the Roles</h2>



<p class="wp-block-paragraph">Let&#8217;s circle back to this statement in the documentation:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">If your model is used within QML and requires roles other than the default ones provided by the <code>roleNames()</code> function, you must override it.</p>
</blockquote>



<p class="wp-block-paragraph">We want to use our own role names for this, so we can use names that make sense in our <span style="color:#2f5496"><code>delegate</code></span> (like <code>name</code>, <code>serial</code>, and <code>connected</code>). The mapping from integer role enum values (like <code>Qt::UserRole</code>) to strings of characters is established with the <a href="https://doc.qt.io/qt-6/qabstractitemmodel.html#roleNames"><code>QAbstractItemModel::roleNames()</code></a> method, which your custom listmodel will inherit. All classes that inherit <code>QAbstractListModel</code> need to implement this method, which returns the map from integers to byte arrays. In C++, this map is a <code>QHash&lt;int, QByteArray&gt;</code>, and in Python it is a basic <code>dict</code>. The integer is the role enum value, and the byte array is the string name used in QML to access that role in each item of the listmodel.</p>



<p class="wp-block-paragraph">I like to set up my roles by doing two things: creating an enum (starting with the value <code>Qt::UserRole</code> and incrementing from there) that enumerates my custom roles, and then creating a dictionary that maps the role enum values to byte arrays (the names used by QML to access elements of the model). In our example, I might do:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>class DeviceItemRoles(IntEnum):    
  NAME = Qt.UserRole
  SERIAL = auto()
  CONNECTED = auto()
  _role_names = {
      DeviceItemRoles.NAME: b'name',
      DeviceItemRoles.SERIAL: b'serial',
      DeviceItemRoles.CONNECTED: b'connected'
  }</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">DeviceItemRoles</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">IntEnum</span><span style="color: #D4D4D4">):    </span></span>
<span class="line"><span style="color: #D4D4D4">  NAME = Qt.UserRole</span></span>
<span class="line"><span style="color: #D4D4D4">  SERIAL = auto()</span></span>
<span class="line"><span style="color: #D4D4D4">  CONNECTED = auto()</span></span>
<span class="line"><span style="color: #D4D4D4">  _role_names = {</span></span>
<span class="line"><span style="color: #D4D4D4">      DeviceItemRoles.NAME: </span><span style="color: #569CD6">b</span><span style="color: #CE9178">&apos;name&apos;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">      DeviceItemRoles.SERIAL: </span><span style="color: #569CD6">b</span><span style="color: #CE9178">&apos;serial&apos;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">      DeviceItemRoles.CONNECTED: </span><span style="color: #569CD6">b</span><span style="color: #CE9178">&apos;connected&apos;</span></span>
<span class="line"><span style="color: #D4D4D4">  }</span></span></code></pre></div>



<p class="wp-block-paragraph">Note again that in Python, the values are byte arrays (<code>b''</code>), not strings.</p>



<p class="wp-block-paragraph">With this setup, the delegate of our <code>ListView</code> can access each piece of data in each list item using the strings <code>name</code>, <code>serial</code>, and <code>connected</code>. QML knows how the role integers (from the enum) map to the names because it knows that a <code>QAbstractListModel</code> must have a <code>roleNames()</code> method, so now we just need to give it a way to access each piece of data given the list index and the role. That is the job of the <a href="https://doc.qt.io/qt-6/qabstractitemmodel.html#data"><code>QAbstractItemModel::data()</code></a> method, which we will get to shortly.</p>



<h2 class="wp-block-heading" id="h-subclassing-a-qabstractlistmodel">Subclassing a QAbstractListModel</h2>



<p class="wp-block-paragraph">Recall from the <a href="https://doc.qt.io/qt-6/qabstractlistmodel.html#subclassing">documentation</a> that subclasses of <code>QAbstractListModel</code> need to implement the <code>rowCount()</code> and <code>data()</code> methods, plus <code>roleNames()</code> if the listmodel is used in QML. We&#8217;ll cover these one-by-one, but first let&#8217;s define how we&#8217;ll store our data.</p>



<h2 class="wp-block-heading" id="h-data-storage">Data Storage</h2>



<p class="wp-block-paragraph">I find that the easiest way to store the data (for a Python application) is with a list of dictionaries, where each dictionary uses the role enum as the key for each data value. This is by no means the only way, but it is very simple and often sufficient. So, you might start developing your custom listmodel class like this:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>class DeviceListModel(QAbstractListModel):

    def __init__(self):

        super().__init__()

        self._data = []



    def add_device(self, name, serial, connected):

        new_row = {

            DeviceItemRoles.NAME: name,

            DeviceItemRoles.SERIAL: serial,

            DeviceItemRoles.CONNECTED: connected

        }



        self._data.append(new_row)</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">DeviceListModel</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">QAbstractListModel</span><span style="color: #D4D4D4">):</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">__init__</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">):</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #4EC9B0">super</span><span style="color: #D4D4D4">().</span><span style="color: #DCDCAA">__init__</span><span style="color: #D4D4D4">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">._data = []</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">add_device</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">serial</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">connected</span><span style="color: #D4D4D4">):</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        new_row = {</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            DeviceItemRoles.NAME: name,</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            DeviceItemRoles.SERIAL: serial,</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">            DeviceItemRoles.CONNECTED: connected</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">._data.append(new_row)</span></span></code></pre></div>



<p class="wp-block-paragraph">When we create a new listmodel, the list of data, <code>self._data</code>, is just an empty list. We can then add device data to the list with the <code>add_device()</code> method, which takes the name, serial number, and connection status, puts them in a dictionary with the appropriate role enum values as keys, and then appends that dictionary to the data list.</p>



<p class="wp-block-paragraph">Now that we&#8217;ve established how the data is stored, we can fill out the required methods.</p>



<h2 class="wp-block-heading" id="h-the-rolenames-method">The roleNames() Method</h2>



<p class="wp-block-paragraph"><code>roleNames()</code> is the easiest to implement, because it&#8217;s already done! The <code>_role_names</code> dictionary from above is exactly what <code>roleNames()</code> should return, so this one&#8217;s a no-brainer:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>def roleNames(self):
    return _role_names</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">roleNames</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">):</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> _role_names</span></span></code></pre></div>



<p class="wp-block-paragraph">That&#8217;s it!</p>



<h2 class="wp-block-heading" id="h-the-rowcount-method">The rowCount() Method</h2>



<p class="wp-block-paragraph"><code>rowCount()</code> is similarly straightforward. The number of rows is just the number of elements in our <code>self._data</code> list. We don&#8217;t need to do much here either:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>def rowCount(self, parent=QModelIndex()):
    return len(self._data)</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">rowCount</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">parent</span><span style="color: #D4D4D4">=QModelIndex()):</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">len</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">._data)</span></span></code></pre></div>



<p class="wp-block-paragraph">The only thing to address is that weird <code>parent</code> argument. What&#8217;s that about?</p>



<p class="wp-block-paragraph">It comes from the base class, <code>QAbstract<u>Item</u>Model</code>. The base class is more general. Whereas <code>QAbstract<u>List</u>Model</code> represents a one-dimensional list of items that all have the same type of elements, <code>QAbstract<u>Item</u>Model</code> can describe trees and other complex hierarchical structures. In those cases, you need to provide the index of a parent object in the tree so the <code>rowCount()</code> method can return the number of children <strong><em>of that parent</em></strong>. Once you get your bearings with the <code>QAbstract<u>List</u>Model</code>, you can dig into the <code>QAbstract<u>Item</u>Model</code>, but for now, let&#8217;s just ignore <code>parent</code>, because it doesn&#8217;t apply to a one-dimensional list. Just give it a default <code>QModelIndex</code>.</p>



<h2 class="wp-block-heading" id="h-the-data-method">The data() Method</h2>



<p class="wp-block-paragraph">Finally, we need to implement a method that will return data values when QML asks for them. <a href="https://doc.qt.io/qt-6/qabstractitemmodel.html#data">The C++ signature of this method is:</a></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C++</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>QVariant QAbstractItemModel::data(const QModelIndex &amp;index, int role = Qt::DisplayRole) const</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #4EC9B0">QVariant</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">QAbstractItemModel</span><span style="color: #D4D4D4">::</span><span style="color: #DCDCAA">data</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">QModelIndex</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">&amp;</span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">int</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">role</span><span style="color: #D4D4D4"> = </span><span style="color: #4EC9B0">Qt</span><span style="color: #D4D4D4">::</span><span style="color: #4EC9B0">DisplayRole</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">const</span></span></code></pre></div>



<p class="wp-block-paragraph">So, our implementation of the method needs to take the index of the row we want (as a <code>QModelIndex</code> object) and the role of the individual data item we want (as an integer, like our convenient <code>DeviceItemRoles</code> enum), and it will return the data as a <code>QVariant</code>. With the PySide6 bindings, there is no <code>QVariant</code>. We can return whatever Python object we want, and if there&#8217;s no data at that index or with that role, we can just return <code>None</code>. A simple implementation in Python looks like:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>def data(self, index, role):

    if role not in list(DeviceItemRoles):

        return None



    try:

        device = self._data&#91;index.row()&#93;

    except IndexError:

        return None



    if role in device:

        return device&#91;role&#93;

    return None</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">data</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">role</span><span style="color: #D4D4D4">):</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> role </span><span style="color: #569CD6">not</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">in</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">list</span><span style="color: #D4D4D4">(DeviceItemRoles):</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">None</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">try</span><span style="color: #D4D4D4">:</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        device = </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">._data&#91;index.row()&#93;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">except</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IndexError</span><span style="color: #D4D4D4">:</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">None</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> role </span><span style="color: #569CD6">in</span><span style="color: #D4D4D4"> device:</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> device&#91;role&#93;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">None</span></span></code></pre></div>



<p class="wp-block-paragraph">There&#8217;s a little more meat here than in our <code>roleNames()</code> and <code>rowCount()</code> methods. First, we check that the role integer that was passed in is an item in our <code>DeviceItemRoles</code> enum. If it isn&#8217;t, then something is looking for a role we aren&#8217;t providing, so we&#8217;ll just return <code>None</code>.</p>



<p class="wp-block-paragraph">Next, we&#8217;ll try to get the index of the item in the list. Note that you can&#8217;t index the <code>self._data</code> list using the <code>index</code> argument directly. You need to call <code>index.row()</code>, which is a consequence of the fact that <code>QAbstract<u>List</u>Model</code> is a child of the more general <code>QAbstract<u>Item</u>Model</code> class, which is not necessarily a 1D list. If you look at the <a href="https://doc.qt.io/qt-6/qmodelindex.html"><code>QModelIndex</code></a> class, you&#8217;ll see that, in addition to <code>row()</code>, it also provides <code>column()</code>, as well as various other methods that only apply to more complex structures.</p>



<p class="wp-block-paragraph">Anyway, if the given index is out of bounds, then something is looking for an invalid row, and we return <code>None</code>. Beyond that, both the index and the role are ok, so we return the appropriate value by indexing the list to get a dictionary, and then looking up the value of the role key in that dictionary. Whatever data was stored there gets returned.</p>



<h2 class="wp-block-heading" id="h-updating-inserting-and-removing-data">Updating, Inserting, and Removing Data</h2>



<p class="wp-block-paragraph">What was implemented above is sufficient for a listmodel that will never change, but that&#8217;s probably in the minority of use cases. If you only have a small-ish amount of static data, it would probably be easier to use a <code>Repeater</code>. More likely, you&#8217;ll want to add data to your list, remove data, or change data at runtime, and <code>QAbstractListModel</code> is a much better fit in these situations. In order to manipulate our list contents, we need to understand a few additional concepts.</p>



<h2 class="wp-block-heading" id="h-signaling-changes-to-existing-row-data-from-the-application">Signaling Changes to Existing Row Data from the Application</h2>



<p class="wp-block-paragraph">In general, when we bind properties to QML, we provide a signal that we emit when the property changes. QML listens for that signal, and when it gets emitted, it calls the property getter to refresh the value. This is done by a <code>QAbstractListModel</code> by using the <a href="https://doc.qt.io/qt-6/qabstractitemmodel.html#dataChanged"><code>dataChanged</code> signal</a> provided by its parent, <code>QAbstract<u>Item</u>Model</code>, which specifies which elements of the model changed (in terms of rows, columns, and roles).</p>



<p class="wp-block-paragraph">In some cases, you need to emit this signal yourself. For example, we might want our listmodel class to have a method that <a href="https://github.com/marklocascio/qml-listmodel-example/blob/main/sample/device_listmodel.py#L73">sets all devices to &#8220;disconnected.&#8221;</a> That would look like:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>def set_all_disconnected(self):

    for d in self._data:

        d&#91;DeviceItemRoles.CONNECTED&#93; = False

    self.dataChanged.emit(self.index(0), self.index(self.rowCount() - 1), [])</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">set_all_disconnected</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">):</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">for</span><span style="color: #D4D4D4"> d </span><span style="color: #C586C0">in</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">._data:</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        d&#91;DeviceItemRoles.CONNECTED&#93; = </span><span style="color: #569CD6">False</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">.dataChanged.emit(</span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">.index(</span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">), </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">.index(</span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">.rowCount() - </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">), [])</span></span></code></pre></div>



<p class="wp-block-paragraph">In this method, we first loop over all items in the data list and set the <code>connected</code> value to <code>False</code>. Then, we only need to emit a single signal that says that all items in the list have changed (i.e., every index from 0 to <code>rowCount() - 1</code>). The empty list in the last parameter of the signal is a list of roles that changed, which can be left empty to indicate that all roles have changed. In this case, you can specify <code>[DeviceItemRoles.CONNECTED]</code> if you prefer. This only makes a difference if you have many roles.</p>



<h2 class="wp-block-heading" id="h-signaling-changes-to-the-collection-of-rows">Signaling Changes to the Collection of Rows</h2>



<p class="wp-block-paragraph">Even if you don&#8217;t <strong><em>change any existing data</em></strong>, you might <strong><em>add or remove entire rows</em></strong>, and QML will need to know what to update when that happens. In this case, we use a pair of methods, <code>beginInsertRows()</code> and <code>endInsertRows()</code>, to specify that we&#8217;re adding data (and how many rows we&#8217;re adding).</p>



<p class="wp-block-paragraph">Let&#8217;s say we want to add a new element to the list after the selected index. <a href="https://github.com/marklocascio/qml-listmodel-example/blob/main/sample/device_listmodel.py#L54">We can do that with a method like:</a></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>def add_device_after_index(self, idx, name, serial, connected):

    index_of_new_device = idx + 1

    new_device = {

        DeviceItemRoles.NAME: name,

        DeviceItemRoles.SERIAL: serial,

        DeviceItemRoles.CONNECTED: connected

    }



    self.beginInsertRows(QModelIndex(), index_of_new_device, index_of_new_device)

    self._data.insert(index_of_new_device, new_device)

    self.endInsertRows()</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">add_device_after_index</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">idx</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">serial</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">connected</span><span style="color: #D4D4D4">):</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    index_of_new_device = idx + </span><span style="color: #B5CEA8">1</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    new_device = {</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        DeviceItemRoles.NAME: name,</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        DeviceItemRoles.SERIAL: serial,</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        DeviceItemRoles.CONNECTED: connected</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">.beginInsertRows(QModelIndex(), index_of_new_device, index_of_new_device)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">._data.insert(index_of_new_device, new_device)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">.endInsertRows()</span></span></code></pre></div>



<p class="wp-block-paragraph"><code>beginInsertRows()</code> needs three things: the <code>QModelIndex</code> of the parent into which rows are inserted (for 1D listmodels that we&#8217;re talking about here, just give it a default one), the row number that <strong><em>the first new row will have after insertion</em></strong>, and the row number that <strong><em>the last new row will have after insertion</em></strong>. I&#8217;m never able to remember this, so I almost always consult the helpful diagrams in <a href="https://doc.qt.io/qt-6/qabstractitemmodel.html#beginInsertRows">the documentation for this method</a>.</p>



<p class="wp-block-paragraph">After that, we&nbsp;insert the new data into our list, and then we call <code>endInsertRows()</code>. The <code>beginInsertRows()</code> method handles emitting a signal for you (<code>rowsAboutToBeInserted</code>), and <code>endInsertRows()</code> handles emitting a different signal for you (<code>rowsInserted</code>) so you don’t have to emit any signals yourself! These signals are used to notify QML that it’s time to refresh the <code>ListView</code> with new rows, and which ones need to be updated (if the model contains large quantities of data, we obviously only want to update as few as possible).</p>



<h2 class="wp-block-heading" id="h-signaling-changes-to-existing-row-data-from-the-gui">Signaling Changes to Existing Row Data from the GUI</h2>



<p class="wp-block-paragraph">The final scenario we&#8217;ll discuss addresses the last part of the <code>QAbstractListModel</code> documentation on subclassing:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">For editable list models, you must also provide an implementation of <code>setData()</code>&nbsp;and implement the <code>flags()</code> function so that it returns a value containing <code>Qt::ItemIsEditable</code>.</p>
</blockquote>



<p class="wp-block-paragraph">As you can see above, if <strong><em>the application</em></strong> manipulates data in the <code>QAbstractListModel</code>, it simply needs to emit a signal (<code>dataChanged</code>) to notify QML that there&#8217;s something new. The <code>setData()</code> method is used when information goes the opposite direction, <strong><em>from the UI to the application</em></strong>. For example, say the delegate contains a checkbox. If the user clicks the checkbox in a particular row, QML needs to tell the <code>QAbstractListModel</code> that there is a new value for the checkbox&#8217;s role at a particular list index. It does this by calling the <a href="https://doc.qt.io/qt-6/qabstractitemmodel.html#setData"><code>setData()</code></a> method, which takes three arguments: the <code>index</code>, the new <code>value</code>, and the <code>role</code>. It will look very similar to the <code>data()</code> method above, perhaps like:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>def setData(self, index, value, role):

    if role != MyRoleEnum.SOME_EDITABLE_ROLE:

        return False



    try:

        data_row = self._data_list&#91;index.row()&#93;

    except IndexError:

        return False



    data_row&#91;MyRoleEnum.SOME_EDITABLE_ROLE&#93; = value

    self.dataChanged.emit(index, index, &#91;MyRoleEnum.SOME_EDITABLE_ROLE&#93;)

    return True</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">def</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">setData</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">self</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">index</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">value</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">role</span><span style="color: #D4D4D4">):</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> role != MyRoleEnum.SOME_EDITABLE_ROLE:</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">False</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">try</span><span style="color: #D4D4D4">:</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        data_row = </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">._data_list&#91;index.row()&#93;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">except</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IndexError</span><span style="color: #D4D4D4">:</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">False</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    data_row&#91;MyRoleEnum.SOME_EDITABLE_ROLE&#93; = value</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">self</span><span style="color: #D4D4D4">.dataChanged.emit(index, index, &#91;MyRoleEnum.SOME_EDITABLE_ROLE&#93;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">True</span></span></code></pre></div>



<p class="wp-block-paragraph">In short, you use the <code>index</code> and <code>role</code> arguments to find the data you’re looking for in the model, you set that data to the new <code>value</code>, and then you emit <code>dataChanged</code>.</p>



<h2 class="wp-block-heading" id="h-summary">Summary</h2>



<p class="wp-block-paragraph">The <code>QAbstract<u>List</u>Model</code> (and its base class, <code>QAbstract<u>Item</u>Model</code>) is a powerful way to present a list of data to a user interface, but the extensive abstraction can make the documentation hard to parse. A <a href="https://github.com/marklocascio/qml-listmodel-example">simple example</a> should help clarify, as well as a small number of important concepts:</p>



<ul class="wp-block-list">
<li>Many of <code>QAbstract<u>List</u>Model</code>&#8216;s methods are inherited from its base classes, and consequently involve a parent <code>QModelIndex</code> that doesn&#8217;t apply to a simple list and can be very confusing.</li>



<li>A &#8220;role&#8221; is simply a way to specify individual pieces of data in a list item.</li>



<li>Roles can be used to make a list seem like a table, and that&#8217;s fine&#8230; you can still use <code>QAbstractListModel</code>!</li>



<li>When adding items to the list or removing items from it, call the <code>beginInsertRows()</code> and <code>endInsertRows()</code> methods before making your changes, and the correct signals will be emitted for you at the right times.</li>



<li>If your application updates the model by changing data in an existing item in the list (or multiple existing items in the list), make sure you emit the <a href="https://doc.qt.io/qt-6/qabstractitemmodel.html#dataChanged"><code>dataChanged</code> signal</a> after the changes are made to notify QML that it needs to update its views.</li>



<li>If the user interacts with the QML UI and modifies data in the model, you will need to implement <code>setData()</code> to store the new information in the model object, and then you will need to emit <code>dataChanged.</code></li>
</ul>



<h2 class="wp-block-heading" id="h-building-a-qt-app">Building a Qt app?</h2>



<p class="wp-block-paragraph">I&#8217;d love to help! Give us a call or <a href="mailto:sales@localhost?subject=Let's%20build%20a%20Qt%20app!">send us an email</a> to discuss!&nbsp;</p>



<p class="wp-block-paragraph"><strong>Learn more about our&nbsp;<a href="https://static.dmcinfo.com/services/application-development">Application Development</a>&nbsp;expertise and&nbsp;<a href="https://static.dmcinfo.com/contact#get-in-touch">contact us</a>&nbsp;for your next project.</strong></p>
<p>The post <a href="https://static.dmcinfo.com/blog/17671/using-a-qabstractlistmodel-in-qml/">Using a QAbstractListModel in QML</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Creating Dynamic Ignition Perspective Projects, Part 2: Button Event Actions</title>
		<link>https://static.dmcinfo.com/blog/19118/creating-dynamic-ignition-perspective-projects-part-2-button-event-actions/</link>
		
		<dc:creator><![CDATA[Casimir Smith]]></dc:creator>
		<pubDate>Fri, 27 Aug 2021 10:28:32 +0000</pubDate>
				<category><![CDATA[HMI and SCADA]]></category>
		<category><![CDATA[Ignition]]></category>
		<category><![CDATA[Manufacturing Automation & Intelligence]]></category>
		<category><![CDATA[MES]]></category>
		<category><![CDATA[button]]></category>
		<category><![CDATA[ignition]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[script]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/19118/creating-dynamic-ignition-perspective-projects-part-2-button-event-actions/</guid>

					<description><![CDATA[<p>One of the fastest growing SCADA/HMI platforms today is Inductive Automation&apos;s Ignition. In addition to the well-known Vision visualization module, Ignition&#160;contains Perspective, a web-friendly visualization module built from the ground up to work in any web browser &#8212; even on mobile! Although most Ignition functions work the same across Perspective or Vision, there are some [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/19118/creating-dynamic-ignition-perspective-projects-part-2-button-event-actions/">Creating Dynamic Ignition Perspective Projects, Part 2: Button Event Actions</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">One of the fastest growing SCADA/HMI platforms today is Inductive Automation&apos;s <a href="https://inductiveautomation.com/ignition/" target="_blank">Ignition</a>. In addition to the well-known <a href="https://inductiveautomation.com/ignition/modules/vision" target="_blank">Vision</a> visualization module, Ignition&nbsp;contains <a href="https://inductiveautomation.com/ignition/modules/perspective" target="_blank">Perspective</a>, a web-friendly visualization module built from the ground up to work in any web browser &mdash; even on mobile!</p>

<p class="wp-block-paragraph">Although most Ignition functions work the same across Perspective or Vision, there are some adjustments to&nbsp;work better in a web-friendly environment. This multi-part blog will serve as a guide to setting up dynamic Perspective screens that take full advantage of its powerful new&nbsp;features.</p>

<p class="wp-block-paragraph">In this part, we will cover how to configure button presses to modify properties and tags in Perspective. Using Python scripting can be intimidating at first, but it is a key skill in creating HMI and SCADA systems that can send commands to other systems. This blog will teach you how to use button events to write to component properties and tags.</p>

<h2 class="wp-block-heading">Example 1: Toggling a Boolean Property</h2>

<p class="wp-block-paragraph">First, we will create a button and use it to toggle the enable/disable custom property that we created in the <a href="https://static.dmcinfo.com/latest-thinking/blog/id/10250/creating-dynamic-ignition-perspective-projects-part-one-bindings-and-transforms">last Perspective blog</a>. Drag a button from the component toolbox onto your view. Then change its text property to &quot;Toggle Enable&quot;</p>

<figure class="wp-block-image"><img decoding="async" alt="Add a button object to the view" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/1_add_button.png"  /></figure>

<p class="wp-block-paragraph">Right click your button and select &quot;Configure Events&quot; to open the event popup. You can also use the handy shortcut <strong>Ctrl+J</strong>.</p>

<figure class="wp-block-image"><img decoding="async" alt="Open the button's configure events popup" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/3_config_events_button.png"  /></figure>

<p class="wp-block-paragraph">The left-hand list shows all possible button events. Select &quot;onActionPerformed&quot;, which is triggered when the button is pressed. Then click the plus icon and add a script action for this event. There are many different actions, but scripts are used for manipulating properties and tags.</p>

<figure class="wp-block-image"><img decoding="async" alt="Configure a script action on the onActionPerformed event" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/4_script_action.png"  /></figure>

<p class="wp-block-paragraph">The runAction Python script is called when the button is pressed. We are going to write a short script to read the &quot;enable custom property&quot; value, toggle that value, then write back to the property. First, press the property reference button, find the property in the tree, and click Ok to add the reference to your script.<figure class="wp-block-image"><img decoding="async" alt="Find the custom property reference" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/5_property_reference.png"  /></figure></p>

<p class="wp-block-paragraph">Next, write the following script. The exact reference may change depending on your object names. First,&nbsp;assign the property value to &quot;input&quot;. Then,&nbsp;assign &quot;output = not input&quot; to toggle the bit. Finally, assign &quot;output&quot; back to the original property.</p>

<figure class="wp-block-image"><img decoding="async" alt="This script toggles the boolean property" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/6_toggle_script.png"  /></figure>

<p class="wp-block-paragraph">To test out your script, enter preview mode by clicking the Play Button icon. This simulates your view as it will run on the Ignition webpage. To exit preview mode and go back to editing, press the button again.</p>

<figure class="wp-block-image"><img decoding="async" alt="The preview button lets you test out your page" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/7_preview_button.png"  /></figure>

<p class="wp-block-paragraph">In preview mode, you can&nbsp;press the button to enable and disable the custom property.</p>

<figure class="wp-block-image"><img decoding="async" alt="The button toggles the enable/disable property" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/8_toggle_result.png"  /></figure>

<h2 class="wp-block-heading">Example 2: Incrementing a Numeric Tag</h2>

<p class="wp-block-paragraph">Next, we will learn how to use a button to update a tag value. The core workflow of using a script to manipulate a property is the same, but that property will now be&nbsp;bidirectionally bound to a tag.</p>

<p class="wp-block-paragraph">Create a new button, rename it&nbsp;&quot;counter&quot;, then add a custom property &quot;count&quot; to it. Next, add a tag binding to the custom property and navigate to an integer or float tag on your gateway.&nbsp;<strong>Make sure to select the bidirectional checkbox.</strong>&nbsp;This makes sure that the property can write back to the tag. For help with these steps, see &quot;Tag Binding&quot; in the <a href="https://static.dmcinfo.com/latest-thinking/blog/id/10250/creating-dynamic-ignition-perspective-projects-part-one-bindings-and-transforms">previous Prespective blog</a> installment.</p>

<figure class="wp-block-image"><img decoding="async" alt="Bind the integer counter tag to a custom button property" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/9_bind_int_tag.png"  /></figure>

<p class="wp-block-paragraph">Next, we will update the button text to display the current counter value. Add an expression binding to the button&apos;s text property. Insert the expression shown in the screenshot below. You can use the property reference button on the right side to find your custom property.&nbsp;</p>

<figure class="wp-block-image"><img decoding="async" alt="Bind the text property to the counter tag" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/10_bind_int_text.png"  /></figure>

<p class="wp-block-paragraph">We will now add a script to manipulate the custom property that is bound to the tag. Like the previous example, create a script action on the button&apos;s onActionPerformed event. Next, insert the property reference to your custom property. This script is even simpler than the last. Simply add &quot; += 1 &quot; to the end of your property reference to increment the value by one.</p>

<figure class="wp-block-image"><img decoding="async" alt="This script increments the counter" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/11_counter_increment_script.png"  /></figure>

<p class="wp-block-paragraph">Enter preview mode and click your new button to watch the counter increment on each press. This basic technique of creating a bidirectional tag binding and an event action script can be used for simple and complex behavior.&nbsp;</p>

<figure class="wp-block-image"><img decoding="async" alt="In preview mode, the button increments the counter" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/12_counter_preview.png"  /></figure>

<p class="wp-block-paragraph"><strong>Learn more about DMC&apos;s&nbsp;<a href="https://static.dmcinfo.com/services/manufacturing-automation-and-intelligence/hmi-and-scada-programming/ignition-designer-programming">Ignition expertise</a>.&nbsp;&nbsp;<a href="https://static.dmcinfo.com/contact">Contact us</a>&nbsp;to get started on your next HMI, SCADA, or MES project.<br />
<br />
See other blogs in this series: </strong>Creating Dynamic Ignition Perspective Projects<br />
<a href="https://static.dmcinfo.com/latest-thinking/blog/id/10250/creating-dynamic-ignition-perspective-projects-part-one-bindings-and-transforms">Part 1: Bindings and Transforms</a><br />
Part 2: Button Event Actions<br />
<a href="https://static.dmcinfo.com/latest-thinking/blog/id/10252/creating-dynamic-ignition-perspective-projects-part-3-embedded-views-and-flex-containers">Part 3: Embedded Views and Flex Containers</a><br />
<a href="https://static.dmcinfo.com/latest-thinking/blog/id/10253/creating-dynamic-ignition-perspective-projects-part-4-parameters-indirect-bindings-and-flex-repeaters">Part 4: View Parameters, Indirect Bindings, and Flex Repeaters</a><br />
Part 5: Flex Repeaters with Variable Number of Instances (coming soon)<br />
Part 6: Changing Colors and Other Properties (coming soon)</p>
<p>The post <a href="https://static.dmcinfo.com/blog/19118/creating-dynamic-ignition-perspective-projects-part-2-button-event-actions/">Creating Dynamic Ignition Perspective Projects, Part 2: Button Event Actions</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Upload a File to SharePoint On-Premises Using Python</title>
		<link>https://static.dmcinfo.com/blog/24542/how-to-upload-a-file-to-sharepoint-on-premises-using-python/</link>
		
		<dc:creator><![CDATA[Curtis Weir]]></dc:creator>
		<pubDate>Thu, 09 Mar 2017 12:12:06 +0000</pubDate>
				<category><![CDATA[Digital Workplace Solutions]]></category>
		<category><![CDATA[SharePoint]]></category>
		<category><![CDATA[Custom SharePoint]]></category>
		<category><![CDATA[How To]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/24542/how-to-upload-a-file-to-sharepoint-on-premises-using-python/</guid>

					<description><![CDATA[<p>There are several tools out there for interacting with SharePoint through a Python script, but today, I am going to demonstrate a very simple way to upload a file to your SharePoint environment&#160;with minimal overhead. This can be useful for users running on Linux environments, such as&#160;a Raspberry PI, who want to script some functionality. [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/24542/how-to-upload-a-file-to-sharepoint-on-premises-using-python/">How to Upload a File to SharePoint On-Premises Using Python</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">There are several tools out there for interacting with SharePoint through a Python script, but today, I am going to demonstrate a very simple way to upload a file to your SharePoint environment&nbsp;with minimal overhead. This can be useful for users running on Linux environments, such as&nbsp;a Raspberry PI, who want to script some functionality.</p>



<h2 class="wp-block-heading" id="h-using-octoprint-to-monitor-3d-printing">Using Octoprint to Monitor 3D Printing</h2>



<p class="wp-block-paragraph">I recently set up a tool called <a href="http://octoprint.org/" target="_blank">Octoprint</a>&nbsp;on a Raspberry Pi, and am using it to control and monitor the 3D printer here at DMC.</p>



<p class="wp-block-paragraph">The tool generates timelapses after each print, and it started to take up a lot of space on the Pi’s storage.</p>



<p class="wp-block-paragraph"><iframe loading="lazy" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/Ndu9Qaakv44" width="560"></iframe></p>



<p class="wp-block-paragraph">I used the following script to take newly rendered timelapses and upload them to SharePoint. This allows users to easily access their print timelapses&nbsp;and frees space on the pi.</p>



<h2 class="wp-block-heading" id="h-setup">Setup</h2>



<p class="wp-block-paragraph">You will need to install the requests package and NTLM authentication package on your environment. In a Linux environment, type the following commands:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>pip install requests

pip install requests_ntlm</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">pip install requests</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">pip install requests_ntlm</span></span></code></pre></div>



<h2 class="wp-block-heading" id="h-python-script">Python Script</h2>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Python</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>#Import required packages
import sys
import requests
from requests_ntlm import HttpNtlmAuth

#Read filename (relative path) from command line
fileName = sys.argv&#91;1&#93;

#Enter your SharePoint site and target library
sharePointUrl = 'https://your.sharepointsite.com/'
folderUrl = '/Your/SharePoint/DocumentLibrary'

#Sets up the url for requesting a file upload
requestUrl = sharePointUrl + '/_api/web/getfolderbyserverrelativeurl(\'' + folderUrl + '\')/Files/add(url=\'' + fileName + '\',overwrite=true)'

#Read in the file that we are going to upload
file = open(fileName, 'rb')

#Setup the required headers for communicating with SharePoint 
headers = {'Content-Type': 'application/json; odata=verbose', 'accept': 'application/json;odata=verbose'}

#Execute a request to get the FormDigestValue. This will be used to authenticate our upload request
r = requests.post(sharePointUrl + "/_api/contextinfo",auth=HttpNtlmAuth('Domain\\username','password'), headers=headers)
formDigestValue = r.json()&#91;'d'&#93;&#91;'GetContextWebInformation'&#93;&#91;'FormDigestValue'&#93;

#Update headers to use the newly acquired FormDigestValue
headers = {'Content-Type': 'application/json; odata=verbose', 'accept': 'application/json;odata=verbose', 'x-requestdigest' : formDigestValue}

#Execute the request. If you run into issues, inspect the contents of uploadResult
uploadResult = requests.post(requestUrl,auth=HttpNtlmAuth('Domain\\username','password'), headers=headers, data=file.read())</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">#Import required packages</span></span>
<span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> sys</span></span>
<span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> requests</span></span>
<span class="line"><span style="color: #C586C0">from</span><span style="color: #D4D4D4"> requests_ntlm </span><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> HttpNtlmAuth</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">#Read filename (relative path) from command line</span></span>
<span class="line"><span style="color: #D4D4D4">fileName = sys.argv&#91;</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">&#93;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">#Enter your SharePoint site and target library</span></span>
<span class="line"><span style="color: #D4D4D4">sharePointUrl = </span><span style="color: #CE9178">&apos;https://your.sharepointsite.com&/#39;</span></span>
<span class="line"><span style="color: #D4D4D4">folderUrl = </span><span style="color: #CE9178">&apos;/Your/SharePoint/DocumentLibrary&apos;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">#Sets up the url for requesting a file upload</span></span>
<span class="line"><span style="color: #D4D4D4">requestUrl = sharePointUrl + </span><span style="color: #CE9178">&apos;/_api/web/getfolderbyserverrelativeurl(</span><span style="color: #D7BA7D">\&apos;</span><span style="color: #CE9178">&apos;</span><span style="color: #D4D4D4"> + folderUrl + </span><span style="color: #CE9178">&apos;</span><span style="color: #D7BA7D">\&apos;</span><span style="color: #CE9178">)/Files/add(url=</span><span style="color: #D7BA7D">\&apos;</span><span style="color: #CE9178">&apos;</span><span style="color: #D4D4D4"> + fileName + </span><span style="color: #CE9178">&apos;</span><span style="color: #D7BA7D">\&apos;</span><span style="color: #CE9178">,overwrite=true)&apos;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">#Read in the file that we are going to upload</span></span>
<span class="line"><span style="color: #D4D4D4">file = </span><span style="color: #DCDCAA">open</span><span style="color: #D4D4D4">(fileName, </span><span style="color: #CE9178">&apos;rb&apos;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">#Setup the required headers for communicating with SharePoint </span></span>
<span class="line"><span style="color: #D4D4D4">headers = {</span><span style="color: #CE9178">&apos;Content-Type&apos;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&apos;application/json; odata=verbose&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;accept&apos;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&apos;application/json;odata=verbose&apos;</span><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">#Execute a request to get the FormDigestValue. This will be used to authenticate our upload request</span></span>
<span class="line"><span style="color: #D4D4D4">r = requests.post(sharePointUrl + </span><span style="color: #CE9178">&quot;/_api/contextinfo&quot;</span><span style="color: #D4D4D4">,</span><span style="color: #9CDCFE">auth</span><span style="color: #D4D4D4">=HttpNtlmAuth(</span><span style="color: #CE9178">&apos;Domain</span><span style="color: #D7BA7D">\\</span><span style="color: #CE9178">username&apos;</span><span style="color: #D4D4D4">,</span><span style="color: #CE9178">&apos;password&apos;</span><span style="color: #D4D4D4">), </span><span style="color: #9CDCFE">headers</span><span style="color: #D4D4D4">=headers)</span></span>
<span class="line"><span style="color: #D4D4D4">formDigestValue = r.json()&#91;</span><span style="color: #CE9178">&apos;d&apos;</span><span style="color: #D4D4D4">&#93;&#91;</span><span style="color: #CE9178">&apos;GetContextWebInformation&apos;</span><span style="color: #D4D4D4">&#93;&#91;</span><span style="color: #CE9178">&apos;FormDigestValue&apos;</span><span style="color: #D4D4D4">&#93;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">#Update headers to use the newly acquired FormDigestValue</span></span>
<span class="line"><span style="color: #D4D4D4">headers = {</span><span style="color: #CE9178">&apos;Content-Type&apos;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&apos;application/json; odata=verbose&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;accept&apos;</span><span style="color: #D4D4D4">: </span><span style="color: #CE9178">&apos;application/json;odata=verbose&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;x-requestdigest&apos;</span><span style="color: #D4D4D4"> : formDigestValue}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">#Execute the request. If you run into issues, inspect the contents of uploadResult</span></span>
<span class="line"><span style="color: #D4D4D4">uploadResult = requests.post(requestUrl,</span><span style="color: #9CDCFE">auth</span><span style="color: #D4D4D4">=HttpNtlmAuth(</span><span style="color: #CE9178">&apos;Domain</span><span style="color: #D7BA7D">\\</span><span style="color: #CE9178">username&apos;</span><span style="color: #D4D4D4">,</span><span style="color: #CE9178">&apos;password&apos;</span><span style="color: #D4D4D4">), </span><span style="color: #9CDCFE">headers</span><span style="color: #D4D4D4">=headers, </span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">=file.read())</span></span></code></pre></div>



<p class="wp-block-paragraph">Browse to your SharePoint site, and you should now see the newly uploaded file.</p>



<p class="wp-block-paragraph">Learn more about DMC&#8217;s&nbsp;<a href="https://static.dmcinfo.com/services/digital-workplace-solutions/microsoft-consulting-services/sharepoint">SharePoint Consulting Services</a>&nbsp;and&nbsp;<a href="https://static.dmcinfo.com/services/digital-workplace-solutions">Digital Workplace Solutions</a>&nbsp;team.&nbsp;<a href="https://static.dmcinfo.com/contact">Contact us</a>&nbsp;for your next custom SharePoint project.</p>
<p>The post <a href="https://static.dmcinfo.com/blog/24542/how-to-upload-a-file-to-sharepoint-on-premises-using-python/">How to Upload a File to SharePoint On-Premises Using Python</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
