Gtkmm scroll_event do not work when a button is pressed - c++

I have an Gtk::EventBox with two events connected: button_press_event and scroll_event.
All the two events work fine, but when I hold down a mouse button, the scroll event is not emitted.
I have implement in my class the two functions bool on_button_press_event (GdkEventButton *e) and bool on_scroll_event (GdkEventScroll *e). This two functions return false to propagate the event further.
Im using gtkmm3.
How can I solve this problem?
An example of code to reproduce the problem:
#include <gtkmm.h>
#include <iostream>
class MyWindow : public Gtk::Window
{
Gtk::EventBox event_box;
Gtk::ScrolledWindow scrolled;
public:
bool on_button_press_event(GdkEventButton *b)
{
std::cout << "button press" << std::endl;
return false;
}
bool on_scroll_event(GdkEventScroll *e)
{
std::cout << "scrollEvent" << std::endl;
return false;
}
MyWindow ()
{
add(scrolled);
scrolled.add(event_box);
set_default_size(640, 480);
show_all();
}
};
int main(int argc, char** argv)
{
Gtk::Main kit(argc, argv);
MyWindow window;
kit.run(window);
return 0;
}

The example code you show has two problems.
First, you say "I have an Gtk::EventBox with two events connected." But in your example you connect to MyWindow's events, and leave the EventBox's events unconnected.
An EventBox allows you to receive events, but you still have to explicitly say which events you want to receive.
This is the corrected code:
#include <gtkmm.h>
#include <iostream>
class MyWindow : public Gtk::Window
{
Gtk::EventBox event_box;
bool event_box_button_press(GdkEventButton *b)
{
std::cout << "button press" << std::endl;
return false;
}
bool event_box_scroll(GdkEventScroll *e)
{
std::cout << "scrollEvent" << std::endl;
return false;
}
public:
MyWindow ()
{
event_box.add_events(Gdk::BUTTON_MOTION_MASK);
event_box.add_events(Gdk::SCROLL_MASK);
event_box.signal_button_press_event().connect(
sigc::mem_fun(*this, &MyWindow::event_box_button_press));
event_box.signal_scroll_event().connect(
sigc::mem_fun(*this, &MyWindow::event_box_scroll));
add(event_box);
set_default_size(640, 480);
show_all();
}
};
int main(int argc, char** argv)
{
Gtk::Main kit(argc, argv);
MyWindow window;
kit.run(window);
return 0;
}
Some notes on this code:
I've omitted the ScrolledWindow, as that is irrelevant to the example. You don't need it to catch scroll events. You can add it back if you actually need a scrolled window for your application.
The code would probably be neater if you derive a custom EventBox with the behavior you need. I didn't do this to stay closer to your original code.
See this documentation for information on connecting signals and the sigc::mem_fun stuff.

It looks like on windows, the scrolledWindow was the right place to watch for scroll events instead of the main window.
Using the following modification, I was able to handle scroll events on windows 7.
#include <gtkmm.h>
#include <iostream>
class MyScrolledWindow : public Gtk::ScrolledWindow
{
public:
bool on_scroll_event(GdkEventScroll *e)
{
std::cout << "scrollEvent" << std::endl;
return false;
}
MyScrolledWindow()
{
}
};
class MyWindow : public Gtk::Window
{
Gtk::EventBox event_box;
MyScrolledWindow scrolled;
public:
bool on_button_press_event(GdkEventButton *b)
{
std::cout << "button press" << std::endl;
return false;
}
MyWindow ()
{
add(scrolled);
scrolled.add(event_box);
set_default_size(640, 480);
show_all();
}
};
int main(int argc, char** argv)
{
Gtk::Main kit(argc, argv);
MyWindow window;
kit.run(window);
return 0;
}
====== Old Answer: ================
I am not able to reproduce your isse. This is the code I used to try to reproduce your issue:
#include <gtkmm.h>
#include <iostream>
class MyEventBox : public Gtk::EventBox
{
bool on_button_press_event(GdkEventButton *b)
{
std::cout << "button press" << std::endl;
return false;
}
bool on_scroll_event(GdkEventScroll *e)
{
std::cout << "scrollEvent" << std::endl;
return false;
}
};
int main(int argc, char** argv)
{
Gtk::Main kit(argc, argv);
Gtk::Window window;
MyEventBox eventBox;
eventBox.show();
window.add(eventBox);
kit.run(window);
return 0;
}
For compiling, I used the folowing command line (using Linux):
g++ main.cpp $(pkg-config --cflags --libs gtkmm-3.0)
If you can reproduce your issue using this minimum example, the problem might be platform specific and a workaround using the window's/event box' Gdk::Window might be necessary.
If you can't reproduce your issue using this code, the issue is caused somewhere else in your code and you'll need to post more information.

