Locking selection in QTableView/QTreeView - c++

Is it possible to "lock" selection in QAbstractView?
I have a form with:
A list of objects I can edit (QTableView).Objects are stored in a database, the list shows their ID.
A New button, whose effect is to append an empty row I can edit to create a new ID and enable controls in the rest of the form.
An Edit button whose effect is to enable the edition of the rest of the form, but leaves the ID non editable.
Everything is driven by a state machine. There are parallel states but for the sake of this question, only transition between a Viewing state and an Editing state is relevant. Clicking either button enters the Editing state, which means testing if I am in the Editing state provides no information about whether I clicked on New or on Edit.
I am trying to lock the selection when entering the Editing state.
I cannot simply disable the view because I need to edit new IDs, I cannot connect a slot to the selectionChanged signal to restore the selection because of side effects (calls to the database + focus going all over the place), and if possible, I wish to avoid having to call QAbstractItemView::setSelectionModel (it is reset by QAbstractItemView::setModel, see below) and wish to drive this behavior thanks only to the Viewing and Editing states.
I have tried to use, but to no avail:
QAbstractItemModel::flags (the best I can do is prevent selecting another item but not clearing the selection.)
QAbstractItemView::selectionMode
QItemSelectionModel::select, with QItemSelectionModel::SelectionFlag::NoUpdate (The description of this enum value made me think it could block the next selection change but that is not the case).
Subclassing QItemSelectionModel (#chehrlic's comment below) to override all the virtual public slots.While it does work (a property can be used to stop the selection from changing), it is a pain that QAbstractItemView::setModel creates a new model. It does work now but I do not see any easy way to prevent the code from breaking after accumulating code changes over the span of several years.
Did I miss any existing property to achieve this? And if I did not, how can I implement a property to lock the selection but no have any other effect on my view?
At this point, the last item in the above list of things I tried does do the job but it has a downside I am trying to avoid.
This is similar to this question except it was for a now old version of Qt and the answer is not very satisfying anyway.

Related

Document has been modified or corrupted since signed! (data)

I'm working in HCL Notes application. I have developed a summary view to show calculated figures to the user. Then the user clicks one of the action buttons and I open a detailed view, but for that view I setup Selection Formula on the fly so that it shows the records filtered specific to that button conditions. It was working almost fine for a few days, but now most of the time it shows some previously shown (filtered) data no matter what button the user has clicked. Means it doesn't set the Selection Formula of the view and shows the view with the old formula and it won't get back to normal condition even if they restart Notes application.
When the user is stuck in this particular condition, and they peep through the status bar it shows this message:
Document has been modified or corrupted since signed! (data).
The necessary code-snippet is as below:
*Set dtlView = db.GetView("Report_Dtl")
dtlView.SelectionFormula =formula
Call dtlView.Refresh()*
where formula is the dynamically built formula. Looks like the line
dtlView.SelectionFormula =formula
is unable to update the selection formula and then the line below generates the above error message:
Call uidb.OpenView(dtlView.Name,,False, False)
Please help!
Thanks
For "on the fly" modification of the view selection formula your user need "Designer"- access to the database, and that is never a good idea. The help document to the function you are using is explicitly stating that this is not a good idea (emphasise of mine):
This is not a good way to display a selected set of documents for a specific user. When you use this property to change the view selection, it applies to all users of the view.
There are problems with using this method to make a view display a new selection of documents for an end user:
Do not give end-users Designer access to an application.
If it is a shared view, users will interfere with each other's searches.
The Notes® client caches design information, and there's no way to tell it to update its cache (except for outlines). Exiting and re-entering the application usually works, but it's hard to programmatically ensure the user exited the application entirely.
In addition the modification of the view selection formula can break the signature of the design element and then other errors occur.
Better use another approach:
Use a Folder for every user and put the selected documents in there (after doing a NotesDatabase.Search with the formula
Use a separate view for every user and let a server agent manipulate its selection formula with a user that has access.
For having a separate view / folder for every user you could use "Shared, Private on first use"- views (they are not easy to maintain), or any process that generates them and is able to assign every view to the users they belong to... in both cases this needs some effort, but at least it will work.

How to launch customized form from code in Epicor ERP 10?

I’m working on customization where I prepared customized Customer form and Case Entry form.
In the Case Entry I have button that should launch customized Customer form.
Is it any other way to do it than adding new menu item for customized Customer?
Assuming I have understood your question correctly, a very similar question was asked on EpiUsers: https://www.epiusers.help/t/launch-customized-form-code/62207/5
Here's a snippet from the post. The link gives a lot more info with screenshots:
You can always create a New Menu ID under Processes section of the tree in Menu Maintenance, so it won’t show up on Menu selection, but then you can call it via code.
Is there a way to call it without adding it, I am sure one could debug what Epicor calls when you enter Customization Mode and select a layer. I believe its possible, I just haven’t taken the time to debug it, because I always use the Menu Maintenance instead, because layer names change and relying on a special layer name is more fragile than adding a Menu Item under Processes section in Menu Maintenance – Plus then you can re-purpose the same Menu ID everywhere easily, without hardcoding layer names.
This way, you can keep the standard Customer form in place on the menu, but use the button to launch the customization. Hope this helps!

Qt 5.2 Model-View-Pattern: How to inform model object about changes in underlying data structure

I have a class used for permanent storage of some item that are organized in a table-like manner. This class is totally unrelated to Qt and comes from a different library. Lets call this class DataContainer for the rest of this question. It provides std-c++ compatible iterators to access and manipulate the content.
I need to display and modify that data through a Qt GUI. My idea was to create a class DataContainerQtAdaptor that inherits from QAbstractTableModel and stores a pointer to the DataContainer object. The DataContainerQtAdaptor serves as an adaptor to the DataContainer object and all manipulation from inside of my Qt app is done through this adaptor. Then I use a QTableView widget to display the information.
Unfortunately the DataContainer might be changed by threads/processes. (For example think of DataContainer as some C++ class that encapsulates a database connection and that database might be changed by someone else.)
Questions:
1) Assume I have a function that is called everytime the internal structur of the DataContainer object has been changed. What is the correct function of the QAbstractTableModel that must be called to inform the model of the underlying change? I need something like "Dear Model, your persistent storage backend changed. Please, update yourself and emit a signal to every attached view in order to reflect this change".
2) Lets say 1) is solved. What is the best way to avoid a "double" GUI update in case the change was triggered through the GUI? E.g: User clicks on a cell in the table widget -> table widget calls setData of the model -> model pushes change to backend -> backend triggers its own "onUpdate" function -> model re-reads complete backend (although it already knows the change) -> GUI is updated a second time
3) The user should be able to insert new rows/columns through the GUI and put data into it. But the position is detemined by this data, because the backend keeps the data sorted. Hence, I have the following problem: The user decides to create a new row at the end and the new data is pushed to the backend. When the backend/model is re-read this data is normally not at the last position, but has been inserted somewhere in the middle and all other data has been moved forward. Ho do I keep all the properties of the the table view widget like "selection of a cell" in sync?
I believe, there must be some simple standard solution to all these question, because it is the same way as QFileSystemModel works. The user selects a file and some other process creates a new file. The new file is displayed in the view and all subsequent rows move forward. The selection moves forward, too.
Matthias
Model Semantics
First of all, you must ensure that the QAbstractItemModel cannot be in an inconsistent state. This means that there are some signals that must be fired on the model before certain changes to the underlying data are done.
There is a fundamental difference between changes to structure and changes to data. Structure changes are the rows/columns of the model being added or removed. Data changes affect the value of existing data items only.
Structural changes require calling beginXxx and endXxx around the modification. You cannot modify any structure before calling beginXxx. When you're done changing the structure, call endXxx. Xxx is one of: InsertColumns, MoveColumns, RemoveColumns, InsertRows, MoveRows, RemoveRows, ResetModel.
If the changes affect many discontiguous rows/columns, it's cheaper to indicate a model reset - but be wary that selections on the views might not survive it.
Data changes that keep the structure intact merely require that dataChanged is sent after the underlying data was modified. This means that there is a window of time when a call to data might return a new value before dataChanged is received by the object that queries the model.
This also implies that non-constant models are almost useless from non-QObject classes, unless of course you implement bridge functionality using observer or similar patterns.
Breaking Update Loops
The Qt-idiomatic way of dealing with update loops on the model is by leveraging the item roles. It's entirely up to you how your model interprets the roles. A simple and useful behavior implemented by QStringListModel is simply to forward the role from the setData call to dataChanged, otherwise ignoring the role.
The stock view widgets react only to dataChanged with the DisplayRole. Yet, when they edit the data, they call setData with the EditRole. This breaks the loop. The approach is applicable both to view widgets and to Qt Quick view items.
Insertion of Data into Sorted Models
As long as the model properly emits the change signals when the sorting is done, you'll be fine.
The sequence of operations is:
The view adds a row and calls model's insertRow method. The model can either add this empty row to the underlying container or not. The key is that the empty row index must be kept for now.
The editing starts on an item in the row. The view state changes to Editing.
Editing is done on the item. The view exits the editing state, and sets the data on the model.
The model determines the final position of the item, based on its contents.
The model invokes beginMoveRows.
The model changes the container by inserting the item at the correct location.
The model invokes endMoveRows.
At this point, everything is as you expect it to be. The views can automatically follow the moved item if it was focused prior to being moved. The edited items are focused by default, so that works fine.
Required Container Functionality
Your DataContainer doesn't have enough functionality to make it work unless all access to it were to be done through the model. If you want to access the container directly, either make the container explicitly inherit QAbstractXxxxModel, or you'll have to add a notification system to the container. The former is an easier option.
Your core question reduces to: can I have model functionality without implementing some variant of the model notification API. The obvious answer is: no, sorry, you can't - by definition. Either the functionality is there, or it isn't. You can implement the notification API using an observer pattern if you don't want the container to be a QObject - then you'll need your model shim class. There's really no way around it.
The QFileSystemModel gets notified by the filesystem about individual directory entries that have changed. Your container has to do the same - and this amounts to providing a dataChanged signal, in some shape or form. If the model has items that get moved around or added/removed - its structure changes - it has to emit the xxxAboutToBeYyy and xxxYyy signals, by calling the relevant beginZzz and endZzz methods.
Indices
The most important underdocumented aspect of QModelIndex is: its instances are only valid for as long as the model's structure hasn't changed. If your model is passed an index that was generated prior to a structure change, you're free to behave in an undefined way (crash, launch a nuclear strike, whatever).
The whole reason for the existence of QModelIndex::internalPointer() is your use case of having an underlying, complex-indexed data container. Your implementation of the model's createIndex method must generate index instances that store references to the DataContainer's indices in some form. If those indices fit in a pointer, you don't need to allocate the data on the heap. If you need to allocate the container index storage on the heap, you must retain a pointer to this data and delete it any time the container's structure changes. You're free to do it, since nobody is supposed to use the index instance after a structure change.
From the documentation of method bool QAbstractItemModel::insertRows(int row, int count, const QModelIndex & parent = QModelIndex()):
If you implement your own model, you can reimplement this function if
you want to support insertions. Alternatively, you can provide your
own API for altering the data. In either case, you will need to call
beginInsertRows() and endInsertRows() to notify other components that
the model has changed.
The same goes for removeRows() and moveRows() (they have their own begin*() and end*() methods). For modifying data of existing item there's a dataChanged() signal.
Here's how it goes (answer for question 1):
Implement your own methods for inserting/deleting/modifying data, where each of those methods must look like this:
beginInsertRows(parentIndex, beginRow, endRow);
// code that modifies underlying data
endInsertRows();
beginRow and endRow must be provided to inform which where the rows will be inserted and how many of them (endRow-beginRow).
For beginDeleteRows() and beginMoveRows() it's the same.
When you have a method which simply modified data in existing item, then this method must emit signal at the end: dataChanged().
If you do a lot of changes in the data, it sometimes is simpler to just call beginResetModel() and endResetModel() in the method performing this huge modification. It will cause all views to refresh all data in it.
Answer for question 2:
This is up to the View class implementation if it will "double-update". When you enter data in the View, data is sent to the model through one of the edition methods in model (insertRows(), setData(), etc). Default implementation of those methods always use begin*() and end*() methods and so the proper notification signals are emitted by the model. All Views listen to those signals, including the one you used for entering the data, therefore the "double-update" will be performed.
The only way to define this behaviour is to inherit the View and reimplement its protected slots (like dataChanged() and similar) to avoid updating if the value was detected to be provided by this view.
I'm not sure if Qt views do that already or not. Answer to this requires someone more educated in Qt internals, or looking into Qt source code (which I don't have at the moment). If somebody knows this, please comment and I will update the answer.
I think it's not that bad to reload the data from model - it guarantees that what you see is indeed the value from the model. You avoid possible problems with the Editor and the View bugs.
Answer for question 3:
When you reload whole model, then there is no simple way to keep track of selection. In that case you need to ask view->selectionModel() about current selection and try to restore it after reload.
However if you do partial refreshing (using methods I described in answer 1), then the View will track the selection for you. Nothing to worry about.
Final comments:
If you want to edit data from outside of model class, you can do it. Just expose begin*() and end*() methods as public API, so other code that edits data can notify model and views about changes.
While it can be done, it's not a good practice. It may lead to bugs, because it's easy to forget about calling notification everywhere you modify the data. If you have to call model API to notify about changes, why not already move all editing code insise the model and expose editing API?

