I'm learning my way around SDL and everything's been working fine up until now. Whenever I try to compile my code, I get the following error:
Undefined symbols for architecture x86_64:
"Tile::draw(SDL_Surface*)", referenced from:
_SDL_main in ccTWWnIW.o
"Tile::update()", referencedfrom:
_SDL_main in ccTWWnIW.o
"Tile::Tile(int)", referenced from:
_SDL_main in ccTWWnIW.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status
What's strange about it is that it only occurs when I include the "level.h" header in my "player.cpp" bit of code.. while including "level.h" in my main program doesn't set off the compiler at all. I make use of the class defined in "level.h" in both the main program - to instantiate the tiles, update them and blit them to the screen - and in "player.cpp" - to check for collisions. I commented out all the parts of "player.cpp" that made use of any components defined in "level.h" and I still got the compiler errors. I've included the same SDL headers as in "player.cpp" and have set the flags accordingly on my compiler so I really don't see why the "ld: symbol(s) not found for architecture x86_64" message is cropping up.
The bits of "level.cpp" that are referenced in the error message:
#include "SDL.h"
#include "SDL_image.h"
#include <iostream>
#include "Variables.h"
#include <cstdlib>
#include "level.h"
class Tile{
public:
int damage;
int velocity;
bool solid;
SDL_Rect position;
SDL_Rect crop;
SDL_Surface* image;
Tile(int type);
~Tile();
bool update();
void draw(SDL_Surface* target);
};
...
Tile::Tile(int type){
if(type == 0){
damage = 0;
velocity = 20;
solid = true;
position.x = 600;
position.y = rand() % 500;
SDL_Surface* temp_image = IMG_Load_RW(SDL_RWFromFile("spritesheet.png", "rb"), 1);
image = SDL_DisplayFormatAlpha(temp_image);
SDL_FreeSurface(temp_image);
crop.x = 0;
crop.y = 0;
crop.w = 50;
crop.h = 50;
}
}
...
bool Tile::update(){
position.x = position.x - velocity;
if(position.x <= 0) {
position.x = 700;
position.y = rand()%500;
}
else return 0;
}
...
void Tile::draw(SDL_Surface* target){
SDL_BlitSurface(image, &crop, target, &position);
}
I know that the coding style is rubbish, this is only me messing about and learning SDL.
I'm not sure what the exact issue is based on this, but there are a few things you should do.
Put header guards around your headers, eg. in level.h, you would want something like:
#ifndef LEVEL_H_
#define LEVEL_H_
at the top, and:
#endif
at the bottom. This is to prevent symbols from being defined multiple times when the file is included by several different source files.
Second, you need to put class definitions into header (.h) files not .cpp files. It would probably be much nicer in your case to move the tile stuff into its own files (tile.h, tile.cpp) but either way the tile class definition needs to be in a header file.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
So, i was trying to make a 2d game with opengl and sfml,
so i created a button class in an input namespace,
i made a render() function in it, but when i call it (no matter wheter i use a pointer or i don't) even if I pass all the required arguments it's still giving
me an error, saying that I'm trying to access a deleted function
here's the Button header:
#pragma once
#include <SFML/Graphics.hpp>
#include "InputManager.h"
namespace input {
class Button {
private:
bool m_Selected, m_Clicked;
sf::Vector2f m_Position;
sf::Sprite m_Sprite;
sf::Texture m_Texture;
public:
Button(sf::Vector2f position, sf::Sprite sprite);
Button(sf::Vector2f position, sf::Texture texture);
Button(sf::Vector2f position);
Button();
~Button();
void update(engine::InputManager inputManager);
void render(sf::RenderWindow window);
inline bool isClicked() { return m_Clicked; }
inline bool isSelected() { return m_Selected; }
inline sf::Vector2f getPosition() { return m_Position; }
void setPosition(sf::Vector2f position) { m_Position = position; }
};
}
here is Button.cpp:
#include "Button.h"
namespace input {
Button::Button(sf::Vector2f position, sf::Sprite texture) : m_Position(position), m_Sprite(texture) {
m_Sprite.setPosition(m_Position);
}
Button::Button(sf::Vector2f position, sf::Texture texture) : m_Position(position), m_Texture(texture) {
m_Sprite.setTexture(m_Texture);
m_Sprite.setPosition(m_Position);
}
Button::Button(sf::Vector2f position) : m_Position(position) {
m_Sprite.setPosition(m_Position);
}
void Button::update(engine::InputManager inputManager) {}
void Button::render(sf::RenderWindow window) {
window.draw(m_Sprite);
}
Button::~Button() {
delete &m_Position;
delete &m_Texture;
delete &m_Clicked;
delete &m_Selected;
}
}
Here is the main.cpp code:
#include <iostream>
#include <vector>
#include "InputManager.h"
#include "Button.h"
#define WIDTH 800
#define HEIGHT 600
#define TITLE "C++ Platformer"
int main() {
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), TITLE, sf::Style::Close | sf::Style::Titlebar);
sf::Texture ButtonTexture;
ButtonTexture.loadFromFile("Texture.png");
sf::Sprite sprite;
sprite.setTexture(ButtonTexture);
input::Button* button = new input::Button(sf::Vector2f(100.f, 100.f), sprite);
int fps = 0;
int ticks = 0;
sf::Clock clock;
sf::Clock FpsClock;
sf::Time time;
sf::Time Fpstime;
long delta = 0;
long target = 1000000 / 60;
engine::InputManager IManager;
sf::Event ev;
while (window.isOpen()) {
while (window.pollEvent(ev)) {
if (ev.type == sf::Event::Closed) window.close();
if (ev.key.code == sf::Keyboard::Escape) window.close();
if (ev.type == sf::Event::KeyPressed) IManager.onKeyDown(ev.key.code);
}
time = clock.getElapsedTime();
if (target > time.asMicroseconds()) {
clock.restart();
// UPDATE
button->update(IManager);
delta = time.asMicroseconds() % 60;
target = 1000000 / 60 + delta;
ticks++;
} Fpstime = FpsClock.getElapsedTime();
if (Fpstime.asSeconds() >= 1) {
std::cout << "FPS: " << fps << " Ticks: " << ticks << std::endl;
FpsClock.restart();
fps = 0;
}
fps++;
// RENDER
button->render(window);
}
return EXIT_SUCCESS;
}
I've searched on microsoft docs and on other stackoverflow questions, but i couldn't find any case like mine, hope someone can help, thanks
The problem is that sf::RenderWindow is non-copyable. This is a good thing, because it means that you can't accidentally get confused about having multiple windows, and it's clear when you create a new window and where it is owned. However, in a function signature like
void Button::render(sf::RenderWindow window);
you are accepting an sf::RenderWindow by value. This means that whenever you call Button::render and pass a window, that window is copied before it is received by the function. See the problem?
You need to accept the render window by reference, to make sure you don't try to create a copy:
void Button::render(sf::RenderWindow& window);
Aside: as pointed out by NathanOliver, you try to delete all of your instance members in your Button destructor, even though they are not pointers, and you didn't specifically allocate them with new. Only delete what you new, and avoid new/delete altogether if you can.
If you're unsure about what it means to pass by reference, or what new and delete are for, I would suggest you pick up a copy of a good C++ book.
First of all. what the...
Button::~Button() {
delete &m_Position;
delete &m_Texture;
delete &m_Clicked;
delete &m_Selected;
}
You shouldn't do that.
But back to your error. It is quite descriptive. You are trying to use a function that was most likely deleted explicitly. Like so:
struct Test {
Test(const Test & other) = delete;
};
Obviously I cannot copy Test now because I deleted the copy constructor.
Which is what sf::RenderWindow does by inheriting from sf::NonCopyable class.
And since your render() method takes it by copy and not reference. It obviously tries to do something it was forbidden.
You should change from this:
void Button::render(sf::RenderWindow window)
To this:
void Button::render(sf::RenderWindow & window)
I am very new to using OpenGL. The program I am trying to run is provided by my professor so I have not actually written any of it, I am having problems getting the program to run. The program is suppose to just make a white square on a black screen. I am using mac Sierra 10.12.2. Also I have already changed the deployment target to 10.8 because of the errors from compiling in anything later than that. Now when I try to build and run in xcode I get 2 errors.
These are the errors im getting,
Undefined symbols for architecture x86_64:
"exit(int)", referenced from:
myKeyboard(unsigned char, int, int) in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Now here is the code exactly as I am trying to compile it.
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <OpenGL/OpenGL.h>
#include <GLUT/glut.h>
const int screenHeight = 480; // window height is 480
const int screenWidth = 640 ; //window width is 640
// <<<<<<<<<<<<<<<<<<<<< Prototypes >>>>>>>>>>>>>>>>>>
void exit(int) ;
// <<<<<<<<<<<<<<<<<<<<<<< myInit >>>>>>>>>>>>>>>>>>>
void myInit(void)
{
glClearColor(1.0,1.0,1.0,0.0); // set white background color
glColor3f(0.0f, 0.0f, 0.0f); // set the drawing color
glPointSize(4.0); // a ?dot? is 4 by 4 pixels
glLineWidth(4.0); // a ?dot? is 4 by 4 pixels
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 640.0, 0.0, 480.0);
}
// <<<<<<<<<<<<<<<<<<<<<<<< myDisplay >>>>>>>>>>>>>>>>>
void myDisplay(void)
{
glClear(GL_COLOR_BUFFER_BIT); // clear the screen
glBegin(GL_POINTS);
// glBegin(GL_LINE_STRIP) ;
// glBegin(GL_LINE_LOOP) ;
// glBegin(GL_POLYGON);
glVertex2i(289, 190); // Dubhe
glVertex2i(320, 128) ; // Merak
glVertex2i(239, 67) ; // Phecda
glVertex2i(194, 101) ; // Megrez
glVertex2i(129, 83) ; // Alioth
glVertex2i(75, 73) ; // Mizar
glVertex2i(74, 74) ; // Alcor
glEnd();
glFlush(); // send all output to display
}
// <<<<<<<<<<<<<<<<<<<<<<<< myKeyboard >>>>>>>>>>>>>>
void myKeyboard(unsigned char theKey, int mouseX, int mouseY)
{
switch(theKey)
{
case 'Q':
case 'q':
exit(-1); //terminate the program
default:
break; // do nothing
}
}
// <<<<<<<<<<<<<<<<<<<<<<<< main >>>>>>>>>>>>>>>>>>>>>>
int main(int argc, char** argv)
{
glutInit(&argc, argv); // initialize the toolkit
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // set display mode
glutInitWindowSize(640, 480); // set window size
glutInitWindowPosition(100, 150); // set window position on screen
glutCreateWindow("Big Deep - Type Q or q to quit") ; // open the screen window
glutDisplayFunc(myDisplay); // register redraw function
glutKeyboardFunc(myKeyboard); // register the keyboard action function
myInit();
glutMainLoop(); // go into a perpetual loop
}
Any help would be greatly appreciated!
You need to add the following near the top of your source file:
#include <stdlib.h>
and remove this line:
void exit(int) ;
First, you should always use the proper system headers to get the declarations of system library functions. This is especially true on macOS where the declaration can have important attributes which affect how the function is linked.
However, the lack of such attributes is not really what tripped you up in this case. The issue here is that you're building a C++ program. In C++, a function's argument types are part of its symbol name. You can see this in the error message you've quoted. But the exit() function is part of the C standard library. It's not natively a C++ interface. Its symbol name is _exit, with no indication of its argument count or types.
Your code has incorporated references to a symbol that translates to exit(int) while the actual symbol in the system library is just _exit. They don't match, so you get a symbol-not-found linker error.
The stdlib.h header takes special care to wrap its function declarations in extern "C" { ... } when it's included for a C++ translation unit. So, including that header to get the declaration tells the C++ compiler not to use a C++-style symbol name but to instead just use the C-style symbol name.
You could also "solve" the issue by putting extern "C" on the declaration of exit() in your own code, but that's the wrong approach. Just include the proper header.
I'm pretty new to C++ so please go easy on me.
I'm trying to use sfml to create a RenderWindow. Then, on creation of a player, that player's associated "window" object gets set to the RenderWindow created previously. My purpose is to be able to run window methods, such as window.draw(), from the player object, i.e.:
player::drawSprite() {
window.draw(sprite);
}
However, I run into the error:
error: use of deleted function ‘sf::RenderWindow& sf::RenderWindow::operator=(const sf::RenderWindow&)’
window = win;
^
Further down in the error log, I also see:
error: initializing argument 1 of ‘Player::Player(sf::RenderWindow)’
Player(sf::RenderWindow win)
^
My code (with anything not pertinent to the question omitted) is as follows:
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <cstring>
#include <cstdlib>
#include <iostream>
class Player
{
private:
float x;
float y;
float speed;
sf::RenderWindow window;
public:
Player(sf::RenderWindow win)
{
x = 640;
y = 360;
speed = 5;
window = win;
}
};
int main()
{
//Window Initialization
sf::RenderWindow window(sf::VideoMode(800, 600), "My window");
//Player Creation
Player player(window);
}
I believe the problem may have something to do with const's, references, or the like, but I am not familiar enough with C++ to easily identify it. How can I remedy this issue?
You should use reference or pointer to window object as I don't think that you will want each player to have its own window.
Thus your Player should look like:
class Player
{
private:
float x;
float y;
float speed;
sf::RenderWindow& window; // reference
public:
Player(sf::RenderWindow& win) // accepts reference
: window(win) // stores reference
{
x = 640;
y = 360;
speed = 5;
// window = win;
}
};
This is the source file where the function setLocation() is called (that includes for the graphics header file should have angle brackets, but it disappeared, so I used quotes)
#include "SFML/Graphics.hpp"
#include "ship.h"
const int WINDOW_WIDTH = 500;
const int WINDOW_HEIGHT = 500;
//==============================================================================
int main()
{
sf::RenderWindow window( sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT),
"Delta Quadrant", sf::Style::Titlebar | sf::Style::Close);
window.setFramerateLimit(120);
// this causes loop to execute 120 times a second at most.
// (a delay is automatically added after screen is drawn)
Ship obj;
//ADD Code to set limits on ships location (call setMaxLocation);
//sets position of the ship in the middle of the screen
obj.setLocation(250, 250);
while (window.isOpen())
{
//----------------------------------------------------------
//handle user input (events and keyboard keys being pressed)
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed)
window.close();
}
//turn left with press of left button
while (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
obj.rotateLeft();
//turn right with press of right button
while (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
obj.rotateRight();
//apply thrust with press of up button
while (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
obj.applyThrust();
//----------------------------------------------------------
//draw new frame
window.clear();
//draw ship
obj.updateLocation();
obj.draw(window);
//redisplay window
window.display();
}
return 0;
}
This is setLocation()'s definition, in the ship source file (same issue with the angle brackets, used quotes again).
#include"cmath"
#include "SFML/Window.hpp"
#include "SFML/Graphics.hpp"
#include "vector.h"
#include "ship.h"
//Constants
const double PI = 3.14159;
const double THRUST = 0.005;
const double TURN_SPEED = 1;
//constructor
Ship::Ship(){
maxLocations.x = 500;
maxLocations.y = 500;
radius = 5;
location.x = 0;
location.y = 0;
velocity.x = 0;
velocity.y = 0;
angleDeg = 5;
}
void Ship::setLocation(double x, double y){
//Check and correct for the ship going out of bounds.
if (x < 0)
location.x = 0;
else if (x > maxLocations.x)
location.x -= maxLocations.x;
else
location.x = x;
if (y < 0)
location.y = 0;
else if (y > maxLocations.y)
location.y -= maxLocations.y;
else
location.y = y;
}
void Ship::updateLocation(){
location.x += velocity.x;
location.y -= velocity.y;
//Check and correct for the ship going out of bounds.
if (location.x < 0)
location.x = 0;
else if (location.x > maxLocations.x)
location.x -= maxLocations.x;
if (location.y < 0)
location.y = 0;
else if (location.y > maxLocations.y)
location.y -= maxLocations.y;
}
void Ship::draw(sf::RenderWindow& win) {
//Initializes the Ship class to an object
Ship obj;
// draw ship
sf::ConvexShape ship;
ship.setPointCount(3);
ship.setPoint(0, sf::Vector2f(10, 0));
ship.setPoint(1, sf::Vector2f(0, 25));
ship.setPoint(2, sf::Vector2f(20, 25));
sf::Vector2f midpoint(10,15);
ship.setOrigin(midpoint);
ship.setFillColor(sf::Color(0, 0, 0));
ship.setOutlineThickness(1);
ship.setOutlineColor(sf::Color(255, 255, 255));
ship.setPosition(obj.getLocation().x, obj.getLocation().y);
obj.setAngle(obj.getAngle());
win.draw(ship);
}
}
Finally this is the header file where the setLocation() prototype is located
#ifndef SHIP_H
#define SHIP_H
#include "vector.h"
class Ship {
private:
Vector maxLocations; //maximum allowable values for location
Vector location; //current location (x,y)
Vector velocity; //current velocity (in pixels/frame)
double angleDeg; //angle ship is facing, in degrees
double radius; //gross radius of ship (for collision detection)
public:
//constructor
Ship();
//=============================================
//mutators
void setLocation(double x, double y);
void updateLocation();
void draw(sf::RenderWindow& win);
...
};
#endif
My issue is when I call setLocation() it doesn't change the location vector (as defined in the second source file) from (0, 0). When compiled the ship remains at (0, 0). What I want to happen is when I call setLocation(), the ship needs to change from (0, 0) to (250, 250), thus the ship on compiling starts in the middle of the screen, not a corner.
The problem at hand is you are not using the Ship object that you believe you are using. What I mean by that is you aren't drawing the ship object in your main function, instead you are creating a temporary ship object in your Ship::draw() method and then using that ships position to draw your shape. So the position will always be (0, 0) because your temp ship object get's initialized every frame with the position (0, 0) and then you use that temporary ship's position to draw your shape.
To make this more clear let's look at your code.
void Ship::draw(sf::RenderWindow& win) {
// This is a temporary object and is not the same as the one you defined in your
// main.cpp file. When this object is constructed it's location is (0, 0) by
// default.
Ship obj;
// draw ship
sf::ConvexShape ship;
ship.setPointCount(3);
ship.setPoint(0, sf::Vector2f(10, 0));
ship.setPoint(1, sf::Vector2f(0, 25));
ship.setPoint(2, sf::Vector2f(20, 25));
sf::Vector2f midpoint(10,15);
ship.setOrigin(midpoint);
ship.setFillColor(sf::Color(0, 0, 0));
ship.setOutlineThickness(1);
ship.setOutlineColor(sf::Color(255, 255, 255));
// Here we are setting the shapes position to the position of the temporary
// Ship object that you defined above. Remember that it's default location is
// (0, 0) when constructed and you never changed it's location in the draw
// method.
ship.setPosition(obj.getLocation().x, obj.getLocation().y);
obj.setAngle(obj.getAngle());
win.draw(ship);
}
Now how do you fix this? That is easy. All you need to do is get rid of that temporary ship object in your draw method and instead use your location vector to set the position of the shape. Like this.
ship.setPosition(location.x, location.y);
It might be beneficial for your to study up on how objects and classes work in C++ before continuing on. It seems you might not understand exactly how they work and how different scopes of code work. But I will leave that for your to decide what to do.
Though I would like to point out a few other pointers for you.
1 - In your draw method you are updating the position of objects. This is generally a bad thing to do and you should avoid it. All updating code like when you change a objects position should happen in a update function/method. The drawing methods of entities should only have drawing code in them. The reason for this is because usually drawing and updating happen at different time steps in the main game loop.
2 - Your updateLocation method won't work like you think it will. The reason is because you are doing location.y -= velocity.y; when in reality you should be adding the velocity of y to location just like you did with x.
3 - I would advise against using sfml's setFrameRateLimit() method and instead use your own timestep which will be much more accurate (SFML's version uses sf::Sleep which is notorious for being inaccurate) and give you much more control over your time step. If you don't know about time steps I would highly suggest reading this great article on it. It will save you hours of headaches. http://gafferongames.com/game-physics/fix-your-timestep/
4 - Let's take a look at this line.
else if (x > maxLocations.x)
location.x -= maxLocations.x;
Now let's say that maxLocations.x = 500 like in your example and let's also say that location.x = 550. Do you really want the location to be x = 50? Wouldn't a more logical step be to set x to x = maxLocations.x? Not sure if this is what you wanted but thought I would point it out.
Hopefully this will help you a bit and I would always be glad to answer any other questions you might have. Wish you the best of luck with your project.
QR in Qt
As a companion question to How to scan for QR codes with Qt, I want to know how to draw a QR code from native C/C++ code in my Qt5 based desktop app, but I could not find an example of how to do this.
I know QtQR exists, but it has dependencies on python-qrtools which in my opinion kind of defeats the purpose of using Qt in the first place. I want a nimble, efficient and dependency-free solution that will compile with my app wherever I decided to take it.
How can I do that?
UPDATE 3/3-2016: It has come to my attention that there is a small library project that does what my answer does but in a more "prepackaged" way. You can check it out here.
QR in Qt
There is a small QR-code generator library in pure C and without dependencies, called libqrencode.
Step 1: Install
Before you can use it, you will have to install it. On my Ubuntu 13.10 that meant typing the following in a shell:
sudo aptitude install libqrencode-dev
On other platforms you may have to build it from source by yourself. Simply download the tarball and follow the instructions from the source code download.
Step 2: Project file
Next, you will have to add the library to your project. In my Qt5.2.0 project file (myproject.pro or similar) that meant appending the following line:
LIBS += -lqrencode
This should be similar for most versions of Qt that I know.
Step 3: encode
Next one must write the code that actually uses the library to encode some input string to QR format. That is one line of code:
QRcode *qr=QRcode_encodeString("my string", 1, QR_ECLEVEL_L, QR_MODE_8,0);
NOTE: After experimenting with the parameters I have passed to this function, I have learned that one needs to be careful. Some combinations of parameters failed for no good reason. For example passing 0 as version or using QR_MODE_AN failed with "Invalid parameters". This might be bugs in the ancient version of the library that I am using You have been warned.
Step 4: render image
Finally, before cleaning up, you need to convert the output to bitmap so that it can be rendered on the screen. This is simpler than it sounds. Instead of listing a bunch of assumptions I will instead included my complete working minimalistic QRWidget implementation here. The interesting bits are in the overridden paintEvent() method.
QRWidget.hpp
#ifndef QRWIDGET_HPP
#define QRWIDGET_HPP
#include <QWidget>
class QRWidget : public QWidget{
Q_OBJECT
private:
QString data;
public:
explicit QRWidget(QWidget *parent = 0);
void setQRData(QString data);
protected:
void paintEvent(QPaintEvent *);
};
#endif // QRWIDGET_HPP
QRWidget.cpp
#include "QRWidget.hpp"
#include <QPainter>
#include <QDebug>
#include <qrencode.h>
QRWidget::QRWidget(QWidget *parent) :
QWidget(parent),
data("Hello QR")//Note: The encoding fails with empty string so I just default to something else. Use the setQRData() call to change this.
{
}
void QRWidget::setQRData(QString data){
this->data=data;
update();
}
void QRWidget::paintEvent(QPaintEvent *pe){
QPainter painter(this);
//NOTE: I have hardcoded some parameters here that would make more sense as variables.
QRcode *qr = QRcode_encodeString(data.toStdString().c_str(), 1, QR_ECLEVEL_L, QR_MODE_8, 0);
if(0!=qr){
QColor fg("black");
QColor bg("white");
painter.setBrush(bg);
painter.setPen(Qt::NoPen);
painter.drawRect(0,0,width(),height());
painter.setBrush(fg);
const int s=qr->width>0?qr->width:1;
const double w=width();
const double h=height();
const double aspect=w/h;
const double scale=((aspect>1.0)?h:w)/s;
for(int y=0;y<s;y++){
const int yy=y*s;
for(int x=0;x<s;x++){
const int xx=yy+x;
const unsigned char b=qr->data[xx];
if(b &0x01){
const double rx1=x*scale, ry1=y*scale;
QRectF r(rx1, ry1, scale, scale);
painter.drawRects(&r,1);
}
}
}
QRcode_free(qr);
}
else{
QColor error("red");
painter.setBrush(error);
painter.drawRect(0,0,width(),height());
qDebug()<<"QR FAIL: "<< strerror(errno);
}
qr=0;
}
Summary
In this little post I have summarized my experience with getting a QR code generator working with Qt.
If you feel that Fukuchi's library is too large[0] for you, consider looking at Nayuki's C++ QR Code generator library[1]: https://github.com/nayuki/QR-Code-generator/tree/master/cpp
Nayuki's library requires C++11, and is portable without needing Autotools. Sample usage:
#include <string>
#include <vector>
#include "QrCode.hpp"
using namespace qrcodegen;
// Create the QR Code object
QrCode qr = QrCode::encodeText("Hello, world!", QrCode::Ecc::MEDIUM);
// Read the black & white pixels
for (int y = 0; y < qr.size; y++) {
for (int x = 0; x < qr.size; x++) {
int color = qr.getModule(x, y); // 0 for white, 1 for black
// You need to modify this part
draw_pixel_onto_QT(x, y, color);
}
}
[0]: Fukuchi: 20 files, ~7200 lines among the main .c and .h files (excluding build and test code).
[1]: Nayuki: 6 files, ~1400 lines among the main .cpp and .hpp files (excluding demo code).
EDIT 2016-12-08 by OP
I decided, with permission, to add my own adaption to Qt. This code compiles and runs fine on my system, And I think it should be independent enough to work elsewhere without too many tweaks as well.
#include "QrCode.hpp"
void paintQR(QPainter &painter, const QSize sz, const QString &data, QColor fg)
{
// NOTE: At this point you will use the API to get the encoding and format you want, instead of my hardcoded stuff:
qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(data.toUtf8().constData(), qrcodegen::QrCode::Ecc::LOW);
const int s=qr.getSize()>0?qr.getSize():1;
const double w=sz.width();
const double h=sz.height();
const double aspect=w/h;
const double size=((aspect>1.0)?h:w);
const double scale=size/(s+2);
// NOTE: For performance reasons my implementation only draws the foreground parts in supplied color.
// It expects background to be prepared already (in white or whatever is preferred).
painter.setPen(Qt::NoPen);
painter.setBrush(fg);
for(int y=0; y<s; y++) {
for(int x=0; x<s; x++) {
const int color=qr.getModule(x, y); // 0 for white, 1 for black
if(0!=color) {
const double rx1=(x+1)*scale, ry1=(y+1)*scale;
QRectF r(rx1, ry1, scale, scale);
painter.drawRects(&r,1);
}
}
}
}
For usage, please see this painter class.
The following is Qt code I used to update a label (qrCode) on a form with the QR code for "text". The label is set to a fixed size (min and max width and height=256 in my case), and scaledContents true. You might do something more efficient than the RGB32 format, but it really shouldn't matter for occasional updates.
void MyClass::updateQrCode( QString text )
{ using namespace qrcodegen;
// Create the QR Code object
QrCode qr = QrCode::encodeText( text.toUtf8().data(), QrCode::Ecc::MEDIUM );
qint32 sz = qr.getSize();
QImage im(sz,sz, QImage::Format_RGB32);
QRgb black = qRgb( 0, 0, 0);
QRgb white = qRgb(255,255,255);
for (int y = 0; y < sz; y++)
for (int x = 0; x < sz; x++)
im.setPixel(x,y,qr.getModule(x, y) ? black : white );
ui->qrCode->setPixmap( QPixmap::fromImage(im.scaled(256,256,Qt::KeepAspectRatio,Qt::FastTransformation),Qt::MonoOnly) );
}
using the Nayuki library - just the QrCode.cpp and .hpp files from here: https://github.com/nayuki/QR-Code-generator/tree/master/cpp
incorporated in my project with this trivial .pri file (kept in the same folder as the QrCode files):
INCLUDEPATH += $$PWD
DEPENDPATH += $$PWD
HEADERS += $$PWD/QrCode.hpp
SOURCES += $$PWD/QrCode.cpp