C++ GUI-based Inventory Project
The inventory system grows a window: tables, forms, and live search.
What you will build
Take inventory management into a real graphical interface with Qt: a sortable product table, add/edit dialogs, live search-as-you-type, and low-stock highlighting. It's event-driven programming — your code waits, the user acts, handlers respond — a fundamentally different rhythm from console apps.
What you will learn
- Event-driven programming with Qt signals/slots
- Model-view architecture (QAbstractTableModel)
- Dialogs and form validation
- Live filtering with a proxy model
- Styling rows conditionally (low stock = red)
Feature checklist
- Product table: sortable columns, row selection
- Add/Edit dialog with validation
- Search box filters the table as you type
- Low-stock rows highlighted automatically
- Sales and restock buttons adjust quantities
- Data persists to a file or SQLite
Starter code
// Qt: a table model over the product list (header sketch)
#include <QAbstractTableModel>
#include <vector>
#include <string>
struct Product { std::string sku, name; double price; int qty; };
class InventoryModel : public QAbstractTableModel {
std::vector<Product> data_;
public:
int rowCount(const QModelIndex& = QModelIndex()) const override {
return (int)data_.size();
}
int columnCount(const QModelIndex& = QModelIndex()) const override {
return 4; // SKU, Name, Price, Qty
}
QVariant data(const QModelIndex& i, int role) const override {
const Product& p = data_[i.row()];
if (role == Qt::BackgroundRole && p.qty <= 5 && i.column() == 3)
return QColor(120, 30, 30); // low stock highlight
if (role != Qt::DisplayRole) return {};
switch (i.column()) {
case 0: return QString::fromStdString(p.sku);
case 1: return QString::fromStdString(p.name);
case 2: return p.price;
case 3: return p.qty;
}
return {};
}
void add(Product p) {
beginInsertRows({}, (int)data_.size(), (int)data_.size());
data_.push_back(std::move(p));
endInsertRows();
}
};Full walkthrough
Step 1: Understand the Qt bargain before writing code
Qt gives you windows, tables, and dialogs; in exchange your code lives inside its event loop (app.exec()) and its object model. The inventory data stays YOUR code — a std::vector<Product> — and Qt only presents it. Keeping that boundary (plain C++ data, Qt presentation) is the architecture decision everything else follows.
Step 2: Build the model: InventoryModel extends QAbstractTableModel
A QTableView does not hold data — it ASKS a model. InventoryModel wraps the vector<Product> and implements the contract: rowCount() = products size, columnCount() = 4 (SKU, Name, Price, Qty), data(index, role) returns the field for that cell as a QVariant, headerData() names the columns. When the data changes, the model emits signals (beginInsertRows/endInsertRows, dataChanged) — the view redraws because it was TOLD, which is the whole model-view idea.
int InventoryModel::rowCount(const QModelIndex&) const override {
return (int)data_.size();
}Step 3: Wire the view: sorting and selection for free
A QTableView over the model, wrapped by QSortFilterProxyModel, gives sortable column headers and as-you-type filtering almost for free: the search box's text goes into the proxy's setFilterFixedString, and the proxy hides non-matching rows WITHOUT touching your data. Row selection comes from the view's selection model — read the selected proxy index, map it back to the source row, and you know which Product the user means.
Step 4: Add/Edit dialogs with validation
A QDialog with line edits for SKU, name, price, quantity; accept only if SKU is non-empty (and unique for Add), price parses to a number ≥ 0, quantity is a non-negative integer. Validation failure shows the problem IN the dialog and keeps it open — a dialog that silently closes on bad data trains users to distrust every button.
Step 5: Highlight low stock inside data()
The model's data() also answers the Qt::BackgroundRole: when a product's qty is below the threshold (10), return a warning tint for that row. The rule lives in the model, so sorting, filtering, and printing all agree on which rows are 'low' — presentation derived from the same data as everything else.
Step 6: Sales and restock buttons adjust the model
Buttons beside the table act on the SELECTED product: Sell asks a quantity (spin box), refuses overselling with a message box (the inventory project's rule, wearing a GUI), decrements, and notifies the view via dataChanged so the row and its low-stock tint update instantly. Restock mirrors it. No button reaches into the vector directly — everything goes through model methods that emit the right signals.
Step 7: Persist to a file (or SQLite) on every change
After each mutation, save the product list (the catalogue's familiar line format, or a QSqlDatabase table if you take the SQLite branch). Load in the model's constructor. The GUI test: add a product, sell some, CLOSE the window, reopen — the table must show the post-sale quantity, or persistence is a rumour.
Common mistakes
- Storing product data inside QTableWidget items instead of a real model, losing sorting, filtering, and every future feature with it.
- Forgetting to emit begin/endInsertRows (or dataChanged) after mutating, so the table shows stale rows until something else forces a redraw.
- Letting the Add dialog accept an empty or duplicate SKU because validation 'is a later feature' — in a GUI, the dialog IS the validation boundary.
Compile and run
g++ -std=c++17 -o gui_inventory gui_inventory.cppThis is a Qt Widgets app, so it is built with CMake/Qt Creator rather than a bare g++ command (Qt generates code with its moc tool as part of the build). On Windows the built program is build\gui_inventory.exe — or just open the project in Qt Creator and press Run.
Sample run
$ ./build/gui_inventory
(the inventory window opens — sample session)
Window: table shows 2 products loaded from inventory.dat
USB-CAB | USB Cable | 1500.00 | 40
MSE-02 | Wireless Mouse | 4500.00 | 6 <- highlighted: low stock
(you type 'mouse' in the search box — the table filters as you type)
1 row shown: MSE-02 Wireless Mouse
(you clear the search, select USB Cable, click Sell, quantity 5)
USB Cable row updates: quantity 40 -> 35
(you select Wireless Mouse and click Sell, quantity 9)
Dialog: 'Only 6 in stock — sale refused.' (nothing changed)
(you click Add, enter a new product, and it appears in sorted position)
HDMI-01 | HDMI Cable 2m | 3200.00 | 25
(you close the window and reopen it — every quantity is exactly as you left it)
Saved to inventory.dat on every change — persistence verified.Try it in your browser
No browser demo is included for this project. Build the C++ version below — that is the real program.