Cancel edit in QTableView when delegate loses focus

I am using a basic QTableView + QAbstractTableModel architecture.
Data can be edited via a QAbstractItemDelegate subclass. Everything works as expected but I need to slightly change the editing behaviour:
Qt default behaviour is:
For an open delegate editor, hitting Enter confirms the edit
For an open delegate editor, hitting Escape cancels the edit
For an open delegate editor, clicking in a different table view cell confirms the edit
I want to get rid of the last one. If the delegate editor loses focus, the edit shall be canceled (i. e. it shall not emit editingFinished()) so that a user can only submit changes by pressing Enter.
Is there a convenient way to do this?
Thanks in advance
Override the class, find out which function that last behaviour is that you dont want, then override that function and just leave the implementation of it empty, then it gets called just as it would usually but now it does nothing (or does something else, you're programming it make it do whatever you want)
I would override the QAbstractItemModel::setData() in your model and skip emitting dataChanged() on attempt to set the same data as it is already existing for the given item.

Issues with keyboard navigation on list with custom renderer

We have a list that uses a custom renderer containing a label, a checkbox and two icons (which have click events). This list needs to be made WCAG 2.0 compliant and in order to do that we need the list to be keyboard navigable.
The problem is with being able to move from one list item to the next and have the focus move to the label for the next/previous list item. Specifically, when the user enters the list using TAB button, the label for the first list item receives focus (highlighted box around text) and the entire row in the list is highlighted as the selected item.
However, when the user then presses the down arrow key to move to the next list item, the next row becomes highlighted (is now the selected item) but the focus remains on the label of the previous row (highlight still shown around label for row 1). The only way to get the focus to move to the newly selected row is to tab through the checkbox and two icons. This isn't a big deal if there are only a couple list items but would be a pain if there are 20+ rows in the list.
Is there a way to get the focus to move to the label of the newly selected row as soon as the user moves (using up/down cursor keys) to the new list item? I know a picture would help but I don't have anyway of posting a screenshot online. Any help would be greatly appreciated.
You're going to have to dig into how focus works in Flex. This is not a complete answer, but hopefully you can put together a solution that works for you. I did this about 4-5 years ago in Flex 3, but it should be similar in Flex 4.
How Focus Works in Flex
The main things to know are the FocusManager singleton class and the IFocusManagerComponent interface.
The FocusManager moves the focus around the UI based on user interactions (mouse clicks, keyboard navigation, etc.).
If a component implements the IFocusManagerComponent interface, then the FocusManager will include it in the "tab" loop and allow the component to be focused via keyboard navigation.
How Focus Works With Flex List Components
You've already stumbled onto the peculiarities of how focus works with the List component and item renderers. The Flex List components implement IFocusMangerComponent and so when you tab through the UI the FocusManager sends the focus to the list.
The List may or may not focus the item renderers. In Flex 3 you had to be using editable item renderers for this to happen, it may or may not be the same in Flex 4.
Some Ideas for Solutions to Your Problem
I think there are numerous ways to solve this. Use some combination of these techniques:
override the protected keyDownHandler() method of the List component. I don't have the code handy, but if you look at it's implementation in the List class you should be able to make your overridden version set the focus on the next renderer.
use methods of the FocusManager to find components in the tab loop: getNextFocusManagerComponent(), findFocusManagerComponent(). Check the docs there are others that will be useful. For example, when the user presses the down arrow, you can let the next item renderer get selected, then use findFocusManagerComponent() (passing in the newly selected renderer) and then tell the FocusManager to focus it with the setFocus() method. This is probably not exactly the right approach ;)
By the way, the FocusManger is a Flex singleton object, every UIComponent in Flex has a focusManager property you can use to get a reference to it.
consider disabling focus on objects that don't need to receive focus (like the Label in your item renderer). There are numerous properties to do this: focusEnabled, hasFocusableChildren, mouseFocusEnabled, tabEnabled, tabChildren etc.
consider disabling focus on the List component, but then making your item renderers implement the IFocusManagerComponent interface. Implementing the interface is simple, you just declare it in your class (there's no actual methods to implement). The tricky part will be now your item renderers need to have key down handlers (just override the protected keyDownHandler() method that all UIComponent objects have).
I think there are other techniques you can use, it's just been too long since I did this. I'd be happy to provide more help if you get stuck somehwere...