Displaying QAbstractListModel items in QTableView columns - qt

I've been toying around with Qt and ran into a small issue.
I want to display a list of pictures as a table of icons. Right now, I'm doing this by subclassing QAbstractTableModel, and plugging it into a subclass of QTableView.
This, in my opinion, overly complicates the code, the model and the view (especially when trying to edit/append items). When trying to implement the model as a QAbstractListModel, the items are displayed as table rows.
Is there a way to make QTableView display items as columns, instead?
Edit: Such that the items are rendered in a single row from left to right, and wrapped to a new row.
Or is it preferable to use the table model for table views in any case and work around the issues?
It's worth mentioning I'm using C# bindings for Qt based on Qt Jambi.

The QListView has exactly the functionality you are talking about.
If you don't need any functionality specific to QTableView, then I would suggest switching.
If you set "isWrapping", then the list will start from the top, go down to the bottom, then wrap to a new column.
Set "flow" to LeftToRight to display the list in rows instead of columns
You might also need to set "resizeMode", instead of Fixed, to Adjust. Which will automatically move things around when the list is resized.
Hope that helps.
Just as a side note, here is FlowLayout example. So if you just want to display a set of images in a self-adjusting grid, this would do it for you without all the overhead of a list/table. However, it doesn't give you any selection/editing capabilities or anything just layout.

I don't understand something here. You implement TableModel, so you can prepare data for view in the most desirable way. Suppose you have data A,B,C,D, then you can return A(00),B(01),C(10),D(11), or for example A(00),C(01),B(10),D(11). Is it what you want to do?

Related

What is the best way to implement this using Qt

I'm totally new to Qt, so I'd be glad to have a wide answer.
Here I drew up some model:
We have a kind of table that contains:
an integer value with a spinbox.
a cell with three(not specifically) grouped radio buttons
Editbox
A button that interacts with this particular editbox.
Also we have 2 buttons to add and remove items from the table.
I did some google search and found out that it can be done via QTableView.
Is there any way to put such complex structures into a cell? Must it be a separate class inherited from QTableView?
If you're going to have up to a hundred or maybe a few hundreds of elements in the table, then use QTableWidget.
If you're going to have too many elements (about thousands), then go for QTableView, and learn model-view programming.
The reason why I recommend QTableWidget is because you're a beginner. All you have to do there is create a widget, and use setCellWidget() and you're done.
If you have thousands of rows, then you're gonna have to draw the widgets yourself using QStyledItemDelegate, which will paint the widgets inside your QTableView. This is a very painful thing to do, but there's no way around it. The reasons you can find here.
I see at least three options to implement that in Qt:
Use a QtableView or QTableWidget and insert some custom controls in it. See comments made be other persons to your post
Use a QGridLayout and fill it with your controls by line and column
Make your own QWidget to store and manage the line elements (the spinbox, edit field, radio button) using a QHBoxLayout. You can design this in QtCreator, it can have it's own .ui. This could make it easy to handle the interaction between each QWidget of a line (directly handled by your QWidget class). Later, you can put an instance of it for every line you need in a QVBoxLayout.
Personnaly, I would go with the last option, but it may not work smartly if the controls of each line have different content/size (see comments), then first options should be prefered.

Complex content in QTableView

