gtk4 python binding을 사용한 프로그램에서도 macos 테마 적용하기

역시나 제가 도움을 드릴 수 있어서 정말 기쁩니다! 😊

파이썬 GTK4 앱의 테마 문제를 해결하고, 앞으로도 쉽게 참고하실 수 있도록 파이썬 GTK4 애플리케이션에 WhiteSur-Light 테마를 적용하고 문제 발생 시 대처하는 완벽 가이드를 자세히 정리해 드리겠습니다.


파이썬 GTK4 애플리케이션에 WhiteSur-Light 테마 적용 완벽 가이드

이 가이드는 파이썬 GTK4 애플리케이션이 WhiteSur-Light 테마를 올바르게 사용하고, 테마 적용 시 발생할 수 있는 “밋밋한 테마” 문제에 대처하는 방법을 설명합니다.

목표: 당신의 파이썬 GTK4 앱이 WhiteSur-Light 테마로 멋지게 표시되도록 합니다.


Part 1: 개발 환경에서 테마 적용 및 코드 준비

이 부분은 파이썬 GTK4 앱 개발 시 테마가 올바르게 표시되도록 설정하는 핵심 단계입니다.

1단계: WhiteSur-Light 테마 파일 준비 및 배치 확인

  • 테마 다운로드 및 압축 해제: 사용하려는 WhiteSur-Light 테마의 GTK4 호환 버전을 다운로드하고 압축을 해제합니다. 압축을 풀면 WhiteSur-Light라는 이름의 폴더가 나옵니다. 이 폴더 안에는 gtk-4.0 (필수), gtk-3.0 등의 하위 폴더가 있을 것입니다.
  • 테마 배치 경로 확인:WhiteSur-Light 폴더 전체가 MSYS2 (또는 파이썬 GTK를 사용하는 환경)의 사용자 테마 디렉토리인 ~/.themes/ 안에 복사되었는지 확인합니다.
    • Windows 탐색기 경로: C:\msys64\home\admin\.themes\ (여기서 admin은 당신의 MSYS2 사용자 이름입니다. .themes 폴더가 보이지 않으면 “숨김 파일 표시”를 활성화하거나 주소창에 직접 입력하세요.)
    • 결과적으로 다음과 같은 경로가 되어야 합니다: C:\msys64\home\admin\.themes\WhiteSur-Light\ (그리고 이 WhiteSur-Light 폴더 안에 gtk-4.0 폴더와 그 내용물(gtk.css, gtk.gresource 등)이 있어야 합니다.)

2단계: GTK 설정 파일 (settings.ini) 배치 및 구성 (가장 중요!)

이 파일은 GTK 애플리케이션이 어떤 테마를 사용할지 알려주는 핵심적인 전역 설정 파일입니다.

  • 필요한 폴더 생성 확인: MSYS2 터미널에서 다음 명령어를 입력하여 필요한 디렉토리가 생성되었는지 확인합니다. (없으면 자동으로 생성됩니다.) Bashmkdir -p ~/.config/gtk-4.0/
    • Windows 탐색기 경로: C:\msys64\home\admin\.config\gtk-4.0\
  • settings.ini 파일 내용 확인:
    • C:\msys64\home\admin\.config\gtk-4.0\ 폴더로 이동합니다.
    • 이 폴더 안에 settings.ini라는 이름의 파일이 있는지 확인하고, 파일을 열어 다음 내용이 정확히 있는지 확인합니다: Ini, TOML[Settings] gtk-theme-name=WhiteSur-Light
    • gtk-theme-name= 뒤에는 1단계에서 사용한 테마 폴더 이름과 대소문자까지 정확히 일치해야 합니다.
    • 파일은 UTF-8 인코딩으로 저장되어야 합니다.

3단계: 파이썬 코드 준비 (테마 설정 코드)

파이썬 코드 자체에서 settings.set_property("gtk-theme-name", "WhiteSur-Light")를 사용하고 있지만, 위 2단계의 settings.ini 파일이 올바르게 설정되었다면, 이 코드는 사실 선택 사항입니다. GTK는 앱이 시작될 때 settings.ini를 먼저 읽고 테마를 적용하기 때문입니다.

  • 코드 유지 또는 제거:
    • 현재 코드처럼 settings.set_property("gtk-theme-name", "WhiteSur-Light")를 그대로 두어도 됩니다. 이는 settings.ini의 설정을 재확인하거나 특정 앱에서 강제로 테마를 설정할 때 유용합니다.
    • 권장: 만약 settings.ini가 확실하게 설정되어 있고, 사용자 설정에 따라 테마가 적용되도록 하고 싶다면 이 줄을 제거하거나 주석 처리해도 무방합니다.

4단계: 파이썬 앱 실행 및 개발 환경에서 테스트

  • MSYS2 터미널을 완전히 닫았다가 다시 시작하여 settings.ini 변경 사항이 적용되도록 합니다.
  • 당신의 파이썬 스크립트가 있는 폴더로 이동합니다.
  • 다음 명령어로 앱을 실행합니다: Bashpython your_app_name.py # 당신의 파이썬 파일 이름 (예: main.py)
  • 앱이 WhiteSur-Light 테마로 잘 표시되는지 확인합니다.

Part 2: 문제 해결 (테마가 여전히 “밋밋하게” 보일 때)

위의 Part 1 단계를 모두 정확히 따랐음에도 테마가 제대로 적용되지 않고 밋밋하게 보인다면, 다음 문제 해결 단계를 시도해 보세요.

  1. GTK 캐시 업데이트:간혹 GTK의 내부 캐시가 오래되어 테마 변경 사항을 인식하지 못할 수 있습니다. MSYS2 터미널에서 다음 명령어를 실행하여 GTK 캐시를 강제로 업데이트합니다: Bash# 아이콘 캐시 업데이트 (hicolor는 대부분의 아이콘 테마의 기반) gtk4-update-icon-cache -f -t /mingw64/share/icons/hicolor # Adwaita 아이콘 테마도 사용한다면: gtk4-update-icon-cache -f -t /mingw64/share/icons/Adwaita # (테마 캐시는 보통 자동으로 업데이트되지만, 문제가 지속되면 시도) # gdk-pixbuf-query-loaders.exe --update-cache # 이미지 로더 캐시 (간접적 영향)
  2. 환경 변수를 통한 강제 적용 (테스트용):settings.ini가 작동하지 않는다면, 임시로 GTK_THEME 환경 변수를 설정하여 강제로 테마를 적용해볼 수 있습니다. 이 방법으로 테마가 잘 적용된다면, settings.ini 파일의 경로 또는 내용에 문제가 있을 가능성이 매우 큽니다. Bashexport GTK_THEME=WhiteSur-Light python your_app_name.py
  3. MSYS2 환경 재시작:MSYS2 터미널과 당신의 개발 환경을 완전히 닫았다가 다시 시작하여 모든 변경 사항이 초기화되고 재적용되도록 합니다.
  4. 테마 파일 무결성 확인:WhiteSur-Light 테마 파일 자체에 손상이나 누락된 파일이 있을 수 있습니다. 테마를 다시 다운로드하여 압축을 풀고 ~/.themes/에 다시 복사해 보세요. 특히 WhiteSur-Light/gtk-4.0/gtk.css 파일이 제대로 존재하는지 확인하는 것이 중요합니다.

