www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - GtkD - how to list 0..100K strings

reply mark <mark qtrac.eu> writes:
I'm trying to develop an application in GtkD.

I need a widget to display a list of strings: there could be 
anything from 0 to 100K strings, but typically a few hundred or 
thousand.

Using the DemoCustomList as a model I have created this code:

// Note: DebNames is an AAset!string (AAset is a wrapper around a 
D AA, so in this case a set of strings)

// namestore.d
import gtk.ListStore: ListStore;

class NameStore : ListStore {

     this(DebNames names) {
         import gobject.c.types: GType;
         import gtk.TreeIter: TreeIter;

         super([GType.STRING]);

         TreeIter iter;
         foreach (name; names) {
             append(iter);
             setValue(iter, 0, name);
         }
     }
}

// appwindow.d
final class AppWindow: ApplicationWindow {
     TreeView debsTreeView;
     NameStore nameStore;
     // ...
     private void populateNames(DebNames names) {
         import gtk.CellRendererText: CellRendererText;
         import gtk.TreeViewColumn: TreeViewColumn;

         nameStore = new NameStore(names);
         auto column = new TreeViewColumn;
         auto renderer = new CellRendererText;
         column.packStart(renderer, true);
         column.addAttribute(renderer, "text", 0);
         column.setTitle("Names");
         debsTreeView.appendColumn(column);
     }
}

When populateNames() is called the treeview expands horizontally 
but shows nothing, so I'm stuck.

Can anyone help?

Note that I don't have to use a tree widget if there's a better 
one for this use case. I did see ListBox but that seemed to be a 
list of widgets which would be a bit heavy for 100K strings?
Apr 26 2020
next sibling parent reply Mike Wey <mike-wey example.com> writes:
On 26-04-2020 10:06, mark wrote:
 I'm trying to develop an application in GtkD.
 
 I need a widget to display a list of strings: there could be anything 
 from 0 to 100K strings, but typically a few hundred or thousand.
 
 Using the DemoCustomList as a model I have created this code:
 
 ...
 
 When populateNames() is called the treeview expands horizontally but 
 shows nothing, so I'm stuck.
 
 Can anyone help?
 
 Note that I don't have to use a tree widget if there's a better one for 
 this use case. I did see ListBox but that seemed to be a list of widgets 
 which would be a bit heavy for 100K strings?
The code looks correct, do you have something that compiles so that we can test where things go wrong? -- Mike Wey
Apr 26 2020
parent reply mark <mark qtrac.eu> writes:
I've now got it to work but it is unusable!

It can show small numbers of rows with no problem.

However, if it has to show 100s of rows it expands the tree 
vertically way beyond the bottom of the screen and is impossible 
to navigate.

However, if it has to show 1000s of rows it goes into an infinite 
loop producing endless error messages:

     (DebFind:6615): Gtk-WARNING **: 10:57:04.275: infinite 
surface size not supported

I suppose I was expecting scrollbars to appear, but maybe with 
Gtk you have do add them separately?

// Here's the view I'm now using
class View : TreeView {
     import gtk.CellRendererText: CellRendererText;
     import gtk.TreeViewColumn: TreeViewColumn;
     import qtrac.debfind.modelutil: NameAndDescription;
     import qtrac.debfind.viewdata: ViewData;

     ViewData viewData;
     TreeViewColumn nameColumn;
     TreeViewColumn descriptionColumn;

     this() {
         super();
         setActivateOnSingleClick(true);
         viewData = new ViewData;
         setModel(viewData);
         auto renderer = new CellRendererText;
         nameColumn = new TreeViewColumn("Name", renderer, "text", 
0);
         nameColumn.setResizable(true);
         appendColumn(nameColumn);
         renderer = new CellRendererText;
         descriptionColumn = new TreeViewColumn("Description", 
renderer,
                                                "text", 1);
         descriptionColumn.setResizable(true);
         appendColumn(descriptionColumn);
     }

     void clear() {
         viewData.clear;
     }

