Input for key mapping - qt

To make my application more flexible to user I want to add possibility user to chose Hot keys for some actions in app.
To do this I'll gooing to make some page on settings window.
where Action name and input(QLineEdit) where keys will be desplayd like
Quit |Alt+Q|
What was the best way to capture such Hot Keys?

Well, to start with, you can setShortcut for your QAction, which will do exactly what you want (trigger QAction, when user hit sequence), example:
myAction->setShortcut(QKeySequence(Qt::Key_Backspace));
for more details, look up documentation of QKeySequence class
EDIT: to grab the sequence itself, look here - https://stackoverflow.com/a/6665017/1741118 . This answer should work. You can derive from widgets, where you would enter sequence, override QKeyPressEvent there or just set QEventFilter on every widget, that you want to capture events for key sequence

Related

How to get the clicked choice in the closing form dialog?

When I close the Form appear this DialogBox
I need to get the action clicked in this dialog (for example Yes or No etc...) in Form's method canClose
In debug the last point is in:
\Forms\MyForm\Methods\canClose
ret = super();
How I can get the clicked choice?
Thanks in advice.
You cannot get the answer from the prompt.
The return value of super is true, if the user can leave the form.
You do not describe what you want to achieve, but "No" is the answer to your question.
The prompt comes when a value in the record is change and the users press the Esc key. If the user selects Yes, the write method is called. So you may set a flag canClose and then test it in write. But I honestly not see the reason why this would be useful.
Check some lookup and dialog forms.
They uses closeOk, closedOk, closeCancel and closedCancel methods.
There are also closeSelect and closeSelectRecord methods to assign selected record (check also selectMode method on the form).

QT Creator: Trigger a Slot with Code?

I may have worked myself into a corner but this sounded to me like a good idea at the time.
I have been developing an interface that permits a user to modify settings of a robotic device, i.e. speed, directions, force, etc. with a very large series of options in the form of ComboBoxes. The problem is that there are about a thousand of these things, in sub categories. e.g. Speed category x1, x2, x3, Y1, y2, etc. So rather than create a thousand comboboxes in QT, I thought the good idea was to create one set of 50 (ish) and then provide a few button to switch between categories. So when the user selects speed QT, populates the comboboxes with the appropriate options, sets the style sheets and text for the labels etc. So it appears as though a dedicated page exists. Then if the user selects Direction, QT Writes the current index of each box to a dedicated array and then repopulates the boxes, labels etc with the appropriate content. I then do this over and over for the various needs of the system.
All of that seems to work fine. However I am now in a bind where the options provided to navigate to each page have grown. For instance I have forward / backward buttons (like you woudl expect in a set-up wizard), as well as action menus at the top to jump to a page. So now the code is becoming very repetitious. If you select the next button, I write the current values to array, then repopulate. If you jump to the page from anywhere, I look to see where I am, write it to array, and populate the boxes. Thus if I need to change anything I have to make the change in numerous places in the code.
I know that this is not optimal. What I woudl like to do is run a continuous loop as I woudl normally do with Micros in C. So the program can look at a variable in each pass and if it is then it does. I am not however skilled enough to figure this loop out in QT. So my new thought was...
Is it possible to trigger an action or slot with a variable. For example, if the user presses the Next button it triggers a slot for a button that does not exist, so that QT will execute a particular line of Code? Then I can have 1 dedicated section focused on reading and writing boxes, with a bunch of actions that will take me there.
You can make a signal that is triggered with an emit call in your code, so you'd hook up the next button signal of clicked to a slot that does some work and moves on, or directly calls another signal that you've created that triggers a slot elsewhere, or do some work in a lambda triggered by the button press.
I would first load all the ComboBoxes options in a QStringList array (or maybe an array of QList<QLatin1String> lists - for memory saving and code efficiency).
Then I would keep an array of a 1000 integers for current ComboBox indexes.
When the user changes a value in some ComboBox, the currentIndexChanged signal will trigger the corresponding slot (a single slot for all the ComboBoxes would be enough - sender()->objectName() to get the name of the ComboBox which had sent the signal):
void WindowWidget::on_ComboBox_currentIndexChanged(int index)
{
name = sender()->objectName();
/* here change the corresponding integer in the current
indexes array */
}
On Next/Back button push repopulate the ComboBoxes. Also, provide some 'Save' button for saving the ComboBoxes indexes (or trigger the Save slot on some action, i.e. on window close either even on a timer signal).

