I'm using Qt's GraphicsView/GraphicsScene framework, and I have to draw some line items.
To be sure these items are always visible (independant of the zoom level) I use a cosmetic pen, with a size of 3 (for example) so I always get lines of 3 pixels width drawn on screen.
But these items doesn't receive mouse events (such as hoverEnterEvent/hoverLeaveEvent) when I'm zooming out a lot.
I've digged in the code, and it appears that all collisions tests are done with the return value of the shape() function.
So I've tried to re-implement "shape()" and also "contains()" and "collidesWithPath()" methods, but I still have problems to detect collisions (because when zoom is changed, I need to re-update the shape for example).
Is there any tricks to do that ?
In an efficient way ? (without re-updating the item's shape at every zoom change)
Thanks
Related
The documentation for the QGraphicsItem::ItemIgnoresTransformations flag says
The item ignores inherited transformations (i.e., its position is still anchored to its parent, but the parent or view rotation, zoom or shear transformations are ignored). This flag is useful for keeping text label items horizontal and unscaled, so they will still be readable if the view is transformed. When set, the item's view geometry and scene geometry will be maintained separately. You must call deviceTransform() to map coordinates and detect collisions in the view. By default, this flag is disabled. This flag was introduced in Qt 4.3. Note: With this flag set you can still scale the item itself, and that scale transformation will influence the item's children.
Of course I have read the QGraphicsItem details, the QGraphicsScene details, the QGraphicsView details, and the Graphics View Framework.
There are also several questions about the ItemIgnoresTransformations flag like Fixed size QGraphicsItems in multiple views?
But I still do not understand the sentence in bold face. What does it mean ?
The problem that rose this concern is described in PyQt: Moving multiple items with different ItemIgnoresTransformations flags, but maybe this question was too long, or too pyqt oriented at first sight. And it was more about moving items. So here I'm trying to better focus.
Imagine situation that parent is rotated 45 degrees or even have some sheer. Since current item ignores this transformation it stays strait (not rotated).
Now ask question how this impacts on item size and position? Parent may maintain geometry of item (for example by using layout) but it doesn't take into account this flag, so some geometry (which is in parent units) will be set but effectively items scene rect may appear different since item ignores transformation and it is not rotated squeezed zoomed as a parent.
So from parent point of view geometry has some value but form scene point of view it is different.
It would be best if you try to see how it works in practice It is hard to describe problem clearly.
I have a QGraphicsRectItem over a scene. I intend to drag and drop this window over the scene. When the rect item reaches the left boundary end I have to show it appearing from the right end. Currently I am using two objects and hiding and showing them by calculating the boundary of scene which involves lot of calculations.
Is there any better way to achieve the same effect using just a single object?
Thank You
You could use a single item that spans the entire scene, and draw the rectangle (or 2 parts of it) in it's paint method.
But then you would lose the optimization of the BSP tree, your item would redraw even if some unrelated area repaints. If this is just 1 item, I guess it would not have much impact.
You would need to implement your own dragging with mousemove event and the like, though this is not much code, you just need to get it right.
I'm working on a simple widget system, and I'm implementing some containers right now.
Here's the situation I find myself in:
I have a Widget base class, a Container class, which is a widget that can contain other widgets, and several widget sub classes like Button.
I have two types of container: Container itself, which positions children absolutely, and Box, which will stack widgets next to each other, either horizontally or vertically.
Each widget draws itself at x=0, y=0. Therefore, containers need to add an offset to the drawing context before the widgets are told to draw themselves.
Each widget does its own hit testing based on its x/y position.
So far, it works fine. But it falls apart now that I'm implementing Box: What I do is that I overwrite the drawfunction inherited from Container to draw them all in next to each other, instead of based on their x/y position. Quite simple.
But event handling is totally off now, as the widget's x/y position has become meaningless.
I think I have two options:
Have the widget do hit testing at position x=0, y=0, like drawing. Then recalculate the mouse position to match that in Container.
Make each layout set x/y for its children, and make children draw themselves at their x/y position again. No more offsets for the drawing context
The first one is a bit ugly, I think. The second one is pretty complicated to implement, since I need to react to position changes in widgets.
How to other widget systems like Qt, Gtk and wxWidgets generally tackle this? I've looked at the source of some of these, but can't quite figure that out, it's too sophisticated. I don't have any resizing or packing issues to consider.
You are trying to implement your own layout system. You should expect it to be difficult.
I would advise against the first method. The x,y coordinates of a widget are not only used by the widgets themselves, but by anyone outside of the container who wants to do something with the widget.
The second solution is what I've chosen to implement custom widgets made of several smaller widgets and it's not that hard to put together if you don't want too many features.
Just get the widgets when they are added to your container, set their position to the current free spot, and move on to the next.
Basically I've got a QGridLayout with a few widgets in it. The important ones are 2 labels, which I use for drawing images to the screen. Well, if the user wants, he can change the resolution of the incoming images, thus, forcing the Labels to resize.
Let's assume the initial size of the label is 320x240. The user changes the VideoMode to 640x480, the label and the entire GUI resizes perfectly. But when the user switches back to 320x240, the label shrinks, but the Layout/Window does NOT.
I've played around with sizePolicies and sizeHints, and resize(0,0), but nothing did the trick. Could somebody help me with this?
Here some screenshots to clarify the problem:
You need to set the size constraint of the layout holding all your widgets to "SetFixedSize". Although the name doesn't sound like it will work, it ensures that your layout will only use the space it needs. You will not have the problem like you do in your second screenshot.
Example:
mainLayout.setSizeConstraint(QLayout::SetFixedSize);
QLayout::setSizeConstraint(QLayout::SetFixedSize) solves this problem well when you prefer keeping your widget's size fixed at all times--that is, if you'd like it to always be fixed to its "packed" size (which may still vary as the child widgets change size). That is what the "fixed" means there: "fixed" to the correct size, even as the latter varies. (In Qt terms, what I'm calling the "packed" size is simply the widget's sizeHint.)
But a constraint may be too strong a solution in some instances. In particular, if you apply it to a top-level window, then the user will not be free to resize the window. If you don't like that, you can instead perform the "set size to sizeHint" operation instantaneously each time it's needed, rather than imposing it as an unrelenting constraint. The way to do that is to call QWidget::adjustSize().
http://doc.qt.io/qt-5/qwidget.html#adjustSize
Note that if the container whose children are changing size is not the top-level window, then adjustSize() may have to be called recursively on the container and its parents. (In my case I had to do that, anyway. I also tried the size-constraint scheme, and found that applying the constraint at only the topmost level was successful in compacting all levels. I haven't enough knowledge of Qt to comment usefully on these observations, so I merely share them.)
You need to store the original size of your widget parent window before applying any changes to the layout and restore it when the user switches back to the original.
Notice that you need to work with the widget parent window size and not the widget parent size.
in your widget before applying the layout changes:
minimumWindowSize = this->window().size();
when you finished reorganizing the widget to the compact size
this->window().resize(minimumWindowSize);
So that is exactly what i'm doing in mu project.
Resolution os doesn't matter. I have only to have a widget for rendering video, or image in your case.
void MainWindow::resizeEvent(QResizeEvent* event)
{
QMainWindow::resizeEvent(event);
if ((player != 0) && ((player->isPlaying()) || player->isLoaded() || player>isLoaded())){
renderer->resize(ui->mainVideoWidget->width(),ui->mainVideoWidget->height());
resizeFilter();
}
}
I've noticed that the default behaviour for a DataGrid's vertical scroll bar is to scroll one row at a time. This is all well and good when the rows are all uniform and small (e.g. displaying a single line of text), but gets really ugly as soon as you have rows with variable heights.
I'm curious, is there a way to make DataGrid scrolling "smooth"? For instance, is there a way to have the DataGrid scroll by a set number of pixels, lines of text, etc. rather than scrolling one row at a time?
So far, the only solution I've managed to come up with is to place the DataGrid in a Canvas and have the Canvas do the scrolling instead of the DataGrid. The issue with this approach, though, is that as soon as the Canvas scrolls far enough, the DataGrid headers scroll off-screen. Ideally, I'd like to get the smooth-scrolling nature of the Canvas, but also keep the DataGrid headers visible. Is that possible?
The way that ItemRenderer's work in Flex 3 makes smooth scrolling difficult to achieve. Basically Flex recycles item renderers scrolled off of the top of the list as the display objects used for new data at the bottom of the list. Adobe's implementation of most list components in Flex 3 creates and adds these items as they come on to the screen rather than just off the screen, so they "pop in" and smooth scrolling isn't available. I'm not sure why they couldn't have done it in a similar manner for items +/- one position above or below the current scroll pane, but they didn't, and we're stuck with sticky scrolling by default.
Work-arounds do exist, though the one you've noted (dropping the datagrid into a canvas) negates the display-object saving intention of item renderers and incurs a performance cost. This will be fixed for most list-based Flex components in Flex 4, though it won't be fixed immediately for DataGrid. The DataGrid / AdvancedDataGrid component is maintained by a separate team based in India, last time I heard, and so it tends to be a bit behind the rest of the SDK.
I'd recommend trying something similar to this implementation of a smooth-scrolling list by Alex Harui. I'm not sure exactly how well it'd work for DataGrid or AdvancedDataGrid, but this is the most intuitive technique I can think of for making the list scroll correctly.
Try this... It's still based on Alex's code that was mentioned above. His should still be a great start for removing the snap-to-row behavior. Original source:
http://blogs.adobe.com/aharui/2008/03/smooth_scrolling_list.html
Alex's original some code for smooth vertical scrolling but that was not an issue I had with the DataGrid. It was smooth scrolling horizontally that I needed. I am using the DataGrid in an unorthodox manner for analyzing plain text reports output by our database (great way of providing visual feedback on a document). The code below allows content to go off screen and the user can scroll without that snap-to-column behavior.
You can adapt this to use the same math routines for vertical scrolling and then it will make scrolling possible and ignore the snap to row behavior. In particular switch the usage of the listContent.move method to move the contents vertically and use a inverse of the rounded pixel value you calculate from the vertical scroll bar (as opposed to my using the horizontal).
This method is bit simpler than Alex's method from the link above - a lot less code so try adapting and see how it works.
override protected function scrollHandler(event:Event):void
{
// Override the default scroll behavior to provide smooth horizontal scrolling and not the usual "snap-to-column" behavior
var scrEvt:ScrollEvent = event as ScrollEvent;
if(scrEvt.direction == ScrollEventDirection.HORIZONTAL) {
// Get individual components of a scroll bar for measuring and get a horizontal position to use
var scrDownArrow:DisplayObject = horizontalScrollBar.getChildAt(3);
var sctThumb:DisplayObject = horizontalScrollBar.getChildAt(2);
// I replaced maxHorizontalScrollPosition in Alex's code with "1300" to fix my exact application. In other situations you may finding using some property or different value is more appropriate. Don't rely on my choice.
var hPos:Number = Math.round((sctThumb.y - scrDownArrow.height) / (scrDownArrow.y - sctThumb.height - scrDownArrow.height) * 1300);
// Inverse the position to scroll the content to the left for large reports
listContent.move(hPos * -1, listContent.y);
}
// Go ahead and use the default handler for vertical scrolling
else {
super.scrollHandler(event);
}
}