-
Notifications
You must be signed in to change notification settings - Fork 37
Introduce a new example using a QSortFilterProxyModel #174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
1e26114
83989fd
aa6a401
2c04594
4bba1b8
acb51f0
24dd359
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "fmt" | ||
| qt "github.com/mappu/miqt/qt6" | ||
| "os" | ||
| ) | ||
|
|
||
| func GetTreeView(parent *qt.QWidget) *qt.QTreeView { | ||
| treeView := qt.NewQTreeView(parent) | ||
| treeView.SetAlternatingRowColors(true) | ||
| treeView.SetSortingEnabled(true) | ||
| treeView.SetColumnWidth(0, 300) | ||
| treeView.SetContentsMargins(0, 0, 0, 0) | ||
| treeView.Header().SetDefaultSectionSize(200) | ||
| treeView.Header().SetStretchLastSection(true) | ||
| treeView.SetSelectionMode(qt.QAbstractItemView__ExtendedSelection) | ||
|
|
||
| return treeView | ||
| } | ||
|
|
||
| func PopulateData(rootItem *TreeItem) { | ||
| cellData := make([]*TreeCell, 0) | ||
| cellData = append(cellData, NewTreeCell(qt.DisplayRole, *qt.NewQVariant11("Big File"))) | ||
| cellData = append(cellData, NewTreeCell(qt.DisplayRole, *qt.NewQVariant11("10.7 MiB"))) | ||
| item := NewTreeItem(cellData, rootItem, false) | ||
| err := item.SetData(1, qt.NewQVariant6(11225400), qt.UserRole) | ||
| if err != nil { | ||
| fmt.Printf("Error setting UserData: %v\n", err) | ||
| } | ||
| err = item.SetData(0, qt.NewQVariant11("Big File"), qt.UserRole) | ||
| if err != nil { | ||
| fmt.Printf("Error setting UserData: %v\n", err) | ||
| } | ||
| rootItem.AppendChild(&item) | ||
|
|
||
| cellData2 := make([]*TreeCell, 0) | ||
| cellData2 = append(cellData2, NewTreeCell(qt.DisplayRole, *qt.NewQVariant11("Small File"))) | ||
| cellData2 = append(cellData2, NewTreeCell(qt.DisplayRole, *qt.NewQVariant11("110 Bytes"))) | ||
| item2 := NewTreeItem(cellData2, rootItem, false) | ||
| err = item2.SetData(1, qt.NewQVariant6(110), qt.UserRole) | ||
| if err != nil { | ||
| fmt.Printf("Error setting UserData: %v\n", err) | ||
| } | ||
| err = item.SetData(0, qt.NewQVariant11("Small File"), qt.UserRole) | ||
| if err != nil { | ||
| fmt.Printf("Error setting UserData: %v\n", err) | ||
| } | ||
| rootItem.AppendChild(&item2) | ||
|
|
||
| cellData3 := make([]*TreeCell, 0) | ||
| cellData3 = append(cellData3, NewTreeCell(qt.DisplayRole, *qt.NewQVariant11("Medium File"))) | ||
| cellData3 = append(cellData3, NewTreeCell(qt.DisplayRole, *qt.NewQVariant11("109.5 KiB"))) | ||
| item3 := NewTreeItem(cellData3, rootItem, false) | ||
| err = item3.SetData(1, qt.NewQVariant6(112200), qt.UserRole) | ||
| if err != nil { | ||
| fmt.Printf("Error setting UserData: %v\n", err) | ||
| } | ||
| err = item.SetData(0, qt.NewQVariant11("Medium File"), qt.UserRole) | ||
| if err != nil { | ||
| fmt.Printf("Error setting UserData: %v\n", err) | ||
| } | ||
| rootItem.AppendChild(&item3) | ||
| } | ||
|
|
||
| func GetDialog() *qt.QDialog { | ||
| dialog := qt.NewQDialog3(nil, qt.Dialog) | ||
| dialog.SetWindowFlags(qt.WindowStaysOnTopHint) | ||
| dialogLayout := qt.NewQVBoxLayout2() | ||
| dialogLayout.SetSpacing(0) | ||
| dialogLayout.SetContentsMargins(0, 0, 0, 0) | ||
| dialog.SetContentsMargins(10, 10, 10, 10) | ||
| dialog.SetLayout(dialogLayout.Layout()) | ||
| dialog.SetWindowTitle("Example Sort Model Usage") | ||
|
|
||
| treeView := GetTreeView(dialog.QWidget) | ||
|
|
||
| // Model | ||
| model, rootItem := GetModel(dialog.QObject) | ||
| PopulateData(rootItem) | ||
|
|
||
| sortModel := qt.NewQSortFilterProxyModel2(treeView.QObject) | ||
| sortModel.SetSortRole(int(qt.UserRole)) | ||
| sortModel.OnLessThan(func(super func(source_left *qt.QModelIndex, source_right *qt.QModelIndex) bool, source_left *qt.QModelIndex, source_right *qt.QModelIndex) bool { | ||
| // In our case we know that the data from our TreeItem will come back as an int for our UserRole | ||
| leftData := sortModel.SourceModel().Data(source_left, sortModel.SortRole()) | ||
| rightData := sortModel.SourceModel().Data(source_right, sortModel.SortRole()) | ||
|
|
||
| if source_left.Column() == 0 { | ||
| return leftData.ToString() < rightData.ToString() | ||
| } | ||
| return leftData.ToLongLong() < rightData.ToLongLong() | ||
| }) | ||
|
|
||
| sortModel.SetSourceModel(model) | ||
| treeView.SetModel(sortModel.QAbstractItemModel) | ||
|
|
||
| dialogLayout.AddWidget3(treeView.QWidget, 0, qt.AlignCenter) | ||
| return dialog | ||
| } | ||
|
|
||
| func main() { | ||
| qt.NewQApplication(os.Args) | ||
|
|
||
| dialog := GetDialog() | ||
| dialog.Show() | ||
|
|
||
| qt.QApplication_Exec() | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| qt "github.com/mappu/miqt/qt6" | ||
| "slices" | ||
| "unsafe" | ||
| ) | ||
|
|
||
| func GetModel(parent *qt.QObject) (*qt.QAbstractItemModel, *TreeItem) { | ||
| model := qt.NewQAbstractItemModel2(parent) | ||
|
|
||
| data := make([]*TreeCell, 0) | ||
| data = append(data, NewTreeCell(qt.DisplayRole, *qt.NewQVariant11("Name"))) | ||
| data = append(data, NewTreeCell(qt.DisplayRole, *qt.NewQVariant11("Size"))) | ||
| rootItem := NewTreeItem(data, nil, true) | ||
|
|
||
| model.OnData(func(index *qt.QModelIndex, role int) *qt.QVariant { | ||
| if !index.IsValid() { | ||
| return qt.NewQVariant() | ||
| } | ||
| if !slices.Contains([]qt.ItemDataRole{qt.DisplayRole, qt.UserRole}, qt.ItemDataRole(role)) { | ||
| return qt.NewQVariant() | ||
| } | ||
| item := (*TreeItem)(index.InternalPointer()) | ||
| return item.Data(index.Column(), qt.ItemDataRole(role)) | ||
| }) | ||
|
|
||
| model.OnHeaderData(func(super func(section int, orientation qt.Orientation, role int) *qt.QVariant, section int, orientation qt.Orientation, role int) *qt.QVariant { | ||
| if qt.ItemDataRole(role) != qt.DisplayRole { | ||
| return qt.NewQVariant() | ||
| } | ||
| if orientation == qt.Horizontal && role == int(qt.DisplayRole) { | ||
| return rootItem.Data(section, qt.DisplayRole) | ||
| } | ||
| return qt.NewQVariant() | ||
| }) | ||
|
|
||
| model.OnIndex(func(row int, column int, parent *qt.QModelIndex) *qt.QModelIndex { | ||
| if !model.HasIndex3(row, column, parent) { | ||
| return qt.NewQModelIndex() | ||
| } | ||
| var parentItem *TreeItem | ||
| if !parent.IsValid() { | ||
| parentItem = &rootItem | ||
| } else { | ||
| parentItem = (*TreeItem)(parent.InternalPointer()) | ||
| } | ||
| child, err := parentItem.Child(row) | ||
| if err != nil { | ||
| return qt.NewQModelIndex() | ||
| } | ||
| newIndex := model.CreateIndex2(row, column, uintptr(unsafe.Pointer(child))) | ||
| return &newIndex | ||
| }) | ||
|
|
||
| model.OnParent(func(child *qt.QModelIndex) *qt.QModelIndex { | ||
| if !child.IsValid() { | ||
| return qt.NewQModelIndex() | ||
| } | ||
| parentItem := (*TreeItem)(child.InternalPointer()).ParentItem() | ||
| if parentItem != &rootItem { | ||
| row, err := parentItem.Row() | ||
| if err != nil { | ||
| return qt.NewQModelIndex() | ||
| } | ||
| index := model.CreateIndex2(row, 0, uintptr(unsafe.Pointer(parentItem))) | ||
| return &index | ||
| } | ||
| return qt.NewQModelIndex() | ||
| }) | ||
|
|
||
| model.OnRowCount(func(parent *qt.QModelIndex) int { | ||
| if parent.Column() > 0 { | ||
| return 0 | ||
| } | ||
| var item *TreeItem | ||
| if !parent.IsValid() { | ||
| item = &rootItem | ||
| } else { | ||
| item = (*TreeItem)(parent.InternalPointer()) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are some extra rules when passing pointers between Go and C. One of the rules is: If there is no Go pointer to the TreeItem, only a C++ pointer, then Go might GC the variable, and this could become a use-after-free. Is there any other place this TreeItem is reachable other than from Qt (C++) memory? If not, a workaround is to either (A) ensure that the TreeItems are stored in some long-lived application struct or global variable; or (B) call runtime.Keepalive() at some later point.
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or (C) Wrap the thing in a cgo.Handle before putting it in Qt space, and unwrap it when pulling it out again. This is what Miqt mostly does internally.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok so the TreeItem is created in I don't know if that is enough to keep it alive. I am not super clear if pulling in a a pointer from the QModelIndex and then casting it to a TreeItem pointer is the right way to do that, I am open to another way to get the modelItem from a QModelIndex if there is one.
|
||
| } | ||
| return item.ChildCount() | ||
| }) | ||
|
|
||
| model.OnColumnCount(func(parent *qt.QModelIndex) int { | ||
| if parent.IsValid() { | ||
| item := (*TreeItem)(parent.InternalPointer()) | ||
| return item.ColumnCount() | ||
| } | ||
| return rootItem.ColumnCount() | ||
| }) | ||
|
|
||
| return model, &rootItem | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "fmt" | ||
| qt "github.com/mappu/miqt/qt6" | ||
| "strconv" | ||
| ) | ||
|
|
||
| type TreeCell struct { | ||
| data map[qt.ItemDataRole]string | ||
| } | ||
|
|
||
| func NewTreeCell(role qt.ItemDataRole, value qt.QVariant) *TreeCell { | ||
| data := make(map[qt.ItemDataRole]string) | ||
| data[role] = value.ToString() | ||
| return &TreeCell{data: data} | ||
| } | ||
|
|
||
| func (cell *TreeCell) SetData(data qt.QVariant, role qt.ItemDataRole) { | ||
| cell.data[role] = data.ToString() | ||
| } | ||
|
|
||
| func (cell *TreeCell) Data(role qt.ItemDataRole) *qt.QVariant { | ||
| if role == qt.UserRole { | ||
| number, err := strconv.ParseInt(cell.data[role], 10, 64) | ||
| if err != nil { | ||
| fmt.Printf("Error converting %s to int: %v\n", number, err) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When starting up the example, I got these terminal messages: They repeat when attempting to sort by the Name column,
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah thanks I will fix that! |
||
| return qt.NewQVariant11(cell.data[role]) | ||
| } | ||
| return qt.NewQVariant6(number) | ||
| } | ||
| return qt.NewQVariant11(cell.data[role]) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the default view, the Size column is initially truncated, so I went to resize the window larger. But something about the
QSizePolicyis meaning the treeview will not grow when the window is resized (although it does shrink). I think this could be fixed in a 1 line change, but I haven't checked exactly.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I took a deeper look at this.
I can't figure out how to make this work as I would expect.
The only way I can get it working is with the following hack:
But having written Qt before, obviously that is a messy hack.
Is there a possibility that the default Resize events aren't being propagated down to child widgets?