How to fill entire window with image and allow resizing of window? - c++

I need an image to be resized to entire window and maintain the option to shrink/resize window.
As a separate task it is easily accomplished but not together.
Example code that I use is:
myWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(myWindow), "name");
gtk_window_set_default_size(GTK_WINDOW(myWindow), myWidth, myHeight);
gtk_window_set_resizable(GTK_WINDOW(myWindow), TRUE);
g_signal_connect(myWindow, "delete-event", G_CALLBACK(PrivDeleteEvent), this);
g_signal_connect(myWindow, "destroy", G_CALLBACK(PrivOnDestroy), this);
myImage = gtk_image_new();
GtkWidget* container = gtk_alignment_new(0, 0, 1, 1);
gtk_container_add(GTK_CONTAINER(myWindow), container);
gtk_container_add(GTK_CONTAINER(container), myImage);
g_signal_connect(myImage, "expose-event", G_CALLBACK(PrivOnImageExposeEvent), this);
gtk_widget_show_all(myWindow);
gtk_main();
void
PrivSetImage()
{
GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data(...);
gtk_image_set_from_pixbuf(GTK_IMAGE(myImage), pixbuf);
}
After image widget resized I call some rendering stuff and then show it in the GtkImage itself. This works good, i see image and I am able to resize window. But after gtk_image_set_from_pixbuf called I no longer able to shrink window smaller than size used to create Pixbuf. As if somewhere inside there is a call to gtk_widget_set_size_request with Pixbuf dimensions which blocks window to shrink past pixbuf size.
Any suggestion ?

Related

wxWidgets: Frame size is not changing in Image panel

