├── .gitignore ├── 01-TableView-Simple-Different-Cols ├── CMakeLists.txt ├── Proc.cpp ├── Proc.h ├── TableModel.cpp ├── TableModel.h ├── main.cpp ├── main.qml └── qml.qrc ├── 02-TableView-ColumnWidth-Headings ├── CMakeLists.txt ├── Proc.cpp ├── Proc.h ├── TableModel.cpp ├── TableModel.h ├── main.cpp ├── main.qml └── qml.qrc ├── 03-TableView-Columns-Sortable ├── CMakeLists.txt ├── Proc.cpp ├── Proc.h ├── ProcessModel.cpp ├── ProcessModel.h ├── SortFilterTableModel.cpp ├── SortFilterTableModel.h ├── SortableColumnHeading.qml ├── main.cpp ├── main.qml └── qml.qrc ├── 04-TableView-Sort-Init-Filter ├── CMakeLists.txt ├── Proc.cpp ├── Proc.h ├── ProcessModel.cpp ├── ProcessModel.h ├── SortFilterTableModel.cpp ├── SortFilterTableModel.h ├── SortableColumnHeading.qml ├── main.cpp ├── main.qml └── qml.qrc ├── 05-TableView-DelegateChooser ├── CMakeLists.txt ├── Proc.cpp ├── Proc.h ├── ProcessModel.cpp ├── ProcessModel.h ├── SortFilterTableModel.cpp ├── SortFilterTableModel.h ├── SortableColumnHeading.qml ├── main.cpp ├── main.qml └── qml.qrc ├── 06-TableView-Column-Resizable ├── CMakeLists.txt ├── Proc.cpp ├── Proc.h ├── ProcessModel.cpp ├── ProcessModel.h ├── SortFilterTableModel.cpp ├── SortFilterTableModel.h ├── SortableColumnHeading.qml ├── main.cpp ├── main.qml └── qml.qrc ├── 07-TableView-Column-Resizable-Image ├── CMakeLists.txt ├── Proc.cpp ├── Proc.h ├── ProcessModel.cpp ├── ProcessModel.h ├── SortFilterTableModel.cpp ├── SortFilterTableModel.h ├── SortableColumnHeading.qml ├── images │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.jpg │ └── images.qrc ├── main.cpp ├── main.qml └── qml.qrc ├── README.md └── TableView-With-Headers ├── CMakeLists.txt ├── Proc.cpp ├── Proc.h ├── TableModel.cpp ├── TableModel.h ├── main.cpp ├── main.qml └── qml.qrc /.gitignore: -------------------------------------------------------------------------------- 1 | /build-01-TableView-Simple-Different-Cols-Qt_5_14_2_MSVC2017_64bit-Release/ 2 | /build-02-TableView-ColumnWidth-Headings-Qt_5_14_2_MSVC2017_64bit-Release/ 3 | /01-TableView-Simple-Different-Cols/CMakeLists.txt.user 4 | /02-TableView-ColumnWidth-Headings/CMakeLists.txt.user 5 | /build-03-TableView-Columns-Sortable-Qt_5_14_2_MSVC2017_64bit-Release/ 6 | /03-TableView-Columns-Sortable/CMakeLists.txt.user 7 | /build-04-TableView-Sort-Init-Filter-Qt_5_14_2_MSVC2017_64bit-Release/ 8 | /04-TableView-Sort-Init-Filter/CMakeLists.txt.user 9 | /build-05-TableView-DelegateChooser-Qt_5_14_2_MSVC2017_64bit-Release/ 10 | /05-TableView-DelegateChooser/CMakeLists.txt.user 11 | /build-TableView-With-Headers-Qt_5_15_0_MSVC2017_64bit-Release/ 12 | /TableView-With-Headers/CMakeLists.txt.user 13 | /build-06-TableView-Column-Resizable-Qt_5_15_0_MSVC2017_64bit-Debug/ 14 | /build-06-TableView-Column-Resizable-Qt_5_15_0_MSVC2017_64bit-Release/ 15 | /06-TableView-Column-Resizable/CMakeLists.txt.user 16 | /build-07-TableView-Column-Resizable-Image-Qt_5_15_0_MSVC2017_64bit-Release/ 17 | /07-TableView-Column-Resizable-Image/CMakeLists.txt.user 18 | -------------------------------------------------------------------------------- /01-TableView-Simple-Different-Cols/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | project(01-TableView-Simple-Different-Cols LANGUAGES CXX) 4 | 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | set(CMAKE_AUTOMOC ON) 7 | set(CMAKE_AUTORCC ON) 8 | set(CMAKE_CXX_STANDARD 11) 9 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 10 | 11 | find_package(Qt5 COMPONENTS Core Quick REQUIRED) 12 | 13 | add_executable(${PROJECT_NAME} main.cpp 14 | qml.qrc 15 | TableModel.cpp 16 | TableModel.h 17 | Proc.cpp 18 | Proc.h) 19 | target_compile_definitions(${PROJECT_NAME} PRIVATE $<$,$>:QT_QML_DEBUG>) 20 | target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core Qt5::Quick) 21 | 22 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 23 | COMMAND ${_qt5Core_install_prefix}/bin/windeployqt.exe 24 | --verbose 0 25 | --force 26 | --no-compiler-runtime 27 | --qmldir ${_qt5Core_install_prefix}/qml 28 | $ 29 | # windeployqt is not able to deploy Qt/labs/platform! Hence we do it by own. 30 | COMMAND ${CMAKE_COMMAND} -E copy_directory 31 | ${_qt5Core_install_prefix}/qml/Qt/labs/platform 32 | $/Qt/labs/platform) 33 | 34 | -------------------------------------------------------------------------------- /01-TableView-Simple-Different-Cols/Proc.cpp: -------------------------------------------------------------------------------- 1 | #include "Proc.h" 2 | 3 | #include 4 | std::random_device dev; 5 | std::mt19937 rng(dev()); 6 | 7 | ProcInfo::ProcInfo() 8 | { 9 | 10 | } 11 | 12 | ProcInfo::ProcInfo(int pid) 13 | { 14 | mpid = pid; 15 | std::uniform_int_distribution dist1(1,6); 16 | ppid = dist1(rng); 17 | std::uniform_int_distribution dist2(0,100); 18 | pgid = dist2(rng); 19 | std::uniform_int_distribution dist3(100,1000); 20 | sid = dist3(rng); 21 | stat1 = QString("%1%").arg(pgid); 22 | stat2 = QString("%1 bytes").arg(pgid + sid); 23 | stat3 = QString("%1 bytes").arg(pgid - sid); 24 | stat4 = QString("%1 bytes").arg(pgid * sid); 25 | cmd = QString("cmd%1").arg(sid); 26 | cmdline = "cmdline"; 27 | } 28 | 29 | ProcInfo::~ProcInfo() 30 | { 31 | 32 | } 33 | 34 | QString ProcInfo::toString(fields f) 35 | { 36 | switch (f) { 37 | case F_PID: return QString("%1").arg(mpid); 38 | case F_PPID: return QString("%1").arg(ppid); 39 | case F_PGID: return QString("%1").arg(pgid); 40 | case F_SID: return QString("%1").arg(sid); 41 | case F_STAT1: return stat1; 42 | case F_STAT2: return stat2; 43 | case F_STAT3: return stat3; 44 | case F_STAT4: return stat4; 45 | case F_CMD: return cmd; 46 | case F_CMDLINE: return cmdline; 47 | default: return ""; 48 | } 49 | } 50 | 51 | 52 | Proc::Proc() 53 | { 54 | 55 | } 56 | 57 | void Proc::refresh() 58 | { 59 | procs.clear(); 60 | std::uniform_int_distribution dist(0,100); 61 | 62 | const int numOfPids = dist(rng); 63 | 64 | for (int i = 0; i < numOfPids; ++i) 65 | { 66 | procs.insert(i, ProcInfo(i)); 67 | } 68 | } 69 | 70 | 71 | -------------------------------------------------------------------------------- /01-TableView-Simple-Different-Cols/Proc.h: -------------------------------------------------------------------------------- 1 | #ifndef PROC_H 2 | #define PROC_H 3 | 4 | #include 5 | #include 6 | 7 | 8 | enum fields 9 | { 10 | F_PID = 0, 11 | F_PPID, 12 | F_PGID, 13 | F_SID, 14 | F_STAT1, 15 | F_STAT2, 16 | F_STAT3, 17 | F_STAT4, 18 | F_CMD, 19 | F_CMDLINE, 20 | F_END = -1 21 | }; 22 | 23 | class ProcInfo 24 | { 25 | public: 26 | ProcInfo(); 27 | ProcInfo(int pid); 28 | ~ProcInfo(); 29 | 30 | int mpid; 31 | int ppid; 32 | int pgid; 33 | int sid; 34 | QString stat1; 35 | QString stat2; 36 | QString stat3; 37 | QString stat4; 38 | QString cmd; 39 | QString cmdline; 40 | 41 | QString toString(fields f); 42 | }; 43 | 44 | using ProcList = QMap; 45 | 46 | 47 | class Proc 48 | { 49 | public: 50 | Proc(); 51 | void refresh(); 52 | 53 | ProcList procs; // processes indexed by pid 54 | 55 | }; 56 | 57 | #endif // PROC_H 58 | -------------------------------------------------------------------------------- /01-TableView-Simple-Different-Cols/TableModel.cpp: -------------------------------------------------------------------------------- 1 | #include "TableModel.h" 2 | 3 | #include 4 | 5 | TableModel::TableModel(QObject *parent) : QAbstractTableModel (parent) 6 | { 7 | update(); 8 | m_timer = new QTimer(this); 9 | connect(m_timer, &QTimer::timeout, this, QOverload<>::of(&TableModel::update)); 10 | m_timer->start(5000); 11 | } 12 | 13 | TableModel::~TableModel() 14 | { 15 | 16 | } 17 | 18 | int TableModel::rowCount(const QModelIndex &) const 19 | { 20 | return m_pids.count(); 21 | } 22 | 23 | int TableModel::columnCount(const QModelIndex &) const 24 | { 25 | return F_CMDLINE + 1; 26 | } 27 | 28 | QVariant TableModel::data(const QModelIndex &index, int /*role*/) const 29 | { 30 | fields field = fields(index.column()); 31 | int pid = m_pids[index.row()]; 32 | ProcInfo pi = m_proc.procs.value(pid); 33 | return pi.toString(field); 34 | } 35 | 36 | void TableModel::update() 37 | { 38 | qDebug() << __FUNCTION__; 39 | beginResetModel(); 40 | m_proc.refresh(); 41 | m_pids = m_proc.procs.keys().toVector(); 42 | std::sort(m_pids.begin(), m_pids.end()); 43 | endResetModel(); 44 | } 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /01-TableView-Simple-Different-Cols/TableModel.h: -------------------------------------------------------------------------------- 1 | #ifndef TABLEMODEL_H 2 | #define TABLEMODEL_H 3 | 4 | 5 | #include "Proc.h" 6 | 7 | #include 8 | #include 9 | 10 | class TableModel : public QAbstractTableModel 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit TableModel(QObject *parent= nullptr); 16 | ~TableModel(); 17 | 18 | int rowCount(const QModelIndex & = QModelIndex()) const override; 19 | int columnCount(const QModelIndex & = QModelIndex()) const override; 20 | QVariant data(const QModelIndex &index, int) const override; 21 | 22 | private slots: 23 | void update(); 24 | 25 | private: 26 | Proc m_proc; 27 | QVector m_pids; 28 | QTimer* m_timer; 29 | 30 | }; 31 | 32 | 33 | #endif 34 | 35 | -------------------------------------------------------------------------------- /01-TableView-Simple-Different-Cols/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "TableModel.h" 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | QGuiApplication app(argc, argv); 11 | 12 | qmlRegisterType("TableModel", 0, 1, "TableModel"); 13 | TableModel tm; 14 | 15 | 16 | QQmlApplicationEngine engine; 17 | 18 | engine.rootContext()->setContextProperty("_TableModel", &tm); 19 | 20 | engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 21 | 22 | 23 | return app.exec(); 24 | } 25 | 26 | -------------------------------------------------------------------------------- /01-TableView-Simple-Different-Cols/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Window 2.12 3 | import TableModel 0.1 4 | import QtQuick.Controls 2.0 5 | 6 | ApplicationWindow { 7 | id: window 8 | visible: true 9 | width: 1024 10 | height: 800 11 | title: qsTr("01-TableView-Simple-Different-Cols") 12 | 13 | TableView { 14 | anchors.fill: parent 15 | columnSpacing: 4; rowSpacing: 4 16 | model: _TableModel 17 | delegate: Rectangle { 18 | color: "#EEE" 19 | implicitWidth: Math.max(60, text.implicitWidth) 20 | implicitHeight: text.implicitHeight 21 | Text { 22 | id: text 23 | text: model.display 24 | width: parent.width 25 | elide: Text.ElideRight 26 | font.preferShaping: false 27 | } 28 | } 29 | Shortcut { sequence: StandardKey.Quit; onActivated: Qt.quit() } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /01-TableView-Simple-Different-Cols/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | 5 | 6 | -------------------------------------------------------------------------------- /02-TableView-ColumnWidth-Headings/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | project(02-TableView-ColumnWidth-Headings LANGUAGES CXX) 4 | 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | set(CMAKE_AUTOMOC ON) 7 | set(CMAKE_AUTORCC ON) 8 | set(CMAKE_CXX_STANDARD 11) 9 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 10 | 11 | find_package(Qt5 COMPONENTS Core Quick REQUIRED) 12 | 13 | add_executable(${PROJECT_NAME} main.cpp 14 | qml.qrc 15 | TableModel.cpp 16 | TableModel.h 17 | Proc.cpp 18 | Proc.h) 19 | target_compile_definitions(${PROJECT_NAME} PRIVATE $<$,$>:QT_QML_DEBUG>) 20 | target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core Qt5::Quick) 21 | 22 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 23 | COMMAND ${_qt5Core_install_prefix}/bin/windeployqt.exe 24 | --verbose 0 25 | --force 26 | --no-compiler-runtime 27 | --qmldir ${_qt5Core_install_prefix}/qml 28 | $ 29 | # windeployqt is not able to deploy Qt/labs/platform! Hence we do it by own. 30 | COMMAND ${CMAKE_COMMAND} -E copy_directory 31 | ${_qt5Core_install_prefix}/qml/Qt/labs/platform 32 | $/Qt/labs/platform) 33 | 34 | -------------------------------------------------------------------------------- /02-TableView-ColumnWidth-Headings/Proc.cpp: -------------------------------------------------------------------------------- 1 | #include "Proc.h" 2 | 3 | #include 4 | std::random_device dev; 5 | std::mt19937 rng(dev()); 6 | 7 | const QMap Proc::Header_Info = { 8 | {F_PID, {"PID"}}, 9 | {F_PPID, {"PPID"}}, 10 | {F_PGID, {"PGID"}}, 11 | {F_SID, {"SID"}}, 12 | {F_STAT1, {"STAT1"}}, 13 | {F_STAT2, {"STAT2"}}, 14 | {F_STAT3, {"STAT3"}}, 15 | {F_STAT4, {"STAT4"}}, 16 | {F_CMD, {"CMD"}}, 17 | {F_CMDLINE, {"CMDLINE"}} 18 | }; 19 | 20 | ProcInfo::ProcInfo() 21 | { 22 | 23 | } 24 | 25 | ProcInfo::ProcInfo(int pid) 26 | { 27 | mpid = pid; 28 | std::uniform_int_distribution dist1(1,6); 29 | ppid = dist1(rng); 30 | std::uniform_int_distribution dist2(0,100); 31 | pgid = dist2(rng); 32 | std::uniform_int_distribution dist3(100,1000); 33 | sid = dist3(rng); 34 | stat1 = QString("%1%").arg(pgid); 35 | stat2 = QString("%1 bytes be or not to be is the question...").arg(pgid + sid); 36 | stat3 = QString("%1 bytes").arg(pgid - sid); 37 | stat4 = QString("%1 bytes").arg(pgid * sid); 38 | cmd = QString("cmd%1").arg(sid); 39 | cmdline = "cmdline"; 40 | } 41 | 42 | ProcInfo::~ProcInfo() 43 | { 44 | 45 | } 46 | 47 | QString ProcInfo::toString(fields f) 48 | { 49 | switch (f) { 50 | case F_PID: return QString("%1").arg(mpid); 51 | case F_PPID: return QString("%1").arg(ppid); 52 | case F_PGID: return QString("%1").arg(pgid); 53 | case F_SID: return QString("%1").arg(sid); 54 | case F_STAT1: return stat1; 55 | case F_STAT2: return stat2; 56 | case F_STAT3: return stat3; 57 | case F_STAT4: return stat4; 58 | case F_CMD: return cmd; 59 | case F_CMDLINE: return cmdline; 60 | default: return ""; 61 | } 62 | } 63 | 64 | 65 | Proc::Proc() 66 | { 67 | 68 | } 69 | 70 | void Proc::refresh() 71 | { 72 | procs.clear(); 73 | std::uniform_int_distribution dist(1000,2500); 74 | 75 | const int numOfPids = dist(rng); 76 | 77 | for (int i = 0; i < numOfPids; ++i) 78 | { 79 | procs.insert(i, ProcInfo(i)); 80 | } 81 | } 82 | 83 | 84 | -------------------------------------------------------------------------------- /02-TableView-ColumnWidth-Headings/Proc.h: -------------------------------------------------------------------------------- 1 | #ifndef PROC_H 2 | #define PROC_H 3 | 4 | #include 5 | #include 6 | 7 | 8 | enum fields 9 | { 10 | F_PID = 0, 11 | F_PPID, 12 | F_PGID, 13 | F_SID, 14 | F_STAT1, 15 | F_STAT2, 16 | F_STAT3, 17 | F_STAT4, 18 | F_CMD, 19 | F_CMDLINE, 20 | F_END = -1 21 | }; 22 | 23 | 24 | class ProcInfo 25 | { 26 | public: 27 | ProcInfo(); 28 | ProcInfo(int pid); 29 | ~ProcInfo(); 30 | 31 | int mpid; 32 | int ppid; 33 | int pgid; 34 | int sid; 35 | QString stat1; 36 | QString stat2; 37 | QString stat3; 38 | QString stat4; 39 | QString cmd; 40 | QString cmdline; 41 | 42 | QString toString(fields f); 43 | }; 44 | 45 | using ProcList = QMap; 46 | 47 | 48 | class Proc 49 | { 50 | public: 51 | Proc(); 52 | void refresh(); 53 | 54 | ProcList procs; // processes indexed by pid 55 | 56 | static const QMap Header_Info; 57 | }; 58 | 59 | #endif // PROC_H 60 | -------------------------------------------------------------------------------- /02-TableView-ColumnWidth-Headings/TableModel.cpp: -------------------------------------------------------------------------------- 1 | #include "TableModel.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | TableModel::TableModel(QObject *parent) : QAbstractTableModel (parent) 8 | { 9 | update(); 10 | m_timer = new QTimer(this); 11 | connect(m_timer, &QTimer::timeout, this, QOverload<>::of(&TableModel::update)); 12 | m_timer->start(2500); 13 | } 14 | 15 | TableModel::~TableModel() 16 | { 17 | 18 | } 19 | 20 | int TableModel::rowCount(const QModelIndex &) const 21 | { 22 | return m_pids.count(); 23 | } 24 | 25 | int TableModel::columnCount(const QModelIndex &) const 26 | { 27 | return F_CMDLINE + 1; 28 | } 29 | 30 | QVariant TableModel::data(const QModelIndex &index, int /*role*/) const 31 | { 32 | fields field = fields(index.column()); 33 | int pid = m_pids[index.row()]; 34 | ProcInfo pi = m_proc.procs.value(pid); 35 | return pi.toString(field); 36 | } 37 | 38 | QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const 39 | { 40 | if (role != Qt::DisplayRole) 41 | return QVariant(); 42 | 43 | if (orientation == Qt::Horizontal) { 44 | // section is interpreted as column 45 | return m_proc.Header_Info.value(static_cast(section)); 46 | } else { 47 | return QString(); 48 | } 49 | } 50 | 51 | int TableModel::columnWidth(int c, const QFont *font) 52 | { 53 | if (!m_columnWidths[c]) { 54 | QString header = m_proc.Header_Info.value(static_cast(c)); 55 | QFontMetrics defaultFontMetrics = QFontMetrics(QGuiApplication::font()); 56 | QFontMetrics fm = (font ? QFontMetrics(*font) : defaultFontMetrics); 57 | int ret = fm.horizontalAdvance(headerData(c, Qt::Horizontal).toString() + QLatin1String(" ^")) + 8; 58 | for (int r = 0; r < m_pids.count(); ++r) { 59 | ProcInfo pi = m_proc.procs.value(m_pids[r]); 60 | ret = qMax(ret, fm.horizontalAdvance(pi.toString(fields(c)))); 61 | } 62 | m_columnWidths[c] = ret; 63 | } 64 | return m_columnWidths[c]; 65 | 66 | } 67 | 68 | void TableModel::update() 69 | { 70 | qDebug() << __FUNCTION__; 71 | beginResetModel(); 72 | m_proc.refresh(); 73 | m_pids = m_proc.procs.keys().toVector(); 74 | std::sort(m_pids.begin(), m_pids.end()); 75 | endResetModel(); 76 | } 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /02-TableView-ColumnWidth-Headings/TableModel.h: -------------------------------------------------------------------------------- 1 | #ifndef TABLEMODEL_H 2 | #define TABLEMODEL_H 3 | 4 | 5 | #include "Proc.h" 6 | 7 | #include 8 | #include 9 | 10 | class TableModel : public QAbstractTableModel 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit TableModel(QObject *parent= nullptr); 16 | ~TableModel(); 17 | 18 | int rowCount(const QModelIndex & = QModelIndex()) const override; 19 | int columnCount(const QModelIndex & = QModelIndex()) const override; 20 | QVariant data(const QModelIndex &index, int) const override; 21 | QVariant headerData(int section, Qt::Orientation orientation, 22 | int role = Qt::DisplayRole) const override; 23 | Q_INVOKABLE int columnWidth(int c, const QFont *font = nullptr); 24 | 25 | 26 | private slots: 27 | void update(); 28 | 29 | private: 30 | Proc m_proc; 31 | QVector m_pids; 32 | QVector m_columnWidths = QVector(F_CMDLINE + 1); 33 | QTimer* m_timer; 34 | 35 | }; 36 | 37 | 38 | #endif 39 | 40 | -------------------------------------------------------------------------------- /02-TableView-ColumnWidth-Headings/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "TableModel.h" 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | QGuiApplication app(argc, argv); 11 | 12 | qmlRegisterType("TableModel", 0, 1, "TableModel"); 13 | TableModel tm; 14 | 15 | 16 | QQmlApplicationEngine engine; 17 | 18 | engine.rootContext()->setContextProperty("_TableModel", &tm); 19 | 20 | engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 21 | 22 | 23 | return app.exec(); 24 | } 25 | 26 | -------------------------------------------------------------------------------- /02-TableView-ColumnWidth-Headings/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Window 2.12 3 | import TableModel 0.1 4 | import QtQuick.Controls 2.0 5 | 6 | ApplicationWindow { 7 | id: window 8 | visible: true 9 | width: 1024 10 | height: 800 11 | title: qsTr("02-TableView-ColumnWidth-Headings") 12 | 13 | Rectangle { 14 | anchors.fill: parent 15 | Row { 16 | id: header 17 | width: table.contentWidth 18 | height: 40 19 | x: -table.contentX 20 | z: 1 21 | spacing: 4 22 | Repeater { 23 | model: table.model.columnCount() 24 | Rectangle { 25 | width: table.model.columnWidth(index); height: parent.height 26 | color: "orange" 27 | 28 | Text { 29 | anchors.verticalCenter: parent.verticalCenter 30 | x: 4 31 | width: parent.width - 4 32 | text: table.model.headerData(index, Qt.Horizontal) 33 | } 34 | } 35 | } 36 | } 37 | TableView { 38 | id: table 39 | anchors.fill: parent 40 | anchors.topMargin: header.height 41 | columnSpacing: 4; rowSpacing: 4 42 | model: _TableModel 43 | columnWidthProvider: function(column) { return Math.min(600, model.columnWidth(column)) } 44 | 45 | delegate: Rectangle { 46 | color: "#EEE" 47 | implicitHeight: text.implicitHeight 48 | Text { 49 | id: text 50 | text: model.display 51 | width: parent.width 52 | elide: column == 49 ? Text.ElideLeft : Text.ElideRight 53 | font.preferShaping: false 54 | } 55 | } 56 | 57 | ScrollBar.horizontal: ScrollBar { } 58 | ScrollBar.vertical: ScrollBar { } 59 | } 60 | Shortcut { sequence: StandardKey.Quit; onActivated: Qt.quit() } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /02-TableView-ColumnWidth-Headings/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | 5 | 6 | -------------------------------------------------------------------------------- /03-TableView-Columns-Sortable/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | project(03-TableView-Columns-Sortable LANGUAGES CXX) 4 | 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | set(CMAKE_AUTOMOC ON) 7 | set(CMAKE_AUTORCC ON) 8 | set(CMAKE_CXX_STANDARD 11) 9 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 10 | 11 | find_package(Qt5 COMPONENTS Core Quick REQUIRED) 12 | 13 | add_executable(${PROJECT_NAME} main.cpp 14 | qml.qrc 15 | ProcessModel.cpp 16 | ProcessModel.h 17 | Proc.cpp 18 | Proc.h 19 | SortFilterTableModel.cpp 20 | SortFilterTableModel.h) 21 | target_compile_definitions(${PROJECT_NAME} PRIVATE $<$,$>:QT_QML_DEBUG>) 22 | target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core Qt5::Quick) 23 | 24 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 25 | COMMAND ${_qt5Core_install_prefix}/bin/windeployqt.exe 26 | --verbose 0 27 | --force 28 | --no-compiler-runtime 29 | --qmldir ${_qt5Core_install_prefix}/qml 30 | $ 31 | # windeployqt is not able to deploy Qt/labs/platform! Hence we do it by own. 32 | COMMAND ${CMAKE_COMMAND} -E copy_directory 33 | ${_qt5Core_install_prefix}/qml/Qt/labs/platform 34 | $/Qt/labs/platform) 35 | 36 | -------------------------------------------------------------------------------- /03-TableView-Columns-Sortable/Proc.cpp: -------------------------------------------------------------------------------- 1 | #include "Proc.h" 2 | 3 | #include 4 | std::random_device dev; 5 | std::mt19937 rng(dev()); 6 | 7 | const QMap Proc::Header_Info = { 8 | {F_PID, {"PID"}}, 9 | {F_PPID, {"PPID"}}, 10 | {F_PGID, {"PGID"}}, 11 | {F_SID, {"SID"}}, 12 | {F_STAT1, {"STAT1"}}, 13 | {F_STAT2, {"STAT2"}}, 14 | {F_STAT3, {"STAT3"}}, 15 | {F_STAT4, {"STAT4"}}, 16 | {F_CMD, {"CMD"}}, 17 | {F_CMDLINE, {"CMDLINE"}} 18 | }; 19 | 20 | ProcInfo::ProcInfo() 21 | { 22 | 23 | } 24 | 25 | ProcInfo::ProcInfo(int pid) 26 | { 27 | mpid = pid; 28 | std::uniform_int_distribution dist1(1,6); 29 | ppid = dist1(rng); 30 | std::uniform_int_distribution dist2(0,100); 31 | pgid = dist2(rng); 32 | std::uniform_int_distribution dist3(100,1000); 33 | sid = dist3(rng); 34 | stat1 = QString("%1%").arg(pgid); 35 | stat2 = QString("%1 bytes be or not to be is the question...").arg(pgid + sid); 36 | stat3 = QString("%1 bytes").arg(pgid - sid); 37 | stat4 = QString("%1 bytes").arg(pgid * sid); 38 | cmd = QString("cmd%1").arg(sid); 39 | cmdline = "cmdline"; 40 | } 41 | 42 | ProcInfo::~ProcInfo() 43 | { 44 | 45 | } 46 | 47 | QVariant ProcInfo::toVariant(fields f) 48 | { 49 | switch (f) { 50 | case F_PID: return mpid; 51 | case F_PPID: return ppid; 52 | case F_PGID: return pgid; 53 | case F_SID: return sid; 54 | case F_STAT1: return stat1; 55 | case F_STAT2: return stat2; 56 | case F_STAT3: return stat3; 57 | case F_STAT4: return stat4; 58 | case F_CMD: return cmd; 59 | case F_CMDLINE: return cmdline; 60 | default: return ""; 61 | } 62 | } 63 | 64 | 65 | 66 | Proc::Proc() 67 | { 68 | 69 | } 70 | 71 | void Proc::refresh() 72 | { 73 | procs.clear(); 74 | std::uniform_int_distribution dist(1000,2500); 75 | 76 | const int numOfPids = dist(rng); 77 | 78 | for (int i = 0; i < numOfPids; ++i) 79 | { 80 | procs.insert(i, ProcInfo(i)); 81 | } 82 | } 83 | 84 | 85 | -------------------------------------------------------------------------------- /03-TableView-Columns-Sortable/Proc.h: -------------------------------------------------------------------------------- 1 | #ifndef PROC_H 2 | #define PROC_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | enum fields 10 | { 11 | F_PID = 0, 12 | F_PPID, 13 | F_PGID, 14 | F_SID, 15 | F_STAT1, 16 | F_STAT2, 17 | F_STAT3, 18 | F_STAT4, 19 | F_CMD, 20 | F_CMDLINE, 21 | F_END = -1 22 | }; 23 | 24 | 25 | class ProcInfo 26 | { 27 | public: 28 | ProcInfo(); 29 | ProcInfo(int pid); 30 | ~ProcInfo(); 31 | 32 | int mpid; 33 | int ppid; 34 | int pgid; 35 | int sid; 36 | QString stat1; 37 | QString stat2; 38 | QString stat3; 39 | QString stat4; 40 | QString cmd; 41 | QString cmdline; 42 | 43 | QVariant toVariant(fields f); 44 | }; 45 | 46 | using ProcList = QMap; 47 | 48 | 49 | class Proc 50 | { 51 | public: 52 | Proc(); 53 | void refresh(); 54 | 55 | ProcList procs; // processes indexed by pid 56 | 57 | static const QMap Header_Info; 58 | }; 59 | 60 | #endif // PROC_H 61 | -------------------------------------------------------------------------------- /03-TableView-Columns-Sortable/ProcessModel.cpp: -------------------------------------------------------------------------------- 1 | #include "ProcessModel.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | ProcessModel::ProcessModel(QObject *parent) : QAbstractTableModel (parent) 8 | { 9 | update(); 10 | } 11 | 12 | ProcessModel::~ProcessModel() 13 | { 14 | 15 | } 16 | 17 | int ProcessModel::rowCount(const QModelIndex &) const 18 | { 19 | return m_pids.count(); 20 | } 21 | 22 | int ProcessModel::columnCount(const QModelIndex &) const 23 | { 24 | return F_CMDLINE + 1; 25 | } 26 | 27 | QVariant ProcessModel::data(const QModelIndex &index, int /*role*/) const 28 | { 29 | fields field = fields(index.column()); 30 | int pid = m_pids[index.row()]; 31 | ProcInfo pi = m_proc.procs.value(pid); 32 | return pi.toVariant(field); 33 | } 34 | 35 | QVariant ProcessModel::headerData(int section, Qt::Orientation orientation, int role) const 36 | { 37 | if (role != Qt::DisplayRole) 38 | return QVariant(); 39 | 40 | if (orientation == Qt::Horizontal) { 41 | // section is interpreted as column 42 | return m_proc.Header_Info.value(static_cast(section)); 43 | } else { 44 | return QString(); 45 | } 46 | } 47 | 48 | int ProcessModel::columnWidth(int c, const QFont *font) 49 | { 50 | if (!m_columnWidths[c]) { 51 | QString header = m_proc.Header_Info.value(static_cast(c)); 52 | QFontMetrics defaultFontMetrics = QFontMetrics(QGuiApplication::font()); 53 | QFontMetrics fm = (font ? QFontMetrics(*font) : defaultFontMetrics); 54 | int ret = fm.horizontalAdvance(headerData(c, Qt::Horizontal).toString() + QLatin1String(" ^")) + 8; 55 | for (int r = 0; r < m_pids.count(); ++r) { 56 | ProcInfo pi = m_proc.procs.value(m_pids[r]); 57 | ret = qMax(ret, fm.horizontalAdvance(pi.toVariant(fields(c)).toString())); 58 | } 59 | m_columnWidths[c] = ret; 60 | } 61 | return m_columnWidths[c]; 62 | 63 | } 64 | 65 | void ProcessModel::update() 66 | { 67 | qDebug() << __FUNCTION__; 68 | beginResetModel(); 69 | m_proc.refresh(); 70 | m_pids = m_proc.procs.keys().toVector(); 71 | std::sort(m_pids.begin(), m_pids.end()); 72 | endResetModel(); 73 | } 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /03-TableView-Columns-Sortable/ProcessModel.h: -------------------------------------------------------------------------------- 1 | #ifndef ProcessModel_H 2 | #define ProcessModel_H 3 | 4 | 5 | #include "Proc.h" 6 | 7 | #include 8 | #include 9 | 10 | class ProcessModel : public QAbstractTableModel 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit ProcessModel(QObject *parent= nullptr); 16 | ~ProcessModel(); 17 | 18 | int rowCount(const QModelIndex & = QModelIndex()) const override; 19 | int columnCount(const QModelIndex & = QModelIndex()) const override; 20 | QVariant data(const QModelIndex &index, int) const override; 21 | QVariant headerData(int section, Qt::Orientation orientation, 22 | int role = Qt::DisplayRole) const override; 23 | Q_INVOKABLE int columnWidth(int c, const QFont *font = nullptr); 24 | Q_INVOKABLE void update(); 25 | 26 | 27 | private: 28 | Proc m_proc; 29 | QVector m_pids; 30 | QVector m_columnWidths = QVector(F_CMDLINE + 1); 31 | }; 32 | 33 | 34 | #endif 35 | 36 | -------------------------------------------------------------------------------- /03-TableView-Columns-Sortable/SortFilterTableModel.cpp: -------------------------------------------------------------------------------- 1 | #include "SortFilterTableModel.h" 2 | #include 3 | 4 | SortFilterTableModel::SortFilterTableModel(QObject *parent) 5 | : QSortFilterProxyModel (parent) 6 | { 7 | setSourceModel(&m_ProcessModel); 8 | // default fields that "top" displays 9 | m_fields << F_PID 10 | << F_PPID 11 | << F_PGID 12 | << F_SID 13 | << F_STAT1 14 | << F_STAT2 15 | << F_STAT3 16 | << F_STAT4 17 | << F_CMD 18 | << F_CMDLINE; 19 | } 20 | 21 | void SortFilterTableModel::sort(int column, Qt::SortOrder order) 22 | { 23 | qDebug() << column << m_fields[column] << order; 24 | QSortFilterProxyModel::sort(m_fields[column], order); 25 | } 26 | 27 | int SortFilterTableModel::columnCount(const QModelIndex &parent) const 28 | { 29 | Q_UNUSED(parent) 30 | return m_fields.count(); 31 | } 32 | 33 | int SortFilterTableModel::columnWidth(int c, const QFont *font) 34 | { 35 | if (c < 0 || c >= m_fields.count()) 36 | return 0; 37 | return m_ProcessModel.columnWidth(m_fields[c], font); 38 | } 39 | 40 | QModelIndex SortFilterTableModel::mapFromSource(const QModelIndex &sourceIndex) const 41 | { 42 | int row = QSortFilterProxyModel::mapFromSource(sourceIndex).row(); 43 | fields field = fields(sourceIndex.column()); 44 | return m_ProcessModel.index(row, m_fields.indexOf(field)); 45 | } 46 | 47 | QModelIndex SortFilterTableModel::mapToSource(const QModelIndex &proxyIndex) const 48 | { 49 | QModelIndex rowIndex = QSortFilterProxyModel::mapToSource(proxyIndex); 50 | int col = -1; 51 | if (proxyIndex.column() >= 0 && proxyIndex.column() < m_fields.count()) 52 | col = m_fields[proxyIndex.column()]; 53 | return m_ProcessModel.index(rowIndex.row(), col); 54 | } 55 | -------------------------------------------------------------------------------- /03-TableView-Columns-Sortable/SortFilterTableModel.h: -------------------------------------------------------------------------------- 1 | #ifndef SortFilterTableModel_H 2 | #define SortFilterTableModel_H 3 | 4 | #include 5 | #include 6 | #include "ProcessModel.h" 7 | 8 | class SortFilterTableModel : public QSortFilterProxyModel 9 | { 10 | Q_OBJECT 11 | Q_PROPERTY(ProcessModel *processModel READ processModel CONSTANT) 12 | Q_CLASSINFO("DefaultProperty", "data") 13 | public: 14 | SortFilterTableModel(QObject *parent = nullptr); 15 | 16 | ProcessModel *processModel() { return &m_ProcessModel; } 17 | Q_INVOKABLE void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; 18 | int columnCount(const QModelIndex &parent = QModelIndex()) const override; 19 | Q_INVOKABLE int columnWidth(int c, const QFont *font = nullptr); 20 | 21 | QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override; 22 | QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; 23 | 24 | private: 25 | ProcessModel m_ProcessModel; 26 | QVector m_fields; 27 | }; 28 | 29 | #endif // SortFilterTableModel_H 30 | -------------------------------------------------------------------------------- /03-TableView-Columns-Sortable/SortableColumnHeading.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | 3 | Rectangle { 4 | id: root 5 | color: "wheat" 6 | property alias text: label.text 7 | signal sorting 8 | 9 | function stopSorting() { 10 | state = "" 11 | } 12 | 13 | Text { 14 | id: label 15 | anchors.verticalCenter: parent.verticalCenter 16 | x: 4 17 | width: parent.width - 4 18 | text: table.model.headerData(index, Qt.Horizontal) 19 | } 20 | 21 | Text { 22 | id: upDownIndicator 23 | anchors.right: parent.right 24 | anchors.margins: 4 25 | anchors.verticalCenter: parent.verticalCenter 26 | text: "^" 27 | visible: false 28 | } 29 | 30 | TapHandler { id: tap; onTapped: nextState() } 31 | 32 | function nextState() { 33 | if (state == "up") 34 | state = "down" 35 | else 36 | state = "up" 37 | root.sorting() 38 | } 39 | 40 | states: [ 41 | State { 42 | name: "up" 43 | PropertyChanges { target: upDownIndicator; visible: true; rotation: 0 } 44 | PropertyChanges { target: root; color: "orange" } 45 | }, 46 | State { 47 | name: "down" 48 | PropertyChanges { target: upDownIndicator; visible: true; rotation: 180 } 49 | PropertyChanges { target: root; color: "orange" } 50 | } 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /03-TableView-Columns-Sortable/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "ProcessModel.h" 7 | #include "SortFilterTableModel.h" 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | QGuiApplication app(argc, argv); 12 | 13 | qmlRegisterType("ProcessModel", 0, 1, "ProcessModel"); 14 | qmlRegisterType("SortFilterTableModel", 0, 1, "SortFilterTableModel"); 15 | 16 | QQmlApplicationEngine engine; 17 | 18 | engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 19 | 20 | 21 | return app.exec(); 22 | } 23 | 24 | -------------------------------------------------------------------------------- /03-TableView-Columns-Sortable/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Window 2.12 3 | import QtQuick.Layouts 1.12 4 | import ProcessModel 0.1 5 | import SortFilterTableModel 0.1 6 | import QtQuick.Controls 2.4 7 | 8 | ApplicationWindow { 9 | id: window 10 | visible: true 11 | width: 1024 12 | height: 800 13 | title: qsTr("03-TableView-Columns-Sortable") 14 | 15 | header: ToolBar { 16 | RowLayout { 17 | anchors.fill: parent 18 | Switch { 19 | id: cbUpdate 20 | checked: true 21 | text: qsTr("Update every") 22 | } 23 | SpinBox { 24 | id: sbUpdate 25 | from: 1 26 | to: 60 27 | value: 2 28 | enabled: cbUpdate.checked 29 | } 30 | Label { 31 | text: "sec" 32 | } 33 | Item { 34 | Layout.fillWidth: true 35 | } 36 | } 37 | } 38 | Row { 39 | id: header 40 | width: table.contentWidth 41 | height: cbUpdate.height 42 | x: -table.contentX 43 | z: 1 44 | spacing: 4 45 | Repeater { 46 | id: peter 47 | model: table.model.columnCount() 48 | SortableColumnHeading { 49 | width: Math.min(600, table.model.columnWidth(index)); height: parent.height 50 | text: table.model.headerData(index, Qt.Horizontal) 51 | onSorting: { 52 | for (var i = 0; i < peter.model; ++i) 53 | if (i != index) 54 | peter.itemAt(i).stopSorting() 55 | table.model.sort(index, state == "up" ? Qt.AscendingOrder : Qt.DescendingOrder) 56 | } 57 | } 58 | } 59 | } 60 | 61 | TableView { 62 | id: table 63 | anchors.fill: parent 64 | anchors.topMargin: header.height 65 | columnSpacing: 4; rowSpacing: 4 66 | model: SortFilterTableModel { } 67 | Timer { 68 | interval: sbUpdate.value * 1000 69 | repeat: true 70 | running: cbUpdate.checked 71 | onTriggered: table.model.processModel.update() 72 | } 73 | columnWidthProvider: function(column) { return Math.min(600, model.columnWidth(column)) } 74 | 75 | delegate: Rectangle { 76 | color: "#EEE" 77 | implicitHeight: text.implicitHeight 78 | Text { 79 | id: text 80 | text: model.display 81 | width: parent.width 82 | elide: column == 49 ? Text.ElideLeft : Text.ElideRight 83 | font.preferShaping: false 84 | } 85 | } 86 | 87 | ScrollBar.horizontal: ScrollBar { } 88 | ScrollBar.vertical: ScrollBar { } 89 | } 90 | Shortcut { sequence: StandardKey.Quit; onActivated: Qt.quit() } 91 | } 92 | -------------------------------------------------------------------------------- /03-TableView-Columns-Sortable/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | SortableColumnHeading.qml 5 | 6 | 7 | -------------------------------------------------------------------------------- /04-TableView-Sort-Init-Filter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | project(04-TableView-Sort-Init-Filter LANGUAGES CXX) 4 | 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | set(CMAKE_AUTOMOC ON) 7 | set(CMAKE_AUTORCC ON) 8 | set(CMAKE_CXX_STANDARD 11) 9 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 10 | 11 | find_package(Qt5 COMPONENTS Core Quick REQUIRED) 12 | 13 | add_executable(${PROJECT_NAME} main.cpp 14 | qml.qrc 15 | ProcessModel.cpp 16 | ProcessModel.h 17 | Proc.cpp 18 | Proc.h 19 | SortFilterTableModel.cpp 20 | SortFilterTableModel.h) 21 | target_compile_definitions(${PROJECT_NAME} PRIVATE $<$,$>:QT_QML_DEBUG>) 22 | target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core Qt5::Quick) 23 | 24 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 25 | COMMAND ${_qt5Core_install_prefix}/bin/windeployqt.exe 26 | --verbose 0 27 | --force 28 | --no-compiler-runtime 29 | --qmldir ${_qt5Core_install_prefix}/qml 30 | $ 31 | # windeployqt is not able to deploy Qt/labs/platform! Hence we do it by own. 32 | COMMAND ${CMAKE_COMMAND} -E copy_directory 33 | ${_qt5Core_install_prefix}/qml/Qt/labs/platform 34 | $/Qt/labs/platform) 35 | 36 | -------------------------------------------------------------------------------- /04-TableView-Sort-Init-Filter/Proc.cpp: -------------------------------------------------------------------------------- 1 | #include "Proc.h" 2 | 3 | #include 4 | #include 5 | std::random_device dev; 6 | std::mt19937 rng(dev()); 7 | 8 | const QMap Proc::Header_Info = { 9 | {F_PID, {"PID"}}, 10 | {F_PPID, {"PPID"}}, 11 | {F_PGID, {"PGID"}}, 12 | {F_SID, {"SID"}}, 13 | {F_STAT1, {"STAT1"}}, 14 | {F_STAT2, {"STAT2"}}, 15 | {F_STAT3, {"STAT3"}}, 16 | {F_STAT4, {"STAT4"}}, 17 | {F_CMD, {"CMD"}}, 18 | {F_CMDLINE, {"CMDLINE"}} 19 | }; 20 | 21 | QString random_string(int length=32, QString allow_symbols=QString("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")) { 22 | QString result; 23 | qsrand(QTime::currentTime().msec()); 24 | for (int i = 0; i < length; ++i) { 25 | std::uniform_int_distribution dist1(0,allow_symbols.length()); 26 | result.append(allow_symbols.at(dist1(rng))); 27 | } 28 | return result; 29 | } 30 | 31 | ProcInfo::ProcInfo() 32 | { 33 | 34 | } 35 | 36 | ProcInfo::ProcInfo(int pid) 37 | { 38 | mpid = pid; 39 | std::uniform_int_distribution dist1(1,6); 40 | ppid = dist1(rng); 41 | std::uniform_int_distribution dist2(0,100); 42 | pgid = dist2(rng); 43 | std::uniform_int_distribution dist3(100,1000); 44 | sid = dist3(rng); 45 | stat1 = QString("%1%").arg(pgid); 46 | stat2 = QString("%1 bytes be or not to be is the question...").arg(pgid + sid); 47 | stat3 = QString("%1 bytes").arg(pgid - sid); 48 | stat4 = QString("%1 bytes").arg(pgid * sid); 49 | cmd = random_string(); 50 | cmdline = random_string(); 51 | } 52 | 53 | ProcInfo::~ProcInfo() 54 | { 55 | 56 | } 57 | 58 | QVariant ProcInfo::toVariant(fields f) 59 | { 60 | switch (f) { 61 | case F_PID: return mpid; 62 | case F_PPID: return ppid; 63 | case F_PGID: return pgid; 64 | case F_SID: return sid; 65 | case F_STAT1: return stat1; 66 | case F_STAT2: return stat2; 67 | case F_STAT3: return stat3; 68 | case F_STAT4: return stat4; 69 | case F_CMD: return cmd; 70 | case F_CMDLINE: return cmdline; 71 | default: return ""; 72 | } 73 | } 74 | 75 | 76 | 77 | Proc::Proc() 78 | { 79 | 80 | } 81 | 82 | void Proc::refresh() 83 | { 84 | procs.clear(); 85 | std::uniform_int_distribution dist(1000,2500); 86 | 87 | const int numOfPids = dist(rng); 88 | 89 | for (int i = 0; i < numOfPids; ++i) 90 | { 91 | procs.insert(i, ProcInfo(i)); 92 | } 93 | } 94 | 95 | 96 | -------------------------------------------------------------------------------- /04-TableView-Sort-Init-Filter/Proc.h: -------------------------------------------------------------------------------- 1 | #ifndef PROC_H 2 | #define PROC_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | enum fields 10 | { 11 | F_PID = 0, 12 | F_PPID, 13 | F_PGID, 14 | F_SID, 15 | F_STAT1, 16 | F_STAT2, 17 | F_STAT3, 18 | F_STAT4, 19 | F_CMD, 20 | F_CMDLINE, 21 | F_END = -1 22 | }; 23 | 24 | 25 | class ProcInfo 26 | { 27 | public: 28 | ProcInfo(); 29 | ProcInfo(int pid); 30 | ~ProcInfo(); 31 | 32 | int mpid; 33 | int ppid; 34 | int pgid; 35 | int sid; 36 | QString stat1; 37 | QString stat2; 38 | QString stat3; 39 | QString stat4; 40 | QString cmd; 41 | QString cmdline; 42 | 43 | QVariant toVariant(fields f); 44 | }; 45 | 46 | using ProcList = QMap; 47 | 48 | 49 | class Proc 50 | { 51 | public: 52 | Proc(); 53 | void refresh(); 54 | 55 | ProcList procs; // processes indexed by pid 56 | 57 | static const QMap Header_Info; 58 | }; 59 | 60 | #endif // PROC_H 61 | -------------------------------------------------------------------------------- /04-TableView-Sort-Init-Filter/ProcessModel.cpp: -------------------------------------------------------------------------------- 1 | #include "ProcessModel.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | ProcessModel::ProcessModel(QObject *parent) : QAbstractTableModel (parent) 8 | { 9 | update(); 10 | } 11 | 12 | ProcessModel::~ProcessModel() 13 | { 14 | 15 | } 16 | 17 | int ProcessModel::rowCount(const QModelIndex &) const 18 | { 19 | return m_pids.count(); 20 | } 21 | 22 | int ProcessModel::columnCount(const QModelIndex &) const 23 | { 24 | return F_CMDLINE + 1; 25 | } 26 | 27 | QVariant ProcessModel::data(const QModelIndex &index, int role) const 28 | { 29 | fields field = fields(index.column()); 30 | int pid = m_pids[index.row()]; 31 | ProcInfo pi = m_proc.procs.value(pid); 32 | switch (role) { 33 | case Qt::DisplayRole: 34 | return pi.toVariant(field); 35 | case Qt::InitialSortOrderRole: { 36 | bool numeric = false; 37 | pi.toVariant(field).toFloat(&numeric); 38 | if (numeric) 39 | return Qt::DescendingOrder; 40 | return Qt::AscendingOrder; 41 | } 42 | case int(ProcessModel::Role::Sort): 43 | return pi.toVariant(field); 44 | default: 45 | return QVariant(); 46 | } 47 | 48 | return pi.toVariant(field); 49 | } 50 | 51 | QVariant ProcessModel::headerData(int section, Qt::Orientation orientation, int role) const 52 | { 53 | if (role != Qt::DisplayRole) 54 | return QVariant(); 55 | 56 | if (orientation == Qt::Horizontal) { 57 | // section is interpreted as column 58 | return m_proc.Header_Info.value(static_cast(section)); 59 | } else { 60 | return QString(); 61 | } 62 | } 63 | 64 | int ProcessModel::columnWidth(int c, const QFont *font) 65 | { 66 | if (!m_columnWidths[c]) { 67 | QString header = m_proc.Header_Info.value(static_cast(c)); 68 | QFontMetrics defaultFontMetrics = QFontMetrics(QGuiApplication::font()); 69 | QFontMetrics fm = (font ? QFontMetrics(*font) : defaultFontMetrics); 70 | int ret = fm.horizontalAdvance(headerData(c, Qt::Horizontal).toString() + QLatin1String(" ^")) + 8; 71 | for (int r = 0; r < m_pids.count(); ++r) { 72 | ProcInfo pi = m_proc.procs.value(m_pids[r]); 73 | ret = qMax(ret, fm.horizontalAdvance(pi.toVariant(fields(c)).toString())); 74 | } 75 | m_columnWidths[c] = ret; 76 | } 77 | return m_columnWidths[c]; 78 | 79 | } 80 | 81 | void ProcessModel::update() 82 | { 83 | qDebug() << __FUNCTION__; 84 | beginResetModel(); 85 | m_proc.refresh(); 86 | m_pids = m_proc.procs.keys().toVector(); 87 | std::sort(m_pids.begin(), m_pids.end()); 88 | endResetModel(); 89 | } 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /04-TableView-Sort-Init-Filter/ProcessModel.h: -------------------------------------------------------------------------------- 1 | #ifndef ProcessModel_H 2 | #define ProcessModel_H 3 | 4 | 5 | #include "Proc.h" 6 | 7 | #include 8 | #include 9 | 10 | class ProcessModel : public QAbstractTableModel 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | // supplemental roles beyond Qt::ItemDataRole 16 | enum class Role { 17 | Sort = Qt::UserRole 18 | }; 19 | 20 | explicit ProcessModel(QObject *parent= nullptr); 21 | ~ProcessModel(); 22 | 23 | int rowCount(const QModelIndex & = QModelIndex()) const override; 24 | int columnCount(const QModelIndex & = QModelIndex()) const override; 25 | QVariant data(const QModelIndex &index, int role) const override; 26 | QVariant headerData(int section, Qt::Orientation orientation, 27 | int role = Qt::DisplayRole) const override; 28 | Q_INVOKABLE int columnWidth(int c, const QFont *font = nullptr); 29 | Q_INVOKABLE void update(); 30 | 31 | 32 | private: 33 | Proc m_proc; 34 | QVector m_pids; 35 | QVector m_columnWidths = QVector(F_CMDLINE + 1); 36 | }; 37 | 38 | 39 | #endif 40 | 41 | -------------------------------------------------------------------------------- /04-TableView-Sort-Init-Filter/SortFilterTableModel.cpp: -------------------------------------------------------------------------------- 1 | #include "SortFilterTableModel.h" 2 | #include 3 | 4 | SortFilterTableModel::SortFilterTableModel(QObject *parent) 5 | : QSortFilterProxyModel (parent) 6 | { 7 | setSourceModel(&m_ProcessModel); 8 | setSortRole(int(ProcessModel::Role::Sort)); 9 | // default fields that "top" displays 10 | m_fields << F_PID 11 | << F_PPID 12 | << F_PGID 13 | << F_SID 14 | << F_STAT1 15 | << F_STAT2 16 | << F_STAT3 17 | << F_STAT4 18 | << F_CMD 19 | << F_CMDLINE; 20 | setFilterKeyColumn(-1); //With -1: Include all columns to filter 21 | } 22 | 23 | void SortFilterTableModel::setFilterText(QString filterText) 24 | { 25 | if (m_filterText == filterText) 26 | return; 27 | 28 | m_filterText = filterText; 29 | setFilterRegularExpression(filterText); 30 | emit filterTextChanged(m_filterText); 31 | } 32 | 33 | 34 | void SortFilterTableModel::sort(int column, Qt::SortOrder order) 35 | { 36 | qDebug() << column << m_fields[column] << order; 37 | QSortFilterProxyModel::sort(m_fields[column], order); 38 | } 39 | 40 | int SortFilterTableModel::columnCount(const QModelIndex &parent) const 41 | { 42 | Q_UNUSED(parent) 43 | return m_fields.count(); 44 | } 45 | 46 | int SortFilterTableModel::columnWidth(int c, const QFont *font) 47 | { 48 | if (c < 0 || c >= m_fields.count()) 49 | return 0; 50 | return m_ProcessModel.columnWidth(m_fields[c], font); 51 | } 52 | 53 | Qt::SortOrder SortFilterTableModel::initialSortOrder(int column) const 54 | { 55 | bool ok = false; 56 | if (column < 0 || column >= m_fields.count()) 57 | return Qt::AscendingOrder; 58 | int ret = m_ProcessModel.data(m_ProcessModel.index(0, m_fields[column]), Qt::InitialSortOrderRole).toInt(&ok); 59 | if (ok) 60 | return Qt::SortOrder(ret); 61 | else 62 | return Qt::AscendingOrder; 63 | } 64 | 65 | QModelIndex SortFilterTableModel::mapFromSource(const QModelIndex &sourceIndex) const 66 | { 67 | int row = QSortFilterProxyModel::mapFromSource(sourceIndex).row(); 68 | fields field = fields(sourceIndex.column()); 69 | return m_ProcessModel.index(row, m_fields.indexOf(field)); 70 | } 71 | 72 | QModelIndex SortFilterTableModel::mapToSource(const QModelIndex &proxyIndex) const 73 | { 74 | QModelIndex rowIndex = QSortFilterProxyModel::mapToSource(proxyIndex); 75 | int col = -1; 76 | if (proxyIndex.column() >= 0 && proxyIndex.column() < m_fields.count()) 77 | col = m_fields[proxyIndex.column()]; 78 | return m_ProcessModel.index(rowIndex.row(), col); 79 | } 80 | 81 | -------------------------------------------------------------------------------- /04-TableView-Sort-Init-Filter/SortFilterTableModel.h: -------------------------------------------------------------------------------- 1 | #ifndef SortFilterTableModel_H 2 | #define SortFilterTableModel_H 3 | 4 | #include 5 | #include 6 | #include "ProcessModel.h" 7 | 8 | class SortFilterTableModel : public QSortFilterProxyModel 9 | { 10 | Q_OBJECT 11 | Q_PROPERTY(ProcessModel *processModel READ processModel CONSTANT) 12 | Q_PROPERTY(QString filterText READ filterText WRITE setFilterText NOTIFY filterTextChanged) 13 | Q_CLASSINFO("DefaultProperty", "data") 14 | public: 15 | SortFilterTableModel(QObject *parent = nullptr); 16 | 17 | ProcessModel *processModel() { return &m_ProcessModel; } 18 | QString filterText() const {return m_filterText;} 19 | void setFilterText(QString filterText); 20 | Q_INVOKABLE void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; 21 | int columnCount(const QModelIndex &parent = QModelIndex()) const override; 22 | Q_INVOKABLE int columnWidth(int c, const QFont *font = nullptr); 23 | Q_INVOKABLE Qt::SortOrder initialSortOrder(int column) const; 24 | 25 | QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override; 26 | QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; 27 | 28 | 29 | signals: 30 | void filterTextChanged(QString filterText); 31 | 32 | private: 33 | ProcessModel m_ProcessModel; 34 | QVector m_fields; 35 | QString m_filterText; 36 | }; 37 | 38 | #endif // SortFilterTableModel_H 39 | -------------------------------------------------------------------------------- /04-TableView-Sort-Init-Filter/SortableColumnHeading.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | 3 | Rectangle { 4 | id: root 5 | color: "wheat" 6 | property int initialSortOrder: Qt.AscendingOrder 7 | property alias text: label.text 8 | signal sorting 9 | 10 | function stopSorting() { 11 | state = "" 12 | } 13 | 14 | Text { 15 | id: label 16 | anchors.verticalCenter: parent.verticalCenter 17 | x: 4 18 | width: parent.width - 4 19 | text: table.model.headerData(index, Qt.Horizontal) 20 | } 21 | 22 | Text { 23 | id: upDownIndicator 24 | anchors.right: parent.right 25 | anchors.margins: 4 26 | anchors.verticalCenter: parent.verticalCenter 27 | text: "^" 28 | visible: false 29 | } 30 | 31 | TapHandler { id: tap; onTapped: nextState() } 32 | 33 | function nextState() { 34 | if (state == "") 35 | state = (initialSortOrder == Qt.DescendingOrder ? "down" : "up") 36 | else if (state == "up") 37 | state = "down" 38 | else 39 | state = "up" 40 | root.sorting() 41 | } 42 | states: [ 43 | State { 44 | name: "up" 45 | PropertyChanges { target: upDownIndicator; visible: true; rotation: 0 } 46 | PropertyChanges { target: root; color: "orange" } 47 | }, 48 | State { 49 | name: "down" 50 | PropertyChanges { target: upDownIndicator; visible: true; rotation: 180 } 51 | PropertyChanges { target: root; color: "orange" } 52 | } 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /04-TableView-Sort-Init-Filter/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "ProcessModel.h" 7 | #include "SortFilterTableModel.h" 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | QGuiApplication app(argc, argv); 12 | 13 | qmlRegisterType("ProcessModel", 0, 1, "ProcessModel"); 14 | qmlRegisterType("SortFilterTableModel", 0, 1, "SortFilterTableModel"); 15 | 16 | QQmlApplicationEngine engine; 17 | 18 | engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 19 | 20 | 21 | return app.exec(); 22 | } 23 | 24 | -------------------------------------------------------------------------------- /04-TableView-Sort-Init-Filter/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Window 2.12 3 | import QtQuick.Layouts 1.12 4 | import ProcessModel 0.1 5 | import SortFilterTableModel 0.1 6 | import QtQuick.Controls 2.4 7 | 8 | ApplicationWindow { 9 | id: window 10 | visible: true 11 | width: 1024 12 | height: 800 13 | title: qsTr("04-TableView-Sort-Init-Filter") 14 | 15 | header: ToolBar { 16 | RowLayout { 17 | anchors.fill: parent 18 | anchors.rightMargin: 6 19 | Switch { 20 | id: cbUpdate 21 | checked: true 22 | text: qsTr("Update every") 23 | } 24 | SpinBox { 25 | id: sbUpdate 26 | from: 1 27 | to: 60 28 | value: 2 29 | enabled: cbUpdate.checked 30 | } 31 | Label { 32 | text: "sec" 33 | } 34 | Item { 35 | Layout.fillWidth: true 36 | } 37 | TextField { 38 | id: tfFilter 39 | implicitWidth: parent.width / 4 40 | onTextEdited: table.contentY = 0 41 | } 42 | } 43 | } 44 | Row { 45 | id: header 46 | width: table.contentWidth 47 | height: cbUpdate.height 48 | x: -table.contentX 49 | z: 1 50 | spacing: 4 51 | Repeater { 52 | id: peter 53 | model: table.model.columnCount() 54 | SortableColumnHeading { 55 | width: Math.min(600, table.model.columnWidth(index)); height: parent.height 56 | text: table.model.headerData(index, Qt.Horizontal) 57 | initialSortOrder: table.model.initialSortOrder(index) 58 | onSorting: { 59 | for (var i = 0; i < peter.model; ++i) 60 | if (i != index) 61 | peter.itemAt(i).stopSorting() 62 | table.model.sort(index, state == "up" ? Qt.AscendingOrder : Qt.DescendingOrder) 63 | } 64 | } 65 | } 66 | } 67 | TableView { 68 | id: table 69 | anchors.fill: parent 70 | anchors.topMargin: header.height 71 | columnSpacing: 4; rowSpacing: 4 72 | model: SortFilterTableModel { 73 | filterText: tfFilter.text 74 | } 75 | Timer { 76 | interval: sbUpdate.value * 1000 77 | repeat: true 78 | running: cbUpdate.checked 79 | onTriggered: table.model.processModel.update() 80 | } 81 | columnWidthProvider: function(column) { return Math.min(600, model.columnWidth(column)) } 82 | 83 | delegate: Rectangle { 84 | color: "#EEE" 85 | implicitHeight: text.implicitHeight 86 | Text { 87 | id: text 88 | text: model.display 89 | width: parent.width 90 | elide: column == 49 ? Text.ElideLeft : Text.ElideRight 91 | font.preferShaping: false 92 | } 93 | } 94 | 95 | ScrollBar.horizontal: ScrollBar { } 96 | ScrollBar.vertical: ScrollBar { } 97 | } 98 | Shortcut { sequence: StandardKey.Quit; onActivated: Qt.quit() } 99 | } 100 | -------------------------------------------------------------------------------- /04-TableView-Sort-Init-Filter/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | SortableColumnHeading.qml 5 | 6 | 7 | -------------------------------------------------------------------------------- /05-TableView-DelegateChooser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | project(05-TableView-DelegateChooser LANGUAGES CXX) 4 | 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | set(CMAKE_AUTOMOC ON) 7 | set(CMAKE_AUTORCC ON) 8 | set(CMAKE_CXX_STANDARD 11) 9 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 10 | 11 | find_package(Qt5 COMPONENTS Core Quick REQUIRED) 12 | 13 | add_executable(${PROJECT_NAME} main.cpp 14 | qml.qrc 15 | ProcessModel.cpp 16 | ProcessModel.h 17 | Proc.cpp 18 | Proc.h 19 | SortFilterTableModel.cpp 20 | SortFilterTableModel.h) 21 | target_compile_definitions(${PROJECT_NAME} PRIVATE $<$,$>:QT_QML_DEBUG>) 22 | target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core Qt5::Quick) 23 | 24 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 25 | COMMAND ${_qt5Core_install_prefix}/bin/windeployqt.exe 26 | --verbose 0 27 | --force 28 | --no-compiler-runtime 29 | --qmldir ${_qt5Core_install_prefix}/qml 30 | $ 31 | # windeployqt is not able to deploy Qt/labs/platform! Hence we do it by own. 32 | COMMAND ${CMAKE_COMMAND} -E copy_directory 33 | ${_qt5Core_install_prefix}/qml/Qt/labs/platform 34 | $/Qt/labs/platform 35 | # windeployqt is not able to deploy Qt/labs/qmlmodels! Hence we do it by own. 36 | COMMAND ${CMAKE_COMMAND} -E copy_directory 37 | ${_qt5Core_install_prefix}/qml/Qt/labs/qmlmodels 38 | $/Qt/labs/qmlmodels 39 | ) 40 | 41 | -------------------------------------------------------------------------------- /05-TableView-DelegateChooser/Proc.cpp: -------------------------------------------------------------------------------- 1 | #include "Proc.h" 2 | 3 | #include 4 | #include 5 | std::random_device dev; 6 | std::mt19937 rng(dev()); 7 | 8 | const QMap Proc::Header_Info = { 9 | {F_PID, {"PID"}}, 10 | {F_PPID, {"PPID"}}, 11 | {F_PGID, {"PGID"}}, 12 | {F_SID, {"SID"}}, 13 | {F_STAT1, {"STAT1"}}, 14 | {F_STAT2, {"STAT2"}}, 15 | {F_STAT3, {"STAT3"}}, 16 | {F_STAT4, {"STAT4"}}, 17 | {F_CMD, {"CMD"}}, 18 | {F_CMDLINE, {"CMDLINE"}} 19 | }; 20 | 21 | QString random_string(int length=32, QString allow_symbols=QString("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")) { 22 | QString result; 23 | qsrand(QTime::currentTime().msec()); 24 | for (int i = 0; i < length; ++i) { 25 | std::uniform_int_distribution dist1(0,allow_symbols.length()); 26 | result.append(allow_symbols.at(dist1(rng))); 27 | } 28 | return result; 29 | } 30 | 31 | ProcInfo::ProcInfo() 32 | { 33 | 34 | } 35 | 36 | ProcInfo::ProcInfo(int pid) 37 | { 38 | mpid = pid; 39 | std::uniform_int_distribution dist1(1,6); 40 | ppid = dist1(rng); 41 | std::uniform_int_distribution dist2(0,100); 42 | pgid = dist2(rng); 43 | std::uniform_int_distribution dist3(100,1000); 44 | sid = dist3(rng); 45 | stat1 = QString("%1%").arg(pgid); 46 | stat2 = QString("%1 bytes be or not to be is the question...").arg(pgid + sid); 47 | stat3 = QString("%1 bytes").arg(pgid - sid); 48 | stat4 = QString("%1 bytes").arg(pgid * sid); 49 | cmd = random_string(); 50 | cmdline = random_string(); 51 | } 52 | 53 | ProcInfo::~ProcInfo() 54 | { 55 | 56 | } 57 | 58 | QVariant ProcInfo::toVariant(fields f) 59 | { 60 | switch (f) { 61 | case F_PID: return mpid; 62 | case F_PPID: return ppid; 63 | case F_PGID: return pgid; 64 | case F_SID: return sid; 65 | case F_STAT1: return stat1; 66 | case F_STAT2: return stat2; 67 | case F_STAT3: return stat3; 68 | case F_STAT4: return stat4; 69 | case F_CMD: return cmd; 70 | case F_CMDLINE: return cmdline; 71 | default: return ""; 72 | } 73 | } 74 | 75 | 76 | 77 | Proc::Proc() 78 | { 79 | 80 | } 81 | 82 | void Proc::refresh() 83 | { 84 | procs.clear(); 85 | std::uniform_int_distribution dist(1000,2500); 86 | 87 | const int numOfPids = dist(rng); 88 | 89 | for (int i = 0; i < numOfPids; ++i) 90 | { 91 | procs.insert(i, ProcInfo(i)); 92 | } 93 | } 94 | 95 | 96 | -------------------------------------------------------------------------------- /05-TableView-DelegateChooser/Proc.h: -------------------------------------------------------------------------------- 1 | #ifndef PROC_H 2 | #define PROC_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | enum fields 10 | { 11 | F_PID = 0, 12 | F_PPID, 13 | F_PGID, 14 | F_SID, 15 | F_STAT1, 16 | F_STAT2, 17 | F_STAT3, 18 | F_STAT4, 19 | F_CMD, 20 | F_CMDLINE, 21 | F_END = -1 22 | }; 23 | 24 | 25 | class ProcInfo 26 | { 27 | public: 28 | ProcInfo(); 29 | ProcInfo(int pid); 30 | ~ProcInfo(); 31 | 32 | int mpid; 33 | int ppid; 34 | int pgid; 35 | int sid; 36 | QString stat1; 37 | QString stat2; 38 | QString stat3; 39 | QString stat4; 40 | QString cmd; 41 | QString cmdline; 42 | 43 | QVariant toVariant(fields f); 44 | }; 45 | 46 | using ProcList = QMap; 47 | 48 | 49 | class Proc 50 | { 51 | public: 52 | Proc(); 53 | void refresh(); 54 | 55 | ProcList procs; // processes indexed by pid 56 | 57 | static const QMap Header_Info; 58 | }; 59 | 60 | #endif // PROC_H 61 | -------------------------------------------------------------------------------- /05-TableView-DelegateChooser/ProcessModel.cpp: -------------------------------------------------------------------------------- 1 | #include "ProcessModel.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | ProcessModel::ProcessModel(QObject *parent) : QAbstractTableModel (parent) 8 | { 9 | m_roleNames = QAbstractTableModel::roleNames(); 10 | // TODO loop over the QMetaEnum 11 | m_roleNames.insert(int(Role::Sort), QByteArray("sort")); 12 | m_roleNames.insert(int(Role::Number), QByteArray("number")); 13 | m_roleNames.insert(int(Role::Type), QByteArray("type")); 14 | update(); 15 | } 16 | 17 | ProcessModel::~ProcessModel() 18 | { 19 | 20 | } 21 | 22 | QHash ProcessModel::roleNames() const 23 | { 24 | return m_roleNames; 25 | } 26 | 27 | int ProcessModel::rowCount(const QModelIndex &) const 28 | { 29 | return m_pids.count(); 30 | } 31 | 32 | int ProcessModel::columnCount(const QModelIndex &) const 33 | { 34 | return F_CMDLINE + 1; 35 | } 36 | 37 | QVariant ProcessModel::data(const QModelIndex &index, int role) const 38 | { 39 | fields field = fields(index.column()); 40 | int pid = m_pids[index.row()]; 41 | ProcInfo pi = m_proc.procs.value(pid); 42 | switch (role) { 43 | case Qt::DisplayRole: 44 | return pi.toVariant(field); 45 | case Qt::InitialSortOrderRole: { 46 | bool numeric = false; 47 | pi.toVariant(field).toFloat(&numeric); 48 | if (numeric) 49 | return Qt::DescendingOrder; 50 | return Qt::AscendingOrder; 51 | } 52 | case int(ProcessModel::Role::Sort): 53 | return pi.toVariant(field); 54 | case int(ProcessModel::Role::Number): 55 | return pi.toVariant(field).toDouble(); 56 | case int(ProcessModel::Role::Type): 57 | // TODO this is silly: make a virtual in the Category perhaps? 58 | switch (field) { 59 | case F_PID: 60 | case F_PPID: 61 | return QLatin1String("readonly"); 62 | case F_PGID: 63 | case F_SID: 64 | return QLatin1String("id"); 65 | case F_STAT1: 66 | case F_STAT2: 67 | case F_STAT3: 68 | case F_STAT4: 69 | return QLatin1String("stat"); 70 | case F_CMD: 71 | case F_CMDLINE: 72 | return QLatin1String("string"); 73 | default: 74 | return QLatin1String("string"); 75 | } 76 | default: 77 | return QVariant(); 78 | } 79 | 80 | return pi.toVariant(field); 81 | } 82 | 83 | QVariant ProcessModel::headerData(int section, Qt::Orientation orientation, int role) const 84 | { 85 | if (role != Qt::DisplayRole) 86 | return QVariant(); 87 | 88 | if (orientation == Qt::Horizontal) { 89 | // section is interpreted as column 90 | return m_proc.Header_Info.value(static_cast(section)); 91 | } else { 92 | return QString(); 93 | } 94 | } 95 | 96 | int ProcessModel::columnWidth(int c, const QFont *font) 97 | { 98 | if (!m_columnWidths[c]) { 99 | QString header = m_proc.Header_Info.value(static_cast(c)); 100 | QFontMetrics defaultFontMetrics = QFontMetrics(QGuiApplication::font()); 101 | QFontMetrics fm = (font ? QFontMetrics(*font) : defaultFontMetrics); 102 | int ret = fm.horizontalAdvance(headerData(c, Qt::Horizontal).toString() + QLatin1String(" ^")) + 8; 103 | for (int r = 0; r < m_pids.count(); ++r) { 104 | ProcInfo pi = m_proc.procs.value(m_pids[r]); 105 | ret = qMax(ret, fm.horizontalAdvance(pi.toVariant(fields(c)).toString())); 106 | } 107 | m_columnWidths[c] = ret; 108 | } 109 | return m_columnWidths[c]; 110 | 111 | } 112 | 113 | void ProcessModel::update() 114 | { 115 | qDebug() << __FUNCTION__; 116 | beginResetModel(); 117 | m_proc.refresh(); 118 | m_pids = m_proc.procs.keys().toVector(); 119 | std::sort(m_pids.begin(), m_pids.end()); 120 | endResetModel(); 121 | } 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /05-TableView-DelegateChooser/ProcessModel.h: -------------------------------------------------------------------------------- 1 | #ifndef ProcessModel_H 2 | #define ProcessModel_H 3 | 4 | 5 | #include "Proc.h" 6 | 7 | #include 8 | #include 9 | 10 | class ProcessModel : public QAbstractTableModel 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | // supplemental roles beyond Qt::ItemDataRole 16 | enum class Role { 17 | Sort = Qt::UserRole, 18 | Number, 19 | Type 20 | }; 21 | Q_ENUM(Role) 22 | 23 | explicit ProcessModel(QObject *parent= nullptr); 24 | ~ProcessModel(); 25 | 26 | QHash roleNames() const override; 27 | int rowCount(const QModelIndex & = QModelIndex()) const override; 28 | int columnCount(const QModelIndex & = QModelIndex()) const override; 29 | QVariant data(const QModelIndex &index, int role) const override; 30 | QVariant headerData(int section, Qt::Orientation orientation, 31 | int role = Qt::DisplayRole) const override; 32 | Q_INVOKABLE int columnWidth(int c, const QFont *font = nullptr); 33 | Q_INVOKABLE void update(); 34 | 35 | 36 | private: 37 | Proc m_proc; 38 | QVector m_pids; 39 | QVector m_columnWidths = QVector(F_CMDLINE + 1); 40 | QHash m_roleNames; 41 | }; 42 | 43 | 44 | #endif 45 | 46 | -------------------------------------------------------------------------------- /05-TableView-DelegateChooser/SortFilterTableModel.cpp: -------------------------------------------------------------------------------- 1 | #include "SortFilterTableModel.h" 2 | #include 3 | 4 | SortFilterTableModel::SortFilterTableModel(QObject *parent) 5 | : QSortFilterProxyModel (parent) 6 | { 7 | setSourceModel(&m_ProcessModel); 8 | setSortRole(int(ProcessModel::Role::Sort)); 9 | // default fields that "top" displays 10 | m_fields << F_PID 11 | << F_PPID 12 | << F_PGID 13 | << F_SID 14 | << F_STAT1 15 | << F_STAT2 16 | << F_STAT3 17 | << F_STAT4 18 | << F_CMD 19 | << F_CMDLINE; 20 | setFilterKeyColumn(-1); //With -1: Include all columns to filter 21 | } 22 | 23 | void SortFilterTableModel::setFilterText(QString filterText) 24 | { 25 | if (m_filterText == filterText) 26 | return; 27 | 28 | m_filterText = filterText; 29 | setFilterRegularExpression(filterText); 30 | emit filterTextChanged(m_filterText); 31 | } 32 | 33 | 34 | void SortFilterTableModel::sort(int column, Qt::SortOrder order) 35 | { 36 | qDebug() << column << m_fields[column] << order; 37 | QSortFilterProxyModel::sort(m_fields[column], order); 38 | } 39 | 40 | int SortFilterTableModel::columnCount(const QModelIndex &parent) const 41 | { 42 | Q_UNUSED(parent) 43 | return m_fields.count(); 44 | } 45 | 46 | int SortFilterTableModel::columnWidth(int c, const QFont *font) 47 | { 48 | if (c < 0 || c >= m_fields.count()) 49 | return 0; 50 | return m_ProcessModel.columnWidth(m_fields[c], font); 51 | } 52 | 53 | Qt::SortOrder SortFilterTableModel::initialSortOrder(int column) const 54 | { 55 | bool ok = false; 56 | if (column < 0 || column >= m_fields.count()) 57 | return Qt::AscendingOrder; 58 | int ret = m_ProcessModel.data(m_ProcessModel.index(0, m_fields[column]), Qt::InitialSortOrderRole).toInt(&ok); 59 | if (ok) 60 | return Qt::SortOrder(ret); 61 | else 62 | return Qt::AscendingOrder; 63 | } 64 | 65 | QModelIndex SortFilterTableModel::mapFromSource(const QModelIndex &sourceIndex) const 66 | { 67 | int row = QSortFilterProxyModel::mapFromSource(sourceIndex).row(); 68 | fields field = fields(sourceIndex.column()); 69 | return m_ProcessModel.index(row, m_fields.indexOf(field)); 70 | } 71 | 72 | QModelIndex SortFilterTableModel::mapToSource(const QModelIndex &proxyIndex) const 73 | { 74 | QModelIndex rowIndex = QSortFilterProxyModel::mapToSource(proxyIndex); 75 | int col = -1; 76 | if (proxyIndex.column() >= 0 && proxyIndex.column() < m_fields.count()) 77 | col = m_fields[proxyIndex.column()]; 78 | return m_ProcessModel.index(rowIndex.row(), col); 79 | } 80 | 81 | -------------------------------------------------------------------------------- /05-TableView-DelegateChooser/SortFilterTableModel.h: -------------------------------------------------------------------------------- 1 | #ifndef SortFilterTableModel_H 2 | #define SortFilterTableModel_H 3 | 4 | #include 5 | #include 6 | #include "ProcessModel.h" 7 | 8 | class SortFilterTableModel : public QSortFilterProxyModel 9 | { 10 | Q_OBJECT 11 | Q_PROPERTY(ProcessModel *processModel READ processModel CONSTANT) 12 | Q_PROPERTY(QString filterText READ filterText WRITE setFilterText NOTIFY filterTextChanged) 13 | Q_CLASSINFO("DefaultProperty", "data") 14 | public: 15 | SortFilterTableModel(QObject *parent = nullptr); 16 | 17 | ProcessModel *processModel() { return &m_ProcessModel; } 18 | QString filterText() const {return m_filterText;} 19 | void setFilterText(QString filterText); 20 | Q_INVOKABLE void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; 21 | int columnCount(const QModelIndex &parent = QModelIndex()) const override; 22 | Q_INVOKABLE int columnWidth(int c, const QFont *font = nullptr); 23 | Q_INVOKABLE Qt::SortOrder initialSortOrder(int column) const; 24 | 25 | QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override; 26 | QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; 27 | 28 | 29 | signals: 30 | void filterTextChanged(QString filterText); 31 | 32 | private: 33 | ProcessModel m_ProcessModel; 34 | QVector m_fields; 35 | QString m_filterText; 36 | }; 37 | 38 | #endif // SortFilterTableModel_H 39 | -------------------------------------------------------------------------------- /05-TableView-DelegateChooser/SortableColumnHeading.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | 3 | Rectangle { 4 | id: root 5 | color: "wheat" 6 | property int initialSortOrder: Qt.AscendingOrder 7 | property alias text: label.text 8 | signal sorting 9 | 10 | function stopSorting() { 11 | state = "" 12 | } 13 | 14 | Text { 15 | id: label 16 | anchors.verticalCenter: parent.verticalCenter 17 | x: 4 18 | width: parent.width - 4 19 | text: table.model.headerData(index, Qt.Horizontal) 20 | } 21 | 22 | Text { 23 | id: upDownIndicator 24 | anchors.right: parent.right 25 | anchors.margins: 4 26 | anchors.verticalCenter: parent.verticalCenter 27 | text: "^" 28 | visible: false 29 | } 30 | 31 | TapHandler { id: tap; onTapped: nextState() } 32 | 33 | function nextState() { 34 | if (state == "") 35 | state = (initialSortOrder == Qt.DescendingOrder ? "down" : "up") 36 | else if (state == "up") 37 | state = "down" 38 | else 39 | state = "up" 40 | root.sorting() 41 | } 42 | states: [ 43 | State { 44 | name: "up" 45 | PropertyChanges { target: upDownIndicator; visible: true; rotation: 0 } 46 | PropertyChanges { target: root; color: "orange" } 47 | }, 48 | State { 49 | name: "down" 50 | PropertyChanges { target: upDownIndicator; visible: true; rotation: 180 } 51 | PropertyChanges { target: root; color: "orange" } 52 | } 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /05-TableView-DelegateChooser/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "ProcessModel.h" 7 | #include "SortFilterTableModel.h" 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | QGuiApplication app(argc, argv); 12 | 13 | qmlRegisterType("ProcessModel", 0, 1, "ProcessModel"); 14 | qmlRegisterType("SortFilterTableModel", 0, 1, "SortFilterTableModel"); 15 | 16 | QQmlApplicationEngine engine; 17 | 18 | engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 19 | 20 | 21 | return app.exec(); 22 | } 23 | 24 | -------------------------------------------------------------------------------- /05-TableView-DelegateChooser/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Window 2.12 3 | import QtQuick.Layouts 1.12 4 | import ProcessModel 0.1 5 | import SortFilterTableModel 0.1 6 | import QtQuick.Controls 2.4 7 | import Qt.labs.qmlmodels 1.0 8 | 9 | ApplicationWindow { 10 | id: window 11 | visible: true 12 | width: 1024 13 | height: 800 14 | title: qsTr("05-TableView-DelegateChooser") 15 | 16 | header: ToolBar { 17 | RowLayout { 18 | anchors.fill: parent 19 | anchors.rightMargin: 6 20 | Switch { 21 | id: cbUpdate 22 | checked: true 23 | text: qsTr("Update every") 24 | } 25 | SpinBox { 26 | id: sbUpdate 27 | from: 1 28 | to: 60 29 | value: 2 30 | enabled: cbUpdate.checked 31 | } 32 | Label { 33 | text: "sec" 34 | } 35 | Item { 36 | Layout.fillWidth: true 37 | } 38 | TextField { 39 | id: tfFilter 40 | implicitWidth: parent.width / 4 41 | onTextEdited: table.contentY = 0 42 | } 43 | } 44 | } 45 | Row { 46 | id: header 47 | width: table.contentWidth 48 | height: cbUpdate.height 49 | x: -table.contentX 50 | z: 1 51 | spacing: 4 52 | Repeater { 53 | id: peter 54 | model: table.model.columnCount() 55 | SortableColumnHeading { 56 | width: Math.min(600, table.model.columnWidth(index)); height: parent.height 57 | text: table.model.headerData(index, Qt.Horizontal) 58 | initialSortOrder: table.model.initialSortOrder(index) 59 | onSorting: { 60 | for (var i = 0; i < peter.model; ++i) 61 | if (i !== index) 62 | peter.itemAt(i).stopSorting() 63 | table.model.sort(index, state == "up" ? Qt.AscendingOrder : Qt.DescendingOrder) 64 | } 65 | } 66 | } 67 | } 68 | TableView { 69 | id: table 70 | anchors.fill: parent 71 | anchors.topMargin: header.height 72 | columnSpacing: 4; rowSpacing: 4 73 | model: SortFilterTableModel { 74 | filterText: tfFilter.text 75 | } 76 | Timer { 77 | interval: sbUpdate.value * 1000 78 | repeat: true 79 | running: cbUpdate.checked 80 | onTriggered: table.model.processModel.update() 81 | } 82 | columnWidthProvider: function(column) { return Math.min(600, model.columnWidth(column)) } 83 | 84 | delegate: DelegateChooser { 85 | role: "type" 86 | DelegateChoice { 87 | roleValue: "readonly" 88 | Rectangle { 89 | color: "grey" 90 | implicitHeight: readonlyText.implicitHeight 91 | Text { 92 | id: readonlyText 93 | text: model.display 94 | width: parent.width 95 | elide: Text.ElideRight 96 | font.preferShaping: false 97 | } 98 | } 99 | } 100 | DelegateChoice { 101 | roleValue: "id" 102 | Rectangle { 103 | color: "yellow" 104 | implicitHeight: idText.implicitHeight 105 | Text { 106 | id: idText 107 | text: model.display 108 | width: parent.width 109 | elide: Text.ElideRight 110 | font.preferShaping: false 111 | } 112 | } 113 | } 114 | DelegateChoice { 115 | roleValue: "string" 116 | Rectangle { 117 | color: "#0048BA" 118 | implicitHeight: stringText.implicitHeight *1.5 119 | 120 | Text { 121 | id: stringText 122 | color: "white" 123 | text: model.display 124 | width: parent.width 125 | elide: Text.ElideRight 126 | font.preferShaping: false 127 | } 128 | } 129 | } 130 | DelegateChoice { 131 | Rectangle { 132 | color: "#EEE" 133 | implicitHeight: defaultText.implicitHeight 134 | Text { 135 | id: defaultText 136 | text: model.display 137 | width: parent.width 138 | elide: Text.ElideRight 139 | horizontalAlignment: Text.AlignRight 140 | font.preferShaping: false 141 | } 142 | } 143 | } 144 | } 145 | ScrollBar.horizontal: ScrollBar { } 146 | ScrollBar.vertical: ScrollBar { } 147 | } 148 | Shortcut { sequence: StandardKey.Quit; onActivated: Qt.quit() } 149 | } 150 | -------------------------------------------------------------------------------- /05-TableView-DelegateChooser/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | SortableColumnHeading.qml 5 | 6 | 7 | -------------------------------------------------------------------------------- /06-TableView-Column-Resizable/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | project(06-TableView-Column-Resizable LANGUAGES CXX) 4 | 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | set(CMAKE_AUTOMOC ON) 7 | set(CMAKE_AUTORCC ON) 8 | set(CMAKE_CXX_STANDARD 11) 9 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 10 | 11 | find_package(Qt5 COMPONENTS Core Quick REQUIRED) 12 | 13 | add_executable(${PROJECT_NAME} main.cpp 14 | qml.qrc 15 | ProcessModel.cpp 16 | ProcessModel.h 17 | Proc.cpp 18 | Proc.h 19 | SortFilterTableModel.cpp 20 | SortFilterTableModel.h) 21 | target_compile_definitions(${PROJECT_NAME} PRIVATE $<$,$>:QT_QML_DEBUG>) 22 | target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core Qt5::Quick) 23 | 24 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 25 | COMMAND ${_qt5Core_install_prefix}/bin/windeployqt.exe 26 | --verbose 0 27 | --force 28 | --no-compiler-runtime 29 | --qmldir ${_qt5Core_install_prefix}/qml 30 | $ 31 | # windeployqt is not able to deploy Qt/labs/platform! Hence we do it by own. 32 | COMMAND ${CMAKE_COMMAND} -E copy_directory 33 | ${_qt5Core_install_prefix}/qml/Qt/labs/platform 34 | $/Qt/labs/platform 35 | # windeployqt is not able to deploy Qt/labs/qmlmodels! Hence we do it by own. 36 | COMMAND ${CMAKE_COMMAND} -E copy_directory 37 | ${_qt5Core_install_prefix}/qml/Qt/labs/qmlmodels 38 | $/Qt/labs/qmlmodels 39 | ) 40 | 41 | -------------------------------------------------------------------------------- /06-TableView-Column-Resizable/Proc.cpp: -------------------------------------------------------------------------------- 1 | #include "Proc.h" 2 | 3 | #include 4 | #include 5 | #include 6 | std::random_device dev; 7 | std::mt19937 rng(dev()); 8 | 9 | const QMap Proc::Header_Info = { 10 | {F_PID, {"PID"}}, 11 | {F_PPID, {"PPID"}}, 12 | {F_PGID, {"PGID"}}, 13 | {F_SID, {"SID"}}, 14 | {F_STAT1, {"STAT1"}}, 15 | {F_STAT2, {"STAT2"}}, 16 | {F_STAT3, {"STAT3"}}, 17 | {F_STAT4, {"STAT4"}}, 18 | {F_CMD, {"CMD"}}, 19 | {F_CMDLINE, {"CMDLINE"}} 20 | }; 21 | 22 | QString random_string(int length=32, QString allow_symbols=QString("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")) { 23 | QString result; 24 | QRandomGenerator(QTime::currentTime().msec()); 25 | for (int i = 0; i < length; ++i) { 26 | std::uniform_int_distribution dist1(0,allow_symbols.length()); 27 | result.append(allow_symbols.at(dist1(rng))); 28 | } 29 | return result; 30 | } 31 | 32 | ProcInfo::ProcInfo() 33 | { 34 | 35 | } 36 | 37 | ProcInfo::ProcInfo(int pid) 38 | { 39 | mpid = pid; 40 | std::uniform_int_distribution dist1(1,6); 41 | ppid = dist1(rng); 42 | std::uniform_int_distribution dist2(0,100); 43 | pgid = dist2(rng); 44 | std::uniform_int_distribution dist3(100,1000); 45 | sid = dist3(rng); 46 | stat1 = QString("%1%").arg(pgid); 47 | stat2 = QString("%1 bytes be or not to be is the question.").arg(pgid + sid); 48 | stat3 = QString("%1 bytes").arg(pgid - sid); 49 | stat4 = QString("%1 bytes").arg(pgid * sid); 50 | cmd = random_string(); 51 | cmdline = random_string(); 52 | } 53 | 54 | ProcInfo::~ProcInfo() 55 | { 56 | 57 | } 58 | 59 | QVariant ProcInfo::toVariant(fields f) 60 | { 61 | switch (f) { 62 | case F_PID: return mpid; 63 | case F_PPID: return ppid; 64 | case F_PGID: return pgid; 65 | case F_SID: return sid; 66 | case F_STAT1: return stat1; 67 | case F_STAT2: return stat2; 68 | case F_STAT3: return stat3; 69 | case F_STAT4: return stat4; 70 | case F_CMD: return cmd; 71 | case F_CMDLINE: return cmdline; 72 | default: return ""; 73 | } 74 | } 75 | 76 | 77 | 78 | Proc::Proc() 79 | { 80 | 81 | } 82 | 83 | void Proc::refresh() 84 | { 85 | procs.clear(); 86 | std::uniform_int_distribution dist(1000,2500); 87 | 88 | const int numOfPids = dist(rng); 89 | 90 | for (int i = 0; i < numOfPids; ++i) 91 | { 92 | procs.insert(i, ProcInfo(i)); 93 | } 94 | } 95 | 96 | 97 | -------------------------------------------------------------------------------- /06-TableView-Column-Resizable/Proc.h: -------------------------------------------------------------------------------- 1 | #ifndef PROC_H 2 | #define PROC_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | enum fields 10 | { 11 | F_PID = 0, 12 | F_PPID, 13 | F_PGID, 14 | F_SID, 15 | F_STAT1, 16 | F_STAT2, 17 | F_STAT3, 18 | F_STAT4, 19 | F_CMD, 20 | F_CMDLINE, 21 | F_END = -1 22 | }; 23 | 24 | 25 | class ProcInfo 26 | { 27 | public: 28 | ProcInfo(); 29 | ProcInfo(int pid); 30 | ~ProcInfo(); 31 | 32 | int mpid; 33 | int ppid; 34 | int pgid; 35 | int sid; 36 | QString stat1; 37 | QString stat2; 38 | QString stat3; 39 | QString stat4; 40 | QString cmd; 41 | QString cmdline; 42 | 43 | QVariant toVariant(fields f); 44 | }; 45 | 46 | using ProcList = QMap; 47 | 48 | 49 | class Proc 50 | { 51 | public: 52 | Proc(); 53 | void refresh(); 54 | 55 | ProcList procs; // processes indexed by pid 56 | 57 | static const QMap Header_Info; 58 | }; 59 | 60 | #endif // PROC_H 61 | -------------------------------------------------------------------------------- /06-TableView-Column-Resizable/ProcessModel.cpp: -------------------------------------------------------------------------------- 1 | #include "ProcessModel.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | ProcessModel::ProcessModel(QObject *parent) : QAbstractTableModel (parent) 8 | { 9 | m_roleNames = QAbstractTableModel::roleNames(); 10 | // TODO loop over the QMetaEnum 11 | m_roleNames.insert(int(Role::Sort), QByteArray("sort")); 12 | m_roleNames.insert(int(Role::Number), QByteArray("number")); 13 | m_roleNames.insert(int(Role::Type), QByteArray("type")); 14 | update(); 15 | } 16 | 17 | ProcessModel::~ProcessModel() 18 | { 19 | 20 | } 21 | 22 | QHash ProcessModel::roleNames() const 23 | { 24 | return m_roleNames; 25 | } 26 | 27 | int ProcessModel::rowCount(const QModelIndex &) const 28 | { 29 | return m_pids.count(); 30 | } 31 | 32 | int ProcessModel::columnCount(const QModelIndex &) const 33 | { 34 | return F_CMDLINE + 1; 35 | } 36 | 37 | QVariant ProcessModel::data(const QModelIndex &index, int role) const 38 | { 39 | fields field = fields(index.column()); 40 | int pid = m_pids[index.row()]; 41 | ProcInfo pi = m_proc.procs.value(pid); 42 | switch (role) { 43 | case Qt::DisplayRole: 44 | return pi.toVariant(field); 45 | case Qt::InitialSortOrderRole: { 46 | bool numeric = false; 47 | pi.toVariant(field).toFloat(&numeric); 48 | if (numeric) 49 | return Qt::DescendingOrder; 50 | return Qt::AscendingOrder; 51 | } 52 | case int(ProcessModel::Role::Sort): 53 | return pi.toVariant(field); 54 | case int(ProcessModel::Role::Number): 55 | return pi.toVariant(field).toDouble(); 56 | case int(ProcessModel::Role::Type): 57 | // TODO this is silly: make a virtual in the Category perhaps? 58 | switch (field) { 59 | case F_PID: 60 | case F_PPID: 61 | return QLatin1String("readonly"); 62 | case F_PGID: 63 | case F_SID: 64 | return QLatin1String("id"); 65 | case F_STAT1: 66 | case F_STAT2: 67 | case F_STAT3: 68 | case F_STAT4: 69 | return QLatin1String("stat"); 70 | case F_CMD: 71 | case F_CMDLINE: 72 | return QLatin1String("string"); 73 | default: 74 | return QLatin1String("string"); 75 | } 76 | default: 77 | return QVariant(); 78 | } 79 | 80 | return pi.toVariant(field); 81 | } 82 | 83 | QVariant ProcessModel::headerData(int section, Qt::Orientation orientation, int role) const 84 | { 85 | if (role != Qt::DisplayRole) 86 | return QVariant(); 87 | 88 | if (orientation == Qt::Horizontal) { 89 | // section is interpreted as column 90 | return m_proc.Header_Info.value(static_cast(section)); 91 | } else { 92 | return QString(); 93 | } 94 | } 95 | 96 | int ProcessModel::columnWidth(int c, const QFont *font) 97 | { 98 | if (!m_columnWidths[c]) { 99 | QString header = m_proc.Header_Info.value(static_cast(c)); 100 | QFontMetrics defaultFontMetrics = QFontMetrics(QGuiApplication::font()); 101 | QFontMetrics fm = (font ? QFontMetrics(*font) : defaultFontMetrics); 102 | int ret = fm.horizontalAdvance(headerData(c, Qt::Horizontal).toString() + QLatin1String(" ^")) + 8; 103 | for (int r = 0; r < m_pids.count(); ++r) { 104 | ProcInfo pi = m_proc.procs.value(m_pids[r]); 105 | ret = qMax(ret, fm.horizontalAdvance(pi.toVariant(fields(c)).toString())); 106 | } 107 | m_columnWidths[c] = ret; 108 | } 109 | return m_columnWidths[c]; 110 | 111 | } 112 | 113 | void ProcessModel::update() 114 | { 115 | qDebug() << __FUNCTION__; 116 | beginResetModel(); 117 | m_proc.refresh(); 118 | m_pids = m_proc.procs.keys().toVector(); 119 | std::sort(m_pids.begin(), m_pids.end()); 120 | endResetModel(); 121 | } 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /06-TableView-Column-Resizable/ProcessModel.h: -------------------------------------------------------------------------------- 1 | #ifndef ProcessModel_H 2 | #define ProcessModel_H 3 | 4 | 5 | #include "Proc.h" 6 | 7 | #include 8 | #include 9 | 10 | class ProcessModel : public QAbstractTableModel 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | // supplemental roles beyond Qt::ItemDataRole 16 | enum class Role { 17 | Sort = Qt::UserRole, 18 | Number, 19 | Type 20 | }; 21 | Q_ENUM(Role) 22 | 23 | explicit ProcessModel(QObject *parent= nullptr); 24 | ~ProcessModel(); 25 | 26 | QHash roleNames() const override; 27 | int rowCount(const QModelIndex & = QModelIndex()) const override; 28 | int columnCount(const QModelIndex & = QModelIndex()) const override; 29 | QVariant data(const QModelIndex &index, int role) const override; 30 | QVariant headerData(int section, Qt::Orientation orientation, 31 | int role = Qt::DisplayRole) const override; 32 | Q_INVOKABLE int columnWidth(int c, const QFont *font = nullptr); 33 | Q_INVOKABLE void update(); 34 | 35 | 36 | private: 37 | Proc m_proc; 38 | QVector m_pids; 39 | QVector m_columnWidths = QVector(F_CMDLINE + 1); 40 | QHash m_roleNames; 41 | }; 42 | 43 | 44 | #endif 45 | 46 | -------------------------------------------------------------------------------- /06-TableView-Column-Resizable/SortFilterTableModel.cpp: -------------------------------------------------------------------------------- 1 | #include "SortFilterTableModel.h" 2 | #include 3 | 4 | SortFilterTableModel::SortFilterTableModel(QObject *parent) 5 | : QSortFilterProxyModel (parent) 6 | { 7 | setSourceModel(&m_ProcessModel); 8 | setSortRole(int(ProcessModel::Role::Sort)); 9 | // default fields that "top" displays 10 | m_fields << F_PID 11 | << F_PPID 12 | << F_PGID 13 | << F_SID 14 | << F_STAT1 15 | << F_STAT2 16 | << F_STAT3 17 | << F_STAT4 18 | << F_CMD 19 | << F_CMDLINE; 20 | setFilterKeyColumn(-1); //With -1: Include all columns to filter 21 | } 22 | 23 | void SortFilterTableModel::setFilterText(QString filterText) 24 | { 25 | if (m_filterText == filterText) 26 | return; 27 | 28 | m_filterText = filterText; 29 | setFilterRegularExpression(filterText); 30 | emit filterTextChanged(m_filterText); 31 | } 32 | 33 | 34 | void SortFilterTableModel::sort(int column, Qt::SortOrder order) 35 | { 36 | qDebug() << column << m_fields[column] << order; 37 | QSortFilterProxyModel::sort(m_fields[column], order); 38 | } 39 | 40 | int SortFilterTableModel::columnCount(const QModelIndex &parent) const 41 | { 42 | Q_UNUSED(parent) 43 | return m_fields.count(); 44 | } 45 | 46 | int SortFilterTableModel::columnWidth(int c, const QFont *font) 47 | { 48 | if (c < 0 || c >= m_fields.count()) 49 | return 0; 50 | return m_ProcessModel.columnWidth(m_fields[c], font); 51 | } 52 | 53 | Qt::SortOrder SortFilterTableModel::initialSortOrder(int column) const 54 | { 55 | bool ok = false; 56 | if (column < 0 || column >= m_fields.count()) 57 | return Qt::AscendingOrder; 58 | int ret = m_ProcessModel.data(m_ProcessModel.index(0, m_fields[column]), Qt::InitialSortOrderRole).toInt(&ok); 59 | if (ok) 60 | return Qt::SortOrder(ret); 61 | else 62 | return Qt::AscendingOrder; 63 | } 64 | 65 | QModelIndex SortFilterTableModel::mapFromSource(const QModelIndex &sourceIndex) const 66 | { 67 | int row = QSortFilterProxyModel::mapFromSource(sourceIndex).row(); 68 | fields field = fields(sourceIndex.column()); 69 | return m_ProcessModel.index(row, m_fields.indexOf(field)); 70 | } 71 | 72 | QModelIndex SortFilterTableModel::mapToSource(const QModelIndex &proxyIndex) const 73 | { 74 | QModelIndex rowIndex = QSortFilterProxyModel::mapToSource(proxyIndex); 75 | int col = -1; 76 | if (proxyIndex.column() >= 0 && proxyIndex.column() < m_fields.count()) 77 | col = m_fields[proxyIndex.column()]; 78 | return m_ProcessModel.index(rowIndex.row(), col); 79 | } 80 | 81 | -------------------------------------------------------------------------------- /06-TableView-Column-Resizable/SortFilterTableModel.h: -------------------------------------------------------------------------------- 1 | #ifndef SortFilterTableModel_H 2 | #define SortFilterTableModel_H 3 | 4 | #include 5 | #include 6 | #include "ProcessModel.h" 7 | 8 | class SortFilterTableModel : public QSortFilterProxyModel 9 | { 10 | Q_OBJECT 11 | Q_PROPERTY(ProcessModel *processModel READ processModel CONSTANT) 12 | Q_PROPERTY(QString filterText READ filterText WRITE setFilterText NOTIFY filterTextChanged) 13 | Q_CLASSINFO("DefaultProperty", "data") 14 | public: 15 | SortFilterTableModel(QObject *parent = nullptr); 16 | 17 | ProcessModel *processModel() { return &m_ProcessModel; } 18 | QString filterText() const {return m_filterText;} 19 | void setFilterText(QString filterText); 20 | Q_INVOKABLE void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; 21 | int columnCount(const QModelIndex &parent = QModelIndex()) const override; 22 | Q_INVOKABLE int columnWidth(int c, const QFont *font = nullptr); 23 | Q_INVOKABLE Qt::SortOrder initialSortOrder(int column) const; 24 | 25 | QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override; 26 | QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; 27 | 28 | 29 | signals: 30 | void filterTextChanged(QString filterText); 31 | 32 | private: 33 | ProcessModel m_ProcessModel; 34 | QVector m_fields; 35 | QString m_filterText; 36 | }; 37 | 38 | #endif // SortFilterTableModel_H 39 | -------------------------------------------------------------------------------- /06-TableView-Column-Resizable/SortableColumnHeading.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | 3 | Rectangle { 4 | id: root 5 | color: "wheat" 6 | property int initialSortOrder: Qt.AscendingOrder 7 | property alias text: label.text 8 | property real initialWidth: 100 9 | signal sorting 10 | width: splitter.x + 6 11 | 12 | function setWidth(newWidth) { 13 | splitter.x = newWidth -6 14 | } 15 | 16 | function stopSorting() { 17 | state = "" 18 | } 19 | 20 | Text { 21 | id: label 22 | anchors.verticalCenter: parent.verticalCenter 23 | x: 4 24 | width: parent.width - 4 25 | text: table.model.headerData(index, Qt.Horizontal) 26 | } 27 | 28 | Text { 29 | id: upDownIndicator 30 | anchors.right: parent.right 31 | anchors.margins: 8 32 | anchors.verticalCenter: parent.verticalCenter 33 | text: "^" 34 | visible: false 35 | } 36 | 37 | TapHandler { id: tap; onTapped: nextState() } 38 | 39 | Item { 40 | id: splitter 41 | x: root.initialWidth - 6 42 | width: 12 43 | height: parent.height 44 | DragHandler { 45 | yAxis.enabled: false 46 | onActiveChanged: { 47 | if (!active) { 48 | if (splitter.x <= 5 ) { 49 | splitter.x = 6 50 | } 51 | table.forceLayout() 52 | } 53 | } 54 | } 55 | Rectangle { 56 | anchors.fill: parent 57 | id: splitterRect 58 | color: "grey" 59 | visible: false 60 | } 61 | 62 | 63 | MouseArea { 64 | hoverEnabled: true 65 | anchors.fill: parent 66 | onEntered: splitterRect.visible = true 67 | onExited: splitterRect.visible = false 68 | } 69 | 70 | } 71 | 72 | 73 | function nextState() { 74 | if (state == "") 75 | state = (initialSortOrder == Qt.DescendingOrder ? "down" : "up") 76 | else if (state == "up") 77 | state = "down" 78 | else 79 | state = "up" 80 | root.sorting() 81 | } 82 | states: [ 83 | State { 84 | name: "up" 85 | PropertyChanges { target: upDownIndicator; visible: true; rotation: 0 } 86 | PropertyChanges { target: root; color: "orange" } 87 | }, 88 | State { 89 | name: "down" 90 | PropertyChanges { target: upDownIndicator; visible: true; rotation: 180 } 91 | PropertyChanges { target: root; color: "orange" } 92 | } 93 | ] 94 | } 95 | -------------------------------------------------------------------------------- /06-TableView-Column-Resizable/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "ProcessModel.h" 7 | #include "SortFilterTableModel.h" 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | QGuiApplication app(argc, argv); 12 | 13 | qmlRegisterType("ProcessModel", 0, 1, "ProcessModel"); 14 | qmlRegisterType("SortFilterTableModel", 0, 1, "SortFilterTableModel"); 15 | 16 | QQmlApplicationEngine engine; 17 | 18 | engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 19 | 20 | 21 | return app.exec(); 22 | } 23 | 24 | -------------------------------------------------------------------------------- /06-TableView-Column-Resizable/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Window 2.12 3 | import QtQuick.Layouts 1.12 4 | import ProcessModel 0.1 5 | import SortFilterTableModel 0.1 6 | import QtQuick.Controls 2.4 7 | import Qt.labs.qmlmodels 1.0 8 | 9 | ApplicationWindow { 10 | id: window 11 | visible: true 12 | width: 1024 13 | height: 800 14 | //title: qsTr("05-TableView-DelegateChooser") 15 | 16 | header: ToolBar { 17 | RowLayout { 18 | anchors.fill: parent 19 | anchors.rightMargin: 6 20 | Switch { 21 | id: cbUpdate 22 | checked: true 23 | text: qsTr("Update every") 24 | } 25 | SpinBox { 26 | id: sbUpdate 27 | from: 1 28 | to: 60 29 | value: 2 30 | enabled: cbUpdate.checked 31 | } 32 | Label { 33 | text: "sec" 34 | } 35 | Button { 36 | id: resizeCols 37 | text: qsTr("Resize cols") 38 | onClicked: { 39 | //console.log(table.model.columnCount()) 40 | for ( var index = 0; index < table.model.columnCount(); ++index) { 41 | var width = Math.min(600, table.model.columnWidth(index)) 42 | //console.log(index + " " + width) 43 | headerRepeater.itemAt(index).setWidth(width) 44 | } 45 | table.forceLayout() 46 | } 47 | } 48 | 49 | Item { 50 | Layout.fillWidth: true 51 | } 52 | TextField { 53 | id: tfFilter 54 | implicitWidth: parent.width / 4 55 | onTextEdited: table.contentY = 0 56 | } 57 | } 58 | } 59 | Row { 60 | id: header 61 | width: table.contentWidth 62 | height: cbUpdate.height 63 | x: -table.contentX 64 | z: 1 65 | spacing: 4 66 | Repeater { 67 | id: headerRepeater 68 | model: table.model.columnCount() 69 | SortableColumnHeading { 70 | initialWidth: Math.min(600, table.model.columnWidth(index)); height: parent.height 71 | text: table.model.headerData(index, Qt.Horizontal) 72 | initialSortOrder: table.model.initialSortOrder(index) 73 | onSorting: { 74 | for (var i = 0; i < headerRepeater.model; ++i) 75 | if (i !== index) 76 | headerRepeater.itemAt(i).stopSorting() 77 | table.model.sort(index, state == "up" ? Qt.AscendingOrder : Qt.DescendingOrder) 78 | } 79 | } 80 | } 81 | } 82 | TableView { 83 | id: table 84 | anchors.fill: parent 85 | anchors.topMargin: header.height 86 | columnSpacing: 4; rowSpacing: 4 87 | model: SortFilterTableModel { 88 | filterText: tfFilter.text 89 | } 90 | Timer { 91 | interval: sbUpdate.value * 1000 92 | repeat: true 93 | running: cbUpdate.checked 94 | onTriggered: table.model.processModel.update() 95 | } 96 | columnWidthProvider: function(column) { return headerRepeater.itemAt(column).width } 97 | 98 | delegate: DelegateChooser { 99 | role: "type" 100 | DelegateChoice { 101 | roleValue: "readonly" 102 | Rectangle { 103 | color: "grey" 104 | implicitHeight: readonlyText.implicitHeight 105 | Text { 106 | id: readonlyText 107 | text: model.display 108 | width: parent.width 109 | elide: Text.ElideRight 110 | font.preferShaping: false 111 | } 112 | } 113 | } 114 | DelegateChoice { 115 | roleValue: "id" 116 | Rectangle { 117 | color: "yellow" 118 | implicitHeight: idText.implicitHeight 119 | Text { 120 | id: idText 121 | text: model.display 122 | width: parent.width 123 | elide: Text.ElideRight 124 | font.preferShaping: false 125 | } 126 | } 127 | } 128 | DelegateChoice { 129 | roleValue: "string" 130 | Rectangle { 131 | color: "#0048BA" 132 | implicitHeight: stringText.implicitHeight *1.5 133 | 134 | Text { 135 | id: stringText 136 | color: "white" 137 | text: model.display 138 | width: parent.width 139 | elide: Text.ElideRight 140 | font.preferShaping: false 141 | } 142 | } 143 | } 144 | DelegateChoice { 145 | Rectangle { 146 | color: "#EEE" 147 | implicitHeight: defaultText.implicitHeight 148 | Text { 149 | id: defaultText 150 | text: model.display 151 | width: parent.width 152 | elide: Text.ElideRight 153 | horizontalAlignment: Text.AlignRight 154 | font.preferShaping: false 155 | } 156 | } 157 | } 158 | } 159 | ScrollBar.horizontal: ScrollBar { } 160 | ScrollBar.vertical: ScrollBar { } 161 | } 162 | Shortcut { sequence: StandardKey.Quit; onActivated: Qt.quit() } 163 | } 164 | -------------------------------------------------------------------------------- /06-TableView-Column-Resizable/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | SortableColumnHeading.qml 5 | 6 | 7 | -------------------------------------------------------------------------------- /07-TableView-Column-Resizable-Image/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | project(07-TableView-Column-Resizable-Image LANGUAGES CXX) 4 | 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | set(CMAKE_AUTOMOC ON) 7 | set(CMAKE_AUTORCC ON) 8 | set(CMAKE_CXX_STANDARD 11) 9 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 10 | 11 | find_package(Qt5 COMPONENTS Core Quick REQUIRED) 12 | 13 | add_executable(${PROJECT_NAME} main.cpp 14 | qml.qrc 15 | images/images.qrc 16 | ProcessModel.cpp 17 | ProcessModel.h 18 | Proc.cpp 19 | Proc.h 20 | SortFilterTableModel.cpp 21 | SortFilterTableModel.h) 22 | target_compile_definitions(${PROJECT_NAME} PRIVATE $<$,$>:QT_QML_DEBUG>) 23 | target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core Qt5::Quick) 24 | 25 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 26 | COMMAND ${_qt5Core_install_prefix}/bin/windeployqt.exe 27 | --verbose 0 28 | --force 29 | --no-compiler-runtime 30 | --qmldir ${_qt5Core_install_prefix}/qml 31 | $ 32 | # windeployqt is not able to deploy Qt/labs/platform! Hence we do it by own. 33 | COMMAND ${CMAKE_COMMAND} -E copy_directory 34 | ${_qt5Core_install_prefix}/qml/Qt/labs/platform 35 | $/Qt/labs/platform 36 | # windeployqt is not able to deploy Qt/labs/qmlmodels! Hence we do it by own. 37 | COMMAND ${CMAKE_COMMAND} -E copy_directory 38 | ${_qt5Core_install_prefix}/qml/Qt/labs/qmlmodels 39 | $/Qt/labs/qmlmodels 40 | ) 41 | 42 | -------------------------------------------------------------------------------- /07-TableView-Column-Resizable-Image/Proc.cpp: -------------------------------------------------------------------------------- 1 | #include "Proc.h" 2 | 3 | #include 4 | #include 5 | #include 6 | std::random_device dev; 7 | std::mt19937 rng(dev()); 8 | 9 | const QMap Proc::Header_Info = { 10 | {F_PID, {"PID"}}, 11 | {F_PPID, {"PPID"}}, 12 | {F_PGID, {"PGID"}}, 13 | {F_SID, {"SID"}}, 14 | {F_IMAGE, {"IMAGE"}}, 15 | {F_STAT1, {"STAT1"}}, 16 | {F_STAT2, {"STAT2"}}, 17 | {F_STAT3, {"STAT3"}}, 18 | {F_STAT4, {"STAT4"}}, 19 | {F_CMD, {"CMD"}}, 20 | {F_CMDLINE, {"CMDLINE"}} 21 | }; 22 | 23 | QString random_string(int length=32, QString allow_symbols=QString("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")) { 24 | QString result; 25 | QRandomGenerator(QTime::currentTime().msec()); 26 | for (int i = 0; i < length; ++i) { 27 | std::uniform_int_distribution dist1(0,allow_symbols.length()); 28 | result.append(allow_symbols.at(dist1(rng))); 29 | } 30 | return result; 31 | } 32 | 33 | ProcInfo::ProcInfo() 34 | { 35 | 36 | } 37 | 38 | ProcInfo::ProcInfo(int pid) 39 | { 40 | mpid = pid; 41 | std::uniform_int_distribution dist1(1,6); 42 | ppid = dist1(rng); 43 | std::uniform_int_distribution dist2(0,100); 44 | pgid = dist2(rng); 45 | std::uniform_int_distribution dist3(100,1000); 46 | sid = dist3(rng); 47 | imageUrl = QString("qrc:/images/%0.jpg").arg(ppid); 48 | stat1 = QString("%1%").arg(pgid); 49 | stat2 = QString("%1 bytes be or not to be is the question.").arg(pgid + sid); 50 | stat3 = QString("%1 bytes").arg(pgid - sid); 51 | stat4 = QString("%1 bytes").arg(pgid * sid); 52 | cmd = random_string(); 53 | cmdline = random_string(); 54 | } 55 | 56 | ProcInfo::~ProcInfo() 57 | { 58 | 59 | } 60 | 61 | QVariant ProcInfo::toVariant(fields f) 62 | { 63 | switch (f) { 64 | case F_PID: return mpid; 65 | case F_PPID: return ppid; 66 | case F_PGID: return pgid; 67 | case F_SID: return sid; 68 | case F_IMAGE: return imageUrl; 69 | case F_STAT1: return stat1; 70 | case F_STAT2: return stat2; 71 | case F_STAT3: return stat3; 72 | case F_STAT4: return stat4; 73 | case F_CMD: return cmd; 74 | case F_CMDLINE: return cmdline; 75 | default: return ""; 76 | } 77 | } 78 | 79 | 80 | 81 | Proc::Proc() 82 | { 83 | 84 | } 85 | 86 | void Proc::refresh() 87 | { 88 | procs.clear(); 89 | std::uniform_int_distribution dist(1000,2500); 90 | 91 | const int numOfPids = dist(rng); 92 | 93 | for (int i = 0; i < numOfPids; ++i) 94 | { 95 | procs.insert(i, ProcInfo(i)); 96 | } 97 | } 98 | 99 | 100 | -------------------------------------------------------------------------------- /07-TableView-Column-Resizable-Image/Proc.h: -------------------------------------------------------------------------------- 1 | #ifndef PROC_H 2 | #define PROC_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | enum fields 10 | { 11 | F_PID = 0, 12 | F_PPID, 13 | F_PGID, 14 | F_SID, 15 | F_IMAGE, 16 | F_STAT1, 17 | F_STAT2, 18 | F_STAT3, 19 | F_STAT4, 20 | F_CMD, 21 | F_CMDLINE, 22 | F_END = -1 23 | }; 24 | 25 | 26 | class ProcInfo 27 | { 28 | public: 29 | ProcInfo(); 30 | ProcInfo(int pid); 31 | ~ProcInfo(); 32 | 33 | int mpid; 34 | int ppid; 35 | int pgid; 36 | int sid; 37 | QString imageUrl; 38 | QString stat1; 39 | QString stat2; 40 | QString stat3; 41 | QString stat4; 42 | QString cmd; 43 | QString cmdline; 44 | 45 | QVariant toVariant(fields f); 46 | }; 47 | 48 | using ProcList = QMap; 49 | 50 | 51 | class Proc 52 | { 53 | public: 54 | Proc(); 55 | void refresh(); 56 | 57 | ProcList procs; // processes indexed by pid 58 | 59 | static const QMap Header_Info; 60 | }; 61 | 62 | #endif // PROC_H 63 | -------------------------------------------------------------------------------- /07-TableView-Column-Resizable-Image/ProcessModel.cpp: -------------------------------------------------------------------------------- 1 | #include "ProcessModel.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | ProcessModel::ProcessModel(QObject *parent) : QAbstractTableModel (parent) 8 | { 9 | m_roleNames = QAbstractTableModel::roleNames(); 10 | // TODO loop over the QMetaEnum 11 | m_roleNames.insert(int(Role::Sort), QByteArray("sort")); 12 | m_roleNames.insert(int(Role::Number), QByteArray("number")); 13 | m_roleNames.insert(int(Role::Type), QByteArray("type")); 14 | update(); 15 | } 16 | 17 | ProcessModel::~ProcessModel() 18 | { 19 | 20 | } 21 | 22 | QHash ProcessModel::roleNames() const 23 | { 24 | return m_roleNames; 25 | } 26 | 27 | int ProcessModel::rowCount(const QModelIndex &) const 28 | { 29 | return m_pids.count(); 30 | } 31 | 32 | int ProcessModel::columnCount(const QModelIndex &) const 33 | { 34 | return F_CMDLINE + 1; 35 | } 36 | 37 | QVariant ProcessModel::data(const QModelIndex &index, int role) const 38 | { 39 | fields field = fields(index.column()); 40 | int pid = m_pids[index.row()]; 41 | ProcInfo pi = m_proc.procs.value(pid); 42 | switch (role) { 43 | case Qt::DisplayRole: 44 | return pi.toVariant(field); 45 | case Qt::InitialSortOrderRole: { 46 | bool numeric = false; 47 | pi.toVariant(field).toFloat(&numeric); 48 | if (numeric) 49 | return Qt::DescendingOrder; 50 | return Qt::AscendingOrder; 51 | } 52 | case int(ProcessModel::Role::Sort): 53 | return pi.toVariant(field); 54 | case int(ProcessModel::Role::Number): 55 | return pi.toVariant(field).toDouble(); 56 | case int(ProcessModel::Role::Type): 57 | // TODO this is silly: make a virtual in the Category perhaps? 58 | switch (field) { 59 | case F_PID: 60 | case F_PPID: 61 | return QLatin1String("readonly"); 62 | case F_PGID: 63 | case F_SID: 64 | return QLatin1String("id"); 65 | case F_IMAGE: 66 | return QLatin1String("image"); 67 | case F_STAT1: 68 | case F_STAT2: 69 | case F_STAT3: 70 | case F_STAT4: 71 | return QLatin1String("stat"); 72 | case F_CMD: 73 | case F_CMDLINE: 74 | return QLatin1String("string"); 75 | default: 76 | return QLatin1String("string"); 77 | } 78 | default: 79 | return QVariant(); 80 | } 81 | 82 | return pi.toVariant(field); 83 | } 84 | 85 | QVariant ProcessModel::headerData(int section, Qt::Orientation orientation, int role) const 86 | { 87 | if (role != Qt::DisplayRole) 88 | return QVariant(); 89 | 90 | if (orientation == Qt::Horizontal) { 91 | // section is interpreted as column 92 | return m_proc.Header_Info.value(static_cast(section)); 93 | } else { 94 | return QString(); 95 | } 96 | } 97 | 98 | int ProcessModel::columnWidth(int c, const QFont *font) 99 | { 100 | if (!m_columnWidths[c]) { 101 | QString header = m_proc.Header_Info.value(static_cast(c)); 102 | QFontMetrics defaultFontMetrics = QFontMetrics(QGuiApplication::font()); 103 | QFontMetrics fm = (font ? QFontMetrics(*font) : defaultFontMetrics); 104 | int ret = fm.horizontalAdvance(headerData(c, Qt::Horizontal).toString() + QLatin1String(" ^")) + 8; 105 | for (int r = 0; r < m_pids.count(); ++r) { 106 | ProcInfo pi = m_proc.procs.value(m_pids[r]); 107 | ret = qMax(ret, fm.horizontalAdvance(pi.toVariant(fields(c)).toString())); 108 | } 109 | m_columnWidths[c] = ret; 110 | } 111 | return m_columnWidths[c]; 112 | 113 | } 114 | 115 | void ProcessModel::update() 116 | { 117 | qDebug() << __FUNCTION__; 118 | beginResetModel(); 119 | m_proc.refresh(); 120 | m_pids = m_proc.procs.keys().toVector(); 121 | std::sort(m_pids.begin(), m_pids.end()); 122 | endResetModel(); 123 | } 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /07-TableView-Column-Resizable-Image/ProcessModel.h: -------------------------------------------------------------------------------- 1 | #ifndef ProcessModel_H 2 | #define ProcessModel_H 3 | 4 | 5 | #include "Proc.h" 6 | 7 | #include 8 | #include 9 | 10 | class ProcessModel : public QAbstractTableModel 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | // supplemental roles beyond Qt::ItemDataRole 16 | enum class Role { 17 | Sort = Qt::UserRole, 18 | Number, 19 | Type 20 | }; 21 | Q_ENUM(Role) 22 | 23 | explicit ProcessModel(QObject *parent= nullptr); 24 | ~ProcessModel(); 25 | 26 | QHash roleNames() const override; 27 | int rowCount(const QModelIndex & = QModelIndex()) const override; 28 | int columnCount(const QModelIndex & = QModelIndex()) const override; 29 | QVariant data(const QModelIndex &index, int role) const override; 30 | QVariant headerData(int section, Qt::Orientation orientation, 31 | int role = Qt::DisplayRole) const override; 32 | Q_INVOKABLE int columnWidth(int c, const QFont *font = nullptr); 33 | Q_INVOKABLE void update(); 34 | 35 | 36 | private: 37 | Proc m_proc; 38 | QVector m_pids; 39 | QVector m_columnWidths = QVector(F_CMDLINE + 1); 40 | QHash m_roleNames; 41 | }; 42 | 43 | 44 | #endif 45 | 46 | -------------------------------------------------------------------------------- /07-TableView-Column-Resizable-Image/SortFilterTableModel.cpp: -------------------------------------------------------------------------------- 1 | #include "SortFilterTableModel.h" 2 | #include 3 | 4 | SortFilterTableModel::SortFilterTableModel(QObject *parent) 5 | : QSortFilterProxyModel (parent) 6 | { 7 | setSourceModel(&m_ProcessModel); 8 | setSortRole(int(ProcessModel::Role::Sort)); 9 | // default fields that "top" displays 10 | m_fields << F_PID 11 | << F_PPID 12 | << F_PGID 13 | << F_SID 14 | << F_IMAGE 15 | << F_STAT1 16 | << F_STAT2 17 | << F_STAT3 18 | << F_STAT4 19 | << F_CMD 20 | << F_CMDLINE; 21 | setFilterKeyColumn(-1); //With -1: Include all columns to filter 22 | } 23 | 24 | void SortFilterTableModel::setFilterText(QString filterText) 25 | { 26 | if (m_filterText == filterText) 27 | return; 28 | 29 | m_filterText = filterText; 30 | setFilterRegularExpression(filterText); 31 | emit filterTextChanged(m_filterText); 32 | } 33 | 34 | 35 | void SortFilterTableModel::sort(int column, Qt::SortOrder order) 36 | { 37 | qDebug() << column << m_fields[column] << order; 38 | QSortFilterProxyModel::sort(m_fields[column], order); 39 | } 40 | 41 | int SortFilterTableModel::columnCount(const QModelIndex &parent) const 42 | { 43 | Q_UNUSED(parent) 44 | return m_fields.count(); 45 | } 46 | 47 | int SortFilterTableModel::columnWidth(int c, const QFont *font) 48 | { 49 | if (c < 0 || c >= m_fields.count()) 50 | return 0; 51 | return m_ProcessModel.columnWidth(m_fields[c], font); 52 | } 53 | 54 | Qt::SortOrder SortFilterTableModel::initialSortOrder(int column) const 55 | { 56 | bool ok = false; 57 | if (column < 0 || column >= m_fields.count()) 58 | return Qt::AscendingOrder; 59 | int ret = m_ProcessModel.data(m_ProcessModel.index(0, m_fields[column]), Qt::InitialSortOrderRole).toInt(&ok); 60 | if (ok) 61 | return Qt::SortOrder(ret); 62 | else 63 | return Qt::AscendingOrder; 64 | } 65 | 66 | QModelIndex SortFilterTableModel::mapFromSource(const QModelIndex &sourceIndex) const 67 | { 68 | int row = QSortFilterProxyModel::mapFromSource(sourceIndex).row(); 69 | fields field = fields(sourceIndex.column()); 70 | return m_ProcessModel.index(row, m_fields.indexOf(field)); 71 | } 72 | 73 | QModelIndex SortFilterTableModel::mapToSource(const QModelIndex &proxyIndex) const 74 | { 75 | QModelIndex rowIndex = QSortFilterProxyModel::mapToSource(proxyIndex); 76 | int col = -1; 77 | if (proxyIndex.column() >= 0 && proxyIndex.column() < m_fields.count()) 78 | col = m_fields[proxyIndex.column()]; 79 | return m_ProcessModel.index(rowIndex.row(), col); 80 | } 81 | 82 | -------------------------------------------------------------------------------- /07-TableView-Column-Resizable-Image/SortFilterTableModel.h: -------------------------------------------------------------------------------- 1 | #ifndef SortFilterTableModel_H 2 | #define SortFilterTableModel_H 3 | 4 | #include 5 | #include 6 | #include "ProcessModel.h" 7 | 8 | class SortFilterTableModel : public QSortFilterProxyModel 9 | { 10 | Q_OBJECT 11 | Q_PROPERTY(ProcessModel *processModel READ processModel CONSTANT) 12 | Q_PROPERTY(QString filterText READ filterText WRITE setFilterText NOTIFY filterTextChanged) 13 | Q_CLASSINFO("DefaultProperty", "data") 14 | public: 15 | SortFilterTableModel(QObject *parent = nullptr); 16 | 17 | ProcessModel *processModel() { return &m_ProcessModel; } 18 | QString filterText() const {return m_filterText;} 19 | void setFilterText(QString filterText); 20 | Q_INVOKABLE void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; 21 | int columnCount(const QModelIndex &parent = QModelIndex()) const override; 22 | Q_INVOKABLE int columnWidth(int c, const QFont *font = nullptr); 23 | Q_INVOKABLE Qt::SortOrder initialSortOrder(int column) const; 24 | 25 | QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override; 26 | QModelIndex mapToSource(const QModelIndex &proxyIndex) const override; 27 | 28 | 29 | signals: 30 | void filterTextChanged(QString filterText); 31 | 32 | private: 33 | ProcessModel m_ProcessModel; 34 | QVector m_fields; 35 | QString m_filterText; 36 | }; 37 | 38 | #endif // SortFilterTableModel_H 39 | -------------------------------------------------------------------------------- /07-TableView-Column-Resizable-Image/SortableColumnHeading.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | 3 | Rectangle { 4 | id: root 5 | color: "wheat" 6 | property int initialSortOrder: Qt.AscendingOrder 7 | property alias text: label.text 8 | property real initialWidth: 100 9 | signal sorting 10 | width: splitter.x + 6 11 | 12 | function setWidth(newWidth) { 13 | splitter.x = newWidth -6 14 | } 15 | 16 | function stopSorting() { 17 | state = "" 18 | } 19 | 20 | Text { 21 | id: label 22 | anchors.verticalCenter: parent.verticalCenter 23 | x: 4 24 | width: parent.width - 4 25 | text: table.model.headerData(index, Qt.Horizontal) 26 | } 27 | 28 | Text { 29 | id: upDownIndicator 30 | anchors.right: parent.right 31 | anchors.margins: 8 32 | anchors.verticalCenter: parent.verticalCenter 33 | text: "^" 34 | visible: false 35 | } 36 | 37 | TapHandler { id: tap; onTapped: nextState() } 38 | 39 | Item { 40 | id: splitter 41 | x: root.initialWidth - 6 42 | width: 12 43 | height: parent.height 44 | DragHandler { 45 | yAxis.enabled: false 46 | onActiveChanged: { 47 | if (!active) { 48 | if (splitter.x <= 5 ) { 49 | splitter.x = 6 50 | } 51 | table.forceLayout() 52 | splitterRect.visible = false 53 | } 54 | else { 55 | splitterRect.visible = true 56 | } 57 | } 58 | } 59 | MouseArea { 60 | anchors.fill: parent 61 | hoverEnabled: true 62 | onEntered: splitterRect.visible = true 63 | onExited: splitterRect.visible = false 64 | } 65 | } 66 | 67 | Rectangle { 68 | id: splitterRect 69 | width: splitter.width 70 | height: splitter.height 71 | anchors.left: splitter.left 72 | anchors.top: splitter.top 73 | color: "grey" 74 | visible: false 75 | } 76 | 77 | 78 | function nextState() { 79 | if (state == "") 80 | state = (initialSortOrder == Qt.DescendingOrder ? "down" : "up") 81 | else if (state == "up") 82 | state = "down" 83 | else 84 | state = "up" 85 | root.sorting() 86 | } 87 | states: [ 88 | State { 89 | name: "up" 90 | PropertyChanges { target: upDownIndicator; visible: true; rotation: 0 } 91 | PropertyChanges { target: root; color: "orange" } 92 | }, 93 | State { 94 | name: "down" 95 | PropertyChanges { target: upDownIndicator; visible: true; rotation: 180 } 96 | PropertyChanges { target: root; color: "orange" } 97 | } 98 | ] 99 | } 100 | -------------------------------------------------------------------------------- /07-TableView-Column-Resizable-Image/images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlaemmlein/TableViewQtQuick2Examples/37f317455156c27e054ff2f46876338a39fc8dd8/07-TableView-Column-Resizable-Image/images/1.jpg -------------------------------------------------------------------------------- /07-TableView-Column-Resizable-Image/images/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlaemmlein/TableViewQtQuick2Examples/37f317455156c27e054ff2f46876338a39fc8dd8/07-TableView-Column-Resizable-Image/images/2.jpg -------------------------------------------------------------------------------- /07-TableView-Column-Resizable-Image/images/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlaemmlein/TableViewQtQuick2Examples/37f317455156c27e054ff2f46876338a39fc8dd8/07-TableView-Column-Resizable-Image/images/3.jpg -------------------------------------------------------------------------------- /07-TableView-Column-Resizable-Image/images/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlaemmlein/TableViewQtQuick2Examples/37f317455156c27e054ff2f46876338a39fc8dd8/07-TableView-Column-Resizable-Image/images/4.jpg -------------------------------------------------------------------------------- /07-TableView-Column-Resizable-Image/images/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlaemmlein/TableViewQtQuick2Examples/37f317455156c27e054ff2f46876338a39fc8dd8/07-TableView-Column-Resizable-Image/images/5.jpg -------------------------------------------------------------------------------- /07-TableView-Column-Resizable-Image/images/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlaemmlein/TableViewQtQuick2Examples/37f317455156c27e054ff2f46876338a39fc8dd8/07-TableView-Column-Resizable-Image/images/6.jpg -------------------------------------------------------------------------------- /07-TableView-Column-Resizable-Image/images/images.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1.jpg 4 | 2.jpg 5 | 3.jpg 6 | 4.jpg 7 | 5.jpg 8 | 6.jpg 9 | 10 | 11 | -------------------------------------------------------------------------------- /07-TableView-Column-Resizable-Image/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "ProcessModel.h" 7 | #include "SortFilterTableModel.h" 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | QGuiApplication app(argc, argv); 12 | 13 | qmlRegisterType("ProcessModel", 0, 1, "ProcessModel"); 14 | qmlRegisterType("SortFilterTableModel", 0, 1, "SortFilterTableModel"); 15 | 16 | QQmlApplicationEngine engine; 17 | 18 | engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 19 | 20 | 21 | return app.exec(); 22 | } 23 | 24 | -------------------------------------------------------------------------------- /07-TableView-Column-Resizable-Image/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Window 2.12 3 | import QtQuick.Layouts 1.12 4 | import ProcessModel 0.1 5 | import SortFilterTableModel 0.1 6 | import QtQuick.Controls 2.4 7 | import Qt.labs.qmlmodels 1.0 8 | 9 | ApplicationWindow { 10 | id: window 11 | visible: true 12 | width: 1024 13 | height: 800 14 | //title: qsTr("05-TableView-DelegateChooser") 15 | 16 | header: ToolBar { 17 | RowLayout { 18 | anchors.fill: parent 19 | anchors.rightMargin: 6 20 | Switch { 21 | id: cbUpdate 22 | checked: true 23 | text: qsTr("Update every") 24 | } 25 | SpinBox { 26 | id: sbUpdate 27 | from: 1 28 | to: 60 29 | value: 2 30 | enabled: cbUpdate.checked 31 | } 32 | Label { 33 | text: "sec" 34 | } 35 | Button { 36 | id: resizeCols 37 | text: qsTr("Resize cols") 38 | onClicked: { 39 | //console.log(table.model.columnCount()) 40 | for ( var index = 0; index < table.model.columnCount(); ++index) { 41 | var width = Math.min(600, table.model.columnWidth(index)) 42 | //console.log(index + " " + width) 43 | headerRepeater.itemAt(index).setWidth(width) 44 | } 45 | table.forceLayout() 46 | } 47 | } 48 | 49 | Item { 50 | Layout.fillWidth: true 51 | } 52 | TextField { 53 | id: tfFilter 54 | implicitWidth: parent.width / 4 55 | onTextEdited: table.contentY = 0 56 | } 57 | } 58 | } 59 | Row { 60 | id: header 61 | width: table.contentWidth 62 | height: cbUpdate.height 63 | x: -table.contentX 64 | z: 1 65 | spacing: 4 66 | Repeater { 67 | id: headerRepeater 68 | model: table.model.columnCount() 69 | SortableColumnHeading { 70 | initialWidth: Math.min(600, table.model.columnWidth(index)); height: parent.height 71 | text: table.model.headerData(index, Qt.Horizontal) 72 | initialSortOrder: table.model.initialSortOrder(index) 73 | onSorting: { 74 | for (var i = 0; i < headerRepeater.model; ++i) 75 | if (i !== index) 76 | headerRepeater.itemAt(i).stopSorting() 77 | table.model.sort(index, state == "up" ? Qt.AscendingOrder : Qt.DescendingOrder) 78 | } 79 | } 80 | } 81 | } 82 | TableView { 83 | id: table 84 | anchors.fill: parent 85 | anchors.topMargin: header.height 86 | columnSpacing: 4; rowSpacing: 4 87 | model: SortFilterTableModel { 88 | filterText: tfFilter.text 89 | } 90 | Timer { 91 | interval: sbUpdate.value * 1000 92 | repeat: true 93 | running: cbUpdate.checked 94 | onTriggered: table.model.processModel.update() 95 | } 96 | columnWidthProvider: function(column) { return headerRepeater.itemAt(column).width } 97 | 98 | delegate: DelegateChooser { 99 | role: "type" 100 | DelegateChoice { 101 | roleValue: "readonly" 102 | Rectangle { 103 | color: "grey" 104 | implicitHeight: readonlyText.implicitHeight 105 | Text { 106 | id: readonlyText 107 | text: model.display 108 | width: parent.width 109 | elide: Text.ElideRight 110 | font.preferShaping: false 111 | } 112 | } 113 | } 114 | DelegateChoice { 115 | roleValue: "id" 116 | Rectangle { 117 | color: "yellow" 118 | implicitHeight: idText.implicitHeight 119 | Text { 120 | id: idText 121 | text: model.display 122 | width: parent.width 123 | elide: Text.ElideRight 124 | font.preferShaping: false 125 | } 126 | } 127 | } 128 | DelegateChoice { 129 | roleValue: "string" 130 | Rectangle { 131 | color: "#0048BA" 132 | implicitHeight: stringText.implicitHeight *1.5 133 | 134 | Text { 135 | id: stringText 136 | color: "white" 137 | text: model.display 138 | width: parent.width 139 | elide: Text.ElideRight 140 | font.preferShaping: false 141 | } 142 | } 143 | } 144 | 145 | DelegateChoice { 146 | roleValue: "image" 147 | Rectangle { 148 | color: "white" 149 | implicitHeight: imageID.implicitHeight 150 | 151 | Image { 152 | id: imageID 153 | width: parent.width 154 | //height: 50 155 | fillMode: Image.PreserveAspectFit 156 | source: model.display 157 | } 158 | } 159 | } 160 | 161 | DelegateChoice { 162 | Rectangle { 163 | color: "#EEE" 164 | implicitHeight: defaultText.implicitHeight 165 | Text { 166 | id: defaultText 167 | text: model.display 168 | width: parent.width 169 | elide: Text.ElideRight 170 | horizontalAlignment: Text.AlignRight 171 | font.preferShaping: false 172 | } 173 | } 174 | } 175 | } 176 | ScrollBar.horizontal: ScrollBar { } 177 | ScrollBar.vertical: ScrollBar { } 178 | } 179 | Shortcut { sequence: StandardKey.Quit; onActivated: Qt.quit() } 180 | } 181 | -------------------------------------------------------------------------------- /07-TableView-Column-Resizable-Image/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | SortableColumnHeading.qml 5 | 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TableViewQtQuick2Examples 2 | 3 | This project is based on https://github.com/ec1oud/qps and the corresponding video https://youtu.be/-GOfY1kEP2A from Shawn Rutledge. 4 | 5 | I'm in love with the examples of Shawn, but I'm a mouse pusher under windows and the code is only working under linux 6 | (Because it is a linux tool). 7 | 8 | My examples are tested under Windows 10, Qt 5.14.2, Visual Studio 2017, Qt Creator 4.12.0. 9 | 10 | So, I transferred the core code which fetchs the system information (proc.h/.cpp) into a dummy/simulation code. 11 | 12 | Shawn shows in the video the git commit numbers for each example which he presents. 13 | For each example I have created a simple under windows working example. 14 | 15 | So here are the transferred examples: 16 | 17 | * 01-TableView-Simple-Different-Cols (-> https://youtu.be/-GOfY1kEP2A?t=492 and commit 3394948) 18 | 19 | * 02-TableView-ColumnWidth-Headings (-> https://youtu.be/-GOfY1kEP2A?t=640 and commit 6a7d765) 20 | 21 | * 03-TableView-Columns-Sortable (-> https://youtu.be/-GOfY1kEP2A?t=889 and commit 1ae40e6) 22 | 23 | * 04-TableView-Sort-Init-Filter (-> https://youtu.be/-GOfY1kEP2A?t=1155 and commit 3394948) 24 | 25 | * 05-TableView-DelegateChooser (-> https://youtu.be/-GOfY1kEP2A?t=1226 and commit 56d5affe) 26 | 27 | * 06-TableView-Column-Resizable (-> https://youtu.be/-GOfY1kEP2A?t=1553 and commit 6bcfbdb70d) 28 | 29 | 30 | ## And what are the new features in Qt 5.15 for TableView? 31 | 32 | Here we have HorizontalHeaderView and VerticalHeaderView. 33 | 34 | Build the following examples with Qt 5.15 or higher. 35 | 36 | * TableView-With-Headers 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /TableView-With-Headers/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | project(TableView-With-Headers LANGUAGES CXX) 4 | 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | set(CMAKE_AUTOMOC ON) 7 | set(CMAKE_AUTORCC ON) 8 | set(CMAKE_CXX_STANDARD 11) 9 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 10 | 11 | find_package(Qt5 COMPONENTS Core Quick REQUIRED) 12 | 13 | add_executable(${PROJECT_NAME} main.cpp 14 | qml.qrc 15 | TableModel.cpp 16 | TableModel.h 17 | Proc.cpp 18 | Proc.h) 19 | target_compile_definitions(${PROJECT_NAME} PRIVATE $<$,$>:QT_QML_DEBUG>) 20 | target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core Qt5::Quick) 21 | 22 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 23 | COMMAND ${_qt5Core_install_prefix}/bin/windeployqt.exe 24 | --verbose 0 25 | --force 26 | --no-compiler-runtime 27 | --qmldir ${_qt5Core_install_prefix}/qml 28 | $ 29 | # windeployqt is not able to deploy Qt/labs/platform! Hence we do it by own. 30 | COMMAND ${CMAKE_COMMAND} -E copy_directory 31 | ${_qt5Core_install_prefix}/qml/Qt/labs/platform 32 | $/Qt/labs/platform) 33 | 34 | -------------------------------------------------------------------------------- /TableView-With-Headers/Proc.cpp: -------------------------------------------------------------------------------- 1 | #include "Proc.h" 2 | 3 | #include 4 | std::random_device dev; 5 | std::mt19937 rng(dev()); 6 | 7 | const QMap Proc::Header_Info = { 8 | {F_PID, {"PID"}}, 9 | {F_PPID, {"PPID"}}, 10 | {F_PGID, {"PGID"}}, 11 | {F_SID, {"SID"}}, 12 | {F_STAT1, {"STAT1"}}, 13 | {F_STAT2, {"STAT2"}}, 14 | {F_STAT3, {"STAT3"}}, 15 | {F_STAT4, {"STAT4"}}, 16 | {F_CMD, {"CMD"}}, 17 | {F_CMDLINE, {"CMDLINE"}} 18 | }; 19 | 20 | ProcInfo::ProcInfo() 21 | { 22 | 23 | } 24 | 25 | ProcInfo::ProcInfo(int pid) 26 | { 27 | mpid = pid; 28 | std::uniform_int_distribution dist1(1,6); 29 | ppid = dist1(rng); 30 | std::uniform_int_distribution dist2(0,100); 31 | pgid = dist2(rng); 32 | std::uniform_int_distribution dist3(100,1000); 33 | sid = dist3(rng); 34 | stat1 = QString("%1%").arg(pgid); 35 | stat2 = QString("%1 bytes be or not to be is the question...").arg(pgid + sid); 36 | stat3 = QString("%1 bytes").arg(pgid - sid); 37 | stat4 = QString("%1 bytes").arg(pgid * sid); 38 | cmd = QString("cmd%1").arg(sid); 39 | cmdline = "cmdline"; 40 | } 41 | 42 | ProcInfo::~ProcInfo() 43 | { 44 | 45 | } 46 | 47 | QString ProcInfo::toString(fields f) 48 | { 49 | switch (f) { 50 | case F_PID: return QString("%1").arg(mpid); 51 | case F_PPID: return QString("%1").arg(ppid); 52 | case F_PGID: return QString("%1").arg(pgid); 53 | case F_SID: return QString("%1").arg(sid); 54 | case F_STAT1: return stat1; 55 | case F_STAT2: return stat2; 56 | case F_STAT3: return stat3; 57 | case F_STAT4: return stat4; 58 | case F_CMD: return cmd; 59 | case F_CMDLINE: return cmdline; 60 | default: return ""; 61 | } 62 | } 63 | 64 | 65 | Proc::Proc() 66 | { 67 | 68 | } 69 | 70 | void Proc::refresh() 71 | { 72 | procs.clear(); 73 | std::uniform_int_distribution dist(1000,2500); 74 | 75 | const int numOfPids = dist(rng); 76 | 77 | for (int i = 0; i < numOfPids; ++i) 78 | { 79 | procs.insert(i, ProcInfo(i)); 80 | } 81 | } 82 | 83 | 84 | -------------------------------------------------------------------------------- /TableView-With-Headers/Proc.h: -------------------------------------------------------------------------------- 1 | #ifndef PROC_H 2 | #define PROC_H 3 | 4 | #include 5 | #include 6 | 7 | 8 | enum fields 9 | { 10 | F_PID = 0, 11 | F_PPID, 12 | F_PGID, 13 | F_SID, 14 | F_STAT1, 15 | F_STAT2, 16 | F_STAT3, 17 | F_STAT4, 18 | F_CMD, 19 | F_CMDLINE, 20 | F_END = -1 21 | }; 22 | 23 | 24 | class ProcInfo 25 | { 26 | public: 27 | ProcInfo(); 28 | ProcInfo(int pid); 29 | ~ProcInfo(); 30 | 31 | int mpid; 32 | int ppid; 33 | int pgid; 34 | int sid; 35 | QString stat1; 36 | QString stat2; 37 | QString stat3; 38 | QString stat4; 39 | QString cmd; 40 | QString cmdline; 41 | 42 | QString toString(fields f); 43 | }; 44 | 45 | using ProcList = QMap; 46 | 47 | 48 | class Proc 49 | { 50 | public: 51 | Proc(); 52 | void refresh(); 53 | 54 | ProcList procs; // processes indexed by pid 55 | 56 | static const QMap Header_Info; 57 | }; 58 | 59 | #endif // PROC_H 60 | -------------------------------------------------------------------------------- /TableView-With-Headers/TableModel.cpp: -------------------------------------------------------------------------------- 1 | #include "TableModel.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | TableModel::TableModel(QObject *parent) : QAbstractTableModel (parent) 8 | { 9 | update(); 10 | m_timer = new QTimer(this); 11 | connect(m_timer, &QTimer::timeout, this, QOverload<>::of(&TableModel::update)); 12 | m_timer->start(2500); 13 | } 14 | 15 | TableModel::~TableModel() 16 | { 17 | 18 | } 19 | 20 | int TableModel::rowCount(const QModelIndex &) const 21 | { 22 | return m_pids.count(); 23 | } 24 | 25 | int TableModel::columnCount(const QModelIndex &) const 26 | { 27 | return F_CMDLINE + 1; 28 | } 29 | 30 | QVariant TableModel::data(const QModelIndex &index, int /*role*/) const 31 | { 32 | fields field = fields(index.column()); 33 | int pid = m_pids[index.row()]; 34 | ProcInfo pi = m_proc.procs.value(pid); 35 | return pi.toString(field); 36 | } 37 | 38 | QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const 39 | { 40 | if (role != Qt::DisplayRole) 41 | return QVariant(); 42 | 43 | if (orientation == Qt::Horizontal) { 44 | // section is interpreted as column 45 | return m_proc.Header_Info.value(static_cast(section)); 46 | } 47 | else { 48 | return QString("%0").arg(section); 49 | } 50 | } 51 | 52 | int TableModel::columnWidth(int c, const QFont *font) 53 | { 54 | if (!m_columnWidths[c]) { 55 | QString header = m_proc.Header_Info.value(static_cast(c)); 56 | QFontMetrics defaultFontMetrics = QFontMetrics(QGuiApplication::font()); 57 | QFontMetrics fm = (font ? QFontMetrics(*font) : defaultFontMetrics); 58 | int ret = fm.horizontalAdvance(headerData(c, Qt::Horizontal).toString() + QLatin1String(" ^")) + 8; 59 | for (int r = 0; r < m_pids.count(); ++r) { 60 | ProcInfo pi = m_proc.procs.value(m_pids[r]); 61 | ret = qMax(ret, fm.horizontalAdvance(pi.toString(fields(c)))); 62 | } 63 | m_columnWidths[c] = ret; 64 | } 65 | return m_columnWidths[c]; 66 | 67 | } 68 | 69 | void TableModel::update() 70 | { 71 | qDebug() << __FUNCTION__; 72 | beginResetModel(); 73 | m_proc.refresh(); 74 | m_pids = m_proc.procs.keys().toVector(); 75 | std::sort(m_pids.begin(), m_pids.end()); 76 | endResetModel(); 77 | } 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /TableView-With-Headers/TableModel.h: -------------------------------------------------------------------------------- 1 | #ifndef TABLEMODEL_H 2 | #define TABLEMODEL_H 3 | 4 | 5 | #include "Proc.h" 6 | 7 | #include 8 | #include 9 | 10 | class TableModel : public QAbstractTableModel 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit TableModel(QObject *parent= nullptr); 16 | ~TableModel(); 17 | 18 | int rowCount(const QModelIndex & = QModelIndex()) const override; 19 | int columnCount(const QModelIndex & = QModelIndex()) const override; 20 | QVariant data(const QModelIndex &index, int) const override; 21 | QVariant headerData(int section, Qt::Orientation orientation, 22 | int role = Qt::DisplayRole) const override; 23 | Q_INVOKABLE int columnWidth(int c, const QFont *font = nullptr); 24 | 25 | 26 | private slots: 27 | void update(); 28 | 29 | private: 30 | Proc m_proc; 31 | QVector m_pids; 32 | QVector m_columnWidths = QVector(F_CMDLINE + 1); 33 | QTimer* m_timer; 34 | 35 | }; 36 | 37 | 38 | #endif 39 | 40 | -------------------------------------------------------------------------------- /TableView-With-Headers/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "TableModel.h" 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | QGuiApplication app(argc, argv); 11 | 12 | qmlRegisterType("TableModel", 0, 1, "TableModel"); 13 | TableModel tm; 14 | 15 | 16 | QQmlApplicationEngine engine; 17 | 18 | engine.rootContext()->setContextProperty("_TableModel", &tm); 19 | 20 | engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 21 | 22 | 23 | return app.exec(); 24 | } 25 | 26 | -------------------------------------------------------------------------------- /TableView-With-Headers/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Window 2.12 3 | import TableModel 0.1 4 | import QtQuick.Controls 2.15 5 | 6 | ApplicationWindow { 7 | id: window 8 | visible: true 9 | width: 1024 10 | height: 800 11 | 12 | HorizontalHeaderView { 13 | id: horizontalHeader 14 | syncView: tableView 15 | anchors.left: tableView.left 16 | width: parent.width 17 | height: contentHeight 18 | } 19 | 20 | VerticalHeaderView { 21 | id: verticalHeader 22 | syncView: tableView 23 | anchors.top: tableView.top 24 | width: contentWidth 25 | height: parent.height 26 | } 27 | 28 | TableView { 29 | id: tableView 30 | anchors.fill: parent 31 | anchors.topMargin: horizontalHeader.height 32 | anchors.leftMargin: verticalHeader.width 33 | columnSpacing: 4; rowSpacing: 4 34 | model: _TableModel 35 | columnWidthProvider: function(column) { return Math.min(600, model.columnWidth(column)) } 36 | 37 | 38 | delegate: Rectangle { 39 | color: "#EEE" 40 | implicitHeight: text.implicitHeight 41 | Text { 42 | id: text 43 | text: model.display 44 | width: parent.width 45 | elide: column == 49 ? Text.ElideLeft : Text.ElideRight 46 | font.preferShaping: false 47 | } 48 | } 49 | 50 | ScrollBar.horizontal: ScrollBar { } 51 | ScrollBar.vertical: ScrollBar { } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /TableView-With-Headers/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | 5 | 6 | --------------------------------------------------------------------------------