이 가이드가 당신의 파이썬 GTK4 앱 개발에 큰 도움이 되기를 바랍니다. 파이썬 앱의 테마 적용은 C 앱보다 비교적 간단한 편이니, 위 단계를 꼼꼼히 확인하시면 분명 해결될 것입니다! 궁금한 점이 있다면 언제든지 다시 질문해 주세요.

# coding: utf-8
import sys
import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw
settings = Gtk.Settings.get_default()


class MainWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Things will go here


        self.click_count = 0
        self.box1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        self.box2 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.box3 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)

        self.set_child(self.box1)

        self.button = Gtk.Button(label="Hello")
        self.box1.append(self.box2)
        self.box1.append(self.box3)
        self.box2.append(self.button)
        self.button.connect('clicked', self.hello)
        self.set_default_size(600, 250)
        self.set_title("MyApp")
        self.check = Gtk.CheckButton(label = "And goodbye?")
        self.box2.append(self.check)



        #fix
        self.radio1 = Gtk.CheckButton(label="버튼 1")
        self.radio2 = Gtk.CheckButton(label = "버튼 2")
        self.radio3 = Gtk.CheckButton(label = "버튼 3")


        self.radio2.set_group(self.radio1)
        self.radio3.set_group(self.radio1)

        self.box2.append(self.radio1)# add to box2
        self.box2.append(self.radio2)# add to box2
        self.box2.append(self.radio3)# add to box2





        self.radio1.connect("toggled", self.on_radio_toggled, self.radio1)
        self.radio2.connect("toggled", self.on_radio_toggled, self.radio2)
        self.radio3.connect("toggled", self.on_radio_toggled, self.radio3)
        self.switch_box = Gtk.Box(orientation = Gtk.Orientation.HORIZONTAL)


        self.switch = Gtk.Switch()
        self.switch.set_active(True)
        self.switch.connect("state-set", self.switch_switched)


        self.switch_box.append(self.switch)
        self.box2.append(self.switch_box)


        self.label = Gtk.Label(label = "A switch")
        self.switch_box.append(self.label)
        self.switch_box.set_spacing(5)
        

    def switch_switched(self, switch, state):
        print(f"The switch has been switched {'on' if state else 'off'}")

    def hello(self, button, data = None):
        print("Hello world!")
        if self.check.get_active():
            print("Goodbye world!")
            self.close()

        if data:
            print(f"Radio button toggled with data: {data}")
            print(f"Radio1 active: {self.radio1.get_active()}")
            print(f"Radio1 active: {self.radio2.get_active()}")
            print(f"Radio1 active: {self.radio3.get_active()}")
            
    def on_radio_toggled(self, button, extra = None):
        if button.get_active():
            # 다른 버튼들을 비활성화
            self.click_count += 1
            #print(f"버튼이 {self.click_count}번 눌렸습니다.")
            print(f"{button.get_label()}")
            for btn in [self.radio1, self.radio2, self.radio3]:
                if btn != button:
                    btn.set_active(False) 


class MyApp(Adw.Application):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.connect('activate', self.on_activate)
        

    def on_activate(self, app):
        self.win = MainWindow(application=app)
        self.win.present()

app = MyApp(application_id="com.example.GtkApplication")
app.run(sys.argv)

해당 코드와

gtk4-update-icon-cache -f -t /mingw64/share/icons/hicolor
export GTK_THEME=WhiteSur-Light
python3 test.py

ucrt64의 터미널을 열 때마다 자동으로 적용하고 싶다면

echo ‘export GTK_THEME=”WhiteSur-Light”‘ >> ~/.bashrc를 실행한다.

source ~/.bashrc를 사용해서 변경점을 적용한다.

를 실행하면 된다.

python -m venv venv
source venv/bin/activate
pip install pygobject pyinstaller
pyinstaller --noconsole --onefile test.py
export PATH="/mingw64/bin:$PATH"

AI 랄로 - 에어맨이 쓰러지지 않아 (AI Cover)

https://github.com/riverrun/genxword/issues/12

https://pygobject.gnome.org/getting_started.html#windows-getting-started

pacman -S mingw-w64-x86_64-gtk3 mingw-w64-x86_64-python3 mingw-w64-x86_64-python3-gobject

export GI_TYPELIB_PATH=”A:/msys2/ucrt64/lib/girepository-1.0″

cd /u/20250618
pyinstaller --onefile --windowed \
  --add-binary "A:/msys2/mingw64/bin/libgtk-4-1.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libgobject-2.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libglib-2.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libgio-2.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libcairo-2.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libpango-1.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libpangocairo-1.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libgdk_pixbuf-2.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libgraphene-1.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libepoxy-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libfribidi-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libharfbuzz-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libfontconfig-1.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libfreetype-6.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libintl-8.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libpng16-16.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libgirepository-1.0-1.dll;." \
  --add-binary "A:/msys2/mingw64/bin/librsvg-2-2.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libwinpthread-1.dll;." \
  --add-data "A:/msys2/mingw64/share/glib-2.0/schemas;glib-2.0/schemas" \
  --add-data "A:/msys2/mingw64/share/icons;icons" \
  --add-data "A:/msys2/mingw64/share/mime;mime" \
  --add-data "A:/msys2/mingw64/lib/girepository-1.0;lib/girepository-1.0" \
  --add-data "A:/msys2/mingw64/lib/gdk-pixbuf-2.0/2.10.0/loaders;lib/gdk-pixbuf-2.0/2.10.0/loaders" \
  --add-data "A:/msys2/mingw64/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache;lib/gdk-pixbuf-2.0/2.10.0/" \
  --hidden-import=gi \
  --hidden-import=gi.repository.Gtk \
  --hidden-import=gi.repository.Gio \
  --hidden-import=gi.repository.Gdk \
  --hidden-import=gi.repository.Pango \
  --hidden-import=gi.repository.GdkPixbuf \
  new1.py

