【问题标题】:Same code with PyQT5/PySide2 runs on MacOS but throws an error on Linux与 PyQT5/PySide2 相同的代码在 MacOS 上运行,但在 Linux 上抛出错误
【发布时间】:2021-08-26 16:29:57
【问题描述】:

我使用 PySide2、numpy 和 matplotlib 编写了 GUI,它们在 MacOS 11.2.3 上运行没有任何问题。但是,在 Ubuntu 20.4 上运行代码时出现错误并且代码有效

我使用 PySide2 和 matplotlib 制作了一个 GUI。目标是单击图像并将单击的坐标存储在 csv 中。在 Ubuntu 上启动应用程序并单击“按钮”按钮后,我收到一条错误消息(在代码之后提供)。似乎在 Linux 下,代码一直运行到第 91 行(尽管还没有点击图像,因此存储它们的数据框显然是空的),而在 MacOS 上,解释器已停止在第 76 行等待用户的点击次数(在 MacOS 上图片已启动,终端上没有任何内容)

代码如下:

import numpy as np 
import pandas as pd
import os
import sys 
import matplotlib.pyplot as plt


from PySide2 import QtGui
from PySide2 import QtCore
from PySide2.QtWidgets import QWidget, QApplication, QPushButton, QFileDialog
os.environ['QT_MAC_WANTS_LAYER'] = '1'



coords_Event = [] 

size_win_x = 10
size_win_y = 7
dpi_val = 100


class GUI(QWidget):
    def __init__(self):
        super(GUI, self).__init__()

        self.initUI()

    def initUI(self):
        height_btn = 40 
        width_btn = 350
        
        button_position_x = 0
        button_position_y = 20 
        
        button_position_x = button_position_x + 50
        btn2 = QPushButton('Button', self)     
        btn2.clicked.connect(self.Event)
        btn2.resize(width_btn, height_btn + 20)
        btn2.move(button_position_y, button_position_x)       

        self.show()


    def Event(self):
        dir_path = os.getcwd()

        figure = plt.figure(figsize = (size_win_x,size_win_y), dpi=dpi_val) 

        global coords_Event
        coords_Event = []

        def onclick(event):
                ix, iy = event.xdata, event.ydata
                print('x = %d, y = %d'%(
                    ix, iy))
                global coords_Event
                coords_Event.append((ix, iy))
                return coords_Event        
        cid = figure.canvas.mpl_connect('button_press_event', onclick)

        pixel_array = np.array([[1,2,3], [100,5,6]])
        figure2 = plt.imshow(pixel_array)
        plt.savefig(dir_path + '/raw.png')
        plt.title("the title")
        plt.show()
        
        print("ok1")

        df = pd.DataFrame(coords_Event,
                columns = ['X', 'Y']) 
        string_csv = dir_path + "/coords_Event.csv"

        print("ok2")
        print(string_csv)
        df.to_csv(string_csv, 
        header=True)
        df2 = pd.read_csv(string_csv)  
        print("df2: ", df2)
        print(df2.iloc[0]["X"])
        print(df2.iloc[1]["X"])
        
        print("ok3")
        ic(coords_Event)

        clicked_data = (coords_Event[1][0] - coords_Event[0][0]) 
        df3 = pd.DataFrame ({"clicked_data": [clicked_data]})
        string_csv = dir_path + "/clicked_data.csv"
        print(string_csv)
        df3.to_csv(string_csv, 
        header=True)
        print("ok5")

def main():

    app = QApplication(sys.argv)
    ex = GUI()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

这是 Ubuntu 上的错误消息

QCoreApplication::exec: The event loop is already running
ok1
[]
ok2
/home/user/Documents/myApp/coords_Event.csv
df2:  Empty DataFrame
Columns: [Unnamed: 0, X, Y]
Index: []
Traceback (most recent call last):
  File "debug_05.py", line 92, in Event
    print(df2.iloc[0]["X"])
  File "/home/user/Documents/myApp/env/lib/python3.8/site-packages/pandas/core/indexing.py", line 895, in __getitem__
    return self._getitem_axis(maybe_callable, axis=axis)
  File "/home/user/Documents/myApp/env/lib/python3.8/site-packages/pandas/core/indexing.py", line 1501, in _getitem_axis
    self._validate_integer(key, axis)
  File "/home/user/Documents/myApp/env/lib/python3.8/site-packages/pandas/core/indexing.py", line 1444, in _validate_integer
    raise IndexError("single positional indexer is out-of-bounds")
IndexError: single positional indexer is out-of-bounds

我检查了Ubuntu和MacOS上的环境包,它们完全一样

这里有一段代码可能更直接,它在 linux 下输出点击的坐标,但在 MacOS 下不输出:

import numpy as np 
import pandas as pd
import os
import sys 
import matplotlib.pyplot as plt


from PySide2 import QtGui
from PySide2 import QtCore
from PySide2.QtWidgets import QWidget, QApplication, QPushButton, QFileDialog
os.environ['QT_MAC_WANTS_LAYER'] = '1'

coords_Event = [] 

size_win_x = 2
size_win_y = 2
dpi_val = 100

