Show accelerator key in menu - c++

I am using Gtkmm 3.22.30 on Ubuntu and I have been trying to show accelerator keys in my application menu, without success. So far, I have been able to register an accelerator key to my menu items, but for some reason, they don't appear in my menu. I know this is possible, because I have seen it in Inkscape, which I believe is using Gtkmm as well (For example: New is bound to <Control>N:
I have prepared a minimal example to show my problem (and what I have done). Here is the code:
#include <gtkmm.h>
#include <iostream>
class MainWindow : public Gtk::ApplicationWindow
{
public:
MainWindow();
private:
void OnActivateSubItem()
{
std::cout << "Handler called" << std::endl;
}
Gtk::MenuBar m_menuBar;
Gtk::MenuItem m_mainMenu{"Menu"};
Gtk::Menu m_menu;
Gtk::MenuItem m_subItem{"Item"}; // Menu|Item
};
MainWindow::MainWindow()
{
// Setting the menu up:
add(m_menuBar);
m_menuBar.append(m_mainMenu);
m_mainMenu.set_submenu(m_menu);
m_menu.append(m_subItem);
// Adding accelerator:
auto accel_group = Gtk::AccelGroup::create();
add_accel_group(accel_group);
m_menu.set_accel_group(accel_group);
// I would have believed this to do the trick but, meh...
m_subItem.add_accelerator("activate",
accel_group,
GDK_KEY_y,
Gdk::ModifierType::CONTROL_MASK,
Gtk::ACCEL_VISIBLE);
m_subItem.signal_activate().connect([this]{OnActivateSubItem();});
}
int main()
{
auto app = Gtk::Application::create("my.menu.problem");
MainWindow window;
window.show_all();
return app->run(window);
}
which leads to the following:
I was expecting a Ctrl + y entry next to the Item menu item (especially with the Gtk::ACCEL_VISIBLE flag for which I have found no documentation), but nothing is showing except a blank space. When I hit Ctrl + y, "Handler called" appears in the console, so the accelerator works.
I have look through the API but it is very confusing and badly documented (I was not able to find a working example for Gtkmm 3.22.30, not even in the examples coming with the source code).
How can I achieve this?
Notes:
I am looking for an answer which does not involve Gtk::Builder if possible.
The answer can use C code if what I want to do is not possible in Gtkmm, but I would need it to integrate with my Gtkmm code.

add_accel_group(accel_group);
m_menu.set_accel_group(accel_group);
I think the same AccelGroup cannot be assigned to a window and a menu. I searched the docs for details on this and found nothing, but my tummy feeling says me you have to delete one of these lines. And in this example they use the AccelGroup only in the window, too. I suggest to delete the second line.
EDIT: i think you are using a different Gtkmm version than i am, because the code you provided works fine on my machine. Could you please compile following c++ program, run it and post its output ?
#include <gtkmm.h>
#include <iostream>
int main() {
cout << gtk_get_major_version() << "." << gtk_get_minor_version() << "." << gtk_get_micro_version();
}

Related

How to set 3-key sequence shortcut with two key modifiers in Qt?

I have been trying to set a shortcut as Ctrl + Shift +C.
I have tried the following ways:
QAction *generalControlAction = new QAction(this);
generalControlAction->setShortcut(QKeySequence("Ctrl+Shift+c"));
connect(generalControlAction, &QAction::triggered, this, &iBexWorkstation::onGeneralConfiguration);
QShortcut *generalControlShortcut = new QShortcut(QKeySequence("Ctrl+Shift+C"), this);
connect(generalControlShortcut, &QShortcut::activated, this, &iBexWorkstation::onGeneralConfiguration);
They didn't work. Nothing is triggered when I press Ctrl + Shift +C.
Is it impossible to set a shortcut with two modifiers in Qt?
I wrote a minimal, complete sample. It worked in my case how you described it. May be, I added something which you didn't on your side. (That's why "minimal, complete, verifiable samples" are preferred.)
// standard C++ header:
#include <iostream>
// Qt header:
#include <QAction>
#include <QApplication>
#include <QLabel>
#include <QMainWindow>
using namespace std;
int main(int argc, char **argv)
{
cout << QT_VERSION_STR << endl;
// main application
#undef qApp // undef macro qApp out of the way
QApplication qApp(argc, argv);
// the short cut
const char *shortCut = "Ctrl+Shift+Q";
// setup GUI
QMainWindow qWin;
QAction qCmdCtrlShiftQ(&qWin);
qCmdCtrlShiftQ.setShortcut(QKeySequence(shortCut));
qWin.addAction(&qCmdCtrlShiftQ); // DON'T FORGET THIS.
QLabel qLbl(
QString::fromLatin1("Please, press ")
+ QString::fromLatin1(shortCut));
qLbl.setAlignment(Qt::AlignCenter);
qWin.setCentralWidget(&qLbl);
qWin.show();
// add signal handlers
QObject::connect(&qCmdCtrlShiftQ, &QAction::triggered,
[&qLbl, shortCut](bool) {
qLbl.setText(
QString::fromLatin1(shortCut)
+ QString::fromLatin1(" pressed."));
});
// run application
return qApp.exec();
}
I suspect that you didn't call QWidget::addAction(). If I comment it out it does not work anymore in my program also.
Compiled with VS2013 and Qt 5.6 on Windows 10 (64 bit):
This snapshot has been made after pressing Ctrl+Shift+Q.
Note:
I realized afterwards that the actual question was about "Ctrl+Shift+C". To be sure, I checked it. The above sample code works with "Ctrl+Shift+C" as well.
generalControlAction->setShortcut(QKeySequence(Ctrl+Shift+c));
just change above mention statement in your code to
generalControlAction->setShortcut((Ctrl+Shift+C));
this should work fine. "C" should be capital later.
please refer key sequence from below given link
http://doc.qt.io/qt-5/qkeysequence.html

