How do you access the menus given a QMenuBar? - c++

In Qt how do you recover the menus given a populated QMenuBar?
They do not seem to be the menu bar's children. For example, after the following (where the menu creation functions succeed and do what you expect)
menuBar()->addMenu(create_file_menu(this));
menuBar()->addMenu(create_view_menu(this));
auto children = menuBar()->children();
auto first_child = children[0];
children ends up with a size of 1 and that one child, first_child, is some object of type QMenuBarExtension. I was expecting to get two children with the first being the file menu.
I'm using Qt6 if that matters.

A QMenu is added to a QMenuBar internaly via QWidget::addAction(menu->menuAction()) (see <QtInstallPath/src\widgets\widgets\qmenubar.cpp>.
From QWidget you can retrieve the added QActions via QWidget::actions() - method which returns a list of associated QActions. In your specific example menuBar()->actions() should retrieve at least two actions for your menus.
However, there seems to be no way to get from a QMenu::menuAction() - created QAction back to the associated menu. Therefore you may need to store a pointer to your created QMenu - objects on your own.

Related

How do I poll a set of RadioButton widgets in tkd (Tinker for the D language)

I've made a block of RadioButton widgets for my tkd interface, which work exactly as expected. However, the documentation for tkd's RadioButton doesn't show how one determines which of the buttons is pressed. Instead, the example code simply shows how to create an array of buttons that are linked together (i.e. so that only one can be selected at a time)
// The radio button group parent.
auto frame = new Frame()
.pack();
auto option1 = new RadioButton(frame, "Foo")
.setSelectedValue("foo")
.select()
.pack();
auto option2 = new RadioButton(frame, "Bar")
.setSelectedValue("bar")
.pack();
The CheckButton widget is a similar, and can be polled withe its associated isChecked() method, but nothing similar seems to be present for the RadioButton.
How do I check whether a given RadioButton has been selected?
Additionally, is there an easy way to check which member of an array of RadioButtons is selected, without iterating over the entire set myself?
After reading more documentation, it appears tkd implements this feature with a series of mixins.
The RadioButton class implements the Value interface described here.
Calling any of the associated methods will allow access to the state variable that is shared by the entire button array. E.g.
option1.getValue(); // Returns "Foo" if option 1 is selected, "Bar" if option 2 is.
option2.getValue(); // Same as above, since the value is shared.
option2.setValue("bar"); //Change value, without changing the selected button.

Firemonkey: Add child control to TListViewItem at run time

I am attempting to add a TEdit control to a TListView control during run time. I want to parent the TEdit control to the selected TListViewItem belonging to my TListView, however, I cannot find a way to do this.
Originally, I tried this:
TEdit * MyEdit = new TEdit( this );
MyEdit->Parent = MyListView->Selected;
However, this gives me the following error:
[bcc32 Error] E2034 Cannot convert 'TListViewItem *' to 'TFmxObject *'
On a whim, I attempted to typecast the selected item on my list view as a TFmxObject like so:
MyEdit->Parent = (TFmxObject *)MyListView->Selected;
While this compiled, this caused an access violation at run time.
I have searched through a lot of documentation and forum posts and cannot find very much information about dynamically adding a control to a list view item in code. I have seen solutions which propose using the style editor, but I want to avoid that if at all possible.
How can I set the parent of a control to an item in my TListView? Is there a better / more proper way to add controls to a TListViewItem during runtime?
According to Embarcadero documentation, TListViewItem is not a TFmxObject descendant and thus it can not be set as a Parent to the desired TEdit instance. It does not have Children property as well. Nor do the TextObject, DetailObject etc. (the TListItemObject descendants contained in TListViewItem) ascend from TFmxObject.
It seems you have the following ways out.
Write and register another ListViewItem class and implement it inside your ListViews or
See this and this SO links. Probably, they might be useful.
Consider using TListBox instead. TListBoxItems can parent other controls.

Deleting a QGraphicsItem/QGraphicsObject from QGraphicsScene?

I have created Qt GUI application. It consists of QGraphicsScene, and items (QGraphicsItems) are added to them by pressing or triggering pushbuttons. Each item added to the scene are members of different classes derived from QGraphicsItem. Now, my challenge is to delete an added item off the scene through one of the following mechanisms:
1) Right click an added item, create a context menu, and then use
scene->removeItem(addedItem);
2) Double click the item which deletes the item
3) Select an item using the flag ItemIsSelectable, and then delete the item by pressing the delete key on the keyboard
But having said that, as a newbie to Qt, I'm unable to do number 1 since the context menu doesn't show up when right clicked. In the case of number 2, I used signals and slots, a single emitted whenever an item is double clicked, and a slot in the mainWindow absorbs the signal and removes the item. But this way, the programs fails to compile because of the error "duplicate symbol found" when I add a Q_OBJECT macro to the header file of the item's class.
So my final option is to select an item on the screen and propane the keyboard signal to delete the item by pressing delete. How can be this done? Please give me advice if any of the above methods can be easily done in case I might be doing it completely wrong.
P.S. : I know there a lot of queries regarding deleting QGraphicsItem off QGraphicsScene, but none of them document a solid answer.
... I'm unable to do number 1 since the context menu doesn't show up when right clicked.
There are two possible methods to accomplish this:
Create a QWidget based menu, attached to the QGraphicsView.
Create your own menu item, derived from a QGraphicsItem.
Whilst the 2nd method will take more time, it's probably a better system in my opinion, as it will feel more integrated with the item you're deleting in the scene. The first method is also possible and if it's not working, then you could post an example question on SO.
2, I used signals and slots, ... because of the error "duplicate symbol found" when I add a Q_OBJECT macro to the header file
It sounds like you're trying to add the signal / slot functionality to a class derived from QGraphicsItem. You don't need to do this. Qt provides the QGraphicsObject class, which you can derive from, instead of QGraphicsItem, if you want signals and slots on items in a QGraphicsScene.
propane the keyboard signal to delete the item by pressing delete.
I assume you mean to 'propagate' keyboard signals. By overriding the QGraphicsScene and its keyPressEvent or keyReleaseEvent, you can get a list of selected items and delete them from the scene. Here's a skeleton example: -
class MyScene : public QGraphicsScene
{
protected:
void keyReleaseEvent(QKeyEvent * keyEvent);
};
void MyScene::keyReleaseEvent(QKeyEvent * keyEvent)
{
if(keyEvent->key() == Qt::Key_Backspace)
{
QList<QGraphicsItem*> selectedItems = selectedItems(); // get list of selected items
foreach(QGraphicsItem* item, selectedItems)
{
removeItem(item);
delete item;
}
}
}
You're seeking a lot of answers, Not so much how to handle QGraphicsItem or QGraphicsScene.
1) Right click an added item, create a context menu, and then use scene->removeItem(addedItem); here.
2) Double click the item, which deletes the item - you'll need to handle double clicks, and hit-testing the QGraphicsItems, you'll have to implement mouseDoubleClickEvent(QMouseEvent *e) and pass e's pos() to this to determine if a QGraphicsItem was clicked or not.
3) Select an item using the flag ItemIsSelectable and then delete the item by pressing the delete key on the keyboard - I'm not sure about the ItemIsSelectable flag. However, you'll need #2. And to learn how to handle keyboard input, by overriding this:
void QWidget::keyPressEvent( QKeyEvent *k ){
switch ( tolower(k->ascii()) ) {
case '\x08': \\backspace
break;
case '\x7F': \\delete
break;
}
}
There's also the Qt::key enumeration, which has Key_Backspace, and Key_Delete. It can be tested against the QKeyEvent::Key()'s return if you don't like dealing with ASCII character codes.

