QWebEngineView:"cannot read property 'pageX' of undefined" 静音散景图例时

QWebEngineView: "cannot read property 'pageX' of undefined" when muting bokeh legend

我正在使用 PyQt5 创建一个 GUI,在这个 GUI 中,我使用 QWebEngineView.

可视化 Bokeh 图表

它工作正常,但是当我尝试像 this 那样实现 "muting" 图例时,我收到一个错误:

js: Uncaught TypeError: Cannot read property 'pageX' of undefined

如果我使用 show 方法,我会在我的浏览器中得到预期的结果。但是,如果我使用保存并将其显示到 QWebEngineView,我会收到上述错误。

有什么想法吗?

我的 Gui Class 中的插槽要在 QWebEngineView 中绘制和显示:

注意:忽略条形图和披萨图,与此事相关的是散点图和折线图

def plotGraph(self, df=None):
    # Get parameters to plot
    x = str(self.ui.comboBox_x_axis.currentText())
    y = str(self.ui.comboBox_y_axis.currentText())
    # Define axis types
    try:
        x_axis_type = str(
            self.ui.comboBox_plot_scale.currentText()).split('-')[0]
        y_axis_type = str(
            self.ui.comboBox_plot_scale.currentText()).split('-')[1]
    except:
        x_axis_type = 'auto'
        y_axis_type = 'auto'
    # Define kind of graph
    kind = str(self.ui.comboBox_plot_style.currentText())
    # For bar chart define groups
    group = str(self.ui.comboBox_group.currentText())
    # Prepare data for plot
    if (kind == 'bar' and group != "Don't group"):
        data = df[[x, y, group]]
    else:
        data = df[[x, y]]
        data = data.sort_values(x, axis=0)
    # Dynamically define plot size
    width = round(self.ui.webViewer.frameGeometry().width())
    height = round(self.ui.webViewer.frameGeometry().height())
    # Plot and save html
    self.plot = self.graph.plot(
        data, kind, x_axis_type, y_axis_type, width, height)
    self.plot_num = 1
    # Display it at QWebEngineView
    self.ui.webViewer.setUrl(QtCore.QUrl(
        "file:///C:/Users/eandrade_brp/Documents/git/tl-data-viewer/plot.html"))

这是处理所有散景图的图表 class(我省略了一些不必要的代码)