Cocos2d-x 3.7 C++ CCLoad UIPageView crashing

This happens on all platforms but this description is for iPhone 5s 8.4 simulator, through the Xcode.
I have been working on the game using C++ and cocos2d-x version 3.6 and Cocostudio 2.3.1. All was fine until i updated to the official release of cocos 3.7. App started getting crashes. One of those is particularly interesting regarding UIPageView. So my header and cpp files are like that:
MKEpochSelectionScene.h
#include "cocos2d.h"
#include "cocos-ext.h"
#include <cocos/ui/CocosGUI.h>
class MKEpochSelectionScene : public cocos2d::Scene {
public:
bool init();
CREATE_FUNC(MKEpochSelectionScene);
private:
cocos2d::ui::PageView * mainPageView;
void previousEpoch(Ref* pSender, cocos2d::ui::Widget::TouchEventType eEventType);
void nextEpoch(Ref* pSender, cocos2d::ui::Widget::TouchEventType eEventType);
void showEpoch(Ref* pSender, cocos2d::ui::Widget::TouchEventType eEventType);
};
MKEpochSelectionScene.cpp
#include "MKEpochSelectionScene.h"
#include <editor-support/cocostudio/CocoStudio.h>
#include "MKGameScene.h"
#include "MKLevelSelectionScene.h"
USING_NS_CC;
bool MKEpochSelectionScene::init()
{
if(!Scene::init()) return false;
auto node = CSLoader::createNode("UI/Epoch/Layer.csb");
this->addChild(node);
auto buttonLeft = node->getChildByName<ui::Button *>("Button_Left");
CCASSERT(buttonLeft != nullptr, "Button left is null");
buttonLeft->addTouchEventListener(CC_CALLBACK_2(MKEpochSelectionScene::nextEpoch,this));
auto buttonRight = node->getChildByName<ui::Button *>("Button_Right");
CCASSERT(buttonRight != nullptr, "Button right is null");
buttonRight->addTouchEventListener(CC_CALLBACK_2(MKEpochSelectionScene::previousEpoch,this));
//******CRASHING LINE
mainPageView = (cocos2d::ui::PageView *)node->getChildByName("selectEpoch");
//******CRASHING LINE
CCASSERT(mainPageView != nullptr, "Main pageview is nil");
CCASSERT(mainPageView->getPages().size() > 0, "Page view has zero pages");
return true;
}
//Other methods
//....
}
Sooo.... The thing happens with the mainPageView. Crash occurs on the line where i reference UIPageView from the design file and assign it to the mainPageView ivar. Crash does not say anything, but crashes in the NavMesh file at the following:
So it crashes when i try to pushscene as to switch to this scene from mainMenu. There is one more thing... If i use local variable like: auto mainPageView instead of ivar in the init() method, it does not crash. And if i comment the UIPageView referencing at all e.g. crashing line - it does not crash.
What i have tried: Updated Cocostudio to 2.3.1.1 which is the latest. Republished all of the UI using that new version. Cleaned all caches and stuff of the build. Updates to the new cocos engine version by creating new project with the new version and copying over files from the old version and importing into the project. This is just to say that i have tried several things.
Any help would be appreciated. And i am sure this might be a silly mistake of mine...
Cheers!
I think you are triggering this bug:
https://github.com/cocos2d/cocos2d-x/issues/12962
It is going to fixed in v3.7.1

