I'm trying to use Qt's animation framework. I'm using some sample code from Qt's website, but it's not working, error: setGeometryDp: Unable to set geometry 100x30+0+0 on QWidgetWindow/'QPushButtonClassWindow'. Resulting geometry: 120x30+0+0 (frame: 8, 31, 8, 8, custom margin: 0, 0, 0, 0, minimum size: 0x0, maximum size: 16777215x16777215).
QPushButton button("Animated Button");
button.setGeometry(0, 0, 100, 30);
button.show();
QPropertyAnimation animation(&button, "geometry");
animation.setDuration(10000);
animation.setStartValue(QRect(0, 0, 100, 30));
animation.setEndValue(QRect(250, 250, 100, 30));
animation.start();
I have no idea what I'm doing wrong.
The objects go out of scope
This works:
QPushButton *button = new QPushButton();
button->setGeometry(0, 0, 100, 30);
button->show();
QPropertyAnimation *animation = new QPropertyAnimation(button, "geometry");
animation->setDuration(10000);
animation->setStartValue(QRect(0, 0, 100, 30));
animation->setEndValue(QRect(250, 250, 100, 30));
animation->start();
Edit:
My final solution
QPushButton *button = new QPushButton(this);
button->setGeometry(0, 0, 100, 30);
button->show();
QPropertyAnimation *animation = new QPropertyAnimation(button, "geometry");
animation->setDuration(10000);
animation->setStartValue(QRect(0, 0, 100, 30));
animation->setEndValue(QRect(250, 250, 100, 30));
connect(animation, SIGNAL(finished()), animation, SLOT(deleteLater()));
animation->start();
Related
I want to make QLabel with the image circle:
Code:
QLabel *label = new QLabel(this);
QPixmap avatarPixmap(":/Icon/default_avatar.png");
label->setPixmap(avatarPixmap);
label->setStyleSheet("border: 0.5px solid red; border-radius: 50%; background-clip: padding;");
It only rounds the QLabel, not the image. How to fix it? Thanks.
Update:
The only way is to override the paintEvent for QLabel
Code:
void AccountImage::paintEvent(QPaintEvent *event)
{
QPixmap pixmap(":/Icon/default_avatar.png");
QBrush brush(pixmap);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setBrush(brush);
painter.drawRoundedRect(0, 0, width(), height(), 100, 100);
QLabel::paintEvent(event);
}
The image is rounded but not properly scaled. Any ideas?
try to set mask on the label like:
int w = // set the width here
int h = // set the height here
QRect *rct = new QRect(0, 0, w, h);
QRegion *reg = new QRegion(*rct, QRegion::Ellipse);
label->setMask(*reg);
see: http://doc.qt.io/archives/qt-4.8/qwidget.html#setMask
The solution by overriding QLabel paintEvent method.
Code:
void AccountImage::paintEvent(QPaintEvent *event)
{
QPixmap pixmap(":/Icon/my_avatar.png");
QPixmap scaled = pixmap.scaled(width(), height(), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
QBrush brush(scaled);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setBrush(brush);
painter.drawRoundedRect(0, 0, width(), height(), 100, 100);
QLabel::paintEvent(event);
}
Result:
This is my code:
void MIDITest::CreateNoteBlock() {
IMidiMsgExt* midiMessage = new IMidiMsgExt;
midiMessage->MakemidiMessageMsg(57, 100, 0, 0, 0);
queuedNotes.insert(*midiMessage);
midiMessage = new IMidiMsgExt;
midiMessage->MakemidiMessageMsg(60, 100, 0, tickSize * 38, 0);
queuedNotes.insert(*midiMessage);
midiMessage = new IMidiMsgExt;
midiMessage->MakemidiMessageMsg(62, 100, 0, 0, 0);
queuedNotes.insert(*midiMessage);
midiMessage = new IMidiMsgExt;
midiMessage->MakemidiMessageMsg(65, 100, 0, tickSize * 32, 0);
queuedNotes.insert(*midiMessage);
midiMessage = new IMidiMsgExt;
midiMessage->MakemidiMessageMsg(57, 0, tickSize * 111, 0);
queuedNotes.insert(*midiMessage);
midiMessage = new IMidiMsgExt;
midiMessage->MakemidiMessageMsg(60, 0, tickSize * 111, 0);
queuedNotes.insert(*midiMessage);
midiMessage = new IMidiMsgExt;
midiMessage->MakemidiMessageMsg(62, 0, tickSize * 75, 0);
queuedNotes.insert(*midiMessage);
midiMessage = new IMidiMsgExt;
midiMessage->MakemidiMessageMsg(65, 0, tickSize * 105, 0);
queuedNotes.insert(*midiMessage);
}
so at every new operator it will allocate a block of memory.
Should I use free after any insert inside the queuedNotes? Or it will be released after void function return? (i.e. the brackets of CreateNoteBlock).
Or I can "reuse" each time the midiMessage pointer for a new IMidiMsgExt?
The answer is not to use new at all. Create a object with automatic storage duration
IMidiMsgExt midiMessage;
And then you can keep calling MakemidiMessageMsg and insert a copy of the message into the multiset.
midiMessage.MakemidiMessageMsg(57, 100, 0, 0, 0);
queuedNotes.insert(midiMessage);
midiMessage.MakemidiMessageMsg(60, 100, 0, tickSize * 38, 0);
queuedNotes.insert(midiMessage);
//...
Now the multiset has a copy of all of the messages and at the end of the function midiMessage is destroyed and no memory management needs to be done.
If IMidiMsgExt has a constructor that is like MakemidiMessageMsg where you can construct a complete message then you could boild this down even further and use something like
queuedNotes.insert(IMidiMsgExt(57, 100, 0, 0, 0));
queuedNotes.insert(IMidiMsgExt(60, 100, 0, tickSize * 38, 0));
And now we don't even need midiMessage.
Do you come from a Java or C# background? In C++ you don't have to use new to create objects, just declaring them will work fine:
IMidiMsgExt midiMessage;
midiMessage.MakemidiMessageMsg(57, 100, 0, 0, 0);
queuedNotes.insert(midiMessage);
It's either that (which is my recommended solution), or you have to explicitly free the objects:
IMidiMsgExt* midiMessage = new IMidiMsgExt;
midiMessage->MakemidiMessageMsg(57, 100, 0, 0, 0);
queuedNotes.insert(*midiMessage);
delete miniMessage;
It seem redundant to dynamically allocate IMidiMsgExt using new. You can just allocate it directly on the stack (no pointers), and then it will be destroyed when your method returns. I.e.:
void MIDITest::CreateNoteBlock() {
IMidiMsgExt midiMessage();
midiMessage.MakemidiMessageMsg(57, 100, 0, 0, 0);
queuedNotes.insert(midiMessage);
midiMessage = IMidiMsgExt();
midiMessage.MakemidiMessageMsg(60, 100, 0, tickSize * 38, 0);
queuedNotes.insert(midiMessage);
midiMessage = IMidiMsgExt();
midiMessage.MakemidiMessageMsg(62, 100, 0, 0, 0);
queuedNotes.insert(midiMessage);
midiMessage = IMidiMsgExt();
midiMessage.MakemidiMessageMsg(65, 100, 0, tickSize * 32, 0);
queuedNotes.insert(midiMessage);
midiMessage = IMidiMsgExt();
midiMessage.MakemidiMessageMsg(57, 0, tickSize * 111, 0);
queuedNotes.insert(midiMessage);
midiMessage = IMidiMsgExt();
midiMessage.MakemidiMessageMsg(60, 0, tickSize * 111, 0);
queuedNotes.insert(midiMessage);
midiMessage = IMidiMsgExt();
midiMessage.MakemidiMessageMsg(62, 0, tickSize * 75, 0);
queuedNotes.insert(midiMessage);
midiMessage = IMidiMsgExt();
midiMessage.MakemidiMessageMsg(65, 0, tickSize * 105, 0);
queuedNotes.insert(midiMessage);
}
Try to make your API more C++ like.
Use one object on the stack instead of creating a lot of new ones in the heap.
void MIDITest::CreateNoteBlock() {
IMidiMsgExt midiMessage;
midiMessage.MakemidiMessageMsg(57, 100, 0, 0, 0);
queuedNotes.insert(midiMessage);
midiMessage.MakemidiMessageMsg(60, 100, 0, tickSize * 38, 0);
queuedNotes.insert(midiMessage);
// ...
}
Initialize your objects in the constructor. Define an operator = (const IMidiMsgExt&) for IMidiMsgExt.
void MIDITest::CreateNoteBlock() {
IMidiMsgExt midiMessage(57, 100, 0, 0, 0);
queuedNotes.insert(midiMessage);
midiMessage = IMidiMsgExt(60, 100, 0, tickSize * 38, 0);
queuedNotes.insert(midiMessage);
// ...
}
I guess, that insert() expects a const IMidiMsgExt&. So you can directly pass freshly initialized objects:
void MIDITest::CreateNoteBlock() {
queuedNotes.insert({57, 100, 0, 0, 0});
queuedNotes.insert({60, 100, 0, tickSize * 38, 0});
// ...
}
BTW: you should prefer to use e.g. std::queue<> for queuedNotes. Then you'll not use insert(), but push(), or emplace(). The advantage of emplace() is, that it constructs the object in the container instead of first creating it and then copying it to the container:
void MIDITest::CreateNoteBlock() {
queuedNotes.emplace(57, 100, 0, 0, 0);
queuedNotes.emplace(60, 100, 0, tickSize * 38, 0);
// ...
}
Also your typename IMidiMsgExt signals to me, that you are trying to mimic the C# thinking in C++. It is possible, but usually it is not the preferred solution. From your question I don't know enough about your class tree and the underlying requirements to provide a suggestion, but in C++ that usually is a code smell.
i.e. if I specify some cubic lines from example specified in Qt5 tutorial:
QPainterPath path;
path.addRect(20, 20, 60, 60);
path.moveTo(0, 0);
path.cubicTo(99, 0, 50, 50, 99, 99);
path.cubicTo(0, 99, 50, 50, 0, 0);
QPainter painter(this);
painter.fillRect(0, 0, 100, 100, Qt::white);
painter.setPen(QPen(QColor(79, 106, 25), 1, Qt::SolidLine,
Qt::FlatCap, Qt::MiterJoin));
painter.setBrush(QColor(122, 163, 39));
painter.drawPath(path);
which constructs this set of curves
Now I'd like to render only a part of those curves on QImage specified by some region with starting point=[20px,50px] with width=80px and height=50px so resulting would look like this:
Or if it is possible, to render with 3x zoom, so resulting QImage would look the same but had size=[240px,150px]
I am new to Qt, so could someone please showed me a working code example?
You can transform the painter coordinate system:
QPainter painter(this);
painter.scale(3, 3); // zoom 3 times
painter.translate(-20, -50); // offset origin to 20x50
// ... render stuff
This has an advantage over the other answer, because it will be rendered as if you provided larger coordinates, instead of rendering it small and then enlarging the raster image, which will degrade image quality. Also, it is possible that Qt will optimize it to not render outside of the image, so it will render less, and you don't need to crop and throw results away.
Result:
Compare that to an upscaled raster:
I've got a code example for you. But it isn't that hard to find out how. You just have to read the documentation, all is easy to find. Qt's documentation is really great.
QApplication a(argc, argv);
QImage img(100, 100, QImage::Format_ARGB32);
QPainterPath path;
path.addRect(20, 20, 60, 60);
path.moveTo(0, 0);
path.cubicTo(99, 0, 50, 50, 99, 99);
path.cubicTo(0, 99, 50, 50, 0, 0);
QPainter painter(&img);
painter.fillRect(0, 0, 100, 100, Qt::white);
painter.setPen(QPen(QColor(79, 106, 25), 1, Qt::SolidLine,
Qt::FlatCap, Qt::MiterJoin));
painter.setBrush(QColor(122, 163, 39));
painter.drawPath(path);
painter.end();
QPixmap pixmap( QPixmap::fromImage(img).copy(20, 50, 80, 50).scaled(240,150) );
// option 1, use a QLabel ( only for simple cases )
QLabel label;
label.setPixmap( pixmap );
label.show();
// option 2, use a QGraphicsScene ( far more flexible )
QGraphicsView view;
QGraphicsScene scene;
scene.addPixmap( pixmap );
scene.setSceneRect( img.rect() );
view.setScene(&scene);
view.show();
return a.exec();
I'm trying to make an easy way to print X's and O's to the grid below without using multiple If-Else statements. I'm using a screen size of 575x620 for the application.
Rectangle(hdc, 5, 570, 550, 5);
Rectangle(hdc, 50, 50, 200, 200);
Rectangle(hdc, 200, 50, 350, 200);
Rectangle(hdc, 350, 50, 500, 200);
Rectangle(hdc, 50, 200, 200, 350);
Rectangle(hdc, 200, 200, 350, 350);
Rectangle(hdc, 350, 200, 500, 350);
Rectangle(hdc, 50, 350, 200, 500);
Rectangle(hdc, 200, 350, 350, 500);
Rectangle(hdc, 350, 350, 500, 500);
if( ttt.board[ 0 ][ 0 ] == 1 )
{
MoveToEx(hdc, 50, 50, NULL);
LineTo(hdc, 200, 200);
MoveToEx(hdc, 200, 50, NULL);
LineTo(hdc, 50, 200);
}
else
{
Ellipse(hdc, 50, 50, 200, 200);
}
The code above is printing the first X and O to the first block and I would like to do the same for the remaining blocks without a ton of If-Else statements. Anyone have an idea or suggestions on how I can implement this? The data member is checking wither or not I clicked the box.
I would write a draw_cross function, to which you pass one of the corners (or the center, if you prefer) of the square where it should draw. Likewise, a draw_circle. Then step through the board, and use those to draw the appropriate one in each square.
How do I erase a row from a Gtkmm::Table in C++?
Documentation is really poor :-(. I have tried using resize() with no effect, and also calling remove at a Gtk::Widget& that I obtained through get_children(), but the I get segfault...
If Gtkmm::Table is GtkTable then gtk_container_remove should, probably, do what you need. I've made a small example (see below) it removes all label widget from the table container.
#include <gtk/gtk.h>
void remove_widgets(GtkWidget *widget, gpointer data)
{
if (GTK_IS_LABEL(widget))
{
g_print("remove label\n");
gtk_container_remove(GTK_CONTAINER(data), widget);
}
}
void remove_rows(GtkWidget *button, GtkWidget* data)
{
g_print("remove rows with labels\n");
gtk_container_foreach(GTK_CONTAINER(data), remove_widgets, data);
}
int main( int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *table;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_widget_set_size_request (window, 300, 250);
gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
gtk_window_set_title(GTK_WINDOW(window), "Test Table Remove");
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
table = gtk_table_new(3, 2, FALSE);
gtk_table_set_col_spacings(GTK_TABLE(table), 3);
GtkWidget *label0 = gtk_label_new("Test Label 0");
gtk_table_attach(GTK_TABLE(table), label0, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
GtkWidget *label1 = gtk_label_new("Test Label 1");
gtk_table_attach(GTK_TABLE(table), label1, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
GtkWidget *label2 = gtk_label_new("Test Label 3");
gtk_table_attach(GTK_TABLE(table), label2, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
GtkWidget *label3 = gtk_label_new("Test Label 4");
gtk_table_attach(GTK_TABLE(table), label3, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
GtkWidget *button0 = gtk_button_new_with_label("Remove Labels");
gtk_widget_set_size_request(button0, 70, 30);
gtk_table_attach(GTK_TABLE(table), button0, 0, 1, 2, 3, GTK_FILL, GTK_FILL, 0, 0);
g_signal_connect(G_OBJECT(button0), "clicked", G_CALLBACK(remove_rows), table);
gtk_container_add(GTK_CONTAINER(window), table);
g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), G_OBJECT(window));
gtk_widget_show_all(window);
gtk_main();
return 0;
}
hope this helps, regards