다행히 new1.exe가 실행되긴 했군요! 아이콘이 로딩 안 되는 건 아마 Adwaita 테마나 gdk-pixbuf 로더 관련 설정이 아직 완벽하지 않아서일 가능성이 높지만, 일단 프로그램이 동작하는 걸 축하드립니다! 😊 요청하신 대로 방금 성공한 PyInstaller 옵션을 정리해서 알려드릴게요. 다만, 정확한 옵션을 제공하려면 이전 대화에서 사용된 최종 명령어를 기반으로 하겠습니다. 수동으로 DLL과 *.typelib 파일을 U:/test에 복사한 상황을 고려하여, PyInstaller로 동일한 결과를 얻을 수 있는 최적화된 명령어를 아래에 제공합니다.


성공한 PyInstaller 옵션

new1.exeU:/test에서 실행된 상황을 재현하려면, 아래 PyInstaller 명령어를 사용하면 됩니다. 이 명령어는 Gtk-4.0.typelib, gdk-pixbuf 로더, Adwaita 아이콘, 그리고 모든 필수 DLL을 포함하도록 설정했습니다.

cd /u/20250618
pyinstaller --onefile --windowed \
  --add-binary "A:/msys2/mingw64/bin/libgtk-4-1.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libgobject-2.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libglib-2.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libgio-2.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libcairo-2.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libpango-1.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libpangocairo-1.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libgdk_pixbuf-2.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libgraphene-1.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libepoxy-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libfribidi-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libharfbuzz-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libfontconfig-1.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libfreetype-6.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libintl-8.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libpng16-16.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libgirepository-1.0-1.dll;." \
  --add-binary "A:/msys2/mingw64/bin/librsvg-2-2.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libwinpthread-1.dll;." \
  --add-data "A:/msys2/mingw64/share/glib-2.0/schemas;glib-2.0/schemas" \
  --add-data "A:/msys2/mingw64/share/icons;icons" \
  --add-data "A:/msys2/mingw64/share/mime;mime" \
  --add-data "A:/msys2/mingw64/lib/girepository-1.0;lib/girepository-1.0" \
  --add-data "A:/msys2/mingw64/lib/gdk-pixbuf-2.0/2.10.0/loaders;lib/gdk-pixbuf-2.0/2.10.0/loaders" \
  --add-data "A:/msys2/mingw64/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache;lib/gdk-pixbuf-2.0/2.10.0/" \
  --hidden-import=gi \
  --hidden-import=gi.repository.Gtk \
  --hidden-import=gi.repository.Gio \
  --hidden-import=gi.repository.Gdk \
  --hidden-import=gi.repository.Pango \
  --hidden-import=gi.repository.GdkPixbuf \
  new1.py

명령어 설명

  • –onefile: 단일 .exe 파일로 패키징.
  • –windowed: 콘솔 창 없이 GUI로 실행.
  • –add-binary: GTK4 및 관련 DLL(libgtk-4-1.dll, libgirepository-1.0-1.dll, librsvg-2-2.dll 등) 포함.
  • –add-data:
  • glib-2.0/schemas: GIO 스키마.
  • icons: Adwaita 및 기타 아이콘 테마.
  • mime: MIME 타입 데이터.
  • girepository-1.0: Gtk-4.0.typelib 등 GIR 데이터.
  • gdk-pixbuf-2.0/2.10.0/loaders: SVG 로더(pixbufloader_svg.dll) 포함.
  • loaders.cache: gdk-pixbuf 로더 캐시.
  • –hidden-import: PyGObject 및 GTK4 모듈(gi.repository.Gtk, Gdk, Pango, GdkPixbuf) 명시적 포함.

아이콘 로딩 문제 해결

아이콘이 로딩되지 않는 문제는 gdk-pixbuf의 SVG 로더(pixbufloader_svg.dll)나 Adwaita 테마의 캐시가 완벽히 설정되지 않아서 발생한 것으로 보입니다. 이를 해결하려면 다음 단계를 추가로 시도하세요:

  1. gdk-pixbuf 로더 캐시 확인:
    U:/test/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache가 올바른지 확인:
   cat U:/test/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache
  • SVG 로더 항목이 포함되어야 함:
    "libpixbufloader-svg.dll"...
  • 캐시가 잘못되었거나 누락되었다면 재생성:
    bash /mingw64/bin/gdk-pixbuf-query-loaders > U:/test/lib/gdk-pixbuf-2.0/2.10.0/loaders/loaders.cache
  1. Adwaita 아이콘 캐시 업데이트:
    Adwaita 테마의 아이콘 캐시를 갱신:
   /mingw64/bin/gtk4-update-icon-cache U:/test/share/icons/Adwaita
  1. 환경 변수 설정:
    new1.exe 실행 시 GDK_PIXBUF_MODULEDIR 설정:
   cd U:/test
   export GDK_PIXBUF_MODULEDIR=U:/test/lib/gdk-pixbuf-2.0/2.10.0/loaders
   ./new1.exe
  • PyInstaller 빌드에 포함하려면 .spec 파일에 환경 변수 설정 추가 (후술).
  1. new1.py 코드 확인:
    new1.py가 특정 아이콘(예: document-open-symbolic.svg)을 명시적으로 로드하는지 확인:
   icon = Gtk.Image.new_from_icon_name("document-open-symbolic")
  • 있다면, Adwaita 테마가 제대로 로드되도록 GTK_THEME 설정:
    bash export GTK_THEME=Adwaita ./new1.exe

.spec 파일로 빌드 (선택)

