I was wondering how to change the width of the area responsible for showing the current value of a Fl_Hor_Value_Slider. The thing is, I want to be able to select a year (that's 4 digits) and the space on the side of the slider is not enough to display the year number correctly.
I've looked through the docs and tried the following functions:
slider_size()
maximum()
minimum()
range()
But they don't seem do do what I want.
I feel as though I've missed something obvious.
Any ideas?
The problem is that the size is hardcoded to 35 for horizontal or 25 for vertical. You need to modify Fl_Value_Slider.cxx. In the routine draw, it has
if (horizontal()) {
bww = 35; sxx += 35; sww -= 35;
} else {
syy += 25; bhh = 25; shh -= 25;
}
This is based on a textsize_ of 10. If you change the text size, then it needs to go up based on the text size. Something like
if (horizontal()) {
int width = textsize() * 3 + 5;
bww = width; sxx += width; sww -= width;
} else {
int height = textsize() * 2 + 5;
syy += height; bhh = height; shh -= height;
}
Then rebuild the fltk library. Either keep this patch or send it to FLTK and check whenever you get an update from FLTK. To test
Fl_Hor_Value_Slider* o = new Fl_Hor_Value_Slider(10, 50, 250, 50, "");
o->tooltip("Value Slider");
o->selection_color((Fl_Color)1);
o->textsize(25);
o->value(2000)
o->range(2000, 2099);
o->precision(0);
Edit
Alternatively, create your own Hor_Value_Slider based on Fl_Hor_Value_Slider and override the draw method.
For the record: meanwhile FLTK 1.4.0 (not yet released) got new methods for sliders: value_width(int) and value_height(int), respectively. These methods let you set the width of the value field on horizontal and the height of the value field on vertical sliders.
Documentation can be found here:
https://www.fltk.org/doc-1.4/classFl__Value__Slider.html
Related
I'm writing a dialog based MFC application on Visual Studio 2017 in C++.
I've added an option for the user to maximize\minimize the window. As the user resizes the dialog I want to adjust the sizes of the controls. In the app there's a list control as shown in the picture below, which adjusts to the new size of the window.
before maximizing the window - the width of each column is adjusted to the list width:
after maximizing the window:
My problem is that I'm having trouble to adjust it's columns to the new size. I've seen some posts on the subject, but unfortunately I still couldn't solve the problem. I want the columns to be about the same size and to fill the whole window.
what I've tried:
1. method 1 - the problem was that the last column was much bigger than the others
void CEditableListControlDlg::OnSize(UINT nType, int cx, int cy) {
if (m_bUseWMSize) {
// adjust column width to window
for (int i = 0; i < m_EditableList.GetHeaderCtrl()->GetItemCount(); ++i) {
m_EditableList.SetColumnWidth(i, LVSCW_AUTOSIZE_USEHEADER);
}
}
m_bUseWMSize = true;
}
the variable m_bUseWMSize can be ignored - it's just a flag so I won't execute the code when the dialog is created. I've also tried switching the flage LVSCW_AUTOSIZE_USEHEADER inside SetColumnWidth to LVSCW_AUTOSIZE, but it didn't help either.
After using method 1 the list looks like this after maximizing the dialog:
2. method 2 - the problem was that the columns width wasn't affected. I think it's because the width variable has a value of 2147483647 and even if I divide it by 11 (the number of columns) it's still too big
void CEditableListControlDlg::OnSize(UINT nType, int cx, int cy) {
if (m_bUseWMSize) {
RECT rect;
m_EditableList.GetViewRect(&rect);
int width = abs(rect.left - rect.right);
int nCol = 11;
// adjust column width to window
for (int i = 0; i < m_EditableList.GetHeaderCtrl()->GetItemCount(); ++i) {
m_EditableList.SetColumnWidth(i, width/nCol);
}
}
m_bUseWMSize = true;
}
3. method 3 - I've tried to retrieve the width of the list in pixels and divide it by the number of columns. the problem was the the columns width was too small because the value of size.cx is 512
void CEditableListControlDlg::OnSize(UINT nType, int cx, int cy) {
if (m_bUseWMSize) {
CSize cz;
CSize size = m_EditableList.ApproximateViewRect(cz, -1);
int nCol = 11;
// adjust column width to window
for (int i = 0; i < m_EditableList.GetHeaderCtrl()->GetItemCount(); ++i) {
m_EditableList.SetColumnWidth(i, size.cx/11);
}
}
m_bUseWMSize = true;
}
relevant documentations:
SetColumnWidth Function
RECT struct
CSize struct
Functions ApproximateViewRect and GetViewRect
Thank you.
The easy option is to let the control do it for you :-). There is a special value you can pass (LVSCW_AUTOSIZE_USEHEADER) when sending an LVM_SETCOLUMNWIDTH message to the control, as per the following documentation topic:
https://learn.microsoft.com/en-us/windows/win32/controls/lvm-setcolumnwidth
Also referenced in that SetColumnWidth function link you mentioned in your post.
You could refer to the following code. Maybe it can help you.
bool CMFCApplication4Dlg::AdjustColumnWidth(CListCtrl* m_acclist)
{
CHeaderCtrl* pHeaderCtrl = m_acclist->GetHeaderCtrl();
int n = m_acclist->GetColumnWidth();
int nColumnCount = pHeaderCtrl->GetItemCount();
for (int i = 0; i < nColumnCount; i++)
{
m_acclist->SetColumnWidth(i, LVSCW_AUTOSIZE);
int nColumnWidth = m_acclist->GetColumnWidth(i);
m_acclist->SetColumnWidth(i, LVSCW_AUTOSIZE_USEHEADER);
int nHeaderWidth = m_acclist->GetColumnWidth(i);
m_acclist->SetColumnWidth(i, nColumnWidth > nHeaderWidth ? nColumnWidth : nHeaderWidth);
}
return true;
}
Because LVSCW_AUTOSIZE_USEHEADER will resize last column to the remaining width, it won't help you since you want to evenly distribute total width across all columns.
Similar to your method 2 you could try the following:
For each column cycle through all rows including the header and use CListCtrl::GetStringWidth to get text width in pixels. Store the maximum width of each column
Sum up all these max widths
Get width of control client rectangle with GetClientRect. If scrollbar is present subtract it with ::GetSystemMetrics (SM_CXVSCROLL)
Subtract result of 2 from 3 and divide by number of columns. Add result to each of max widths obtained in 1
Use SetColumnWidth to assign new width for each column
I have trouble getting TreeView in GTK3 to wrap text correctly.
I set it up to wrap in this way:
Gtk::TreeViewColumn* pColumn = mTreeView.get_column(2);
static_cast<Gtk::CellRendererText *>(pColumn->get_first_cell())
->property_wrap_mode().set_value(Pango::WRAP_WORD_CHAR);
static_cast<Gtk::CellRendererText *>(pColumn->get_first_cell())
->property_wrap_width().set_value(200);
This works, text is wrapped, but when I resize the window and make it bigger, there is a lot of ugly white-space above and below cell with long text. It seems, that GTK reserves height for cell based on wrap width. Which makes no sense to me.
I tried to get around with setting needed in signal_check_resize with calculating needed width like this:
Gtk::TreeViewColumn* pColumn = mTreeView.get_column(2);
auto width = this->get_allocated_width()
- mTreeView.get_column(0)->get_width()
- mTreeView.get_column(1)->get_width();
static_cast<Gtk::CellRendererText *>(pColumn->get_first_cell())
->property_wrap_width().set_value(width-100);
this->forceRecreateModel = true; //Needed to work
But this lets me only make window bigger. It cannot be shrinked, after it was resized.
The question is, how this is properly done?
I am using gtk3.20.3-1 and gtkmm3.20.1-1 on Arch linux.
EDIT: fixed typo in the title...
In the end I found how to do it.
In the setup of the window (for me constructor of the window derived class) it was necessary to set column to be AUTOSIZE in order to allow shrinking of the width.
//Last Column setup
{
mTreeView.append_column("Translation", mColumns.mEnglish);
Gtk::TreeViewColumn* pColumn = mTreeView.get_column(2);
pColumn->set_sizing(Gtk::TreeViewColumnSizing::TREE_VIEW_COLUMN_AUTOSIZE);
static_cast<Gtk::CellRendererText *>(pColumn->get_first_cell())
->property_wrap_mode().set_value(Pango::WRAP_WORD_CHAR);
}
Also there is needed to set correct wrap width on every resize. Without this, height of the row was as big as it would be necessary for currently set wrap_width with no regard on current width (resulting in big padding on the top, when stretched more and prohibiting to make window smaller).
This code was also in the constructor.
this->signal_check_resize().connect([this]()
{
//calculate remaining size
Gtk::TreeViewColumn* pColumn = mTreeView.get_column(2);
auto width = this->get_allocated_width()
- mTreeView.get_column(0)->get_width()
- mTreeView.get_column(1)->get_width()-30;
//minimum reasonable size for column
if(width < 150)
width = 150;
static_cast<Gtk::CellRendererText *>(pColumn->get_first_cell())
->property_wrap_width().set_value(width);
//debounce
static auto oldsize = 0;
{
oldsize = width;
//trigger redraw of mTreeView (by clearing and refilling Model,
//it is done in 100ms pulse)
this->mRedrawNeeded = true;
}
});
And maybe it is worth noting, that I have mTreeView encapsulated in Gtk::ScrolledWindow. So this is a chunk which comes before column setup. :)
//in class is: Gtk::ScrolledWindow mScrollForResults;
//scrolling area
mGrid.attach(mScrollForResults, 0,2,10,1);
mScrollForResults.set_hexpand();
mScrollForResults.set_policy(Gtk::PolicyType::POLICY_AUTOMATIC,
Gtk::PolicyType::POLICY_ALWAYS);
mScrollForResults.set_margin_top(10);
mScrollForResults.set_min_content_width(400);
mScrollForResults.set_min_content_height(200);
mScrollForResults.add(mTreeView);
//results treeView
mRefListStore = Gtk::ListStore::create(mColumns);
mTreeView.set_model(mRefListStore);
mTreeView.set_hexpand();
mTreeView.set_vexpand();
I am coding a small map editor (with rectangle tiles) and I need a way to draw a large amount of images OR one big image. The application is simple: You draw images on an empty screen with your mouse and when you are finished you can save it. A tile consists of a small image.
I tried out several solutions to display the tiles:
Each tile has its own QGraphicsItem (This works until you have a
1000x1000 map)
Each tile gets drawn on one big QPixmap (This means a very large image. Example: Map with 1000x100 and each tile has a size of 32x32 means that the QPixmap has a size of 32000x32000. This is a problem for QPainter.)
The current solution: Iterate through width & height of the TileLayer and draw each single tile with painter->drawPixmap(). The paint() method of my TileLayer looks like this:
void TileLayerGraphicsItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option,QWidget* /*widget*/)
{
painter->setClipRect(option->exposedRect);
int m_width=m_layer->getSize().width();
int m_height=m_layer->getSize().height();
for(int i=0;i<m_width;i++)
{
for(int j=0;j<(m_height);j++)
{
Tile* thetile=m_layer->getTile(i,j);
if(thetile==NULL)continue;
const QRectF target(thetile->getLayerPos().x()*thetile->getSize().width(),thetile->getLayerPos().y()*thetile->getSize().height(),thetile->getSize().width(),thetile->getSize().height());
const QRectF source(0, 0, thetile->getSize().width(), thetile->getSize().height());
painter->drawImage(target,*thetile->getImage(),source);
}
}}
This works for small maps with 100x100 or even 1000x100 tiles. But not for 1000x1000. The whole application begins to lag, this is of course because I have a for loop that is extremely expensive. To make my tool useful I need to be able to make at least 1000x1000 tilemaps without lags. Does anyone have an idea what I can do? How should I represent the tiles?
Update:
I changed the following: Only maps that exceed the window size of the minimap will be drawn with drawing single pixels for each tile. This is my render function now:
void RectangleRenderer::renderMinimapImage(QPainter* painter, TileMap* map,QSize windowSize)
{
for(int i=0;i<map->getLayers().size();i++)
{
TileLayer* currLayer=map->getLayers().at(i);
//if the layer is small draw it completly
if(windowSize.width()>currLayer->getSize().width()&&windowSize.height()>currLayer->getSize().height())
{
...
}
else // This is the part where the map is so big that only some pixels are drawn!
{
painter->fillRect(0,0,windowSize.width(),windowSize.height(),QBrush(QColor(map->MapColor)));
for(float i=0;i<windowSize.width();i++)
for(float j=0;j<windowSize.height();j++)
{
float tX=i/windowSize.width();
float tY=j/windowSize.height();
float pX=lerp(i,currLayer->getSize().width(),tX);
float pY=lerp(j,currLayer->getSize().height(),tY);
Tile* thetile=currLayer->getTile((int)pX,(int)pY);
if(thetile==NULL)continue;
QRgb pixelcolor=thetile->getImage()->toImage().pixel(thetile->getSize().width()/2,thetile->getSize().height()/2);
QPen pen;
pen.setColor(QColor::fromRgb(pixelcolor));
painter->setPen(pen);
painter->drawPoint(i,j);
}
}
}
}
This works not correct, however it is pretty fast. The problem is my lerp(linear interpolation) function to get the correct tiles to draw a pixel from.
Does anyone have a better solution to get the correct tiles while I iterate through the minimap pixels? At the moment I use linear interpolation between 0 and the maximum size of the tilemap and it does not work correctly.
UPDATE 2
//currLayer->getSize() returns how many tiles are in the map
// currLayer->getTileSize() returns how big each tile is (32 pixels width for example)
int raw_width = currLayer->getSize().width()*currLayer->getTileSize().width();
int raw_height = currLayer->getSize().height()*currLayer->getTileSize().height();
int desired_width = windowSize.width();
int desired_height = windowSize.height();
int calculated_width = 0;
int calculated_height = 0;
// if dealing with a one dimensional image buffer, this ensures
// the rows come out clean, and you don't lose a pixel occasionally
desired_width -= desired_width%2;
// http://qt-project.org/doc/qt-5/qt.html#AspectRatioMode-enum
// Qt::KeepAspectRatio, and the offset can be used for centering
qreal ratio_x = (qreal)desired_width / raw_width;
qreal ratio_y = (qreal)desired_height / raw_height;
qreal floating_factor = 1;
QPointF offset;
if(ratio_x < ratio_y)
{
floating_factor = ratio_x;
calculated_height = raw_height*ratio_x;
calculated_width = desired_width;
offset = QPointF(0, (qreal)(desired_height - calculated_height)/2);
}
else
{
floating_factor = ratio_y;
calculated_width = raw_width*ratio_y;
calculated_height = desired_height;
offset = QPointF((qreal)(desired_width - calculated_width)/2,0);
}
for (int r = 0; r < calculated_height; r++)
{
for (int c = 0; c < calculated_width; c++)
{
//trying to do the following: use your code to get the desired pixel. Then divide that number by the size of the tile to get the correct pixel
Tile* thetile=currLayer->getTile((int)((r * floating_factor)*raw_width)/currLayer->getTileSize().width(),(int)(((c * floating_factor)*raw_height)/currLayer->getTileSize().height()));
if(thetile==NULL)continue;
QRgb pixelcolor=thetile->getImage()->toImage().pixel(thetile->getSize().width()/2,thetile->getSize().height()/2);
QPen pen;
pen.setColor(QColor::fromRgb(pixelcolor));
painter->setPen(pen);
painter->drawPoint(r,c);
}
}
Trying to reverse engineer the example code, but it still does not work correctly.
Update 3
I tried (update 1) with linear interpolation again. And while I looked at the code I saw the error:
float pX=lerp(i,currLayer->getSize().width(),tX);
float pY=lerp(j,currLayer->getSize().height(),tY);
should be:
float pX=lerp(0,currLayer->getSize().width(),tX);
float pY=lerp(0,currLayer->getSize().height(),tY);
That's it. Now it works.
This shows how to do it properly. You use a level of detail (lod) variable to determine how to draw the elements that are currently visible on the screen, based on their zoom.
http://qt-project.org/doc/qt-5/qtwidgets-graphicsview-chip-example.html
Also don't iterate through all the elements that could be visible, but only go through the ones that have changed, and of those, only the ones that are currently visible.
Your next option to use is some other manual caching, so you don't have to repeatedly iterate through O(n^2) constantly.
If you can't optimize it for QGraphicsView/QGraphicsScene... then OpenGL is probably what you may want to look into. It can do a lot of the drawing and caching directly on the graphics card so you don't have to worry about it as much.
UPDATE:
Pushing changes to QImage on a worker thread can let you cache, and update a cache, while leaving the rest of your program responsive, and then you use a Queued connection to get back on the GUI thread to draw the QImage as a Pixmap.
QGraphicsView will let you know which tiles are visible if you ask nicely:
http://qt-project.org/doc/qt-5/qgraphicsview.html#items-5
UPDATE 2:
http://qt-project.org/doc/qt-5/qtwidgets-graphicsview-chip-chip-cpp.html
You may need to adjust the range of zooming out that is allowed on the project to test this feature...
Under where it has
const qreal lod = option->levelOfDetailFromTransform(painter->worldTransform());
if (lod < 0.2) {
if (lod < 0.125) {
painter->fillRect(QRectF(0, 0, 110, 70), fillColor);
return;
}
QBrush b = painter->brush();
painter->setBrush(fillColor);
painter->drawRect(13, 13, 97, 57);
painter->setBrush(b);
return;
}
Add in something like:
if(lod < 0.05)
{
// using some sort of row/col value to know which ones to not draw...
// This below would only draw 1/3 of the rows and 1/3 of the column
// speeding up the redraw by about 9x.
if(row%3 != 0 || col%3 != 0)
return;// don't do any painting, return
}
UPDATE 3:
Decimation Example:
// How to decimate an image to any size, properly
// aka fast scaling
int raw_width = 1000;
int raw_height = 1000;
int desired_width = 300;
int desired_height = 200;
int calculated_width = 0;
int calculated_height = 0;
// if dealing with a one dimensional image buffer, this ensures
// the rows come out clean, and you don't lose a pixel occasionally
desired_width -= desired_width%2;
// http://qt-project.org/doc/qt-5/qt.html#AspectRatioMode-enum
// Qt::KeepAspectRatio, and the offset can be used for centering
qreal ratio_x = (qreal)desired_width / raw_width();
qreal ratio_y = (qreal)desired_height / raw_height();
qreal floating_factor = 1;
QPointF offset;
if(ratio_x < ratio_y)
{
floating_factor = ratio_x;
calculated_height = raw_height*ratio_x;
calculated_width = desired_width;
offset = QPointF(0, (qreal)(desired_height - calculated_height)/2);
}
else
{
floating_factor = ratio_y;
calculated_width = raw_width*ratio_y;
calculated_height = desired_height;
offset = QPointF((qreal)(desired_width - calculated_width)/2);
}
for (int r = 0; r < calculated_height; r++)
{
for (int c = 0; c < calculated_width; c++)
{
pixel[r][c] = raw_pixel[(int)(r * floating_factor)*raw_width][(int)(c * floating_factor)];
}
}
Hope that helps.
I am using a custom font called KomikaTitle. In some cases the font appears cut off on the left in the first character. This doesn't happen when I use a native font such as Arial.
The following is the code I am using:
scoreDisplayLabel = [CCLabelTTF labelWithString:#"0" dimensions:CGSizeMake(200,30) hAlignment:UITextAlignmentLeft fontName:#"KomikaTitle" fontSize:18];
scoreDisplayLabel.color = (ccc3(r,b,g));
[self addChild:scoreDisplayLabel z:2];
[scoreDisplayLabel setPosition:ccp(115,wins.height-73)];
How do I prevent this from happening? I am attaching a screenshot of the issue.
I tried messing around as suggested in http://www.cocos2d-iphone.org/forums/topic/custom-font-being-cut-off/, but no luck.
Thanks guys!
This maybe isn't a real answer, but I had the same problem with that font in an old cocos2d project I made. Just just added an extra space and a row.
This may or may not be related, but according to this source you have to include the file extension of your font. Where you have
fontName:#"KomikaTitle"
it should be
fontName:#"KomikaTitle.ttf"
for example.
If there are any android users out there using cocos2dx, this is not necessarily an easy problem to solve, but it is doable once you go down the rabbit hole. It does require editing the Cocos2dxBitmap.java file, which means that any changes made could be overrided by an update. Basically, the methods that are used to measure text are, while not incorrect, inadequate.
First, we need to add a new variable to the TextProperty
private final int mX;
Next, replace the computeTextProperty code with the following:
private static TextProperty computeTextProperty(final String pString, final int unusedWidth, final int unusedHeight, final Paint pPaint) {
final FontMetricsInt fm = pPaint.getFontMetricsInt();
final int h = (int) Math.ceil(fm.bottom - fm.top);
int maxContentWidth = 0;
final String[] lines = Cocos2dxBitmap.splitString(pString, 0,
0, pPaint);
/* Compute the max width. */
int temp = 0;
float left = 0;
for (final String line : lines) {
//get a path from text
Path path = new Path();
pPaint.getTextPath(line, 0, line.length(), 0, 0, path);
RectF bounds = new RectF();
path.computeBounds(bounds, true);
temp = (int) FloatMath.ceil(bounds.width());
//if the text extends to the left of 0
if (bounds.left < left) {
left = bounds.left;
}
if (temp > maxContentWidth) {
maxContentWidth = temp;
//extend the width to account for text rendered to the left of 0
if (left < bounds.left) {
maxContentWidth += (int) FloatMath.ceil(Math.abs(left));
}
}
}
left = Math.abs(left);
return new TextProperty(maxContentWidth, h, lines, (int) FloatMath.ceil(left));
}
What has basically happened is that we have used information returned by the text path to get if the left bound is less than 0, which would mean it would be rendered outside the bitmap. We also extend the width when there are multiple lines of text, as we are going to shift everything to match the left bounds, we need the right bounds shifted too.
Finally, replace computeX with
private static int computeX(final String pText, final int pMaxWidth,
final int pHorizontalAlignment, final int pX) {
int ret = 0;
int expectedWidth = pX + pMaxWidth;
switch (pHorizontalAlignment) {
case HORIZONTALALIGN_CENTER:
ret = expectedWidth / 2;
break;
case HORIZONTALALIGN_RIGHT:
ret = expectedWidth;
break;
case HORIZONTALALIGN_LEFT:
ret = pX;
default:
break;
}
return ret;
}
You'll have to do all the hookups yourself, but this will provide the most accurate text rendering.
I've made a QGraphicsScene with a mouseClickEvent that lets the user create blue squares inside of it. But I want to make the scene grow when an item is placed against its border so that the user never runs out of space on the graphics scene.
What's the best way to make a graphics scene bigger in this case?
I suggest doing something like the following:
Get the bounding rect of all items in the scene using QGraphicsScene::itemsBoundingRect().
Add some padding around that rect to make sure the bounds of the items won't hit the edge of the view. Something like myRect.adjust(-20, -20, 20, 20) should be sufficient.
Use QGraphicsView::fitInView(myRect, Qt::KeepAspectRatio) to ensure the taken area is within the visible bounds of the view.
That should do it. This code should be called whenever something has changed in the scene. You can use QRectF::intersects() function to find out if the new rect has been placed on the edge of the view.
What's the best way to make a graphics scene bigger in this case?
The GraphicsScene is an infinite coordinate system. Most clients will use itemsBoundingRect() to get an idea how much space is actually used by items in the scene. If you have cleared the scene, you might want to call QGraphicsScene::setSceneRect(QRectF()) to "make it smaller" again.
Hope that helps.
sorry if this is a little bit late(6 years) but I will provide an answer if someone still struggling with this or want another approach.I implement this in mouseReleaseEvent in the custom class derive from QGraphicsObject. Note that I initialize the size of my QGraphicsScene (1000,1000) with the following code.scene->setSceneRect(0,0,1000,1000). So here what my code will do. If the Item(the item is draggable) placed against the border, that border will increase. So here is my code:
void MyItem::mouseReleaseEvent(QgraphicsceneMouseEvent* event){
QRectF tempRect = this->scene()->sceneRect();
if(this->scenePos().y() < this->scene()->sceneRect().top()){
tempRect.adjust(0,-200,0,0);
if(this->scenePos().x() < this->scene()->sceneRect().left()){
tempRect.adjust(-200,0,0,0);
}
else if(this->scenePos().x() + 200> this->scene()->sceneRect().right()){
tempRect.adjust(0,0,200,0);
}
}
else if(this->scenePos().y() + 200 > this->scene()->sceneRect().bottom()){
tempRect.adjust(0,0,0,200);
if(this->scenePos().x() < this->scene()->sceneRect().left()){
tempRect.adjust(-200,0,0,0);
}
else if(this->scenePos().x() + 200> this->scene()->sceneRect().right()){
tempRect.adjust(0,0,200,0);
}
}
else if(this->scenePos().x() < this->scene()->sceneRect().left()){
tempRect.adjust(-200,0,0,0);
if(this->scenePos().y() < this->scene()->sceneRect().top()){
tempRect.adjust(0,-200,0,0);
}
else if(this->scenePos().y() + 200 > this->scene()->sceneRect().bottom()){
tempRect.adjust(0,0,0,200);
}
}
else if(this->scenePos().x() + 200> this->scene()->sceneRect().right()){
tempRect.adjust(0,0,200,0);
if(this->scenePos().y() < this->scene()->sceneRect().top()){
tempRect.adjust(0,-200,0,0);
}
else if(this->scenePos().y() + 200 > this->scene()->sceneRect().bottom()){
tempRect.adjust(0,0,0,200);
}
}
this->scene()->setSceneRect(tempRect);
I know its late, but for anyone looking for python code here:
class Scene(QtWidgets.QGraphicsScene):
def __init__(self):
super(Scene, self).__init__()
self.setSceneRect(0, 0, 2000, 2000)
self.sceneRect().adjust(-20, -20, 20, 20)
self.old_rect = self.itemsBoundingRect()
def adjust(self):
w = self.sceneRect().width()
h = self.sceneRect().height()
x = self.sceneRect().x()
y = self.sceneRect().y()
adjust_factor = 500
adjust_factor2 = 300
smaller = self.is_smaller()
self.old_rect = self.itemsBoundingRect()
if not self.sceneRect().contains(self.old_rect):
self.setSceneRect(-adjust_factor + x, -adjust_factor + y, adjust_factor + w, adjust_factor + h)
if smaller:
self.setSceneRect(adjust_factor2 + x, adjust_factor2 + y, abs(adjust_factor2 - w), abs(adjust_factor2 - h))
def is_smaller(self):
x = self.old_rect.x()
y = self.old_rect.y()
h = self.old_rect.height()
w = self.old_rect.width()
if ((x <= self.itemsBoundingRect().x()) and (y <= self.itemsBoundingRect().y())
and (h > self.itemsBoundingRect().height()) and (w > self.itemsBoundingRect().width())):
return True
return False
Explanation:
use self.sceneRect().contains(self.itemBoundingRect) check whether the itemBoundingRect is within the sceneRect, if its not in the sceneRect then use self.setSceneRect() to increase the sceneRect size
(Note: make sure you add to the previous sceneRect like shown in the above code).
If you also want to decrease the sceneRect. Store the old itemBoundingRect and compare it with the new one, if the new itemSceneRect Rectangle is smaller then decrease the size by some factor (refer to the above code).
Usage:
you may call the adjust method from anywhere you like. But Calling the adjust method from mouseReleaseEvent worked the best for me.
*If you have any suggestions or query you may comment.