python 图片下面加边框TK界面

news2025/1/12 20:51:26

python 对图片增加边框,logo贴图,获取图片exif参数,填写图片文本内容-CSDN博客

import tkinter as tk
from tkinter import ttk
import os
import glob
import json
import tkinter.messagebox as messagebox  # 弹出提示框
from PIL import Image, ImageDraw, ImageFont
import exifread

def photo_exif(image_path):

    f = open(image_path, 'rb')

    tags = exifread.process_file(f)

    # 打印所有照片信息,会以键值对的方法保存
    # for tag in tags.keys():
    #     print("Key: {0}, value {1}".format(tag, tags[tag]))
    # print(str(tags['EXIF FocalLength']) + 'mm', tags['EXIF ExposureTime'], 'ISO' + str(tags['EXIF ISOSpeedRatings']))
    return tags



def add_logo_with_text(image_path, logo_path, logo_size, text1, text2, text3, font_path, font_size, font_color, border_size,
                       border_color, output_path):
    # 打开原始图片
    image = Image.open(image_path).convert("RGB")
    width, height = image.size

    # 计算边框区域大小和位置
    font = ImageFont.truetype(font_path, font_size)
    text1_width, text1_height = font.getsize(text1)
    text2_width, text2_height = font.getsize(text2)
    text3_width, text3_height = font.getsize(text3)
    text_width = max(text1_width, text2_width, text3_width)
    text_height = text1_height + text2_height + text3_height
    border_width = logo_size[0] + text_width + border_size * 3
    border_height = max(logo_size[1], text_height) + border_size * 2
    border_position = ((width - border_width) // 2, height)

    # 打开logo图片并调整大小
    logo = Image.open(logo_path).resize(logo_size, Image.ANTIALIAS)

    # 创建新的图片
    new_width = width
    new_height = height + border_height
    new_image = Image.new("RGB", (new_width, new_height), "white")

    # 将原始图片复制到新图片的顶部
    new_image.paste(image, (0, 0, width, height))

    # 在新图片上绘制边框
    draw = ImageDraw.Draw(new_image)
    border_rect = (border_position[0], height, border_position[0] + border_width, height + border_height)
    draw.rectangle(border_rect, fill=None, outline=border_color, width=border_size)

    # 在边框区域内绘制logo图片
    logo_position = (border_position[0] + border_size, height + (border_height - logo_size[1]) // 2)
    new_image.paste(logo, logo_position)

    # 在边框区域内绘制文本
    text1_position = (border_position[0] + border_size * 2 + logo_size[0], height + (border_height - text_height) // 2)
    text2_position = (border_position[0] + border_size * 2 + logo_size[0], text1_position[1] + text1_height)
    text3_position = (border_position[0] + border_size * 2 + logo_size[0], text2_position[1] + text2_height)
    draw.text(text1_position, text1, font=font, fill=font_color)
    draw.text(text2_position, text2, font=font, fill=font_color)
    draw.text(text3_position, text3, font=font, fill=font_color)

    # 保存合成后的图片
    new_image.save(output_path)

def generate_callback():
    # 获取下拉列表的选中值
    param1 = combo1.get()
    param2 = combo2.get()
    param3 = combo3.get()
    param4 = combo4.get()
    param5 = combo5.get()
    param6 = combo6.get()
    param66 = combo66.get()
    param7 = combo7.get()

    # 判断输入是否为空,弹出提示框
    if not param1:
        messagebox.showerror("Error", "图片路径不能为空!")
        return
    if not param2:
        messagebox.showerror("Error", "Logo路径不能为空!")
        return
    if not param3:
        messagebox.showerror("Error", "参数1不能为空!")
        return
    if not param4:
        messagebox.showerror("Error", "参数2不能为空!")
        return
    if not param5:
        messagebox.showerror("Error", "参数3不能为空!")
        return
    if not param6:
        messagebox.showerror("Error", "字体路径不能为空!")
        return
    if not param66:
        messagebox.showerror("Error", "字体大小不能为空!")
        return
    if not param7:
        messagebox.showerror("Error", "输出路径不能为空!")
        return

    # 调用后端处理函数,并传递参数
    # print(param1, param2, param22, param3, param4, param5, param6, param66, param7)

    # 示例用法
    # 照片路径
    image_path = param1
    # logo图片路径
    logo_path = param2
    # logo图片大小
    logo_size = (255, 255)
    # 图片信息
    tags = photo_exif(image_path)
    text1 = param3
    text2 = param4
    text3 = param5
    # 字体路径
    font_path = param6
    font_size = 55
    font_color = (0, 0, 0)  # 黑色
    border_size = 55
    border_color = (255, 255, 255)  # 白色
    # 输出照片 .后缀为png为无损图片 ,jpg为压缩后的图片
    output_path = str(image_path)+str(param7)

    add_logo_with_text(image_path, logo_path, logo_size, text1, text2, text3, font_path, font_size, font_color, border_size,
                       border_color, output_path)
    print("图片已保存至:", output_path)
    messagebox.showinfo("提示", "图片已保存至:\n" + output_path)


def on_file_list_double_click(event):
    selected_index = event.widget.curselection()
    if selected_index:
        selected_file = event.widget.get(selected_index)
        combo1.set(selected_file)

def on_logo_list_double_click(event):
    selected_index = event.widget.curselection()
    if selected_index:
        selected_file = event.widget.get(selected_index)
        combo2.set(selected_file)

def on_font_list_double_click(event):
    selected_index = event.widget.curselection()
    if selected_index:
        selected_file = event.widget.get(selected_index)
        combo6.set(selected_file)

# 创建主窗口
root = tk.Tk()


# 读取json文件参数
f = open('config.json', 'r',  encoding='utf-8')
content = f.read()
a = json.loads(content)

# 创建参数名称和下拉列表的组件
label1 = tk.Label(root, text="图片路径:")
combo1 = ttk.Combobox(root, state="normal", values=a["param1"])
label2 = tk.Label(root, text="logo路径:")
combo2 = ttk.Combobox(root, state="normal", values=a["param2"])
label3 = tk.Label(root, text="参数1:")
combo3 = ttk.Combobox(root, state="normal", values=a["param3"])
label4 = tk.Label(root, text="参数2:")
combo4 = ttk.Combobox(root, state="normal", values=a["param4"])
label5 = tk.Label(root, text="参数3:")
combo5 = ttk.Combobox(root, state="normal", values=a["param5"])
label6 = tk.Label(root, text="字体路径:")
combo6 = ttk.Combobox(root, state="normal", values=a["param6"])
label66 = tk.Label(root, text="字体大小:")
combo66 = ttk.Combobox(root, state="normal", values=a["param66"])
label7 = tk.Label(root, text="输出路径:")
combo7 = ttk.Combobox(root, state="normal", values=a["param7"])

# label7 = tk.Label(root, text="输出路径:")
# combo7 = ttk.Combobox(root, state="normal", values=["选项1", "选项2", "选项3"])

# 设置下拉框的高度和宽度
combo1.configure(width=68)
combo2.configure(width=68)
combo3.configure(width=68)
combo4.configure(width=68)
combo5.configure(width=68)
combo6.configure(width=68)
combo66.configure(width=68)
combo7.configure(width=68)



# 创建生成按钮并绑定回调函数
generate_btn = tk.Button(text="生成", command=generate_callback ,width=79 ,height=2)

# 使用grid布局来放置各个组件
label1.grid(row=0, column=0)
combo1.grid(row=0, column=1)
label2.grid(row=1, column=0)
combo2.grid(row=1, column=1)
label3.grid(row=2, column=0)
combo3.grid(row=2, column=1)
label4.grid(row=3, column=0)
combo4.grid(row=3, column=1)
label5.grid(row=4, column=0)
combo5.grid(row=4, column=1)
label6.grid(row=5, column=0)
combo6.grid(row=5, column=1)
label66.grid(row=6, column=0)
combo66.grid(row=6, column=1)
label7.grid(row=7, column=0)
combo7.grid(row=7, column=1)
generate_btn.grid(row=8, columnspan=2)  # 使用columnspan设置按钮横跨两列

# 设置默认选中值
combo1.current(0)
combo2.current(0)
combo3.current(0)
combo4.current(0)
combo5.current(0)
combo6.current(0)
combo66.current(0)
combo7.current(0)

# 添加标题
title_label = tk.Label(root, text="文件路径:")
title_label.grid(row=0, column=2, sticky="w")

# 添加标题
logo_title_label = tk.Label(root, text="Logo路径:")
logo_title_label.grid(row=0, column=4, sticky="w")

font_title_label = tk.Label(root, text="字体路径:")
font_title_label.grid(row=0, column=6, sticky="w")

# 获取当前py文件目录下的所有文件路径
file_paths = glob.glob('*')

# 创建文件路径列表框组件
file_listbox_var = tk.StringVar(value=file_paths)
file_listbox = tk.Listbox(root, listvariable=file_listbox_var)
file_listbox.grid(row=1, column=2, rowspan=7, padx=10, pady=10, sticky="nsew")

# 绑定双击事件处理函数
file_listbox.bind('<Double-Button-1>', on_file_list_double_click)

# 设置列表框自动填充满父容器
root.grid_rowconfigure(1, weight=1)
root.grid_columnconfigure(2, weight=1)

# 创建滚动条
file_scrollbar = tk.Scrollbar(root, orient="vertical", command=file_listbox.yview)
file_scrollbar.grid(row=1, column=3, rowspan=7, sticky="ns")

# 将滚动条与文件路径列表框关联
file_listbox.configure(yscrollcommand=file_scrollbar.set)

# 创建logo路径列表框组件
logo_listbox_var = tk.StringVar(value=file_paths)
logo_listbox = tk.Listbox(root, listvariable=logo_listbox_var)
logo_listbox.grid(row=1, column=4, rowspan=7, padx=10, pady=10, sticky="nsew")

# 绑定双击事件处理函数
logo_listbox.bind('<Double-Button-1>', on_logo_list_double_click)

# 创建滚动条
logo_scrollbar = tk.Scrollbar(root, orient="vertical", command=logo_listbox.yview)
logo_scrollbar.grid(row=1, column=5, rowspan=7, sticky="ns")

# 将滚动条与logo路径列表框关联
logo_listbox.configure(yscrollcommand=logo_scrollbar.set)

# 创建字体路径列表框组件
font_listbox_var = tk.StringVar(value=file_paths)
font_listbox = tk.Listbox(root, listvariable=font_listbox_var)
font_listbox.grid(row=1, column=6, rowspan=7, padx=10, pady=10, sticky="nsew")

# 绑定双击事件处理函数
font_listbox.bind('<Double-Button-1>', on_font_list_double_click)

# 创建滚动条
font_scrollbar = tk.Scrollbar(root, orient="vertical", command=font_listbox.yview)
font_scrollbar.grid(row=1, column=7, rowspan=7, sticky="ns")

# 将滚动条与字体路径列表框关联
font_listbox.configure(yscrollcommand=font_scrollbar.set)

#
# root.geometry("900x400")  # 设置窗口的宽度为600个像素,高度为400个像素

# 运行主循环
root.mainloop()

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1099632.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue 如何检测 data 中 数组的变化?

Vue 可以使用 watch 和 computed 监听数组的变化。 ① 使用 watch 监听数组 可以通过 deep 选项深度监听数组内部元素的变化 ② 使用 computed 监听数组 创建一个计算属性&#xff0c;返回数组的长度或者某个数组元素的值&#xff0c;当数组发生变化时&#xff0c;计算属性会…

在nodejs中实现调度任务

在nodejs中实现调度任务 node.js帮助开发人员简化了工作流程&#xff0c;创建了高效的应用程序。它的许多有用功能之一是任务调度。本文将探讨在nodejs中调度任务的重要性、各种使用第三方库的代码示例&#xff0c;以及需要遵循的一些有用的操作。 为什么我们需要安排任务 调…

#创作纪念日#我的256天创作纪念日

我的创作256天纪念日 机缘收获日常成就憧憬 机缘 机缘……好像128天的时候已经写过了…… 小升初时&#xff0c;我开始接触编程&#xff0c;进入了一个全新的世界。刚开始学习编程时&#xff0c;我只是对电脑的一些操作比较感兴趣&#xff0c;但慢慢地&#xff0c;我开始对编…

模式植物GO背景基因集制作

一边学习&#xff0c;一边总结&#xff0c;一边分享&#xff01; 写在前面 关于GO背景基因集文件的制作&#xff0c;我们在很早以前也发过。近两天&#xff0c;自己在分析时候&#xff0c;也是被搞了头疼。想重新制作一份GO背景基因集&#xff0c;进行富集分析。但是结果&…

vueday01——ref响应式

特性&#xff1a;持续监控某个响应式变量的属性名变化&#xff0c;可以使用shallowRef来取消这一特性&#xff0c;只监控对象整体的变化 ref测试代码&#xff1a; <template><div :id"idValue" ref"myDiv">打印obj{{ obj }}</div><…

大数据Flink(九十七):EXPLAIN、USE和SHOW 子句

文章目录 EXPLAIN、USE和SHOW 子句 一、EXPLAIN 子句 二、USE 子句

QQd挂源码已更新最新加速项目程序全开源

1、99 公益日活动加速任务已全部完成适配&#xff0c;空间公益说说和评论并分享小世界内容任务在已有的功能上进行挂机&#xff0c; 其中【发小世界】功能暂时更名为【公益小世界】。 2、上线新功能【公益答题】用于完成参加 Qbox 公益答题任务&#xff0c;等级套装有任意一项…

期中考Web复现

第一题 1z_php <?php //Yeedo told you to study hard! echo !(!(!(!(include "flag.php")||(!error_reporting(0))||!isset($_GET[OoO])||!isset($_GET[0o0])||($_GET[OoO]2023)||!(intval($_GET[OoO][0])2023)||$_GET[0o0]$_GET[OoO]||!(md5($_GET[0o0])md5($_…

Java Static

Static 变量被 static 修饰 static 修饰的变量在类中只有一份&#xff0c;可以称为类变量&#xff0c;其他变量称为实例变量在方法区加载类的时候&#xff0c;会检查类中是否存在静态变量&#xff0c;如果存在则会在堆内存区域开辟一块空间用于存储静态变量。方法区中的静态变…

A114-经典赛题-Web应用程序文件包含安全攻防

实验步骤: Web应用程序文件包含安全攻防 任务环境说明&#xff1a; 服务器场景&#xff1a;WebServ2003&#xff08;用户名&#xff1a;administrator&#xff1b;密码&#xff1a;空&#xff09; 服务器场景操作系统&#xff1a;Microsoft Windows2003 Server 服务器场景…

IPv6知识概述 - ND协议

IPv6知识概述 - ND协议 参考文章&#xff1a;https://blog.csdn.net/Gina_wj/article/details/106708770 IPv6基础篇&#xff08;四&#xff09;&#xff1a;邻居发现协议NDP ND协议功能概述 ND&#xff08;Neighbor Discovery&#xff0c;邻居发现&#xff09;协议是IPv6的…

01【Git的基本使用与底层原理】

下一篇&#xff1a;02【Git的分支与数据恢复】 目录&#xff1a;【Git系列教程-目录大纲】 文章目录 一、Git概述1.1 Git简介1.2 集中式与分布式1.2.1 集中式版本控制1.2.2 分布式版本控制 1.3 Git的使用流程1.3.1 本地仓库1.3.2 协同开发 1.4 Git的配置1.4.1 Git的配置等级1…

从头开始机器学习:线性回归

从头开始机器学习&#xff1a;线性回归 跟随 16 分钟阅读 28月 <> 1 一、说明 本篇实现线性回归的先决知识是&#xff1a;基本线性代数&#xff0c;微积分&#xff08;偏导数&#xff09;、梯度和、Python &#xff08;NumPy&#xff09;&#xff1b;从线性方程入手。 代…

【LeetCode刷题(数据结构与算法)】:用栈实现队列

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQueue 类&#xff1a; void push(int x) 将元素 x 推到队列的末尾 int pop() 从队列的开头移除并返回元素 int peek() 返回队列开头…

运维 | 如何在 Linux 系统中删除软链接 | Linux

运维 | 如何在 Linux 系统中删除软链接 | Linux 介绍 在 Linux 中&#xff0c;符号链接&#xff08;symbolic link&#xff0c;或者symlink&#xff09;也称为软链接&#xff0c;是一种特殊类型的文件&#xff0c;用作指向另一个文件的快捷方式。 使用方法 我们可以使用 ln…

双目标定之张正友标定法数学原理详解matlab版

目录 前言 1.相机标定 1.1 双目视觉基本原理 1. 2 相机的四个坐标系 1.3 相机畸变与校正 2.1 相机标定 张正友友棋盘格标定法在matlab的实现 这一篇主要详细介绍标定原理和相机各个坐标系之间的关系为后续的定位测距和重建做基础 前言 最近重新整理了一下自己做过的双目…

【单片机基础】使用51单片机制作函数信号发生器(DAC0832使用仿真)

文章目录 &#xff08;1&#xff09;DA转换&#xff08;2&#xff09;DAC0832简介&#xff08;3&#xff09;电路设计&#xff08;4&#xff09;参考例程&#xff08;5&#xff09;参考文献 &#xff08;1&#xff09;DA转换 单片机作为一个数字电路系统&#xff0c;当需要采集…

ICML2021 | RSD: 一种基于几何距离的可迁移回归表征学习方法

目录 引言动机分析主角&#xff08;Principal Angle&#xff09;表征子空间距离正交基错配惩罚可迁移表征学习实验数据集介绍 实验结果总结与展望 论文链接 相关代码已经开源 引言 深度学习的成功依赖大规模的标记数据&#xff0c;然而人工标注数据的代价巨大。域自适应&…

10种新型网络安全威胁和攻击手法

2023年&#xff0c;网络威胁领域呈现出一些新的发展趋势&#xff0c;攻击类型趋于多样化&#xff0c;例如&#xff1a;从MOVEit攻击可以看出勒索攻击者开始抛弃基于加密的勒索软件&#xff0c;转向窃取数据进行勒索&#xff1b;同时&#xff0c;攻击者们还减少了对传统恶意软件…

android U广播详解(二)

android U广播详解&#xff08;一&#xff09; 基础代码介绍 广播相关 // 用作单个进程批量分发receivers&#xff0c;已被丢弃 frameworks/base/services/core/java/com/android/server/am/BroadcastReceiverBatch.java // 主要逻辑所在类&#xff0c;包括入队、分发、结束…