TreeWidgetBuilder.h
Go to the documentation of this file.
1#pragma once
2
3#include <functional>
4#include <map>
5#include <sstream>
6
7#include <QTreeWidget>
8
10
11namespace armarx
12{
13
14 /**
15 * A class to efficiently build and maintain sorted items of `QTreeWidget`
16 * or `QTreeWidgetItem` based on a sorted container matching the intended structure.
17 */
18 template <class _ElementT>
20 {
21 using ElementT = _ElementT;
22
23 /// Return < 0 if `element < item`, 0 if `element == item`, and > 0 if `element > item`.
24 using CompareFn = std::function<int(const ElementT& element, QTreeWidgetItem* item)>;
25 using NameFn = std::function<std::string(const ElementT& element)>;
26 using MakeItemFn = std::function<QTreeWidgetItem*(const ElementT& element)>;
27 using UpdateItemFn = std::function<bool(const ElementT& element, QTreeWidgetItem* item)>;
28
29
30 TreeWidgetBuilder() = default;
31
32 /// Constructor to automatically derive the template argument.
34 {
35 }
36
38 MakeItemFn makeItemFn,
39 UpdateItemFn updateItemFn = NoUpdate) :
40 compareFn(compareFn), makeItemFn(makeItemFn), updateItemFn(updateItemFn)
41 {
42 }
43
45 MakeItemFn makeItemFn,
46 UpdateItemFn updateItemFn = NoUpdate,
47 int nameColumn = 0) :
48 compareFn(MakeCompareNameFn(nameFn, nameColumn)),
49 makeItemFn(makeItemFn),
50 updateItemFn(updateItemFn)
51 {
52 }
53
54 void
56 {
57 this->compareFn = compareFn;
58 }
59
60 void
61 setNameFn(NameFn nameFn, int nameColumn = 0)
62 {
63 compareFn = MakeCompareNameFn(nameFn, nameColumn);
64 }
65
66 void
68 {
69 this->makeItemFn = makeItemFn;
70 }
71
72 void
74 {
75 this->updateItemFn = updateItemFn;
76 }
77
78 void
79 setExpand(bool expand)
80 {
81 this->expand = expand;
82 }
83
84
85 /**
86 * @brief Update the tree according to the elements iterated over by `iteratorFn`.
87 *
88 * @param parent
89 * The parent, i.e. the tree widget or a tree widget item.
90 * @param iteratorFn
91 * Function taking a function `bool fn(const ElementT& element)`
92 * and calling it for each element in the underlying container, i.e. like:
93 * void iteratorFn(bool (*elementFn)(const ElementT& element));
94 */
95 template <class ParentT, class IteratorFn>
96 void updateTreeWithIterator(ParentT* parent, IteratorFn&& iteratorFn);
97
98 /**
99 * @brief Update the tree with the iterable container.
100 *
101 * @param parent
102 * The parent, i.e. the tree widget or a tree widget item.
103 * @param iteratorFn
104 * Function taking a function `bool fn(const ElementT& element)`
105 * and calling it for each element in the underlying container, i.e. like:
106 * void iteratorFn(bool (*elementFn)(const ElementT& element));
107 */
108 template <class ParentT, class ContainerT>
109 void updateTreeWithContainer(ParentT* parent, const ContainerT& elements);
110
111 /// No update function (default).
112 static bool
113 NoUpdate(const ElementT& element, QTreeWidgetItem* item)
114 {
115 (void)element, (void)item;
116 return true;
117 }
118
119 /// Uses the name for comparison.
120 static CompareFn MakeCompareNameFn(NameFn nameFn, int nameColumn);
121
122
123 private:
124 CompareFn compareFn;
125 MakeItemFn makeItemFn;
126 UpdateItemFn updateItemFn;
127
128 /// Whether to expand items on first creation.
129 bool expand = false;
130 };
131
132 /**
133 * A class to efficiently build and maintain sorted items of `QTreeWidget`
134 * or `QTreeWidgetItem` based on a map matching the intended structure.
135 */
136 template <class KeyT, class ValueT>
138 {
139 using MapT = std::map<KeyT, ValueT>;
141 using ElementT = typename Base::ElementT;
142
143 using CompareFn = std::function<int(const ElementT& element, QTreeWidgetItem* item)>;
144 using NameFn = std::function<std::string(const KeyT& key, const ValueT& value)>;
145
146 using MakeItemFn = std::function<QTreeWidgetItem*(const KeyT& key, const ValueT& value)>;
148 std::function<bool(const KeyT& key, const ValueT& value, QTreeWidgetItem* item)>;
149
154
155 /// Allows declaring instance from container without explicit template arguments.
159
162 {
163 setMakeItemFn(makeItemFn);
164 setUpdateItemFn(updateItemFn);
165 }
166
168 MakeItemFn makeItemFn,
169 UpdateItemFn updateItemFn = NoUpdate)
170 {
171 setNameFn(nameFn);
172 setMakeItemFn(makeItemFn);
173 setUpdateItemFn(updateItemFn);
174 }
175
176 void
178 {
179 builder.setNameFn(
180 [nameFn](const ElementT& element)
181 {
182 const auto& [key, value] = element;
183 return nameFn(key, value);
184 });
185 }
186
187 void
189 {
190 builder.setCompareFn(compareFn);
191 }
192
193 void
195 {
196 builder.setMakeItemFn(
197 [makeItemFn](const ElementT& element)
198 {
199 const auto& [key, value] = element;
200 return makeItemFn(key, value);
201 });
202 }
203
204 void
206 {
207 builder.setUpdateItemFn(
208 [updateItemFn](const ElementT& element, QTreeWidgetItem* item)
209 {
210 const auto& [key, value] = element;
211 return updateItemFn(key, value, item);
212 });
213 }
214
215 void
216 setExpand(bool expand)
217 {
218 builder.setExpand(expand);
219 }
220
221 template <class ParentT>
222 void
223 updateTree(ParentT* tree, const MapT& elements)
224 {
225 builder.updateTreeWithContainer(tree, elements);
226 }
227
228 /// A name function using the key as name.
229 static std::string
230 KeyAsName(const KeyT& key, const ValueT& value)
231 {
232 (void)value;
233 if constexpr (std::is_same<KeyT, std::string>())
234 {
235 return key;
236 }
237 else
238 {
239 std::stringstream ss;
240 ss << key;
241 return ss.str();
242 }
243 }
244
245 /// No update function (default).
246 static bool
247 NoUpdate(const KeyT& key, const ValueT& value, QTreeWidgetItem* item)
248 {
249 (void)key, (void)value, (void)item;
250 return true;
251 }
252
253
254 private:
256 };
257
258 namespace detail
259 {
260 template <class ParentT>
261 struct ParentAPI;
262
263 template <>
264 struct ParentAPI<QTreeWidget>
265 {
266 static int
267 getItemCount(QTreeWidget* tree)
268 {
269 return tree->topLevelItemCount();
270 }
271
272 static QTreeWidgetItem*
273 getItem(QTreeWidget* tree, int index)
274 {
275 return tree->topLevelItem(index);
276 }
277
278 static void
279 insertItem(QTreeWidget* tree, int index, QTreeWidgetItem* item)
280 {
281 tree->insertTopLevelItem(index, item);
282 }
283
284 static QTreeWidgetItem*
285 takeItem(QTreeWidget* tree, int index)
286 {
287 return tree->takeTopLevelItem(index);
288 }
289 };
290
291 template <>
292 struct ParentAPI<QTreeWidgetItem>
293 {
294 static int
295 getItemCount(QTreeWidgetItem* parent)
296 {
297 return parent->childCount();
298 }
299
300 static QTreeWidgetItem*
301 getItem(QTreeWidgetItem* parent, int index)
302 {
303 return parent->child(index);
304 }
305
306 static QTreeWidgetItem*
307 takeItem(QTreeWidgetItem* parent, int index)
308 {
309 return parent->takeChild(index);
310 }
311
312 static void
313 insertItem(QTreeWidgetItem* parent, int index, QTreeWidgetItem* item)
314 {
315 parent->insertChild(index, item);
316 }
317 };
318
319 template <typename T>
320 int
321 compare(const T& lhs, const T& rhs)
322 {
323 if (lhs < rhs)
324 {
325 return -1;
326 }
327 else if (lhs == rhs)
328 {
329 return 0;
330 }
331 else
332 {
333 return 1;
334 }
335 }
336
337 inline int
338 compare(const std::string& lhs, const std::string& rhs)
339 {
340 return lhs.compare(rhs);
341 }
342 } // namespace detail
343
344 template <class ElementT>
345 auto
347 {
348 return [nameFn, nameColumn](const ElementT& element, QTreeWidgetItem* item)
349 { return detail::compare(nameFn(element), item->text(nameColumn).toStdString()); };
350 }
351
352 template <class ElementT>
353 template <class ParentT, class ContainerT>
354 void
356 const ContainerT& elements)
357 {
358 this->updateTreeWithIterator(parent,
359 [&elements](auto&& elementFn)
360 {
361 for (const auto& element : elements)
362 {
363 if (not elementFn(element))
364 {
365 break;
366 }
367 }
368 });
369 }
370
371 template <class ElementT>
372 template <class ParentT, class IteratorFn>
373 void
374 TreeWidgetBuilder<ElementT>::updateTreeWithIterator(ParentT* parent, IteratorFn&& iteratorFn)
375 {
376 using api = detail::ParentAPI<ParentT>;
377
378 ARMARX_CHECK_NOT_NULL(makeItemFn) << "makeItemFn must be set";
379 ARMARX_CHECK_NOT_NULL(updateItemFn) << "updateItemFn must be set";
380 ARMARX_CHECK_NOT_NULL(compareFn) << "compareFn must be set";
381
382 int currentIndex = 0;
383 iteratorFn(
384 [this, &parent, &currentIndex](const auto& element)
385 {
386 bool inserted = false;
387 QTreeWidgetItem* item = nullptr;
388 if (currentIndex >= api::getItemCount(parent))
389 {
390 // Add elements to the end of the list.
391 ARMARX_CHECK_NOT_NULL(makeItemFn);
392 item = makeItemFn(element);
393 api::insertItem(parent, api::getItemCount(parent), item);
394 ++currentIndex;
395 inserted = true;
396 }
397 else
398 {
399 QTreeWidgetItem* currentItem = api::getItem(parent, currentIndex);
400 while (currentItem != nullptr && compareFn(element, currentItem) > 0)
401 {
402 delete api::takeItem(parent, currentIndex);
403 currentItem = api::getItem(parent, currentIndex);
404 }
405 if (currentItem == nullptr || compareFn(element, currentItem) < 0)
406 {
407 // Insert new item before child.
408 item = makeItemFn(element);
409 api::insertItem(parent, currentIndex, item);
410 ++currentIndex;
411 inserted = true;
412 }
413 else if (currentItem != nullptr && compareFn(element, currentItem) == 0)
414 {
415 // Already existing.
416 item = currentItem;
417 ++currentIndex;
418 }
419 }
421 bool cont = updateItemFn(element, item);
422 if (inserted && expand)
423 {
424 item->setExpanded(true);
425 }
426 return cont;
427 });
428 // Remove superfluous items. (currentIndex must point behind the last item)
429 while (api::getItemCount(parent) > currentIndex)
430 {
431 delete api::takeItem(parent, api::getItemCount(parent) - 1);
432 }
433 ARMARX_CHECK_EQUAL(currentIndex, api::getItemCount(parent));
434 }
435
436} // namespace armarx
uint8_t index
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#define ARMARX_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
int compare(const T &lhs, const T &rhs)
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::function< QTreeWidgetItem *(const KeyT &key, const ValueT &value)> MakeItemFn
MapTreeWidgetBuilder(NameFn nameFn, MakeItemFn makeItemFn, UpdateItemFn updateItemFn=NoUpdate)
static bool NoUpdate(const KeyT &key, const ValueT &value, QTreeWidgetItem *item)
No update function (default).
std::function< std::string(const KeyT &key, const ValueT &value)> NameFn
std::function< int(const ElementT &element, QTreeWidgetItem *item)> CompareFn
MapTreeWidgetBuilder(const MapT &)
Allows declaring instance from container without explicit template arguments.
void setUpdateItemFn(UpdateItemFn updateItemFn)
std::map< KeyT, ValueT > MapT
static std::string KeyAsName(const KeyT &key, const ValueT &value)
A name function using the key as name.
typename Base::ElementT ElementT
TreeWidgetBuilder< typename MapT::value_type > Base
void updateTree(ParentT *tree, const MapT &elements)
std::function< bool(const KeyT &key, const ValueT &value, QTreeWidgetItem *item)> UpdateItemFn
MapTreeWidgetBuilder(MakeItemFn makeItemFn, UpdateItemFn updateItemFn=NoUpdate)
void setMakeItemFn(MakeItemFn makeItemFn)
void setCompareFn(CompareFn compareFn)
A class to efficiently build and maintain sorted items of QTreeWidget or QTreeWidgetItem based on a s...
void setNameFn(NameFn nameFn, int nameColumn=0)
std::function< int(const ElementT &element, QTreeWidgetItem *item)> CompareFn
Return < 0 if element < item, 0 if element == item, and > 0 if element > item.
TreeWidgetBuilder(const ElementT &)
Constructor to automatically derive the template argument.
void setUpdateItemFn(UpdateItemFn updateItemFn)
void updateTreeWithIterator(ParentT *parent, IteratorFn &&iteratorFn)
Update the tree according to the elements iterated over by iteratorFn.
std::function< QTreeWidgetItem *(const ElementT &element)> MakeItemFn
TreeWidgetBuilder(CompareFn compareFn, MakeItemFn makeItemFn, UpdateItemFn updateItemFn=NoUpdate)
static CompareFn MakeCompareNameFn(NameFn nameFn, int nameColumn)
static bool NoUpdate(const ElementT &element, QTreeWidgetItem *item)
std::function< bool(const ElementT &element, QTreeWidgetItem *item)> UpdateItemFn
void setMakeItemFn(MakeItemFn makeItemFn)
void updateTreeWithContainer(ParentT *parent, const ContainerT &elements)
Update the tree with the iterable container.
TreeWidgetBuilder(NameFn nameFn, MakeItemFn makeItemFn, UpdateItemFn updateItemFn=NoUpdate, int nameColumn=0)
void setCompareFn(CompareFn compareFn)
std::function< std::string(const ElementT &element)> NameFn
static QTreeWidgetItem * getItem(QTreeWidgetItem *parent, int index)
static void insertItem(QTreeWidgetItem *parent, int index, QTreeWidgetItem *item)
static QTreeWidgetItem * takeItem(QTreeWidgetItem *parent, int index)
static int getItemCount(QTreeWidgetItem *parent)
static void insertItem(QTreeWidget *tree, int index, QTreeWidgetItem *item)
static QTreeWidgetItem * takeItem(QTreeWidget *tree, int index)
static QTreeWidgetItem * getItem(QTreeWidget *tree, int index)
static int getItemCount(QTreeWidget *tree)