     void populate(NameAndDescription[] namesAndDescriptions) {
         viewData.populate(namesAndDescriptions);
     }
}

// Here's the ListStore I'm using:
class ViewData : ListStore {
     import qtrac.debfind.modelutil: NameAndDescription;

     this() {
         import gobject.c.types: GType;
         super([GType.STRING, GType.STRING]);
     }

     void populate(NameAndDescription[] namesAndDescriptions) {
         import gtk.TreeIter: TreeIter;

         clear;
         TreeIter iter;
         foreach (i, nameAndDescription; namesAndDescriptions) {
             append(iter);
             setValue(iter, 0, nameAndDescription.name);
             setValue(iter, 1,                  
nameAndDescription.description);
         }
     }
}

// I have the TreeView side-by-side with a TextView: here're 
snippets:
         Paned splitter;
         View debsView;
         TextView debTextView;
...
         splitter = new Paned(GtkOrientation.HORIZONTAL);
         splitter.setWideHandle(true);
         debsView = new View;
         debsView.setHexpand(true);
         debsView.setVexpand(true);
         debTextView = new TextView;
...
         auto grid = new Grid;
...
         splitter.pack1(debsView, false, true);
         splitter.pack2(debTextView, true, true);
         grid.attach(splitter, 0, 3, 6, 1);
Apr 27 2020
parent reply mark <mark qtrac.eu> writes:
I renamed the class shown in my previous post from View to 
InnerView, then created a new View class:

class View : ScrolledWindow {
     import qtrac.debfind.modelutil: NameAndDescription;

     InnerView innerView;

     this() {
         super();
         innerView = new InnerView;
         addWithViewport(innerView);
     }

     void clear() {
         innerView.viewData.clear;
     }

     void populate(NameAndDescription[] namesAndDescriptions) {
         innerView.viewData.populate(namesAndDescriptions);
     }
}
Apr 27 2020
next sibling parent reply mark <mark qtrac.eu> writes:
With the new code if I have 1000s of rows I get this error:

(DebFind:8087): Gdk-ERROR **: 11:50:46.787: The program 'DebFind' 
received an X Window System error.
This probably reflects a bug in the program.
The error was 'BadAlloc (insufficient resources for operation)'.
   (Details: serial 8810 error_code 11 request_code 130 (MIT-SHM) 
minor_code 5)
   (Note to programmers: normally, X errors are reported 
asynchronously;
    that is, you will receive the error a while after causing it.
    To debug your program, run it with the GDK_SYNCHRONIZE 
environment
    variable to change this behavior. You can then get a meaningful
    backtrace from your debugger if you break on the gdk_x_error() 
function.)
Program exited with code -5

This means the program is unusable (again) because there are 
often queries that result in 1000s of rows of results.
Apr 27 2020
parent reply mark <mark qtrac.eu> writes:
: dub build
Performing "debug" build using 
/home/mark/opt/ldc2-1.21.0-linux-x86_64/bin/ldc2 for x86_64.
aaset 0.2.5: target for configuration "library" is up to date.
gtk-d:gtkd 3.9.0: target for configuration "library" is up to 
date.
debfind ~master: target for configuration "application" is up to 
date.
To force a rebuild of up-to-date targets, run again with --force.
: gdb DebFind
GNU gdb (Ubuntu 8.1-0ubuntu3.2) 8.1.0.20180409-git
[snip]
Reading symbols from DebFind...done.
(gdb) run
Starting program: /home/mark/app/d/debfind/DebFind
[Thread debugging using libthread_db enabled]
Using host libthread_db library 
"/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7fffeddfc700 (LWP 8186)]
[New Thread 0x7fffed5fb700 (LWP 8187)]
[New Thread 0x7fffe7fff700 (LWP 8188)]
[New Thread 0x7ffff7fe2700 (LWP 8189)]
[New Thread 0x7ffff7fdc700 (LWP 8190)]
[New Thread 0x7ffff7fd6700 (LWP 8191)]
[New Thread 0x7ffff7e64700 (LWP 8192)]
[New Thread 0x7ffff7e5e700 (LWP 8193)]
[New Thread 0x7ffff7e58700 (LWP 8194)]
[New Thread 0x7ffff7e52700 (LWP 8195)]