We would like to do a TableView that allows complex content in its cells.
The TableView should be as generic that I can do simple stuff like in picture 1. The left image is a simple example, where I fill a simple TableModel, set it for the TableView and display it.
But what if I want to add more complex content to one cell? Please again look at the first picture. The right part is more complex, for every cell we want to display an image, a description, and more description, so three items in one cell.
I understand, that I can put widgets to the cells of a TableView.
But, if I want to have a proper TableModel in the background, how would I store the data?
On top, the view should automatically resize when I make the widget of the TableView smaller the content should adapt
So if I use TableView and want to resize, I would have to shovel the content from one colum to another.
From what I understand, the columns also define the layout.
Would I be better of if I used a QGridLayout for this purpose?
Do I have to define a completely new model for QGridLayout?
Thanks for any help!
I am a newbie to QT and would appreciate your input a lot!
Qt's proposed solution to having a complex view in each cell of a table view is to use a custom delegate. Take a look at Star Delegate example, it demonstrates exactly this technique.
There are basically two options to proceed with a custom delegate: either you subclass QStyledItemDelegate (or its base class QItemDelegate if you need to draw the items of Qt's datatypes somewhat specially) or subclass QAbstractItemDelegate to have the full control over the delegate's appearance and behaviour.
However, your second requirement of automatic layout rearrangement on widget resizing suggests that your view doesn't really has to follow the underlying table's schema. Qt has a flow layout example which implements a layout with exactly this rearrange-on-resize property and I suppose the simplest approach would be just using this layout along with custom widgets representing the table model's cells. To make it happen you could implement a custom view class listening to the model's signals and creating/deleting widgets and updating the flow layout as necessary. This book, even though a little outdated nowadays as it covers Qt4, contains a chapter (#6) on implementing a custom view which is not a subclass of QAbstractItemView but is just a widget updating itself as its underlying model updates. To me it feels the right approach to your problem.

'Manual' multi-selection in QTableView and partially hidden grid

I know that topic about multi-select arise at least once, but honestly neither can't find it anymore, nor remember that it had and decent solution.
There are two questions I propose to discuss:
1) Creating a behaviour similar to MS Excel cell's selection. So user click one cell in QTableView and gets clicked cell highlighted and in additional several 'dependant' cells change appearance (get selected or just gets highlighted in any way). In Excel it's widely used to show cell formula dependencies.
I know there are several approaches to solve it. Most simple one is to modify view selection with dependant cell in any of appropriate signal handlers (for example QAbastractModelView clicked()). That way does the job but has ugly side effect, that due to fact that signals delivered after redraw of selected cell occurs so dependant selection is drawn after first cell which produce flickering.
Second approach is go Delegate way.. That's also have some issues because you get paintEvent only for selected cell, so there is not that much you can do about 'dependent' cells. Actually I was able to solve it through this way, by catching on-click, modifying selection and using completely custom delegate which draws everything as soon as complete selection is formed, so actually it skips first redraw, but again I wasn't completely satisfy with results although visually it looked completely right.. mostly because overall TableView response time decreased a lot. Reasons for that is Qt draws native selection right after mouse click received before sending any signals to user classes and in case of this approach paintEvent in delegate arrives after several main loops. So there is a noticeable delay in case of using 'draw selection in delegate' in compare to 'draw native selection'.
I am already starting to think that best option can be completely overwrite most of QTableView to add support for such selection schemas, but may be there are more straight approach?
2) Second question (I put them together because there are something common issues).
Let say you have a grid representing financial information by months and within a month there are several columns of information, so block of N columns repeated M times. Obvious way to make such grid more readable is to use different style for vertical lines in grid for first data column in each month. Let's say to make them 1-2 pixels wider.
As you cannot specify grid style per cell, what I did was to setGrid(false) and then draw my own grid lines as a cell content in delegate.
But then I faced a problems from point 1. Then you instruct Qt to use delegate on certain cell, before delegate will get a paintEvent Qt clears a background of the cell. And in case of hidden grid the background rect which Qt clears is one pixel bigger then required. Probably it can be consider to be a Qt bug because they dont respect grid visibility, but this results in removing grid lines in neighbour cell, so you have to draw in delegate not only cell own grid, but also recalculate proper cell rectangle, check if Qt made a mistake (by analysing QPainter rect), decide if whats being removed from neighbour cell needs restore and repaint it also. This leads to really complicated delegate logic and I cannot consider it to be a decent solution.
So question 2 can be rephrased as do we know a decent way to style a grid per cell in QTableView?
OMG,so many words,can you just pick the most important info?
I'd do something like that:
Create a delegate. Subclass QAbstractTableModel and reimplement data method. Your implementation should return cell text for Qt::DisplayRole, but also can return whatever you want if role is one of your user-defined roles (like font or color or whatever of cell's text. You can use any role number above Qt::UserRole). Your model should emit dataChanged signal to notify QTableView that the content is changed and should be redrawn.
Then in delegate you just request this data using your overloaded QAbstractTableModel::data and draw it the way you want.

qt combobox scrollbar behaviour

Is there any way to change the behaviour of the scrollbar inside qcombobox?
The behaviour is:
position of the scrollbar corresponds to the pointer position of the record in the dataset. While changing the position of the scrollbar the records are added and removed from the combobox.
So it becomes possible to browse the dataset (~1000000 records) but without adding all the records to the combobox (it's very slow after 1000 records).
The standard Qt way of doing this is with a QAbstractItemModel to populate the portion of the view that is visible.
That said, I'm not sure you have fully considered the implications of a combobox with 1 million items from a user interface point of view. That many items and your users will have difficulty finding what they are looking for. To make this usable, you would probably need a way of sorting or categorizing entries in a way I don't think QComboBox provides. There must be a better way. That question might be better suited for the guys at http://ux.stackexchange.com, though.

How to show only 30 rows and hide the remaining rows of QTableWidget

I'm having a QTableWidget with 10,000 records. I need to show only 30 rows at a time and hide the remaining rows. While dragining/clicking vertical scrollbar it should show corresponding rows and hide the other rows.
ie, if one clicks on upper scroll button it should show one more upper item and hide one lower item and vice versa. It should happen while scrolling as well.
Can anyone help me in doing this?
I think you can trap the events of up/down buttons as Nicholas said, for the tracking of slider/scroll bar u need use 'setTracking(true)'followed by the handling of signal either 'valueChanged(int)' or 'sliderMoved(int)'.
Thanks,
Pradeep
Qt unfortunately won't make this easy for you, there is ways and means of doing it however. Depending on how you're populating your widget I'd recommend setting it up so you initially populate your 30 rows, you then need to trap the signals coming from the scroll buttons being clicked and tell the table widget to remove the upper/lower item and add the next one, there's trade off's to this method, but it'll make it easier than trying to maintain a large list of hidden rows.
I don't know if explicitly showing/hiding things is the way you want to go. Instead, take a look at Qt's model and view classes. If you are using a database, have a look at QSqlTableModel, which should handle these things for you. Otherwise, have a look at the Model-View programming document, their related examples, and especially the portion about optimizing for performance (although in a model, 10,000 records isn't really all that many for most uses).
There is a way to change table scrolling from pixels to items. In Designer Property Editor for the table, in the QAbstractItemView section, there is a VerticalScrollMode selection. This can either be ScrollPerPixel or what you probably want, ScrollPerItem.
That may change the behavior of the signal from pixels to items which would make your calculations easier.
I'm using 4.4.3.

Resources