Related

gtkmm: Why button only reacts on double click

I want to get a response from the button also if button is only clicked once. The following code only reacts on "double-click". Why does this behavior occur and how can I change that behavior?
#include <iostream>
#include <gtkmm.h>
#include <gtkmm/window.h>
class ExampleWindow: public Gtk::Window
{
Gtk::Button button;
public:
ExampleWindow(): button("Hallo")
{
add(button);
button.signal_button_press_event().connect( [this]( GdkEventButton* ev)->bool{ std::cout << "Press" << std::endl; return true; });
}
};
int main(int argc, char* argv[])
{
Gtk::Main kit(argc, argv);
ExampleWindow window;
window.show_all_children();
Gtk::Main::run(window);
return 0;
}

Gtk::Window shows and exits while porting my gtkmm2 to gtkmm3 application

I'm porting my gtkmm2 to gtkmm3 application, this is what I have so far:
// The main.cxx:
#include "alarmui.hxx"
int main (int argc, char *argv[]) {
Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm." PACKAGE_ID);
alarm_ui win(app);
app->run ();
return 0;
}
Header:
// The alarmui.hxx
#ifndef ALARMUI_HXX_INC
#define ALARMUI_HXX_INC
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gtkmm/application.h>
#include <gtkmm/window.h>
#include <gtkmm/statusicon.h>
#include <iostream>
#include <memory>
#include <functional>
class alarm_ui : public Gtk::Window
{
private:
Glib::RefPtr<Gtk::Application> _refApp;
Glib::RefPtr<Gtk::StatusIcon> m_status_icon;
public:
alarm_ui (Glib::RefPtr<Gtk::Application>&);
virtual ~alarm_ui ();
protected:
virtual bool delete_event (GdkEventAny*);
void status_icon_activate_cb ();
};
#endif
source code:
#include "alarmui.hxx"
alarm_ui::alarm_ui (Glib::RefPtr<Gtk::Application>& refApp) : _refApp(refApp)
{
std::cout << "init" << std::endl;
set_icon_from_file (ICON_PNG_PATH);
m_status_icon = Gtk::StatusIcon::create_from_file (ICON_PNG_PATH);
m_status_icon->signal_activate().connect (std::bind(&alarm_ui::status_icon_activate_cb, this));
show_all ();
}
alarm_ui::~alarm_ui () {
std::cout << "done" << std::endl;
}
bool alarm_ui::delete_event (GdkEventAny* event) {
return false;
}
void alarm_ui::status_icon_activate_cb () {
if (get_visible ()) {
iconify ();
hide ();
} else {
deiconify ();
show();
}
}
I'm trying to show my window with a status icon. Toggle the window visibility, while clicking in the status icon. The code compiles fine but seems that when I execute the binary the constructor and destructor are invoked. If I use something like this:
Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm." PACKAGE_ID);
alarm_ui win(app);
app->run (win);
The windows shows, but...as expected, exits on hide() command...any ideas?
Are hold() and release() my only options?
By default, Gtk::Application::run() returns when all the Application's windows have been closed (hidden). Your window (win) will then be destroyed when it goes out of scope when your main() ends.
Gtk::Application::hold() and release() might indeed be what you need. Or maybe you can just do whatever you need to do after run() returns. I guess it depends on what you want to do and when.

Qt 5 and QProcess redirect stdout with signal/slot readyRead

