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 translation from my Chinese article.
In the previous post, I added a dialog into KiCad, now I need to add something content into the dialog.
In wxWidgets, we usually ( I am actually a new learner to wxWidgets, and I learn it through DIALOG_NET_INSPECTOR as a guide ) use wxDataViewCtrl ( or subclasses ) to show a list. For using wxDataViewCtrl, it needs to associate a wxDataViewModel to it.
So first we need to define it in protected or private section of DIALOG_LENGTH_TUNING
class:
class DATA_MODEL; // here is forward declaction
wxObjectDataPtr<DATA_MODEL> m_dataModel; // according to official doc, it is recommended to use wxObjectDataPtr to avoid memory leak
friend DATA_MODEL;
Next step is defining DATA_MODEL
in dialog_length_tuning.cpp
. As described in [official doc](file:///home/hongbo/Developer/Engineering/wxWidgets-3.2.4-docs-html/classwx_data_view_model.html#a2c61a09270fdda6720966742f0e4f09c), you need to at least overload wxDataViewModel::IsContainer, wxDataViewModel::GetParent, wxDataViewModel::GetChildren, and wxDataViewModel::GetValue
. But in fact, you still need to overload SetValue
and GetColumnCount
too. I don’t know whether it is because the doc is not updated.
a (not working) DATA_MODEL skeleton is following:
32 class DIALOG_LENGTH_TUNING::DATA_MODEL : public wxDataViewModel 4 refs
33 {
34 public:
35 DATA_MODEL() {} 0 ref
36 ~DATA_MODEL() {} 0 ref
37
38 bool IsContainer(const wxDataViewItem &item) const override { return false; }
39
40 wxDataViewItem GetParent(const wxDataViewItem &item) const override { return wxDataViewItem(); }
41
42 unsigned int GetChildren(const wxDataViewItem &item, wxDataViewItemArray &children) const override
43 {
44 return 0;
45 }
46
47 void GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned int col) const override
48 {
49 variant = "xxx";
50 }
51
52 bool SetValue(const wxVariant &variant, const wxDataViewItem &item, unsigned int col) override { return false; }
53
54 unsigned int GetColumnCount() const override { return 0; }
55 };
In the constructor of `DIALOG_LENGTH_TUNING’, we associate the wxDataViewCtrl and wxDataViewModel:
57 DIALOG_LENGTH_TUNING::DIALOG_LENGTH_TUNING( PCB_EDIT_FRAME* aParent )
58 DIALOG_LENGTH_TUNING_BASE( aParent ),
59 m_frame(aParent)
60 {
61 m_dataModel = new DATA_MODEL();
62 m_datactrlList->AssociateModel(&*m_dataModel);
63 }
Now I suggest you do a build to check whether there are errors in previous steps.
Then we need to add headers to the list ( inside dialog_length_tuning.cpp
).
32 enum COLUMN_INDEX {
33 COLUMN_NET = 0,
34 COLUMN_START_PAD,
35 COLUMN_END_PAD,
36 COLUMN_TRACK_LENGTH,
37 COLUMN_VIA_COUNT,
38 COLUMN_TOTAL_LENGTH,
39 COLUMN_MAX,
40 };
41
42 struct DIALOG_LENGTH_TUNING::COLUMN_DESC
43 {
44 COLUMN_DESC(const COLUMN_INDEX aIdx, const wxString& aName):
45 idx(aIdx),
46 name(aName) {}
47
48 COLUMN_INDEX idx;
49 wxString name;
50 };
add m_columns
into DATA_MODEL
71 private:
72 std::vector<COLUMN_DESC> m_columns;
55 DATA_MODEL() 1 ref
56 {
57 m_columns.emplace_back(COLUMN_NET, _("Net"));
58 m_columns.emplace_back(COLUMN_START_PAD, _("Start Pad"));
59 m_columns.emplace_back(COLUMN_END_PAD, _("End Pad"));
60 m_columns.emplace_back(COLUMN_TRACK_LENGTH, _("Track Length"));
61 m_columns.emplace_back(COLUMN_VIA_COUNT, _("Vias Count"));
62 m_columns.emplace_back(COLUMN_TOTAL_LENGTH, _("Total Length"));
63 }
73 const std::vector<DIALOG_LENGTH_TUNING::COLUMN_DESC>& GetColumns() const { return m_columns; }
Add several statements into constructor of DIALOG_LENGTH_TUNING
79 DIALOG_LENGTH_TUNING::DIALOG_LENGTH_TUNING( PCB_EDIT_FRAME* aParent ) : 1 ref|2 refs
80 DIALOG_LENGTH_TUNING_BASE( aParent ),
81 m_frame(aParent)
82 {
83 m_dataModel = new DATA_MODEL();
84 m_datactrlList->AssociateModel(&*m_dataModel);
85
86 for ( auto c : m_dataModel->GetColumns() ) {
87 m_datactrlList->AppendTextColumn(c.name, c.idx);
88 }
89 }
Now build again and run, it shows list view with headers:
Next we add net names as “Dummy Data” to show in the dialog
Define DATA_ITEM
, for representing a line of the data
52 class DIALOG_LENGTH_TUNING::DATA_ITEM
53 {
54 public:
55 DATA_ITEM(const int aNetCode,
56 const wxString& aNetName)
57 m_netCode(aNetCode),
58 m_netName(aNetName) {}
59
60 private:
61 int m_netCode;
62 wxString m_netName;
63 };
An interface to add an item
118 void AddItem(std::unique_ptr<DATA_ITEM> item)
119 {
120 m_items.emplace_back(std::move(item));
121 std::unique_ptr<DATA_ITEM>& refItem = m_items.back();
122 ItemAdded(wxDataViewItem(), wxDataViewItem(refItem.get()));
123 }
Rewrite DIALOG_LENGTH_TUNING::DATA_MODEL::GetChildren
84 unsigned int GetChildren(const wxDataViewItem &item, wxDataViewItemArray &children) const override
85 {
86 if( !item.IsOk() ) { // is Root node
87 for (auto& i : m_items) {
88 children.Add(wxDataViewItem(i.get()));
89 }
90 return m_items.size();
91 }
92
93 return 0;
94 }
Rewrite DIALOG_LENGTH_TUNING::DATA_MODEL::GetValue
96 void GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned int col) const override
97 {
98 DATA_ITEM* dataItem = static_cast<DATA_ITEM *>(item.GetID());
99 if( dataItem ) {
100 switch (col) {
101 case COLUMN_NET:
102 variant = dataItem->GetNetName();
103 break;
104 default:
105 variant = "Unhandled";
106 break;
107 }
108 return;
109 }
110 variant = "";
111 }
Add Net info into our data model in constructor of DIALOG_LENGTH_TUNING
129 DIALOG_LENGTH_TUNING::DIALOG_LENGTH_TUNING( PCB_EDIT_FRAME* aParent ) :
130 DIALOG_LENGTH_TUNING_BASE( aParent ),
131 m_frame(aParent)
132 {
133 m_dataModel = new DATA_MODEL();
134 m_datactrlList->AssociateModel(&*m_dataModel);
135
136 for ( auto c : m_dataModel->GetColumns() ) {
137 m_datactrlList->AppendTextColumn(c.name, c.idx);
138 }
139
140 const BOARD* board = m_frame->GetBoard();
141 const NETINFO_LIST& netsList = board->GetNetInfo();
142
143 for( auto ni : netsList.NetsByNetcode() ) {
144 NETINFO_ITEM* netInfo = ni.second;
145 std::unique_ptr<DATA_ITEM> item = std::make_unique<DATA_ITEM>(
146 netInfo->GetNetCode(),
147 netInfo->GetNetname());
148 m_dataModel->AddItem(std::move(item));
149 }
150 }
Now build again and (if no errors) run, it shows:
Working in Progress code is here