How do I properly set up generic QT actions for a menu constructed at run time?

I am populating a sytem tray icon menu (QMenu) from entries in an xml file which is read when my application starts up.
I am unsure of how to properly set up the SLOT end of the action:
QList<CMenuItem> menuItems = m_layout->getMenuItems();
QListIterator<CMenuItem> iter(menuItems);
while (iter.hasNext())
{
CMenuItem menuItem = iter.next();
QAction *action = new QAction(menuItem.qsTitle, this);
connect(action, SIGNAL(triggered()), this, SLOT(launchMenuItem()));
trayIconMenu->addAction(action);
}
How does my "launchMenuItem()" SLOT know which menu item was triggered? I can't make a SLOT for each menu item as I don't know how many items will exist until run time.
I can think of some ugly ways to do this, but I am looking for the RIGHT way.
What I usually do is to use QAction::setData(const QVariant&) to store whatever action ID I need. Then on slot side I retrieve ID with QAction::data() and behave accordingly.
Note that QVariant obviously accepts much more than basic int (which is what I use to identify actions), you can pass any QVariant-compatible info.
edit : oh! btw, this is somehow ugly because I make use of QObject::sender() to cast triggered action back. Sorry for that, but it works anyway.

Remove QLayoutItem from a Layout, but still use it later on?

Here is the environment first:
I have a self defined "Property Editor" which is a QGroupBox (derives from QWidget)
Currently I have a class let's call it "Holder", which has a reference to two of the Property Editors.
Now I have multiple "Holder" classes and one vertical QVBoxLayout (called Sidebar).
In this layout I want both of the Property Editors of the currently selected Holder class to be displayed.
And there is the issue:
When the user selects another holder class, I want the Property Editors of the previously selected Holder class to disappear, and add the Property Editors of the new selected Holder class.
Selecting another Holder class works once. But when I select the first Holder class again, The editors don't seem to change. Why? Does "takeAt(..)" destroy the reference in the holder class? How can I get the desired behavior?
Here is the code, thanks in advance:
void App::setSelection(Holder * holder){
if(m_currentlySelected == holder) return;
m_viewer->sideBarRemoveAt(0);
m_viewer->sideBarInsertAt(0, holder->firstPropEditor);
m_viewer->sideBarRemoveAt(1);
m_viewer->sideBarInsertAt(1, holder->secondPropEditor);
m_currentlySelected = holder;
}
void QtViewer::sideBarRemoveAt(int i){
m_sideBar->m_layout->takeAt(i);
}
void QtViewer::sideBarInsertAt(int i, QWidget * widget){
m_sideBar->m_layout->insertWidget(i, widget);
}
QLayout::takeAt() doesn't remove the widget of the QLayoutItem from its parent widget. The only reason it may seem to work the first time is probably because the other widgets were above (z-index wise) the first ones.
Rather than playing with the layout, you could
just hide/show your 2 PropertyEditor whenever the holder changes, hidden items don't generate holes in the layout, the next visible item is displayed as if the hidden items were not part of the layout, or
use a QStackedWidget to stack all the PropertyEditor at the same place and select which one is displayed (with QStackedWidget::setCurrentIndex()).
Does "takeAt(..)" destroy the reference in the holder class?
No, this method removes the QLayoutItem from the layout. See reference page for takeAt. This class doesn't release the layout item (it is your responsibility to do).
But when I select the first Holder class again, The editors don't seem
to change. Why?
I am not quite clear what you are trying to achieve (not enough code in your example), but if you are trying to change the layout using QLayoutItem's, then it is the simplest to create new layout and add items you want to display to it. Or simply, remove all items from the layout, and add the items that should be visible.