All my series of “Learning to Develop KiCad” posts are listed in FAQ
Index:
- Use cases in regexp filter used during DDR4 routing
- Add a Dialog
- Add Netlist in the Dialog
- Add two levels data items
- Code reading / analysis of length calculation in Length Tuning Tool
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.
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:
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 }