diff --git a/README-cn.md b/README-cn.md index 7ef1d9e..3939b62 100644 --- a/README-cn.md +++ b/README-cn.md @@ -12,6 +12,13 @@ 可生成*普通二维码*、*带图片的艺术二维码(黑白与彩色)*、*动态二维码(黑白与彩色)*。 +## 快速指南 +* 克隆仓库 +* `py -m venv venv` +* `./venv/Scripts/activate` +* `pip install -e .` +* 运行 GUI:`amzqrgui` + ## Contents 目录 * [Amazing-QR](#amazing-qr) diff --git a/README.md b/README.md index ebe6478..2dc8a94 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,13 @@ Generate *common qr-code*, *artistic qr-code (black & white or colorized)*, *animated qr-code (black & white or colorized)*. +## Fast instructions +* Clone the repo +* `py -m venv venv` +* `./venv/Scripts/activate` +* `pip install -e .` +* `amzqrgui` to run the GUI + ## Contents * [Amazing-QR](#amazing-qr) diff --git a/amzqr/amzqr.py b/amzqr/amzqr.py index 35a506e..abdc781 100755 --- a/amzqr/amzqr.py +++ b/amzqr/amzqr.py @@ -25,8 +25,10 @@ def run(words, version=1, level='H', picture=None, colorized=False, contrast=1.0 # check every parameter - if not isinstance(words, str) or any(i not in supported_chars for i in words): - raise ValueError('Wrong words! Make sure the characters are supported!') + assert isinstance(words, str), f'"{words}" is not a string.' + nonmatching = [c for c in words if c not in supported_chars] + if nonmatching: + raise ValueError(f"Wrong words! The following characters are unsupported: '{set(nonmatching)}'") if not isinstance(version, int) or version not in range(1, 41): raise ValueError('Wrong version! Please choose a int-type value from 1 to 40!') if not isinstance(level, str) or len(level)>1 or level not in 'LMQH': diff --git a/amzqr/ui.py b/amzqr/ui.py new file mode 100644 index 0000000..23a5682 --- /dev/null +++ b/amzqr/ui.py @@ -0,0 +1,157 @@ +import os +import tkinter as tk +from tkinter import filedialog, ttk + +from sv_ttk import use_dark_theme + +from amzqr.amzqr import run + + +class QRCodeApp(tk.Tk): + """A simpler UI interface for creating QR codes""" + + def __init__(self): + super().__init__() + self.title("Amazing QR") + self.vars = { + "input_text": tk.StringVar(), + "version": tk.IntVar(value=7), + "level": tk.StringVar(value="H"), + "picture": tk.StringVar(), + "colorized_picture": tk.BooleanVar(value=True), + "contrast": tk.DoubleVar(value=1.0), + "brightness": tk.DoubleVar(value=1.0), + "filename_out": tk.StringVar(), + "output_directory": tk.StringVar(value=os.getcwd()), + } + self.create_widgets() + + def add_picture_quality_frame(self): + """Image quality settings""" + ttk.Label(self, text="📷 Image Quality")\ + .grid(row=2, column=0, sticky="NSEW", padx=5, pady=3) + frame0 = ttk.Frame(self) + frame0.grid(row=3, column=0, columnspan=5, sticky="NSEW", pady=10) + frame1 = ttk.Frame(self) + frame1.grid(row=4, column=0, columnspan=5, sticky="NSEW", pady=10) + frame2 = ttk.Frame(self) + frame2.grid(row=5, column=0, columnspan=5, sticky="NSEW", pady=10) + + # Row 0 : file picker + ttk.Label(frame0, text="Picture: ")\ + .grid(row=0, column=0, sticky="NSEW", padx=(30, 10)) + ttk.Entry(frame0, textvariable=self.vars["picture"], width=45)\ + .grid(row=0, column=1, padx=5) + ttk.Button(frame0, text="Browse", command=self.select_picture)\ + .grid(row=0, column=2, padx=5) + + # Row 1: image dimensions, ecc + ttk.Label(frame1, text="Height/Width:")\ + .grid(row=0, column=0, sticky="NSEW", padx=(30, 2)) + ttk.Combobox( + frame1, textvariable=self.vars["version"], values=list(range(1, 41)), width=8)\ + .grid(row=0, column=1) + + ttk.Label(frame1, text="Error Correction Level:")\ + .grid(row=0, column=2, sticky="NSEW", padx=(30, 2)) + ttk.Combobox( + frame1, textvariable=self.vars["level"], values=("L", "M", "Q", "H"), width=8)\ + .grid(row=0, column=3) + + # Row 2: Colorized, Contrast, Brightness + ttk.Checkbutton(frame2, text="Colorized", variable=self.vars["colorized_picture"])\ + .grid(row=0, column=0, sticky="NSEW", padx=30) + ttk.Label(frame2, text="Contrast:")\ + .grid(row=0, column=1, sticky="NSEW", padx=(63, 5)) + ttk.Entry(frame2, textvariable=self.vars["contrast"], width=4)\ + .grid(row=0, column=2, sticky="NSEW") + + ttk.Label(frame2, text="Brightness:")\ + .grid(row=0, column=3, sticky="NSEW", padx=(63, 5)) + ttk.Entry(frame2, textvariable=self.vars["brightness"], width=4)\ + .grid(row=0, column=4, sticky="NSEW") + + def add_output_frame(self): + """Output settings frame""" + ttk.Label(self, text="🗃 Output settings")\ + .grid(row=6, column=0, sticky="NSEW", padx=5, pady=3) + + outframe = ttk.Frame(self) + outframe.grid(row=7, column=0, columnspan=5, sticky="NSEW", pady=10) + outframe2 = ttk.Frame(self) + outframe2.grid(row=8, column=0, columnspan=5, sticky="NSEW", pady=10) + + ttk.Label(outframe, text="Filename:")\ + .grid(row=0, column=0, sticky="NSEW", padx=(30, 10)) + ttk.Entry(outframe, textvariable=self.vars["filename_out"], width=45)\ + .grid(row=0, column=1, padx=5) + + ttk.Label(outframe2, text="Directory:")\ + .grid(row=0, column=0, sticky="NSEW", padx=(30, 8)) + ttk.Entry(outframe2, textvariable=self.vars["output_directory"], width=45)\ + .grid(row=0, column=1, padx=5) + ttk.Button(outframe2, text="Browse", command=self.select_directory)\ + .grid(row=0, column=2, padx=(0, 0)) + + def create_widgets(self): + """Input fields similar to those in terminal.py""" + ttk.Label(self, text="⌨ Text to encode")\ + .grid(row=0, column=0, sticky="NSEW", padx=5, pady=3) + ttk.Entry(self, textvariable=self.vars["input_text"], width=65)\ + .grid(row=1, column=0, padx=(30, 10), pady=2, sticky="NSEW") + + self.add_picture_quality_frame() + self.add_output_frame() + + ttk.Button(self, text="Generate QR Code", command=self.generate_qr)\ + .grid(row=9, column=0, pady=5, sticky="NSEW") + + def select_picture(self): + """Image handler""" + file_path = filedialog.askopenfilename( + filetypes=[("Images", "*.jpg *.png *.bmp *.gif")]) + if file_path: + self.vars["picture"].set(file_path) + if not self.vars["filename_out"].get(): + name, ext = os.path.splitext(file_path.split("/")[-1]) + self.vars["filename_out"].set(f'{name}_qrcode{ext}') + + def select_directory(self): + """Output directory picker""" + dir_path = filedialog.askdirectory() + if dir_path: + self.vars["output_directory"].set(dir_path) + + def display_qrcode(self, qr_name): + """Display the QR code in a new window""" + img = tk.PhotoImage(file=qr_name) + label = tk.Label(tk.Toplevel(self), image=img) + label.garbage_collection_prevention = img + label.pack() + + def generate_qr(self): + """Run main method""" + params = { + "Words": self.vars["input_text"].get(), + "Version": self.vars["version"].get(), + "Level": self.vars["level"].get(), + "Picture": self.vars["picture"].get(), + "Colorized": self.vars["colorized_picture"].get(), + "Contrast": self.vars["contrast"].get(), + "Brightness": self.vars["brightness"].get(), + "Name": self.vars["filename_out"].get(), + "Directory": self.vars["output_directory"].get(), + } + _, _, filename = run(*params.values()) + self.display_qrcode(filename) + + +def main(): + """Runs the tkinter UI""" + app = QRCodeApp() + use_dark_theme(app) + app.mainloop() + + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt index 26e6a8a..a2d6012 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ -imageio==1.5 -numpy==1.11.1 -Pillow==8.1.1 +imageio==2.37.0 +numpy==2.2.2 +pillow==11.1.0 +sv-ttk==2.6.0 \ No newline at end of file diff --git a/setup.py b/setup.py index 7fafbe9..bfa4d29 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name='amzqr', - version='0.0.2', + version='0.0.3', keywords='qr qrcode amazing artistic animated gif colorized', description='Generater for amazing QR Codes. Including Common, Artistic and Animated QR Codes.', long_description=long_description, @@ -21,7 +21,8 @@ install_requires=[ 'imageio >= 1.5', 'numpy >= 1.11.1', - 'Pillow>=3.3.1' + 'Pillow>=3.3.1', + 'sv-ttk>=2.6.0' ], packages=['amzqr', 'amzqr.mylibs'], license='GPLv3', @@ -35,6 +36,7 @@ entry_points={ 'console_scripts': [ 'amzqr=amzqr.terminal:main', + 'amzqrgui=amzqr.ui:main', ], }, python_requires=">=3",