This problem is bothering me because it should work, but sadly it does not.
What i try to achieve is to read the standard output of a certain process and make another process handle it i.e. print it out.
The process that produces output looks like this:
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
int main() {
for (int i = 0; i < 100; i++) {
printf("yes %d\n",i);
fflush(stdout);
sleep(1);
}
return 0;
}
The process is started in another application like this:
#include <QProcess>
...
QProcess * process = new QProcess;
SomeClass * someClass = new SomeClass(process);
connect(process,SIGNAL(readyRead()),someClass,SLOT(onReadyRead()));
process->start("../Test/Test",QStringList());
if (!process->waitForStarted(4000)) {
qDebug() << "Process did not start.";
}
...
void SomeClass::onReadyRead() {
qDebug() << "Reading:" << process->readAllStdOutput();
}
My expected output would be:
Reading: yes 0
Reading: yes 1
...
Reading: yes 99
However i get no output at all.
And when i use QCoreApplication i get all the output but not through the signal/slot but directly in the console.
I dont understand because it works in another application that uses Qt 4.8.
My question is, is anyone experiencing the same problem or does anyone know how i can get the expected behaviour?
Your problem in the answer you provide lies in misunderstanding how the reading works. It simply returns whatever data you've got there, whether there are line endings or not. By spawning a thread and sleeping between lines, you're effectively sending the inter-process data in line-sized chunks since the pipe is flushed when you wait long enough.
So, your answer, while working, is not really how one should do it. You need to use readLine() to chop the incoming data into lines. Below is an example with following qualities:
There's just one executable :)
Only Qt apis are used. This reduces the runtime memory consumption.
Both processes cleanly terminate.
The amount of code is as minimal as practicable.
// https://github.com/KubaO/stackoverflown/tree/master/questions/process-17856897
#include <QtCore>
QTextStream out{stdout};
class Slave : public QObject {
QBasicTimer m_timer;
int m_iter = 0;
void timerEvent(QTimerEvent * ev) override {
if (ev->timerId() == m_timer.timerId()) {
out << "iteration " << m_iter++ << endl;
if (m_iter > 35) qApp->quit();
}
}
public:
Slave(QObject *parent = nullptr) : QObject(parent) {
m_timer.start(100, this);
}
};
class Master : public QObject {
Q_OBJECT
QProcess m_proc{this};
Q_SLOT void read() {
while (m_proc.canReadLine()) {
out << "read: " << m_proc.readLine();
out.flush(); // endl implicitly flushes, so we must do the same
}
}
Q_SLOT void started() {
out << "started" << endl;
}
Q_SLOT void finished() {
out << "finished" << endl;
qApp->quit();
}
public:
Master(QObject *parent = nullptr) : QObject(parent) {
connect(&m_proc, SIGNAL(readyRead()), SLOT(read()));
connect(&m_proc, SIGNAL(started()), SLOT(started()));
connect(&m_proc, SIGNAL(finished(int)), SLOT(finished()));
m_proc.start(qApp->applicationFilePath(), {"dummy"});
}
};
int main(int argc, char *argv[])
{
QCoreApplication app{argc, argv};
if (app.arguments().length() > 1)
new Slave{&app}; // called with an argument, this is the slave process
else
new Master{&app}; // no arguments, this is the master
return app.exec();
}
#include "main.moc"
Based on the code you've posted, you're connecting to the class slot with this: -
connect(process,SIGNAL(readyRead()),someClass,SLOT(onReadyReadStdOutput()));
But the function in the class is declared like this: -
void SomeClass::onReadyRead();
If you're expecting onReadyRead to be called, then you should be calling it in the SLOT, rather than onReadyReadStdOutput. So change your connection to: -
connect(process,SIGNAL(readyRead()),someClass,SLOT(onReadyRead()));
Well i solved my problem.
If the process is started with startDetached() it will not receive the signals from readyRead(), readyReadStandardOutput() and readyReadStandardError().
So just starting it with start() solved the problem.
However i noticed that if i start and do the while loop and prints in main() it will read everything at once even if it ends with \n. So i started the while loop in a thread and that problem was also solved. Everything prints as expected.
#include <QThread>
class Thread : public QThread
{
Q_OBJECT
public:
explicit Thread(QObject *parent = 0) : QThread(parent) {}
protected:
void run() {
for (int i = 0; i < 100; i++) {
std::cout << "yes" << i << std::endl;
msleep(200);
}
exit(0);
}
};
int main(int argc, char ** argv) {
QCoreApplication app(argc,argv);
Thread * t = new Thread();
t->start();
return app.exec();
}
TestP main.cpp
#include <QProcess>
#include <iostream>
class Controller : public QObject
{
Q_OBJECT
private:
QProcess * process;
public:
Controller(QObject *parent = 0) :
QObject(parent), process(new QProcess) {}
void init(const QString &program) {
connect(process,SIGNAL(readyRead()),this,SLOT(readStdOut()));
connect(process,SIGNAL(started()),this,SLOT(onStarted()));
connect(process,SIGNAL(finished(int)),this,SLOT(onFinished(int)));
process->start(program);
}
private slots:
void readStdOut() {
std::cout << "YES " << QString(process->readAllStandardOutput()).toUtf8().constData() << std::endl;
}
void onStarted(){
std::cout << "Process started" << std::endl;
}
void onFinished(int) {
std::cout << "Process finished: " << signal << std::endl;
}
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
Controller c;
c.init("../Test/Test");
return a.exec();
}