Thread 1 "DebFind" received signal SIGUSR1, User defined signal 1.
0x00007ffff50ba2ea in ?? () from 
/usr/lib/x86_64-linux-gnu/libfontconfig.so.1
(gdb) bt

/usr/lib/x86_64-linux-gnu/libfontconfig.so.1

/usr/lib/x86_64-linux-gnu/libfontconfig.so.1

/lib/x86_64-linux-gnu/libexpat.so.1

/lib/x86_64-linux-gnu/libexpat.so.1

/lib/x86_64-linux-gnu/libexpat.so.1

/lib/x86_64-linux-gnu/libexpat.so.1

/lib/x86_64-linux-gnu/libexpat.so.1

/usr/lib/x86_64-linux-gnu/libfontconfig.so.1

/usr/lib/x86_64-linux-gnu/libfontconfig.so.1

/usr/lib/x86_64-linux-gnu/libfontconfig.so.1

/usr/lib/x86_64-linux-gnu/libfontconfig.so.1

/lib/x86_64-linux-gnu/libexpat.so.1

/lib/x86_64-linux-gnu/libexpat.so.1

/lib/x86_64-linux-gnu/libexpat.so.1

/lib/x86_64-linux-gnu/libexpat.so.1

/lib/x86_64-linux-gnu/libexpat.so.1

/usr/lib/x86_64-linux-gnu/libfontconfig.so.1

/usr/lib/x86_64-linux-gnu/libfontconfig.so.1

/usr/lib/x86_64-linux-gnu/libfontconfig.so.1

/usr/lib/x86_64-linux-gnu/libfontconfig.so.1

/usr/lib/x86_64-linux-gnu/libfontconfig.so.1

/usr/lib/x86_64-linux-gnu/libfontconfig.so.1

/usr/lib/x86_64-linux-gnu/libpangocairo-1.0.so.0

/usr/lib/x86_64-linux-gnu/libpangoft2-1.0.so.0

/usr/lib/x86_64-linux-gnu/libpango-1.0.so.0
warning: (Internal error: pc 0x55555584c444 in read in psymtab, 
but not in symtab.)

warning: (Internal error: pc 0x55555584c430 in read in psymtab, 
but not in symtab.)

warning: (Internal error: pc 0x55555584c444 in read in psymtab, 
but not in symtab.)


/usr/lib/x86_64-linux-gnu/libpango-1.0.so.0
warning: (Internal error: pc 0x55555584c444 in read in psymtab, 
but not in symtab.)


/usr/lib/x86_64-linux-gnu/libpango-1.0.so.0

() at /usr/lib/x86_64-linux-gnu/libpango-1.0.so.0

/usr/lib/x86_64-linux-gnu/libgtk-3.so.0

/usr/lib/x86_64-linux-gnu/libgtk-3.so.0

/usr/lib/x86_64-linux-gnu/libgtk-3.so.0

/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0

/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0

/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0

/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0

(this=0x7ffff7f98800) at Entry.d:215

qtrac.debfind.appwindow.AppWindow.makeWidgets() 
(this=0x7ffff7ed7a00) at appwindow.d:85

_D5qtrac7debfind9appwindow9AppWindow6__ctorMFC3gtk11ApplicationQnZCQCnQCkQCfQBy
(this=0x7ffff7ed7a00, application=0x7ffff7ed0360) at appwindow.d:62

_D5qtrac7debfind3app4mainFAAyaZ__T12__dgliteral2TC3gio11Appl
cationQnZQBkMFQBaZv (GioApplication=0x7ffff7ed0360) at app.d:16

