Getting a menu system onto an embedded screen shouldn't take three weeks

I've been shipping Qt-based GUIs for industrial HMIs and automotive clusters for longer than I care to admit. The menu systems are where most projects either look polished or look like they were thrown together at 4 PM on a Friday. Qt gives you the pieces. The trick is knowing which ones to actually use and which ones will haunt your deployment. Qt's menu architecture spans QMenu, QAction, and QMenuBar on desktop, but embedded is a different animal entirely. On a 480x272 resistive touchscreen running Qt 6.5 on an i.MX RT1170, you don't have a mouse hovering over a toolbar. You have a thumb pressing a glass screen that occasionally misfires. That changes everything about how you design menus. The core advantage people miss is that QMenu in QtQuick Controls 2 doesn't just render pretty popups. It gives you a declarative item tree that compiles down to an optimized scene graph. For embedded, that matters because your texture memory is measured in megabytes, not gigabytes. A QML MenuItem backed by a plain Rectangle gets the same treatment as any other visual node. You're not loading widgets. You're building scenes.

Here's the practical part. If you're building a settings menu hierarchy for a medical device controller, your structure looks something like this: QAction-based approach (older QtQuick or Widgets route): Define your actions in C++, register them, then wire them into a QMenu. This works fine when your menu items are static. Define actions, call addAction, set shortcuts, connect triggered signals. The menu builds itself from the action list. It's fast to prototype. But it falls apart when you need dynamic menu construction based on runtime state, which is nearly every embedded project I've seen.

QML-based approach (the one that actually ships): Use Menu and MenuItem from QtQuick.Controls. Build nested structures declaratively. Bind enabled properties to application state. Update text dynamically from C++ context objects. This is where embedded projects actually live.

Get the Full Details

GigaDevice and Qt Partner on Embedded GUI Development ...
GigaDevice and Qt Partner on Embedded GUI Development ...

What actually breaks in production

I ran into a real problem with a project last year. We had a 6-level deep navigation menu on a Qt 6.4 embedded build running on NXP's S32G2 processor. Every time a user navigated down into submenu level 4 and then pressed the hardware back button, the menu would sometimes close two levels instead of one. The event queue was consuming a queued activated signal from a MenuItem that had already been destroyed during a layout recalculation. Qt's signal system doesn't check whether the sender still exists before delivering queued signals. Classic. The workaround wasn't elegant. I ended up blocking the menu's close animation before processing the back button release, connecting the back button to a dedicated slot that explicitly cleared the activeAction and set the menu's visible property false in a single synchronous call. No animated transition. Just immediate closure. You lose the polish but you stop getting phantom menu pops that crash the application on restart. Another thing nobody warns you about: dynamic menu generation from C++ data models. If you're pulling menu items from a JSON config file and rebuilding the menu tree every time the config changes, do not call menu.clear() and then rebuild in the same event loop iteration. On embedded systems with limited CPU, the renderer drops frames visibly. Instead, accumulate changes and apply them in a QTimer.singleShot(0) callback. It batches the update to the next cycle and the screen stays smooth. This cut our menu reload time from a jarring 400ms stutter to basically nothing noticeable.

Performance numbers that matter

On a Cortex-M7 at 600MHz with 512MB RAM running Qt 6.6, a flat menu with 20 top-level items and three submenu levels renders at about 58fps with standard MenuItem styling. Switch to custom styled items with gradient backgrounds and rounded corners and you drop to 44fps. The drop is real. It's not dramatic but it's measurable and it adds up when you're doing other work simultaneously, like reading CAN bus data or updating a gauge cluster. If you need to stay above 50fps on constrained hardware, keep your menu items simple. Avoid heavy shaders. Use implicit size sparingly because layout recalculation on every frame is the silent frame-killer. Set explicit widths where you can. Qt's layout engine will do fewer passes and your render thread stays happy.

When Qt menus are the wrong tool

Let me be blunt about where this doesn't work. If your embedded device has less than 64MB of RAM, Qt's menu system is going to be expensive relative to what you have available. The overhead of the Quick renderer, the state management, the signal system, and the QML engine adds up. In those cases, consider writing a lightweight menu using Qt Widgets with a custom draw function, or just skip Qt entirely and use LVGL. LVGL menus consume roughly a tenth of the memory and are purpose-built for microcontroller-class displays. Also, if your product requires ISO 26262 compliance for the HMI layer, Qt's menu system introduces certification overhead. You'll need to document every code path through the menu hierarchy, validate edge cases like rapid repeated taps, and prove deterministic behavior under all input conditions. This can add weeks to your timeline. Some teams in automotive have switched to simpler state machines with button-driven navigation specifically to reduce the certification surface area.

What is Qt framework and how to use it for GUI development? - Somco ...
What is Qt framework and how to use it for GUI development? - Somco ...

Practical setup steps

If you're starting fresh with Qt 6 on embedded, here's what I actually do rather than what the documentation says: Create a main.qml with a ApplicationWindow as the root. Inside it, define a MenuBar only if you're targeting a device with enough screen real estate. For small displays under 5 inches, skip the bar and use overlay menus triggered by icon buttons. It saves layout complexity and avoids covering critical gauge information. Define your menu items as a reusable component. Put the Menu and MenuItem definitions in a separate file. Import it where needed. This lets you test the menu in isolation before wiring it to your application logic. I typically spend more time on the menu component than any other part of the UI because once it's wired to data sources, debugging becomes significantly harder.

For the C++ side, expose your menu model through a QQmlContext property. Use a QListModel or a custom QAbstractListModel depending on whether your items change frequently. Static menus work fine with simple arrays. Dynamic menus need the model approach. The difference in code complexity is small but the runtime behavior is completely different. Qt's menu system is genuinely good for embedded when you understand its constraints. It's not magic. It doesn't solve every problem. But it does give you a structured way to build navigation that actually scales beyond a prototype. The companies that treat their menu system as an afterthought are the ones with support calls about buttons that don't respond. Build it right from the start.