class GUI(QWidget):
    def __init__(self):
        super(GUI, self).__init__()

        self.initUI()

    def initUI(self):
        height_btn = 40 
        width_btn = 350
        
        button_position_x = 0
        button_position_y = 20 
        
        button_position_x = button_position_x + 50
        btn2 = QPushButton('Button', self)     
        btn2.clicked.connect(self.Event)
        btn2.resize(width_btn, height_btn + 20)
        btn2.move(button_position_y, button_position_x)       

        self.show()

    def Event(self):
        dir_path = os.getcwd()

        def onclick(event):
            global ix, iy
            ix, iy = event.xdata, event.ydata
            print('x = %d, y = %d'%(
                ix, iy))
            global coords_Event
            coords_Event.append((ix, iy))
            return coords_Event        

        fig = plt.figure(figsize = (size_win_x,size_win_y), dpi=dpi_val) 
        pixel_array = np.array([[1,2,3,4,5,6], [1,2,3,4,5,6], [1,2,3,4,5,6]])
        fig2 = plt.imshow(pixel_array)
        
        cid = fig.canvas.mpl_connect('button_press_event', onclick)

        plt.show()
        print(coords_Event)

def main():

    app = QApplication(sys.argv)
    ex = GUI()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

【问题讨论】:

  • 看起来 coords_Event.csv 中没有任何内容。这不是 PyQt/PySide 问题。
  • @justengel:非常感谢您的评论。我猜您在 linux 上尝试过代码,或者只是阅读了这个问题。如果是这样:确实,代码不会在coords_Event 中存储任何内容。但是在 MacOS 上确实如此,这就是我的问题的目的:为什么会有差异以及它来自哪里?

标签: python linux macos matplotlib pyqt5


【解决方案1】:

我在 Windows 上运行,你的代码也有同样的问题。

我注意到的第一件事是您没有指定后端。在我的系统上,单击按钮只会显示没有 GUI 的图像。在这种情况下,没有任何要连接的点击事件,也没有要关闭的 GUI 窗口。

我假设在 MacOS 上会显示一个绘图图并让您多次单击该图。关闭图形后,print("ok1") 就会运行。如果是这种情况,plt.show() 正在阻塞并等待窗口关闭。这确实是一个 matplotlib 问题。我尝试运行 plt.show(block=True),但它不适用于 PySide2。可能存在 PySide2 兼容性问题或配置文件或系统设置导致不同的行为。

下面的代码会检查 figure.canvas 是否可见并阻塞直到它关闭。

import numpy as np
import pandas as pd
import os
import sys

from PySide2 import QtGui
from PySide2 import QtCore
from PySide2.QtWidgets import QWidget, QApplication, QPushButton, QFileDialog
os.environ['QT_MAC_WANTS_LAYER'] = '1'

import matplotlib
try:
    matplotlib.rcParams['backend.qt5'] = 'PySide2'
except (KeyError, Exception):
    pass
matplotlib.use('Qt5Agg')
import matplotlib.pyplot as plt


coords_Event = []

size_win_x = 10
size_win_y = 7
dpi_val = 100


dir_path = os.getcwd()


class GUI(QWidget):
    def __init__(self):
        super(GUI, self).__init__()

        self.initUI()

    def initUI(self):
        self.coords_Event = []
        height_btn = 40
        width_btn = 350

        button_position_x = 0
        button_position_y = 20

        button_position_x = button_position_x + 50
        btn2 = QPushButton('Button', self)
        btn2.clicked.connect(self.Event)
        btn2.resize(width_btn, height_btn + 20)
        btn2.move(button_position_y, button_position_x)

        self.show()

    def save_coord(self, event):
        ix, iy = event.xdata, event.ydata
        print('x = %d, y = %d' % (ix, iy))
        self.coords_Event.append((ix, iy))
        return coords_Event

    def Event(self):
        figure = plt.figure(figsize=(size_win_x, size_win_y), dpi=dpi_val)

        self.coords_Event = []
        cid = figure.canvas.mpl_connect('button_press_event', self.save_coord)

        pixel_array = np.array([[1, 2, 3], [100, 5, 6]])
        figure2 = plt.imshow(pixel_array)
        plt.savefig(dir_path + '/raw.png')
        plt.title("the title")

        # Block here until figure is closed.
        plt.show()
        while figure.canvas.isVisible():
            QApplication.processEvents()

        self.finish_coords()

    def finish_coords(self):
        print("ok1")

        df = pd.DataFrame(self.coords_Event, columns = ['X', 'Y'])
        string_csv = dir_path + "/coords_Event.csv"

        print("ok2")
        print(string_csv)
        df.to_csv(string_csv, header=True)
        df2 = pd.read_csv(string_csv)
        print("df2: ", df2)
        print(df2.iloc[0]["X"])
        print(df2.iloc[1]["X"])

        print("ok3")
        # ic(coords_Event)

        clicked_data = (self.coords_Event[1][0] - self.coords_Event[0][0])
        df3 = pd.DataFrame({"clicked_data": [clicked_data]})
        string_csv = dir_path + "/clicked_data.csv"
        print(string_csv)
        df3.to_csv(string_csv, header=True)
        print("ok5")


def main():
    app = QApplication(sys.argv)
    ex = GUI()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

【讨论】:

  • 非常感谢@justengel!我不知道“后端”部分。您对有关该主题的文献/文档有特别的建议吗?再次感谢!
猜你喜欢
  • 2021-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多