Call child form programmatically with parameter / filter

I'm creating a customization where on a click of a button, I need to allocate a charge for a particular purchase order / invoice journal.
From the front end, I would accomplish this by following the purchase order life-cycle and invoicing it. I would then go under the invoice tab of the PO, click Invoice Journals -> Charges -> Adjustment . This will open up my desired form where I will select a charges code, charges value, currency and category, and then I will click 'Ok' and have the system take care of the rest of the process.
Form name: MarkupAllocation_VendInvoiceTrans
Parent form Name: VendInvoiceJournal
You can see that the child form gets called with a few parameters such as the invoice number, there obviously needs to be that link. If I go into the AOT under forms, I right click and open up VendInvoiceJournal, but I wouldn't be able to open up MarkupAllocation_VendInvoiceTrans because it requires parameters.
Objective:
A: To open MarkupAllocation_VendInvoiceTrans through code where I manually pass those parameters to link to the parent table. I would provide the invoice number and such. The objective is to skip opening the parent table and manually going into the adjustments. I want to open that form directly and have it link to whichever record I specify.
B: I need to be able to pass a _ChargesValue parameter and have that be pre-populated for me. I don't know if this is possible, so I wanted to ask and confer. Ideally, I should be able to click a button on my custom form, and have MarkupAllocation_VendInvoiceTrans form directly open for a specified invoice, with pre-populated values on the line.
I know I should be tackling this problem one step at a time, so step A is priority number one.
I can open up the parent form with relative ease like so, but I cannot do the same for the child form. Obviously the same time of approach won't work, as I need to specify the relationship of the parent table before I open it.
private void allocateMarkup()
{
Object formRun;
Args args = new Args();
VendInvoiceJour jourTable;
;
select * from jourTable where jourTable.PurchId == 'PO000001191';
args.name(formstr(VendInvoiceJournal));
args.record(jourTable);
formRun = ClassFactory.formRunClass(args);
formRun.init();
formRun.run();
formRun.wait();
}
How would I be able to do so?
(Side note, I realize this whole form calling could be avoided if do all the transactions programmatically instead of letting the out of the box functionality handle it, but the markup and allocation logic is a beast of it's own and to me seems much more complicated than doing this. If someone has done it this manual way, any help on that would be greatly appreciated as well)
If I read your post right, you just want to open the Charges>Adjustment for a certain invoice. Here is one simple method:
MarkupAdjustment markupAdjustment = new MarkupAdjustment();
markupAdjustment.vendInvoiceJour(VendInvoiceJour::findFromPurchId('PO 120079'));
markupAdjustment.run();

Asynchronous validation in QWizard

I'm writing a wizard UI based on the QWizard Qt object. There's one particular situation where I want the user to log in to a service using host, username, and password. The rest of the wizard then manipulates this service to do various setup tasks. The login may take a while, especially in error cases where the DNS name takes a long time to resolve -- or perhaps it may not even resolve at all.
So my idea is to make all three fields mandatory using the registerField mechanism, and when the user hits Next, we show a little throbber on the wizard page saying "Connecting to server, please wait..." while we try to connect in the background. If the connection succeeds, we advance to the next page. If not, we highlight the offending field and ask the user to try again.
However, I'm at a loss for how to accomplish this. The options I've thought of:
1) Override validatePage and have it start a thread in the background. Enter a wait inside validatePage() that pumps the Qt event loop until the thread finishes. You'd think this was the ugliest solution, but...
2) Hide the real Next button and add a custom Next button that, when clicked, dispatches my long running function in a thread and waits for a 'validation complete' signal to be raised by something. When that happens, we manually call QWizard::next() (and we completely bypass the real validation logic from validatePage and friends.) This is even uglier, but moves the ugliness to a different level that may make development easier.
Surely there's a better way?
It's not as visually appealing, but you could add a connecting page, and move to that page. If the connection succeeds, call next() on the wizard, and if the connection fails, call previous() and highlight the appropriate fields. It has the advantage of being relatively straightforward to code.
My final choice was #2 (override the Next button, simulate its behavior, but capture its click events manually and do the things I want to with it.) Writing the glue to define the Next button's behavior was minimal, and I was able to subclass QWizardPage with a number of hooks that let me run my task ON the same page, instead of having to switch to an interstitial page and worry about whether to go forwards or backwards. Thanks Caleb for your answer though.
I know this has already been answered (a long time ago!) but in case anyone else is having the same challenge. Another method for this is to create a QLineEdit, initiate it as empty and set it as a mandatory registered field. This will mean that "Next" is not enabled until it is filled with some text.
Run your connection task as normal and when it completes use setText to update the QLineEdit to "True" or "Logged in" or anything other than empty. This will then mean the built in isComplete function will be passed as this previously missing mandatory field is now complete. If you never add it to the layout then it won't be seen and the user won't be able to interact with it.
As an example ...
self.validated_field = QLineEdit("")
self.registerField('validated*', self.validated_field)
and then when your login process completes successfully
self.validated_field.setText("True")
This should do it and is very lightweight. Be sure though that you consider the scenario where a user then goes back to that page and whether you need to reset the field to blank. If that's the case then just add in the initialisePage() function to set it back to blank
self.validated_field.setText("")
Thinking about it you could also add the line edit to the display and disable it so that a user cannot update it and then give it a meaningful completion message to act as a status update...
self.validated_field = QLineEdit("")
self.validated_field.setDisabled(True)
self.validated_field.setStyleSheet("border:0;background-color:none")
self.main_layout.addWidget(self.validated_field)
self.registerField('validated*', self.validated_field)
and then when you update it..
self.validated_field.setText("Logged in")

