when I am programming with "gtkmm", there is a widget "Gtk::DrawingArea".
I can program that widget "by hand" (so write the code) or more elegant way is to use "glade" user interface designer, where I can do the same "graphically".
Now I am trying to connect OpenGL with gtkmm through "gtkglextmm" library. In that library, there is a widget "Gtk::GL::DrawingArea" - but this widget "IS NOT" available in glade.
So is there any way to program with "OpenGL + gtkglextmm" using "glade" (for the "graphical user interface" part)?
Thanks.
First of all libglade is an old library. If you are writing new project start with gtk builder.
As you can see here gtkmm provide easy way to create your own widgets and see them (almost) in glade tool. You simply insert plain DrawinArea widget to window and then tell gtk-builder to put in this place yours derived class.
Here is all together:
Setting up gtk-builder:
refBuilder = Gtk::Builder::create_from_file(ui_file);
GlDrawingArea*glArea = NULL;
refBuilder->get_widget_derived("drawing_gl",glArea);
Opengl DrawingArea class:
class GlDrawingArea : public Gtk::DrawingArea ,
public Gtk::GL::Widget<GlDrawingArea>
{
public:
GlDrawingArea(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& builder);
virtual ~GlDrawingArea();
protected:
void on_realize();
bool on_expose_event(GdkEventExpose* event);
bool on_configure_event(GdkEventConfigure* event);
private:
Glib::RefPtr<Gtk::Builder> refBuilder;
};
Constructing opengl drawingarea:
// GlDrawingArea:
GlDrawingArea::GlDrawingArea(BaseObjectType*cobject, const Glib::RefPtr<Gtk::Builder>& builder)
: Gtk::DrawingArea(cobject),
refBuilder(builder),
screen_tex(0)
{
//
// Configure OpenGL-capable visual.
//
Glib::RefPtr<Gdk::GL::Config> glconfig;
// Try double-buffered visual
glconfig = Gdk::GL::Config::create(Gdk::GL::MODE_RGB |
Gdk::GL::MODE_DEPTH |
Gdk::GL::MODE_DOUBLE);
if (!glconfig) {
std::cerr << "*** Cannot find the double-buffered visual.\n"
<< "*** Trying single-buffered visual.\n";
// Try single-buffered visual
glconfig = Gdk::GL::Config::create(Gdk::GL::MODE_RGB |Gdk::GL::MODE_DEPTH);
if (!glconfig) {
std::cerr << "*** Cannot find any OpenGL-capable visual.\n";
std::exit(1);
}
}
// print frame buffer attributes.
GLConfigUtil::examine_gl_attrib(glconfig);
//
// Set OpenGL-capability to the widget.
//
set_gl_capability(glconfig);
}
Related
I have some serious issues with sdkTrays in Ogre.
I have my OgreKinectGame that inherits from BaseApplication. BaseApplication is creating logo etc. using sdkTrayManager. I thought this mTrayMgr was inherited and can be used in my application as well.
I am trying to setup widgets for a HUD, but I'm getting unhandled exception errors.
My setupWidgets() function looks like this.
void OgreKinectGame::setupWidgets()
{
if(!mTrayMgr)
mTrayMgr = new SdkTrayManager("InterfaceName", mWindow, mMouse);
//mTrayMgr->destroyAllWidgets(); this caused exceptions as well
// create check boxes to toggle the visibility of our particle systems
const int WIDTH_UI = 160;
// main menu
mTrayMgr->createLabel(TL_CENTER, "mMainMenuLabel", "Main Menu", WIDTH_UI);
mTrayMgr->createButton(TL_CENTER, "mOptionButton", "Option");
mTrayMgr->createButton(TL_CENTER, "mCreditButton", "About");
mTrayMgr->createButton(TL_CENTER, "mQuitButton", "Quit");
mTrayMgr->showAll();
}
First, where did you initialize your setupWidgets()? , on BaseApplication class, they have setup(), you can create a virtual of this setup() to your main class and then initialize your setupWiget() there, e.g.,
bool OgreKinectGame::setup(void)
{
if (!BaseApplication::setup()) {
return false;
}
// Load fonts for tray captions
FontManager::getSingleton().getByName("SdkTrays/Caption")->load();
setupWidgets();//initialize here for your setupWidget()
}
second, I think your setupWidget() should be like this,
void OgreKinectGame::setupWidgets()
{
const int WIDTH_UI = 160;
// main menu
mTrayMgr->createLabel(TL_CENTER, "mMainMenuLabel", "Main Menu", WIDTH_UI);
mTrayMgr->createButton(TL_CENTER, "mOptionButton", "Option");
mTrayMgr->createButton(TL_CENTER, "mCreditButton", "About");
mTrayMgr->createButton(TL_CENTER, "mQuitButton", "Quit");
}
Can you try this solution and back again if still get crash?
I am working on a Qt project consisting in a QMainWindow and multiple Qt and non-Qt classes. Many of them use QStrings with tr() that are translated with Qt Linguist. The language change (QTranslator load & install/QTranslator load & remove) is triggered by QActions in the app's menu.
I have read the official Qt documentation concerning dynamic translation, and it basically suggests the following overload:
void MainWindow::changeEvent(QEvent *event)
{
if (event->type() == QEvent::LanguageChange) {
titleLabel->setText(tr("Document Title"));
... // all my tr() QStrings here
okPushButton->setText(tr("&OK"));
} else
QWidget::changeEvent(event);
}
The problem I am facing is that the QStrings to translate are many (58 in QMainWindow alone), and several are filled at runtime as well, through user interaction; e. g. myFunction(a,b) below is called through a QPushButton:
void MainWindow::myFunction(MyClassA a, MyClassB b)
{
...
if(b.myCondition() == 0)
{
...
// below is the problem
myLabel->setText(myLabel->text() + QString("\n" + a->getName() + tr(" gagne ") + exp + tr(" points d'expérience")));
}
else
{
myLabel->setText(QString(tr("something else")));
}
...
}
So I hardly see how I can include this type of QString in the changeEvent() method above. What about the classes outside of MainWindow, which also have QStrings to translate but are not QWidget (so no changeEvent overload possible) ?
I have read that there's another way to use this method, with a UI form:
void MainWindow::changeEvent(QEvent* event)
{
if (event->type() == QEvent::LanguageChange)
{
ui.retranslateUi(this);
}
...
}
But this involves that I am using a UI form in my project, which I am not doing (all widgets are created in code).
I tried to export my MainWindow in a UI form, but when I try to include the generated header into the project, I get the following error:
ui_fenetreprincipale.h:32: error: qmainwindowlayout.h: No such file or directory
Thank you by advance for any suggestion to select the best way to translate my application.
Organize your code so that all the setting of translatable strings is done in one method in each class.
eg give every class that has translatable strings a setTrs() method that actually sets the strings.
class A
{
void setTrs()
{
okPushButton->setText(tr("&OK"));
}
}
//--------------
class B
{
int _trCond;
void myFunction(MyClassA a, MyClassB b)
{
_trCond = b.myCondition();
setTrs();
}
void setTrs()
{
if(_trCond == 0)
myLabel->setText(myLabel->text() + QString("\n" + a->getName() + tr(" gagne ") + exp + tr(" points d'expérience")));
else
myLabel->setText(QString(tr("something else")));
}
Then whenever the language for the application changes (eg connect to a menu entry selection, or MainWindow::event() or however the required language can change), you have to manually call the setTrs method of each of these objects
eg
void MainWindow::changeEvent(QEvent *event)
{
if (event->type() == QEvent::LanguageChange)
{
setTrs();
objA.setTrs();
objB.setTrs();
}
}
Even more elegant would be to store the objects in a QList and just iterate through it calling the setTrs method on each element in turn
I had the same problem, menus not translating, fixed completely by creating and installing the QTranslator...
QScopedPointer<QApplication> app(new QApplication(argc, argv));
QTranslator myappTranslator;
myappTranslator.load(QString("Languages/de"))
app->installTranslator(&myappTranslator);
...before creating and showing the main window...
MainWindow *mainWin;
mainWin = new MainWindow(&splash);
mainWin->show();
In our application that uses Qt 4 and supports touch input, we use the QFileDialog with the options QFileDialog::DontUseNativeDialog and QFileDialog::ExistingFiles.
The first is needed because we set our own stylesheet and that does not work with the native dialog. The second is for needed for selecting multiple files, which is what we want to do.
The problem ist that one can not select multiple files with touch input in the QFileDialog, because we have no "shift" or "ctrl"-key available. In Windows the problem is solved by adding checkboxes to the items. QFileDialog has no checkboxes.
I tried to manipulate the QFileDialog to make it displays check boxes for the items, but I failed.
I tried to exchanged the QFileSystemModel that is used by the underlying QTreeView and QListView, but this breaks the signal-slot connections between the model and the dialog. I could not find a way to restore them because they are burried deep in the private intestants of the dialog.
At this moment the only solution I can imagine is writing a whole new dialog, but I would like to avoid the effort.
So is there a way to add checkboxes to the QFileDialog model views ?
Do you have another idea how selecting multiple files could be made possible?
Is the problem fixed in Qt 5? We want to update anyway.
Thank you for your Help.
As I failed to add checkboxes to the item views, I implemented a "hacky" work-around. It adds an extra checkable button to the dialog that acts as a "ctrl"-key. When the button is checked, multiple files can be selected. The solution is a little bit ugly, because it relies on knowing the internals of the dialog, but it does the job.
So here is the code for the header ...
// file touchfiledialog.h
/*
Event filter that is used to add a shift modifier to left mouse button events.
This is used as a helper class for the TouchFileDialog
*/
class EventFilterCtrlModifier : public QObject
{
Q_OBJECT;
bool addCtrlModifier;
public:
EventFilterCtrlModifier( QObject* parent);
void setAddCtrlModifier(bool b);
protected:
virtual bool eventFilter( QObject* watched, QEvent* e);
};
/*
TouchDialog adds the possibility to select multiple files with touch input to the QFileDialog.
This is done by adding an extra button which can be used as control key replacement.
*/
class QTOOLS_API TouchFileDialog : public QFileDialog
{
Q_OBJECT
EventFilterCtrlModifier* listViewEventFilter;
EventFilterCtrlModifier* treeViewEventFilter;
bool initialized;
public:
TouchFileDialog( QWidget* parent);
protected:
virtual void showEvent( QShowEvent* e);
private slots:
void activateCtrlModifier(bool b);
private:
void initObjectsForMultipleFileSelection();
};
with the implementation ...
// file touchfiledialog.cpp
#include "touchfiledialog.h"
EventFilterCtrlModifier::EventFilterCtrlModifier(QObject* parent)
: QObject(parent)
, addCtrlModifier(false)
{
}
void EventFilterCtrlModifier::setAddCtrlModifier(bool b)
{
addCtrlModifier = b;
}
bool EventFilterCtrlModifier::eventFilter(QObject* watched, QEvent* e)
{
QEvent::Type type = e->type();
if( type == QEvent::MouseButtonPress || type == QEvent::MouseButtonRelease)
{
if( addCtrlModifier)
{
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(e);
// Create and post a new event with ctrl modifier if the event does not already have one.
if( !mouseEvent->modifiers().testFlag(Qt::ControlModifier))
{
QMouseEvent* newEventWithModifier = new QMouseEvent(
type,
mouseEvent->pos(),
mouseEvent->globalPos(),
mouseEvent->button(),
mouseEvent->buttons(),
mouseEvent->modifiers() | Qt::ControlModifier
);
QCoreApplication::postEvent(watched, newEventWithModifier);
return true; // absorb the original event
}
}
}
return false;
}
//#######################################################################################
TouchFileDialog::TouchFileDialog(QWidget* parent)
: QFileDialog(parent)
, listViewEventFilter(NULL)
, treeViewEventFilter(NULL)
, initialized(false)
{
}
void TouchFileDialog::showEvent(QShowEvent* e)
{
// install objects that are needed for multiple file selection if needed
if( !initialized)
{
if( fileMode() == QFileDialog::ExistingFiles)
{
initObjectsForMultipleFileSelection();
}
initialized = true;
}
QFileDialog::showEvent(e);
}
void TouchFileDialog::initObjectsForMultipleFileSelection()
{
// install event filter to item views that are used to add ctrl modifiers to mouse events
listViewEventFilter = new EventFilterCtrlModifier(this);
QListView* listView = findChild<QListView*>();
listView->viewport()->installEventFilter(listViewEventFilter);
treeViewEventFilter = new EventFilterCtrlModifier(this);
QTreeView* treeView = findChild<QTreeView*>();
treeView->viewport()->installEventFilter(treeViewEventFilter);
QGridLayout* dialogLayout = static_cast<QGridLayout*>(layout()); // Ugly because it makes assumptions about the internals of the QFileDialog
QPushButton* pushButtonSelectMultiple = new QPushButton(this);
pushButtonSelectMultiple->setText(tr("Select multiple"));
pushButtonSelectMultiple->setCheckable(true);
connect( pushButtonSelectMultiple, SIGNAL(toggled(bool)), this, SLOT(activateCtrlModifier(bool)));
dialogLayout->addWidget(pushButtonSelectMultiple, 2, 0);
}
void ZFFileDialog::activateCtrlModifier(bool b)
{
listViewEventFilter->setAddCtrlModifier(b);
treeViewEventFilter->setAddCtrlModifier(b);
}
The TouchFileDialog installs an event filter to the item views that will add a ControlModifier to the mouse events of the views when the corresponging button in the dialog is checked.
Feel free to post other solutions, because this is somewhat improvised.
Hi, I have a problem with changing window title and central widget in Qt.
There is MainWindow:
class MainWindow : public QMainWindow
{
// (...)
QStackedWidget* widgets;
Quiz* widget1, *widget2;
}
and there is a class Quiz:
class Quiz : public QWidget
{
public slots:
void myClicked();
}
I wanted to change MainWindow title after clicking on button, which is a element of Quiz (and it is connected with slot myClicked).
void Quiz::myClicked()
{
static_cast<MainWindow>(parent).myFunction();
}
void MainWindow::myFunction()
{
widget2 = new Quiz(this,2);
widgets->addWidget(widget2);
std::cout<<"current wdgt: " << widgets->currentIndex() << std::endl; // shows: 0
widgets->setCurrentWidget(widget2);
std::cout<<"current wdgt " << widgets->currentIndex() << std::endl; // shows: 1
setWindowTitle("newTitle");
std::cout<<"Title is " << windowTitle().toStdString() << std::endl;
}
So widgets->currentIndex shows index of new widget but nothing is changed in my window. The same problem is with window title - method windowTitle() returns new title, but title on a titlebar is old. Why?
If I change title in Quiz::myClicked by:
parent->setWindowTitle("newTitle");
it works! Why it works how strange? Please help.
it works! Why it works how strange? Please help.
It is not strange. That is how the Qt API is designed. See the documentation for the explanation:
windowTitle : QString
This property holds the window title (caption).
This property only makes sense for top-level widgets, such as windows and dialogs.
Let us analyze the last sentence: your quiz is neither a QMainWindow, nor a QDialog, hence it cannot work. Windows titles only make sense for those based on the documentation. When you call it on the parent, it will work respectively since that is a QMainWindow.
The title bar shows the title of the MainWindow. Your Quiz widgets are "inside" the MainWindow, so their titles are not shown.
If you want to change the title bar text, you must call MainWindow::setWindowTitle().
I'm having problem (basically, I'm confused.) while trying to create a worker thread for my wxWidgets GUI application that WILL MODIFY one of the GUI property itself. (In this case, wxTextCtrl::AppendText).
So far, I have 2 source files and 2 header files for the wx program itself (excluding my own libs, MySQL lib, etc), say MainDlg.cpp which contains a derived class of wxFrame called 'MainDlg' and MainForm.cpp which contains a derived class of wxApp called 'MainForm'.
MainForm.cpp
#include "MainHeader.h" // contains multiple header files
IMPLEMENT_APP(MainForm)
bool MainForm::OnInit()
{
MainDlg *Server = new MainDlg(wxT("App Server 1.0"), wxDEFAULT_FRAME_STYLE - wxRESIZE_BORDER - wxMAXIMIZE_BOX);
Editor->Show();
return true;
}
MainDlg.cpp:
#include "MainHeader.h"
BEGIN_EVENT_TABLE(MainDlg, wxFrame)
EVT_BUTTON(6, MainDlg::StartServer)
EVT_BUTTON(7, MainDlg::StopServer)
END_EVENT_TABLE()
CNETServerConnection *cnServCon;
std::string ServerIP, DBHost, DBUser, DBName, DBPass;
int UserCapacity, DBPort, ServerPort;
MYSQL *sqlhnd;
MainDlg::MainDlg(const wxString &title, long style) : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(301, 230), style)
{
cnServCon = new CNETServerConnection(100);
this->InitializeComponent();
}
void MainDlg::InitializeComponent()
{
this->SetTitle(wxT("App Server 1.0"));
this->SetSize(396, 260);
this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
this->Centre();
statBox = new wxTextCtrl(this, 4, wxT("Welcome to AppServer 1.0\n\n"), wxPoint(10, 10), wxSize(371, 141), wxTE_MULTILINE | wxTE_READONLY);
//.................................
}
void MainDlg::StartServer(wxCommandEvent &event)
{
this->startBtn->Enable(false);
this->AppendStatus(wxT("\nLoading server configuration... "));
//.................................
this->AppendStatus(wxT("OK\n\nServer ready!\n\n"));
// When the server is ready, I need to run a thread
// that will update the statBox (through AppendStatus func or wxTextCtrl::AppendText directly)
// regularly without interrupting the GUI itself.
// Because the thread will contain a while-loop
// to make the program keep receiving message from clients.
this->startBtn->Hide();
this->stopBtn->Show();
this->cmdBtn->Enable();
this->cmdBox->Enable();
}
void MainDlg::StopServer(wxCommandEvent &event)
{
//...................................
}
void MainDlg::AppendStatus(const wxString &message)
{
statBox->AppendText(message);
}
// Well, here is the function I'd like to run in a new thread
void MainDlg::ListenForMessages()
{
int MsgSender = 0;
while(1)
{
if(!cnServCon->GetNewMessage())
continue;
if(MsgSender = cnServCon->GetJoiningUser())
this->AppendStatus(wxT("Someone connected to the server."));
}
}
I also found an usage example of wxThread from Simple example of threading in C++:
class MessageThread : public wxThread
{
private:
MessageThread(const MessageThread ©);
public:
MessageThread() : wxThread(wxTHREAD_JOINABLE)
{
}
void *Entry(void)
{
// My works goes here
return;
}
};
wxThread *CreateThread()
{
wxThread *_hThread = new MessageThread();
_hThread->Create();
_hThread->Run();
return _hThread;
}
But I don't know how to associate it with my program and make it able to modify my GUI property (statBox).
Any kind of help would be appreciated! :)
Thanks.
The easiest is to create an event type id, and use a wxCommandEvent using the type id, set its string member ("evt.SetText"), and send the event to one of your windows (using AddPendingEvent). In the handler of that event, you can then call AppendText on your control using the text you sent (evt.GetText), because you are in the GUI thread by then.
// in header
DECLARE_EVENT_TYPE(wxEVT_MY_EVENT, -1)
// in cpp file
DEFINE_EVENT_TYPE(wxEVT_MY_EVENT)
// the event macro used in the event table. id is the window id you set when creating
// the `wxCommandEvent`. Either use -1 or the id of some control, for example.
EVT_COMMAND(window-id, event-id, handler-function)
Here is an overview how it works: Custom Events.