PyQt5中的按钮连接有啥问题?

Posted

技术标签:

【中文标题】PyQt5中的按钮连接有啥问题?【英文标题】:What is the problem with buttons connection in PyQt5?PyQt5中的按钮连接有什么问题? 【发布时间】:2019-10-29 08:48:43 【问题描述】:

我的问题是我无法连接两个按钮之间的实现。在我按下“灰度”按钮并获得灰度图像后,我按下“Canny”按钮但用户界面突然关闭。 我不知道我的代码有什么问题。

def getImage(self):
    global fname
    fname = QFileDialog.getOpenFileName(self, 'Open file', 
       'C:\\Users\binil-ping\Desktop\CODE',"Image Files (*.jpg *.gif *.bmp *.png)")
    pixmap = QPixmap(fname[0])
    self.label.setPixmap(QPixmap(pixmap))
    self.resize(pixmap.width(), pixmap.height())

def Grayscale(self):
    global edges
    edges = cv2.imread(fname[0], 0)
    edges = cv2.GaussianBlur(edges, (5, 5), 0)
    height, width = edges.shape[:2]
    ret,edges = cv2.threshold(edges,150,255,cv2.THRESH_BINARY)
    kernel = np.ones((5,5),np.uint8)
    edges = cv2.morphologyEx(edges, cv2.MORPH_OPEN, kernel)
    edges = cv2.morphologyEx(edges, cv2.MORPH_OPEN, kernel)

    edges = QImage(edges, width, height, QImage.Format_Grayscale8)
    pixmap = QPixmap.fromImage(edges)
    self.label.setPixmap(pixmap)
    self.resize(pixmap.width(), pixmap.height())

def Canny(self):
    edges2 = cv2.imread(edges[0],-1)
    edges2 = cv2.Canny(edges2,180,200)

    edges2 = QImage(edges2, width, height, QImage.Format_Grayscale8)
    pixmap = QPixmap.fromImage(edges2)
    self.label.setPixmap(pixmap)
    self.resize(pixmap.width(), pixmap.height())

【问题讨论】:

【参考方案1】:

你的代码有很多问题(包括你没有提供minimal, reproducible example),其中一些解释了崩溃或可能导致另一个问题(我用 [*] 标记了它们):

如前所述,尽可能避免使用全局变量(几乎总是如此),而是使用类属性; 您不断地覆盖这些全局变量,这也让调试变得非常混乱;实际上,您将edges 用于numpy 数组 QImage; [*] 您正在尝试 cv2.imread(edges[0],-1),但不仅 imread 需要一个字符串,而且由于前一点,此时 edges 甚至是一个 QImage; [*] 一些变量未在函数范围内声明(Canny 函数中的宽度/高度); 您确实应该避免对函数和变量使用大写的名称,因为它们通常只用于类名和内置常量; 在转换回 QImage 的过程中存在一些问题,我想这是由于您对数组应用的各种转换(其中一些似乎也不必要)对 Format_Grayscale8 格式不太友好,也许您应该对此进行更好的调查;

这是对您的代码的可能改进。我正在添加小部件创建部分,因为原始示例中缺少它。

