【自由生长的Python】为了能有资格放弃,增加生成、识别二维码的功能【07】

tkinter操作二维码

  • 目标
  • 总结
  • 说一下做了啥
    • 1、说一下界面
    • 2、二维码的生成:qrcode模块
    • 3、识别二维码:opencv-python模块
    • 4、其他要说的东西
  • 最后

目标

顺着这个系列的传统,这次桌面应用增加一个二维码相关的功能,一方面可以输入文字,产生对应的二维码,一方面可以输入图像,识别图像中的二维码。

  • 增加一个toplevel,里面分成两个frame,一个frame用来展示图片,一个用来显示文字;
  • 文本显示的frame中增加一个按钮,当文本框中有文字时,按下按钮,生成文字对应的二维码;在另一个展示图片的frame中展示二维码;
  • 在菜单栏中,增加几个功能。在文件菜单中增加:打开图片功能,图片另存为功能;在操作菜单中增加:适应画布,清空画布,识别图中二维码的功能
  • 整体代码打个标签:V1.2.5;

【自由生长的python】二维码的生成和识别

总结

这次在GUI编程上没有什么新的内容,都是老的组件,老的用法。学到的两个新的东西:
1、调用qrcode模块生成二维码;
2、调用opencv-python识别二维码。

(以上两个都是使用现成的API,对于二维码的的具体知识,图像中二维码识别的具体难度没有了解。我感觉真要识别一张图片中的二维码,可能是难度很大的事情,没有具体思路,只能使用别人的成果)

最终你可以通过可以使用下面四行命令,体验一下程序运行的效果

git clone https://github.com/kamigo2018/myTkinter.git
cd myTkinter
git checkout V1.2.5 
python HelloTkinter.py

说一下做了啥

1、说一下界面

如图所示,左右分别是两个框体(frame):左边的框体用来展示二维码图片;右边的框体用来输入文字。同时,左边的框体可以打开带有二维码的图片,右边的框体用来展示识别出二维码的内容(这个功能主要在菜单栏中实现)。
【自由生长的Python】为了能有资格放弃,增加生成、识别二维码的功能【07】_第1张图片

2、二维码的生成:qrcode模块

生成qrcode非常简单,引入qrcode模块后,调用qrcode.make(),返回的就是一个二维码图像对象。下面的代码中data是从Text组件中获取的字符串,是用户输入的,要编码在二维码里的内容。

            ... ...
            data = strInfo[:-1] #Text中的字符串会默认添加一个换行符,这里干掉换行符。
            if len(data) == 0:
                tkMessageBox.showinfo(title="说明",message="无输入字符,不生成二维码")
                return
            else:
                tempFile = qrcode.make(data) # 直接调用make,生成二维码图像
                tempFile.save('tmp/qrcode.jpg') # 将图像保存成临时文件,

            image = Image.open('tmp/qrcode.jpg') # 用Image对象打开这个文件,传给图像展示窗体,其实不用二次打开,直接把tempFile传进来就可以。
            self.myQRCodeImageViewFrame.showImage(image)
            ... ...

仔细想一下,其实二维码在生成的时候肯定遵守了一些规范。这里直接用qrcode,而不去深究编码规范,确实属于拿来主义。看起来很简单的,直接生成一张二维码,背后可能还隐藏着很多细节。但是如果我知道编码规则,能否做一个二维码出来?应该可以,到时候应该就是操作图片,将一部分点涂黑,一部分点留白。

虽然会骑自行车,不需要知道自行车轮子是怎么造的。但是如果有一天,出现一个像共享单车一样的机会,真需要我去了解轮子是怎么造的,怎么改进,我有这个能力去挖细节么?

3、识别二维码:opencv-python模块

我认为识别二维码是很难,至少有两个难点:
1、根据编码规范,将二维码解码;
2、一张图片中定位二维码的位置;这个对我来说,想想就觉得无从下手。
所以这里只能寻找现成的模块,利用现成的方法。

