AttributeError: 'PySide6.QtWidgets.QWidget' object has no attribute 'geometryChanged'
-
I'm replacing graphs I had created with Matplolib with plots I create with pyqtgraph. They look great but I can't seem to get them to display on a tab. I had used FirgureCanvas with Matplotlib. PyCharm's Ai Assistant says there is an open issue on Github. I don't see it. Is there an open issue?
-
The C++ QWidget doesn't have a geometryChanged member. There are other classes (eg QGraphicsWidget) that have a geometryChanged signal. PyQt generally sticks to matching the C++ API. I believe that PySide does as well.
What's the code that generates this error?
-
@jeremy_k It's not in my code. It looks like it's in pyqtgraph. Here's more info from the traceback. File "C:\Users\E_J_S\OneDrive\Documents\Projects\Python\NewWorld\312\Lib\site-packages\pyqtgraph\graphicsItems\GraphicsLayout.py", line 136, in addItem
item.geometryChanged.connect(self._updateItemBorder)
^^^^^^^^^^^^^^^^^^^^ -
@Ed-Schneider
But it depends on what your code does that can lead to such an error, especially in Python.Could you be in the situation described in 'ScatterPlotItem' object has no attribute 'geometryChanged' which generates exactly your error?
-
@JonI've been doing handstands to see if I could see a situation where my code would control whether an object has an attribute. The only thing I came up with is when the class which my object is an instance of does not have an attribute 'geometryChanged'. In my situation the offending object "container" appears to be an instance of the QWidget class. I'm trying to avoid putting my plot directly onto the GraphicsLayoutWidget. This seems to be what the solution is also suggesting in "ScatterPlotItem...." , so I thought I was avoiding/addressing this issue. Here's what I used -
container = QWidget() # Create a container widget
layout = QVBoxLayout() # Create a vertical box layout
layout.addWidget(self.graphWidget)
container.setLayout(layout)return container
The exception happens when "container" is passed as "plot_item"
Here's the entire traceback.Traceback (most recent call last):
File "C:\Users\E_J_S\OneDrive\Documents\Projects\Python\NewWorld\src\Rubicon_12.py", line 7079, in <lambda>
lambda: self.traffic_control("open_configuration"))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\E_J_S\OneDrive\Documents\Projects\Python\NewWorld\src\Rubicon_12.py", line 6381, in traffic_control
self.this_instance.execute(self.common)
File "C:\Users\E_J_S\OneDrive\Documents\Projects\Python\NewWorld\src\Rubicon_12.py", line 13233, in execute
self._director.configuration_active.create_a_configuration_plot_for_plot_and_gallery_tabs(
File "C:\Users\E_J_S\OneDrive\Documents\Projects\Python\NewWorld\src\Rubicon_12.py", line 2225, in create_a_configuration_plot_for_plot_and_gallery_tabs
director.plot_to_gui_from_pyqtgraph_ai_assistant(new_fig)
File "C:\Users\E_J_S\OneDrive\Documents\Projects\Python\NewWorld\src\Rubicon_12.py", line 5969, in plot_to_gui_from_pyqtgraph_ai_assistant
widget_plot.addItem(plot_item)
File "C:\Users\E_J_S\OneDrive\Documents\Projects\Python\NewWorld\312\Lib\site-packages\pyqtgraph\graphicsItems\GraphicsLayout.py", line 136, in addItem
item.geometryChanged.connect(self._updateItemBorder)
^^^^^^^^^^^^^^^^^^^^
AttributeError: 'PySide6.QtWidgets.QWidget' object has no attribute 'geometryChanged' -
@Ed-Schneider said in AttributeError: 'PySide6.QtWidgets.QWidget' object has no attribute 'geometryChanged':
The only thing I came up with is when the class which my object is an instance of does not have an attribute 'geometryChanged'.
That's the symptom.
In my situation the offending object "container" appears to be an instance of the QWidget class. I'm trying to avoid putting my plot directly onto the GraphicsLayoutWidget.
Why? I'm presuming that the error is in reference to this GraphicsLayout:
Used for laying out GraphicsWidgets in a grid.
Trying to add a non-GraphicsWidget appears to be contrary to the expectation of the class.
Here's what I used -
container = QWidget() # Create a container widget layout = QVBoxLayout() # Create a vertical box layout layout.addWidget(self.graphWidget) container.setLayout(layout) return container
The exception happens when "container" is passed as "plot_item"
This isn't enough code to make a meaningful guess. Also, please use code tags ( </> in the tool bar).
Is this the PlotItem referred to?
GraphicsWidget implementing a standard 2D plotting area with axes.
Note that it says GraphicsWidget, not QWidget.
-
@jeremy_k The full application is more than 17,000 lines. Here's the function which creates and returns container. The returned variable becomes "new_fig". New_fig is then passed as an argument to another function in which it is called "plot_item". When plot_item is passed to addItem, the result is the exception. Here's the basic code . I've tried several variations which is why some statements are commented out
def plot_a_configuration(self, director, common): # self.graphWidget = pg.GraphicsWidget() self.graphWidget = pg.PlotWidget() # director.setCentralWidget(self.graphWidget) x = [director.configuration_active.point_coords.iloc[each_point] .iloc[director.configuration_active.hor_dim] for each_point in director.configuration_active.range_points] y = [director.configuration_active.point_coords.iloc[each_point] .iloc[director.configuration_active.vert_dim] for each_point in director.configuration_active.range_points] common.show_connector = True common.show_bisector = True director.configuration_active.rival_a = 2 director.configuration_active.rival_b = 4 director.configuration_active.bisector_start_x = -1.1 director.configuration_active.bisector_end_x = .65 director.configuration_active.bisector_start_y = self.vert_min director.configuration_active.bisector_end_y = self.vert_max self.graphWidget.setBackground('w') self.graphWidget.setTitle("Candidates", color="k", size="20pt") styles = {'color': 'k', 'font-size': '15pt'} self.graphWidget.setLabel( 'left', director.configuration_active.dim_names[self.vert_dim]) self.graphWidget.setLabel( 'bottom', director.configuration_active.dim_names[self.hor_dim]) self.graphWidget.showGrid(x=True, y=True) self.graphWidget.setXRange( director.configuration_active.hor_min, director.configuration_active.hor_max, padding=None) self.graphWidget.setYRange( director.configuration_active.vert_min, director.configuration_active.vert_max, padding=None) # for each_label in self.range_points: for each_label in director.configuration_active.range_points: self.a_label = pg.TextItem( text=director.configuration_active.point_labels[each_label], color="k", anchor=(.5, 1), border='w', fill=None) self.a_label.setPos( x[each_label], y[each_label]) self.graphWidget.addItem(self.a_label) pen = pg.mkPen(color=(255, 0, 0)) self.graphWidget.scatterPlot(x, y, pen=pen, symbol='o', symbolSize=5, symbolBrush='k') if common.show_connector: self.graphWidget.plot( [x[director.configuration_active.rival_a], x[director.configuration_active.rival_b]], [y[director.configuration_active.rival_a], y[director.configuration_active.rival_b]], pen=pen) if common.show_bisector: self.graphWidget.plot( [director.configuration_active.bisector_start_x, director.configuration_active.bisector_end_x], [director.configuration_active.bisector_start_y, director.configuration_active.bisector_end_y], pen=pen) container = QWidget() # Create a container widget # container = pg.GraphicsWidget() layout = QVBoxLayout() # Create a vertical box layout layout.addWidget(self.graphWidget) container.setLayout(layout) return container
-
@Ed-Schneider said in AttributeError: 'PySide6.QtWidgets.QWidget' object has no attribute 'geometryChanged':
@jeremy_k The full application is more than 17,000 lines.
Understood, but while you are faced with narrowing down a code base that is too large to post, readers are left guessing at the infinite possibilities of code that they can't read. It's the original poster's job to do the hard work of formulating a reasonably complete and self contained question.
Here's the function which creates and returns container. The returned variable becomes "new_fig". New_fig is then passed as an argument to another function in which it is called "plot_item". When plot_item is passed to addItem, the result is the exception.
The type of the object which
addItem()
belongs to is an important missing piece. Most of the code, including setting labels and ranges, isn't relevant for this question. -
@jeremy_k I understand your point. On my list of todo's is to put together a small file with all the relevant information. Unfortunately I'm in the midst of some massive reactoring which I want to complete before I create that small file. With luck I'll get to that within a week. In the mean time here is the code of the function which has the addItem which raises the exception. I understand this code will not work even after the initial addItem issue is solved. I'll fix that once the initial problem is resolved.
def plot_to_gui_from_pyqtgraph_ai_assistant(self, plot_item): print(f"DEBUG -- at top of plot_to_gui_from_pyqtgraph_ai_assistant {plot_item=}") # Create a GraphicsLayoutWidget to place your pyqtgraph plot onto widget_plot = GraphicsLayoutWidget() # Add your plot_item to the GraphicsLayoutWidget widget_plot.addItem(plot_item) self.tab_plot_scroll_area.setWidget(widget_plot) # plot_item_clone = deepcopy(plot_item) # Repeat the process for the Gallery tab widget_gallery = GraphicsLayoutWidget() # widget_gallery.addItem(plot_item_clone) self.tab_gallery_layout.addWidget(widget_gallery) # Add a spacer at the end to push plots to the top spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.tab_gallery_layout.addItem(spacer)
-
@jeremy_k I put together a file with all the relevant information. In the process I was able to get the plots to appear in the tabs but with two problems. The Plot tab works correctly with subsequent plots replacing previous ones. The Gallery plot should be scrollable with all plots appearing with a spacer between them. I am unable to get the spacer to work at all. In the attached code I've commented out the spacer line to at least get it to run. I've also included the matplotlib code to show what I would like it to be doing. I've commented out the matplotlib code to allow the pyqtgraph code to run. Any insight into how to make the pyqtgraph code work like the matplotlib code would be appreciated.
import sys from PySide6.QtWidgets import QApplication, QTabWidget, QWidget, QScrollArea import matplotlib.pyplot as plt from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas from PySide6.QtWidgets import QVBoxLayout, QSpacerItem, QSizePolicy, QMainWindow import pyqtgraph as pg from pyqtgraph import GraphicsLayoutWidget, PlotWidget class TabbedWindow(QMainWindow): def __init__(self): super(TabbedWindow, self).__init__() self.setWindowTitle("Tabbed Window") self.setGeometry(100, 100, 800, 600) self.tab_widget = QTabWidget() self.setCentralWidget(self.tab_widget) self.tab_widget.setTabPosition(QTabWidget.South) self.tab_plot_scroll_area = QScrollArea() self.tab_gallery_widget = QWidget() self.tab_gallery_layout = QVBoxLayout() self.tab_gallery_widget.setLayout(self.tab_gallery_layout) self.tab_gallery_scroll_area = QScrollArea() self.tab_gallery_scroll_area.setWidgetResizable(True) self.tab_gallery_scroll_area.setWidget(self.tab_gallery_widget) self.tab_widget.addTab(self.tab_plot_scroll_area, "Plot") self.tab_widget.addTab(self.tab_gallery_scroll_area, "Gallery") def add_plot_using_pyqtgraph(self, plot_widget1, plot_widget2): # Add the plot to the Plot tab (replace the current plot) self.tab_plot_scroll_area.setWidget(plot_widget1) # Add the plot to the Gallery tab (append to existing plots) self.tab_gallery_scroll_area.setWidget(plot_widget2) # Add a spacer at the end to push plots to the top spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) # self.tab_gallery_scroll_area.addItem(spacer) def add_plot_using_matplotlib(self, fig): # Add the plot to the Plot tab (replace the current plot) canvas_plot = FigureCanvas(fig) width, height = int(fig.get_size_inches()[0] * fig.dpi), int(fig.get_size_inches()[1] * fig.dpi) canvas_plot.setFixedSize(width, height) canvas_plot.draw() self.tab_plot_scroll_area.setWidget(canvas_plot) # Add the plot to the Gallery tab (append to existing plots) canvas_gallery = FigureCanvas(fig) canvas_gallery.setFixedSize(width, height) canvas_gallery.draw() self.tab_gallery_layout.addWidget(canvas_gallery) # Add a spacer at the end to push plots to the top spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.tab_gallery_layout.addItem(spacer) def example_plot_A_using_matplotlib(): fig, ax = plt.subplots() ax.plot([1, 2, 3, 4], [1, 4, 9, 16], 'ro') ax.set_title('Example Plot A using Matplotlib') ax.set_xlabel('X-axis') ax.set_ylabel('Y-axis') return fig def example_plot_B_using_matplotlib(): fig, ax = plt.subplots() ax.plot([16, 9, 4, 1], [1, 2, 3, 4], 'ro') ax.set_title('Example Plot B using Matplotlib') ax.set_xlabel('X-axis') ax.set_ylabel('Y-axis') return fig def example_plot_A_using_pyqtgraph(): win = pg.GraphicsLayoutWidget() p1 = win.addPlot(title="Example Plot A using pyqtgraph") p1.scatterPlot([1, 2, 3, 4], [1, 4, 9, 16], symbol='o') win.setBackground('w') p1.setLabel('left', 'Y-axis', units='A') p1.setLabel('bottom', 'X-axis', units='s') return win def example_plot_B_using_pyqtgraph(): win = pg.GraphicsLayoutWidget() p1 = win.addPlot(title="Example Plot B using pyqtgraph") p1.scatterPlot([16, 9, 4, 1], [1, 2, 3, 4], symbol='o') win.setBackground('w') p1.setLabel('left', 'Y-axis', units='A') p1.setLabel('bottom', 'X-axis', units='s') return win if __name__ == "__main__": app = QApplication(sys.argv) window = TabbedWindow() # fig = example_plot_A_using_matplotlib() # window.add_plot_using_matplotlib(fig) # fig = example_plot_B_using_matplotlib() # window.add_plot_using_matplotlib(fig) A_plot = example_plot_A_using_pyqtgraph() same_plot_A_gallery = example_plot_A_using_pyqtgraph() B_plot = example_plot_B_using_pyqtgraph() same_plot_B_gallery = example_plot_B_using_pyqtgraph() window.add_plot_using_pyqtgraph(A_plot, same_plot_A_gallery) window.add_plot_using_pyqtgraph(B_plot, same_plot_B_gallery) window.show() sys.exit(app.exec())
-
I worked with PyCharm's Ai Assistant and solved the problem. Here are the modifications needed to what I posted previously. It also freezes tab_gallery so it serves as a historical record while tab_plot is still modifiable.
code_tex def add_plot_using_pyqtgraph(self, plot_widget1, plot_widget2): # Add the plot to the Plot tab (replace the current plot) self.tab_plot_scroll_area.setWidget(plot_widget1) # Disable mouse events on the plot in tab_gallery # Disable mouse events on all plots in plot_widget2 disable_mouse_events(plot_widget2) # Add the plot to the Gallery tab (append to existing plots) widget = QWidget() widget.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) layout = QVBoxLayout() layout.addWidget(plot_widget2) layout.setSizeConstraint(QVBoxLayout.SetNoConstraint) # self.tab_gallery_scroll_area.setWidget(plot_widget2) # Add a spacer at the end to push plots to the top spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) layout.addItem(spacer) widget.setLayout(layout) self.tab_gallery_layout.addWidget(widget) def disable_mouse_events(graphics_layout_widget): # Loop over each row for row in range(graphics_layout_widget.ci.layout.rowCount()): # Loop over each column for col in range(graphics_layout_widget.ci.layout.columnCount()): # Get the item in the cell item = graphics_layout_widget.ci.layout.itemAt(row, col) # Check if item is a PlotItem if isinstance(item, pg.graphicsItems.PlotItem.PlotItem): # Disable mouse events for this PlotItem item.vb.setMouseEnabled(x=False, y=False)t
-
How can we get this switched from unsolved to solved? Is this something I can do as the author, or is this something a moderator needs to do?
-
@Ed-Schneider hi,
As the author of the thread, you can do it yourself. Click on the cog button and you will have an option near the bottom of the menu to mark the thread as solved.
-
Thanks. Will do
-