GTK::main::run segfaults

I'm trying to write a GTK program. I managed to get my window to spawn with a button in it, but now when I try to pack a box and add 2 buttons to the box I segfault. What confuses me is that it doesn't segfault when I create anything, but insead when i run GTK::Main::run.
int main(int argc, char *argv[])
{
Glib::RefPtr<Gtk::Application> app =
Gtk::Application::create(argc, argv,
"org.gtkmm.examples.base");
MainWindow mainWindow;
cout << "trying to run window"<< endl;
Gtk::Main::run(mainWindow);
cout << "done running window"<< endl;
return 0;
}
and
MainWindow::MainWindow()
:quit_button("Quit"),
write_button("Write"),
window_box()
{
set_border_width(10);
quit_button.signal_clicked().connect(sigc::mem_fun(*this,
&MainWindow::quit_button_clicked));
write_button.signal_clicked().connect(sigc::mem_fun(*this,
&MainWindow::write_button_clicked));
window_box.start_pack(quit_button);
window_box.start_pack(write_button);
add(window_box);
write_button.show();
quit_button.show();
window_box.show();
}
MainWindow::~MainWindow()
{
}
void MainWindow::write_button_clicked()
{
std::cout << "Hello World" << std::endl;
}
void MainWindow::quit_button_clicked()
{
exit(0);
}
are my main method and my constructor for my MainWindow class. I've tried not packing anything or packing less things and I still segfault. I'm brand new to GTK so I know I must be missing something simple.
Edit: Main Window Declaration
#include <gtkmm.h>
#include <iostream>
using namespace std;
class MainWindow : public Gtk::Window
{
public:
MainWindow();
~MainWindow();
protected:
//Signal Handlers
void write_button_clicked();
void quit_button_clicked();
//Widgets
Gtk::Button quit_button;
Gtk::Button write_button;
Gtk::VBox window_box;asd
};
#endif // GTKMM_EXAMPLE_HELLOWORLD_H
Gtk::Main has been deprecated and replaced by Gtk::Application, it handles the event loop now.
It works if instead of Gtk::Main::run(mainWindow); you use app->run(mainWindow);
like ergosys said.
For proper cleanup, you should probably call app->quit() instead of libc's quit(), too (or just close the window, which will terminate the main loop too)

gtkmm and gstreamermm test code throws run time error