wxWidgets: launching a bunch of code generating output without blocking the window

For my work, I developped an algorithm that computes stuff (which I'm not allowed to talk about just yet). It is a time consumming algorithm. Due to my sincere hatred of command line hard to configure algorithms, I am developping a GUI tool that should help me configure the algorithm and launch it. Here it is for the background.
Where I'm stuggling is here: I have a bunch of parameters to set, which is fine, and then I hit a generate button. What it does (or what I want it to do) is launch a custom popup wxDialog with a wxTextField containing the log info generated by the algorithm. The custom wxDialog only has construtor that sets the widgets in the right position.
Here is how I coded it so far:
void TnF_LoadingFrame::OnGenerate(wxCommandEvent &)
{
std::cout << "Generating!" << std::endl;
LogDialog dialog (m_pController,this,-1,_("Generation"));
{
wxStreamToTextRedirector redirect(dialog.m_textRedirection);
m_pController->Generate();
}
if(dialog.ShowModal() == wxID_OK)
std::cout << "Saving to do" << std::endl;
else
std::cout << "Cancelled" << std::endl;
}
As expected, the dialog shows itself only when the algorithm has finished computing. What I would like is for the window to show itself and for the algorithm to run in the background and its logs to be shown in realtime in the textfield. I was thinking in the line of threads, but I am not sure I understand how it works. I was also thinking in the line of an update function that would automatically be called every frame or so in order to let the dialog shows itself and then launch the algorithm in the update function and finally allows the user to exit the dialog.
I hope I am clear. Does anyone has any suggestions, pointers, ...? Help would be much appreciated ! Thanks in advance.
----EDIT----
So I have tried to implement the wxThread stuff with the LogDialog, and there is a problem. It seems that I have done something wrong with the events that are sent from the thread to the log dialog.
What I wanted was to create a thread when the LogDialog is created. Also, the ok button of the logdialog is disabled while the . This thread launches the algorithm so it runs in the background. When the algorithm is finished, it sends an event to the LogDialog. This event is captured by the log dialog, and the callback function enables the ok button, and deletes the thread.
However, it seems that the LogDialog never captures the event, and while the algorithm is running, finishes and an event is sent somewhere, the callback function is never called... Maybe it something I've done wrong. I included the code below. Thanks again for your help.
Here is what I've wrote: the OnGenerate function is as follows:
void TnF_LoadingFrame::OnGenerate(wxCommandEvent &)
{
std::cout << "Generating!" << std::endl;
LogDialog dialog (m_pController,this,-1,_("Generation"));
if(dialog.ShowModal() == wxID_OK)
std::cout << "Saving to do" << std::endl;
else
std::cout << "Cancelled" << std::endl;
}
It creates a dialog that launches a thread. The thread files are as follows:
MyThread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include "controller.h"
#include <tr1/memory>
using std::tr1::shared_ptr;
class Controller;
class MyThread : public wxThread
{
public:
MyThread(shared_ptr<Controller> pController, wxEvtHandler* parent)
: wxThread(wxTHREAD_DETACHED)
{
m_pController = pController;
m_parent = parent;
}
~MyThread(){}
protected:
virtual ExitCode Entry();
shared_ptr<Controller> m_pController;
wxEvtHandler* m_parent;
};
#endif // MYTHREAD_H
MyThread.cpp
#include "MyThread.h"
DEFINE_EVENT_TYPE(GENERATION_FINISHED_EVENT)
wxThread::ExitCode MyThread::Entry()
{
m_pController->Generate();
wxCommandEvent evt(GENERATION_FINISHED_EVENT, wxID_ANY);
m_parent->ProcessThreadEvent( evt );
//m_parent->ProcessPendingEvents();
return 0;
}
The log dialog files are as follows:
LogDialog.h
#ifndef LOGDIALOG_H
#define LOGDIALOG_H
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include "controller.h"
DECLARE_EVENT_TYPE(GENERATION_FINISHED_EVENT, wxID_ANY)
#include "MyThread.h"
class MyThread;
class LogDialog : public wxDialog
{
public:
LogDialog(shared_ptr<Controller> pController, wxWindow *parent, wxWindowID id, const wxString & title,
const wxPoint & pos = wxDefaultPosition,
const wxSize & size = wxDefaultSize,
long style = wxDEFAULT_DIALOG_STYLE );
~LogDialog(){}
wxTextCtrl *m_textRedirection;
private:
void OnGenerationFinished(wxCommandEvent &evt);
shared_ptr<Controller> m_pController;
wxButton *m_buttonOk;
wxStreamToTextRedirector *m_redirect;
MyThread *m_thread;
DECLARE_EVENT_TABLE()
};
#endif
LogDialog.cpp
#include "LogDialog.h"
// how to define the custom event
DEFINE_EVENT_TYPE(GENERATION_FINISHED_EVENT)
LogDialog::LogDialog(shared_ptr<Controller> pController, wxWindow *parent, wxWindowID id, const wxString & title,
const wxPoint & pos,
const wxSize & size,
long style)
: wxDialog(parent, id, title, pos, size, style)
{
m_pController = pController;
//some stuff
m_thread = new MyThread(m_pController,this);
m_thread->Create();
m_thread->Run();
}
void LogDialog::OnGenerationFinished(wxCommandEvent &evt)
{
m_buttonOk->Enable(true);
m_thread->Delete();
}
BEGIN_EVENT_TABLE(LogDialog, wxDialog)
EVT_COMMAND(wxID_ANY, GENERATION_FINISHED_EVENT, LogDialog::OnGenerationFinished)
END_EVENT_TABLE()
there are basically 2 options:
Single threaded Application: Do everything in the main (wx) thread, just implement your algorithm in a loop triggered by an eventhandler, since you want the gui to be responsive and even show output you have to pass processing time back to wx, this can be done by calling yield (or better safeyield), you can do this somewhere in the loop implementing your algorithm
Multithreaded Application: create a separated thread for your algorithm and use events to communicate with the main thread (and the gui) - take a look at the wx-wiki page
So I have managed to make it work... I looked at the sample thread code provided with the library. First of all, I started using wxMACROs instead of MACROs. Don't if that helped... but hey, who am I to judge. Next instead of having a:
DECLARE_EVENT_TYPE(NAME,TYPE)
call in LogDialog.h, and MyThread.h, I use an enum in LogDialog.h:
enum{
wxEVT_COMMAND_MYTHREAD_COMPLETED = wxID_HIGHEST+1
};
This seems to be equivalent to declaring the event, and reduces the macro calls everywhere, so less messy.
Then I declare the event table at the beginning of LogDialog.cpp:
wxBEGIN_EVENT_TABLE(LogDialog, wxDialog)
EVT_THREAD(wxEVT_COMMAND_MYTHREAD_COMPLETED, LogDialog::OnGenerationFinished)
wxEND_EVENT_TABLE()
Again, don't know if that helped. But the using an EVT_THREAD is probablly better. Finally, and I think it is what did the trick, I create and launch the event as follows:
wxThreadEvent evt(wxEVT_THREAD,wxEVT_COMMAND_MYTHREAD_COMPLETED);
wxQueueEvent(m_parent,evt.Clone());
This does the trick.
Also the other problem was to get the standard output back and show it in a text control but it seems that
wxStreamToTextRedirector(m_textRedirection);
is not thread safe... This is a story for another time.
Thanks to #Baxit for his insights.

Why doesn't my QDeclarativeItem get any mouse/keyboard events?

This is a reduced down, minimum complete example demonstrating my problem:
I have an application which hosts a QDeclarativeView; file events.cpp:
#include <QApplication>
#include <QDeclarativeView>
#include "TestItem.h"
int main(int argc,char* argv[]) {
QApplication app(argc,argv);
qmlRegisterType<TestItem>("Testing",1,0,"Tester");
QDeclarativeView page;
page.setSource(QUrl("page.qml"));
Q_ASSERT(page.status()==QDeclarativeView::Ready);
page.show();
return app.exec();
}
That TestItem is a subclassed QDeclarativeItem defined in file TestItem.h:
#ifndef _TestItem_h_
#define _TestItem_h_
#include <iostream>
#include <QDeclarativeItem>
#include <QPainter>
class TestItem : public QDeclarativeItem {
Q_OBJECT
public:
TestItem() {
setFlag(QGraphicsItem::ItemHasNoContents,false);
std::cerr << "[TestItem created]";
}
void paint(QPainter* painter,const QStyleOptionGraphicsItem*,QWidget*) {
painter->drawLine(0,0,width(),height());
painter->drawLine(0,height(),width(),0);
}
protected:
void mousePressEvent(QGraphicsSceneMouseEvent*) {
std::cerr << "[TestItem::mousePressEvent]";
}
void keyPressEvent(QKeyEvent*) {
std::cerr << "[TestItem::keyPressEvent]";
}
};
#endif
and the page.qml file loaded into the QDeclarativeView is simply:
import QtQuick 1.0
import Testing 1.0
Tester {
width: 200
height: 200
}
It's all built (with Qt 4.8 on Debian-Wheezy amd64) with qmake file
CONFIG += debug
QT += core gui declarative
TARGET = events
TEMPLATE = app
SOURCES += events.cpp
HEADERS += TestItem.h
and, once built, when I run ./events I get a window displaying the Tester's painted 'X', as expected:
and a [TestItem created] logged to the console. However, clicking within the window or pressing keys completely fails to invoke either of the mouse or key event handlers.
I'm completely mystified. Is some extra magic (in the C++ or QML domains) needed to get mouse/keyboard events routed to these sort of "plugin" QDeclarativeItem classes? I certainly don't have any problems defining a MouseArea in the QML file and having it do stuff to QML state, and the code this is reduced from has no problems with signals and slots interoperating between the C++ item and QML code... but when it comes to mouse/keyboard events, there's just no sign of them on the C++ side.
To get (left) mouse events, all that is needed is to add
setAcceptedMouseButtons(Qt::LeftButton);
in the TestItem constructor. This is a little surprising as the documentation for the inherited QGraphicsItem::setAcceptedMouseButtons says "By default, all mouse buttons are accepted", but maybe something else in the setup messes with the state.
To get keyboard events, setFocus(true) just needs to be invoked. The documentation seems to imply setFlag(QGraphicsItem::ItemIsFocusable,true) should also be called, but it doesn't actually seem to be necessary in my test.

Creating Custom IconFactory in Gtkmm

I have a small application, where I want to create some buttons, toolbars, etc. but I do not want to use a pre-existing GTK icon theme, I want to create my own. So I looked for tutorials and whatnot, but as it turned out, it was not very well discussed online. So I tried to do something on my own:
Header File:
#include <gtkmm.h>
#include <string>
#include <iostream>
#include <errno.h>
class IconFactoryBuilder
{
public:
IconFactoryBuilder();
~IconFactoryBuilder();
void RegisterNewIcons(std::string pPath);
private:
Glib::RefPtr<Gtk::IconFactory> mCustomFactory;
};
Cpp File:
#include <IconFactoryBuilder.h>
IconFactoryBuilder::IconFactoryBuilder() {
mCustomFactory = Gtk::IconFactory::create();
}
IconFactoryBuilder::~IconFactoryBuilder() {
}
void IconFactoryBuilder::RegisterNewIcons(std::string pPath) {
Glib::RefPtr<Gtk::IconSet> iconSet = Gtk::IconSet::create();
Gtk::IconSource someSource;
try{
Gtk::Image *someImage=Gtk::manage(new Gtk::Image(pPath+"appbar.at.png"));
someImage->set_pixel_size(Gtk::IconSize(48));
someSource.set_pixbuf(someImage->get_pixbuf());
someSource.set_size(Gtk::ICON_SIZE_DIALOG);
someSource.set_size_wildcarded();
}
catch(const Glib::Exception &ex) {
std::cerr << "An error occurred while opening the icon file!" << strerror(errno) << std::endl;
}
catch(...) {
std::cerr << "Unknown Error!" << std::endl;
}
iconSet->add_source(someSource);
const Gtk::StockID somestock("MyNewIcon");
Gtk::Stock::add(Gtk::StockItem(somestock, "somelabel"));
mCustomFactory->add(somestock, iconSet);
mCustomFactory->add_default();
}
But now I am quite stuck, because I do not really know how to call this new icon I have created. I also do not know if the above written code is enough to actually find the icon or not.
You created an icon factory... factory?
;-)
The serious answer to your question is that you don't need Gtk::IconFactory. Unfortunately the GTK 2 documentation doesn't tell you that it's unnecessary. What you do need is the freedesktop.org Standard Icon Naming Specification. Create your icons, give them simple names, organize according to the directory structure described there, install it to the appropriate place, and your icons will "just work" when you create a Pixbuf or Image using the ...from_icon_name() functions. (example: Gtk::Image::set_from_icon_name())
Here is a page from the Gnome developer wiki on how to provide your own icons:
http://developer.gnome.org/integration-guide/stable/icons.html.en
And here is a page from a tutorial I wrote about installing custom icons: http://ptomato.name/advanced-gtk-techniques/html/desktop-file.html