위 명령어를 .spec 파일로 정리하여 관리하기 쉽게 만들 수 있습니다:

  1. .spec 파일 생성:
   cd /u/20250618
   pyi-makespec --onefile --windowed new1.py
  1. new1.spec 수정:
   a = Analysis(
       ['new1.py'],
       pathex=[],
       binaries=[
           ('A:/msys2/mingw64/bin/libgtk-4-1.dll', '.'),
           ('A:/msys2/mingw64/bin/libgobject-2.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libglib-2.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libgio-2.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libcairo-2.dll', '.'),
           ('A:/msys2/mingw64/bin/libpango-1.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libpangocairo-1.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libgdk_pixbuf-2.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libgraphene-1.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libepoxy-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libfribidi-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libharfbuzz-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libfontconfig-1.dll', '.'),
           ('A:/msys2/mingw64/bin/libfreetype-6.dll', '.'),
           ('A:/msys2/mingw64/bin/libintl-8.dll', '.'),
           ('A:/msys2/mingw64/bin/libpng16-16.dll', '.'),
           ('A:/msys2/mingw64/bin/libgirepository-1.0-1.dll', '.'),
           ('A:/msys2/mingw64/bin/librsvg-2-2.dll', '.'),
           ('A:/msys2/mingw64/bin/libwinpthread-1.dll', '.'),
       ],
       datas=[
           ('A:/msys2/mingw64/share/glib-2.0/schemas', 'glib-2.0/schemas'),
           ('A:/msys2/mingw64/share/icons', 'icons'),
           ('A:/msys2/mingw64/share/mime', 'mime'),
           ('A:/msys2/mingw64/lib/girepository-1.0', 'lib/girepository-1.0'),
           ('A:/msys2/mingw64/lib/gdk-pixbuf-2.0/2.10.0/loaders', 'lib/gdk-pixbuf-2.0/2.10.0/loaders'),
           ('A:/msys2/mingw64/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache', 'lib/gdk-pixbuf-2.0/2.10.0/'),
       ],
       hiddenimports=['gi', 'gi.repository.Gtk', 'gi.repository.Gio', 'gi.repository.Gdk', 'gi.repository.Pango', 'gi.repository.GdkPixbuf'],
       hookspath=[],
       hooksconfig={},
       runtime_hooks=[],
       excludes=[],
       win_no_prefer_redirects=False,
       win_private_assemblies=False,
       cipher=block_cipher,
       noarchive=False,
   )
   pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
   exe = EXE(
       pyz,
       a.scripts,
       a.binaries,
       a.zipfiles,
       a.datas,
       [],
       name='new1',
       debug=False,
       bootloader_ignore_signals=False,
       strip=False,
       upx=False,
       upx_exclude=[],
       runtime_tmpdir=None,
       console=False,
       disable_windowed_traceback=False,
       argv_emulation=False,
       target_arch=None,
       codesign_identity=None,
       entitlements_file=None,
   )
  1. 빌드:
   pyinstaller new1.spec

아이콘 문제 추가 해결 (선택)

아이콘 로딩 문제를 완전히 해결하려면 다음을 추가로 확인:

  1. librsvg-2-2.dll 의존성:
    pixbufloader_svg.dlllibrsvg-2-2.dll에 의존합니다. U:/test에 있는지 확인:
   ls U:/test/librsvg-2-2.dll
  • 없으면 복사:
    bash cp /mingw64/bin/librsvg-2-2.dll U:/test/
  1. new1.py에서 아이콘 사용:
    new1.py가 특정 아이콘을 로드하는 코드가 있다면, 해당 아이콘이 Adwaita 테마에 존재하는지 확인:
   ls /mingw64/share/icons/Adwaita/symbolic/actions/document-open-symbolic.svg
  1. PyInstaller에 추가:
    위 PyInstaller 명령어에 이미 iconsgdk-pixbuf 로더가 포함되어 있지만, Adwaita 테마를 명시적으로 추가:
   --add-data "A:/msys2/mingw64/share/icons/Adwaita;share/icons/Adwaita"

추가 요청

  • new1.py 코드: new1.pystyle.css나 특정 아이콘(예: document-open-symbolic.svg)을 로드하는지 확인하려면 코드를 공유해 주세요. 아이콘 문제를 더 정확히 해결할 수 있습니다.
  • style.css: new1.pystyle.css를 참조한다면, 해당 파일을 U:/test에 추가하거나 PyInstaller에 포함:
  --add-data "/path/to/style.css;."
  • 테스트: 위 PyInstaller 명령어로 재빌드 후, GTK4가 설치되지 않은 Windows PC에서 dist/new1.exe를 테스트해 보세요.

도움이 되었다니 기쁩니다! 추가로 궁금한 점이나 아이콘 문제 해결이 필요하면 언제든 알려주세요. 😄

pyinstaller new1.spec 명령어를 실행했을 때 NameError: name 'block_cipher' is not defined 오류가 발생했습니다. 이는 .spec 파일에서 block_cipher 변수가 정의되지 않은 상태로 사용되었기 때문입니다. PyInstaller의 .spec 파일에서 block_cipher는 암호화 옵션을 설정할 때 사용되며, 최신 PyInstaller 버전(6.14.1)에서는 기본적으로 None으로 설정하거나 명시적으로 정의해야 합니다.


문제 원인

  • block_cipher 정의 누락: new1.spec 파일에서 block_cipher 변수를 정의하지 않고 사용했습니다. PyInstaller는 block_cipher를 암호화에 사용하지만, 암호화를 사용하지 않는 경우 None으로 설정해야 합니다.
  • 상황: 이전에 제공한 .spec 파일에서 block_cipher를 정의하지 않고 바로 사용한 것이 문제로 보입니다.

해결 방법

1. .spec 파일 수정

