I have a QTextEdit widget with a vertical scroll bar.
report_text = new QTextEdit();
report_text->setAcceptRichText(true);
report_text->setReadOnly(true);
report_text->setTextInteractionFlags(Qt::NoTextInteraction);
report_text->setAlignment(Qt::AlignTop);
report_text->setWordWrapMode(QTextOption::NoWrap);
report_text->setFrameStyle(QFrame::NoFrame);
report_text->setMinimumSize(600, 380);
report_text->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
This is Qt 4.8 embedded.
Now I need a method or an event I can send to the widget to make it it scroll up or down just as if the up or down buttons on the scroll bar had been pressed.
I tried the scroll() method, but I scrolls the whole widget, including the scroll bar.
I also tried sending a QWheelEvent, but nothing happens.
QWheelEvent ev(QPoint(), 10, 0, 0);
report_text->setFocus();
QApplication::sendEvent(report_text, &ev);
What am I missing?
It is probable that the delta you provided is just way too small.
From the documentation of QWheelEvent::delta():
Returns the distance that the wheel is rotated, in eighths of a
degree. A positive value indicates that the wheel was rotated forwards
away from the user; a negative value indicates that the wheel was
rotated backwards toward the user.
Most mouse types work in steps of 15 degrees, in which case the delta
value is a multiple of 120; i.e., 120 units * 1/8 = 15 degrees.
The text scrolling in Qt unit is the number of whole line. So if the widget computes that you want to scroll 0.9 lines, he might do nothing.
So try again with
QWheelEvent ev(QPoint(), 120, 0, 0);
Note: Nothing here apply to an event with control or shift modifier.
Here is what I found as a suitable solution:
The vertical scrollbar may be accessed using the QTextEdit::verticalScrollBar() method.
Scrollbars have a triggerAction() method to conveniently simulate user interaction with the buttons and the slider. The available actions are defined in QAbstractSlider.
So the resulting code is only a single line:
report_text->verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepAdd);
Interestingly, this works even when the scrollbar is hidden with
report_text->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
Related
I got a multiple selection CListBox with horizontal scroll bar enabled and showed correctly. Problem is, that when I use function
lst.ResetContent() and fill it back, I can't find way to scroll text in the rows back to the same position. I tried to use
lst.SetScrollPos(SB_HORZ, horizScroll, TRUE); , where horizScroll = lst.GetScrollPos(SB_HORZ); This works correctly on scroll bar itself, but
text in the row stays not scrolled (manual scrolling functions OK).
Structure of my program is:
CListBox lst;
int horizScroll;
/*Periodically doing code bellow*/
//Get current scroll position
horizScroll = lst.GetScrollPos(SB_HORZ);
//Reset current content
lst.ResetContent();
//Add item into CListBox (UNICODE in my application)
lst.AddString(L"Some longer text then width of CListBox");
//Calculate horizontal extent and set it through
lst.SetHorizontalExtent(calculatedWidth);
//Try to scroll text (scrolls only scroll bar, not text itself)
lst.SetScrollPos(SB_HORZ, horizScroll, TRUE);
UpdateData(FALSE);
Thanks in advance!
EDIT:
As "rrirower" answered correctly,
lst.PostMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, 250), 0);
message does the job. Scroll position from horizScroll works perfectly. I suggest posting this message twice, because if you do it only once, text is re-scrolled visually from beginning to the wanted position. When you post it twice, text visually stays at the correct position and scroll bar just quickly comes to the right place.
If I understand you correctly, you're trying to scroll the text in the list box horizontally using the program code. If you use Spy++, you'll see that when you manually scroll, using the mouse, a series of WM_HSCROLL messages is posted to the list box control. You can accomplish the same thing by doing this...
lst.PostMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, 250), 0);
You need to calculate the position (I used 250 above), but, the above code should move the text and the scroll bar horizontally.
After some reading it seems that Invalidate should do the trick. Since as I understand you have one text line this should be fine, however if the painting itself is complex and requires resources you can use ScrollWindowEx and then InvalidateRect on the rectangle returned by the latter to repaint only the changed area.
I am failing in trying to restore a particular view (visible area) in a subclassed QGraphicsView on a subclassed QGraphicsScene.
The visible rectangle of the scene viewed should be
mapToScene( rect() ).boundingRect()
which is
QRectF(27.8261,26.9565 673.043x507.826)
I obtain these values in the destructor of QGraphicsView, they seem valid.
After restoring the window geometry, I am using said values in the first QGraphicsView::resizeEvent (the first event has an invalid old size QSize(-1, -1)) in the attempt to restore the area shown in view using
fitInView( QRectF(....) , Qt::IgnoreAspectRatio);
This triggers a number of scrollContentsBy events before showing the view, another resize and scroll event after, then the mainwindow showEvent fires, causing more resize scroll and resize events.
I am sure this sequence is neccessary for the GUI layout to build, but I am now confused as to how I could run fitInView once everything is set up.
Using a QTimer::singleShot to run the function well after the GUI is shown, I get approximately the desired result, but the matrix and viewed area differ:
ui->graphicsView->matrix();
ui->graphicsView->mapToScene( ui->graphicsView->rect()).boundingRect();
Before:
"[m11=1 m12=0 m21=0 m22=1 dx=0 dy=0] (x=27 y=27 w=774 h=508)"
Restored:
"[m11=0.954724 m12=0 m21=0 m22=0.954724 dx=0 dy=0] (x=17.8062 y=24.0907 w=810.705 h=532.091)"
So fitInView() does not serve my purpose very well - is there another, reliable way?
I'm using Qt 4.8.1 with MSVC2010
Another approach is to restore transform and window scroll position like so:
settings->setValue("view", mapToScene( rect() ).boundingRect() );
settings->setValue("transform", transform() );
settings->sync();
and restore them
QTransform trans = settings->value("transform", QTransform()).value<QTransform>();
QRectF view = settings->value( "view", QRectF() ).value<QRectF>();
setTransform( trans );
centerOn( view.center() );
But this method, too, has an offset.
Before
"[m11=4.96282 m12=0 m21=0 m22=4.96282 dx=0 dy=0] (x=29.6203 y=29.4188 w=155.96 h=104.981)"
After
"[m11=4.96282 m12=0 m21=0 m22=4.96282 dx=0 dy=0] (x=54.8076 y=53.8001 w=155.96 h=104.981)"
An offset also is present when scrollbars are hidden.
Moving the code to showEvent() does not affect result.
What is finally working: in showEvent, restore the transform, restore scroll-bar values:
setTransform( trans );
verticalScrollBar()->setValue( v );
horizontalScrollBar()->setValue( h );
Scroll bar visibility does not affect the view position and size, they are just "overlays".
I have a window inherited from CScrollView that handles WM_PAINT and displays a graph. This graph has elements of different types and type of element is marked by the shape of element.
I want to display some legend so that user knows what each shape means. Since user will not look at this legend often the idea is that in the upper right corner of the view control there will be a small icon. When user moves the mouse over this icon it will expand into small rectangle displaying the legend of shapes used in graph. When mouse is moved outside this rectangle it will collapse back to small icon.
This is what I have tried so far:
Draw the hotspot icon and legend rectangle using GDI in handler of WM_PAINT of view class.
Implement hotspot as a separate window that is created as a child of a scroll view.
Implement hotspot as a separate window but create it as a popup window with no parent.
Version 1 and 2 behave strangely if user scrolls the graph view.
In version 1 I can see artifacts (button is smeared across the view) while performing scroll I guess that during the scroll existing image is not invalidated but only moved.
In version 2 there is no smearing but I need to move the child window whenever I get VM_HSCROLL and WM_VSCROLL messages and these messages are not precise enough to be used this way. During scroll I can see my button moving couple of pixels and then jumping to correct position.
Version 3 is the most disturbing and because legend is not child of a graph view I get some strange behaviour where legend button is displayed even when graph view is not shown and I think that there are too many problems with this one to be viable development path to invest time in.
I think that the version 2 is the most promising but I need to find a way to make a child window stay at one place during scroll.
Did anyone have success in implementing something like this? Is there some other implementation I can investigate?
CScrollView has OnScrollBy virtual method that scrolls the entire client area of view control. You can override this method and implement your own scroll functionality.
::ScrollWindowEx has a parameter prcScroll that can specify only a portion of client area that will be scrolled.
New OnScrollBy splits client area into 4 stripes and calls ::ScrollWindowEx for each one of them.
These stripes are:
client area above icon (rectangle width across entire client area)
client area on the left of icon (rectangle height same as icon)
client area on the right of icon (rectangle height same as icon)
client area underneath icon (rectangle width across entire client area)
1 1 1 1 1
2 2 2 * 3
4 4 4 4 4
4 4 4 4 4
After scrolling all individual client rectangles, just invalidate client area where icon is located.
You may just simply use
CPoint GetScrollPosition( ) const;
method to obtain current scroll position of scroll view and recalculate your 'static' label offset accordingly.
BTW: Instead of WM_PAINT use CScrollView's method
virtual void OnDraw( CDC* pDC );
it's important
I have a component extending a Spark List, and when I scroll using the mouse wheel it scrolls too much in one go. I have tried looking for the handler that deals with mouse wheel scrolling in the List class and VerticalLayout class to override but I cannot find it.
Is there another way I'm supposed to change this, or am I missing something?
The "delta" property of MouseEvent.MOUSE_WHEEL defines how many lines will be scrolled by one wheel-scrolling. You could try changing it in the MOUSE_WHEEL handler (during capture phase). For example the following code will scroll line-by-line:
protected function init(event:FlexEvent):void
{
list.addEventListener(MouseEvent.MOUSE_WHEEL, list_mouseWheelHandler, true);
}
protected function list_mouseWheelHandler(event:MouseEvent):void
{
event.delta = event.delta > 0 ? 1 : -1;
}
The "horizontalLineScrollSize" and "verticalLineScrollSize" properties determine how many pixels to scroll when the user selects the scroll bar arrows. The "verticalLineScrollSize" property also controls the amount of scrolling when using the "mouse wheel". The default value is 5 pixels.
The "horizontalPageScrollSize" and "verticalPageScrollSize" properties determine how many pixels to scroll when the user selects the "scroll bar track". The default value is 20 pixels.
More details: http://livedocs.adobe.com/flex/3/html/help.html?content=containers_intro_4.html
My QMainWindow contains a QGraphicsView, which should've minimum width and height. So, I've used the following code in the QMainWindow constructor:
ui.graphicsView->setMinimumHeight(VIEWWIDTH);
ui.graphicsView->setMinimumWidth(VIEWWIDTH);
Then I used following code to set QMainWindow at the center of the screen:
QRect available_geom = QDesktopWidget().availableGeometry();
QRect current_geom = frameGeometry();
setGeometry(available_geom.width() / 2 - current_geom.width() / 2,
available_geom.height() / 2 - current_geom.height() / 2,
current_geom.width(),
current_geom.height());
But it is not set at the center of the screen. If I omit setMinimumHeight() and setMinimumWidth() from QGraphicsView, then the main window is set at the center of the screen. How to overcome this problem? I'm using Qt 4.5.2.
Thanks.
The problem you are encountering is that Qt will delay many calculations as long as it can. When you set the minimum width and height of your graphics view, it sets a flag somewhere that the window the graphics view is in needs re-layed out. However, it won't do that until it has to... a few milliseconds before it is actually shown on screen. So, when you call rect() on your main window, you are getting the old rectangle, and not the new one.
My best recommendation is to extend the size change event in your main window, and adjust the positioning in that event. You may also have to flag when the window has actually been shown, so that you don't reposition the window if the user resizes it after it has been shown.
Alternately, you could try repositioning the window by extending the show event function and doing it there. It would probably happen before the user actually sees the window, but might flicker on occasion.