目 录CONTENT

文章目录

多种风格二维码生成

Administrator
2025-01-22 / 0 评论 / 0 点赞 / 22 阅读 / 0 字

参考来源

项目介绍

这是一个基于PySide6开发的多风格二维码生成器,提供了丰富的二维码定制选项,包括多种模板样式、自定义颜色、背景图片和Logo添加等功能。

项目参考

二维码的核心生成逻辑来源于开源项目二维码美化组件

主要功能

  • 11种风格模板(默认、液态、菱形、六边形等)
  • 自定义颜色方案(前景色、背景色、定位点颜色)
  • 支持背景图片和Logo添加
  • 支持二维码下方文字添加
  • 实时预览功能
  • HTML格式导出

核心代码展示

1. 自定义颜色选择器组件

class ColorFrame(QFrame):
   def __init__(self, color="#000000", parent=None):
       super().__init__(parent)
       self.setFrameStyle(QFrame.Box | QFrame.Plain)
       self.setLineWidth(2)
       self.color = color
       self.setMinimumSize(40, 25)
       self.setMaximumSize(40, 25)
       self.setStyleSheet(f"background-color: {color};")  
       
   def mousePressEvent(self, event):
       color = QColorDialog.getColor(QColor(self.color))
       if color.isValid():
           self.color = color.name()
           self.setStyleSheet(f"background-color: {self.color};")

2. 统一的界面样式管理

self.setStyleSheet("""
   QLabel {
       font-size: 12px;
   }
   QLabel[section="true"] {
       font-size: 14px;
       font-weight: bold;
       padding: 5px 0;
       margin: 0px;
   }
   QRadioButton {
       font-size: 12px;
       padding: 2px;
       spacing: 5px;
   }
   QLineEdit {
       font-size: 12px;
       padding: 5px;
       min-height: 25px;
   }
   QPushButton {
       font-size: 12px;
       padding: 5px 10px;
       min-height: 25px;
   }
""")

3. 图片选择和处理功能

def choose_image(self, image_type):
   try :
       file_name, _ = QFileDialog.getOpenFileName(
           self,
           "选择图片",
           "",
           "图片文件 (*.png *.jpg *.jpeg *.gif *.bmp)"
       )
       
       if file_name:
           # 生成唯一的文件名
           file_ext = os.path.splitext(file_name)[1]
           new_name = f"{image_type}_{os.urandom(4).hex()}{file_ext}"
           dest_path = os.path.join(self.temp_dir, new_name)
           
           # 复制文件
           shutil.copy2(file_name, dest_path)
           
           # 更新UI和变量
           if image_type == "background":
               self.background_image = dest_path
               self.bg_image_label.setText(os.path.basename(file_name))
           else:
               self.logo_image = dest_path
               self.logo_image_label.setText(os.path.basename(file_name))
               
   except Exception as e:
       QMessageBox.warning(self, "错误", f"图片处理失败: {str(e)}")

技术栈

  • Python 3.x
  • PySide6
  • widget-qrcode.js
  • HTML5

开发亮点

  1. 采用PySide6构建现代化GUI界面
  2. 使用HTML方式渲染,确保二维码质量
  3. 支持多种自定义选项,满足不同需求
  4. 代码结构清晰,易于维护和扩展

完整代码

from     PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, 
                             QHBoxLayout, QLabel, QLineEdit, QRadioButton,
                             QPushButton, QSpinBox, QColorDialog, QFileDialog, 
                             QButtonGroup, QFrame, QMessageBox, QComboBox, QGridLayout)
from PySide6.QtCore import Qt
from PySide6.QtGui import QColor
import sys
import os
import tempfile
import webbrowser
import shutil

class ColorFrame(QFrame):
    def __init__(self, color="#000000", parent=None):
        super().__init__(parent)
        self.setFrameStyle(QFrame.Box | QFrame.Plain)
        self.setLineWidth(2)
        self.color = color
        self.setMinimumSize(40, 25)
        self.setMaximumSize(40, 25)
        self.setStyleSheet(f"background-color: {color};")
        
    def mousePressEvent(self, event):
        color = QColorDialog.getColor(QColor(self.color))
        if color.isValid():
            self.color = color.name()
            self.setStyleSheet(f"background-color: {self.color};")