_D7gobject8DClosureQj__T17d_closure_marshalTDFC3gio11ApplicationQnZvZQBtUPSQCv1c5types8GClosurePSQDr
wQw6GValuekQrPvQcZv (closure=0x555555cd52c0, return_value=0x0,
n_param_values=1, param_values=0x7ffff7ef39a0, invocation_hint=0x7fffffffd6e0
"\b", marshal_data=0x0) at DClosure.d:122

/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0

/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0

/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0

/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0

/usr/lib/x86_64-linux-gnu/libgio-2.0.so.0

/usr/lib/x86_64-linux-gnu/libgio-2.0.so.0

(this=0x7ffff7ed0360, argv=...) at Application.d:931

Apr 27 2020
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Mon, Apr 27, 2020 at 10:56:09AM +0000, mark via Digitalmars-d-learn wrote:
 Thread 1 "DebFind" received signal SIGUSR1, User defined signal 1.
The GC sends that signal to pause other threads when it is about to collect. You can tell gdb to just ignore it. handle SIGUSR1 noprint handle SIGUSR2 noprint I added those to my .gdbinit personally.
Apr 27 2020
parent mark <mark qtrac.eu> writes:
On Monday, 27 April 2020 at 12:26:23 UTC, Adam D. Ruppe wrote:
 On Mon, Apr 27, 2020 at 10:56:09AM +0000, mark via 
 Digitalmars-d-learn wrote:
 Thread 1 "DebFind" received signal SIGUSR1, User defined 
 signal 1.
The GC sends that signal to pause other threads when it is about to collect. You can tell gdb to just ignore it. handle SIGUSR1 noprint handle SIGUSR2 noprint I added those to my .gdbinit personally.
Thanks Adam, I took your advice and now have a bt. I have put it in a new message thread: "GtkD crash: 'BadAlloc (insufficient resources for operation)'"
Apr 27 2020
prev sibling parent reply Kagamin <spam here.lot> writes:
On Monday, 27 April 2020 at 10:28:04 UTC, mark wrote:
 I renamed the class shown in my previous post from View to 
 InnerView, then created a new View class:

 class View : ScrolledWindow {
     import qtrac.debfind.modelutil: NameAndDescription;

     InnerView innerView;

     this() {
         super();
         innerView = new InnerView;
         addWithViewport(innerView);
     }

     void clear() {
         innerView.viewData.clear;
     }

     void populate(NameAndDescription[] namesAndDescriptions) {
         innerView.viewData.populate(namesAndDescriptions);
     }
 }
Try this: void populate(NameAndDescription[] namesAndDescriptions) { if(namesAndDescriptions.length>100)namesAndDescriptions=namesAndDescriptions[0..100]; innerView.viewData.populate(namesAndDescriptions); }
Apr 28 2020
parent reply mark <mark qtrac.eu> writes:
On Tuesday, 28 April 2020 at 18:46:18 UTC, Kagamin wrote:
 Try this:

     void populate(NameAndDescription[] namesAndDescriptions) {
         
 if(namesAndDescriptions.length>100)namesAndDescriptions=namesAndDescriptions[0..100];
         innerView.viewData.populate(namesAndDescriptions);
     }
I tried that and it worked fine. So I then used a binary chop and discovered that 0..n where n <= 1170 works fine; n > 1170 crashes. I'm not sure where that takes me but seems suggestive that the problem is Gtk or GtkD rather than my code?
Apr 28 2020
parent mark <mark qtrac.eu> writes:
Continuing this in the GtkD mailing list: 
https://forum.gtkd.org/groups/GtkD/thread/1370/
Apr 29 2020
prev sibling parent drug <drug2004 bk.ru> writes:
26.04.2020 11:06, mark пишет:
 snipped
 
Sorry for offtopic, imho both Qt and Gtk are too complex in case of virtual list/tree view. I think the reason is they are retained mode gui. I gave up to use them for that and develop custom virtual tree view that is easy in use and currently capable to handle over 2M heterogeneous items. But it is in alpha state and for general use needs much more attention.
Apr 26 2020