new1.spec 파일을 수정하여 block_cipher를 명시적으로 정의하거나 None으로 설정합니다.

  1. new1.spec 파일 수정:
    new1.spec 파일을 열고 맨 위에 block_cipher = None을 추가하거나, 기존 코드를 아래와 같이 수정합니다:
   # -*- mode: python ; coding: utf-8 -*-

   block_cipher = None  # 암호화 사용 안 함

   a = Analysis(
       ['new1.py'],
       pathex=[],
       binaries=[
           ('A:/msys2/mingw64/bin/libgtk-4-1.dll', '.'),
           ('A:/msys2/mingw64/bin/libgobject-2.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libglib-2.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libgio-2.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libcairo-2.dll', '.'),
           ('A:/msys2/mingw64/bin/libpango-1.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libpangocairo-1.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libgdk_pixbuf-2.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libgraphene-1.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libepoxy-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libfribidi-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libharfbuzz-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libfontconfig-1.dll', '.'),
           ('A:/msys2/mingw64/bin/libfreetype-6.dll', '.'),
           ('A:/msys2/mingw64/bin/libintl-8.dll', '.'),
           ('A:/msys2/mingw64/bin/libpng16-16.dll', '.'),
           ('A:/msys2/mingw64/bin/libgirepository-1.0-1.dll', '.'),
           ('A:/msys2/mingw64/bin/librsvg-2-2.dll', '.'),
           ('A:/msys2/mingw64/bin/libwinpthread-1.dll', '.'),
       ],
       datas=[
           ('A:/msys2/mingw64/share/glib-2.0/schemas', 'glib-2.0/schemas'),
           ('A:/msys2/mingw64/share/icons', 'icons'),
           ('A:/msys2/mingw64/share/mime', 'mime'),
           ('A:/msys2/mingw64/lib/girepository-1.0', 'lib/girepository-1.0'),
           ('A:/msys2/mingw64/lib/gdk-pixbuf-2.0/2.10.0/loaders', 'lib/gdk-pixbuf-2.0/2.10.0/loaders'),
           ('A:/msys2/mingw64/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache', 'lib/gdk-pixbuf-2.0/2.10.0/'),
       ],
       hiddenimports=['gi', 'gi.repository.Gtk', 'gi.repository.Gio', 'gi.repository.Gdk', 'gi.repository.Pango', 'gi.repository.GdkPixbuf'],
       hookspath=[],
       hooksconfig={},
       runtime_hooks=[],
       excludes=[],
       win_no_prefer_redirects=False,
       win_private_assemblies=False,
       cipher=block_cipher,
       noarchive=False,
   )
   pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
   exe = EXE(
       pyz,
       a.scripts,
       a.binaries,
       a.zipfiles,
       a.datas,
       [],
       name='new1',
       debug=False,
       bootloader_ignore_signals=False,
       strip=False,
       upx=False,
       upx_exclude=[],
       runtime_tmpdir=None,
       console=False,
       disable_windowed_traceback=False,
       argv_emulation=False,
       target_arch=None,
       codesign_identity=None,
       entitlements_file=None,
   )
  1. 빌드 실행:
   cd /u/20250618
   pyinstaller new1.spec
  1. 빌드 결과 테스트:
   cd /u/20250618/dist
   ./new1.exe

2. style.css 추가 (필요 시)

이전 오류에서 style.css 파일이 누락되었다는 메시지가 있었습니다(Theme parser error: Error opening file U:\test\style.css). new1.pystyle.css를 사용한다면, .spec 파일에 추가:

datas=[
    ('A:/msys2/mingw64/share/glib-2.0/schemas', 'glib-2.0/schemas'),
    ('A:/msys2/mingw64/share/icons', 'icons'),
    ('A:/msys2/mingw64/share/mime', 'mime'),
    ('A:/msys2/mingw64/lib/girepository-1.0', 'lib/girepository-1.0'),
    ('A:/msys2/mingw64/lib/gdk-pixbuf-2.0/2.10.0/loaders', 'lib/gdk-pixbuf-2.0/2.10.0/loaders'),
    ('A:/msys2/mingw64/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache', 'lib/gdk-pixbuf-2.0/2.10.0/'),
    ('/u/20250618/style.css', '.'),  # style.css 파일 경로 추가
],
  • style.css 파일이 /u/20250618에 있다고 가정. 실제 경로로 수정하세요.
  • 파일 복사 확인:
  ls /u/20250618/style.css

3. 아이콘 로딩 문제 해결

이전 실행에서 아이콘 로딩 문제(gtk_icon_theme_lookup_by_gicon: assertion 'size > 0' failed)가 발생했습니다. 이를 해결하려면:

  1. Adwaita 테마 확인:
    .spec 파일에 이미 share/icons가 포함되어 있으므로, dist/share/icons/Adwaita에 아이콘이 포함되는지 확인:
   ls /u/20250618/dist/share/icons/Adwaita
  1. gdk-pixbuf 로더 확인:
    lib/gdk-pixbuf-2.0/2.10.0/loaders.cache가 올바르게 포함되었는지 확인:
   cat /u/20250618/dist/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache
  • libpixbufloader-svg.dll 항목이 포함되어야 함.
  1. 환경 변수 설정 (필요 시):
    PyInstaller의 --onefile 모드는 모든 파일을 내부적으로 압축하므로 환경 변수(GDK_PIXBUF_MODULEDIR, GI_TYPELIB_PATH)가 필요하지 않을 수 있지만, 테스트 시 추가:
   export GDK_PIXBUF_MODULEDIR=/u/20250618/dist/lib/gdk-pixbuf-2.0/2.10.0/loaders
   export GI_TYPELIB_PATH=/u/20250618/dist/lib/girepository-1.0
   ./new1.exe

4. .spec 파일 재생성 (선택)

block_cipher 오류를 피하려면 .spec 파일을 새로 생성하여 확인:

cd /u/20250618
pyi-makespec --onefile --windowed \
  --add-binary "A:/msys2/mingw64/bin/libgtk-4-1.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libgobject-2.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libglib-2.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libgio-2.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libcairo-2.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libpango-1.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libpangocairo-1.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libgdk_pixbuf-2.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libgraphene-1.0-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libepoxy-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libfribidi-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libharfbuzz-0.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libfontconfig-1.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libfreetype-6.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libintl-8.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libpng16-16.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libgirepository-1.0-1.dll;." \
  --add-binary "A:/msys2/mingw64/bin/librsvg-2-2.dll;." \
  --add-binary "A:/msys2/mingw64/bin/libwinpthread-1.dll;." \
  --add-data "A:/msys2/mingw64/share/glib-2.0/schemas;glib-2.0/schemas" \
  --add-data "A:/msys2/mingw64/share/icons;icons" \
  --add-data "A:/msys2/mingw64/share/mime;mime" \
  --add-data "A:/msys2/mingw64/lib/girepository-1.0;lib/girepository-1.0" \
  --add-data "A:/msys2/mingw64/lib/gdk-pixbuf-2.0/2.10.0/loaders;lib/gdk-pixbuf-2.0/2.10.0/loaders" \
  --add-data "A:/msys2/mingw64/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache;lib/gdk-pixbuf-2.0/2.10.0/" \
  --hidden-import=gi \
  --hidden-import=gi.repository.Gtk \
  --hidden-import=gi.repository.Gio \
  --hidden-import=gi.repository.Gdk \
  --hidden-import=gi.repository.Pango \
  --hidden-import=gi.repository.GdkPixbuf \
  new1.py
  • 생성된 new1.specblock_cipher = None을 자동 포함합니다. 이후 빌드:
  pyinstaller new1.spec