I am testing the following code on Ubuntu 10.04/12.04. I am receiving an error
#include <gtkmm.h>
#include <gstreamermm.h>
#include <iostream>
using namespace std;
class Sound
{
public:
Sound();
void start_playing(double frequency);
bool stop_playing();
private:
Glib::RefPtr<Gst::Pipeline> m_pipeline;
Glib::RefPtr<Gst::Element> m_source;
Glib::RefPtr<Gst::Element> m_sink;
};
Sound::Sound()
{
m_pipeline = Gst::Pipeline::create("note");
m_source = Gst::ElementFactory::create_element("audiotestsrc",
"source");
m_sink = Gst::ElementFactory::create_element("autoaudiosink",
"output");
m_pipeline->add(m_source);
m_pipeline->add(m_sink);
m_source->link(m_sink);
}
void Sound::start_playing (double frequency)
{
m_source->set_property("freq", frequency);
m_pipeline->set_state(Gst::STATE_PLAYING);
/* stop it after 200ms */
Glib::signal_timeout().connect(sigc::mem_fun(*this, &Sound::stop_playing),
200);
}
bool Sound::stop_playing()
{
m_pipeline->set_state(Gst::STATE_NULL);
return false;
}
class Buttons : public Gtk::Window
{
public:
Buttons();
virtual ~Buttons();
protected:
//Signal handlers:
//void on_button_clicked();
Sound sound;
//void on_button_clicked(double frequency, Sound& sound);
void on_button_clicked(double frequency, Sound* sound);
//Child widgets:
Gtk::Button m_button;
};
Buttons::Buttons()
{
m_button.add_pixlabel("info.xpm", "cool button");
set_title("Pixmap'd buttons!");
set_border_width(10);
//m_button.signal_clicked().connect( sigc::mem_fun(*this,
// &Buttons::on_button_clicked) );
//m_button.signal_clicked().connect (sigc::bind<double, Sound*>(sigc::ptr_fun(&Buttons::on_button_clicked),
// 369.23, &sound));
//m_button.signal_clicked().connect( sigc::bind<double, Sound&>( sigc::mem_fun(*this, &Buttons::on_button_clicked), 369.23, sound) );
m_button.signal_clicked().connect( sigc::bind<double, Sound*>( sigc::mem_fun(*this, &Buttons::on_button_clicked), 369.23, &sound) );
add(m_button);
show_all_children();
}
Buttons::~Buttons()
{
}
/*
void Buttons::on_button_clicked()
{
std::cout << "The Button was clicked." << std::endl;
}
*/
/*
void
Buttons::on_button_clicked(double frequency, Sound& sound)
{
sound.start_playing (frequency);
}
*/
void
Buttons::on_button_clicked(double frequency, Sound* sound)
{
sound->start_playing (frequency);
}
int main(int argc, char *argv[])
{
Gtk::Main kit(argc, argv);
Buttons buttons;
//Shows the window and returns when it is closed.
Gtk::Main::run(buttons);
return 0;
}
I compile using g++ gstmm1.cc -o gstmm1 ``pkg-config --cflags --libs gstreamermm-0.10 gtkmm-2.4
terminate called after throwing an instance of 'std::runtime_error'
what(): Failed to add null element.
Aborted
Any Idea what could be wrong? It almost appears that this may be related to the following post
http://old.nabble.com/gstmm-add-element-to-pipeline-test-td14042055.html
This worked for me! I was missing code Gst::init(); in main()
#include <gtkmm.h>
#include <gstreamermm.h>
#include <glibmm/main.h>
#include <iostream>
class Sound
{
public:
Sound();
void start_playing(double frequency);
bool stop_playing();
private:
Glib::RefPtr<Gst::Pipeline> m_pipeline;
Glib::RefPtr<Gst::Element> m_source;
Glib::RefPtr<Gst::Element> m_sink;
};
Sound::Sound()
{
m_pipeline = Gst::Pipeline::create("note");
m_source = Gst::ElementFactory::create_element("audiotestsrc",
"source");
m_sink = Gst::ElementFactory::create_element("autoaudiosink",
"output");
m_pipeline->add(m_source);
m_pipeline->add(m_sink);
m_source->link(m_sink);
}
void Sound::start_playing (double frequency)
{
m_source->set_property("freq", frequency);
//m_pipeline->set_state(Gst::STATE_NULL);
//m_pipeline->set_state(Gst::STATE_PAUSED);
//m_pipeline->set_state(Gst::STATE_READY);
m_pipeline->set_state(Gst::STATE_PLAYING);
/* stop it after 200ms */
Glib::signal_timeout().connect(sigc::mem_fun(*this, &Sound::stop_playing),
200);
}
bool Sound::stop_playing()
{
//m_pipeline->set_state(Gst::STATE_PAUSED);
m_pipeline->set_state(Gst::STATE_NULL);
return false;
}
class HelloWorld : public Gtk::Window
{
public:
HelloWorld();
virtual ~HelloWorld();
protected:
//Signal handlers:
void on_button_clicked();
//Member widgets:
Gtk::Button m_button;
Sound sound;
};
HelloWorld::HelloWorld()
: m_button("Hear Sound") // creates a new button with label "Hello World".
{
// Sets the border width of the window.
set_border_width(10);
// When the button receives the "clicked" signal, it will call the
// on_button_clicked() method defined below.
m_button.signal_clicked().connect(sigc::mem_fun(*this,
&HelloWorld::on_button_clicked));
// This packs the button into the Window (a container).
add(m_button);
// The final step is to display this newly created widget...
m_button.show();
}
HelloWorld::~HelloWorld()
{
}
void HelloWorld::on_button_clicked()
{
std::cout << "Hello World" << std::endl;
sound.start_playing(400);
}
int main(int argc, char* argv[]) {
Gst::init();
Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
HelloWorld helloworld;
//Shows the window and returns when it is closed.
return app->run(helloworld);
return 0;
}