Learning to Develop in KiCad(3) - Adding two levels data items

All my series of “Learning to Develop KiCad” posts are listed in FAQ

Index:

This is a copy of my Chinese article.

Disclaimer: All the code is written during learning, it lacks of necessary edge conditions or error handlers. These may be added into my code repo later, but will not be shown in articles.

The reason learning development of KiCad, is that I want to improve length tuning. At the moment, the length tuning in KiCad is limited: Although length tuning tool is recommended as a baseline result, rather than the value shown in Net Inspector, but length tuning tool only support simple situation: pad to pad without in a net without any extra segment in the track, nor branches.

my WIP Soc Board with DDR

The code analysis of length tuning is till in progress, may be posted here later.

For now I need to add simple two levels items management in previous dialog as following:

image

The first level is group, like “DDR Ctrl”, “DDR Addr”, “DDR Data”, etc., the second level is rule item which defines a “Pad” to “Pad” pairs, to let algorithm to sum up the track length between them, which may support track branch I hope.

Again: following code lacks of error or edge condition handlers.

Based on the previous dialog,

I added a new column named Group:


 33 enum COLUMN_INDEX { 2 refs|2 vars
 34     COLUMN_GROUP = 0, 2 refs
 35     COLUMN_NET, 3 refs
 36     COLUMN_START_PAD, 3 refs
 37     COLUMN_END_PAD, 3 refs
 38     COLUMN_TRACK_LENGTH, 1 ref
 39     COLUMN_VIA_COUNT, 1 ref
 40     COLUMN_TOTAL_LENGTH, 1 ref
 41     COLUMN_MAX, 0 ref
 42 };

 100     DATA_MODEL() 1 ref
 101     {
 102         m_columns.emplace_back(COLUMN_GROUP, _("Group"));
 103         m_columns.emplace_back(COLUMN_NET, _("Net"));
 104         m_columns.emplace_back(COLUMN_START_PAD, _("Start Pad"));
 105         m_columns.emplace_back(COLUMN_END_PAD, _("End Pad"));
 106         m_columns.emplace_back(COLUMN_TRACK_LENGTH, _("Track Length"));
 107         m_columns.emplace_back(COLUMN_VIA_COUNT, _("Vias Count"));
 108         m_columns.emplace_back(COLUMN_TOTAL_LENGTH, _("Total Length"));
 109     }

 146     void GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned int col) const override 1 b.ref|3 refs|3 refs|1 ref
 147     {
 148         wxASSERT(item.IsOk());
 149         DATA_ITEM* dataItem = static_cast<DATA_ITEM *>(item.GetID()); 2 refs
 150         if( dataItem ) {
 151             switch (col) {
 152             case COLUMN_GROUP:
 153                 variant = dataItem->GetName();
 154                 break;
 155             default:
 156                 variant = "Unhandled";
 157                 break;
 158             }
 159             return;
 160         }
 161         variant = "";
 162     }

wxDataViewCtrl itself suports multple levels (tree) data view, I just need to add support inside DATA_MODEL.

in wxDataViewCtrl there is always an invisible root: wxDataViewItem, or wxDataViewItem((void *)0). Anytime doing with DATA_ITEM, we need to check this root.

Updating DATA_ITEM, to support two types: GROUP, ITEM, to keep tree structure of parent and children relationship . As of simplicity here, I used only raw pointer DATA_ITEM*. I may replace it with std::shared_ptr and std::weak_ptr to manage pointers more safely.

56 enum DATA_ITEM_TYPE { 4 refs|2 vars
  57     GROUP, 2 refs
  58     ITEM, 0 ref
  59 };
  60
  61 class DIALOG_LENGTH_TUNING::DATA_ITEM 27 refs|12 vars
  62 {
  63 public:
  64     DATA_ITEM(const wxString& name, const DATA_ITEM* parent, const DATA_ITEM_TYPE type = DATA_ITEM_TYPE::ITEM) : 1 ref|1 ref|1 ref|1 ref
  65         m_name(name),
  66         m_parent(parent),
  67         m_type(type) {}
  68
  69     DATA_ITEM(const wxString& name) : 0 ref|1 ref
  70         m_name(name),
  71         m_parent(nullptr),
  72         m_type(DATA_ITEM_TYPE::GROUP) {}
  73
  74     const wxString& GetName() { return m_name; } 1 ref
  75
  76     bool IsContainer() 2 refs
  77     {
  78         return DATA_ITEM_TYPE::GROUP == m_type;
  79     }
  80
  81     const DATA_ITEM* GetParent() { return m_parent; } 1 ref
  82     DataItemArray& GetChildren() { return m_children; } 1 ref
  83     size_t GetChildrenCount() { return m_children.size(); } 1 ref
  84
  85     void AddChild(DIALOG_LENGTH_TUNING::DATA_ITEM* child) 1 ref|1 ref
  86     {
  87         m_children.Add(child);
  88     }
  89 private:
  90     wxString m_name; 3 refs
  91     const DATA_ITEM* m_parent; 3 refs
  92     DATA_ITEM_TYPE m_type; 3 refs
  93     DataItemArray m_children; 3 refs
  94
  95 };

