I constantly generate new HTML pages that I display in a QWebView. Now I have trouble to restore the current position of the vertical scrollbar after the setHtml() call, if the HTML contains images. The scrollbar always jumps back to the top.
The following code works as long as the HTML only contains text:
void MainWindow::htmlResultReady(const QString &html)
{
// remember scrollbar position
int scrollBarPos = ui->webView->page()->mainFrame()->scrollBarValue(Qt::Vertical);
ui->webView->setHtml(html);
// restore previous scrollbar position
ui->webView->page()->mainFrame()->setScrollBarValue(Qt::Vertical, scrollBarPos);
}
I also tried to use the signal QWebView::loadFinished() without success:
void MainWindow::setupHtmlPreview()
{
connect(ui->webView, SIGNAL(loadFinished(bool)),
this, SLOT(restoreScrollBarPosition()));
}
void MainWindow::htmlResultReady(const QString &html)
{
// remember scrollbar position
scrollBarPos = ui->webView->page()->mainFrame()->scrollBarValue(Qt::Vertical);
ui->webView->setHtml(html);
}
void MainWindow::restoreScrollBarPosition()
{
// restore previous scrollbar position
ui->webView->page()->mainFrame()->setScrollBarValue(Qt::Vertical, scrollBarPos);
}
Perhaps the size of the page changes. Call setScrollBarValue when contentsSizeChanged is emitted by the frame (typically webView->page()->mainFrame()).
Related
I am new to Javafx and after some readings on the subject I decided to start building my first application.
My application has a main page and an hamburgerDrawer on left side panel. The trouble I am having is that when I load the application, the drawer overlay is blocking the nodes beneath it. I tried to use the method SetDefaultDrawerSize() to 0 when I close the drawer, and to a specific size, say SetDefaultDrawerSize(900), with no success, the drawer is still blocking the content beneath.
#Override
public void initialize(URL url, ResourceBundle rb) {
try {
AnchorPane drawerContent = FXMLLoader.load(getClass().getResource("/materialitemtracker/view/AnchorPaneHamburgerDrawerView.fxml"));
hamburgerDrawer.setSidePane(drawerContent);
hamburgerDrawer.setOverLayVisible(true);
hamburgerDrawer.setResizableOnDrag(true);
HamburgerBackArrowBasicTransition burgerTask = new HamburgerBackArrowBasicTransition(hamburger);
burgerTask.setRate(-1);
hamburger.addEventHandler(MouseEvent.MOUSE_PRESSED, (e) -> {
burgerTask.setRate(burgerTask.getRate() * -1);
burgerTask.play();
if (hamburgerDrawer.isShown()) {
hamburgerDrawer.close();
// hamburgerDrawer.setDefaultDrawerSize(0);
} else {
hamburgerDrawer.open();
// hamburgerDrawer.setDefaultDrawerSize(900);
}
});
} catch (IOException ex) {
Logger.getLogger(AnchorPaneMainController.class.getName()).log(Level.SEVERE, null, ex);
}
Sample images:
Application Main Page
Drawer content
I had a similar issue to the one you describe. However in my case it wasn't the overlay but the drawer itself. The overlay when visible would encompass the entire UI when visible so I quickly determined that this wasn't my issue since it was only the buttons on the left of the UI which were not receiving events. Instead it seemed the area affected was limited the default drawer size I had set.
There are two solutions i could see:
Move the buttons above the drawer in the UI structure. This didn't work for me since the buttons would then overlap the expanded drawer in my UI.
Give the drawer a negative offset and change the constraints depending on the drawer state at runtime. My drawer resides on the left of the UI inside an AnchorPane so set a negative left anchor value to begin with (-255 for this example). Then depending on the state I do as follows.
drawer.setOnDrawerOpening(event ->
{
AnchorPane.setRightAnchor(drawer, 0.0);
AnchorPane.setLeftAnchor(drawer, 0.0);
AnchorPane.setTopAnchor(drawer, 0.0);
AnchorPane.setBottomAnchor(drawer, 0.0);
});
drawer.setOnDrawerClosed(event ->
{
AnchorPane.clearConstraints(drawer);
AnchorPane.setLeftAnchor(drawer, -255.0);
AnchorPane.setTopAnchor(drawer, 0.0);
AnchorPane.setBottomAnchor(drawer, 0.0);
});
Try the following code:
if (drawer.isOpened()) {
drawer.close();
drawer.setVisible(false);
} else {
drawer.setVisible(true);
drawer.open();
}
While implementing virtual scrolling with wijmo grid, as per the below code, when the new data is appended to data array (once we scroll and reach to the last record of the grid), the scroll gets reset to the initial position.
Check this JSFiddle
scrollPositionChanged: function(s, e) {
// if we're close to the bottom, add 10 items
if (s.viewRange.bottomRow >= s.rows.length - 1) {
addData(data, 20);
s.collectionView.refresh();
}
}
Any idea how can we stop this behaviour? Other grids like slickgrid, ag-grid provides smooth behaviour - once the data is appended, the previous last record stays in the view. Can we achieve similar kind of behaviour for wijmo-grid?
You can save scroll postion before refreshing the grid and restore the same after scroll.
var pos;
var grid = new wijmo.grid.FlexGrid('#flex', {
itemsSource: data,
scrollPositionChanged: function (s, e) {
// if we're close to the bottom, add 10 items
if (s.viewRange.bottomRow >= s.rows.length - 1) {
addData(data, 20);
//save current scroll postion
pos = grid.scrollPosition;
s.collectionView.refresh();
}
//restore scroll position
if (pos) {
s.scrollPosition = Object.assign({}, pos);
pos = null;
}
}
});
Check the updated fiddle:- http://jsfiddle.net/797tjc5u/
You need to store scroll position before making http request and set back once items have been added to FlexGrid.
Please refer to the following updated fiddle with http service delay simulation:
[http://jsfiddle.net/hwr2ra1q/41/][1]
I want to add a gif animated image in QLabel that add into the QGraphicsScene.
My code is here:
QLabel *lbl = new QLabel;
QMovie *mv = new QMovie(":/Images/sun.gif");
mv->start();
lbl->setWindowFlags(Qt::FramelessWindowHint);
lbl->setMask((new QPixmap(":/Images/sun.gif"))->mask()); // for create transparent for QLabel image
lbl->setMovie(mv);
lbl->setGeometry(10,10,10,10);
scene.addWidget(lbl);
but when I run that it will transparent with first frame of that gif and when the gif is running the photo will not show completely and it will run with transparented area in the first frame.
How can I solve that?
Thanks
The problem is that QLabel has window background by default. You're trying to remove it by do it incorrectly:
FramelessWindowHint doesn't make sense here, since it's only used for top level widgets, and a widget added to scene is technically hidden and doesn't have system window frame. This line should be removed.
setMask does exactly what you describe it does. Since QPixmap is not animated, its mask is the alpha mask of the first frame of animation. And you permanently apply this mask to the label. It's not surpising that it works, but obviously it's not what you want. This line should also be removed.
setGeometry line is incorrect. It prevents picture from being visible for me. Label has good size by default and there is no need for setGeometry. If you want to scale or move the item on the scene, you can do it after addWidget as for any other QGraphicsItem. E.g. addWidget(lbl)->setPos(10, 10).
The magic bullet you need is WA_NoSystemBackground. It disables background painting for QLabel completely. So, the full code would be:
QLabel *lbl = new QLabel;
QMovie *mv = new QMovie("c:/tmp/sun.gif");
mv->start();
lbl->setAttribute(Qt::WA_NoSystemBackground);
lbl->setMovie(mv);
scene.addWidget(lbl);
It works fine for me. However I consider it over-complicated. You should not use proxy widgets in scene unless necessary. You can easily add a movie using QMovie and QGraphicsPixmapItem and switching pixmaps as movie frames change. I wrote a convenient class for this:
Header:
class GraphicsMovieItem : public QObject, public QGraphicsPixmapItem {
Q_OBJECT
public:
GraphicsMovieItem(QGraphicsItem* parent = 0);
void setMovie(QMovie* movie);
private:
QMovie* m_movie;
private slots:
void frameChanged();
};
Source:
GraphicsMovieItem::GraphicsMovieItem(QGraphicsItem *parent)
: QGraphicsPixmapItem(parent), m_movie(0) {
}
void GraphicsMovieItem::setMovie(QMovie *movie) {
if (m_movie) {
disconnect(m_movie, SIGNAL(frameChanged(int)), this, SLOT(frameChanged()));
}
m_movie = movie;
if (m_movie) {
connect(m_movie, SIGNAL(frameChanged(int)), this, SLOT(frameChanged()));
}
frameChanged();
}
void GraphicsMovieItem::frameChanged() {
if (!m_movie) { return; }
setPixmap(m_movie->currentPixmap());
}
Usage:
QMovie *mv = new QMovie("c:/tmp/sun.gif");
GraphicsMovieItem* item = new GraphicsMovieItem();
item->setMovie(mv);
scene.addItem(item);
I would like to write a custom QLabel subclass with some more features for responsive design. In thisexample, I want to write a QLabel which scales the text based on the useable space. This is quite easy but also has some problems because of Qt-intern stuff. (I have to scale the text to 0.9 of the useable space, otherwise resizing the window / widget gets buggy)
Now I wan't to add a way to hide the label completely when the font size is bellow a specific threshold. However, this seems to be quite a complex task.
Here is what I have sofar in the classes resizeEvent(QResizeEvent *event) function.
Right now, my function only sets the text to "" when the size would be bellow the threshold.
void CustomLabel::resizeEvent (QResizeEvent * event ) {
if(autoFontResize) {
this->setSilentText(labelText); // just the normal setText function, I overwrote it for the subclass
QFont f = this->font();
int flags = Qt::TextDontClip|Qt::TextWordWrap;
QRect fontBoundRect = this->fontMetrics().boundingRect(this->rect(), flags, this->text());
float xFactor = (float)event->size().width() / (float)fontBoundRect.width();
float yFactor = (float)event->size().height() / (float)fontBoundRect.height();
float factor = xFactor < yFactor ? xFactor : yFactor;
f.setPointSizeF(f.pointSize()*factor*0.9); //
if(minimumFontSize != 0) { // 0 = no minimum Size for the font
if(f.pointSize() < minimumFontSize) {
if(hideFontOnMinimum) { // either Hide or set to the limit size
this->setSilentText(""); //replace text
} else {
f.setPointSizeF(minimumFontSize);
}
}
}
this->setFont(f);
}
QLabel::resizeEvent(event);
}
By the way, some parts of the code are found on stackoverflow, not mine. ;)
What I would like to do is to completely hide() the label. However the label doesn't know when It can show() again since the resizeEvent doesn't seem to be called after that.
Any ideas?
Thanks!
As you've noticed, if you call hide() on the widget it fails to receive a resize event. Since you're customising the class anyway, rather than calling hide(), you could just set a class variable to note that it's hidden and overload the paintEvent function, not to draw the widget if the variable is set: -
void CustomLabel::paintEvent(QPaintEvent * event)
{
if(m_hideOnMinimum)
return;
QLabel::paintEvent(event);
}
Note that by not painting the label, it will be hidden, but the user may still be able to interact with it, so you will need to disable it or overload keyboard / mouse events too.
Maybe someone can help me with the following problem:
I want to draw the content of a QImage in a QGLWidget, but the widget is painted in black.
class QGLCanvas {
public:
QGLCanvas(QWidget* parent) : QGLWidget(parent) {
}
void setImage(const QImage* image) {
img = image;
}
void paintEvent(QPaintEvent*) {
// From Painter Documentation Qt
QPainter p(this);
p.setRenderHint(QPainter::SmoothPixmapTransform, 1);
p.drawImage(this->rect(), *img);
p.end();
}
public slots:
void rgb_data(const void *data) {
memcpy((void *)img->bits(), data, img->byteCount()); // data will be copied (sizes are known)
// img.save("text.png"); // saves right image
this->update(); // calls repaint, but does not draw the image.
}
private:
QImage *img;
}
The Bug:
When the slot is called, the memory is copied to the image. If the image is saved, the content is correct. But the repaint method just draws black content to the widget.
The Fix:
If the memcpy line is implemented outside the slot, the image content is drawn to the widget. This fix increased code complexity a lot. Thus, i've got the following question:
The Question:
Why does the memcpy not work within the slot? Is this a general problem with Qt?
There's nothing special about a slot which would stop your code from working.
What's probably the issue is that when you call update(), a repaint is scheduled but happens asynchronously. From the code you've provided the most likely cause is img being modified between the calls to rbg_data and paintEvent
You want to be sure about the format of the QImage. When you call bits and are expecting it to be RGB, you need to check the format.
if( img->format() != QImage::Format_RGB888 )
{
// convert the image format to RGB888
*img = img->convertToFormat(QImage::Format_RGB888);
}
This way, Qt will know the image format when trying to paint it. If you fill it with RGB data but the QImage is "formatted" as ARGB, you will get some painting errors.