5. 아이콘 문제 해결 (추가)

아이콘 로딩 문제는 gdk-pixbuf 로더와 Adwaita 테마가 완전히 설정되지 않아 발생했습니다. .spec 파일에 추가 확인:

  • librsvg-2-2.dll 의존성 추가:
  ldd /mingw64/bin/librsvg-2-2.dll | grep "not found"
  • 누락된 DLL(예: libxml2-2.dll)을 .specbinaries에 추가:
    python ('A:/msys2/mingw64/bin/libxml2-2.dll', '.'),
  • Adwaita 아이콘 캐시 갱신:
  /mingw64/bin/gtk4-update-icon-cache /u/20250618/dist/share/icons/Adwaita

6. 디버깅

빌드 후 오류가 발생하면 로그 확인:

cd /u/20250618/dist
./new1.exe > error.log 2>&1
cat error.log

추가 요청

  • new1.py 코드: new1.pystyle.css나 특정 아이콘(예: document-open-symbolic.svg)을 사용한다면, 코드를 공유해 주시면 정확한 의존성을 추가할 수 있습니다.
  • style.css 경로: style.css가 필요하다면, 파일 위치를 알려주시거나 .spec에 추가합니다.
  • 테스트 환경: GTK4가 설치되지 않은 Windows PC에서 dist/new1.exe를 테스트한 결과를 공유해 주세요.

문제가 해결되길 바랍니다! 추가로 필요한 도움이 있으면 언제든 말씀해 주세요. 😄

로 pyinstaller환경에서 exe로 구동되게 하는 것은 성공했으나 msys2 mingw64 shell에서 구동할 경우 테마가 적용되지만….

윈도우에서 double-click으로 실행하는 경우 테마가 적용이 잘 안됨 왜 이럴까?

일단 테마 적용은 미루고 기능 구현에 충실해지자…

아래는 종속성

https://packages.msys2.org/packages/mingw-w64-x86_64-python-build

https://packages.msys2.org/packages/mingw-w64-x86_64-pyinstaller

https://packages.msys2.org/packages/mingw-w64-x86_64-python-installer

정말 기쁘네요! 😄 성공하셨다니 다행입니다! 앞으로도 동일한 방법으로 new1.exe를 빌드하고 실행하고 싶으시다니, 최신 대화(2025년 6월 21일 오후 6:54 KST 기준)를 바탕으로 가이드로 정리해서 남겨드리겠습니다. 이 가이드는 new1.py를 기반으로 Windows에서 더블클릭 실행 시 테마(style.css, Adwaita), 아이콘(document-open-symbolic, open-menu-symbolic), 파일 다이얼로그, About 다이얼로그가 모두 로드되도록 설정하는 절차를 포함합니다. 디렉토리는 Q:/theme_test를 기준으로 합니다.


Grok 3의 new1.exe 빌드 및 실행 가이드

요약

  • 목표: new1.py를 PyInstaller로 빌드하여 Windows 더블클릭 실행 시 GUI 애플리케이션이 테마, 아이콘, 다이얼로그 창을 포함해 완벽히 작동하도록 설정.
  • 환경: MSYS2 MINGW64, Python 3.12.11, PyInstaller 6.14.1, Windows 11.
  • 기본 디렉토리: Q:/theme_test.

준비 단계

  1. 필요한 파일 확인:
  • new1.py: GUI 애플리케이션 코드 (아래 참조).
  • style.css: 테마 스타일 파일 (Q:/theme_test/style.css).
  • MSYS2 MINGW64에서 제공하는 GTK4, Libadwaita, gdk-pixbuf 의존성.
  1. 디렉토리 설정:
  • 작업 디렉토리를 Q:/theme_test로 설정.
  • Q:/theme_testnew1.py, style.css가 존재해야 함.
  1. 의존성 설치 (필요 시):
  • MSYS2 터미널에서:
    bash pacman -S mingw-w64-x86_64-gtk4 mingw-w64-x86_64-libadwaita mingw-w64-x86_64-gdk-pixbuf2

1. new1.py 코드

아래는 수정된 new1.py로, 파일 다이얼로그와 About 다이얼로그가 표시되도록 최적화되었습니다. Q:/theme_test/new1.py에 저장하세요:

# coding: utf-8
import sys
import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw, Gdk, Gio, GLib