在寻找过程中,我看到大多数中文博客提到了zxing。由于这么多人推荐,而且出自google,相信zxing会比较好用。但是我从github在下载zxing(不是zxing-python)时,124M的东西总是无法下载成功。所以只能试试寻找别的模块。

在这篇帖子中,提到了opencv-python,并且提供了完整的例子。我觉得挺好的,很实用。使用cv2模块,只用三步:
1、通过cv2读入图片数据

img = cv2.imread(fileName)

2、初始化一个解码器

detector = cv2.QRCodeDetector()

3、解码

data, bbox, straight_qrcode = detector.detectAndDecode(img)

返回的data是二维码中识别出来的字符串;bbox是一个列表,记录了二维码的顶点坐标;straight_qrcode是二进制序列码(不知道这个东西杂用)。

下面的例子中,将二维码识别出来后,将二维码区域用红线框了起来,显示在图片展示区域。

            fileName  = "tmp/decode.jpg" 
            image = self.myQRCodeImageViewFrame.image
            image.save(fileName) # 先把他存成临时文件,给cv2用
            
            # cv2读入图片数据
            img = cv2.imread(fileName)
            
            # 初始化一个二维码解码器
            detector = cv2.QRCodeDetector()
            
            # 识别二维码
            data, bbox, straight_qrcode = detector.detectAndDecode(img)
            
            # 如果有二维码,将二维码区域识别画出来
            if bbox is not None:
                self.myQRCodeInputViewFrame.txtArea.insert(0.0,data)
                n_lines = len(bbox)
                for i in range(n_lines):
                    # draw all lines
                    point1 = tuple(bbox[i][0])
                    point2 = tuple(bbox[(i+1) % n_lines][0])
                    # bbox 是这个二维码的四个顶点,用红线将二维码框起来。
                    
                    cv2.line(img, point1, point2, color=(0, 0, 255), thickness=5)
                    cv2.imwrite(fileName,img) # 这时候打开fileName对应的文件,就可以看圈出二维码的新图片。
                    
                    image = Image.open(fileName)
                    self.myQRCodeImageViewFrame.showImage(image)

4、其他要说的东西

  1. 这次在开发的过程中,我觉得有些组件的组合以后还可能会用到。这样就把几个组件封装到一个frame里面,为了以后使用方便,可以看一下CommonFrame.py文件。
    第一个封装,是将Cavnas和Scrollbar封装在一起,用来展示图片。我觉得这个封装的不太好,一是因为有些必要的动作没有封装进去,比如适应窗口大小,放大,缩小。二是因为封装了以后,frame肯定是放在某个父容器,和别的组件发生交互。这个交互方式没有设计。
    第二个封装,是将Text和一个Button封装在一起。同样有我上面的两个问题。没想好,以后应该会改进吧。

  2. 大家是否注意到,右边窗体中有个竖向的标签:“二维码内容”。这个竖向的标签实现方式是:使用了一个wraplength属性,让他在每个字后面换行,从而实现竖向排列。

    self.inputStringLabel = tk.Label(self,text="二维码内容",wraplength=1,font = self.showFont) # 父容器就是self,这个frame
    # 这里还有个知识点:wraplength,让他换行。参考:
    # https://stackoverflow.com/questions/17650232/python-tkinter-label-orientation

我搜索了中文的网页,发现好像大家没有讨论过这个问题,所以转向英文网页,在stackoverflow上,果然有人回答过这类似的的问题。特别想给那个提问和回答的人一个赞!另外中文创作者们也应该加油,遇到的问题应该记录下来,没有遇到问题的时候,可以多想想,还有什么没做。

最后

最后,我觉得我也应该把tkinter相关的知识总结一下了,以后自由生长的部分可能就是python相关的内容了。

你可能感兴趣的:(【自由生长的Python】为了能有资格放弃,增加生成、识别二维码的功能【07】)