-
-
Save pavel-perina/1324ff064aedede0e01311aab315f83d to your computer and use it in GitHub Desktop.
/* | |
Copyright (c) 2017 Pavel Perina | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all | |
copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
SOFTWARE. | |
*/ | |
/// \file fixed_aspect_ratio_qlayout.h | |
/// \brief Header only implementation of AspectRatioSingleItemLayout | |
#ifndef ASPECTRATIO_SINGLEITEM_LAYOUT_H | |
#define ASPECTRATIO_SINGLEITEM_LAYOUT_H | |
#include <QtWidgets/QLayout> | |
/// \brief Layout that keeps single item and maintains given aspect ratio | |
class AspectRatioSingleItemLayout : public QLayout { | |
public: | |
/// \brief Constructor | |
AspectRatioSingleItemLayout(QWidget *parent = nullptr, double aspectRatio = 16.0/9.0) | |
: QLayout(parent) | |
, m_aspectRatio(aspectRatio) | |
, m_item(nullptr) | |
{ | |
} | |
/// \brief Destructor | |
/// \warning Let's hope we're responsible for deleting item | |
~AspectRatioSingleItemLayout() | |
{ | |
delete m_item; | |
} | |
/// \brief Set aspect ratio to keep | |
/// \todo Invalidate geometry | |
void setAspectRatio(double aspectRatio) | |
{ | |
m_aspectRatio = aspectRatio; | |
} | |
/// \brief Return number of items | |
int count() const override | |
{ | |
return m_item != nullptr ? 1 : 0; | |
} | |
/// \brief Return item at given index, nullptr if index is out of range | |
QLayoutItem *itemAt(int i) const override | |
{ | |
return i == 0 ? m_item : nullptr; | |
} | |
/// \brief Take item at index. Now caller is responsible for deletion, we no longer own it | |
QLayoutItem *takeAt(int) override | |
{ | |
QLayoutItem *retval = m_item; | |
m_item = nullptr; | |
return retval; | |
} | |
/// \brief Returns directions where we want to expand beyond sizeHint() | |
Qt::Orientations expandingDirections() const override | |
{ | |
// we'd like grow beyond sizeHint() | |
return Qt::Horizontal | Qt::Vertical; | |
} | |
/// \brief We want to limit height based on width | |
bool hasHeightForWidth() const override | |
{ | |
return false; | |
} | |
/// \brief We want that much height for given \a width | |
int heightForWidth(int width) const override | |
{ | |
int height = (width - 2 * margin()) / m_aspectRatio + 2 * margin(); | |
return height; | |
} | |
/// \brief Set geometry of our only widget | |
void setGeometry(const QRect &rect) override | |
{ | |
QLayout::setGeometry(rect); | |
if (m_item) { | |
QWidget *wdg = m_item->widget(); | |
int availW = rect.width() - 2 * margin(); | |
int availH = rect.height() - 2 * margin(); | |
int w, h; | |
h = availH; | |
w = h * m_aspectRatio; | |
if (w > availW) { | |
// fill width | |
w = availW; | |
h = w / m_aspectRatio; | |
int y; | |
if (m_item->alignment() & Qt::AlignTop) | |
y = margin(); | |
else if (m_item->alignment() & Qt::AlignBottom) | |
y = rect.height() - margin() - h; | |
else | |
y = margin() + (availH - h) / 2; | |
wdg->setGeometry(rect.x() + margin(), rect.y() + y, w, h); | |
} | |
else { | |
int x; | |
if (m_item->alignment() & Qt::AlignLeft) | |
x = margin(); | |
else if (m_item->alignment() & Qt::AlignRight) | |
x = rect.width() - margin() - w; | |
else | |
x = margin() + (availW - w) / 2; | |
wdg->setGeometry(rect.x() + x, rect.y() + margin(), w, h); | |
} | |
} | |
} | |
QSize sizeHint() const override | |
{ | |
const int margins = 2 * margin(); | |
return m_item ? m_item->sizeHint() + QSize(margins, margins) : QSize(margins, margins); | |
} | |
QSize minimumSize() const override | |
{ | |
const int margins = 2 * margin(); | |
return m_item ? m_item->minimumSize() + QSize(margins, margins) : QSize(margins,margins); | |
} | |
/// \brief Add item, this removes existing from container | |
void addItem(QLayoutItem *item) override | |
{ | |
delete m_item; | |
m_item = item; | |
item->setAlignment(0); | |
} | |
private: | |
QLayoutItem *m_item; // at most one :-) | |
double m_aspectRatio; | |
}; | |
#endif // ASPECTRATIO_SINGLEITEM_LAYOUT_H |
Hi, could you please add a license? I would like to use this layout in a closed-source, non-commercial project and I wonder whether I am allowed to do so. Thanks.
Ok, I've added MIT license.
Here is a Python version:
https://gist.github.com/sjdv1982/75899d10e6983b878f63083e3c47b39b
Hi,
I am using the QWebEngineView for html5 + hybrid application.
I need to apply to keep aspect ratio 9:4 for the QWebEngineView when resize the window.
int main(int argc, char *argv[]) {
....
QWebEngineView *view = new QWebEngineView();
view->setUrl(QUrl(QStringLiteral("http://localhost/qttest/index.html")));
view->resize(990, 701);
....
}
How to apply your sample for QWebEngineView??
I tried as like below. But, it dose not applied when I run application and resize window.
AspectRatioSingleItemLayout *dummyLayout = new AspectRatioSingleItemLayout(view);
dummyLayout->setContentsMargins(0, 0, 0, 0);
view->setLayout(dummyLayout);
dummyLayout->addWidget(view);
Please help me...
Thanks a lot !
Sorry for belated reply.
You can't have view, add dummyLayout into it and then add view into dummyLayout. You need something like placeholderWidget->aspectRatioLayout->view
placeHolder widget is empty resizeable widget, e.g. from Qt Designer.
view is your widget that will have fixed aspect ratio, e.g. image, live camera view, ...
I've added image for clarification.
PS: don't forget this: setAspectRatio ((double) width / height).
Thank you @pavel-perina! I spent so much time trying to fix the aspect ratio of a video and finally this is something that works.
@pavel-perina Can it be used in QGridLayout? Look at the effect in this video I recorded. It should be customized Layout
https://www.youtube.com/watch?v=PcZdCChcwPQ
It looks like proportional scaling, in a Layout
QLayout that keeps fixed aspect ratio of it's only widget.
This may be useful when you need layouts keeping widgets at fixed aspect ratio frequently. Like images or videos.
I guess it's easier to handle resize event and place widgets manually for a few cases.
It's far from perfect, as of 2017-05-31 it can't handle margins properly for example.
Sample usage: