How to use the same QAction in two QMenus with different text? - c++

So I have an action I want to expose in multiple menus (one of the main menus, and some context menus). I'd like the menu item text to differ slightly from menu to menu (to make the mnemonic unique, and to be more or less verbose as necessary in each context).
In MFC (which I have the pleasure of migrating away from at the moment) this was easy, as each menu's items were defined separately, and could map to the same ID, which would be linked to the handler. In Qt though, the QAction encapsulates the behaviour as well as the text/icon/etc. So I don't suppose there's straightforward support for it to return a different text dependent on where it's being used.
My thought about how to handle this is, for each location, to create a "proxy" QAction, which has the text specific to that context and simply has its triggered() signal connected to the original QAction's one. But I thought I should check first about whether there's an easier way to approach this.

I don't know about MFC, but in Qt - QAction is just an interface. One QAction object can have only one text to display. But the real action QAction does, you will implement in what Qt calls SLOT. Then you can have as much interfaces(or QActions objects) pointing to the same slot - just connect all QAction objects, that you want to do the same thing, to the same slot.
Hope this helps.

Your suggested solution is the simplest, I think. You can change the text of an action dynamically, when a menu is activated, but this looks more complicated to me.

Related

Which class should take care of shortcuts?

I'm coding a simple text editor with only few basic features.
At the moment it has a QMainWindow with QTabWidget set as it's centralWidget.
What I'm implementing now are some shortcuts; ctrl+s to save, ctrl+o to open, and ctrl+t/ctrl+w to create a new tab/close the current tab.
I know how to implement all the features, but the question is where to implement the features?
Should all the shortcuts be members of QMainWindow and let it take care of everything, or should I try separating the shortcuts into their correspoding classes?
For example, creating a new tab (ctrl+t) would be part of QTabWidget.
Now what about ctrl+s (save, duh), should it be part of QTextEdit since it's the text I'm saving, or..?
Basically my program contains three classes; QMainWindow, which contains QTabWidget, and each tab is a QTextEdit.
Your setup sounds a lot like many of the applications I've built.
I generally handle shortcuts via the QAction::setShortcut() method, so it's really more about where does it make sense to store the QAction objects. This usually ends up being my MainWindow class as a lot of the actions are all used in the MainWindw's menus. When these actions are triggered, the result is usually that the TabWidget is notified which in turn notifies all the necessary tabs and can handle things like a "Close All" action triggering only one save prompt.
However, if it doesn't make sense to store those QActions on the MainWindow object, then I don't, as in the case of the context menu I usually have available on my individual tabs.
Hope that helps.

QMenu modeless/async

QMenu can be created by using popup() or exec(). The former creates it asynchrously while the latter blocks. But this isn't useful when you're using a QMenuBar (AFAIK).
My question is, is it possible to tell QMenuBar to only popup asynchrous/modeless QMenus? I'm not sure the terms are correct, but all I want is a Menu that won't block the rest of the application when users click on it.
The workaround you're looking for is to move your objects that can't live with such "abuse" to a separate QThread. If you have a clean interface using signals and slots, this is trivial. Just use moveToThread() and you're done. You don't need to worry about anything else.

Qt/C++ - confused about caller/callee, object ownership