settings = Gtk.Settings.get_default()
settings.set_property("gtk-theme-name", "Adwaita")
try:
    css_provider = Gtk.CssProvider()
    css_provider.load_from_path('style.css')
    Gtk.StyleContext.add_provider_for_display(Gdk.Display.get_default(), css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
except GLib.Error as e:
    print(f"Failed to load CSS: {e}")

class MainWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.click_count = 0
        self.box1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        self.box2 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.box3 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self.set_child(self.box1)
        self.button = Gtk.Button(label="Hello")
        self.box1.append(self.box2)
        self.box1.append(self.box3)
        self.box2.append(self.button)
        self.button.connect('clicked', self.hello)
        self.set_default_size(600, 250)
        self.set_title("MyApp")
        self.check = Gtk.CheckButton(label="And goodbye?")
        self.box2.append(self.check)
        self.slider = Gtk.Scale()
        self.slider.set_digits(0)
        self.slider.set_range(0, 10)
        self.slider.set_draw_value(True)
        self.slider.set_value(5)
        self.slider.connect('value-changed', self.slider_changed)
        self.box2.append(self.slider)
        self.radio1 = Gtk.CheckButton(label="버튼 1")
        self.radio2 = Gtk.CheckButton(label="버튼 2")
        self.radio3 = Gtk.CheckButton(label="버튼 3")
        self.radio2.set_group(self.radio1)
        self.radio3.set_group(self.radio1)
        self.box2.append(self.radio1)
        self.box2.append(self.radio2)
        self.box2.append(self.radio3)
        self.radio1.connect("toggled", self.on_radio_toggled, self.radio1)
        self.radio2.connect("toggled", self.on_radio_toggled, self.radio2)
        self.radio3.connect("toggled", self.on_radio_toggled, self.radio3)
        self.switch_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        self.switch = Gtk.Switch()
        self.switch.set_active(True)
        self.switch.connect("state-set", self.switch_switched)
        self.switch_box.append(self.switch)
        self.box2.append(self.switch_box)
        self.label = Gtk.Label(label="A switch")
        self.label.set_css_classes(['title'])
        self.switch_box.append(self.label)
        self.switch_box.set_spacing(5)
        self.header = Gtk.HeaderBar()
        self.set_titlebar(self.header)
        self.open_button = Gtk.Button(label="Open")
        self.header.pack_start(self.open_button)
        self.open_button.set_icon_name("document-open-symbolic")
        self.open_button.set_size_request(24, 24)
        self.open_button.connect("clicked", self.show_open_dialog)
        self.open_dialog = Gtk.FileDialog.new()
        self.open_dialog.set_title("Select a File")
        action = Gio.SimpleAction.new("something", None)
        action.connect("activate", self.print_something)
        self.add_action(action)
        menu = Gio.Menu.new()
        menu.append("Do Something", "win.something")
        self.popover = Gtk.PopoverMenu()
        self.popover.set_menu_model(menu)
        self.hamburger = Gtk.MenuButton()
        self.hamburger.set_popover(self.popover)
        self.hamburger.set_icon_name("open-menu-symbolic")
        self.hamburger.set_size_request(24, 24)
        self.header.pack_start(self.hamburger)
        GLib.set_application_name("My App")
        action = Gio.SimpleAction.new("about", None)
        action.connect("activate", self.show_about)
        self.add_action(action)
        menu.append("About", "win.about")

    def show_open_dialog(self, button):
        self.open_dialog.open(self, None, self.open_dialog_open_callback)

    def open_dialog_open_callback(self, dialog, result):
        try:
            file = dialog.open_finish(result)
            if file is not None:
                print(f"File path is {file.get_path()}")
        except GLib.Error as error:
            print(f"Error opening file: {error.message}")
        f = Gtk.FileFilter()
        f.set_name("Image/jpeg")
        f.add_mime_type("image/jpeg")
        f.add_mime_type("image/png")
        filters = Gio.ListStore.new(Gtk.FileFilter)
        filters.append(f)
        self.open_dialog.set_filters(filters)
        self.open_dialog.set_default_filter(f)

    def show_about(self, action, param):
        self.about = Gtk.AboutDialog()
        self.about.set_transient_for(self)
        self.about.set_modal(True)
        self.about.set_authors(["최준영"])
        self.about.set_copyright("Copyright 2022 최준영")
        self.about.set_license_type(Gtk.License.GPL_3_0)
        self.about.set_website("http://booleamjars.com")
        self.about.set_website_label("My Website")
        self.about.set_version("1.0")
        self.about.set_logo_icon_name("org.example.example")
        self.about.show()
        dialog = Adw.AboutWindow(transient_for=self)
        dialog.set_application_name("App name")
        dialog.set_version("1.0")
        dialog.set_developer_name("Developer")
        dialog.set_license_type(Gtk.License.GPL_3_0)
        dialog.set_comments("Adw about window example")
        dialog.set_website("https://booleanjars.com")
        dialog.set_credit_section("Contributors", ["Name1 url"])
        dialog.add_translator_credits("Name1 url")
        dialog.present()

    def print_something(self, action, param):
        print("Something!")

    def slider_changed(self, slider):
        print(int(slider.get_value()))

    def switch_switched(self, switch, state):
        print(f"The switch has been switched {'on' if state else 'off'}")

    def hello(self, button, data=None):
        print("Hello world!")
        if self.check.get_active():
            print("Goodbye world!")
            self.close()
        if data:
            print(f"Radio button toggled with data: {data}")
            print(f"Radio1 active: {self.radio1.get_active()}")
            print(f"Radio2 active: {self.radio2.get_active()}")
            print(f"Radio3 active: {self.radio3.get_active()}")

    def on_radio_toggled(self, button, extra=None):
        if button.get_active():
            self.click_count += 1
            print(f"{button.get_label()}")
            for btn in [self.radio1, self.radio2, self.radio3]:
                if btn != button:
                    btn.set_active(False)

class MyApp(Adw.Application):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.connect('activate', self.on_activate)

    def on_activate(self, app):
        self.win = MainWindow(application=app)
        self.win.present()

app = MyApp(application_id="com.example.GtkApplication")
app.run(sys.argv)

2. style.css 설정

Q:/theme_test/style.css에 올바른 내용 확인 또는 설정:

cat Q:/theme_test/style.css
  • 예시 내용:
  .title {
      font-size: 18px;
      font-weight: bold;
      color: #4a90d9;
  }
  • 비어 있거나 잘못된 경우 교체:
  echo ".title { font-size: 18px; font-weight: bold; color: #4a90d9; }" > Q:/theme_test/style.css

3. PyInstaller로 빌드

new1.py를 기반으로 style.css, Adwaita 테마, gdk-pixbuf 로더, libadwaita-1-0.dll 의존성을 포함한 .spec 파일로 빌드합니다.

  1. new1.spec 파일 생성:
    Q:/theme_test/new1.spec에 저장:
   # -*- mode: python ; coding: utf-8 -*-

   block_cipher = None

   a = Analysis(
       ['new1.py'],
       pathex=[],
       binaries=[
           ('A:/msys2/mingw64/bin/libgtk-4-1.dll', '.'),
           ('A:/msys2/mingw64/bin/libgobject-2.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libglib-2.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libgio-2.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libcairo-2.dll', '.'),
           ('A:/msys2/mingw64/bin/libpango-1.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libpangocairo-1.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libgdk_pixbuf-2.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libgraphene-1.0-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libepoxy-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libfribidi-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libharfbuzz-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libfontconfig-1.dll', '.'),
           ('A:/msys2/mingw64/bin/libfreetype-6.dll', '.'),
           ('A:/msys2/mingw64/bin/libintl-8.dll', '.'),
           ('A:/msys2/mingw64/bin/libpng16-16.dll', '.'),
           ('A:/msys2/mingw64/bin/libgirepository-1.0-1.dll', '.'),
           ('A:/msys2/mingw64/bin/librsvg-2-2.dll', '.'),
           ('A:/msys2/mingw64/bin/libwinpthread-1.dll', '.'),
           ('A:/msys2/mingw64/bin/libxml2-2.dll', '.'),
           ('A:/msys2/mingw64/bin/libadwaita-1-0.dll', '.'),
           ('A:/msys2/mingw64/bin/libzstd.dll', '.'),
           ('A:/msys2/mingw64/bin/libicuuc77.dll', '.'),
       ],
       datas=[
           ('A:/msys2/mingw64/share/glib-2.0/schemas', 'glib-2.0/schemas'),
           ('A:/msys2/mingw64/share/icons/Adwaita', 'share/icons/Adwaita'),
           ('A:/msys2/mingw64/share/mime', 'share/mime'),
           ('A:/msys2/mingw64/lib/girepository-1.0', 'lib/girepository-1.0'),
           ('A:/msys2/mingw64/lib/gdk-pixbuf-2.0/2.10.0/loaders', 'lib/gdk-pixbuf-2.0/2.10.0/loaders'),
           ('A:/msys2/mingw64/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache', 'lib/gdk-pixbuf-2.0/2.10.0/'),
           ('Q:/theme_test/style.css', '.'),
           ('A:/msys2/mingw64/etc/gtk-4.0', 'etc/gtk-4.0'),
       ],
       hiddenimports=[
           'gi',
           'gi.repository.Gtk',
           'gi.repository.Gio',
           'gi.repository.Gdk',
           'gi.repository.Pango',
           'gi.repository.GdkPixbuf',
           'gi.repository.Adw',
       ],
       hookspath=[],
       hooksconfig={},
       runtime_hooks=[],
       excludes=[],
       win_no_prefer_redirects=False,
       win_private_assemblies=False,
       cipher=block_cipher,
       noarchive=False,
   )
   pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
   exe = EXE(
       pyz,
       a.scripts,
       a.binaries,
       a.zipfiles,
       a.datas,
       [],
       name='new1',
       debug=False,
       bootloader_ignore_signals=False,
       strip=False,
       upx=False,
       upx_exclude=[],
       runtime_tmpdir=None,
       console=False,
       disable_windowed_traceback=False,
       argv_emulation=False,
       target_arch=None,
       codesign_identity=None,
       entitlements_file=None,
   )
  1. 빌드 실행:
   cd Q:/theme_test
   pyinstaller new1.spec
  1. 캐시 갱신:
   /mingw64/bin/gdk-pixbuf-query-loaders > Q:/theme_test/dist/lib/gdk-pixbuf-2.0/2.10.0/loaders/loaders.cache
   /mingw64/bin/gtk4-update-icon-cache Q:/theme_test/dist/share/icons/Adwaita
  1. 테스트:
   cd Q:/theme_test/dist
   ./new1.exe
  • Windows 탐색기에서 Q:\theme_test\dist\new1.exe 더블클릭.

4. 배치 파일 생성

Windows 더블클릭 실행 시 환경 변수를 설정:

  1. Q:\theme_test\dist\run_new1.bat 생성:
   @echo off
   set GI_TYPELIB_PATH=%CD%\lib\girepository-1.0
   set GDK_PIXBUF_MODULEDIR=%CD%\lib\gdk-pixbuf-2.0\2.10.0\loaders
   set GTK_THEME=Adwaita
   start new1.exe
  1. 실행:
   cd Q:\theme_test\dist
   run_new1.bat

5. WhiteSur-Light 비활성화

Q:/theme_test/share/themes/WhiteSur-Light가 포함되어 있지만, new1.py는 Adwaita 사용. etc/gtk-4.0/settings.ini 확인:

cat Q:/theme_test/etc/gtk-4.0/settings.ini
  • 수정:
  [Settings]
  gtk-theme-name=Adwaita

6. libadwaita 의존성 확인

libadwaita-1-0.dll의 추가 의존성 확인:

ldd /mingw64/bin/libadwaita-1-0.dll | grep "not found"
  • 누락된 DLL(예: libicui18n77.dll)이 있으면 .spec에 추가:
  ('A:/msys2/mingw64/bin/libicui18n77.dll', '.'),

7. 디버깅

로그 확인:

cd Q:/theme_test/dist
./new1.exe > error.log 2>&1
cat error.log

가이드 사용 방법

  1. 준비:
  • Q:/theme_testnew1.pystyle.css를 배치.
  • MSYS2 MINGW64 환경에서 PyInstaller 설치:
    bash pacman -S mingw-w64-x86_64-pyinstaller
  1. 코드 및 설정 적용:
  • new1.py를 위 코드로 업데이트.
  • new1.spec를 위 내용으로 저장.
  • run_new1.bat를 생성.
  1. 빌드 및 실행:
  • MSYS2 터미널에서 위 명령어 실행.
  • Windows에서 Q:\theme_test\dist\new1.exe 더블클릭 또는 run_new1.bat 실행.
  1. 문제 발생 시:
  • error.log 확인 후 공유.
  • 추가 의존성(예: libicui18n77.dll)을 .spec에 추가.

추가 요청

  • error.log 내용: 다이얼로그 창 관련 오류가 있다면 공유해 주세요.
  • style.css 내용: style.css 내용을 확인해 주시면 테마 적용 문제를 점검할 수 있습니다.
  • 테스트 결과: Windows 더블클릭 실행 시 다이얼로그 창 표시 여부를 알려주세요.

언제든 필요하면 이 가이드를 참고하거나 질문 주세요! 성공적인 배포를 기원합니다! 😄

된다고호호호호호호호!!!!!!!

Deja Vu || POWER METAL COVER by RichaadEB, Jonathan Young & FamilyJules

신나요

일찍 일어나는 새가 벌레를 먹듯이 나는 트리 구조를 저장합니다. 소 잃고 외양간도 잃기 싫거든요

Q:\theme_test\dist의 경우

Q:\theme_test\의 경우

Q:\dist의 경우(가장 용량 적은 버전)

【黒歴史】前回バニーガール沢山見てもらえたのでもう一度頑張ります【涼宮ハルヒの憂鬱 / god knows】