From cee2e218e34e5701f65c4e75be74a81a5f87de55 Mon Sep 17 00:00:00 2001 From: Aareon Sullivan Date: Mon, 2 Sep 2024 12:56:05 -0700 Subject: [PATCH 1/8] Add documentation --- tkloguru/__init__.py | 59 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/tkloguru/__init__.py b/tkloguru/__init__.py index ccf04c2..56cfc05 100644 --- a/tkloguru/__init__.py +++ b/tkloguru/__init__.py @@ -6,16 +6,41 @@ import threading class LoguruWidget(ttk.Frame): + """ + A custom Tkinter widget for displaying Loguru log messages. + + This widget creates a text area with optional scrollbar to display + log messages from Loguru. It supports different color modes and + can limit the number of displayed lines. + + Attributes: + queue (Queue): A thread-safe queue for storing log records. + show_scrollbar (bool): Whether to show a scrollbar or not. + color_mode (str): The coloring mode for log messages. + max_lines (int): Maximum number of lines to display in the widget. + """ + def __init__(self, master=None, show_scrollbar=True, color_mode='level', max_lines=1000, **kwargs): + """ + Initialize the LoguruWidget. + + Args: + master: The parent widget. + show_scrollbar (bool): Whether to show a scrollbar. + color_mode (str): The coloring mode ('level', 'message', or 'full'). + max_lines (int): Maximum number of lines to display. + **kwargs: Additional keyword arguments for the ttk.Frame. + """ super().__init__(master, **kwargs) self.queue = queue.Queue() self.show_scrollbar = show_scrollbar - self.color_mode = color_mode # 'level', 'message', or 'full' + self.color_mode = color_mode self.max_lines = max_lines self.create_widgets() self.after(100, self.check_queue) def create_widgets(self): + """Create and configure the Text widget and optional Scrollbar.""" self.text = tk.Text(self, wrap=tk.WORD, state=tk.DISABLED) if self.show_scrollbar: @@ -34,6 +59,12 @@ def create_widgets(self): self.text.tag_configure("CRITICAL", foreground="white", background="#c0392b") # White on dark red def check_queue(self): + """ + Check the queue for new log records and update the widget. + + This method is called periodically to process new log records + from the queue and update the text widget. + """ try: while self.queue.qsize(): record = self.queue.get_nowait() @@ -44,6 +75,12 @@ def check_queue(self): self.after(100, self.check_queue) def update_widget(self, record): + """ + Update the text widget with a new log record. + + Args: + record (dict): A dictionary containing log record information. + """ self.text.configure(state=tk.NORMAL) time_str = record["time"].strftime("%Y-%m-%d %H:%M:%S") @@ -68,10 +105,28 @@ def update_widget(self, record): self.text.configure(state=tk.DISABLED) def sink(self, message): + """ + Add a log message to the queue. + + This method is used as a sink for Loguru to add log records + to the queue for later processing. + + Args: + message: A Loguru message object. + """ record = message.record self.queue.put(record) def setup_logger(widget): + """ + Set up the Loguru logger to use the LoguruWidget. + + This function removes all existing Loguru handlers and adds + the LoguruWidget as a new handler. + + Args: + widget (LoguruWidget): The LoguruWidget instance to use as a handler. + """ logger.remove() logger.add(widget.sink, backtrace=True, diagnose=True) @@ -90,8 +145,8 @@ def setup_logger(widget): # Add console handler for debugging logger.add(sys.stdout, level="DEBUG") - # Example log messages def generate_logs(): + """Generate example log messages of various levels.""" logger.debug("This is a debug message") logger.info("This is an info message") logger.success("This is a success message") From f6dec9b0db513b40e10add942656634709ef28b4 Mon Sep 17 00:00:00 2001 From: Aareon Sullivan Date: Mon, 2 Sep 2024 12:56:11 -0700 Subject: [PATCH 2/8] Increment version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e082eeb..0db33e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "tkloguru" -version = "0.1.0" +version = "0.1.1" description = "A custom Tkinter widget that displays Loguru log messages in real-time with support for styling and colors. Monitor application logs directly within your Tkinter GUI." authors = ["Aareon Sullivan "] license = "MIT" From ec37dc22c005e00525a93abe4434aa40aafe6611 Mon Sep 17 00:00:00 2001 From: Aareon Sullivan Date: Tue, 3 Sep 2024 11:51:15 -0500 Subject: [PATCH 3/8] Add ttkbootstrap development dependency --- poetry.lock | 113 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 33 ++++++++------- 2 files changed, 130 insertions(+), 16 deletions(-) diff --git a/poetry.lock b/poetry.lock index e20abc0..657fe1d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -29,6 +29,117 @@ win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} [package.extras] dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "mypy (==v1.5.1)", "pre-commit (==3.4.0)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"] +[[package]] +name = "pillow" +version = "10.4.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, + {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"}, + {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"}, + {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"}, + {file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"}, + {file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"}, + {file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"}, + {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, + {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"}, + {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"}, + {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"}, + {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"}, + {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"}, + {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"}, + {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"}, + {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"}, + {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"}, + {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"}, + {file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"}, + {file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"}, + {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"}, + {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"}, + {file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"}, + {file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"}, + {file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"}, + {file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"}, + {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"}, + {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"}, + {file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"}, + {file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"}, + {file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"}, + {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] + +[[package]] +name = "ttkbootstrap" +version = "1.10.1" +description = "A supercharged theme extension for tkinter that enables on-demand modern flat style themes inspired by Bootstrap." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ttkbootstrap-1.10.1-py3-none-any.whl", hash = "sha256:c6a10ee5ea64051a0f8b75c20ef8e5f2417c62b4d0858c831683d4f2135aa573"}, + {file = "ttkbootstrap-1.10.1.tar.gz", hash = "sha256:53925525c4104f9e345627500dced2d0392ad74324b2a81467aaee3ffbe1a474"}, +] + +[package.dependencies] +pillow = ">=8.2.0" + [[package]] name = "win32-setctime" version = "1.1.0" @@ -46,4 +157,4 @@ dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "c712c408c2953a9f08da9983d1196217452da666df3a171c2b1bde109b9d6d71" +content-hash = "f334973e6f1991a62c6662db64c6e0b45c0faf45b5e9e5155db86f4e7d06c646" diff --git a/pyproject.toml b/pyproject.toml index 0db33e4..bdd1708 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,30 +6,33 @@ authors = ["Aareon Sullivan "] license = "MIT" readme = "README.md" packages = [{ include = "tkloguru" }] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12" +] [tool.poetry.dependencies] python = "^3.10" loguru = "^0.7.2" +[tool.poetry.group.dev.dependencies] +ttkbootstrap = "^1.10.1" + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.poetry.urls] "Homepage" = "https://github.com/Aareon/TkLoguru" -"Bug Tracker" = "https://github.com/Aareon/TkLoguru/issues" - -[tool.poetry.classifiers] -"Development Status" = "3 - Alpha" -"Intended Audience" = "Developers" -"License" = "MIT" -"Operating System" = "OS Independent" -"Programming Language :: Python" = "3" -"Programming Language :: Python :: 3.6" -"Programming Language :: Python :: 3.7" -"Programming Language :: Python :: 3.8" -"Programming Language :: Python :: 3.9" -"Programming Language :: Python :: 3.10" -"Programming Language :: Python :: 3.11" -"Programming Language :: Python :: 3.12" +"Bug Tracker" = "https://github.com/Aareon/TkLoguru/issues" \ No newline at end of file From efce9e79b3f7e7eb1c01dd145b851050ba2611c1 Mon Sep 17 00:00:00 2001 From: Aareon Sullivan Date: Tue, 3 Sep 2024 11:51:30 -0500 Subject: [PATCH 4/8] Update example usage --- examples/ttkbootstrap-example.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/examples/ttkbootstrap-example.py b/examples/ttkbootstrap-example.py index 3fffbc7..603a48c 100644 --- a/examples/ttkbootstrap-example.py +++ b/examples/ttkbootstrap-example.py @@ -49,6 +49,15 @@ def create_widgets(self): ) self.continuous_log_btn.pack(fill=X, pady=5) + # Add a button to change log level + self.change_level_btn = ttk.Button( + button_frame, + text="Change Log Level", + command=self.change_log_level, + style="secondary.Outline.TButton" + ) + self.change_level_btn.pack(fill=X, pady=5) + def log_message(self, level): log_func = getattr(logger, level) log_func(f"This is a {level} message") @@ -70,6 +79,23 @@ def continuous_log(self): count += 1 time.sleep(0.5) + def change_log_level(self): + levels = ["TRACE", "DEBUG", "INFO", "SUCCESS", "WARNING", "ERROR", "CRITICAL"] + level_no_to_name = {5: "TRACE", 10: "DEBUG", 20: "INFO", 25: "SUCCESS", 30: "WARNING", 40: "ERROR", 50: "CRITICAL"} + + current_level_no = logger._core.min_level + current_level = level_no_to_name.get(current_level_no, "INFO") # Default to INFO if level is not found + + current_index = levels.index(current_level) + new_index = (current_index + 1) % len(levels) + new_level = levels[new_index] + + self.log_widget.set_logging_level(new_level) + + # Use the appropriate logging function based on the new level + log_func = getattr(logger, new_level.lower()) + log_func(f"Changed logging level from {current_level} to: {new_level}") + if __name__ == "__main__": app = TkLoguruDemo() - app.mainloop() + app.mainloop() \ No newline at end of file From 35e0f1bb1ad9c8ba349b4cb5668843bc36d047e8 Mon Sep 17 00:00:00 2001 From: Aareon Sullivan Date: Tue, 3 Sep 2024 12:22:59 -0500 Subject: [PATCH 5/8] Rename example --- examples/{ttkbootstrap-example.py => ttkbootstrap_example.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{ttkbootstrap-example.py => ttkbootstrap_example.py} (100%) diff --git a/examples/ttkbootstrap-example.py b/examples/ttkbootstrap_example.py similarity index 100% rename from examples/ttkbootstrap-example.py rename to examples/ttkbootstrap_example.py From f8fbf9990397b55188b3e035600c5260c70b161e Mon Sep 17 00:00:00 2001 From: Aareon Sullivan Date: Tue, 3 Sep 2024 12:24:26 -0500 Subject: [PATCH 6/8] Intercept `logging`. Add support for `grid` and `pack`. Configurable level colors. Improved documentation and example usage. --- tkloguru/__init__.py | 270 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 213 insertions(+), 57 deletions(-) diff --git a/tkloguru/__init__.py b/tkloguru/__init__.py index 56cfc05..a3fba65 100644 --- a/tkloguru/__init__.py +++ b/tkloguru/__init__.py @@ -4,87 +4,134 @@ from loguru import logger import sys import threading +import logging +from datetime import datetime -class LoguruWidget(ttk.Frame): +LEVELS = ["TRACE", "DEBUG", "INFO", "SUCCESS", "WARNING", "ERROR", "CRITICAL"] +LEVEL_NO_TO_NAME = {5: "TRACE", 10: "DEBUG", 20: "INFO", 25: "SUCCESS", 30: "WARNING", 40: "ERROR", 50: "CRITICAL"} + +class LoggingInterceptHandler(logging.Handler): """ - A custom Tkinter widget for displaying Loguru log messages. + A custom logging handler that intercepts standard logging messages and redirects them to a tkinter widget. - This widget creates a text area with optional scrollbar to display - log messages from Loguru. It supports different color modes and - can limit the number of displayed lines. + This handler is used to capture messages from the standard logging module and display them in a custom tkinter widget. - Attributes: - queue (Queue): A thread-safe queue for storing log records. - show_scrollbar (bool): Whether to show a scrollbar or not. - color_mode (str): The coloring mode for log messages. - max_lines (int): Maximum number of lines to display in the widget. + Args: + widget (LoguruWidget): The tkinter widget to which the log messages will be sent. """ - def __init__(self, master=None, show_scrollbar=True, color_mode='level', max_lines=1000, **kwargs): + def __init__(self, widget): + super().__init__() + self.widget = widget + + def emit(self, record): """ - Initialize the LoguruWidget. + Emit a log record by formatting it and sending it to the associated widget. Args: - master: The parent widget. - show_scrollbar (bool): Whether to show a scrollbar. - color_mode (str): The coloring mode ('level', 'message', or 'full'). - max_lines (int): Maximum number of lines to display. - **kwargs: Additional keyword arguments for the ttk.Frame. + record (logging.LogRecord): The log record to be emitted. """ + try: + msg = self.format(record) + level = record.levelname + self.widget.queue.put({"time": datetime.fromtimestamp(record.created), "level": level, "message": msg}) + except Exception: + self.handleError(record) + +class LoguruWidget(ttk.Frame): + """ + A custom tkinter widget for displaying log messages using the loguru library. + + This widget creates a text area where log messages are displayed with customizable colors and formatting. + + Args: + master (tk.Widget, optional): The parent widget. Defaults to None. + show_scrollbar (bool, optional): Whether to show a scrollbar. Defaults to True. + color_mode (str, optional): The coloring mode for log messages. Can be 'level', 'message', or 'full'. Defaults to 'level'. + max_lines (int, optional): Maximum number of lines to display before starting to remove old ones. Defaults to 1000. + intercept_logging (bool, optional): Whether to intercept messages from the standard logging module. Defaults to False. + **kwargs: Additional keyword arguments to pass to the ttk.Frame constructor. + """ + + def __init__(self, master=None, show_scrollbar=True, color_mode='level', max_lines=1000, intercept_logging=False, **kwargs): super().__init__(master, **kwargs) + self.master = master self.queue = queue.Queue() self.show_scrollbar = show_scrollbar self.color_mode = color_mode self.max_lines = max_lines + self.intercept_logging = intercept_logging + self.log_colors = { + "TRACE": "#999999", + "DEBUG": "#4a4a4a", + "INFO": "#3498db", + "SUCCESS": "#2ecc71", + "WARNING": "#f39c12", + "ERROR": "#e74c3c", + "CRITICAL": ("#ffffff", "#c0392b") + } + self._layout_manager = None self.create_widgets() + self._is_destroyed = False self.after(100, self.check_queue) def create_widgets(self): - """Create and configure the Text widget and optional Scrollbar.""" + """Create and configure the text widget and scrollbar.""" self.text = tk.Text(self, wrap=tk.WORD, state=tk.DISABLED) if self.show_scrollbar: self.scrollbar = ttk.Scrollbar(self, orient="vertical", command=self.text.yview) self.text.configure(yscrollcommand=self.scrollbar.set) - self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) - - self.text.pack(expand=True, fill=tk.BOTH) - # Configure tags for different log levels with colors - self.text.tag_configure("DEBUG", foreground="#4a4a4a") # Dark gray - self.text.tag_configure("INFO", foreground="#3498db") # Blue - self.text.tag_configure("SUCCESS", foreground="#2ecc71") # Green - self.text.tag_configure("WARNING", foreground="#f39c12") # Orange - self.text.tag_configure("ERROR", foreground="#e74c3c") # Red - self.text.tag_configure("CRITICAL", foreground="white", background="#c0392b") # White on dark red + self.update_tag_colors() + + def _configure_layout(self): + """Configure the layout of child widgets based on the chosen geometry manager.""" + if self._layout_manager == "pack": + if self.show_scrollbar: + self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + self.text.pack(side=tk.LEFT, expand=True, fill=tk.BOTH) + elif self._layout_manager == "grid": + self.grid_rowconfigure(0, weight=1) + self.grid_columnconfigure(0, weight=1) + self.text.grid(row=0, column=0, sticky="nsew") + if self.show_scrollbar: + self.scrollbar.grid(row=0, column=1, sticky="ns") + self.grid_columnconfigure(1, weight=0) - def check_queue(self): - """ - Check the queue for new log records and update the widget. + def update_tag_colors(self): + """Update the color tags for different log levels in the text widget.""" + for level, color in self.log_colors.items(): + if isinstance(color, tuple): + self.text.tag_configure(level, foreground=color[0], background=color[1]) + else: + self.text.tag_configure(level, foreground=color) - This method is called periodically to process new log records - from the queue and update the text widget. - """ + def check_queue(self): + """Check the queue for new log messages and update the widget.""" + if self._is_destroyed: + return try: - while self.queue.qsize(): + while True: record = self.queue.get_nowait() self.update_widget(record) except queue.Empty: pass finally: - self.after(100, self.check_queue) + if not self._is_destroyed: + self.after(100, self.check_queue) def update_widget(self, record): """ - Update the text widget with a new log record. + Update the text widget with a new log message. Args: - record (dict): A dictionary containing log record information. + record (dict): A dictionary containing the log record information. """ self.text.configure(state=tk.NORMAL) time_str = record["time"].strftime("%Y-%m-%d %H:%M:%S") - level = record["level"].name + level = record["level"] message = record["message"] if self.color_mode == 'full': @@ -97,7 +144,6 @@ def update_widget(self, record): self.text.insert(tk.END, f"{level:8}", level) self.text.insert(tk.END, f" | {message}\n", "") - # Limit the number of lines in the widget if int(self.text.index('end-1c').split('.')[0]) > self.max_lines: self.text.delete('1.0', '2.0') @@ -106,38 +152,110 @@ def update_widget(self, record): def sink(self, message): """ - Add a log message to the queue. + A sink function to be used with loguru. - This method is used as a sink for Loguru to add log records - to the queue for later processing. + This method is called by loguru for each log message and puts the message into the queue. Args: - message: A Loguru message object. + message (loguru.Message): The log message object from loguru. """ record = message.record - self.queue.put(record) + self.queue.put({ + "time": record["time"], + "level": record["level"].name, + "message": record["message"] + }) + + def set_color(self, level, color): + """ + Set the color for a specific log level. + + Args: + level (str): The log level to set the color for. + color (str or tuple): The color to set. Can be a string for foreground color only, + or a tuple of (foreground, background) colors. + """ + self.log_colors[level] = color + self.update_tag_colors() + + @staticmethod + def get_logging_level(): + """ + Get the current logging level. + + Returns: + str: The name of the current logging level. + """ + current_level_no = logger._core.min_level + current_level = LEVEL_NO_TO_NAME.get(current_level_no, "INFO") # Default to INFO if level is not found + return current_level + + def set_logging_level(self, level): + """ + Set the logging level for the loguru logger. + + Args: + level (str): The name of the logging level to set. + """ + logger.remove() + logger.add(self.sink, level=level) + + def pack(self, **kwargs): + """Pack the widget and its children.""" + if self._layout_manager is None: + self._layout_manager = "pack" + self._configure_layout() + super().pack(**kwargs) + + def grid(self, **kwargs): + """Grid the widget and its children.""" + if self._layout_manager is None: + self._layout_manager = "grid" + self._configure_layout() + super().grid(**kwargs) + + def place(self, **kwargs): + """Place the widget and configure its children for pack layout.""" + if self._layout_manager is None: + self._layout_manager = "pack" + self._configure_layout() + super().place(**kwargs) + + def destroy(self): + """Destroy the widget and stop the queue checking.""" + self._is_destroyed = True + super().destroy() + + def process_all_events(self): + """Process all pending events in the Tkinter event loop.""" + while self.master.dooneevent(tk._tkinter.ALL_EVENTS | tk._tkinter.DONT_WAIT): + pass + self.update() def setup_logger(widget): """ - Set up the Loguru logger to use the LoguruWidget. + Set up the loguru logger to use the custom widget as a sink. - This function removes all existing Loguru handlers and adds - the LoguruWidget as a new handler. + If intercept_logging is True, also set up interception of standard logging messages. Args: - widget (LoguruWidget): The LoguruWidget instance to use as a handler. + widget (LoguruWidget): The widget to use as a sink for log messages. """ logger.remove() logger.add(widget.sink, backtrace=True, diagnose=True) + + if widget.intercept_logging: + logging.getLogger().addHandler(LoggingInterceptHandler(widget)) + logging.getLogger().setLevel(logging.DEBUG) + # Example usage if __name__ == "__main__": root = tk.Tk() - root.title("Thread-Safe Loguru Tkinter Widget") - root.geometry("600x400") + root.title("LoguruWidget Example") + root.geometry("800x600") - # Choose color_mode from: 'level', 'message', or 'full' - log_widget = LoguruWidget(root, show_scrollbar=True, color_mode='level', max_lines=1000) + log_widget = LoguruWidget(root, show_scrollbar=True, color_mode='level', max_lines=1000, intercept_logging=True) log_widget.pack(expand=True, fill=tk.BOTH, padx=10, pady=10) setup_logger(log_widget) @@ -145,16 +263,54 @@ def setup_logger(widget): # Add console handler for debugging logger.add(sys.stdout, level="DEBUG") - def generate_logs(): - """Generate example log messages of various levels.""" + def generate_sample_logs(): + """Generate sample log messages for demonstration purposes.""" logger.debug("This is a debug message") logger.info("This is an info message") logger.success("This is a success message") logger.warning("This is a warning message") logger.error("This is an error message") logger.critical("This is a critical message") + + # Test standard logging + logging.debug("Standard logging: debug message") + logging.info("Standard logging: info message") + logging.warning("Standard logging: warning message") + logging.error("Standard logging: error message") + logging.critical("Standard logging: critical message") + + # Create buttons to generate logs and change settings + button_frame = ttk.Frame(root) + button_frame.pack(fill=tk.X, padx=10, pady=5) + + generate_logs_button = ttk.Button(button_frame, text="Generate Sample Logs", command=generate_sample_logs) + generate_logs_button.pack(side=tk.LEFT, padx=5) + + def change_color_mode(): + """Change the color mode of the log widget.""" + current_mode = log_widget.color_mode + new_mode = 'full' if current_mode == 'level' else 'level' if current_mode == 'message' else 'message' + log_widget.color_mode = new_mode + + current_level = log_widget.get_logging_level() + log_func = getattr(logger, current_level.lower()) + log_func(f"Changed color mode to: {new_mode}") + + color_mode_button = ttk.Button(button_frame, text="Change Color Mode", command=change_color_mode) + color_mode_button.pack(side=tk.LEFT, padx=5) + + def change_log_level(): + """Change the logging level of the log widget.""" + current_level = log_widget.get_logging_level() + current_index = LEVELS.index(current_level) + new_index = (current_index + 1) % len(LEVELS) + new_level = LEVELS[new_index] + + log_widget.set_logging_level(new_level) + log_func = getattr(logger, new_level.lower()) + log_func(f"Changed logging level from {current_level} to: {new_level}") - # Run log generation in a separate thread - threading.Thread(target=generate_logs).start() + log_level_button = ttk.Button(button_frame, text="Change Log Level", command=change_log_level) + log_level_button.pack(side=tk.LEFT, padx=5) root.mainloop() \ No newline at end of file From 902a1b7a7498ac8e312d1025fdaaa3c0e9e75b3e Mon Sep 17 00:00:00 2001 From: Aareon Sullivan Date: Tue, 3 Sep 2024 12:24:44 -0500 Subject: [PATCH 7/8] Add examples of usage for different layout managers --- examples/grid_example.py | 98 ++++++++++++++++++++++++++++++++++++++++ examples/pack_example.py | 91 +++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 examples/grid_example.py create mode 100644 examples/pack_example.py diff --git a/examples/grid_example.py b/examples/grid_example.py new file mode 100644 index 0000000..421172e --- /dev/null +++ b/examples/grid_example.py @@ -0,0 +1,98 @@ +import tkinter as tk +from tkinter import ttk +from loguru import logger +import sys + +# Assuming the LoguruWidget class is in a file named loguru_widget.py +from tkloguru import LoguruWidget, setup_logger + +class LoguruWidgetGridExample: + """ + An example application demonstrating the use of LoguruWidget with grid layout. + """ + + def __init__(self, root): + """ + Initialize the example application. + + Args: + root (tk.Tk): The root window of the application. + """ + self.root = root + self.root.title("LoguruWidget Grid Example") + self.root.geometry("800x600") + + self.create_widgets() + self.setup_logging() + + def create_widgets(self): + """Create and arrange the widgets using grid layout.""" + self.root.grid_columnconfigure(0, weight=1) + self.root.grid_rowconfigure(0, weight=1) + self.root.grid_rowconfigure(1, weight=0) + + # Create a frame for the log widget + log_frame = ttk.Frame(self.root) + log_frame.grid(row=0, column=0, sticky="nsew", padx=10, pady=(10, 5)) + log_frame.grid_columnconfigure(0, weight=1) + log_frame.grid_rowconfigure(0, weight=1) + + # Create the LoguruWidget + self.log_widget = LoguruWidget(log_frame, show_scrollbar=True, color_mode='level', max_lines=1000) + self.log_widget.grid(row=0, column=0, sticky="nsew") + + # Create a frame for buttons + button_frame = ttk.Frame(self.root) + button_frame.grid(row=1, column=0, sticky="ew", padx=10, pady=5) + button_frame.grid_columnconfigure((0, 1, 2), weight=1) + + # Create buttons + generate_logs_button = ttk.Button(button_frame, text="Generate Sample Logs", command=self.generate_sample_logs) + generate_logs_button.grid(row=0, column=0, padx=5) + + change_color_mode_button = ttk.Button(button_frame, text="Change Color Mode", command=self.change_color_mode) + change_color_mode_button.grid(row=0, column=1, padx=5) + + change_log_level_button = ttk.Button(button_frame, text="Change Log Level", command=self.change_log_level) + change_log_level_button.grid(row=0, column=2, padx=5) + + def setup_logging(self): + """Set up the logger to use the LoguruWidget.""" + setup_logger(self.log_widget) + logger.add(sys.stdout, level="DEBUG") # Add console handler for debugging + + def generate_sample_logs(self): + """Generate sample log messages for demonstration purposes.""" + logger.debug("This is a debug message") + logger.info("This is an info message") + logger.success("This is a success message") + logger.warning("This is a warning message") + logger.error("This is an error message") + logger.critical("This is a critical message") + + def change_color_mode(self): + """Change the color mode of the log widget.""" + current_mode = self.log_widget.color_mode + new_mode = 'full' if current_mode == 'level' else 'level' if current_mode == 'message' else 'message' + self.log_widget.color_mode = new_mode + + logger.info(f"Changed color mode to: {new_mode}") + + def change_log_level(self): + """Change the logging level of the log widget.""" + current_level = self.log_widget.get_logging_level() + levels = ["TRACE", "DEBUG", "INFO", "SUCCESS", "WARNING", "ERROR", "CRITICAL"] + current_index = levels.index(current_level) + new_index = (current_index + 1) % len(levels) + new_level = levels[new_index] + + self.log_widget.set_logging_level(new_level) + logger.log(new_level, f"Changed logging level from {current_level} to: {new_level}") + +def main(): + root = tk.Tk() + app = LoguruWidgetGridExample(root) + root.mainloop() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/examples/pack_example.py b/examples/pack_example.py new file mode 100644 index 0000000..c2025d9 --- /dev/null +++ b/examples/pack_example.py @@ -0,0 +1,91 @@ +import tkinter as tk +from tkinter import ttk +from loguru import logger +import sys + +# Assuming the LoguruWidget class is in a file named loguru_widget.py +from tkloguru import LoguruWidget, setup_logger + +class LoguruWidgetPackExample: + """ + An example application demonstrating the use of LoguruWidget with pack layout. + """ + + def __init__(self, root): + """ + Initialize the example application. + + Args: + root (tk.Tk): The root window of the application. + """ + self.root = root + self.root.title("LoguruWidget Pack Example") + self.root.geometry("800x600") + + self.create_widgets() + self.setup_logging() + + def create_widgets(self): + """Create and arrange the widgets using pack layout.""" + # Create a frame for the log widget + log_frame = ttk.Frame(self.root) + log_frame.pack(expand=True, fill=tk.BOTH, padx=10, pady=(10, 5)) + + # Create the LoguruWidget + self.log_widget = LoguruWidget(log_frame, show_scrollbar=True, color_mode='level', max_lines=1000) + self.log_widget.pack(expand=True, fill=tk.BOTH) + + # Create a frame for buttons + button_frame = ttk.Frame(self.root) + button_frame.pack(fill=tk.X, padx=10, pady=5) + + # Create buttons + generate_logs_button = ttk.Button(button_frame, text="Generate Sample Logs", command=self.generate_sample_logs) + generate_logs_button.pack(side=tk.LEFT, padx=5) + + change_color_mode_button = ttk.Button(button_frame, text="Change Color Mode", command=self.change_color_mode) + change_color_mode_button.pack(side=tk.LEFT, padx=5) + + change_log_level_button = ttk.Button(button_frame, text="Change Log Level", command=self.change_log_level) + change_log_level_button.pack(side=tk.LEFT, padx=5) + + def setup_logging(self): + """Set up the logger to use the LoguruWidget.""" + setup_logger(self.log_widget) + logger.add(sys.stdout, level="DEBUG") # Add console handler for debugging + + def generate_sample_logs(self): + """Generate sample log messages for demonstration purposes.""" + logger.debug("This is a debug message") + logger.info("This is an info message") + logger.success("This is a success message") + logger.warning("This is a warning message") + logger.error("This is an error message") + logger.critical("This is a critical message") + + def change_color_mode(self): + """Change the color mode of the log widget.""" + current_mode = self.log_widget.color_mode + new_mode = 'full' if current_mode == 'level' else 'level' if current_mode == 'message' else 'message' + self.log_widget.color_mode = new_mode + + logger.info(f"Changed color mode to: {new_mode}") + + def change_log_level(self): + """Change the logging level of the log widget.""" + current_level = self.log_widget.get_logging_level() + levels = ["TRACE", "DEBUG", "INFO", "SUCCESS", "WARNING", "ERROR", "CRITICAL"] + current_index = levels.index(current_level) + new_index = (current_index + 1) % len(levels) + new_level = levels[new_index] + + self.log_widget.set_logging_level(new_level) + logger.log(new_level, f"Changed logging level from {current_level} to: {new_level}") + +def main(): + root = tk.Tk() + app = LoguruWidgetPackExample(root) + root.mainloop() + +if __name__ == "__main__": + main() \ No newline at end of file From ff2d9fc371d1517eba287b27bb5ba4ae8cf471b5 Mon Sep 17 00:00:00 2001 From: Aareon Sullivan Date: Tue, 3 Sep 2024 12:27:16 -0500 Subject: [PATCH 8/8] Bump version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index bdd1708..79c821f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "tkloguru" -version = "0.1.1" +version = "0.1.2" description = "A custom Tkinter widget that displays Loguru log messages in real-time with support for styling and colors. Monitor application logs directly within your Tkinter GUI." authors = ["Aareon Sullivan "] license = "MIT"