QWebEngineView触摸滚动

如何解决QWebEngineView触摸滚动

我有一个从旧版Qt 4.7.4移植到Qt5的应用程序,据我了解,QWebView变成了QWebEngineView,并且在我使用FlickCharm的QWebView中,它在Qt示例中仍然可以与QScrollArea正常工作(例如QListWidget,QTableWidget,...,当然还有QScrollArea),但不再包含QWebEngineView,下面是激活FlickCharm的代码,该代码正在Qt4上运行:

void FlickCharm::activateOn(QWidget *widget,QWidget* p_viewport)
{
    QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea*>(widget);
    if (scrollArea) {
        // Widget is a scroll area

        QAbstractItemView *itemView = qobject_cast<QAbstractItemView*>(widget);
        if(itemView)
        {
            itemView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
            itemView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
        }

        QWidget *viewport = scrollArea->viewport();
        if ( p_viewport )
        {
            viewport = p_viewport;
        }
        else
        {
            scrollArea->installEventFilter(this);
        }

        viewport->installEventFilter(this);

        QHash<QWidget*,FlickData*>::iterator oldViewport;
        if ( ( oldViewport = d->flickData.find(viewport) ) != d->flickData.end() )
        {
            delete oldViewport.value();
        }

        d->flickData.remove(viewport);
        d->flickData[viewport] = new FlickData;
        d->flickData[viewport]->widget = widget;
        d->flickData[viewport]->state = FlickData::Steady;
        d->flickData[viewport]->customViewPort = (viewport != scrollArea->viewport());

        return;
    }

    QWebView *webView = qobject_cast<QWebView*>(widget);
    if (webView) {
        // Widget is a web view
        webView->installEventFilter(this);

        QHash<QWidget*,FlickData*>::iterator oldViewport;
        if ( ( oldViewport = d->flickData.find(webView) ) != d->flickData.end() )
        {
            delete oldViewport.value();
        }

        d->flickData.remove(webView);
        d->flickData[webView] = new FlickData;
        d->flickData[webView]->widget = webView;
        d->flickData[webView]->state = FlickData::Steady;

        return;
    }

    qWarning() << "FlickCharm only works on QAbstractScrollArea (and derived classes)";
    qWarning() << "or QWebView (and derived classes)";
}

在FlickData类中,有以下函数可以进行滚动:

bool scrollWidget(const int dx,const int dy)
    {
        {
            QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea*>(widget);
            if (scrollArea) {
                const int x = scrollArea->horizontalScrollBar()->value();
                const int y = scrollArea->verticalScrollBar()->value();
                scrollArea->horizontalScrollBar()->setValue(x - dx);
                scrollArea->verticalScrollBar()->setValue(y - dy);
                return (scrollArea->horizontalScrollBar()->value() != x
                        || scrollArea->verticalScrollBar()->value() != y);
            }
        }

        {
            QWebEngineView *webEngineView = qobject_cast<QWebEngineView*>(widget);
            if (webEngineView) {
                const QPointF position = webEngineView->page()->scrollPosition();
                const QPointF newPosition = position - QPointF(dx,dy);
                webEngineView->page()->runJavaScript(QString("window.scrollTo(%1,%2);").arg(newPosition.x()).arg(newPosition.y()));
                return webEngineView->page()->scrollPosition() != position;
            }
        }
        return false;
    }

在Qt5中,我尝试如上所述直接应用于QWebEngineView:

    QWebEngineView *webEngineView = qobject_cast<QWebEngineView*>(widget);
    if (webEngineView) {
        webEngineView->installEventFilter(this);

        QHash<QWidget*,FlickData*>::iterator oldViewport;
        if ( ( oldViewport = d->flickData.find(webEngineView) ) != d->flickData.end() )
        {
            delete oldViewport.value();
        }

        d->flickData.remove(webEngineView);
        d->flickData[webEngineView] = new FlickData;
        d->flickData[webEngineView]->widget = webEngineView;
        d->flickData[webEngineView]->state = FlickData::Steady;

        return;
    }

并且还尝试分页显示我认为是视口的视图:

    QWebEngineView *webEngineView = qobject_cast<QWebEngineView*>(widget);
    if (webEngineView) {
        QWidget *viewport = webEngineView->page()->view();

        webEngineView->installEventFilter(this);
        viewport->installEventFilter(this);

        QHash<QWidget*,FlickData*>::iterator oldViewport;
        if ( ( oldViewport = d->flickData.find(viewport) ) != d->flickData.end() )
        {
            delete oldViewport.value();
        }

        d->flickData.remove(viewport);
        d->flickData[viewport] = new FlickData;
        d->flickData[viewport]->widget = webEngineView;
        d->flickData[viewport]->state = FlickData::Steady;

        return;
    }

在QWebEngineView :: page()(即QWebEnginePage)中,有一个scrollPosition()函数,但这来自Q_PROPERTY,但没有写访问器函数,但是我发现一个可以和平使用我尝试使用的javascript滚动的代码:

    QWebEngineView *webEngineView = qobject_cast<QWebEngineView*>(widget);
    if (webEngineView) {
        const QPointF position = webEngineView->page()->scrollPosition();
        const QPointF newPosition = position - QPointF(dx,dy);
        webEngineView->page()->runJavaScript(QString("window.scrollTo(%1,%2);").arg(newPosition.x()).arg(newPosition.y()));
        return webEngineView->page()->scrollPosition() != position;
    }