I am creating a GUI to manipulate a robot arm. The location of the arm can be described by 6 floats (describing the positions of the various arm joints.
The interface consists of a QGraphicsView with a diagram of the arm (which can be clicked to change the arm position - adjusting the 6 floats). The interface also has 6 lineEdit boxes, to also adjust those values separately.
When the graphics view is clicked, and when the line edit boxes are changed, I'd like the line edit boxes / graphics view to stay in synchronisation.
This brings me to confusion about how to store the 6 floats, and trigger events when they're updated. My current idea is this:
The robot arm's location should be represented by a class, RobotArmLocation. Objects of this class then have methods such as obj.ShoulderRotation() and obj.SetShoulderRotation().
The MainWindow has a single instance of RobotArmLocation.
Next is the bit I'm more confused about, how to join everything up. I am thinking:
The MainWindow has a ArmLocationChanged slot. This is signalled whenever the location object is changed.
The diagram class will have a SetRobotArmLocation(RobotArmLocation &loc). When the diagram is changed, it's free to change the location object, and fire a signal to the ArmLocationChanged slot.
Likewise, changing any of the text boxes will fire a signal to that ArmLocationChanged slot. The slot then has code to synchronise all the elements.
This kind of seems like a mess to me, does anyone have any other suggestions? I've also thought of the following, does it have any merrit?
The RobotArmLocation class has a ValueChanged slot, the diagram and textboxes can use that directly, and bypass the MainWindow directly (seems cleaner?)
thanks for any wisdom!
Except for really simple cases (e.g. a label that shows the value of a slider) my experience has been to stay away from inter-component signal/slot connections inside Qt Designer. Rather, have the component firing the signal of interest connect to a slog defined in the top level QWidget class you're subclassing (i.e. QMainWidow, etc... let's just call it the Form class). You can also go the other way: if you have a custom signal in the Form class, you can connect it with Qt Designer to one of the public widget slots.
To be specific using your example:
I'm going to assume that you have subclassed QMainWindow and QGraphicsView. Let's call the subclasses RobotMainWindow and RobotView.
RobotMainWindow contains the QLineEdit fields and RobotView. The way you specify RobotView in Qt Designer is to insert a QWidget and use the Promote to... feature to tell Qt that the QWidget should be replaced at compile time with your custom RobotView.
Name the QLineEdit fields edit1, edit2...edit6.
In the Qt Designer signal/slot editor define slots in RobotMainWindow to be called when the value in a QLineEdit changes. There are some more elegant ways of doing this, but for simplicity let's say you define 6 slots named setValue1(float), setValue2(float), etc.
In the source code for RobotMainWindow, go declare and define these slots, such that they update your arm, shoulder, whatever.
Also in the source code, define a signal valueChanged() (or multiple for each field, your choice). Have the slots you defined emit valueChanged().
Back in Qt Designer you can now link the appropriate signal from each QLineEdit to the corresponding slot in RobotMainWindow.
Back in the signal/slot editor add the valueChanged() signal to the RobotMainWindow (so Qt Designer knows about it). Connect this signal to a new slot in RobotView, using the procedure above, so it can update the rendering.
Repeat all of this for handling changes from RobotView to the editing fields (via RobotMainWindow.
In short, I think you'll find everything more straight-forward if your route the signals through your Form subclass (which I think of as the Controller in MVC parlance).

Callback for button in Qt Designer?

I just started using QtCreator tonight, and it seems it puts all of the interface stuff inside of the ui file. I followed a tutorial to create a resource for my icons, then I added them to a menu bar at the top.
I need to make a connection when one of them is clicked though, and cannot figure out how to make a callback for it.
Am I going to have to completely create them through code or is there some way to add a callback for them (rather than just making them interact with other objects).
Menu bar items are action objects. To do something when they are clicked, you need to catch the triggered() signal from the action. Read more about signals and slots here.
To do this, you need to declare a new slot in your MainWindow class. Qt also supports doing this automatically, without the need to connect anything, but I prefer doing it myself. If you're not interested, just skip this part.
First, we declare a new slot in your window class:
private slots:
void clickMenuButton();
Then, in your constructor, you need to connect the triggered signal to your new slot:
connect(ui.actionObject, SIGNAL(triggered()), this, SLOT(clickMenuButton()));
The first argument is the object that holds the signal we'll listen to (your menu button). The second is the name of the signal. The third is the object that holds the receiving slot (in this case, our window). The fourth is the slot.
And just like that, clickMenuButton() will be called whenever the action is clicked.
As I said before, Qt can also automatically connect signals to slots. The disadvantage here seems to be that you can't change the slot's name, but you don't need to connect it either.
Qt Creator supports creation of slots for widgets: in the case of your menu action, you should go to the form designer, and you should see a list of actions in your form (if you don't, find the Action Editor). Right click the action you want, and push Go to slot.... There, double click triggered().
Qt Creator will then open the new slot in your code editor, and you can do whatever you want to here!
To do that you'll need to add a QAction, add it to the menu, associate an icon with it and then create a callback for it. I'm using the VS Integration so I don't know the details of how to do it in Creator but it should be possible without creating stuff in code.
There should be somewhere an actions editor. from there you add an action, then right-click it or something to add an icon to it, then drag it do the menu and then possibly double click it to create a slot for it. This is how it works in the VS Integration atleast.

How do I create a custom slot in qt4 designer?

Whenever I use the signal/slot editor dialog box, I have to choose from the existing list of slots. So the question is how do I create a custom named slot?
right click on the main window and select "change signals and slots" and add a new slot.
It will appear in your signal slot editor.
This does seem to be possible in the version of Qt Designer 4.5.2, but it can't be done from the Signal/Slot Editor dock-widget in the main window.
This is what worked for me
Switch to Edit Signals/Slots mode (F4)
Drag and drop from the widget which is to emit the signal, to the widget which is to receive the signal.
A Configure Connection dialog appears, showing the signals for the emitting widget, and the slots for the receiving widget. Click Edit... below the slots column on the right.
A Signals/Slots of ReceivingWidget dialog appears. In here its is possible to click the plus icon beneath slots to add a new slot of any name.
You can then go back and connect to your new slot in the Configure Connection dialog, or indeed in the Signal/Slot Editor dockwidget back in the main window.
Caveat: I'm using PyQt, and I've only tried to use slots added in this way from Python, not from C++, so your mileage may vary...
Unfortunately this is not possible in Qt4.
In Qt3 you could create custom slots which where then implemented in the ui.h file. However, Qt4 does not use this file so custom slots are not supported.
There is some discussion of this issue over on QtForum
I am able to do it by:
In MainWindow.h, add the line:
public slots:
void example();
in the MainWindow class.
In MainWindow.cpp
void MainWindow::example() {
<code>
}
This doesn't seem to be possible in a simple way.
The designer only allows you to promote existing widgets to your own custom widgets. yet it doesn't allow you to connect the signals and slots of the class of promoted widgets.
The way this is possible is creating a plugin for the designer as is described here and in the pages that follow it.
The normal course of action is to promote a widget to your own class and then to connect it manually in your own code. this process is described here
It is not possible to do it, because it means you would add a slot to an existing Qt class like QPushButton which is not really the way to go.
You should create your own QWidget eventually by subclassing an existing one. Then integrating it into Qt Designer as a plugin as suggested. Having your own class allows you to add/modifiy the signals/slots available as you want.
Don't forget about the slot auto-connection features. There are a few drawbacks, like having to rename your function if you rename your widget, but we use those a lot at my company.
You can use the magic slot format of
void on_objectName_signal() {
// slot code here, where objectname is the Qt Designer object name
// and the signal is the emission
}
The connection to this method is established by the method connectSlotsByName and whenever the signal is emitted, this slot is invoked.
Maybe it'll help.
By default you have to choose from the existing list of slots. But you can add slot by right-clicking at you object in the list at right side of designer and choose "slot/signals" and add your custom slot/signal. After that, you can choose it in signal/slot editor.
click the widget by right button
promote the widget into a class you defined
click the widget by right button again
you will see that signal and slot is editable