I'm trying to use wxWidgets to create a open/save FileDialog window in a cross-platform way. So I've looked at the examples in the documentation. I also want to create standalone windows, with no parent, because I am not using any other instance of a wxApp/wxWindow elsewhere in my program.
Additionally, I need to have my own main function, so I don't want to use a macro such as IMPLEMENT_APP. I tried to follow the instructions given here, and came up with the following minimal program:
#include <wx/wx.h>
#include <string>
#include <iostream>
std::string openFile() {
wxFileDialog openFileDialog(NULL, _("Open XYZ file"), "", "",
"XYZ files (*.xyz)|*.xyz", wxFD_OPEN|wxFD_FILE_MUST_EXIST);
if (openFileDialog.Show() == wxID_CANCEL)
return ""; // the user changed idea...
// proceed loading the file chosen by the user;
return "something";
}
int main(int argc, char *argv[]) {
std::cout << wxEntryStart(argc, argv) << std::endl;
std::string s = openFile();
wxEntryCleanup();
}
And here is the CMakeLists.txt I used to compile the code:
CMake_Minimum_Required(VERSION 2.8.11)
Project(test)
Find_Package(wxWidgets REQUIRED)
Include(${wxWidgets_USE_FILE})
Add_Executable(test main.cpp)
Target_Link_Libraries(test ${wxWidgets_LIBRARIES})
Still, when I run this program, I get a Segmentation Fault, despite the wxEntryStart returning true, and I have no idea where the problem comes from. Any tip?
I wouldn't be so bold with stripping down wx's initialization code. It may work today, but in the next version, who knows...
This is what I use:
class MyApp : public wxApp { };
wxIMPLEMENT_APP_NO_MAIN(MyApp);
int main()
{
wxDISABLE_DEBUG_SUPPORT();
int dummy = 0;
if(!wxEntryStart(dummy, static_cast<wxChar**>(nullptr)))
return 1;
auto onx1 = on_exit([]{ wxEntryCleanup(); }); //using RAII for cleanup
//Other initialization, create main window, show it.
wxGetApp().OnRun(); //This starts the event loop.
//In your case, it looks like ShowModal's event loop is enough,
//so you don't need this.
}
I think those macros, ugly as they are, provide much better insulation against future changes in the library initialization code.
Ok, after some fiddling here is a code sample that works for me. Comments welcome on what'd be the best practices. What I did was keep ShowModal() instead of Show() in the openFile function. I also created the instance of singleton wxApp. The final code is here:
#include <wx/wx.h>
#include <string>
#include <iostream>
std::string openFile() {
wxFileDialog openFileDialog(NULL, _("Open XYZ file"), "", "",
"XYZ files (*.xyz)|*.xyz", wxFD_OPEN|wxFD_FILE_MUST_EXIST);
if (openFileDialog.ShowModal() == wxID_CANCEL)
return ""; // the user changed idea...
// proceed loading the file chosen by the user;
return "something";
}
int main(int argc, char *argv[]) {
wxApp::SetInstance( new wxApp() );
wxEntryStart(argc, argv);
std::string s = openFile();
wxEntryCleanup();
}
Not sure this is completely leak-free, as valgrind seems to complain a little after exit. Any tip about whether I could also put the wxEntryStart() in the openFile() function welcome (I am guaranteed that this is the only place where the wxWidgets lib is used, and I want an API as simple as possible).
Related
I'm trying to interface with the xfce4-settings-manager which I was successfully able to do in the standard c gtk+-3.0 libraries, but I've been struggling to replicate it in gtkmm3. xfce4-settings-manager passes a --socked-id option to the client, and the client is meant to use a GtkPlug to connect to the socket via the id. As I mentioned before I was successful in writing it in C, and I've put that code in a github gist here. I prefer to use C++ as a means to learn the language in a more applied way and also because of its higher functionality to C.
I've struggled with the proper way to handle arguments to the proper way to use Gtk::Plug with hours of research and little results. If anyone can provide some insight into the proper way/documentation to handle command line arguments and GtkPlugs in gtkmm3 that would be greatly appreciated, and if you could provide any examples that would also be greatly appreciated. Thank you in advance!
Here is an example similar to yours, in C++ with Gtkmm 3:
#include <string>
#include <gtkmm.h>
#include <gtkmm/plug.h>
// Simple command line argument parser.
//
// Documented here:
//
// https://gitlab.gnome.org/GNOME/glibmm/-/blob/master/examples/options/main.cc
//
class CmdArgParser : public Glib::OptionGroup
{
public:
CmdArgParser(const std::string& p_name, const std::string& p_description, const std::string& p_help)
: Glib::OptionGroup{p_name, p_description, p_help}
{
// Define the 'socket ID' argument options:
Glib::OptionEntry socketIDArg;
socketIDArg.set_long_name("socket-id");
socketIDArg.set_short_name('s');
socketIDArg.set_flags(Glib::OptionEntry::FLAG_IN_MAIN);
socketIDArg.set_description("Settings manager socket");
// Register it in the parser. It value will be recorded in m_socketID for later usage.
add_entry(socketIDArg, m_socketID);
}
// Override this to handle errors. I skipped it for simplicity.
// void on_error(Glib::OptionContext& context, const Glib::Error& error) override;
::Window GetSocketID() const
{
return m_socketID;
}
private:
int m_socketID = 0;
};
// This is what is going to be plugged into xfce4-settings-manager:
//
// Documented here:
//
// https://developer.gnome.org/gtkmm-tutorial/3.22/sec-plugs-sockets-example.html.en
//
class SettingsPlug : public Gtk::Plug
{
public:
SettingsPlug(::Window p_socketID)
: Gtk::Plug{p_socketID}
{
m_button.set_label("A plug with Gtkmm3!");
add(m_button);
show_all_children();
}
private:
Gtk::Button m_button;
};
int main(int argc, char** argv)
{
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example.plug");
// Parse command line arguments and retreive socket ID:
Glib::init();
setlocale(LC_ALL, "");
Glib::OptionContext context;
CmdArgParser parser{
"Socket ID",
"Command line argument for socket ID communication.",
"No help available, sorry"
};
context.set_main_group(parser);
context.parse(argc, argv);
::Window socketID = parser.GetSocketID();
// Handle plug:
SettingsPlug plug{socketID};
plug.show();
app->run(plug);
return 0;
}
I have removed error handling and the usage of Glade files to simplify the code. You can build it with:
g++ main.cpp -o example.out `pkg-config --cflags --libs gtkmm-3.0`
Original Post
I have a Qt application. This application needs to call some function in a dynamic library that is loaded implicitly. In the dynamic library, there is one global variable that is created when the dll is loaded and destroyed when it is unloaded.
Here's the code:
#include <QApplication>
#include <base/BASE_TEST.h>
int main(int qargc, char** qargv)
{
QApplication application(qargc, qargv);
BASE_TEST::myDLLFunction(); // call to a function in an implicitly loaded dynamic library.
return 0;
}
Implementation of myDLLFunction and of the private class of the global object.
#include <base/BASE_TEST.h>
#include <stdio.h>
class MyTest
{
public:
MyTest() { printf("------------------------------\nTEST BEGIN\n------------------------------\n"); }
~MyTest() { printf("------------------------------\nTEST END\n------------------------------\n"); }
};
MyTest test; // created at the library's loading
void BASE_TEST::myDLLFunction()
{
printf("Call from dynamic library\n");
}
If I run the application, here's what being printed in the command prompt:
------------------------------
TEST BEGIN
------------------------------
Call from dynamic library
------------------------------
TEST END
------------------------------
Up to here all is well. However, if I retrieve some information about the number of screens using QApplication::desktop(), the global object of the dynamic library isn't destroyed.
int main(int qargc, char** qargv)
{
QApplication application(qargc, qargv);
QDesktopWidget* desktop = QApplication::desktop(); // This call prevent the global objects to be destroyed.
BASE_TEST::myDLLFunction(); // call to a function in an implicitly loaded dynamic library.
return 0;
}
Here's what is printed in the command prompt:
------------------------------
TEST BEGIN
------------------------------
Call from dynamic library
The main function still returns normally and no exception is thrown.
I looked at the code of QApplication and QDesktopWidget and the QDesktopWidget destructor is being called at the end of the main function's scope and QDesktopWidgetPrivate::cleanup() is called.
I'm on Windows, using Qt 4.8.6.
Does someone has any idea?
Thanks! :)
Edit
As mentionned in the answer below, the problem seems to be linked to loading wintab32.dll which will load the Wacom driver's dynamic library if installed.
I finally found the source of the issue:
Calling QApplication::desktop() made Wacom_Tablet.dll be loaded. By uninstalling the Wacom driver, the problem went away.
I was able to reduce the sample program to:
#include "../baseTest/BASE_TEST.h"
#include <wtypes.h>
#include "wintab.h"
typedef UINT(WINAPI *PtrWTInfo)(UINT, UINT, LPVOID);
static PtrWTInfo ptrWTInfo = 0;
int main(int /*qargc*/, char** /*qargv*/)
{
BASE_TEST::myDLLFunction(); // call to a function in an implicitly loaded dynamic library.
HMODULE hWintab = LoadLibrary(L"wintab32.dll");
PtrWTInfo pWTInfo = (PtrWTInfo)GetProcAddress(hWintab, "WTInfoW");
WORD thisVersion;
pWTInfo(WTI_INTERFACE, IFC_SPECVERSION, &thisVersion);
if (hWintab)
FreeLibrary(hWintab);
return 0;
}
and still be able to reproduce the issue.
I've contacted Wacom about it and am waiting their reply.
I currently trying to move some code from Python to C++ for practice reasons and tried to create a line chart with the QtCharts library.
I installed the Qt library with the Qt open source installer provided by Qt on their website and finally managed to include the library in the project on Clion with the following code:
CMakelist.txt
cmake_minimum_required(VERSION 3.12)
project(Uebung1)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# Qt
# Set this to your Qt installation
set(CMAKE_PREFIX_PATH /Users/DaniBook/Qt/5.11.2/clang_64)
set(RESOURCE_FOLDER res)
set(RESOURCE_FILES ${RESOURCE_FOLDER}/resources.qrc)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
find_package(Qt5Charts CONFIG REQUIRED)
set(PROJECT_SOURCE_DIR src/)
include_directories(${PROJECT_SOURCE_DIR})
add_executable(Uebung1 src/main.cpp src/tools.h src/tools.cpp)
target_link_libraries(Uebung1 Qt5::Charts)
So far so good I was able to then use the library in my code which basically consists of a two functions. One that reads data from a file and converts it to a vector of integers and one that takes this vector and attempts to create a Qt line chart.
tools.cpp
#include "tools.h"
#include <vector>
#include <string>
#include <map>
#include <iostream>
#include <QtCharts>
#include <fstream>
using namespace std;
void GC_skew(vector<long> &gc_data, string &genome_file) {
ifstream gfile(genome_file);
string line;
long g_sum = 0, c_sum = 0;
if(gfile.is_open()) {
//reading file line by line
while(getline(gfile, line)) {
//iterating over string characters to count G and C
for(auto &character : line) {
switch(character) {
case 'G' :
g_sum++;
break;
case 'C' :
c_sum++;
break;
default :
continue;
}
//appending skew to vector
gc_data.push_back(g_sum - c_sum);
}
}
}
else {
cout << "Unable to open file!" << endl;
}
}
void skew_chart(vector<long> &gc_data) {
auto *series = new QLineSeries();//creating new series object
auto *chart = new QChart(); //creating new chart object
auto *chartview = new QChartView(chart); //creating chartview object
QMainWindow window; //window object for display
long pos = 0;
//iterating over the vector and appending data to series
for(auto it : gc_data) {
series->append(pos, it);
pos++;
}
//adding series to chart and viewing chart
chart->addSeries(series);
chartview->setRenderHint(QPainter::Antialiasing);
window.setCentralWidget(chartview);
window.show();
}
main.cpp
#include <iostream>
#include "tools.h"
#include <vector>
#include <map>
using namespace std;
int main() {
vector<long> gc_data = {};
string genome_file = "<path_to_file>";
GC_skew(gc_data, genome_file);
skew_chart(gc_data);
}
The code compiles without an error but when running it terminates with exit code 11 (whatever that means). However, upon debugging I figured there is a problem with the creation of a new QChart object where I keep getting the following exception as stated by the debugger:
Exception = error: use of undeclared identifier 'Exception' //occurs somewhere before the one below but does not effect the application
Exception = EXC_BAD_ACCESS (code=1, address=0x0) //causes the application to crash
Debug information
The way of initialization above is exactly the way given by Qt (https://doc.qt.io/qt-5/qtcharts-linechart-example.html) additional research in their resources did not lead to any solution. Does anybody of you might know what to do?
P.S.: by stepping through the lines one by one I discovered a jump upon initialization into a header which seems to define an exception which is raised at whatever event
qtflags.h (part of Qt lib)
Q_DECL_CONSTEXPR inline QFlags(Zero = Q_NULLPTR) Q_DECL_NOTHROW : i(0) {}
however the stack trace of the debugger indicates a successful object creation.
stack trace of debugger
The Qt version is 5.11.2
Compiler version Apple LLVM version 10.0.0 (clang-1000.11.45.2)
Thanks in advance if anybody can make sense of this
You dont have any QApplication instance.
Here's a quote from Qt Docs:
The QApplication class manages the GUI application's control flow and
main settings.
QApplication contains the main event loop, where all events from the
window system and other sources are processed and dispatched. It also
handles the application's initialization, finalization, and provides
session management. In addition, QApplication handles most of the
system-wide and application-wide settings.
For any GUI application using Qt, there is precisely one QApplication
object, no matter whether the application has 0, 1, 2 or more windows
at any given time. For non-GUI Qt applications, use QCoreApplication
instead, as it does not depend on the QtGui library.
The QApplication object is accessible through the instance() function
that returns a pointer equivalent to the global qApp pointer.
So, the line
QApplication app(argc, argv);
creates an instance of the QApplication class.
In the end you need to call app.exec() which enters the main event loop and waits until exit() is called, then returns the value that was set to exit() (which is 0 if exit() is called via quit()).
It is necessary to call this function to start event handling. The main event loop receives events from the window system and dispatches these to the application widgets.
Overview
I am trying to develop a C++ application which allows for user-created plugins.
I found a nice library called Pluma (http://pluma-framework.sourceforge.net/) which functionally seems to be exactly what I want.
After going through their tutorial, I was able to (with a bit of difficulty) convince the plugin to compile. However, it refuses to play nice and connect with the main program; returning various errors depending on how I try to implement them.
Problem
If I comment out the line labeled 'Main problem line' (in the last file, main.cpp), the plugin compiles successfully, and the main app can recognize it, but it says that "Nothing registered by plugin 'libRNCypher'", and none of the functions can be called.
If I compile that line, the main application instead says "Failed to load library 'Plugins/libRNCypher.so'. OS returned error: 'Plugins/libRNCypher.so: undefined symbol: _ZTIN5pluma8ProviderE".
My guess is that it has something to do with the way the plugin was compiled, as compiling it initially did not work and Code::Blocks told me to compile with "-fPIC" as a flag (doing so made it compile).
Code
Code below:
Main.cpp
#include "Pluma/Pluma.hpp"
#include "CryptoBase.h"
int main()
{
pluma::Pluma manager;
manager.acceptProviderType< CryptoBaseProvider >();
manager.loadFromFolder("Plugins", true);
std::vector<CryptoBaseProvider*> providers;
manager.getProviders(providers);
return 0;
}
CryptoBase.h
#ifndef CRYPTOBASE_H_INCLUDED
#define CRYPTOBASE_H_INCLUDED
#include "Pluma/Pluma.hpp"
#include <string>
#include <vector>
#include <bitset>
//Base class from which all crypto plug-ins will derive
class CryptoBase
{
public:
CryptoBase();
~CryptoBase();
virtual std::string GetCypherName() const = 0;
virtual std::vector<std::string> GetCryptoRecApps() const = 0;
virtual void HandleData(std::vector< std::bitset<8> > _data) const = 0;
};
PLUMA_PROVIDER_HEADER(CryptoBase)
#endif // CRYPTOBASE_H_INCLUDED
RNCypher.h (This is part of the plugin)
#ifndef RNCYPHER_H_INCLUDED
#define RNCYPHER_H_INCLUDED
#include <string>
#include <vector>
#include <bitset>
#include "../Encoder/Pluma/Pluma.hpp"
#include "../Encoder/CryptoBase.h"
class RNCypher : public CryptoBase
{
public:
std::string GetCypherName() const
{
return "RNCypher";
}
std::vector<std::string> GetCryptoRecApps() const
{
std::vector<std::string> vec;
vec.push_back("Storage");
return vec;
}
void HandleData(std::vector< std::bitset<8> > _data) const
{
char letter = 'v';
_data.clear();
_data.push_back(std::bitset<8>(letter));
return;
}
};
PLUMA_INHERIT_PROVIDER(RNCypher, CryptoBase);
#endif // RNCYPHER_H_INCLUDED
main.cpp (This is part of the plugin)
#include "../Encoder/Pluma/Connector.hpp"
#include "RNCypher.h"
PLUMA_CONNECTOR
bool connect(pluma::Host& host)
{
host.add( new RNCypherProvider() ); //<- Main problem line
return true;
}
Additional Details
I'm compiling on Ubuntu 16.04, using Code::Blocks 16.01.
The second error message seems to not come from Pluma itself, but a file I also had to link, #include <dlfcn.h> (which might be a Linux file?).
I would prefer to use an existing library rather than write my own code as I would like this to be cross-platform. I am, however, open to any suggestions.
Sorry for all of the code, but I believe this is enough to reproduce the error that I am having.
Thank You
Thank you for taking the time to read this, and thank you in advance for your help!
All the best, and happy holidays!
I was not able to reproduce your problem, however looking at
http://pluma-framework.sourceforge.net/documentation/index.htm,
I've noticed that:
in your RNCypher.h file you miss something like
PLUMA_INHERIT_PROVIDER(RNCypher, CryptoBase)
it seems also that there's no file CryptoBase.cpp containing something like
#include "CryptoBase.h"
PLUMA_PROVIDER_SOURCE(CryptoBase, 1, 1);
finally, in CryptoBase.h I would declare a virtual destructor (see Why should I declare a virtual destructor for an abstract class in C++?) and provide a definition to it, while you should not declare a default constructor without providing a definition to it (see for instance Is it correct to use declaration only for empty private constructors in C++?); of course the last consideration is valid unless there's another file in which you have provided such definitions.
I am using Google test framework for C++. Each file includes a config.hpp which defined a global configuration variable. I would like to define my config in a variable, not a compile-time const or constexpr. How can I define the dependencies to have the same variable in different files that are linked together? Do I have to use a singleton? Can I avoid that? Is there a better recommended way to use multiple test files xUnit style?
My config file: config.hpp:
#pragma once
struct {
const float tolerance = 0.001;
// ...
} CONFIG_VAR;
Each test *.cpp source file is like:
#include "gtest/gtest.h"
#include "core/config.hpp"
TEST(a, b) { ... }
My main file:
#include "gtest/gtest.h"
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
I compile and link using:
em++ -I $GTEST_ROOT/googletest/include main_all_tests.cpp test_*.cpp
PS. My problem is multiple definition of the variable CONFIG_VAR.
My solution is based on a related question.
Everything you need is right here at the Google Test's official repository on GitHub.
Anyway, to sharing something in the same file test you do it like that:
class YourTestCase : public ::testing::Test
{
protected:
virtual void SetUp()
{
globalObject = new YourObject();
}
virtual void TearDown() {
delete globalObject;
globalObject = nullptr;
}
Object * globalObject = nullptr;
};
so, in your test cases:
TEST_F(YourTestCase, TestOne) {
ASSERT_EQ("your value here", globalObject->getValue());
}
TEST_F(YourTestCase, TestTwo) {
ASSERT_EQ("your value here", globalObject->getValue());
}
TEST_F(YourTestCase, TestThree) {
ASSERT_EQ("your value here", globalObject->getValue());
}
Note.: Pay attention to the function's name. It is TEST_F not TEST.
On the other hand, if what you want to do it is at the test program level ― sharing something among files, you will need to set up an environment object. Something like this:
Environment * AddGlobalTestEnvironment(Environment * env);
I have never worked with that before, so I can not tell you so much about it, but there is more information at that link I shared above. Usually, global variables make the code harder to read and may cause problems. You'd be better off avoiding them.