class QRCodeGenerator(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("二维码生成器")
        self.setMinimumSize(550, 680)
        
        # 设置全局样式
        self.setStyleSheet("""
            QLabel {
                font-size: 12px;
            }
            QLabel[section="true"] {
                font-size: 14px;
                font-weight: bold;
                padding: 5px 0;
                margin: 0px;
            }
            QRadioButton {
                font-size: 12px;
                padding: 2px;
                spacing: 5px;
            }
            QLineEdit {
                font-size: 12px;
                padding: 5px;
                min-height: 25px;
            }
            QPushButton {
                font-size: 12px;
                padding: 5px 10px;
                min-height: 25px;
            }
            QSpinBox {
                font-size: 12px;
                padding: 5px;
                min-width: 70px;
                min-height: 25px;
            }
            QComboBox {
                font-size: 12px;
                padding: 2px 5px;
            }
        """)
        
        # 初始化变量
        self.background_image = ""
        self.logo_image = ""
        self.save_path = os.path.expanduser("~/Documents")
        
        # 初始化颜色选择器
        self.fg_color = ColorFrame("#000000")
        self.bg_color = ColorFrame("#FFFFFF")
        self.inner_color = ColorFrame("#FFA500")
        self.outer_color = ColorFrame("#FFD700")
        
        # 创建主布局
        main_widget = QWidget()
        self.setCentralWidget(main_widget)
        control_layout = QVBoxLayout(main_widget)
        control_layout.setSpacing(10)
        control_layout.setContentsMargins(15, 15, 15, 15)
        
        # 修改颜色选择器尺寸
        for color_frame in [self.fg_color, self.bg_color, self.inner_color, self.outer_color]:
            color_frame.setMinimumSize(30, 20)
            color_frame.setMaximumSize(30, 20)
        
        # 1. 文本内容
        content_layout = QVBoxLayout()
        content_layout.setSpacing(8)
        content_label = QLabel("文本内容")
        content_label.setProperty("section", True)  # 设置为段落标题
        self.content_input = QLineEdit()
        self.content_input.setPlaceholderText("请在此输入二维码内容")
        self.content_input.setMinimumHeight(35)  # 增加输入框高度
        content_layout.addWidget(content_label)
        content_layout.addWidget(self.content_input)
        control_layout.addLayout(content_layout)
        
        # 2. 风格模板
        template_layout = QVBoxLayout()
        template_layout.setSpacing(5)  # 减小垂直间距
        
        # 标题使用水平布局
        template_header = QHBoxLayout()
        template_label = QLabel("风格模板")
        template_label.setProperty("section", True)
        template_header.addWidget(template_label)
        template_header.addStretch()
        template_layout.addLayout(template_header)
        
        # 创建网格布局来放置单选按钮
        templates_grid = QGridLayout()
        templates_grid.setHorizontalSpacing(30)  # 水平间距
        templates_grid.setVerticalSpacing(10)    # 垂直间距
        templates_grid.setContentsMargins(0, 5, 0, 5)  # 上下留出空间
        
        templates = [
            ('default', '默认'), ('water', '液态'), ('diamond', '菱形'),
            ('hexagon', '六边形'), ('star', '星星'), ('rect', '方块'),
            ('bar', '条形'), ('heart', '心形'), ('glitter', '闪烁'),
            ('stroke', '描边'), ('fusion', '融合')
        ]
        
        # 修改单选按钮样式
        radio_style = """
            QRadioButton {
                padding: 2px;
                min-height: 20px;
                spacing: 5px;
            }
            QRadioButton:hover {
                background-color: #f0f0f0;
            }
        """
        
        self.template_group = QButtonGroup(self)
        
        # 使用网格布局排列按钮,每行5个
        for i, (template_id, template_name) in enumerate(templates):
            radio = QRadioButton(template_name)
            radio.setStyleSheet(radio_style)
            if template_id == 'default':
                radio.setChecked(True)
            self.template_group.addButton(radio)
            row = i // 5
            col = i % 5
            templates_grid.addWidget(radio, row, col)
        
        template_layout.addLayout(templates_grid)
        control_layout.addLayout(template_layout)
        
        # 添加分隔线
        separator = QFrame()
        separator.setFrameShape(QFrame.HLine)
        separator.setFrameShadow(QFrame.Sunken)
        separator.setStyleSheet("""
            QFrame {
                background-color: #e0e0e0;
                height: 1px;
                margin: 10px 0px;
            }
        """)
        control_layout.addWidget(separator)
        
        # 3. 纠错等级和尺寸设置(放在同一行)
        level_size_layout = QHBoxLayout()
        
        # 纠错等级(改为下拉框)
        level_layout = QHBoxLayout()
        level_layout.setSpacing(5)
        level_label = QLabel("纠错等级:")
        level_label.setFixedWidth(60)
        self.level_combo = QComboBox()
        self.level_combo.addItems(['L', 'M', 'Q', 'H'])
        self.level_combo.setCurrentText('M')
        self.level_combo.setFixedWidth(60)
        level_layout.addWidget(level_label)
        level_layout.addWidget(self.level_combo)
        level_layout.addStretch()
        
        # 尺寸设置
        size_layout = QHBoxLayout()
        size_layout.setSpacing(5)
        width_label = QLabel("宽度:")
        width_label.setFixedWidth(40)
        self.width_spin = QSpinBox()
        self.width_spin.setRange(100, 1000)
        self.width_spin.setValue(300)
        self.width_spin.setFixedWidth(60)
        height_label = QLabel("高度:")
        height_label.setFixedWidth(40)
        self.height_spin = QSpinBox()
        self.height_spin.setRange(100, 1000)
        self.height_spin.setValue(300)
        self.height_spin.setFixedWidth(60)
        
        size_layout.addWidget(width_label)
        size_layout.addWidget(self.width_spin)
        size_layout.addWidget(height_label)
        size_layout.addWidget(self.height_spin)
        size_layout.addStretch()
        
        level_size_layout.addLayout(level_layout)
        level_size_layout.addLayout(size_layout)
        control_layout.addLayout(level_size_layout)
        
        # 4. 颜色方案
        colors_layout = QVBoxLayout()
        colors_layout.setSpacing(8)
        
        # 标题使用水平布局
        colors_header = QHBoxLayout()
        colors_label = QLabel("颜色方案")
        colors_label.setProperty("section", True)
        colors_header.addWidget(colors_label)
        colors_header.addStretch()  # 添加弹性空间
        colors_layout.addLayout(colors_header)
        
        colors_grid = QHBoxLayout()
        colors_grid.setSpacing(15)  # 减小颜色选择器间距
        
        # 颜色选择器布局
        color_items = [
            (self.fg_color, "前景"),
            (self.bg_color, "背景"),
            (self.inner_color, "定位内框"),
            (self.outer_color, "定位外框")
        ]
        
        for color_frame, label_text in color_items:
            item_layout = QHBoxLayout()
            item_layout.addWidget(color_frame)
            item_layout.addWidget(QLabel(label_text))
            colors_grid.addLayout(item_layout)
        
        colors_grid.addStretch()  # 添加弹性空间
        colors_layout.addLayout(colors_grid)
        control_layout.addLayout(colors_layout)
        
        # 5. 图片设置
        images_layout = QVBoxLayout()
        images_label = QLabel("图片设置")
        images_label.setProperty("section", True)
        images_layout.addWidget(images_label)
        
        images_row_layout = QHBoxLayout()
        
        # 背景图
        bg_image_layout = QHBoxLayout()
        self.bg_image_label = QLabel("未选择")  # 添加标签初始化
        bg_image_btn = QPushButton("选择背景图")
        bg_image_clear = QPushButton("清除")
        bg_image_btn.setFixedWidth(100)
        bg_image_clear.setFixedWidth(50)
        bg_image_layout.addWidget(self.bg_image_label)  # 添加标签到布局
        bg_image_layout.addWidget(bg_image_btn)
        bg_image_layout.addWidget(bg_image_clear)
        
        # Logo图
        logo_image_layout = QHBoxLayout()
        self.logo_image_label = QLabel("未选择")  # 添加标签初始化
        logo_image_btn = QPushButton("选择Logo")
        logo_image_clear = QPushButton("清除")
        logo_image_btn.setFixedWidth(100)
        logo_image_clear.setFixedWidth(50)
        logo_image_layout.addWidget(self.logo_image_label)  # 添加标签到布局
        logo_image_layout.addWidget(logo_image_btn)
        logo_image_layout.addWidget(logo_image_clear)
        
        # 连接按钮事件
        bg_image_btn.clicked.connect(lambda: self.choose_image("background"))
        bg_image_clear.clicked.connect(lambda: self.clear_image("background"))
        logo_image_btn.clicked.connect(lambda: self.choose_image("logo"))
        logo_image_clear.clicked.connect(lambda: self.clear_image("logo"))
        
        images_row_layout.addLayout(bg_image_layout)
        images_row_layout.addLayout(logo_image_layout)
        images_layout.addLayout(images_row_layout)
        control_layout.addLayout(images_layout)
        
        # 7. 文字设置(提示文字和颜色放在同一行)
        text_layout = QVBoxLayout()
        text_label = QLabel("文字设置")
        text_layout.addWidget(text_label)
        
        text_row_layout = QHBoxLayout()
        
        # 提示文字输入
        text_input_layout = QHBoxLayout()
        text_input_label = QLabel("提示文字:")
        text_input_label.setFixedWidth(60)
        self.text_input = QLineEdit()
        self.text_input.setPlaceholderText("请输入二维码下方显示的文字")
        text_input_layout.addWidget(text_input_label)
        text_input_layout.addWidget(self.text_input)
        
        # 文字颜色选择
        text_color_layout = QHBoxLayout()
        text_color_layout.setSpacing(5)
        self.text_color = ColorFrame("#0000FF")
        text_color_layout.addWidget(QLabel("文字颜色:"))
        text_color_layout.addWidget(self.text_color)
        
        text_row_layout.addLayout(text_input_layout)
        text_row_layout.addLayout(text_color_layout)
        text_layout.addLayout(text_row_layout)
        
        control_layout.addLayout(text_layout)
        
        # 8. 保存设置
        save_layout = QVBoxLayout()
        save_label = QLabel("保存设置")
        save_layout.addWidget(save_label)
        
        save_path_layout = QHBoxLayout()
        save_path_layout.setSpacing(5)
        self.save_path_label = QLabel(self.save_path)  # 使用初始化的save_path
        save_path_btn = QPushButton("选择保存位置")
        save_path_btn.clicked.connect(self.choose_save_path)
        save_path_layout.addWidget(self.save_path_label, 1)
        save_path_layout.addWidget(save_path_btn)
        save_layout.addLayout(save_path_layout)
        control_layout.addLayout(save_layout)
        
        # 9. 操作按钮
        buttons_layout = QHBoxLayout()
        buttons_layout.setSpacing(10)
        
        preview_btn = QPushButton("生成预览")
        save_btn = QPushButton("保存二维码")
        
        # 设置按钮大小
        for btn in [preview_btn, save_btn]:
            btn.setMinimumWidth(100)
            btn.setMinimumHeight(30)
        
        # 连接按钮点击事件
        preview_btn.clicked.connect(self.generate_preview)
        save_btn.clicked.connect(self.save_qrcode)
        
        buttons_layout.addWidget(preview_btn)
        buttons_layout.addWidget(save_btn)
        control_layout.addLayout(buttons_layout)
        
        # 设置各部分之间的分隔线
        separator_style = """
            QFrame {
                background-color: #e0e0e0;
                height: 1px;
                margin: 5px 0px;
            }
        """
        
        # 为每个部分添加分隔线
        for i in range(control_layout.count()):
            if isinstance(control_layout.itemAt(i), QVBoxLayout):
                separator = QFrame()
                separator.setFrameShape(QFrame.HLine)
                separator.setFrameShadow(QFrame.Sunken)
                separator.setStyleSheet(separator_style)
                control_layout.insertWidget(control_layout.indexOf(control_layout.itemAt(i)) + 1, separator)

        # 添加底部弹性空间
        control_layout.addStretch()

        # 修改临时目录位置到用户文档目录下
        self.temp_dir = os.path.join(os.path.expanduser("~/Documents"), "qrcode_temp")
        try:
            if not os.path.exists(self.temp_dir):
                os.makedirs(self.temp_dir)
        except Exception as e:
            QMessageBox.warning(self, "警告", f"创建临时目录失败: {str(e)}")
            self.temp_dir = os.getcwd()  # 如果创建失败,使用当前目录
        
        # 添加底部版权信息
        self.about_label = QLabel(
        '<p><a href="https://www.allfather.top">愿代码流畅无阻,愿调试轻松自如</a></p>',
        self
        )
        # self.about_label.setStyleSheet("background: lightblue")
        self.about_label.setAlignment(Qt.AlignBottom | Qt.AlignRight)
        self.about_label.setOpenExternalLinks(True)  # 允许 QLabel 中的链接被点击跳转
        # 将 QLabel 添加到布局中
        control_layout.addWidget(self.about_label)

    def choose_color(self, button):
        color = QColorDialog.getColor()
        if color.isValid():
            button.setStyleSheet(f"background-color: {color.name()};")
            self.preview_qrcode()
            
    def choose_image(self, image_type):
        try:
            file_name, _ = QFileDialog.getOpenFileName(
                self,
                "选择图片",
                "",
                "图片文件 (*.png *.jpg *.jpeg *.gif *.bmp)"
            )
            
            if file_name:
                # 生成唯一的文件名
                file_ext = os.path.splitext(file_name)[1]
                new_name = f"{image_type}_{os.urandom(4).hex()}{file_ext}"
                dest_path = os.path.join(self.temp_dir, new_name)
                
                # 复制文件
                shutil.copy2(file_name, dest_path)
                
                # 更新UI和变量
                if image_type == "background":
                    if hasattr(self, 'background_image') and self.background_image:
                        try:
                            os.remove(self.background_image)
                        except:
                            pass
                    self.background_image = dest_path
                    self.bg_image_label.setText(os.path.basename(file_name))
                else:
                    if hasattr(self, 'logo_image') and self.logo_image:
                        try:
                            os.remove(self.logo_image)
                        except:
                            pass
                    self.logo_image = dest_path
                    self.logo_image_label.setText(os.path.basename(file_name))
                
        except Exception as e:
            QMessageBox.warning(self, "错误", f"图片处理失败: {str(e)}")

    def clear_image(self, image_type):
        """清除图片"""
        try:
            if image_type == "background":
                if self.background_image:
                    os.remove(self.background_image)
                self.background_image = ""
                self.bg_image_label.setText("未选择")
            else:
                if self.logo_image:
                    os.remove(self.logo_image)
                self.logo_image = ""
                self.logo_image_label.setText("未选择")
            
        except Exception as e:
            QMessageBox.warning(self, "错误", "清除图片失败")
            
    def choose_save_path(self):
        folder_path = QFileDialog.getExistingDirectory(
            self,
            "选择保存位置",
            self.save_path
        )
        if folder_path:
            self.save_path = folder_path
            self.save_path_label.setText(folder_path)

    def preview_qrcode(self):
        # 移除此方法,因为不再需要预览功能
        pass

    def generate_preview(self):
        """生成二维码并在浏览器中打开"""
        if not self.content_input.text():
            QMessageBox.warning(self, "警告", "请输入二维码内容!")
            return
            
        # 生成HTML内容
        html_content = self.generate_html_content()
        
        # 保存到临时文件
        preview_html = os.path.join(self.temp_dir, "preview.html")
        with open(preview_html, "w", encoding="utf-8") as f:
            f.write(html_content)
        
        # 在浏览器中打开
        webbrowser.open(f"file://{preview_html}")

    def generate_html_content(self):
        # 获取所有参数
        value = self.content_input.text()
        template = self.template_group.checkedButton().text()  # 获取模板名称
        # 将中文模板名称转换为英文ID
        template_map = {
            '默认': 'default', '液态': 'water', '菱形': 'diamond',
            '六边形': 'hexagon', '星星': 'star', '方块': 'rect',
            '条形': 'bar', '心形': 'heart', '闪烁': 'glitter',
            '描边': 'stroke', '融合': 'fusion'
        }
        template = template_map.get(template, 'default')  # 如果找不到对应的英文ID,使用default
        
        level = self.level_combo.currentText()
        width = self.width_spin.value()
        height = self.height_spin.value()
        bg_color = self.bg_color.color
        fg_color = self.fg_color.color
        inner_color = self.inner_color.color
        outer_color = self.outer_color.color
        text = self.text_input.text()
        text_color = self.text_color.color
        
        # 构建HTML内容
        html_content = f"""
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <script type="text/javascript" src="https://passer-by.com/widget-qrcode/dist/widget-qrcode.min.js"></script>
        </head>
        <body style="margin:0;display:flex;justify-content:center;align-items:center;min-height:100vh;">
            <widget-qrcode 
                value="{value}"
                template="{template}"
                level="{level}"
                width="{width}"
                height="{height}"
                background-color="{bg_color}"
                foreground-color="{fg_color}"
                inner-color="{inner_color}"
                outer-color="{outer_color}"
        """
        
        if self.background_image:
            html_content += f'background-image="file://{self.background_image}"\n'
        if self.logo_image:
            html_content += f'logo="file://{self.logo_image}"\n'
        if text:
            html_content += f'text="{text}"\n'
            html_content += f'text-color="{text_color}"\n'
            html_content += 'text-stroke="2px solid #000000"\n'
            
        html_content += """>
            </widget-qrcode>
        </body>
        </html>
        """
        return html_content
            
    def save_qrcode(self):
        if not self.content_input.text():
            QMessageBox.warning(self, "警告", "请输入二维码内容!")
            return
            
        file_name = os.path.join(self.save_path, "qrcode.html")
        file_name, _ = QFileDialog.getSaveFileName(
            self,
            "保存二维码",
            file_name,
            "HTML文件 (*.html)"
        )
        
        if file_name:
            html_content = self.generate_html_content()
            with open(file_name, "w", encoding="utf-8") as f:
                f.write(html_content)
            
            # 如果选择了图片,复制到保存目录
            save_dir = os.path.dirname(file_name)
            if self.background_image:
                shutil.copy2(self.background_image, save_dir)
            if self.logo_image:
                shutil.copy2(self.logo_image, save_dir)
                
            QMessageBox.information(self, "成功", "二维码已保存!")
            # 保存后自动在浏览器中打开
            webbrowser.open(f"file://{file_name}")

    def create_separator(self):
        """创建分隔线"""
        separator = QFrame()
        separator.setFrameShape(QFrame.HLine)
        separator.setFrameShadow(QFrame.Sunken)
        separator.setStyleSheet("""
            QFrame {
                background-color: #cccccc;
                height: 1px;
                margin: 10px 0px;
            }
        """)
        return separator

    def __del__(self):
        """析构函数,清理临时文件"""
        try:
            if os.path.exists(self.temp_dir):
                shutil.rmtree(self.temp_dir, ignore_errors=True)
        except:
            pass
    

def main():
    app = QApplication(sys.argv)
    window = QRCodeGenerator()
    window.show()
    sys.exit(app.exec())

if __name__ == "__main__":
    main() 

1737510800169.jpg

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区