Implementing Undo/Redo within a TextArea

Im wondering how to implement undo redo functionality with a TextArea. I already have an undoredo framework functionality working, now I have two questions.
When do I start/stop a new undo/redo command, eg when a user hits undo, how far back do I go.
How do I implement this(1.) in a normal TextArea
My thinking:
I thinking that I should create a new undo command, when anything but a alphanumber+space is hit. To do this I would use the keyDown event and test if the key is alpha num if it is not I will reset the command.
Sound good?
Listening for keydown events would miss any text editing that user does with the mouse (cut/copy/paste).
I think a better approach would be to listen for 'change' event on the control (which fires whenever the content changes through user input), and just push the full content of the control (its 'text' or 'htmlText' attribute) with every change event into a undo-buffer (an Array of Strings). I assume that the memory usage is not an issue (it probably isn't, depending on the expected size of the controls content and number of undo levels).
This way, you implement undo/redo just by copying the corresponding control state (moving up and down through array, basically) in the undo buffer back into the control.
The 'proper' approach would be to track the actual edits, and would be condsiderably more complicated.
1.When do I start/stop a new undo/redo command, eg when a user hits undo, how far back do I go.
Do you think your users will need to undo multiple steps? If so, then you may want to have a history (e.g. Paint .NET) and allow infinite undo-s. Otherwise, just remember the most recently performed action.
1.) You should listen for the Event.CHANGE event on the TextField and create a history step each time the event is fired. A history step consists in your case of two values: old and new.
Old is the value of the TextField before change, new is its value after the change.
2.) Your history is a sequence of actions or you can use the Memento Pattern. I think actions are much easier to use. A history action has two methods, undo() and redo(). So in undo() you have to say textField.text = oldContent and in the redo() method you say textField.text = newContent. Your history will also need a pointer to the current action.
3.) To make it a little bit better. You should not listen only for Event.CHANGE but instead listen for the first CHANGE and then the next FOCUS_OUT for that TextField. In that case, a history step is only created once I stop editing the TextField. But it depends on your TextField and how you want to distribute history steps. A multiline TextField should not create a history step only on FOCUS_OUT :)

Resources