class ShowImage(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        layout = QVBoxLayout(self)
        self.label = QLabel()
        layout.addWidget(self.label)

        self.getImageBtn = QPushButton()
        layout.addWidget(self.getImageBtn)
        self.getImageBtn.clicked.connect(self.getImage)

        self.grayBtn = QPushButton('gray')
        layout.addWidget(self.grayBtn)
        self.grayBtn.clicked.connect(self.grayScale)

        self.cannyBtn = QPushButton('canny')
        layout.addWidget(self.cannyBtn)
        self.cannyBtn.clicked.connect(self.canny)

        self.fileName = None
        self.edges = None

    def getImage(self):
        fname = QFileDialog.getOpenFileName(self, 'Open file', 
           'C:\\Users\binil-ping\Desktop\CODE',"Image Files (*.jpg *.gif *.bmp *.png)")
        if fname[0]:
            self.fileName = fname[0]
            pixmap = QPixmap(self.fileName)
            self.label.setPixmap(pixmap)

    def grayScale(self):
        if not self.fileName:
            return
        edges = cv2.imread(self.fileName, 0)
        edges = cv2.GaussianBlur(edges, (5, 5), 0)
        ret,edges = cv2.threshold(edges, 150, 255, cv2.THRESH_BINARY)
        kernel = np.ones((5, 5), np.uint8)
        edges = cv2.morphologyEx(edges, cv2.MORPH_OPEN, kernel)
        edges = cv2.morphologyEx(edges, cv2.MORPH_OPEN, kernel)

        self.edges = edges
        height, width = edges.shape[:2]
        image = QImage(edges, width, height, QImage.Format_Grayscale8)
        pixmap = QPixmap.fromImage(image)
        self.label.setPixmap(pixmap)

    def canny(self):
        if self.edges is None:
            return
        edges2 = cv2.Canny(self.edges, 180, 200)
        height, width = edges2.shape[:2]

        edges2 = QImage(edges2, width, height, QImage.Format_Grayscale8)
        pixmap = QPixmap.fromImage(edges2)
        self.label.setPixmap(pixmap)

【讨论】:

非常感谢您的代码。顺便说一句,我刚加入论坛几天,所以在发布问题时出错,谢谢提醒。 没问题;也许你可以花点时间阅读how to ask a good question;如果它解决了您的问题,请记住将这个(或任何)答案标记为已接受,并支持您认为有帮助或以任何方式为您提供其他帮助的任何答案。 谢谢。顺便说一句,我试过你的代码,它工作。但是“def canny(self)”由于“self.fileName”而不是“def grayScale(self)”从“def getImage”中获取图像。你能帮我修改一下吗?非常感谢。 @NguyễnAnhDuy 查看更新的代码。我只是在 grayScale() 函数的末尾创建了一个类属性,然后在 canny() 中使用它。 很抱歉打扰您,但它在 canny() 中不起作用。 :(【参考方案2】:

规则 #1:不要使用全局变量。

现在来看看真正的答案。

PyQt 有一个非常非常糟糕、令人讨厌的习惯,如果您的代码中有未捕获的异常,它就会崩溃。这使得调试变得困难。就个人而言,我在调试时在槽周围使用了一个装饰器,它会在 PyQt 杀死应用程序之前打印我的异常。

import logging
from functools import wraps
logger = logging.getLogger(__name__)
# ...lots of other code...
def debugFunc(*errors):
    def decorator(func):
        @wraps(func)
        def runner(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except errors as err:
                logger.debug(f"Function func.__name__ raised a err.__class__.__name__, which was ignored.",
                             exc_info=True)
            except Exception as err:
                logger.critical(f"err.__class__.__name__ - err", exc_info=True)
        return runner
    return decorator

在调试时,这允许我忽略某些错误,并将所有其他错误发送到logging 系统。如果您自己不使用它并且不愿意开始使用它,您可以改为导入 traceback 并使用它的功能,而不是调用我的 logger 对象。

只需将此函数用作最里面的装饰器,如下所示:

@debugFunc()
def Canny(self):
    edges2 = cv2.imread(edges[0],-1)
    edges2 = cv2.Canny(edges2,180,200)

    edges2 = QImage(edges2, width, height, QImage.Format_Grayscale8)
    pixmap = QPixmap.fromImage(edges2)
    self.label.setPixmap(pixmap)
    self.resize(pixmap.width(), pixmap.height())[enter image description here][1]

虽然我对 cv2 不够熟悉,无法肯定地说,但我认为您的问题可能是 cv2.Canny。它看起来不像其他 cv2 函数的命名,如果我是对的,可能会导致 AttributeError。

【讨论】:

问题是“什么问题”,而不是“我怎样才能找到问题所在”。虽然您的一个很好的建议,但在我看来,它并不是问题中所问问题的答案 @musicamante 问题是 PyQt 在崩溃之前不会打印异常,这就是解决方法。即使这不是根本问题,也是阻碍 OP 进展的问题。这与任何框架挑战答案一样多。 这取决于异常,一般来说,如果错误不是由于 (Py)Qt 本身引起的,python 无论如何都会像这种情况一样抛出它的回溯,这是由于事实上, imread 函数需要一个文件名字符串并得到一个 QImage 。也就是说,我仍然认为您的建议是有用的 suggestion (可以找到一种方法来解释如何使用它或最终在 cmets 中链接到问题),但不是有用的 suggestion我>回答。很抱歉你不同意,但这是我的观点。 我会说 PyQt 有一些内疚,因为它主动抑制了回溯的打印。但不同的意见是公平的。 @Gloweye 我没有投反对票,但您回答的第一部分是基于一些误解。您描述的行为是正常的,完全是设计使然 - 请参阅 PyQt5 文档:Unhandled Python Exceptions。如果你总是在你的 PyQt5 程序中设置一个异常钩子,你将永远不会遇到任何调试问题。请参阅here 了解恢复旧行为的最小示例。

以上是关于PyQt5中的按钮连接有啥问题?的主要内容,如果未能解决你的问题,请参考以下文章

PyQt5快速上手基础篇2-按钮控制LCD屏显示

PYQT5中如何将按钮点击连接的结果输出到界面上

如何将pyqt5中的后退按钮和前进按钮添加到我的QGridLayout

如何使用按钮在 PyQt5 中的两个菜单栏之间切换?菜单栏消失

pyQt5从dict创建按钮连接到具有附加值的函数

输入用户数据时如何更新 Matplotlib 中的绘图(通过 PyQt5 按钮)