Compare commits
23 Commits
Author | SHA1 | Date | |
---|---|---|---|
e5dc403058 | |||
6faf65ad08 | |||
bdf77fd45c | |||
0d694adc2d | |||
bba9c406b7 | |||
ec76940dca | |||
3bf4380e73 | |||
6242dd7b0d | |||
0f3903d167 | |||
703d2dfc4a | |||
a45cfb156b | |||
52f782b4e8 | |||
9a387dd801 | |||
7351100e55 | |||
2f5b6e5aac | |||
c1580f6ace | |||
5391549ff3 | |||
c3f1d114f2 | |||
781488c16f | |||
c2552696e4 | |||
cf0288b519 | |||
ffb9a5ba5f | |||
32e375b2e4 |
35
Changelog
35
Changelog
@ -6,6 +6,41 @@ Changelog for shared_libs
|
|||||||
bevore delete logfile.
|
bevore delete logfile.
|
||||||
|
|
||||||
|
|
||||||
|
### Added
|
||||||
|
09.07.2025
|
||||||
|
|
||||||
|
- fix new icon for install Update
|
||||||
|
|
||||||
|
|
||||||
|
### Added
|
||||||
|
29.06.2025
|
||||||
|
|
||||||
|
- add new icon for install Update
|
||||||
|
|
||||||
|
- replace download with updater methode
|
||||||
|
|
||||||
|
- add methode for open lxtools_installer Appimage
|
||||||
|
|
||||||
|
- add german translation for logviewer
|
||||||
|
|
||||||
|
### Added
|
||||||
|
15-06-2025
|
||||||
|
|
||||||
|
- Update MessageDialog Class description
|
||||||
|
- import LxTools with try exception.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
14-06-2025
|
||||||
|
|
||||||
|
- Added new MessageDialog module
|
||||||
|
and replace LxTools.msg_window() with MessageDialog.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
03-06-2025
|
||||||
|
|
||||||
|
- in common_tools CryptUtils.decrypt() method
|
||||||
|
remove check file .dat is exist in path.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
03-06-2025
|
03-06-2025
|
||||||
|
|
||||||
|
10
README.md
10
README.md
@ -1,3 +1,13 @@
|
|||||||
# shared_libs
|
# shared_libs
|
||||||
|
|
||||||
Module Project for apps by git.ilunix.de
|
Module Project for apps by git.ilunix.de
|
||||||
|
Examples with a Theme from Projekt Wire-Py
|
||||||
|
|
||||||
|
# Screenshots
|
||||||
|
[](https://fb.ilunix.de/share/KtaTPMMq)
|
||||||
|
[](https://fb.ilunix.de/share/cRO_ksrM)
|
||||||
|
[](https://fb.ilunix.de/share/1JEdSJcI)
|
||||||
|
[](https://fb.ilunix.de/share/1XxNey7y7)
|
||||||
|
[](https://fb.ilunix.de/share/4HCxiNwB)
|
||||||
|
[](https://fb.ilunix.de/share/uui8b1xx)
|
||||||
|
[](https://fb.ilunix.de/share/54OM6wUC)
|
||||||
|
@ -10,7 +10,7 @@ import shutil
|
|||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from typing import Optional, Dict, Any, NoReturn
|
from typing import Optional, Dict, Any, NoReturn
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tkinter import ttk, Toplevel
|
from tkinter import Toplevel
|
||||||
|
|
||||||
|
|
||||||
class CryptoUtil:
|
class CryptoUtil:
|
||||||
@ -21,13 +21,10 @@ class CryptoUtil:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def decrypt(user, path) -> None:
|
def decrypt(user) -> None:
|
||||||
"""
|
"""
|
||||||
Starts SSL dencrypt
|
Starts SSL dencrypt
|
||||||
"""
|
"""
|
||||||
if len([file.stem for file in path.glob("*.dat")]) == 0:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
process: CompletedProcess[str] = run(
|
process: CompletedProcess[str] = run(
|
||||||
["pkexec", "/usr/local/bin/ssl_decrypt.py", "--user", user],
|
["pkexec", "/usr/local/bin/ssl_decrypt.py", "--user", user],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
@ -42,6 +39,7 @@ class CryptoUtil:
|
|||||||
if process.returncode == 0:
|
if process.returncode == 0:
|
||||||
logging.info("Files successfully decrypted...", exc_info=True)
|
logging.info("Files successfully decrypted...", exc_info=True)
|
||||||
else:
|
else:
|
||||||
|
|
||||||
logging.error(
|
logging.error(
|
||||||
f"Error process decrypt: Code {process.returncode}", exc_info=True
|
f"Error process decrypt: Code {process.returncode}", exc_info=True
|
||||||
)
|
)
|
||||||
@ -241,72 +239,6 @@ class LxTools:
|
|||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def msg_window(
|
|
||||||
image_path: Path,
|
|
||||||
image_path2: Path,
|
|
||||||
w_title: str,
|
|
||||||
w_txt: str,
|
|
||||||
txt2: Optional[str] = None,
|
|
||||||
com: Optional[str] = None,
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
Creates message windows
|
|
||||||
|
|
||||||
:param image_path2:
|
|
||||||
:param image_path:
|
|
||||||
AppConfig.IMAGE_PATHS["icon_info"] = Image for TK window which is displayed to the left of the text
|
|
||||||
AppConfig.IMAGE_PATHS["icon_vpn"] = Image for Task Icon
|
|
||||||
:argument w_title = Windows Title
|
|
||||||
:argument w_txt = Text for Tk Window
|
|
||||||
:argument txt2 = Text for Button two
|
|
||||||
:argument com = function for Button command
|
|
||||||
"""
|
|
||||||
msg: tk.Toplevel = tk.Toplevel()
|
|
||||||
msg.resizable(width=False, height=False)
|
|
||||||
msg.title(w_title)
|
|
||||||
msg.configure(pady=15, padx=15)
|
|
||||||
|
|
||||||
# load first image for a window
|
|
||||||
try:
|
|
||||||
msg.img = tk.PhotoImage(file=image_path)
|
|
||||||
msg.i_window = tk.Label(msg, image=msg.img)
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Error on load Window Image: {e}", exc_info=True)
|
|
||||||
msg.i_window = tk.Label(msg, text="Image not found")
|
|
||||||
|
|
||||||
label: tk.Label = tk.Label(msg, text=w_txt)
|
|
||||||
label.grid(column=1, row=0)
|
|
||||||
|
|
||||||
if txt2 is not None and com is not None:
|
|
||||||
label.config(font=("Ubuntu", 11), padx=15, justify="left")
|
|
||||||
msg.i_window.grid(column=0, row=0, sticky="nw")
|
|
||||||
button2: ttk.Button = ttk.Button(
|
|
||||||
msg, text=f"{txt2}", command=com, padding=4
|
|
||||||
)
|
|
||||||
button2.grid(column=0, row=1, sticky="e", columnspan=2)
|
|
||||||
button: ttk.Button = ttk.Button(
|
|
||||||
msg, text="OK", command=msg.destroy, padding=4
|
|
||||||
)
|
|
||||||
button.grid(column=0, row=1, sticky="w", columnspan=2)
|
|
||||||
else:
|
|
||||||
label.config(font=("Ubuntu", 11), padx=15)
|
|
||||||
msg.i_window.grid(column=0, row=0)
|
|
||||||
button: ttk.Button = ttk.Button(
|
|
||||||
msg, text="OK", command=msg.destroy, padding=4
|
|
||||||
)
|
|
||||||
button.grid(column=0, columnspan=2, row=1)
|
|
||||||
|
|
||||||
try:
|
|
||||||
icon = tk.PhotoImage(file=image_path2)
|
|
||||||
msg.iconphoto(True, icon)
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Error loading the window icon: {e}", exc_info=True)
|
|
||||||
|
|
||||||
msg.columnconfigure(0, weight=1)
|
|
||||||
msg.rowconfigure(0, weight=1)
|
|
||||||
msg.winfo_toplevel()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def sigi(file_path: Optional[Path] = None, file: Optional[Path] = None) -> None:
|
def sigi(file_path: Optional[Path] = None, file: Optional[Path] = None) -> None:
|
||||||
"""
|
"""
|
||||||
|
25
gitea.py
25
gitea.py
@ -5,7 +5,7 @@ import requests
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import subprocess
|
import subprocess
|
||||||
import shutil
|
import shutil
|
||||||
from shared_libs.common_tools import LxTools
|
from shared_libs.message import MessageDialog
|
||||||
|
|
||||||
|
|
||||||
class GiteaUpdate:
|
class GiteaUpdate:
|
||||||
@ -67,34 +67,23 @@ class GiteaUpdate:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
to_down: str = f"wget -qP {Path.home()} {" "} {urld}"
|
to_down: str = f"wget -qP {Path.home()} {' '} {urld}"
|
||||||
result: int = subprocess.call(to_down, shell=True)
|
result: int = subprocess.call(to_down, shell=True)
|
||||||
if result == 0:
|
if result == 0:
|
||||||
shutil.chown(f"{Path.home()}/{res}.zip", 1000, 1000)
|
shutil.chown(f"{Path.home()}/{res}.zip", 1000, 1000)
|
||||||
|
|
||||||
LxTools.msg_window(
|
MessageDialog("info", text=Msg.STR["ok_message"])
|
||||||
AppConfig.IMAGE_PATHS["icon_info"],
|
|
||||||
AppConfig.IMAGE_PATHS["icon_download"],
|
|
||||||
Msg.STR["title"],
|
|
||||||
Msg.STR["ok_message"],
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
LxTools.msg_window(
|
MessageDialog(
|
||||||
AppConfig.IMAGE_PATHS["icon_error"],
|
"error", text=Msg.STR["error_message"], title=Msg.STR["error_title"]
|
||||||
AppConfig.IMAGE_PATHS["icon_download_error"],
|
|
||||||
Msg.STR["error_title"],
|
|
||||||
Msg.STR["error_massage"],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
|
|
||||||
LxTools.msg_window(
|
MessageDialog(
|
||||||
AppConfig.IMAGE_PATHS["icon_error"],
|
"error", text=Msg.STR["error_no_internet"], title=Msg.STR["error_title"]
|
||||||
AppConfig.IMAGE_PATHS["icon_msg"],
|
|
||||||
Msg.STR["error_title"],
|
|
||||||
Msg.STR["error_no_internet"],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
BIN
languages/de/logviewer.mo
Normal file
BIN
languages/de/logviewer.mo
Normal file
Binary file not shown.
163
languages/de/logviewer.po
Normal file
163
languages/de/logviewer.po
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR polunga40@unity-mail.de, YEAR.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: \n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2025-06-29 17:25+0200\n"
|
||||||
|
"PO-Revision-Date: 2025-06-29 18:00+0200\n"
|
||||||
|
"Last-Translator: Désiré Werner Menrath <polunga40@unity-mail.de>\n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"Language: de_DE\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Generator: Poedit 3.4.2\n"
|
||||||
|
|
||||||
|
#: gitea.py:127
|
||||||
|
msgid "Download Successful"
|
||||||
|
msgstr "Herunterladen erfolgreich"
|
||||||
|
|
||||||
|
#: gitea.py:128
|
||||||
|
msgid "Your zip file is in home directory"
|
||||||
|
msgstr "Ihre ZIP-Datei befindet sich im Home-Verzeichnis"
|
||||||
|
|
||||||
|
#: gitea.py:129
|
||||||
|
msgid "Download error"
|
||||||
|
msgstr "Fehler beim Herunterladen"
|
||||||
|
|
||||||
|
#: gitea.py:130
|
||||||
|
msgid "Download failed! Please try again"
|
||||||
|
msgstr "Herunterladen fehlgeschlagen! Bitte versuchen Sie es erneut."
|
||||||
|
|
||||||
|
#: gitea.py:131
|
||||||
|
msgid "Download failed! No internet connection!"
|
||||||
|
msgstr "Herunterladen fehlgeschlagen! Keine Internetverbindung!"
|
||||||
|
|
||||||
|
#: logviewer.py:102
|
||||||
|
msgid "Load Log"
|
||||||
|
msgstr "Logdatei laden"
|
||||||
|
|
||||||
|
#: logviewer.py:107
|
||||||
|
msgid "Options"
|
||||||
|
msgstr "Optionen"
|
||||||
|
|
||||||
|
#: logviewer.py:116
|
||||||
|
msgid "Disable Updates"
|
||||||
|
msgstr "Updates deaktivieren"
|
||||||
|
|
||||||
|
#: logviewer.py:149
|
||||||
|
msgid "About"
|
||||||
|
msgstr "Über"
|
||||||
|
|
||||||
|
#: logviewer.py:184
|
||||||
|
msgid "Update search off"
|
||||||
|
msgstr "Suche nach Updates ausgeschaltet"
|
||||||
|
|
||||||
|
#: logviewer.py:185
|
||||||
|
msgid "Updates you have disabled"
|
||||||
|
msgstr "Sie haben Updates deaktiviert"
|
||||||
|
|
||||||
|
#: logviewer.py:192
|
||||||
|
msgid "No Server Connection!"
|
||||||
|
msgstr "Keine Verbindung zum Server!"
|
||||||
|
|
||||||
|
#: logviewer.py:197
|
||||||
|
msgid "Could not connect to update server"
|
||||||
|
msgstr "Verbindung zum Update-Server nicht möglich"
|
||||||
|
|
||||||
|
#: logviewer.py:202
|
||||||
|
msgid "No Updates"
|
||||||
|
msgstr "Keine Updates verfügbar"
|
||||||
|
|
||||||
|
#: logviewer.py:203
|
||||||
|
msgid "Congratulations! Wire-Py is up to date"
|
||||||
|
msgstr "Glückwunsch! Wire-Py ist aktuell."
|
||||||
|
|
||||||
|
#: logviewer.py:223
|
||||||
|
msgid "Click to install new version"
|
||||||
|
msgstr "Klicken Sie, um die neue Version zu installieren"
|
||||||
|
|
||||||
|
#: logviewer.py:232
|
||||||
|
msgid ""
|
||||||
|
"Logviewer a simple Gui for View Logfiles.\n"
|
||||||
|
"\n"
|
||||||
|
"Logviewer is open source software written in Python.\n"
|
||||||
|
"\n"
|
||||||
|
"Email: polunga40@unity-mail.de also likes for donation.\n"
|
||||||
|
"\n"
|
||||||
|
"Use without warranty!\n"
|
||||||
|
msgstr ""
|
||||||
|
"Logviewer eine einfache GUI zur Anzeige von Protokolldateien.\n"
|
||||||
|
"\n"
|
||||||
|
"Logviewer ist Open-Source-Software, geschrieben in Python.\n"
|
||||||
|
"\n"
|
||||||
|
"E-Mail: polunga40@unity-mail.de (Spenden sind willkommen).\n"
|
||||||
|
"\n"
|
||||||
|
"Verwendung ohne Gewähr!\n"
|
||||||
|
|
||||||
|
#: logviewer.py:288
|
||||||
|
msgid "Disable Tooltips"
|
||||||
|
msgstr "Tooltips deaktivieren"
|
||||||
|
|
||||||
|
#: logviewer.py:291
|
||||||
|
msgid "Enable Tooltips"
|
||||||
|
msgstr "Tooltips aktivieren"
|
||||||
|
|
||||||
|
#: logviewer.py:319
|
||||||
|
msgid "Dark"
|
||||||
|
msgstr "Dunkel"
|
||||||
|
|
||||||
|
#: logviewer.py:321
|
||||||
|
msgid "Light"
|
||||||
|
msgstr "Hell"
|
||||||
|
|
||||||
|
#: logviewer.py:362
|
||||||
|
msgid "Copy"
|
||||||
|
msgstr "Kopieren"
|
||||||
|
|
||||||
|
#: logviewer.py:363
|
||||||
|
msgid "Paste"
|
||||||
|
msgstr "Einfügen"
|
||||||
|
|
||||||
|
#: logviewer.py:367
|
||||||
|
msgid "Search"
|
||||||
|
msgstr "Suchen"
|
||||||
|
|
||||||
|
#: logviewer.py:371
|
||||||
|
msgid "Delete_Log"
|
||||||
|
msgstr "Logdatei löschen"
|
||||||
|
|
||||||
|
#: logviewer.py:456
|
||||||
|
msgid "A mistake occurred: {str(e)}"
|
||||||
|
msgstr "Ein Fehler ist aufgetreten: {str(e)}"
|
||||||
|
|
||||||
|
#: logviewer.py:457
|
||||||
|
msgid ""
|
||||||
|
"A mistake occurred:\n"
|
||||||
|
"{str(e)}\n"
|
||||||
|
msgstr ""
|
||||||
|
"Ein Fehler ist aufgetreten:\n"
|
||||||
|
"{str(e)}\n"
|
||||||
|
|
||||||
|
#: logviewer.py:474
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "A mistake occurred: {e}"
|
||||||
|
msgstr "Ein Fehler ist aufgetreten: {e}"
|
||||||
|
|
||||||
|
#: logviewer.py:475
|
||||||
|
#, python-brace-format
|
||||||
|
msgid ""
|
||||||
|
"A mistake occurred:\n"
|
||||||
|
"{e}\n"
|
||||||
|
msgstr ""
|
||||||
|
"Ein Fehler ist aufgetreten:\n"
|
||||||
|
"{e}\n"
|
||||||
|
|
||||||
|
#: logview_app_config.py:146
|
||||||
|
msgid "Click for Settings"
|
||||||
|
msgstr "Klick für Einstellungen"
|
@ -51,14 +51,14 @@ class AppConfig:
|
|||||||
|
|
||||||
# Updates
|
# Updates
|
||||||
# 1 = 1. Year, 09 = Month of the Year, 2924 = Day and Year of the Year
|
# 1 = 1. Year, 09 = Month of the Year, 2924 = Day and Year of the Year
|
||||||
VERSION: str = "v. 1.06.3125"
|
VERSION: str = "v. 1.07.0925"
|
||||||
UPDATE_URL: str = "https://git.ilunix.de/api/v1/repos/punix/Wire-Py/releases"
|
UPDATE_URL: str = "https://git.ilunix.de/api/v1/repos/punix/shared_libs/releases"
|
||||||
DOWNLOAD_URL: str = "https://git.ilunix.de/punix/Wire-Py/archive"
|
DOWNLOAD_URL: str = "https://git.ilunix.de/punix/shared_libs/archive"
|
||||||
|
|
||||||
# UI configuration
|
# UI configuration
|
||||||
UI_CONFIG: Dict[str, Any] = {
|
UI_CONFIG: Dict[str, Any] = {
|
||||||
"window_title2": "LogViewer",
|
"window_title2": "LogViewer",
|
||||||
"window_size": (600, 383),
|
"window_size": (590, 460),
|
||||||
"font_family": "Ubuntu",
|
"font_family": "Ubuntu",
|
||||||
"font_size": 11,
|
"font_size": 11,
|
||||||
"resizable_window": (True, True),
|
"resizable_window": (True, True),
|
||||||
@ -66,8 +66,6 @@ class AppConfig:
|
|||||||
|
|
||||||
# Images and icons paths
|
# Images and icons paths
|
||||||
IMAGE_PATHS: Dict[str, Path] = {
|
IMAGE_PATHS: Dict[str, Path] = {
|
||||||
"icon_info": "/usr/share/icons/lx-icons/64/info.png",
|
|
||||||
"icon_error": "/usr/share/icons/lx-icons/64/error.png",
|
|
||||||
"icon_log": "/usr/share/icons/lx-icons/48/log.png",
|
"icon_log": "/usr/share/icons/lx-icons/48/log.png",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
84
logviewer.py
84
logviewer.py
@ -4,7 +4,12 @@ import logging
|
|||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import TclError, filedialog, ttk
|
from tkinter import TclError, filedialog, ttk
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import os
|
||||||
|
import webbrowser
|
||||||
|
import subprocess
|
||||||
|
from functools import partial
|
||||||
from shared_libs.gitea import GiteaUpdate
|
from shared_libs.gitea import GiteaUpdate
|
||||||
|
from shared_libs.message import MessageDialog
|
||||||
from shared_libs.common_tools import (
|
from shared_libs.common_tools import (
|
||||||
LogConfig,
|
LogConfig,
|
||||||
ConfigManager,
|
ConfigManager,
|
||||||
@ -14,7 +19,6 @@ from shared_libs.common_tools import (
|
|||||||
)
|
)
|
||||||
import sys
|
import sys
|
||||||
from file_and_dir_ensure import prepare_app_environment
|
from file_and_dir_ensure import prepare_app_environment
|
||||||
import webbrowser
|
|
||||||
|
|
||||||
|
|
||||||
class LogViewer(tk.Tk):
|
class LogViewer(tk.Tk):
|
||||||
@ -42,9 +46,12 @@ class LogViewer(tk.Tk):
|
|||||||
theme = ConfigManager.get("theme")
|
theme = ConfigManager.get("theme")
|
||||||
ThemeManager.change_theme(self, theme)
|
ThemeManager.change_theme(self, theme)
|
||||||
LxTools.center_window_cross_platform(self, self.x_width, self.y_height)
|
LxTools.center_window_cross_platform(self, self.x_width, self.y_height)
|
||||||
self.createWidgets(_)
|
self.createWidgets(modul_name, _)
|
||||||
self.load_file(_, modul_name=modul_name)
|
self.load_file(_, modul_name=modul_name)
|
||||||
self.log_icon = tk.PhotoImage(file=modul_name.AppConfig.IMAGE_PATHS["icon_log"])
|
self.log_icon = tk.PhotoImage(file="/usr/share/icons/lx-icons/48/log.png")
|
||||||
|
self.update_icon = tk.PhotoImage(
|
||||||
|
file="/usr/share/icons/lx-icons/16/settings.png"
|
||||||
|
)
|
||||||
self.iconphoto(True, self.log_icon)
|
self.iconphoto(True, self.log_icon)
|
||||||
self.grid_rowconfigure(0, weight=1)
|
self.grid_rowconfigure(0, weight=1)
|
||||||
self.grid_rowconfigure(1, weight=1)
|
self.grid_rowconfigure(1, weight=1)
|
||||||
@ -162,6 +169,15 @@ class LogViewer(tk.Tk):
|
|||||||
else:
|
else:
|
||||||
self.updates_lb.grid_remove()
|
self.updates_lb.grid_remove()
|
||||||
|
|
||||||
|
def updater(self):
|
||||||
|
"""Start the lxtools_installer"""
|
||||||
|
tmp_dir = Path("/tmp/lxtools")
|
||||||
|
Path.mkdir(tmp_dir, exist_ok=True)
|
||||||
|
os.chdir(tmp_dir)
|
||||||
|
result = subprocess.run(["/usr/local/bin/lxtools_installer"], check=False)
|
||||||
|
if result.returncode != 0:
|
||||||
|
MessageDialog("error", result.stderr)
|
||||||
|
|
||||||
# Update the labels based on the result
|
# Update the labels based on the result
|
||||||
def update_ui_for_update(self, res, modul_name, _):
|
def update_ui_for_update(self, res, modul_name, _):
|
||||||
"""Update UI elements based on an update check result"""
|
"""Update UI elements based on an update check result"""
|
||||||
@ -198,25 +214,20 @@ class LogViewer(tk.Tk):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
self.set_update.set(value=0)
|
self.set_update.set(value=0)
|
||||||
update_text = f"Update {res} {_('available!')}"
|
|
||||||
|
|
||||||
# Clear the label text since we'll show the button instead
|
# Clear the label text since we'll show the button instead
|
||||||
self.update_label.set("")
|
self.update_label.set("")
|
||||||
|
|
||||||
# Create the update button
|
# Create the update button
|
||||||
self.update_btn = ttk.Menubutton(self.menu_frame, text=update_text)
|
self.update_btn = ttk.Button(
|
||||||
|
self.menu_frame,
|
||||||
|
image=self.update_icon,
|
||||||
|
style="Toolbutton",
|
||||||
|
command=self.updater,
|
||||||
|
)
|
||||||
self.update_btn.grid(column=5, row=0, padx=0)
|
self.update_btn.grid(column=5, row=0, padx=0)
|
||||||
Tooltip(
|
Tooltip(
|
||||||
self.update_btn, _("Click to download new version"), self.tooltip_state
|
self.update_btn, _("Click to install new version"), self.tooltip_state
|
||||||
)
|
|
||||||
|
|
||||||
self.download = tk.Menu(self, relief="flat")
|
|
||||||
self.update_btn.configure(menu=self.download, style="Toolbutton")
|
|
||||||
self.download.add_command(
|
|
||||||
label=_("Download"),
|
|
||||||
command=lambda: GiteaUpdate.download(
|
|
||||||
f"{modul_name.AppConfig.DOWNLOAD_URL}/{res}.zip", res
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -224,10 +235,6 @@ class LogViewer(tk.Tk):
|
|||||||
"""
|
"""
|
||||||
a tk.Toplevel window
|
a tk.Toplevel window
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def link_btn() -> None:
|
|
||||||
webbrowser.open("https://git.ilunix.de/punix/shared_libs")
|
|
||||||
|
|
||||||
msg_t = _(
|
msg_t = _(
|
||||||
"Logviewer a simple Gui for View Logfiles.\n\n"
|
"Logviewer a simple Gui for View Logfiles.\n\n"
|
||||||
"Logviewer is open source software written in Python.\n\n"
|
"Logviewer is open source software written in Python.\n\n"
|
||||||
@ -235,13 +242,16 @@ class LogViewer(tk.Tk):
|
|||||||
"Use without warranty!\n"
|
"Use without warranty!\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
LxTools.msg_window(
|
MessageDialog(
|
||||||
modul_name.AppConfig.IMAGE_PATHS["icon_log"],
|
"info",
|
||||||
modul_name.AppConfig.IMAGE_PATHS["icon_log"],
|
text=msg_t,
|
||||||
_("Info"),
|
buttons=["OK", "Go to Logviewer"],
|
||||||
msg_t,
|
commands=[
|
||||||
_("Go to shared_libs git"),
|
None, # Default on "OK"
|
||||||
link_btn,
|
partial(webbrowser.open, "https://git.ilunix.de/punix/shared_libs"),
|
||||||
|
],
|
||||||
|
icon=modul_name.AppConfig.IMAGE_PATHS["icon_log"],
|
||||||
|
title="Logviewer",
|
||||||
)
|
)
|
||||||
|
|
||||||
def update_setting(self, update_res, modul_name, _) -> None:
|
def update_setting(self, update_res, modul_name, _) -> None:
|
||||||
@ -326,7 +336,7 @@ class LogViewer(tk.Tk):
|
|||||||
# Update Menulfield
|
# Update Menulfield
|
||||||
self.settings.entryconfigure(2, label=self.theme_label.get())
|
self.settings.entryconfigure(2, label=self.theme_label.get())
|
||||||
|
|
||||||
def createWidgets(self, _):
|
def createWidgets(self, modul_name, _):
|
||||||
|
|
||||||
text_frame = ttk.Frame(self)
|
text_frame = ttk.Frame(self)
|
||||||
text_frame.grid(row=1, column=0, padx=5, pady=5, sticky=tk.NSEW)
|
text_frame.grid(row=1, column=0, padx=5, pady=5, sticky=tk.NSEW)
|
||||||
@ -361,11 +371,11 @@ class LogViewer(tk.Tk):
|
|||||||
self.text_area.bind("<Button-3>", self.show_context_menu)
|
self.text_area.bind("<Button-3>", self.show_context_menu)
|
||||||
self._entry.bind("<Button-3>", self.show_context_menu)
|
self._entry.bind("<Button-3>", self.show_context_menu)
|
||||||
|
|
||||||
search_button = ttk.Button(next_frame, text="Search", command=self._onFind)
|
search_button = ttk.Button(next_frame, text=_("Search"), command=self._onFind)
|
||||||
search_button.grid(row=0, column=0, padx=5, pady=5, sticky=tk.EW)
|
search_button.grid(row=0, column=0, padx=5, pady=5, sticky=tk.EW)
|
||||||
|
|
||||||
delete_button = ttk.Button(
|
delete_button = ttk.Button(
|
||||||
next_frame, text="Delete_Log", command=self.delete_file
|
next_frame, text=_("Delete_Log"), command=self.delete_file
|
||||||
)
|
)
|
||||||
delete_button.grid(row=0, column=2, padx=5, pady=5, sticky=tk.EW)
|
delete_button.grid(row=0, column=2, padx=5, pady=5, sticky=tk.EW)
|
||||||
|
|
||||||
@ -451,17 +461,12 @@ class LogViewer(tk.Tk):
|
|||||||
self.text_area.insert(tk.END, file.read())
|
self.text_area.insert(tk.END, file.read())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(_(f"A mistake occurred: {str(e)}"))
|
logging.error(_(f"A mistake occurred: {str(e)}"))
|
||||||
LxTools.msg_window(
|
MessageDialog("error", _(f"A mistake occurred:\n{str(e)}\n"))
|
||||||
modul_name.AppConfig.IMAGE_PATHS["icon_error"],
|
|
||||||
modul_name.AppConfig.IMAGE_PATHS["icon_log"],
|
|
||||||
"LogViewer",
|
|
||||||
_(f"A mistake occurred:\n{str(e)}\n"),
|
|
||||||
)
|
|
||||||
|
|
||||||
def directory_load(self, modul_name, _):
|
def directory_load(self, modul_name, _):
|
||||||
|
|
||||||
filepath = filedialog.askopenfilename(
|
filepath = filedialog.askopenfilename(
|
||||||
initialdir=f"{Path.home() / ".local/share/lxlogs/"}",
|
initialdir=f"{Path.home() / '.local/share/lxlogs/'}",
|
||||||
title="Select a Logfile File",
|
title="Select a Logfile File",
|
||||||
filetypes=[("Logfiles", "*.log")],
|
filetypes=[("Logfiles", "*.log")],
|
||||||
)
|
)
|
||||||
@ -474,12 +479,7 @@ class LogViewer(tk.Tk):
|
|||||||
print("File load: abort by user...")
|
print("File load: abort by user...")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(_(f"A mistake occurred: {e}"))
|
logging.error(_(f"A mistake occurred: {e}"))
|
||||||
LxTools.msg_window(
|
MessageDialog("error", _(f"A mistake occurred:\n{e}\n"))
|
||||||
modul_name.AppConfig.IMAGE_PATHS["icon_error"],
|
|
||||||
modul_name.AppConfig.IMAGE_PATHS["icon_log"],
|
|
||||||
"LogViewer",
|
|
||||||
_(f"A mistake occurred:\n{e}\n"),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
332
message.py
Normal file
332
message.py
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
import os
|
||||||
|
from typing import List, Optional, Dict
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import ttk
|
||||||
|
|
||||||
|
try:
|
||||||
|
from manager import LxTools
|
||||||
|
except (ModuleNotFoundError, NameError):
|
||||||
|
from shared_libs.common_tools import LxTools
|
||||||
|
|
||||||
|
|
||||||
|
class MessageDialog:
|
||||||
|
"""
|
||||||
|
A customizable message dialog window using tkinter for user interaction.
|
||||||
|
|
||||||
|
This class creates modal dialogs for displaying information, warnings, errors,
|
||||||
|
or questions to the user. It supports multiple button configurations, custom
|
||||||
|
icons, keyboard navigation, and command binding. The dialog is centered on the
|
||||||
|
screen and handles user interactions with focus management and accessibility.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
message_type (str): Type of message ("info", "error", "warning", "ask").
|
||||||
|
text (str): Main message content to display.
|
||||||
|
buttons (List[str]): List of button labels (e.g., ["OK", "Cancel"]).
|
||||||
|
result (bool or None):
|
||||||
|
- True for positive actions (Yes, OK)
|
||||||
|
- False for negative actions (No, Cancel)
|
||||||
|
- None if "Cancel" was clicked with ≥3 buttons
|
||||||
|
icons: Dictionary mapping message types to tkinter.PhotoImage objects
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
message_type: Type of message dialog (default: "info")
|
||||||
|
text: Message content to display
|
||||||
|
buttons: List of button labels (default: ["OK"])
|
||||||
|
master: Parent tkinter window (optional)
|
||||||
|
commands: List of callables for each button (default: [None])
|
||||||
|
icon: Custom icon path (overrides default icons if provided)
|
||||||
|
title: Window title (default: derived from message_type)
|
||||||
|
font: Font tuple for text styling
|
||||||
|
wraplength: Text wrapping width in pixels
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
_get_title(): Returns the default window title based on message type.
|
||||||
|
_load_icons(): Loads icons from system paths or fallback locations.
|
||||||
|
_on_button_click(button_text): Sets result and closes the dialog.
|
||||||
|
show(): Displays the dialog and waits for user response.
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
|
||||||
|
1. Basic Info Dialog:
|
||||||
|
>>> MessageDialog(
|
||||||
|
... text="This is an information message.")
|
||||||
|
>>> result = dialog.show()
|
||||||
|
>>> print("User clicked OK:", result)
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
My Favorite Example,
|
||||||
|
for simply information message:
|
||||||
|
|
||||||
|
>>> MessageDialog(text="This is an information message.")
|
||||||
|
>>> result = MessageDialog(text="This is an information message.").show()
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
|
||||||
|
2. Error Dialog with Custom Command:
|
||||||
|
>>> def on_retry():
|
||||||
|
... print("User selected Retry")
|
||||||
|
|
||||||
|
>>> dialog = MessageDialog(
|
||||||
|
... message_type="error",
|
||||||
|
... text="An error occurred during processing.",
|
||||||
|
... buttons=["Retry", "Cancel"],
|
||||||
|
... commands=[on_retry, None],
|
||||||
|
... title="Critical Error"
|
||||||
|
... )
|
||||||
|
>>> result = dialog.show()
|
||||||
|
>>> print("User selected Retry:", result)
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
|
||||||
|
3. And a special example for a "open link" button:
|
||||||
|
Be careful not to forget to import it into the script in which
|
||||||
|
this dialog is used!!! import webbrowser from functools import partial
|
||||||
|
|
||||||
|
>>> MessageDialog(
|
||||||
|
... "info"
|
||||||
|
... text="This is an information message.",
|
||||||
|
... buttons=["Yes", "Go to Exapmle"],
|
||||||
|
... commands=[
|
||||||
|
... None, # Default on "OK"
|
||||||
|
... partial(webbrowser.open, "https://exapmle.com"),
|
||||||
|
... ],
|
||||||
|
... icon="/pathh/to/custom/icon.png",
|
||||||
|
... title="Example",
|
||||||
|
... )
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- Returns None if "Cancel" was clicked with ≥3 buttons
|
||||||
|
- Supports keyboard navigation (Left/Right arrows and Enter)
|
||||||
|
- Dialog automatically centers on screen
|
||||||
|
- Result is False for window close (X) with 2 buttons
|
||||||
|
- Font and wraplength parameters enable text styling
|
||||||
|
"""
|
||||||
|
|
||||||
|
DEFAULT_ICON_PATH = "/usr/share/icons/lx-icons"
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
message_type: str = "info",
|
||||||
|
text: str = "",
|
||||||
|
buttons: List[str] = ["OK"],
|
||||||
|
master: Optional[tk.Tk] = None,
|
||||||
|
commands: List[Optional[callable]] = [None],
|
||||||
|
icon: str = None,
|
||||||
|
title: str = None,
|
||||||
|
font: tuple = None,
|
||||||
|
wraplength: int = None,
|
||||||
|
):
|
||||||
|
self.message_type = message_type or "info" # Default is "info"
|
||||||
|
self.text = text
|
||||||
|
self.buttons = buttons
|
||||||
|
self.master = master
|
||||||
|
self.result: bool = False # Default is False
|
||||||
|
|
||||||
|
self.icon_path = self._get_icon_path()
|
||||||
|
self.icon = icon
|
||||||
|
self.title = title
|
||||||
|
# Window creation
|
||||||
|
self.window = tk.Toplevel(master)
|
||||||
|
self.window.grab_set()
|
||||||
|
self.window.resizable(False, False)
|
||||||
|
ttk.Style().configure("TButton")
|
||||||
|
self.buttons_widgets = []
|
||||||
|
self.current_button_index = 0
|
||||||
|
self._load_icons()
|
||||||
|
|
||||||
|
# Window title and icon
|
||||||
|
self.window.title(self._get_title() if not self.title else self.title)
|
||||||
|
self.window.iconphoto(False, self.icons[self.message_type])
|
||||||
|
|
||||||
|
# Layout
|
||||||
|
frame = ttk.Frame(self.window)
|
||||||
|
frame.pack(expand=True, fill="both", padx=15, pady=8)
|
||||||
|
|
||||||
|
# Grid-Configuration
|
||||||
|
frame.grid_rowconfigure(0, weight=1)
|
||||||
|
frame.grid_columnconfigure(0, weight=1)
|
||||||
|
frame.grid_columnconfigure(1, weight=3)
|
||||||
|
|
||||||
|
# Icon and Text
|
||||||
|
icon_label = ttk.Label(frame, image=self.icons[self.message_type])
|
||||||
|
pady_value = 5 if self.icon is not None else 15
|
||||||
|
icon_label.grid(
|
||||||
|
row=0, column=0, padx=(20, 10), pady=(pady_value, 15), sticky="nsew"
|
||||||
|
)
|
||||||
|
|
||||||
|
text_label = tk.Label(
|
||||||
|
frame,
|
||||||
|
text=text,
|
||||||
|
wraplength=wraplength if wraplength else 300,
|
||||||
|
justify="left",
|
||||||
|
anchor="center",
|
||||||
|
font=font if font else ("Helvetica", 12),
|
||||||
|
pady=20,
|
||||||
|
)
|
||||||
|
text_label.grid(
|
||||||
|
row=0,
|
||||||
|
column=1,
|
||||||
|
padx=(10, 20),
|
||||||
|
sticky="nsew",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create button frame
|
||||||
|
self.button_frame = ttk.Frame(frame)
|
||||||
|
self.button_frame.grid(row=1, columnspan=2, pady=(8, 10))
|
||||||
|
|
||||||
|
for i, btn_text in enumerate(buttons):
|
||||||
|
if commands and len(commands) > i and commands[i] is not None:
|
||||||
|
# Button with individual command
|
||||||
|
btn = ttk.Button(
|
||||||
|
self.button_frame,
|
||||||
|
text=btn_text,
|
||||||
|
command=commands[i],
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Default button set self.result and close window
|
||||||
|
btn = ttk.Button(
|
||||||
|
self.button_frame,
|
||||||
|
text=btn_text,
|
||||||
|
command=lambda t=btn_text: self._on_button_click(t),
|
||||||
|
)
|
||||||
|
|
||||||
|
padx_value = 50 if self.icon is not None and len(buttons) == 2 else 10
|
||||||
|
btn.pack(side="left" if i == 0 else "right", padx=padx_value, pady=5)
|
||||||
|
btn.focus_set() if i == 0 else None # Set focus on first button
|
||||||
|
self.buttons_widgets.append(btn)
|
||||||
|
|
||||||
|
self.window.bind("<Return>", lambda event: self._on_enter_pressed())
|
||||||
|
self.window.bind("<Left>", lambda event: self._navigate_left())
|
||||||
|
self.window.bind("<Right>", lambda event: self._navigate_right())
|
||||||
|
self.window.update_idletasks()
|
||||||
|
self.window.attributes("-alpha", 0.0) # 100% Transparencence
|
||||||
|
self.window.after(200, lambda: self.window.attributes("-alpha", 100.0))
|
||||||
|
self.window.update() # Window update before centering!
|
||||||
|
LxTools.center_window_cross_platform(
|
||||||
|
self.window, self.window.winfo_width(), self.window.winfo_height()
|
||||||
|
)
|
||||||
|
|
||||||
|
# Close Window on Cancel
|
||||||
|
self.window.protocol(
|
||||||
|
"WM_DELETE_WINDOW", lambda: self._on_button_click("Cancel")
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_title(self) -> str:
|
||||||
|
return {
|
||||||
|
"error": "Error",
|
||||||
|
"info": "Info",
|
||||||
|
"ask": "Question",
|
||||||
|
"warning": "Warning",
|
||||||
|
}[self.message_type]
|
||||||
|
|
||||||
|
def _load_icons(self):
|
||||||
|
# Try to load the icon from the provided path
|
||||||
|
self.icons = {}
|
||||||
|
icon_paths: Dict[str, str] = {
|
||||||
|
"error": os.path.join(self.icon_path, "64/error.png"),
|
||||||
|
"info": os.path.join(self.icon_path, "64/info.png"),
|
||||||
|
"warning": os.path.join(self.icon_path, "64/warning.png"),
|
||||||
|
"ask": os.path.join(self.icon_path, "64/question_mark.png"),
|
||||||
|
}
|
||||||
|
|
||||||
|
fallback_paths: Dict[str, str] = {
|
||||||
|
"error": "./lx-icons/64/error.png",
|
||||||
|
"info": "./lx-icons/64/info.png",
|
||||||
|
"warning": "./lx-icons/64/warning.png",
|
||||||
|
"ask": "./lx-icons/64/question_mark.png",
|
||||||
|
}
|
||||||
|
|
||||||
|
for key in icon_paths:
|
||||||
|
try:
|
||||||
|
# Check if an individual icon is provided
|
||||||
|
if (
|
||||||
|
self.message_type == key
|
||||||
|
and self.icon is not None
|
||||||
|
and os.path.exists(self.icon)
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
self.icons[key] = tk.PhotoImage(file=self.icon)
|
||||||
|
except Exception as e:
|
||||||
|
print(
|
||||||
|
f"Erro on loading individual icon '{key}': {e}\n",
|
||||||
|
"Try to use the default icon",
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Check for standard path
|
||||||
|
if os.path.exists(icon_paths[key]):
|
||||||
|
self.icons[key] = tk.PhotoImage(file=icon_paths[key])
|
||||||
|
else:
|
||||||
|
if os.path.exists(fallback_paths[key]):
|
||||||
|
self.icons[key] = tk.PhotoImage(file=fallback_paths[key])
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error on load Icon '{[key]}': {e}")
|
||||||
|
self.icons[key] = tk.PhotoImage()
|
||||||
|
print(f"⚠️ No Icon found for '{key}'. Use standard Tkinter icon.")
|
||||||
|
return self.icons
|
||||||
|
|
||||||
|
def _get_icon_path(self) -> str:
|
||||||
|
"""Get the path to the default icon."""
|
||||||
|
if os.path.exists(self.DEFAULT_ICON_PATH):
|
||||||
|
return self.DEFAULT_ICON_PATH
|
||||||
|
else:
|
||||||
|
# Fallback to the directory of the script
|
||||||
|
return os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
def _navigate_left(self):
|
||||||
|
if not self.buttons_widgets:
|
||||||
|
return
|
||||||
|
self.current_button_index = (self.current_button_index - 1) % len(
|
||||||
|
self.buttons_widgets
|
||||||
|
)
|
||||||
|
self.buttons_widgets[self.current_button_index].focus_set()
|
||||||
|
|
||||||
|
def _navigate_right(self):
|
||||||
|
if not self.buttons_widgets:
|
||||||
|
return
|
||||||
|
self.current_button_index = (self.current_button_index + 1) % len(
|
||||||
|
self.buttons_widgets
|
||||||
|
)
|
||||||
|
self.buttons_widgets[self.current_button_index].focus_set()
|
||||||
|
|
||||||
|
def _on_enter_pressed(self):
|
||||||
|
focused = self.window.focus_get()
|
||||||
|
if isinstance(focused, ttk.Button):
|
||||||
|
focused.invoke()
|
||||||
|
|
||||||
|
def _on_button_click(self, button_text: str) -> None:
|
||||||
|
"""
|
||||||
|
Sets `self.result` based on the clicked button.
|
||||||
|
- Returns `None` if the button is "Cancel", "Abort", or "Exit" **and** there are 3 or more buttons.
|
||||||
|
- Returns `True` if the button is "Yes", "Ok", "Continue", "Next", or "Start".
|
||||||
|
- Returns `False` in all other cases (e.g., "No", closing with X, or fewer than 3 buttons).
|
||||||
|
"""
|
||||||
|
# Check: If there are 3+ buttons and the button text matches "Cancel", "Abort", or "Exit"
|
||||||
|
if len(self.buttons) >= 3 and button_text.lower() in [
|
||||||
|
"cancel",
|
||||||
|
"abort",
|
||||||
|
"exit",
|
||||||
|
]:
|
||||||
|
self.result = None
|
||||||
|
# Check: Button text is "Yes", "Ok", "Continue", "Next", or "Start"
|
||||||
|
elif button_text.lower() in ["yes", "ok", "continue", "next", "start"]:
|
||||||
|
self.result = True
|
||||||
|
else:
|
||||||
|
# Fallback for all other cases (e.g., "No", closing with X, or fewer than 3 buttons)
|
||||||
|
self.result = False
|
||||||
|
|
||||||
|
self.window.destroy()
|
||||||
|
|
||||||
|
def show(self) -> Optional[bool]:
|
||||||
|
"""
|
||||||
|
Displays the dialog window and waits for user interaction.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool or None:
|
||||||
|
- `True` if "Yes", "Ok", etc. was clicked.
|
||||||
|
- `False` if "No" was clicked, or the window was closed with X (when there are 2 buttons).
|
||||||
|
- `None` if "Cancel", "Abort", or "Exit" was clicked **and** there are 3+ buttons,
|
||||||
|
or the window was closed with X (when there are 3+ buttons).
|
||||||
|
"""
|
||||||
|
self.window.wait_window()
|
||||||
|
return self.result
|
Reference in New Issue
Block a user