<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Mouse Vs Python</title>
	<atom:link href="https://blog.pythonlibrary.org/feed/" rel="self" type="application/rss+xml" />
	<link>https://papayawhip-oyster-325761.hostingersite.com/</link>
	<description>Where You Can Learn All About Python Programming</description>
	<lastBuildDate>Tue, 14 Jul 2026 13:09:27 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.2</generator>

<image>
	<url>https://blog.pythonlibrary.org/wp-content/uploads/2019/05/cropped-narnyish_highres-32x32.png</url>
	<title>Mouse Vs Python</title>
	<link>https://papayawhip-oyster-325761.hostingersite.com/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Python 101 3rd Edition Kickstarter Launch</title>
		<link>https://blog.pythonlibrary.org/2026/07/14/python-101-3rd-edition-kickstarter-launch/</link>
		
		<dc:creator><![CDATA[Mike]]></dc:creator>
		<pubDate>Tue, 14 Jul 2026 13:05:15 +0000</pubDate>
				<category><![CDATA[beginner]]></category>
		<category><![CDATA[Books]]></category>
		<category><![CDATA[Education]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://blog.pythonlibrary.org/?p=12811</guid>

					<description><![CDATA[<p>Learn Python with a Book Designed for Beginners Hello! I&#8217;m Michael Driscoll, and I&#8217;m excited to announce the third edition of Python 101. For years, Python 101 has helped readers take their first steps into programming. Whether you&#8217;re a complete beginner, a student, or someone looking to learn Python for work or personal projects, the [&#8230;]</p>
<p>The post <a href="https://blog.pythonlibrary.org/2026/07/14/python-101-3rd-edition-kickstarter-launch/">Python 101 3rd Edition Kickstarter Launch</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h4>Learn Python with a Book Designed for Beginners</h4>
<p>Hello! I&#8217;m Michael Driscoll, and I&#8217;m excited to announce the third edition of <a href="https://www.kickstarter.com/projects/driscollis/python-101-3rd-edition"><strong>Python 101</strong></a>.</p>
<p><img fetchpriority="high" decoding="async" class="aligncenter size-full wp-image-12815" src="https://blog.pythonlibrary.org/wp-content/uploads/2026/07/py101_kick_3rd.png" alt="" width="715" height="398" srcset="https://blog.pythonlibrary.org/wp-content/uploads/2026/07/py101_kick_3rd.png 715w, https://blog.pythonlibrary.org/wp-content/uploads/2026/07/py101_kick_3rd-300x167.png 300w" sizes="(max-width: 715px) 100vw, 715px" /></p>
<p>For years, <i>Python 101</i> has helped readers take their first steps into programming. Whether you&#8217;re a complete beginner, a student, or someone looking to learn Python for work or personal projects, the goal has always been the same: make learning Python approachable, practical, and enjoyable.</p>
<p>Python has continued to evolve, and it&#8217;s time for the book to evolve with it. This <a href="https://www.kickstarter.com/projects/driscollis/python-101-3rd-edition">Kickstarter</a> will help fund the writing, editing, production, and publication of the fully updated third edition.</p>
<h3>Why a Third Edition?</h3>
<p>Python has changed significantly since the previous edition. New language features, updated best practices, and improvements throughout the ecosystem make this the right time for a comprehensive update.</p>
<p>The third edition will include:</p>
<ul>
<li>Updated examples compatible with modern versions of Python</li>
<li>New coverage of current Python features and best practices</li>
<li>Improved code samples and exercises (when applicable)</li>
<li>Corrections and refinements based on reader feedback</li>
</ul>
<p>The goal isn&#8217;t simply to revise the previous edition—it&#8217;s to create the best beginner-focused version of <i>Python 101</i> yet.</p>
<h3>Who Is This Book For?</h3>
<p>This book is designed for:</p>
<ul>
<li>Complete beginners with no programming experience</li>
<li>Students learning Python</li>
<li>Self-taught developers</li>
<li>Professionals transitioning into software development</li>
<li>Anyone looking for a clear, practical introduction to Python</li>
</ul>
<p>No prior programming knowledge is required.</p>
<h3>Why Kickstarter?</h3>
<p>Kickstarter allows readers to directly support the creation of the book while helping ensure it receives the time and attention needed for a high-quality release.</p>
<p>Funding will help cover:</p>
<ul>
<li>Writing</li>
<li>Ebook production</li>
<li>Print preparation</li>
<li>Kickstarter fulfillment and shipping materials</li>
<li>Any cover refinements</li>
</ul>
<p>Most importantly, it allows me to focus on creating the strongest edition possible.</p>
<h3>Rewards</h3>
<p>Depending on your pledge level, rewards may include:</p>
<ul>
<li>DRM-free PDF edition</li>
<li>EPUB and Kindle editions</li>
<li>Printed paperback</li>
<li>Signed copies</li>
<li>Early access to chapters during development</li>
<li>Exclusive Kickstarter updates</li>
</ul>
<p>Project updates will be shared throughout the process so backers can follow the book&#8217;s progress.</p>
<h3>Thank You</h3>
<p>Whether you back this project or simply share it with others, your support means a great deal.</p>
<p>Writing technical books is a collaborative effort between authors and readers. Your encouragement has helped make previous editions successful, and I&#8217;m excited to bring an updated <i>Python 101</i> to the next generation of Python learners.</p>
<p>Thank you for helping make the third edition possible.</p>
<h2 style="text-align: center;"><a href="https://www.kickstarter.com/projects/driscollis/python-101-3rd-edition">Back the project</a></h2>
<p>The post <a href="https://blog.pythonlibrary.org/2026/07/14/python-101-3rd-edition-kickstarter-launch/">Python 101 3rd Edition Kickstarter Launch</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>An Intro to Spiel &#8211; Creating Presentations in Your Terminal with Python</title>
		<link>https://blog.pythonlibrary.org/2026/07/10/an-intro-to-spiel-creating-presentations-in-your-terminal-with-python/</link>
		
		<dc:creator><![CDATA[Mike]]></dc:creator>
		<pubDate>Fri, 10 Jul 2026 15:13:38 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://blog.pythonlibrary.org/?p=12700</guid>

					<description><![CDATA[<p>Have you ever wanted to create a presentation in your computer&#8217;s terminal? While this is an uncommon need, a clever open source developer has provided a solution to this problem! The project is called Spiel, and while it is currently archived, the idea is pretty cool. Spiel uses the Rich package to create the slides [&#8230;]</p>
<p>The post <a href="https://blog.pythonlibrary.org/2026/07/10/an-intro-to-spiel-creating-presentations-in-your-terminal-with-python/">An Intro to Spiel &#8211; Creating Presentations in Your Terminal with Python</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Have you ever wanted to create a presentation in your computer&#8217;s terminal? While this is an uncommon need, a clever open source developer has provided a solution to this problem! The project is called <a href="https://github.com/JoshKarpel/spiel">Spiel</a>, and while it is currently archived, the idea is pretty cool. Spiel uses the <a href="https://github.com/Textualize/rich">Rich</a> package to create the slides for your presentation. Note: while the GitHub page doesn&#8217;t explain why the project is archived, it appears to use a very old version of Textual which cannot be upgraded.</p>
<p>Let&#8217;s spend a little time learning how this all works.</p>
<h2>Installing Spiel</h2>
<p>According to the Spiel GitHub page, you can try Spiel without even installing it if you have docker installed. Here&#8217;s how to try Spiel:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">$ docker run -it --rm ghcr.io/joshkarpel/spiel</pre>
<p>However, for the purposes of this tutorial, you really should install Spiel. To do that, you will be using pip. Open up your terminal and run the following:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">pip install spiel</pre>
<p>Feel free to create a Python virtual environment first if you don&#8217;t want to install Spiel into your global Python packages.</p>
<p>Once you have Spiel installed, you can check that it is working by running the Spiel demo, like this:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">spiel demo present</pre>
<p>If that works, you are good to go!</p>
<h2>Creating Your Presentation</h2>
<p>The documentation gives a good example of how to create a one-slide presentation. Here&#8217;s their example:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">from rich.console import RenderableType

from spiel import Deck, present

deck = Deck(name="Your Deck Name")


@deck.slide(title="Slide 1 Title")
def slide_1() -&gt; RenderableType:
    return "Your content here!"


if __name__ == "__main__":
    present(__file__)</pre>
<p>According to the <a href="https://www.spiel.how/slides/">documentation</a>, there are two ways to add slides:</p>
<ul>
<li>Use the decorator like in the example above</li>
<li>Use `<span class="n">deck</span><span class="o">.</span><span class="n">add_slides()` and pass in one or more Slide objects</span></li>
</ul>
<p>Here is a more complete example that creates a couple of custom slides:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">from rich.align import Align
from rich.console import RenderableType
from rich.style import Style
from rich.text import Text
from spiel import Deck, Slide, present


def make_slide(
    title_prefix: str,
    text: Text,
) -&gt; Slide:
    def content() -&gt; RenderableType:
        return Align(text, align="center", vertical="middle")
    return Slide(title=f"{title_prefix} Slide", content=content)

deck = Deck("Test Deck")

title_slide = make_slide(title_prefix="First", text=Text("Python 101 - All About Lists",
                                             style=Style(color="blue")))

intro_slide = make_slide(title_prefix="Second",
                    text=Text("A Python list is",
                              style=Style(color="red"))
                    )

deck.add_slides(title_slide, intro_slide)

if __name__ == "__main__":
    present(__file__)</pre>
<p>When you run this code in your terminal, you will see something like this:</p>
<p><img decoding="async" class="aligncenter size-large wp-image-12808" src="https://blog.pythonlibrary.org/wp-content/uploads/2026/07/spiel_slide-1024x547.png" alt="Spiel slide example" width="1024" height="547" srcset="https://blog.pythonlibrary.org/wp-content/uploads/2026/07/spiel_slide-1024x547.png 1024w, https://blog.pythonlibrary.org/wp-content/uploads/2026/07/spiel_slide-300x160.png 300w, https://blog.pythonlibrary.org/wp-content/uploads/2026/07/spiel_slide-768x410.png 768w, https://blog.pythonlibrary.org/wp-content/uploads/2026/07/spiel_slide.png 1108w" sizes="(max-width: 1024px) 100vw, 1024px" /></p>
<p>You can move to the next or previous slide using the arrow keys on your keyboard. If you want to exit, press CTRL+C.</p>
<h2>Wrapping Up</h2>
<p>Spiel seems like a neat package. It&#8217;s a shame that it is currently archived. Hopefully, the author will reopen it at some point, or someone else will pick up the torch. In the meantime, you can easily use it in a Python virtual environment and give it a try.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>The post <a href="https://blog.pythonlibrary.org/2026/07/10/an-intro-to-spiel-creating-presentations-in-your-terminal-with-python/">An Intro to Spiel &#8211; Creating Presentations in Your Terminal with Python</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>New Book Release: Python Typing</title>
		<link>https://blog.pythonlibrary.org/2026/07/08/new-book-release-python-typing/</link>
		
		<dc:creator><![CDATA[Mike]]></dc:creator>
		<pubDate>Wed, 08 Jul 2026 18:46:18 +0000</pubDate>
				<category><![CDATA[beginner]]></category>
		<category><![CDATA[Books]]></category>
		<category><![CDATA[Education]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://blog.pythonlibrary.org/?p=12802</guid>

					<description><![CDATA[<p>I am happy to announce that my latest book, Python Typing, is now available on all platforms. You can get your copy on Gumroad or Leanpub or Amazon Python has had type hinting support since Python 3.5, over TEN years ago! However, Python’s type annotations have changed repeatedly over the years. In Python Typing: Type Checking [&#8230;]</p>
<p>The post <a href="https://blog.pythonlibrary.org/2026/07/08/new-book-release-python-typing/">New Book Release: Python Typing</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p data-pm-slice="1 1 []">I am happy to announce that my latest book, <strong>Python Typing</strong>, is now available on all platforms. You can get your copy on <a href="https://driscollis.gumroad.com/l/pytyping" target="_blank" rel="noopener noreferrer nofollow">Gumroad</a> or <a href="https://leanpub.com/pythontyping" target="_blank" rel="noopener noreferrer nofollow">Leanpub</a> or <a href="https://www.amazon.com/dp/B0H5L5V9YZ" target="_blank" rel="noopener noreferrer nofollow">Amazon</a></p>
<p>Python has had type hinting support since Python 3.5, over TEN years ago! However, Python’s type annotations have changed repeatedly over the years. In <strong>Python Typing: Type Checking for Python Programmers</strong>, you will learn all you need to know to add type hints to your Python applications effectively.</p>
<p>You will also learn how to use Python type checkers, configure them, and set them up in pre-commit or GitHub Actions. This knowledge will give you the power to check your code and your team’s code automatically before merging, hopefully catching defects before they make it into your products.</p>
<p><a href="https://driscollis.gumroad.com/l/pytyping"><img decoding="async" class="aligncenter size-large wp-image-12749" src="https://blog.pythonlibrary.org/wp-content/uploads/2026/02/python_typing_cover-683x1024.webp" alt="Python Typing Book Cover" width="683" height="1024" srcset="https://blog.pythonlibrary.org/wp-content/uploads/2026/02/python_typing_cover-683x1024.webp 683w, https://blog.pythonlibrary.org/wp-content/uploads/2026/02/python_typing_cover-200x300.webp 200w, https://blog.pythonlibrary.org/wp-content/uploads/2026/02/python_typing_cover-768x1152.webp 768w, https://blog.pythonlibrary.org/wp-content/uploads/2026/02/python_typing_cover-1024x1536.webp 1024w, https://blog.pythonlibrary.org/wp-content/uploads/2026/02/python_typing_cover-1365x2048.webp 1365w, https://blog.pythonlibrary.org/wp-content/uploads/2026/02/python_typing_cover-scaled.webp 1707w" sizes="(max-width: 683px) 100vw, 683px" /></a></p>
<h2><strong>What You’ll Learn</strong></h2>
<p>You will learn all about Python’s support for type hinting (annotations). Specifically, you will learn about the following topics:</p>
<ul>
<li>Variable annotations</li>
<li>Function annotations</li>
<li>Type aliases</li>
<li>New types</li>
<li>Generics</li>
<li>Hinting callables</li>
<li>Annotating TypedDict</li>
<li>Annotating Decorators and Generators</li>
<li>Using Mypy for type checking</li>
<li>Mypy configuration</li>
<li>Using ty for type checking</li>
<li>ty configuration</li>
<li>and more!</li>
</ul>
<h2>Where to Purchase</h2>
<ul>
<li><a href="https://driscollis.gumroad.com/l/pytyping" target="_blank" rel="noopener noreferrer nofollow">Gumroad</a> (PDF / epub)</li>
<li><a href="https://leanpub.com/pythontyping" target="_blank" rel="noopener noreferrer nofollow">Leanpub</a> (PDF / epub)</li>
<li><a href="https://www.amazon.com/dp/B0H5L5V9YZ" target="_blank" rel="noopener noreferrer nofollow">Amazon</a> (Kindle / paperback)</li>
</ul>
<p>The post <a href="https://blog.pythonlibrary.org/2026/07/08/new-book-release-python-typing/">New Book Release: Python Typing</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Python eBook and Course Summer Sale</title>
		<link>https://blog.pythonlibrary.org/2026/06/29/python-ebook-and-course-summer-sale/</link>
		
		<dc:creator><![CDATA[Mike]]></dc:creator>
		<pubDate>Mon, 29 Jun 2026 17:38:15 +0000</pubDate>
				<category><![CDATA[Books]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://blog.pythonlibrary.org/?p=12799</guid>

					<description><![CDATA[<p>It&#8217;s officially summer, and I am bringing you some HOT Python deals today! Get 33% off almost all my books and courses on Gumroad today using the following H5N5F7K You can start learning the basics of Python with Python 101, or get more targeted learning with my book, Python Logging. If you want to create [&#8230;]</p>
<p>The post <a href="https://blog.pythonlibrary.org/2026/06/29/python-ebook-and-course-summer-sale/">Python eBook and Course Summer Sale</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p data-pm-slice="1 1 []">It&#8217;s officially summer, and I am bringing you some HOT Python deals today! Get 33% off almost all my books and courses on Gumroad today using the following <strong>H5N5F7K</strong></p>
<p>You can start learning the basics of Python with Python 101, or get more targeted learning with my book, Python Logging. If you want to create a user interface, then you might enjoy Creating TUI Applications with Textual and Python.</p>
<p>I have over a DOZEN Python books to choose from! Check them out today: <a href="https://driscollis.gumroad.com/" target="_blank" rel="noopener noreferrer nofollow">https://driscollis.gumroad.com/</a></p>
<p><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-12619" src="https://blog.pythonlibrary.org/wp-content/uploads/2025/07/all_books_2025-1024x588.jpg" alt="" width="1024" height="588" srcset="https://blog.pythonlibrary.org/wp-content/uploads/2025/07/all_books_2025-1024x588.jpg 1024w, https://blog.pythonlibrary.org/wp-content/uploads/2025/07/all_books_2025-300x172.jpg 300w, https://blog.pythonlibrary.org/wp-content/uploads/2025/07/all_books_2025-768x441.jpg 768w, https://blog.pythonlibrary.org/wp-content/uploads/2025/07/all_books_2025.jpg 1275w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></p>
<p>Plus even more that aren&#8217;t pictured here!</p>
<p>The post <a href="https://blog.pythonlibrary.org/2026/06/29/python-ebook-and-course-summer-sale/">Python eBook and Course Summer Sale</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Get TIFF MetaData with Python</title>
		<link>https://blog.pythonlibrary.org/2026/06/10/how-to-get-tiff-metadata-with-python/</link>
		
		<dc:creator><![CDATA[Mike]]></dc:creator>
		<pubDate>Wed, 10 Jun 2026 19:15:42 +0000</pubDate>
				<category><![CDATA[beginner]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Image Processing]]></category>
		<category><![CDATA[Pillow]]></category>
		<guid isPermaLink="false">https://blog.pythonlibrary.org/?p=12795</guid>

					<description><![CDATA[<p>In previous articles on this website, you learned how to extract EXIF data from JPG image files. This week, you will learn how to get similar data from the TIFF image format. The TIFF format also has its metadata. Pillow provides a similar dictionary for TIFF images in its TiffTags module. If you need a TIFF image, [&#8230;]</p>
<p>The post <a href="https://blog.pythonlibrary.org/2026/06/10/how-to-get-tiff-metadata-with-python/">How to Get TIFF MetaData with Python</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>In previous <a href="https://blog.pythonlibrary.org/2010/03/28/getting-photo-metadata-exif-using-python/">articles</a> on this website, you learned how to extract EXIF data from JPG image files. This week, you will learn how to get similar data from the TIFF image format.</p>
<p>The TIFF format also has its metadata. Pillow provides a similar dictionary for TIFF images in its <code>TiffTags</code> module. If you need a TIFF image, you can use this one, which is a cover from one of the author&#8217;s other books on <strong>ReportLab</strong>:</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-12796" src="https://blog.pythonlibrary.org/wp-content/uploads/2026/06/reportlab.png" alt="ReportLab cover" width="546" height="762" srcset="https://blog.pythonlibrary.org/wp-content/uploads/2026/06/reportlab.png 546w, https://blog.pythonlibrary.org/wp-content/uploads/2026/06/reportlab-215x300.png 215w" sizes="auto, (max-width: 546px) 100vw, 546px" /></p>
<p>You can create your own TIFF metadata extractor utility by making a new file named <code>tiff_metadata.py</code> and adding this code to it:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python"># tiff_metadata.py

from PIL import Image
from PIL.TiffTags import TAGS


def get_metadata(image_file_path):
    image = Image.open(image_file_path)
    metadata = {}
    for tag in image.tag.items():
        metadata[TAGS.get(tag[0])] = tag[1]
    return metadata


if __name__ == "__main__":
    metadata = get_metadata("reportlab_cover.tiff")
    print(metadata)</pre>
<p dir="auto">Here you import the <code>TAGS</code> dictionary from the <code>PIL.TiffTags</code> submodule. Then in <code>get_metadata()</code>, you access the tag elements in the image by iterating over the contents of <code>tag.items()</code>. To make that information more readable, you use the <code>TAGS</code> dictionary that you imported.</p>
<p dir="auto">Here is a sample of the output you will get when you run this code:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">{'ImageWidth': (400,),
 'ImageLength': (562,),
 'BitsPerSample': (8, 8, 8),
 'Compression': (1,),
 'PhotometricInterpretation': (2,),
 'FillOrder': (1,),
 'StripOffsets': (82, 130882, 261682, 392482, 523282, 654082),
 'Orientation': (1,),
 'SampleFormat': (1, 1, 1),
 'SamplesPerPixel': (3,),
 'RowsPerStrip': (109,),
 'StripByteCounts': (130800, 130800, 130800, 130800, 130800, 20400),
 'XResolution': ((300, 1),),
 'YResolution': ((300, 1),),
 'PlanarConfiguration': (1,),
 'ResolutionUnit': (2,),
 'ExifIFD': (8,),
 'Software': ('Pixelmator 3.9',),
 'DateTime': ('2020:10:27 12:10:37',),
}</pre>
<p>You can see that the value entries above are all tuples. This is because of how the data is returned from the tag data. If you would like a challenge, you can attempt to clean up this data a bit in your version of the metadata extraction utility.</p>
<h2 class="header-anchor-post">Wrapping Up</h2>
<div class="pencraft pc-display-flex pc-alignItems-center pc-position-absolute pc-reset header-anchor-parent">
<div class="pencraft pc-display-contents pc-reset pubTheme-yiXxQA">
<div id="§wrapping-up" class="pencraft pc-reset header-anchor offset-top"><span style="font-size: 16px;">EXIF and TIFF metadata are really useful for encoding lots of information in your images. However, most people don’t even know that data is there! Knowing how to access your photo’s metadata allows you to do all kinds of programmatic tasks, such as resizing, sorting files by various parameters, and much more.</span></div>
</div>
</div>
<p>You can use Pillow and Python to do all kinds of other image processing, so this is just scratching the surface. Download Pillow today and start learning!</p>
<h2 class="header-anchor-post"><strong>Want to Learn More?</strong></h2>
<div class="pencraft pc-display-flex pc-alignItems-center pc-position-absolute pc-reset header-anchor-parent">
<div class="pencraft pc-display-contents pc-reset pubTheme-yiXxQA"><span style="font-size: 16px;">You can learn more about what you can do with Python and Pillow in Mike’s book, </span><strong style="font-size: 16px;">Pillow: Image Processing with Python</strong></div>
</div>
<p>Purchase at <a href="https://driscollis.gumroad.com/l/pypillow">Gumroad</a>, <a href="https://leanpub.com/pillow">Leanpub</a>, or <a href="https://amzn.to/3ZvLfav">Amazon</a></p>
<p>The post <a href="https://blog.pythonlibrary.org/2026/06/10/how-to-get-tiff-metadata-with-python/">How to Get TIFF MetaData with Python</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Textual &#8211; An Intro to DOM Queries (Part II)</title>
		<link>https://blog.pythonlibrary.org/2026/05/06/textual-an-intro-to-dom-queries-part-ii/</link>
		
		<dc:creator><![CDATA[Mike]]></dc:creator>
		<pubDate>Wed, 06 May 2026 12:47:50 +0000</pubDate>
				<category><![CDATA[GUI Toolkits]]></category>
		<category><![CDATA[intermediate]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Textual]]></category>
		<category><![CDATA[TUIs]]></category>
		<guid isPermaLink="false">https://blog.pythonlibrary.org/?p=12789</guid>

					<description><![CDATA[<p>Last month, you learned the basics of Textual’s DOM queries. If you missed it, you can read the article now! In this tutorial you will be learning about the following topics: The DOMQuery object Getting the first or last widget Query filters Query exclusions Other query methods Let’s get started! The DOMQuery Object The DOMQuery object gets [&#8230;]</p>
<p>The post <a href="https://blog.pythonlibrary.org/2026/05/06/textual-an-intro-to-dom-queries-part-ii/">Textual &#8211; An Intro to DOM Queries (Part II)</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Last month, you learned the basics of Textual’s DOM queries. If you missed it, you can <a href="https://blog.pythonlibrary.org/2026/04/17/textual-an-intro-to-dom-queries-part-i/">read the article</a> now!</p>
<p>In this tutorial you will be learning about the following topics:</p>
<ul>
<li>The DOMQuery object</li>
<li>Getting the first or last widget</li>
<li>Query filters</li>
<li>Query exclusions</li>
<li>Other query methods</li>
</ul>
<p>Let’s get started!</p>
<h2 class="header-anchor-post"><strong>The DOMQuery Object</strong></h2>
<div class="pencraft pc-display-flex pc-alignItems-center pc-position-absolute pc-reset header-anchor-parent">
<div class="pencraft pc-display-contents pc-reset pubTheme-yiXxQA"><span style="font-size: 16px;"><br />
The </span><code style="font-size: 16px; font-style: inherit; font-weight: inherit;">DOMQuery</code><span style="font-size: 16px;"> object gets returned whenever you call </span><code style="font-size: 16px; font-style: inherit; font-weight: inherit;">query()</code><span style="font-size: 16px;">. The </span><code style="font-size: 16px; font-style: inherit; font-weight: inherit;">DOMQuery</code><span style="font-size: 16px;"> object works just like a Python list and supports all the same operations that you do with a list:</span></div>
</div>
<ul>
<li><code>query[1]</code></li>
<li><code>len(query)</code></li>
<li><code>reverse(query)</code></li>
<li>etc</li>
</ul>
<p>However, <code>DOMQuery</code> has additional methods of its very own. You can get a list of these methods using Python’s <code>dir()</code> function against the <code>DOMQuery</code> object.</p>
<p>To see this in action, create a new file named <code>domquery_methods.py</code> in your Python IDE and add this code:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python"># domquery_methods.py
import pprint

from textual.app import App, ComposeResult
from textual.widgets import Button, Label


class QueryApp(App):

    def compose(self) -&gt; ComposeResult:
        yield Label("Press a button", id="label")
        yield Button("Get DomQuery Methods", id="one")

    def on_button_pressed(self) -&gt; None:
        widgets = self.query("Button")
        s = ""
        s += f"{type(widgets)}\n"
        for entry in dir(widgets):
            s += f"{entry}\n"
        label = self.query_one("#label")
        label.update(s)


if __name__ == "__main__":
    app = QueryApp()
    app.run()</pre>
<p>Here, you loop over all the entries that <code>dir()</code> returns and add them to your string. Then, you update the <code>Label</code> as before.</p>
<p>When you run this code, you will see something like this:</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-12790" src="https://blog.pythonlibrary.org/wp-content/uploads/2026/05/domquery-1024x551.png" alt="Textual DOM query" width="1024" height="551" srcset="https://blog.pythonlibrary.org/wp-content/uploads/2026/05/domquery-1024x551.png 1024w, https://blog.pythonlibrary.org/wp-content/uploads/2026/05/domquery-300x161.png 300w, https://blog.pythonlibrary.org/wp-content/uploads/2026/05/domquery-768x413.png 768w, https://blog.pythonlibrary.org/wp-content/uploads/2026/05/domquery-1536x827.png 1536w, https://blog.pythonlibrary.org/wp-content/uploads/2026/05/domquery.png 2018w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></p>
<p>Let’s spend a few moments learning about the most helpful methods from this list!</p>
<h2 class="header-anchor-post"><strong>Getting the First or Last Widget</strong></h2>
<div class="pencraft pc-display-flex pc-alignItems-center pc-position-absolute pc-reset header-anchor-parent">
<div class="pencraft pc-display-contents pc-reset pubTheme-yiXxQA">
<div id="§getting-the-first-or-last-widget" class="pencraft pc-reset header-anchor offset-top"></div>
</div>
</div>
<p>Textual’s <code>DOMQuery</code> object has a couple of handy methods you can use to get the first or last matching widget. You may have noticed them in the output from the previous code example. They are called: <code>first()</code> and <code>last()</code>.</p>
<p>You can take the example code from earlier with three buttons and slightly update it to ask for the first and last widgets. For this example, you will name the file <code>first_and_last.py</code> and use this code:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python"># first_and_last.py

from textual.app import App, ComposeResult
from textual.widgets import Button, Label


class QueryApp(App):

    def compose(self) -&gt; ComposeResult:
        yield Label("Press a button", id="label")
        yield Button("One", id="one")
        yield Button("Two", id="two")
        yield Button("Three", id="three")

    def on_button_pressed(self) -&gt; None:
        widgets = self.query("Button")
        s = ""
        s += f"The first button: {widgets.first()}\n"
        s += f"The last button: {widgets.last()}"
        label = self.query_one("#label")
        label.update(s)


if __name__ == "__main__":
    app = QueryApp()
    app.run()</pre>
<p dir="auto">To make everything explicit, you add an <code>id</code> to all the <code>Button</code> widgets. Then, in <code>on_button_pressed()</code>, you grab the first and last widgets from the <code>DOMQuery</code> object and put them in the <code>Label</code>.</p>
<p dir="auto">When you run this code and press any of the buttons, you will see something like this:</p>
<p dir="auto"><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-12791" src="https://blog.pythonlibrary.org/wp-content/uploads/2026/05/first_last-1024x551.png" alt="Textual example" width="1024" height="551" srcset="https://blog.pythonlibrary.org/wp-content/uploads/2026/05/first_last-1024x551.png 1024w, https://blog.pythonlibrary.org/wp-content/uploads/2026/05/first_last-300x161.png 300w, https://blog.pythonlibrary.org/wp-content/uploads/2026/05/first_last-768x413.png 768w, https://blog.pythonlibrary.org/wp-content/uploads/2026/05/first_last-1536x827.png 1536w, https://blog.pythonlibrary.org/wp-content/uploads/2026/05/first_last.png 2018w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></p>
<p dir="auto">Of course, you don&#8217;t need to use <code>first()</code> and <code>last()</code> if you don&#8217;t want to because the <code>DOMQuery</code> object behaves like a list. You could use <code>widgets[0]</code> and <code>widgets[-1]</code> to get the first and last widgets if you want to instead.</p>
<p dir="auto">However, there are benefits to using these methods. For example, you can pass in an <code>expect_type</code> argument to <code>first()</code> and <code>last()</code>, making this even more explicit about what type you expect the first and last widgets to be.</p>
<p dir="auto">Here&#8217;s an example that expects the last widget to be a <code>Button</code>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">last_button = self.query().last(Button)</pre>
<p dir="auto">If the last widget is <strong>not</strong> a <code>Button</code>, you will receive a <code>WrongType</code> exception.</p>
<div class="markdown-heading" dir="auto">
<h2 class="heading-element" dir="auto" tabindex="-1">Query Filters</h2>
<p>&nbsp;</p>
</div>
<p dir="auto">Textual also provides a <code>filter()</code> method that you may use on your <code>DOMQuery</code> objects. As you might expect, the <code>filter()</code> is useful for getting subsets of widgets from the query list.</p>
<p dir="auto">Here is an example where you run a query to get all the widgets, and then you extract the <code>Label</code> and the <code>Button</code> widgets into new <code>DOMQuery</code> objects:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python"># Get all the widgets
widgets = self.query()
# Get the Label widgets
label_widgets = widgets.filter("Label")
# Get the Button widgets
button_widgets = widgets.filter("Button")</pre>
<div class="markdown-heading" dir="auto">
<h2 class="heading-element" dir="auto" tabindex="-1">Query Exclusions</h2>
<p>&nbsp;</p>
</div>
<p dir="auto">You may exclude widgets from a <code>DOMQuery</code> using the <code>exclude()</code> method. You can think of <code>exclude()</code> as the logical opposite of <code>filter()</code>. It will remove any widgets from the <code>DOMQuery</code> that match a CSS selector.</p>
<p dir="auto">You can use the previous example to get all the widgets. However, this time, you want to exclude all the <code>Button</code> widgets.</p>
<p dir="auto">Here&#8217;s one way you could do that:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python"># Get all the widgets
widgets = self.query()
# Exclude the buttons
non_buttons = widgets.exclude("Button")</pre>
<p dir="auto">With a little practice, you can soon filter and exclude as many or as few widgets as you want from your query set.</p>
<div class="markdown-heading" dir="auto">
<h2 class="heading-element" dir="auto" tabindex="-1">Other Query Methods</h2>
<p>&nbsp;</p>
</div>
<p dir="auto">So far, you have seen how to get list-like <code>DOMQuery</code> objects that you can loop over and call each widget&#8217;s own method on. However, the <code>DOMQuery</code> object supports several methods that you can run, which will update all the matched widgets without needing to loop over them.</p>
<p dir="auto">A good example in the Textual documentation mentions you can use <code>add_class()</code> to mark widgets as &#8220;started&#8221; or &#8220;ended&#8221;. Here&#8217;s an example:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">self.query("Input").add_class("started")</pre>
<p dir="auto">Of course, you could add any other CSS class to all your widgets, too.</p>
<p dir="auto">Here is a list of all the methods you can use that will affect everything in the query set:</p>
<ul dir="auto">
<li><code>add_class</code> &#8211; Add a CSS class or classes.</li>
<li><code>blur</code> &#8211; Removes the focus from the widgets you matched.</li>
<li><code>focus</code> &#8211; Focuses the first matching widgets. It&#8217;s kind of the opposite of <code>blur</code>.</li>
<li><code>refresh</code> &#8211; Need to refresh a set of widgets? Use this method!</li>
<li><code>remove_class</code> &#8211; Removes a CSS class. In other words, if you disabled your widgets with <code>add_class()</code>, you could re-enable them with this one.</li>
<li><code>remove</code> &#8211; Remove all the matched widgets from the DOM.</li>
<li><code>set_class</code> &#8211; Sets a CSS class or classes on your widget query set</li>
<li><code>set</code> &#8211; Sets common attributes on your widgets. For example, you can set <code>display</code>, <code>visible</code>, <code>disabled</code>, or <code>loading</code>.</li>
<li><code>toggle_class</code> &#8211; Sets a CSS class or classes if it is not already set or vice-versa</li>
</ul>
<p dir="auto">You will need to spend some time trying each of these out. You can use the code examples in this chapter and try calling them against various <code>DOMQuery</code> objects. If you need a reminder of what pre-built classes are available to you, refer to chapter three under <strong>pseudo classes</strong> or check the <a href="https://textual.textualize.io/guide/CSS/#pseudo-classes" rel="nofollow">Textual documentation</a>.</p>
<h2 class="header-anchor-post"><strong>Wrapping Up</strong></h2>
<div class="pencraft pc-display-flex pc-alignItems-center pc-position-absolute pc-reset header-anchor-parent">
<div class="pencraft pc-display-contents pc-reset pubTheme-yiXxQA">
<div id="§wrapping-up" class="pencraft pc-reset header-anchor offset-top"></div>
</div>
</div>
<p>Learning how to use DOM queries in Textual will help you whenever you need to do bulk updates to widgets in your application. You will be using some kind of query in most of the applications you build, so understanding how to use them can only help you be more effective.</p>
<p>Let’s review what you learned:</p>
<ul>
<li>The DOMQuery object</li>
<li>Getting the first or last widget</li>
<li>Query filters</li>
<li>Query exclusions</li>
<li>Other query methods</li>
</ul>
<p>Now that you have a good grounding in working with the <code>DOMQuery</code> object, you should spend some time reviewing the code in this chapter and utilizing the methods you learned about. Give them a try, and you’ll be ready to make bulk updates to your user interface in no time!</p>
<h2 class="header-anchor-post">Learn More</h2>
<div class="pencraft pc-display-flex pc-alignItems-center pc-position-absolute pc-reset header-anchor-parent">
<div class="pencraft pc-display-contents pc-reset pubTheme-yiXxQA">
<div id="§learn-more" class="pencraft pc-reset header-anchor offset-top"></div>
<p>&nbsp;</p>
</div>
</div>
<p>Want to learn more about Textual? Check out my book:</p>
<h3 class="header-anchor-post">Purchase on <a href="https://driscollis.gumroad.com/l/textual">Gumroad,</a> <a href="https://leanpub.com/textual/">Leanpub</a>, or <a href="https://www.amazon.com/dp/B0FGDNC3H6">Amazon</a></h3>
<p>The post <a href="https://blog.pythonlibrary.org/2026/05/06/textual-an-intro-to-dom-queries-part-ii/">Textual &#8211; An Intro to DOM Queries (Part II)</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Textual-cogs 0.0.5 Released</title>
		<link>https://blog.pythonlibrary.org/2026/05/01/textual-cogs-0-0-5-released/</link>
		
		<dc:creator><![CDATA[Mike]]></dc:creator>
		<pubDate>Fri, 01 May 2026 14:58:03 +0000</pubDate>
				<category><![CDATA[beginner]]></category>
		<category><![CDATA[GUI Toolkits]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[GUI]]></category>
		<category><![CDATA[Textual]]></category>
		<category><![CDATA[TUI]]></category>
		<guid isPermaLink="false">https://blog.pythonlibrary.org/?p=12785</guid>

					<description><![CDATA[<p>I always thought it would be fun to create my own open source libraries or applications and distribute them somehow. When I started writing my book, Creating TUI Applications with Textual and Python, I took the plunge and wrote a helper package called textual-cogs, which is a collection of reusable dialogs and widgets for Textual. Right now, [&#8230;]</p>
<p>The post <a href="https://blog.pythonlibrary.org/2026/05/01/textual-cogs-0-0-5-released/">Textual-cogs 0.0.5 Released</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>I always thought it would be fun to create my own open source libraries or applications and distribute them somehow. <span style="box-sizing: border-box;">When I started writing my book, <a href="https://driscollis.gumroad.com/l/textual" target="_blank" rel="noopener">Creating TUI Applications with Textual and Python</a>, I took the plunge and wrote a helper package called <a href="https://github.com/driscollis/textual-cogs" target="_blank" rel="noopener">textual-cogs,</a> which is a collection of reusable dialogs and widgets for Textual.</span> Right now, it is mostly just dialogs, but I do hope to add some widgets to it as well.</p>
<p>Anyway, I have released two new dialogs in the past week, with one in v0.0.4 and the other in v0.0.5.</p>
<h2>A Textual Directory Dialog</h2>
<p>In v0.0.5, I added a directory dialog similar to wxPython&#8217;s wx.DirDialog. The dialog will display the user&#8217;s directories and allow the user to choose one. It will also allow the user to create a new folder.</p>
<p>Here&#8217;s a screenshot:</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-12786" src="https://blog.pythonlibrary.org/wp-content/uploads/2026/05/cog_dir_dlg.png" alt="Textual cogs - Directory Dialog" width="739" height="578" srcset="https://blog.pythonlibrary.org/wp-content/uploads/2026/05/cog_dir_dlg.png 739w, https://blog.pythonlibrary.org/wp-content/uploads/2026/05/cog_dir_dlg-300x235.png 300w" sizes="auto, (max-width: 739px) 100vw, 739px" /></p>
<h2>A Textual Open File Dialog</h2>
<p>In v0.0.4, I also added an open file dialog. Textual cogs already has a save file dialog, and I had meant to include the open file dialog originally, but only recently got it added.</p>
<p>Here is what that looks like:</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-12787" src="https://blog.pythonlibrary.org/wp-content/uploads/2026/05/cog_open_flle_dlg.png" alt="Textual cogs - Open File Dialog" width="941" height="575" srcset="https://blog.pythonlibrary.org/wp-content/uploads/2026/05/cog_open_flle_dlg.png 941w, https://blog.pythonlibrary.org/wp-content/uploads/2026/05/cog_open_flle_dlg-300x183.png 300w, https://blog.pythonlibrary.org/wp-content/uploads/2026/05/cog_open_flle_dlg-768x469.png 768w" sizes="auto, (max-width: 941px) 100vw, 941px" /></p>
<h2>How to Install textual-cogs</h2>
<p>You can install textual-cogs using pip or uv:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">python -m pip install textual-cogs</pre>
<h2>Where to Get textual-cogs</h2>
<p>You can find textual-cogs on the following websites:</p>
<ul>
<li><a href="https://github.com/driscollis/textual-cogs">GitHub</a></li>
<li><a href="https://pypi.org/project/textual-cogs/0.0.5/">Python Packaging Index</a></li>
</ul>
<p>The post <a href="https://blog.pythonlibrary.org/2026/05/01/textual-cogs-0-0-5-released/">Textual-cogs 0.0.5 Released</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Textual &#8211; Logging to File and to Textual Console</title>
		<link>https://blog.pythonlibrary.org/2026/04/20/textual-logging-to-file-and-to-textual-console/</link>
		
		<dc:creator><![CDATA[Mike]]></dc:creator>
		<pubDate>Mon, 20 Apr 2026 12:40:23 +0000</pubDate>
				<category><![CDATA[GUI Toolkits]]></category>
		<category><![CDATA[intermediate]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[logging]]></category>
		<category><![CDATA[Textual]]></category>
		<guid isPermaLink="false">https://blog.pythonlibrary.org/?p=12732</guid>

					<description><![CDATA[<p>When you are developing a user interface, it can be valuable to have a log of what&#8217;s going on. Creating a log in Textual, a text-based user interface, is even easier than creating one for wxPython or Tkinter. Why? Well, because Textual includes a logger that is compatible with Python&#8217;s own logging module, so it&#8217;s [&#8230;]</p>
<p>The post <a href="https://blog.pythonlibrary.org/2026/04/20/textual-logging-to-file-and-to-textual-console/">Textual &#8211; Logging to File and to Textual Console</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>When you are developing a user interface, it can be valuable to have a log of what&#8217;s going on. Creating a log in Textual, a text-based user interface, is even easier than creating one for wxPython or Tkinter. Why? Well, because Textual includes a logger that is compatible with Python&#8217;s own logging module, so it&#8217;s almost plug-and-play to hook it all up!</p>
<p>You&#8217;ll learn how to do this in this short tutorial!</p>
<h2>Logging to File and the Console</h2>
<p>Textual includes a built-in <a href="https://textual.textualize.io/api/logging/#textual.logging.TextualHandler">logging-type handler</a> that you can use with Python&#8217;s own logging module called <strong>TextualHandler</strong>. Python has many built-in <a href="https://docs.python.org/3/library/logging.html#handler-objects">logging handler objects</a> that you can use to write to stdout, a file, or even to an email address!</p>
<p>You can hook up multiple handlers to a logger object and write to all of them at once, which gives you a lot of flexibility.</p>
<p>To see how this works in Textual, you will create a very simple application that contains only two buttons. Go ahead and open your favorite Python IDE or text editor and create a new file called <strong>log_to_file.py</strong>. Then enter the following code into it:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python"># log_to_file.py

import logging

from textual.app import App, ComposeResult
from textual.logging import TextualHandler
from textual.widgets import Button


class LogExample(App):

    def __init__(self) -&gt; None:
        super().__init__()
        self.logger = logging.getLogger(name="log_example")
        self.logger.setLevel(logging.INFO)
        file_handler = logging.FileHandler("tui.log")
        self.logger.addHandler(file_handler)
        formatter = logging.Formatter(("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
        file_handler.setFormatter(formatter)

        textual_handler = TextualHandler()
        self.logger.addHandler(textual_handler)

    def compose(self) -&gt; ComposeResult:
        yield Button("Toggle Dark Mode", classes="dark mode")
        yield Button("Exit", id="exit")

    def on_button_pressed(self, event: Button.Pressed) -&gt; None:
        if event.button.id == "exit":
            self.logger.info("User exited")
            self.exit()
        elif event.button.has_class("dark", "mode"):
            self.theme = (
                "textual-dark" if self.theme == "textual-light" else "textual-light"
            )

            self.logger.info(f"User toggled app theme to {self.theme}")



if __name__ == "__main__":
    app = LogExample()
    app.run()
</pre>
<p>As you can see, you have just two buttons for the user to interact with:</p>
<ul>
<li>Toggle Dark Mode &#8211; for toggling dark or light mode</li>
<li>Exit &#8211; for exiting the application</li>
</ul>
<p>No matter which button the user presses, the application will log out something. By default, Textual logs to stdout, but you cannot see it because your application will be on screen. <span style="box-sizing: border-box;">If you want to see the logs, you will need to use one of the Textual Console applications, which is part of <a href="https://textual.textualize.io/guide/devtools/#console" target="_blank" rel="noopener">Textual&#8217;s devtools</a>.</span> If you do not have the dev tools installed, you can do so by running this command:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">pip install textual-dev</pre>
<p>Now that you have the dev tools handy, open up a new terminal window or tab and run this command:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">textual console</pre>
<p>To get Textual to send the log messages to console, you need to run your Textual application in developer mode. You will run it in a different terminal than Textual Console!</p>
<p>Here&#8217;s the special command:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">textual run --dev log_to_file.py</pre>
<p>You will see various events and other logged metadata appear in the Textual Console regardless of whether you specifically log to it. However, now if you do call <strong>self.log</strong> or you use Python&#8217;s <strong>print()</strong> function, you will see those appear in your log.</p>
<p>You will also see your log messages in your log file (tui.log), though it won&#8217;t include all the extra stuff that Textual Console displays. You only get what you log explicitly written into your log file.</p>
<h2>Wrapping Up</h2>
<p>And there you have it. You now know how to use Textual&#8217;s own built-in logging handler in conjunction with Python&#8217;s logging module. Remember, you can use Textual&#8217;s logging handler in addition to one or more of Python&#8217;s logging modules. You can format the output any way you want too!</p>
<h2>Learn More About Logging</h2>
<p>If you want to learn more about logging in Python, you might find my book, <strong>Python Logging</strong>, helpful.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-12315" src="https://blog.pythonlibrary.org/wp-content/uploads/2024/05/pylogging_thumb.jpg" alt="Python Logging book cover" width="200" height="320" srcset="https://blog.pythonlibrary.org/wp-content/uploads/2024/05/pylogging_thumb.jpg 200w, https://blog.pythonlibrary.org/wp-content/uploads/2024/05/pylogging_thumb-188x300.jpg 188w" sizes="auto, (max-width: 200px) 100vw, 200px" /></p>
<p>Purchase the book today on <a href="https://driscollis.gumroad.com/l/pylogging">Gumroad</a>, <a href="https://leanpub.com/pythonlogging/">Leanpub</a> or <a href="https://amzn.to/4dQbzUw">Amazon</a>!</p>
<p>The post <a href="https://blog.pythonlibrary.org/2026/04/20/textual-logging-to-file-and-to-textual-console/">Textual &#8211; Logging to File and to Textual Console</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Textual &#8211; An Intro to DOM Queries (Part I)</title>
		<link>https://blog.pythonlibrary.org/2026/04/17/textual-an-intro-to-dom-queries-part-i/</link>
		
		<dc:creator><![CDATA[Mike]]></dc:creator>
		<pubDate>Fri, 17 Apr 2026 12:57:02 +0000</pubDate>
				<category><![CDATA[Books]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Textual]]></category>
		<category><![CDATA[TJUI]]></category>
		<guid isPermaLink="false">https://blog.pythonlibrary.org/?p=12778</guid>

					<description><![CDATA[<p>In this article, you will learn how to query the DOM in Textual. You will discover that the DOM keeps track of all the widgets in your application. By running queries against the DOM, you can find widgets quickly and update them, too. You will be learning the following topics related to the DOM: The [&#8230;]</p>
<p>The post <a href="https://blog.pythonlibrary.org/2026/04/17/textual-an-intro-to-dom-queries-part-i/">Textual &#8211; An Intro to DOM Queries (Part I)</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>In this article, you will learn how to query the DOM in Textual. You will discover that the DOM keeps track of all the widgets in your application. By running queries against the DOM, you can find widgets quickly and update them, too.</p>
<p>You will be learning the following topics related to the DOM:</p>
<ul>
<li>The query one method</li>
<li>Textual queries</li>
</ul>
<p>You will learn more in the second part of this series next week!</p>
<p>You will soon see the value of working with DOM queries and the power that these queries give you. Let&#8217;s get started!</p>
<h2>The Query One Method</h2>
<p>You will find the query_one() method throughout the Textual documentation and many Textual applications on GitHub. You may use query_one() to retrieve a single widget that matches a CSS selector or a widget type.</p>
<p>You can pass in up to two parameters to query_one():</p>
<ul>
<li>The CSS selector</li>
<li>The widget type</li>
<li>Or both at the same time</li>
</ul>
<p>If you pass both, pass the CSS selector first, with the widget type as the second parameter.</p>
<p>Try some of this out. Open up your Python editor and create a file named query_input.py. Then enter this code in it:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python"># query_input.py

from textual.app import App, ComposeResult
from textual.widgets import Button, Input


class QueryInput(App):

    def compose(self) -&gt; ComposeResult:
        yield Input()
        yield Button("Update Input")

    def on_button_pressed(self) -&gt; None:
        input_widget = self.query_one(Input)
        new_string = f"You entered: {input_widget.value}"
        input_widget.value = new_string


if __name__ == "__main__":
    app = QueryInput()
    app.run()</pre>
<p>Your code creates an <code>Input</code> and a <code>Button</code> widget. Enter some text in the <code>Input</code> widget and press the button. Your <code>on_button_pressed()</code> method will get called. You call <code>query_one()</code> and pass it an <code>Input</code> widget. Then, you update the returned <code>Input</code> widget’s value with a new string.</p>
<p>Here is what the application might look like:</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-12779" src="https://blog.pythonlibrary.org/wp-content/uploads/2026/04/query_input-1024x551.png" alt="" width="1024" height="551" srcset="https://blog.pythonlibrary.org/wp-content/uploads/2026/04/query_input-1024x551.png 1024w, https://blog.pythonlibrary.org/wp-content/uploads/2026/04/query_input-300x161.png 300w, https://blog.pythonlibrary.org/wp-content/uploads/2026/04/query_input-768x413.png 768w, https://blog.pythonlibrary.org/wp-content/uploads/2026/04/query_input-1536x827.png 1536w, https://blog.pythonlibrary.org/wp-content/uploads/2026/04/query_input.png 2018w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></p>
<p>Now, you will try writing a new piece of code where you use <code>query_one()</code> with a CSS selector. Create a new file called <code>query_one_same_ids.py</code> and use this code:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python"># query_one_same_ids.py

from textual.app import App, ComposeResult
from textual.widgets import Button, Label


class QueryApp(App):

    def compose(self) -&gt; ComposeResult:
        yield Label("Press a button", id="label")
        yield Button("Test", id="button")

    def on_button_pressed(self) -&gt; None:
        widget = self.query_one("#label")
        widget.update("You pressed the button!")


if __name__ == "__main__":
    app = QueryApp()
    app.run()</pre>
<p>In this example, you create two widgets with different IDs. Then you use <code>query_one()</code> to select the <code>Label</code> widget and update its text.</p>
<p>If you call <code>query_one()</code> and there are no matches, you will get a <code>NoMatches</code> exception. On the other hand, if there is more than one match, the method will return the first item that does match.</p>
<p>What will the following code do if you put it in your example above?</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">self.query_one(”#label”, Button)</pre>
<p>If you guessed that Textual will raise an exception, you should congratulate yourself. You have good intuition! If the widget matches the CSS selector but not the widget type, then you will get a <code>WrongType</code> exception raised.</p>
<h2 class="header-anchor-post"><strong>Textual Queries</strong></h2>
<div class="pencraft pc-display-flex pc-alignItems-center pc-position-absolute pc-reset header-anchor-parent">
<div class="pencraft pc-display-contents pc-reset pubTheme-yiXxQA">
<div id="§textual-queries" class="pencraft pc-reset header-anchor offset-top"></div>
</div>
</div>
<p>Textual has more than one way to query the DOM. You may also use the <code>query()</code> method, which you can use to query or find multiple widgets. When you call <code>query()</code>, it will return a DOMQuery object, which behaves as a list-like container of widgets.</p>
<p>You can see how this works by writing some code. Create a new Python file named <code>query_all.py</code> and add this code to it:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python"># query_all.py

from textual.app import App, ComposeResult
from textual.widgets import Button, Label


class QueryApp(App):

    def compose(self) -&gt; ComposeResult:
        yield Label("Press a button", id="label")
        yield Button("Test", id="button")

    def on_button_pressed(self) -&gt; None:
        widgets = self.query()
        s = ""
        for widget in widgets:
            s += f"{widget}\n"
        label = self.query_one("#label")
        label.update(s)


if __name__ == "__main__":
    app = QueryApp()
    app.run()</pre>
<p dir="auto">The idea is to get all the widgets in your application and print them out. Of course, you can&#8217;t print out anything when your terminal application is blocking stdout, so instead, you create a string of widgets separated by new lines and update the <code>Label</code> widget.</p>
<p dir="auto">Here is an example of what you might get if you run the code and press the button on your machine:</p>
<p dir="auto"><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-12780" src="https://blog.pythonlibrary.org/wp-content/uploads/2026/04/query_all-1024x551.png" alt="" width="1024" height="551" srcset="https://blog.pythonlibrary.org/wp-content/uploads/2026/04/query_all-1024x551.png 1024w, https://blog.pythonlibrary.org/wp-content/uploads/2026/04/query_all-300x161.png 300w, https://blog.pythonlibrary.org/wp-content/uploads/2026/04/query_all-768x413.png 768w, https://blog.pythonlibrary.org/wp-content/uploads/2026/04/query_all-1536x827.png 1536w, https://blog.pythonlibrary.org/wp-content/uploads/2026/04/query_all.png 2018w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></p>
<p dir="auto">You might be surprised by that output. Perhaps you thought you would only see a <code>Label</code> and a <code>Button</code> widget in that list? If so, you forgot that a <code>Screen</code> widget is always lurking in the background. But there are also two more: a <code>ToastRack</code> and a <code>Tooltip</code> widget. These come with all your applications. The <code>ToastRack</code> positions <code>Toast</code> widgets, which you use to display a notification message. A <code>Tooltip</code> is a message that appears when you hover your mouse over a widget.</p>
<p dir="auto">You do not need to know more about those extra widgets now.</p>
<p dir="auto">Also note that all query methods can be used on both the <code>App</code> and <code>Widget</code> subclasses, which is very handy.</p>
<div class="markdown-heading" dir="auto">
<h3 class="heading-element" dir="auto" tabindex="-1">Using Selectors</h3>
<p><a id="user-content-using-selectors" class="anchor" href="https://github.com/driscollis/creating_tuis/blob/main/manuscript/05_query.md#using-selectors" aria-label="Permalink: Using Selectors"></a></div>
<p dir="auto">You can use CSS selectors with <code>query()</code> in much the same way as you can with <code>query_one()</code>. The difference, of course, is that <code>query()</code> always returns an iterable DOMObject.</p>
<p dir="auto">Let&#8217;s pretend you want to get all the <code>Button</code> widgets in your application and iterate over them. Create a new Python script called <code>query_button.py</code> with this code:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python"># query_buttons.py

from textual.app import App, ComposeResult
from textual.widgets import Button, Label


class QueryApp(App):

    def compose(self) -&gt; ComposeResult:
        yield Label("Press a button", id="label")
        yield Button("One", id="one")
        yield Button("Two", id="two")
        yield Button("Three")

    def on_button_pressed(self) -&gt; None:
        s = ""
        for widget in self.query("Button"):
            s += f"{widget}\n"
        label = self.query_one("#label")
        label.update(s)


if __name__ == "__main__":
    app = QueryApp()
    app.run()</pre>
<p>Here you are passing in a string, &#8220;Button&#8221;, to <code>query()</code>. If using <code>query_one</code>, you would use the <code>Button</code> type directly. Regardless, when you run this code and press the button, you will see the following:</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-12781" src="https://blog.pythonlibrary.org/wp-content/uploads/2026/04/query_buttons-1024x551.png" alt="" width="1024" height="551" srcset="https://blog.pythonlibrary.org/wp-content/uploads/2026/04/query_buttons-1024x551.png 1024w, https://blog.pythonlibrary.org/wp-content/uploads/2026/04/query_buttons-300x161.png 300w, https://blog.pythonlibrary.org/wp-content/uploads/2026/04/query_buttons-768x413.png 768w, https://blog.pythonlibrary.org/wp-content/uploads/2026/04/query_buttons-1536x827.png 1536w, https://blog.pythonlibrary.org/wp-content/uploads/2026/04/query_buttons.png 2018w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></p>
<p dir="auto">That worked great! This time, you queried the DOM and returned all the <code>Button</code> widgets in your application.</p>
<p dir="auto">What if you wanted to find all the disabled buttons in your code? You can disable widgets using the <code>disabled</code> style flag or the CSS attribute. To find those widgets, you would update the query like this: <code>widgets = self.query("Button.disabled")</code>.</p>
<div class="markdown-heading" dir="auto">
<h3 class="heading-element" dir="auto" tabindex="-1">Results</h3>
<p><a id="user-content-results" class="anchor" href="https://github.com/driscollis/creating_tuis/blob/main/manuscript/05_query.md#results" aria-label="Permalink: Results"></a></div>
<p dir="auto">The query objects in Textual also provide a <code>results()</code> method that you can use as an alternative way of iterating over the widgets. For example, you can use <code>results()</code> to rewrite the query above that would retrieve all the disabled buttons to be something like this:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">widgets = self.query(".disabled").results(Button)
s = ""
for widget in widgets:
    s += f"{widget}\n"</pre>
<p dir="auto">This code combines the last example query with the last full code example. Although this latter version is more verbose, you might find it easier to read than the original query for disabled widgets.</p>
<p dir="auto">Another benefit of using <code>results()</code> is that Python type checkers, such as Mypy, can use it to determine the widget type in the loop. When you do not use <code>results()</code>, then Mypy will only know that you are looping over a <code>Widget</code> object, rather than a <code>Button</code> object.</p>
<h2 class="header-anchor-post">Wrapping Up</h2>
<div class="pencraft pc-display-flex pc-alignItems-center pc-position-absolute pc-reset header-anchor-parent">
<div class="pencraft pc-display-contents pc-reset pubTheme-yiXxQA">
<div id="§wrapping-up" class="pencraft pc-reset header-anchor offset-top"></div>
</div>
</div>
<p>You learned the basics of using Textual’s DOM query methods in this article. You can use these query methods to access one or more widgets in your user interface.</p>
<p>Specifically, you learned about the following:</p>
<ul>
<li>The query one method</li>
<li>Textual queries</li>
</ul>
<p>Textual is a great way to create a user interface with Python. You should check it out today!</p>
<h2 class="header-anchor-post">Learn More</h2>
<div class="pencraft pc-display-flex pc-alignItems-center pc-position-absolute pc-reset header-anchor-parent">
<div class="pencraft pc-display-contents pc-reset pubTheme-yiXxQA"><span style="font-size: 16px;">Want to learn more about Textual? Check out my book, <strong>Creating TUI Applications with Textual and Python</strong>:</span></div>
<div>
<ul>
<li class="header-anchor-post"><a href="https://driscollis.gumroad.com/l/textual">Gumroad</a></li>
<li class="header-anchor-post"><a href="https://leanpub.com/textual/">Leanpub</a></li>
<li class="header-anchor-post"><a href="https://www.amazon.com/dp/B0FGDNC3H6">Amazon</a></li>
</ul>
</div>
</div>
<p>The post <a href="https://blog.pythonlibrary.org/2026/04/17/textual-an-intro-to-dom-queries-part-i/">Textual &#8211; An Intro to DOM Queries (Part I)</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Python Pop Quiz &#8211; Number Explosion</title>
		<link>https://blog.pythonlibrary.org/2026/04/02/python-pop-quiz-number-explosion/</link>
		
		<dc:creator><![CDATA[Mike]]></dc:creator>
		<pubDate>Thu, 02 Apr 2026 12:29:39 +0000</pubDate>
				<category><![CDATA[beginner]]></category>
		<category><![CDATA[Books]]></category>
		<category><![CDATA[Education]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[quiz]]></category>
		<guid isPermaLink="false">https://blog.pythonlibrary.org/?p=12775</guid>

					<description><![CDATA[<p>You will sometimes come across examples of code that use one or two asterisks. Depending on how the asterisks are used, they can mean different things to Python. Check your understanding of what a single asterisk means in the following quiz! The Quiz What will be the output if you run this code? numbers = [&#8230;]</p>
<p>The post <a href="https://blog.pythonlibrary.org/2026/04/02/python-pop-quiz-number-explosion/">Python Pop Quiz &#8211; Number Explosion</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p dir="auto">You will sometimes come across examples of code that use one or two asterisks. Depending on how the asterisks are used, they can mean different things to Python.</p>
<p dir="auto">Check your understanding of what a single asterisk means in the following quiz!</p>
<div class="markdown-heading" dir="auto">
<h2 class="heading-element" dir="auto" tabindex="-1">The Quiz</h2>
<p><a id="user-content-the-quiz" class="anchor" href="https://github.com/driscollis/pyquiz_book/blob/main/02_number_explosion.md#the-quiz" aria-label="Permalink: The Quiz"></a></div>
<p dir="auto">What will be the output if you run this code?</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">numbers = range(3)
output = {*numbers}
print(output)</pre>
<p dir="auto">A) <code>{range}</code></p>
<p dir="auto">B) <code>(range)</code></p>
<p dir="auto">C) <code>[0, 1, 2]</code></p>
<p dir="auto">D) <code>(0, 1, 2)</code></p>
<p dir="auto">E) <code>{0, 1, 2}</code></p>
<div class="markdown-heading" dir="auto">
<h2 class="heading-element" dir="auto" tabindex="-1">Hint</h2>
<p><a id="user-content-hint" class="anchor" href="https://github.com/driscollis/pyquiz_book/blob/main/02_number_explosion.md#hint" aria-label="Permalink: Hint"></a></div>
<p dir="auto">&#8220;Unpacking generalizations&#8221; is the term to look up if you get stuck..</p>
<p dir="auto"><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-11679" src="https://blog.pythonlibrary.org/wp-content/uploads/2022/10/quiz_kick-1024x698.jpg" alt="The Python Quiz Book" width="1024" height="698" srcset="https://blog.pythonlibrary.org/wp-content/uploads/2022/10/quiz_kick-1024x698.jpg 1024w, https://blog.pythonlibrary.org/wp-content/uploads/2022/10/quiz_kick-300x204.jpg 300w, https://blog.pythonlibrary.org/wp-content/uploads/2022/10/quiz_kick-768x523.jpg 768w, https://blog.pythonlibrary.org/wp-content/uploads/2022/10/quiz_kick-1536x1046.jpg 1536w, https://blog.pythonlibrary.org/wp-content/uploads/2022/10/quiz_kick-2048x1395.jpg 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></p>
<div class="markdown-heading" dir="auto">
<h2 class="heading-element" dir="auto" tabindex="-1">The Answer</h2>
<p><a id="user-content-the-answer" class="anchor" href="https://github.com/driscollis/pyquiz_book/blob/main/02_number_explosion_answer.md#the-answer" aria-label="Permalink: The Answer"></a></div>
<p dir="auto"><strong>E) <code>{0, 1, 2}</code></strong></p>
<div class="markdown-heading" dir="auto">
<h2 class="heading-element" dir="auto" tabindex="-1">Explanation</h2>
<p><a id="user-content-explanation" class="anchor" href="https://github.com/driscollis/pyquiz_book/blob/main/02_number_explosion_answer.md#explanation" aria-label="Permalink: Explanation"></a></div>
<p dir="auto">A single asterisk before a Python dictionary or list is known as the unpacking operator. In this example, you tell Python to unpack three integers (0 &#8211; 2) into a set.</p>
<p dir="auto">Here is the example running in a REPL:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">&gt;&gt;&gt; numbers = range(3)
&gt;&gt;&gt; output = {*numbers}
&gt;&gt;&gt; print(output)
{0, 1, 2}
&gt;&gt;&gt; print(type(output))
&lt;class 'set'&gt;</pre>
<p dir="auto">The code output shows that you have created a set!</p>
<p dir="auto">You can also use a single asterisk to unpack a dictionary&#8217;s keys:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">&gt;&gt;&gt; my_dict = {1: "one", 2: "two", 3: "three"}
&gt;&gt;&gt; print({*my_dict})
{1, 2, 3}</pre>
<p>If you want to take your knowledge of unpacking further, it can help to see Python functions use asterisks:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">&gt;&gt;&gt; def my_func(*args):
...     print(args)
... 
&gt;&gt;&gt; my_func(1)
(1,)
&gt;&gt;&gt; numbers = range(3)
&gt;&gt;&gt; output = {*numbers}
&gt;&gt;&gt; my_func(output)
({0, 1, 2},)
&gt;&gt;&gt; my_func(*output)
(0, 1, 2)</pre>
<p dir="auto">When you see a single asterisk in a function definition, the asterisk means that the function can take an unlimited number of arguments. In the second example, you pass in the set as a single argument, while in the last example, you use a single asterisk to unpack the numbers and pass them in as three separate arguments.</p>
<p dir="auto">For more information, see <a href="https://peps.python.org/pep-0448/" rel="nofollow">PEP 448 – Additional Unpacking Generalizations</a>, which has many more examples!</p>
<h2 dir="auto">Get the Book</h2>
<p>Want to try out over one HUNDRED more quizzes? Check out the book!</p>
<p><strong>Purchase at <a href="https://driscollis.gumroad.com/l/pyquiz">Gumroad</a> or <a href="https://leanpub.com/pyquiz">Leanpub</a> or <a href="https://www.amazon.com/dp/B0BVT8DZV1">Amazon</a></strong></p>
<p>The post <a href="https://blog.pythonlibrary.org/2026/04/02/python-pop-quiz-number-explosion/">Python Pop Quiz &#8211; Number Explosion</a> appeared first on <a href="https://blog.pythonlibrary.org">Mouse Vs Python</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