I am using wxWidgets 'An Image Panel' code. Here I made only one change.
I want frame size should be equal to image size, My Image size is 762x463, but my frame size is different. frame SetSize function is not working.
wxImagePanel::wxImagePanel(wxFrame* parent, wxString file, wxBitmapType format) :
wxPanel(parent)
{
image.LoadFile(file, format);
}
void wxImagePanel::paintEvent(wxPaintEvent & evt)
{
// depending on your system you may need to look at double-buffered dcs
wxPaintDC dc(this);
render(dc);
}
void wxImagePanel::paintNow()
{
// depending on your system you may need to look at double-buffered dcs
wxClientDC dc(this);
render(dc);
}
void wxImagePanel::render(wxDC& dc)
{
dc.DrawBitmap( image, 0, 0, false );
}
class MyApp: public wxApp
{
wxFrame *frame;
wxImagePanel * drawPane;
public:
bool OnInit()
{
// make sure to call this first
wxInitAllImageHandlers();
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
wxBitmap image(wxT("properties.png"), wxBITMAP_TYPE_PNG);
frame = new wxFrame(NULL, wxID_ANY, wxT("Hello wxDC"), wxPoint(50,50), wxSize(image.GetWidth(), image.GetHeight())); // 762x463
// then simply create like this
drawPane = new wxImagePanel( frame, wxT("image.jpg"), wxBITMAP_TYPE_JPEG);
sizer->Add(drawPane, 1, wxEXPAND);
frame->SetSizer(sizer);
frame->Show();
return true;
}
};
To make the frame sized to display the image, you need to make 2 changes:
In the constructor wxImagePanel::wxImagePanel, you need to add a line to set a minimum size for the image panel.
wxImagePanel::wxImagePanel(wxFrame* parent, wxString file, wxBitmapType format) :
wxPanel(parent)
{
image.LoadFile(file, format);
SetMinSize(image.GetSize());
...
An alternate, and possibly better, solution would be to override wxWindow::DoGetBestClientSize for your wxImagePanel class and have it return image.GetSize();.
Incidentally, in the code shown above, there is nothing that actually connects the paint handler to the paint event, so you probably also want to add a line like
Bind(wxEVT_PAINT, &wxImagePanel::paintEvent, this);
to the constructor as well.
In the body of MyApp::OnInit, change the line
frame->SetSizer(sizer);
to
frame->SetSizerAndFit(sizer);
Using the SetSizerAndFit method will tell the frame to resize itself to the smallest size necessary to display all of its contents. Since in step 1 the minimum size of the image panel was set to the size of image and the image panel is the only content of the frame, the frame will be sized to fit the image.

How to resize QWidget added in QGraphicScene through QGraphicsProxy?

I have added a widget to a graphic scene (QGraphicScene) through a QGraphicsProxyWidget. To move and select the widget added QGraphicsRectItem handle.
To resize widget added QSizegrip to widget. But when i resize widget more than the QGraphicsRect item rect right and bottom edges goes behind .How to overcome this problem?
When i resize widget graphics rect item should resize or vice-versa should happen. how to do this? Any other ideas are welcome.
Here is the code
auto *dial= new QDial(); // The widget
auto *handle = new QGraphicsRectItem(QRect(0, 0, 120, 120)); // Created to move and select on scene
auto *proxy = new QGraphicsProxyWidget(handle); // Adding the widget through the proxy
dial->setGeometry(0, 0, 100, 100);
dial->move(10, 10);
proxy->setWidget(dial);
QSizeGrip * sizeGrip = new QSizeGrip(dial);
QHBoxLayout *layout = new QHBoxLayout(dial);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(sizeGrip, 0, Qt::AlignRight | Qt::AlignBottom);
handle->setPen(QPen(Qt::transparent));
handle->setBrush(Qt::gray);
handle->setFlags(QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemIsSelectable);
Scene->addItem(handle); // adding to scene
Here is the Output::
Before Resize
After Resize
Cause
The QGraphicsRectItem, which you use as a handle, is not aware of the size changes of QDial, so it does not respond by resizing itself.
Limitation
QWidget and its subclases do not provide something like a sizeChanged signal out of the box.
Solution
Considering the cause and the given limitation, my solution would be the following:
In a subcalss of QDial, say Dial, add a new signal void sizeChanged();
Reimplement the resizeEvent of Dial like this:
in dial.cpp
void Dial::resizeEvent(QResizeEvent *event)
{
QDial::resizeEvent(event);
sizeChanged();
}
Change auto *dial= new QDial(); to auto *dial= new Dial();
Add the following code after Scene->addItem(handle); // adding to scene:
in the place, where your example code is
connect(dial, &Dial::sizeChanged, [dial, handle](){
handle->setRect(dial->geometry().adjusted(-10, -10, 10, 10));
});
Note: This could be also solved using eventFilter instead of subclassing QDial. However, from your other question I know that you already subclass QDial, that is why I find the proposed solution more suitable for you.
Result
This is the result of the proposed solution:

qt remove layouts code

I have less than 1 day of experience in QT (that's why I do not know much of it) I have a window full of information (labels, text, buttons, etc) organized by layouts.
I need that after I press one button, all of the components in a window be hidden (which I already did) except for one label which should increase to barely the size of the whole window
Despite I tried modifying the "geometry" attribute (with code) the hidden layouts do not let the label to be increased. I thought also of using the option of layout breaking, but the label losses its dynamism. Could anyone please recommend me anything to do? Thanks.
Has anyone done something like this before. Thanks.
I once provided an answer to SO: Qt - How to create Image that scale with window, and keeps aspect ratio?. The actual intention was to scale an image in a QLabel with original aspect ratio to consume maximum available size.
However, I got the feedback that the suggested solution would not work properly when my Label would be used in a QGridLayout. (This sounds very similar to the issue of the OP.) Hence, I modified the sample to reproduce the issue and fiddled a little bit around with. For me, it seems that resize events of the main window are processed in the QGridLayout but affect layouted image label only partially. (Shrinking is applied but growing not.) Fortunately, I found a very simple work-around: Setting a non-empty frame to the QLabel solved the problem. I had a look into the source code on woboq.org. I hoped to get a hint what the changed frame style would activate (to apply this as fix for my resize issue). Finally, I was not patient enough and put it aside.
Beside of this QLabel in a QGridLayout resize issue, changing the visibility of widgets should cause a proper re-layout. I would prefer show/hide (instead of delete and re-new) as this is surely easier to implement, more efficient, and less error-prone.
I took the old sample code and added a tool button which can be used to toggle the visibilty of some of the layouted widgets:
// Qt header:
#include <QtWidgets>
class LabelImage: public QLabel {
private:
QPixmap _qPixmap, _qPixmapScaled;
public:
void setPixmap(const QPixmap &qPixmap) { setPixmap(qPixmap, size()); }
protected:
virtual void resizeEvent(QResizeEvent *pQEvent);
private:
void setPixmap(const QPixmap &qPixmap, const QSize &size);
};
void LabelImage::resizeEvent(QResizeEvent *pQEvent)
{
QLabel::resizeEvent(pQEvent);
setPixmap(_qPixmap, pQEvent->size());
}
void LabelImage::setPixmap(const QPixmap &qPixmap, const QSize &size)
{
_qPixmap = qPixmap;
_qPixmapScaled = _qPixmap.scaled(size, Qt::KeepAspectRatio);
QLabel::setPixmap(_qPixmapScaled);
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
// main application
QApplication app(argc, argv);
// setup GUI
QMainWindow qWin;
QToolBar qToolbar;
QAction qCmdTgl(QString::fromUtf8("Decoration"));
qCmdTgl.setCheckable(true);
qCmdTgl.setChecked(true);
qToolbar.addAction(&qCmdTgl);
qWin.addToolBar(&qToolbar);
QGroupBox qBox;
QGridLayout qGrid;
// a macro for the keyboard lazy:
#define Q_LBL_WITH_POS(ROW, COL) \
QLabel qLbl##ROW##COL(QString::fromLatin1(#ROW", "#COL)); \
/*qLbl##ROW##COL.setFrameStyle(QLabel::Raised | QLabel::Box);*/ \
qGrid.addWidget(&qLbl##ROW##COL, ROW, COL, Qt::AlignCenter)
Q_LBL_WITH_POS(0, 0);
Q_LBL_WITH_POS(0, 1);
Q_LBL_WITH_POS(0, 2);
Q_LBL_WITH_POS(1, 0);
LabelImage qLblImg;
qLblImg.setFrameStyle(QLabel::Raised | QLabel::Box);
qLblImg.setAlignment(Qt::AlignCenter);
//qLblImg.setMinimumSize(QSize(1, 1)); // seems to be not necessary
qLblImg.setSizePolicy(
QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored));
QPixmap qPM;
if (qPM.load("cats.jpg")) qLblImg.setPixmap(qPM);
else {
qLblImg.setText(
QString::fromLatin1("Sorry. Cannot find file 'cats.jpg'."));
}
qGrid.addWidget(&qLblImg, 1, 1, Qt::AlignCenter);
qGrid.setRowStretch(1, 1); // tell QGridLayout to stretch this cell...
qGrid.setColumnStretch(1, 1); // ...prior to other cells (w/ stretch 0)
Q_LBL_WITH_POS(1, 2);
Q_LBL_WITH_POS(2, 0);
Q_LBL_WITH_POS(2, 1);
Q_LBL_WITH_POS(2, 2);
qBox.setLayout(&qGrid);
qWin.setCentralWidget(&qBox);
qWin.show();
// install signal handlers
QObject::connect(&qCmdTgl, &QAction::triggered,
[&](bool on) {
qLbl00.setVisible(on); qLbl01.setVisible(on); qLbl02.setVisible(on);
qLbl10.setVisible(on); qLbl12.setVisible(on);
qLbl20.setVisible(on); qLbl21.setVisible(on); qLbl22.setVisible(on);
});
// run application
return app.exec();
}
I compiled and tested in VS2013 on Windows 10:
After toggling the Decoration tool button:
Note:
Out of curiosity, I commented the line which changes the frame style
qLblImg.setFrameStyle(QLabel::Raised | QLabel::Box);
and again, resizing of image didn't work properly anymore.
You can remove and hide widgets inside a layout using QLayout::removeWidget(*widget); but you do not need to actually remove it. You should use QWidget::hide() for the content to disappear and for the video label's cell to be able to take that space. I think you need to pay attention to the video label's size policy if it does not increase in size. Assuming you have a QGridLayout like so:
label1 label2 label3
label4 videoLabel label5
button1 button2 button3
And let's say, when you click button3, label1, label2 and label4 should all disappear and videoLabel takes the newly created space. I would group the widgets label1, label2, label4 and videoLabel into a single widget having its own sub-layout. I use QSizePolicy::Expaning to make sure my videoLabel takes the maximum space possible. Here is the implementation:
Widget::Widget(QWidget *parent) :
QWidget(parent)
{
setStyleSheet("QLabel{font-size:20px;}");
fullScreen = false; //current fullscreen state
//main grid layout
baseLayout = new QGridLayout(this);
baseLayout->setMargin(0);
baseLayout->setAlignment(Qt::AlignCenter);
setLayout(baseLayout);
//widget container for label1, label2, label4, videolabel
groupWidget = new QWidget();
//sub-layout inside the group layout
subLayout = new QGridLayout();
subLayout->setAlignment(Qt::AlignCenter);
subLayout->setMargin(0);
groupWidget->setLayout(subLayout);
//label and button instantializing. I set background colors to show their sizes
label1 = new QLabel("Label1");
label1->setStyleSheet("background-color:white;");
label2 = new QLabel("Label2");
label2->setStyleSheet("background-color:orange;");
label3 = new QLabel("Label3");
label4 = new QLabel("Label4");
label4->setStyleSheet("background-color:blue;color:white;");
label5 = new QLabel("Label5");
videoLabel = new QLabel("videoLabel");
videoLabel->setStyleSheet("background-color:red;");
videoLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
button1 = new QPushButton("button1");
button2 = new QPushButton("button2");
button3 = new QPushButton("button3");
//the grouped widget spans for 2 rows and columns, hence "2,2"
baseLayout->addWidget(groupWidget, 0,0,2,2);
subLayout->addWidget(label1, 0, 0);
subLayout->addWidget(label2, 0, 1);
subLayout->addWidget(label4, 1, 0);
subLayout->addWidget(videoLabel, 1, 1);
//adding rest of the labels and buttons to the base grid
baseLayout->addWidget(label3, 0, 2);
baseLayout->addWidget(label5, 1, 2);
baseLayout->addWidget(button1, 2, 0);
baseLayout->addWidget(button2, 2, 1);
baseLayout->addWidget(button3, 2, 2);
//button3 toggles fullscreen
connect(button3, SIGNAL(clicked(bool)), this, SLOT(onButton3Clicked(bool)));
}
//slot for button3 click
void Widget::onButton3Clicked(bool)
{
if (!fullScreen){
//removing widget from layouts is not really necessary. Make sure to hide
/*subLayout->removeWidget(label1);
subLayout->removeWidget(label2);
subLayout->removeWidget(label4);*/
label1->hide();
label2->hide();
label4->hide();
fullScreen = true;
}
else{
label1->show();
label2->show();
label4->show();
/*subLayout->addWidget(label1, 0, 0);
subLayout->addWidget(label2, 0, 1);
subLayout->addWidget(label4, 1, 0);*/
fullScreen = false;
}
}
I got the following results for this:
Keep in mind there are other approaches to this question. This one need not be necessarily the best when it comes to memory, but it is quite easy to follow.

Allegro get current window size

In Allegro, I can set the Window to be resizeable by running
al_set_new_display_flags(ALLEGRO_RESIZABLE);
before
display = al_create_display(800, 600);
however, if I resize the window, how will I know how big the window is that I resize?
use al_get_window_constraints(ALLEGRO_DISPLAY *display,
int *min_w, int *min_h, int *max_w, int *max_h).
Source
If you want to respond dynamically as the window is resized, you should listen for ALLEGRO_EVENT_DISPLAY_RESIZE. Note that you'll have to register your display as a source for your event queue first:
al_register_event_source(event_queue, al_get_display_event_source(display));

How to create a resizable Qt thumbnail preview?

I'm working on a basic image viewer/tagger that will need a thumbnail view to select an image. So far, I've used a QDockWidget enclosing a QScrollArea with a QHBoxLayout to contain a series of QLabels, each of which has its QPixMap set.
This seems very inelegant, and it only gets uglier to consider how I might implement auto-scaling of the thumbnails when the QDockWidget is resized. It's further complicated by the additional need to resize the thumbnails when the scroll bar appears and disappears.
There must be a better way to do this?
I've ran into a similar problem when trying to animate resizing a qlabel with a qpixmap. The method I found that worked best was to use a QWidget instead and re-implement the paintEvent function. Then your QWidget image will automatically be scaled if it's resized. Here is an example:
In my case I had the private variables in a private object called private_:
bool image_set_;
QImage image_;
QBrush paintbrush_;
void MyClass::paintEvent( QPaintEvent* event )
{
// if the QWidget has an image set, then we use our custom painting.
if( this->private_->image_set_ )
{
//I've made it so that my QWidget has a 1px white border
this->private_->paintbrush_.setTextureImage( this->private_->image_.scaled(QSize( this->width() - 2, this->height() - 2 ) ) );
QPainter painter( this );
QRect temp_rect = QRect( 1, 1, this->width()-2, this->height() - 2 );
painter.fillRect( this->rect(), Qt::white );
painter.fillRect( temp_rect, this->private_->paintbrush_ );
}
else
{
QWidget::paintEvent( event );
}
}