但是在添加一些日志后,我看到我从未传递过QWebEngineView的scrollWidget,并且由于我拥有自己的实例化了继承了QApplication的类,因此我可以看到,它不是直接单击的QWebEngineView,而是一个RenderWidgetHostViewQtDelegateWidget(的QWebEngineView作为父级)(无法从任何地方访问),我看到了这一点,因为我重新实现了MyApplication :: notify来登录单击了哪些小部件:

bool MyApplication::notify(QObject* p_object,QEvent* p_event)
{
    [...]
    if ( p_event->type() == QEvent::MouseButtonPress )
    {
        QWidget* widget = dynamic_cast<QWidget*>(p_object);
        if (widget != 0)
        {
            qDebug().nospace() << "Mouse pressed on [" << (widget->isEnabled() ? "enabled" :"disabled") << "] widget: "
                               << p_object
                               << ",parent: " << p_object->parent();
        }
        else
        {
            qDebug().nospace() << "Mouse pressed on object: " << p_object
                               << ",parent: " << p_object->parent();
        }
    }
    else if ( p_event->type() == QEvent::MouseButtonRelease )
    {
        QWidget* widget = dynamic_cast<QWidget*>(p_object);
        if (widget != 0)
        {
            qDebug().nospace() << "Mouse release on [" << (widget->isEnabled() ? "enabled" :"disabled") << "] widget: "
                               << p_object
                               << ",parent: " << p_object->parent();
        }
        else
        {
            qDebug().nospace() << "Mouse release on object: " << p_object
                               << ",parent: " << p_object->parent();
        }
    }
    [...]
}

此外,当我尝试在QWebEngineView上滚动时,将改为选择文本。

这是flickcharm的完整代码:https://doc.qt.io/archives/qt-4.8/qt-demos-embedded-anomaly-src-flickcharm-cpp.html

此外,我已经看到有一种使WebEngineView可滑动的解决方案,但仅适用于QtQuick QML:https://stackoverflow.com/a/42817245

任何人都知道如何在QWebEngineView上进行触摸滚动吗?

谢谢

编辑:由于RenderWidgetHostViewQtDelegateWidget是QWebEngineView的子级,因此我尝试从webEngineView-> children()访问它(如果可能的话,至少作为QWidget),但未成功,因为其唯一的子级是其QVBoxLayout(为空) )和FlickCharm。

解决方法

我终于在FlickCharm :: activateOn为我放置的QWebEngineView中做我想做的事情:

        QWebEngineView *webEngineView = qobject_cast<QWebEngineView*>(widget);
        if (webEngineView) {
            QLayout* webEngineViewLayout = webEngineView->layout();
            QWidget* webEngineViewChildWidget = 0;
            for (int i = 0; i < webEngineViewLayout->count(); ++i)
            {
                webEngineViewChildWidget = qobject_cast<QWidget*>(webEngineViewLayout->itemAt(i)->widget());

                if (webEngineViewChildWidget != nullptr)
                {
                    // There should be only one QWidget under QWebEngineView,but break just in case
                    break;
                }
            }

            if (webEngineViewChildWidget != nullptr)
            {
               // Install event filter on widget found in QWebEngineView layout
                webEngineViewChildWidget->installEventFilter(this);

                QHash<QWidget*,FlickData*>::iterator oldViewport;
                if ( ( oldViewport = d->flickData.find(webEngineViewChildWidget) ) != d->flickData.end() )
                {
                    delete oldViewport.value();
                }

                d->flickData.remove(webEngineViewChildWidget);
                d->flickData[webEngineViewChildWidget] = new FlickData;
                d->flickData[webEngineViewChildWidget]->widget = webEngineView;
                d->flickData[webEngineViewChildWidget]->state = FlickData::Steady;
            }
            else
            {
                // Web engine view has not yet a page loaded,activate "again" when a page has been loaded
                connect(webEngineView,&QWebEngineView::loadFinished,this,&FlickCharm::ReactToWebEngineViewLoaded);
            }

            return;
        }

FlickCharm::ReactToWebEngineViewLoaded()是:

void FlickCharm::ReactToWebEngineViewLoaded()
{
    QWebEngineView* webEngineView = qobject_cast<QWebEngineView*>(sender());

    if (webEngineView != nullptr)
    {
        // We need to pass only once there then we can disconnect now
        disconnect(webEngineView,&FlickCharm::ReactToWebEngineViewLoaded);

        // Activate "again" so that view actually uses flick charm
        activateOn(webEngineView);
    }
    else
    {
        LOG_ERROR("Web engine view is NULL");
        assert(webEngineView != nullptr);
    }
}

当然void ReactToWebEngineViewLoaded()被声明为flickcharm.h中的广告位。

FlickData::scrollWidget中使用QWebEngineView (这很棘手,我不喜欢qApp-> processEvents调用,但是如果我们要实际比较值以返回函数,则必须使用它们):


            QWebEngineView *webEngineView = qobject_cast<QWebEngineView*>(widget);
            if (webEngineView) {
                 webEngineView->page()->runJavaScript(QString("window.scrollBy(%1,%2);").arg(-dx).arg(-dy));
                return true;
            }

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-