Creating Custom IconFactory in Gtkmm - c++

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

Related

Show accelerator key in menu

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();
}

boost, coroutine2 (1.63.0): throwing exception crashes visual studio on 32bit windows

In my application I'm using coroutine2 to generate some objects which I have to decode from a stream. These objects are generated using coroutines. My problem is that as soon as I reach the end of the stream and would theoretically throw std::ios_base::failure my application crashes under certain conditions.
The function providing this feature is implemented in C++, exported as a C function and called from C#. This all happens on a 32bit process on Windows 10 x64. Unfortunately it only reliably crashes when I start my test from C# in debugging mode WITHOUT the native debugger attached. As soon as I attach the native debugger everything works like expected.
Here is a small test application to reproduce this issue:
Api.h
#pragma once
extern "C" __declspec(dllexport) int __cdecl test();
Api.cpp
#include <iostream>
#include <vector>
#include <sstream>
#include "Api.h"
#define BOOST_COROUTINES2_SOURCE
#include <boost/coroutine2/coroutine.hpp>
int test()
{
using coro_t = boost::coroutines2::coroutine<bool>;
coro_t::pull_type source([](coro_t::push_type& yield) {
std::vector<char> buffer(200300, 0);
std::stringstream stream;
stream.write(buffer.data(), buffer.size());
stream.exceptions(std::ios_base::eofbit | std::ios_base::badbit | std::ios_base::failbit);
try {
std::vector<char> dest(100100, 0);
while (stream.good() && !stream.eof()) {
stream.read(&dest[0], dest.size());
std::cerr << "CORO: read: " << stream.gcount() << std::endl;
}
}
catch (const std::exception& ex) {
std::cerr << "CORO: caught ex: " << ex.what() << std::endl;
}
catch (...) {
std::cerr << "CORO: caught unknown exception." << std::endl;
}
});
std::cout << "SUCCESS" << std::endl;
return 0;
}
C#:
using System;
using System.Runtime.InteropServices;
namespace CoroutinesTest
{
class Program
{
[DllImport("Api.dll", EntryPoint = "test", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
internal static extern Int32 test();
static void Main(string[] args)
{
test();
Console.WriteLine("SUCCESS");
}
}
}
Some details:
We are using Visual Studio 2015 14 and dynamically link the c++ runtime.
The test library statically links Boost 1.63.0.
We also tried to reproduce this behaviour with calling the functionallity directly from c++ and from python. Both tests have not been successful so far.
If you start the c# code with CTRL F5 (meaning without the .net debugger) everything will also be fine. Only if you start it with F5 (meaning the .NET Debugger attached) the visual studio instance will crash. Also be sure not to enable the native debugger!
Note: If we don't use the exceptions in the stream, everything seams to be fine as well. Unfortunately the code decoding my objects makes use of them and therefore I cannot avoid this.
It would be amazing if you had some additional hints on what might go wrong here or a solution. I'm not entirely sure if this is a boost bug, could also be the c# debugger interfering with boost-context.
Thanks in advance! Best Regards, Michael
I realize this question is old but I just finished reading a line in the docs that seemed pertinent:
Windows using fcontext_t: turn off global program optimization (/GL) and change /EHsc (compiler assumes that functions declared as extern "C" never throw a C++ exception) to /EHs (tells compiler assumes that functions declared as extern "C" may throw an exception).
This is just a guess but in your coroutine I think you are supposed to push a boolean to your sink (named as yield in your code) and the code is not doing it.

C++ Awesomium setting up listener problems

I have an odd error I can't figure out.
I am using Awesomium(latest ver) with C++ and I want to create a listener to connect the JS to the running program.
I have included the /include and /lib folders correctly,
I am also including necessary headers
#include <Awesomium/WebCore.h>
#include <Awesomium/STLHelpers.h>
#include <Awesomium/JSObject.h>
#include <Awesomium/WebViewListener.h>
However, visual studio keeps saying there's something wrong with my code
class BrowserListener : public Awesomium::WebViewListener
{
public:
virtual void onCallback(Awesomium::WebView* caller, const std::wstring& objectName,
const std::wstring& callbackName, const Awesomium::JSArray& args)
{
// Check starts here
if (objectName == L"app" && callbackName == L"settings") {
std::cout << "callback called with " << args.size() << " args\n";
}
}
};
It underlines Awesomium::WebViewListener as red, saying:
Namespace Awesomium, not a class or struct name.
Namespace Awesomium has no member class WebViewListener
This is the part I can't solve,
please help :)
edit: to be clear, I am able to use awesomium to load sites as intended. It's just the listener object that refuses to work
WebViewListener is a namespace nested in the Awesomium namespace, not a class. Change the base class to Awesomium::WebViewListener::Load.

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.