DATA_MODEL::IsContainer tells wxDataViewCtrl the item is wheter a child or a parent. When a parent has one or more children, there will be a triangle can show the group is expanded or collapsed.

I then need to implement code to return this tree structure in DATA_MODEL

112     bool IsContainer(const wxDataViewItem &item) const override { 1 b.ref|2 refs
 113         if (!item.IsOk()) {
 114             return true;
 115         }
 116
 117         DATA_ITEM* dataItem = static_cast<DATA_ITEM*>(item.GetID()); 1 ref
 118         return dataItem->IsContainer();
 119     }
 120
 121     wxDataViewItem GetParent(const wxDataViewItem &item) const override 4 b.refs|2 refs
 122     {
 123         if( !item.IsOk() ) {
 124             return wxDataViewItem();
 125         }
 126         DATA_ITEM* dataItem = static_cast<DATA_ITEM*>(item.GetID()); 1 ref
 127         return wxDataViewItem((void *)dataItem->GetParent());
 128     }
 129
 130     unsigned int GetChildren(const wxDataViewItem &item, wxDataViewItemArray &children) const override 5 b.refs|2 refs|2 refs
 131     {
 132         if( !item.IsOk() ) { // is Root node
 133             for (auto& i : m_groups) { 1 ref
 134                 children.Add(wxDataViewItem(i.get()));
 135             }
 136             return m_groups.size();
 137         } else {
 138             DATA_ITEM* dataItem = static_cast<DATA_ITEM*>(item.GetID()); 2 refs
 139             for( auto c : dataItem->GetChildren() ) { 1 ref
 140                 children.Add(wxDataViewItem((void *)c));
 141             }
 142             return dataItem->GetChildrenCount();
 143         }
 144     }

Add two methods for adding group and rule (Oh I find I mixed using item and rule, I’ll fix it later )

169     void AddGroup(const wxString& groupName) 1 ref|1 ref
 170     {
 171         std::unique_ptr<DATA_ITEM> group = std::make_unique<DATA_ITEM>(groupName); 1 ref
 172         m_groups.emplace_back(std::move(group));
 173         std::unique_ptr<DATA_ITEM>& refItem = m_groups.back(); 1 ref
 174         ItemAdded(wxDataViewItem(),  wxDataViewItem(refItem.get()));
 175     }
 176
 177     void AddRule(DATA_ITEM* group, const wxString& ruleName) 1 ref|3 refs|1 ref
 178     {
 179         DATA_ITEM* rule = new DATA_ITEM(ruleName, group); 2 refs
 180         group->AddChild(rule);
 181         ItemAdded(wxDataViewItem((void *)group),  wxDataViewItem(rule));
 182     }

I added two new buttons: “Add Group” and “Nets”, with related event handlers. I didn’t implement removing code yet.

233 void DIALOG_LENGTH_TUNING::OnAddGroup( wxCommandEvent& event ) 4 b.refs|0 ref
 234 {
 235     wxTextEntryDialog addGroupDlg(this, _( "Group Name" ), _( "Group Name" )); 3 refs
 236     addGroupDlg.SetTextValidator(wxFILTER_ALPHANUMERIC);
 237     if (wxID_OK == addGroupDlg.ShowModal()) {
 238         wxString groupName = addGroupDlg.GetValue(); 1 ref
 239         m_dataModel->AddGroup(groupName);
 240     }
 241 }
 242
 243 void DIALOG_LENGTH_TUNING::OnGroupRules( wxCommandEvent& event ) 4 b.refs|0 ref
 244 {
 245     // DIALOG_LENGTH_TUNING_GROUPS * dlgGroups = new DIALOG_LENGTH_TUNING_GROUPS(this, m_frame->GetBoard());
 246     // dlgGroups->Raise();
 247
 248     wxDataViewItem selected =  m_datactrlList->GetSelection(); 3 refs
 249
 250     if( 0 != selected.GetID() ) {
 251         DATA_ITEM* group = static_cast<DATA_ITEM*>(selected.GetID()); 1 ref
 252         if( group->IsContainer()) {
 253             wxTextEntryDialog addRuleDlg(this, _( "Rule Name" ), _( "Rule Name" )); 3 refs
 254             addRuleDlg.SetTextValidator(wxFILTER_ALPHANUMERIC);
 255             if( wxID_OK == addRuleDlg.ShowModal() ) {
 256                 const wxString ruleName = addRuleDlg.GetValue(); 1 ref|1 ref
 257                 m_dataModel->AddRule(group, ruleName);
 258             }
 259         }
 260     }
 261 }
5 Likes
1 Like

@jmk This topic will be deleted in 6 days. How can it be fixed ?

I removed the timer.

I made it a WIKI. That should keep it open indefinitely. The potential downside is more people may be able to edit it but I doubt this will be an actual problem.

This should never be a problem. I (as a “level 4”) see a “pencil” appear in the upper right corner of a post if it has been edited. When I click on it, I can make a comparison between the current and previous versions. Apparently this forum software uses git (or other Version Control Software) in the background automatically.