class Graph(object):
    """docstring for ClassName"""

    def __init__(self, file_name="plot.html"):
        super(Graph, self).__init__()
        output_file(file_name)

    def plot(self, data, kind, x_axis_type, y_axis_type, width, height):
        p = None
        if kind == 'scatter' or kind == 'line':
            layout, p = self.createFigure(
                data, kind, x_axis_type, y_axis_type, width, height)
        elif kind == 'bar':
            layout = self.plot_Bar(data, width, height)
        elif kind == 'pizza':
            layout = self.plot_Pizza(
                data, width, height)
        # Show/save
        save(layout)
        return p

    def createFigure(self, data, kind, x_axis_type, y_axis_type, width, height):
        source, xdata, ydata, xvalues, yvalues = self.prepare_data(data)
        # Define tool
        tools = "pan, box_zoom, lasso_select, undo, redo"
        wheel_zoom = WheelZoomTool()
        hover = HoverTool(
            tooltips=[
                (data.columns[0],          '$x'),
                (data.columns[1],          '$y')],
            mode='mouse')
        # Create first figure and customize
        fig1 = figure(title="{} vs {}" .format(ydata, xdata), tools=tools,
                      x_axis_type=x_axis_type, y_axis_type=y_axis_type,
                      toolbar_location="right", plot_width=round(0.9 * width),
                      plot_height=round(0.75 * height))
        fig1.add_tools(wheel_zoom)
        fig1.add_tools(hover)
        fig1.toolbar.active_scroll = wheel_zoom
        fig1.background_fill_color = "beige"
        fig1.background_fill_alpha = 0.4

        # Create second figure and customize
        fig2 = figure(title='Overview', title_location="left",
                      x_axis_type=x_axis_type, y_axis_type=y_axis_type,
                      tools='', plot_width=round(0.9 * width), plot_height=round(0.25 * height))
        fig2.xaxis.major_tick_line_color = None
        fig2.xaxis.minor_tick_line_color = None
        fig2.yaxis.major_tick_line_color = None
        fig2.yaxis.minor_tick_line_color = None
        fig2.xaxis.major_label_text_color = None
        fig2.yaxis.major_label_text_color = None

        # Add View box to second figure
        rect = Rect(x='x', y='y', width='width', height='height', fill_alpha=0.1,
                    line_color='black', fill_color='black')
        fig2.add_glyph(source, rect)

        # Add JS callBacks
        self.JS_linkPlots(fig1, source)

        # Plots
        plots = self.plot_continuous(source, xvalues, yvalues, fig1, kind)
        self.plot_continuous(source, xvalues, yvalues, fig2, kind)
        s2 = ColumnDataSource(data=dict(ym=[0.5, 0.5]))
        fig1.line(x=[0, 1], y='ym', color="orange",
                  line_width=5, alpha=0.6, source=s2)

        # Add legends
        legend = Legend(items=[
            (ydata, plots)],
            location=(0, 0),
            click_policy="mute")
        # Add legend to fig layout
        fig1.add_layout(legend, 'below')
        # Layout
        layout = col(fig1, fig2)
        return layout, fig1

    def plot_continuous(self, source, xvalues, yvalues, fig, kind, color=0):
        if kind == 'scatter':
            s = fig.scatter(
                xvalues, yvalues,
                fill_color='white', fill_alpha=0.6,
                line_color=Spectral10[color], size=8,
                selection_color="firebrick",
                nonselection_fill_alpha=0.2,
                nonselection_fill_color="blue",
                nonselection_line_color="firebrick",
                nonselection_line_alpha=1.0)
            return [s]

        elif kind == 'line':
            l = fig.line(
                xvalues, yvalues, line_width=2, color=Spectral10[color], alpha=0.8,
                muted_color=Spectral10[color], muted_alpha=0.2)

            s = fig.scatter(
                xvalues, yvalues,
                fill_color="white", fill_alpha=0.6,
                line_color=Spectral10[color], size=8,
                selection_color="firebrick",
                nonselection_fill_alpha=0.2,
                nonselection_fill_color="blue",
                nonselection_line_color="firebrick",
                nonselection_line_alpha=1.0)
            return [s, l]
        else:
            raise 'Wrong type of plot'

    def prepare_data(self, data):
        xdata = data.columns[0]
        xvalues = data[xdata]
        ydata = data.columns[1]
        yvalues = data[ydata]
        source = ColumnDataSource(data)
        return source, xdata, ydata, xvalues, yvalues

首先,免责声明:Bokeh 未声明可与 Qt 浏览器小部件一起运行,无论是全部还是部分。我们根本没有能力在持续测试下严格地维持这一主张,因此我们无法做到。如果有人愿意作为该功能的维护者介入,我们将来可能会提出更有力的支持声明。


Bokeh 使用第三方库 Hammer.js 跨不同平台提供统一的低级事件处理。 Bokeh 期望生成的事件具有 pageXpageY 属性。看来 Qt 的浏览器小部件不满足这种期望,导致您看到的错误。更新 Bokeh 使用的 Hammer 版本可能会解决这个问题。可能会引入解决方法。无论如何,它需要对 BokehJS 本身进行新的工作。

简短的回答是:这个交互式图例可能不适用于 Qt。作为解决方法,使用 Bokeh 小部件或 Qt 小部件来调高和显示字形,并且不要依赖交互式图例功能。

长期来看:Wo 可以研究上面建议的一些想法。但是我们需要帮助才能做到。 我们没有带宽、能力或经验来自己构建 Qt 应用程序来测试潜在的修复。如果您有能力与核心开发人员一起寻找解决方案,请感觉免费在 issue tracker.

上发布问题