Tools for \LaTeX Typesettings
Python GUI to help insert images and PDF documents in documents.
Quickly insert images
The following code uses PySimpleGUI
import os
import PySimpleGUI as sg
import tkinter as tk # Add this line to import the tk module
# Define the command-line arguments
import argparse
import ctypes
import platform
def make_dpi_aware():
if int(platform.release()) >= 8:
ctypes.windll.shcore.SetProcessDpiAwareness(True)
# 为了正确处理含空格的路径
class MyAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, self.dest, ' '.join(values))
parser = argparse.ArgumentParser(description="Generate LaTeX commands for inserting figures.")
parser.add_argument("--callPath", nargs='+', help="the path of the calling file", action=MyAction)
args = parser.parse_args()
if args.callPath:
dir = os.path.abspath(args.callPath)
dis = True
else:
dir = "C:/Users/LiuGe/Documents"
dis = False
# 设置全局选项
sg.set_options(font=('Arial', 11))
# Create the layout for the window
layout = [
[sg.Frame("文件选择",[
[sg.Text("选择一个图像文件:")],
[sg.Input(key="-FILE-"), sg.FileBrowse(key="fileSelector",initial_folder=dir)],
[sg.Text("-callPath 参数"), sg.FolderBrowse(key="callPath", initial_folder="C:/Users/LiuGe/Documents", disabled=args.callPath is not None)],
])],
[sg.Frame("路径相关设置",[
[sg.Checkbox("使用相对路径", key="-USE RELATIVE PATH-")],
[sg.Text("相对路径: -", key="-RELATIVE PATH TEXT-")],
])],
[sg.Frame("布局设置",[
[sg.Text("浮动体位置:"),sg.Combo(["H", "T", "P"], default_value="H", key="-FLOAT-")],
[sg.Checkbox("包含 center 环境", key="-CENTER-",default=True)],
[sg.Text("图像宽度 (0-1):"),sg.Slider(range=(0, 1), default_value=0.7, resolution=0.01, orientation="h", key="-WIDTH-")],
[sg.Checkbox("Only Contain Inclusion", key="-GRAPHICS-", default=False)],
])],
[sg.Frame("标题部分",[
[sg.Checkbox("添加图像标签", key="-CAPTION-")],
[sg.Checkbox("不编号的图像标签", key="-UNNUMBERED CAPTION-")],
[sg.InputText(key="-LABEL-", disabled=False)],
])],
[sg.Frame("CMD Part",[
[sg.Button("生成命令"), sg.Button("复制到剪贴板"), sg.Button("退出")],
[sg.Multiline(key="-LATEX CMDS-", size=(80, 10))]
])]
]
# Create the window with the layout, size=(800, 600)
window = sg.Window("插入图像", layout)
# Wait for the window to be closed
while True:
make_dpi_aware()
event, values = window.read()
if event == sg.WIN_CLOSED or event == "退出":
break
if args.callPath or values["callPath"]:
if args.callPath:
window["fileSelector"].update(os.path.abspath(args.callPath))
else:
window["fileSelector"].update(os.path.abspath(values["callPath"]))
# Render the LaTeX command
if values["-FILE-"] != "":
# Get the values of the input fields
align = values["-FLOAT-"]
include_center = values["-CENTER-"]
width = values["-WIDTH-"]
image_path = values["-FILE-"]
captionTF = values["-CAPTION-"]
unnumbered_caption = values["-UNNUMBERED CAPTION-"]
caption = values["-LABEL-"]
# Check if the "use relative path" checkbox is checked
if values["-USE RELATIVE PATH-"]:
# Get the relative path to the image file
if args.callPath or values["callPath"]:
if args.callPath:
# If the callPath argument is used, use the path of the calling file
call_dir = os.path.abspath(args.callPath)
else:
# If the callPath argument is not used, use the path of the selected directory
call_dir = os.path.abspath(values["callPath"])
# # Get the directory of the calling file
# call_dir = os.path.dirname(os.path.abspath(args.callPath))
# Get the relative path to the image file
relative_path = os.path.relpath(image_path, call_dir).replace("\\", "/")
else:
# If there is no callPath argument, use the absolute path of the image file
relative_path = "-"
# window["-USE RELATIVE PATH-"].update(disabled=True)
image_path = relative_path
window["-RELATIVE PATH TEXT-"].update(f"相对路径: {relative_path}")
else:
window["-RELATIVE PATH TEXT-"].update("相对路径: -")
# Render the LaTeX command using a formatted string
if values["-GRAPHICS-"]:
result = f"\\includegraphics[width={width}\\textwidth]{{{image_path}}}"
else:
result = f"\\begin{{figure}}[{align}]"
if include_center:
result += "\n\\begin{center}"
result += f"\n\\includegraphics[width={width}\\textwidth]{{{image_path}}}"
if include_center:
result += "\n\\end{center}"
if captionTF or unnumbered_caption:
if unnumbered_caption:
result += f"\n\\caption*{{{caption}}}"
else:
result += f"\n\\caption{{{caption}}}"
result += "\n\\end{figure}"
# Update the output field with the rendered LaTeX command
window["-LATEX CMDS-"].update(result)
# Copy the generated commands to the clipboard
if event == "复制到剪贴板":
root = tk.Tk()
root.withdraw()
root.clipboard_clear()
root.clipboard_append(values["-LATEX CMDS-"])
root.update()
root.destroy()
#sg.popup("已复制到剪贴板!")
sg.popup_quick_message("已复制到剪贴板!", title=None, background_color=None, text_color=None, auto_close_duration=500)
# Close the window
window.close()
Quickly insert PDF files
import argparse
# import click
import os
import PySimpleGUI as sg
from PyPDF2 import PdfReader
import pyperclip
# Click CLI-Arg
# @click.command()
# @click.option('--mainPath',help='CallPath')
import ctypes
import platform
def make_dpi_aware():
if int(platform.release()) >= 8:
ctypes.windll.shcore.SetProcessDpiAwareness(True)
def get_pdf_page_count(file_path):
try:
with open(file_path, 'rb') as file:
pdf_reader = PdfReader(file)
page_count = len(pdf_reader.pages)
return page_count
except Exception as e:
sg.popup(f"无法打开文件: {file_path}\n错误信息: {e}")
return None
def parse_page_range(page_range, page_count):
try:
if page_range:
# If the user has input a page range, parse it into a list of page numbers
pages = []
for part in page_range.split(","):
if "-" in part:
# If the part is a sub-range, add the pages in the sub-range to the list
start, end = map(int, part.split("-") if part else [])
if start <= 0 or end > page_count or start >= end:
# If the sub-range is invalid, show an error message and reset the page range
sg.popup("无效的页码范围")
page_range = f"1-{page_count}"
break
pages.extend(range(start, end+1))
else:
# If the part is a single page number, add it to the list
page = int(part)
if page <= 0 or page > page_count:
# If the page number is invalid, show an error message and reset the page range
sg.popup("无效的页码")
page_range = f"1-{page_count}"
break
pages.append(page)
else:
# If all parts of the page range are valid, join them into a comma-separated string
page_range = ",".join(map(str, pages))
else:
# If the user has not input a page range, use all pages in the PDF file
pages = list(range(1, page_count+1))
page_range = f"1-{page_count}"
# Group the page numbers into contiguous ranges
page_ranges = []
current_range = []
for page in sorted(pages):
if not current_range or page == current_range[-1] + 1:
current_range.append(page)
else:
page_ranges.append(current_range)
current_range = [page]
if current_range:
page_ranges.append(current_range)
# Generate a string representation of the page ranges
page_range_text = ",".join(
[f"{r[0]}-{r[-1]}" if len(r) > 1 else str(r[0]) for r in page_ranges])
return page_range, page_range_text
except Exception as e:
sg.popup(f"无法解析页码范围: {page_range}\n错误信息: {e}")
return None, None
def get_page_ranges(pages):
"""Convert a list of page numbers to a list of page ranges."""
ranges = []
start = pages[0]
end = pages[0]
for page in pages[1:]:
if page == end + 1:
end = page
else:
if start == end:
ranges.append(str(start))
else:
ranges.append(f"{start}-{end}")
start = page
end = page
if start == end:
ranges.append(str(start))
else:
ranges.append(f"{start}-{end}")
return ranges
def parse_page_range2(page_range, page_count):
"""Parse a page range string and return a list of page numbers."""
if not page_range:
return list(range(1, page_count+1)), "全部"
pages = []
parts = page_range.split(",")
for part in parts:
if "-" in part:
start, end = part.split("-")
start = int(start.strip())
end = int(end.strip())
pages.extend(range(start, end+1))
else:
pages.append(int(part.strip()))
pages = sorted(set(pages))
page_range_text = ",".join(get_page_ranges(pages))
return pages, page_range_text
# 为了正确处理含空格的路径
class MyAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, self.dest, ' '.join(values))
# Parse command-line arguments
parser = argparse.ArgumentParser()
parser.add_argument("-callPath", nargs='+' ,help="Directory of the calling application", action=MyAction)
args = parser.parse_args()
# 设置全局选项
sg.set_options(font=('msyh.ttc', 11))
# 检查args.path是否非空
if args.callPath:
default_path = os.path.abspath(args.callPath)
else:
default_path = "C:/Users/LiuGe/Documents"
# Define the layout of the GUI
layout = [
[sg.Frame("选择文件", [
[sg.Text("文件选择"), sg.InputText(), sg.FileBrowse(key="file1",initial_folder=default_path)]
])],
[sg.Frame("命令行选项", [
[sg.Text("-callPath 参数"), sg.FolderBrowse(key="callPath", initial_folder="C:/Users/LiuGe/Documents/Mac-Windows-NUC",disabled=args.callPath is not None)]
])],
[sg.Frame("PDF 选项", [
[sg.Checkbox("Fit Paper", key="fit_paper")],
[sg.Text("Scale:"), sg.Slider(range=(0.1, 1.0), default_value=1.0,
resolution=0.1, orientation="h", size=(20, 15), key="scale_slider")],
[sg.Text("页码范围 (例如: 1-5, 7, 9-10)"), sg.InputText(key="page_range")]
])],
[sg.Frame("路径选项", [
[sg.Text("相对路径: -", key="relative_path_text")],
[sg.Checkbox("使用相对路径", key="use_relative_path_button")]
])],
[sg.Frame("命令选项", [
[sg.Submit("生成 LaTeX 命令"), sg.Button("生成分离的 LaTeX 命令"), sg.Cancel()],
[sg.Text("页数信息: -", key="page_count_text")],
[sg.Multiline("LaTeX Commands: -", size=(80, 20),
key="latex_commands_text")],
[sg.Button("复制 LaTeX 命令到剪贴板", key="copy_latex_commands_button")]
])]
]
# Create the GUI window
window = sg.Window("插入PDF文件", layout)
while True:
# 根据DPI更改清晰度
make_dpi_aware()
# Wait for an event to occur
event, values = window.read()
# If the window is closed or the Cancel button is clicked, exit the loop
if event == sg.WINDOW_CLOSED or event == "Cancel":
break
# Persistent Scale
window['scale_slider'].update(sg.user_settings_get_entry('-Scale-'))
# Get the path of the selected PDF file
pdf_file_path = values["file1"]
# Get the number of pages in the PDF file
page_count = get_pdf_page_count(pdf_file_path)
page_count_text = f"页数信息: {page_count} 页"
window["page_count_text"].update(page_count_text)
# Determine whether the "Fit Paper" option should be included in the LaTeX command
fitpaper_option = "fitpaper" if values["fit_paper"] else ""
sg.user_settings_set_entry('-Scale-', values['scale_slider'])
# Parse the user's input for the page range
page_range = values["page_range"]
page_range, page_range_text = parse_page_range(page_range, page_count)
# Get the scale value from the slider
scale = values["scale_slider"]
# If the 生成 LaTeX 命令 button is clicked, generate the LaTeX command
if event == "生成 LaTeX 命令":
# Include the "Fit Paper" option and page range in the LaTeX command
comma = "," if fitpaper_option else ""
latex_cmd = f"\\includepdf[offset=0mm 0mm, pages={{{page_range_text}}}{comma}{fitpaper_option}, scale={scale}, pagecommand={{}}]{{{pdf_file_path}}} "
window["latex_commands_text"].update(latex_cmd)
if args.callPath or values["callPath"]:
if args.callPath:
call_path = os.path.abspath(args.callPath)
window["file1"].update(call_path)
else:
call_path = os.path.abspath(values["callPath"])
window["file1"].update(call_path)
# Get the relative path of the selected file in terms of the callPath argument
relative_path_text = ""
if args.callPath or values["callPath"]:
if args.callPath:
call_path = os.path.abspath(args.callPath)
else:
call_path = os.path.abspath(values["callPath"])
if os.path.splitdrive(pdf_file_path)[0] != os.path.splitdrive(call_path)[0]:
# If the selected file is on a different drive than the callPath argument,
# use the absolute path of the selected file instead of the relative path
relative_path_text = f"相对路径: -"
else:
relative_path = os.path.relpath(
pdf_file_path, call_path).replace("\\", "/")
relative_path_text = f"相对路径: {relative_path}"
# else:
# window["use_relative_path_button"].update(disabled=True)
window["relative_path_text"].update(relative_path_text)
if values["use_relative_path_button"]:
latex_cmd = latex_cmd.replace(pdf_file_path, relative_path)
window["latex_commands_text"].update(latex_cmd)
window["relative_path_text"].update(f"相对路径: {relative_path}")
# Print the LaTeX command and the relative path of the selected file
print(latex_cmd)
print(relative_path_text)
if event == "生成分离的 LaTeX 命令":
# Parse the page range and generate the separate LaTeX commands
page_range = values["page_range"]
pages, page_range_text = parse_page_range2(page_range, page_count)
separate_cmds = []
separate_cmds = []
comma = "," if fitpaper_option else ""
for page in pages:
separate_cmd = f"\\includepdf[offset=0mm 0mm, pages={{{page}}}{comma}{fitpaper_option}, scale={scale}, pagecommand={{}}]{{{pdf_file_path}}} \n"
separate_cmds.append(separate_cmd)
separate_cmds_text = "\n".join(separate_cmds)
window["latex_commands_text"].update(separate_cmds_text)
# Get the relative path of the selected file in terms of the callPath argument
relative_path_text = ""
if args.callPath:
call_path = os.path.abspath(args.callPath)
if os.path.splitdrive(pdf_file_path)[0] != os.path.splitdrive(call_path)[0]:
# If the selected file is on a different drive than the callPath argument,
# use the absolute path of the selected file instead of the relative path
relative_path_text = f"相对路径: -"
else:
relative_path = os.path.relpath(
pdf_file_path, call_path).replace("\\", "/")
relative_path_text = f"相对路径: {relative_path}"
else:
window["use_relative_path_button"].update(disabled=True)
window["relative_path_text"].update(relative_path_text)
if values["use_relative_path_button"]:
separate_cmds_text = separate_cmds_text.replace(
pdf_file_path, relative_path)
window["latex_commands_text"].update(separate_cmds_text)
window["relative_path_text"].update(f"相对路径: {relative_path}")
print(separate_cmds_text)
print(relative_path_text)
# If the 复制 LaTeX 命令到剪贴板 button is clicked, copy the LaTeX command to the clipboard
if event == "copy_latex_commands_button":
pyperclip.copy(window["latex_commands_text"].get())
sg.popup(f"LaTeX 命令已复制到剪贴板。\n {window['latex_commands_text'].get()}")
# Close the GUI window
window.close()