Planet Python
Last update: November 30, 2023 09:43 PM UTC
November 30, 2023
PyCharm
PyCharm 2023.3 Release Candidate Is Out
PyCharm 2023.3 Release Candidate is now available!
You can get the latest build from our website, through the free Toolbox App, or via snaps for Ubuntu.
To use this build, you need to have an active subscription to PyCharm Professional.
As we prepare to release PyCharm 2023.3, our team is making its final tweaks to ensure the product is polished and ready to go. Thanks to everyone who actively tested the new features and shared feedback during the Early Access Program for version 2023.3!
For the latest updates and standout features in the upcoming release, check out the PyCharm EAP 2023.3 blog posts.
If you spot any bugs, please report them using our issue tracker. We’re also eager to hear your thoughts on the latest IDE updates, so feel free to connect with us on X (formerly Twitter) or drop a comment under this blog post.
Happy developing!
November 30, 2023 09:09 PM UTC
Zero to Mastery
Python Monthly Newsletter 💻🐍
48th issue of Andrei Neagoie's must-read monthly Python Newsletter: Python Errors, Tools in Python Land, Architecting Monorepos, and much more. Read the full newsletter to get up-to-date with everything you need to know from last month.
November 30, 2023 10:00 AM UTC
Talk Python to Me
#440: Talking to Notebooks with Jupyter AI
We all know that LLMs and generative AI has been working its way into many products. It's Jupyter's turn to get a really awesome integration. We have David Qiu here to tell us about Jupyter AI. Jupyter AI provides a user-friendly and powerful way to apply generative AI to your notebooks. It lets you choose from many different LLM providers and models to get just the help you're looking for. And it does way more than just a chat pane in the UI. Listen to find out.<br/> <br/> <strong>Links from the show</strong><br/> <br/> <div><b>David Qiu</b>: <a href="https://www.linkedin.com/in/dlq/" target="_blank" rel="noopener">linkedin.com</a><br/> <br/> <b>Jupyter AI</b>: <a href="https://jupyter-ai.readthedocs.io/en/latest/" target="_blank" rel="noopener">jupyter-ai.readthedocs.io</a><br/> <br/> <b>Asking about something in your notebook</b>: <a href="https://jupyter-ai.readthedocs.io/en/latest/users/index.html#asking-about-something-in-your-notebook" target="_blank" rel="noopener">jupyter-ai.readthedocs.io</a><br/> <b>Generating a new notebook</b>: <a href="https://jupyter-ai.readthedocs.io/en/latest/users/index.html#generating-a-new-notebook" target="_blank" rel="noopener">jupyter-ai.readthedocs.io</a><br/> <b>Learning about local data</b>: <a href="https://jupyter-ai.readthedocs.io/en/latest/users/index.html#learning-about-local-data" target="_blank" rel="noopener">jupyter-ai.readthedocs.io</a><br/> <b>Formatting the output</b>: <a href="https://jupyter-ai.readthedocs.io/en/latest/users/index.html#formatting-the-output" target="_blank" rel="noopener">jupyter-ai.readthedocs.io</a><br/> <b>Interpolating in prompts</b>: <a href="https://jupyter-ai.readthedocs.io/en/latest/users/index.html#interpolating-in-prompts" target="_blank" rel="noopener">jupyter-ai.readthedocs.io</a><br/> <b>JupyterCon 2023 Talk</b>: <a href="https://www.youtube.com/watch?v=bbj_oDh81hY" target="_blank" rel="noopener">youtube.com</a><br/> <b>PyData Seattle 2023 Talk</b>: <a href="https://www.youtube.com/watch?v=T0rzH_KslKQ" target="_blank" rel="noopener">youtube.com</a><br/> <b>Watch this episode on YouTube</b>: <a href="https://www.youtube.com/watch?v=7Sxw6gh6Gr8" target="_blank" rel="noopener">youtube.com</a><br/> <b>Episode transcripts</b>: <a href="https://talkpython.fm/episodes/transcript/440/talking-to-notebooks-with-jupyter-ai" target="_blank" rel="noopener">talkpython.fm</a><br/> <br/> <b>--- Stay in touch with us ---</b><br/> <b>Subscribe to us on YouTube</b>: <a href="https://talkpython.fm/youtube" target="_blank" rel="noopener">youtube.com</a><br/> <b>Follow Talk Python on Mastodon</b>: <a href="https://fosstodon.org/web/@talkpython" target="_blank" rel="noopener"><i class="fa-brands fa-mastodon"></i>talkpython</a><br/> <b>Follow Michael on Mastodon</b>: <a href="https://fosstodon.org/web/@mkennedy" target="_blank" rel="noopener"><i class="fa-brands fa-mastodon"></i>mkennedy</a><br/></div><br/> <strong>Sponsors</strong><br/> <a href='https://talkpython.fm/posit'>Posit</a><br> <a href='https://talkpython.fm/training'>Talk Python Training</a>
November 30, 2023 08:00 AM UTC
Test and Code
210: TDD - Refactor while green
Test Driven Development. Red, Green, Refactor.
- Do we have to do the refactor part?
- Does the refactor at the end include tests?
- Or can I refactor the tests at any time?
- Why is refactor at the end?
This episode is to talk about this with a an example.
Sponsored by PyCharm Pro
- Use code PYTEST for 20% off PyCharm Professional at jetbrains.com/pycharm
- First 10 to sign up this month get a free month of AI Assistant
- See how easy it is to run pytest from PyCharm at pythontest.com/pycharm
The Complete pytest Course
- For the fastest way to learn pytest, go to courses.pythontest.com
- Whether your new to testing or pytest, or just want to maximize your efficiency and effectiveness when testing.
November 30, 2023 12:29 AM UTC
Armin Ronacher
Bundleless: Not Doing Things Makes You Fast
I recently came across a tweet and one statement in it really triggered me: the claim that a bundleless dev server does not work. The idea here being that you cannot avoid bundling during development for performance reasons. This challenges the code concept and premise of vite's design. Its dev server primarily operates by serving individual files post-initial transpiling.
There's some belief that bundleless development isn't feasible, especially for projects with thousands of modules, due to potential performance issues. However, I contend that this thinking overlooks the benefits of a bundleless approach.
There is obviously some truth to it having issues. If you have thousands of modules, that can take a while to load and on contrast if most of those are bundled up into a single file, that will take less time to load.
I believe this to be the wrong way to think of this issue. Consider Python as an illustrative example: Python loads each module as needed from the file system, without bundling numerous modules into larger files. This approach has a downside: in large applications, the startup time can become impractically long due to excessive code execution during import.
The solution isn't to increase bundling but to reduce overall code execution, particularly at startup. By optimizing module structure, minimizing cross-dependencies, and adopting lazy loading, you can significantly decrease load times and enable hot reloading of components. Don't forget that in addition to all the bytes you're not loading, you're also not parsing or executing code. You become faster by not doing all of this.
The objective for developers, both end-users and framework creators, should be to make bundleless development viable and at least in principle preferred. This means structuring applications to minimize initial load requirements, thereby enhancing iteration speeds. With a focus on doing less, the elimination of the bundling step becomes an attainable and beneficial goal. This is also one of the larger lessons I took from creating Flask: the many side effects of decorators and imports are a major frustration for large scale apps.
Then once that has been accomplished, bundleless does away with the last bit of now not important part: the bundling step which has a lot of other benefits on its own.
Of course, there are nuances. For instance, rarely changing third-party libraries with hundreds of internal modules will still benefit from bundling. Tools like Vite do address this need by optimizing this case.
Therefore, when embarking on a new project or framework, prioritize lazy loading and effective import management from the outset. Avoid circular dependencies and carefully manage code isolation. This initial effort in organizing your code will pay dividends as your project expands, making future development faster and more efficient.
Future you will be happy — and bundleless as evidenced by vite, with the right project setup works.
November 30, 2023 12:00 AM UTC
November 29, 2023
Ned Batchelder
Say it again: values not expressions
Sometimes you can explain a simple thing for the thousandth time, and come away with a deeper understanding yourself. It happened to me the other day with Python mutable argument default values.
This is a classic Python “gotcha”: you can provide a default value for a function argument, but it will only be evaluated once:
>>> def doubled(item, the_list=[]):
... the_list.append(item)
... the_list.append(item)
... return the_list
...
>>> print(doubled(10))
[10, 10]
>>> print(doubled(99))
[10, 10, 99, 99] # WHAT!?
I’ve seen people be surprised by this and ask about it countless times. And countless times I’ve said, “Yup, the value is only calculated once, and stored on the function.”
But recently I heard someone answer with, “it’s a value, not an expression,” which is a good succinct way to say it. And when a co-worker brought it up again the other day, I realized, it’s right in the name: people ask about “default values” not “default expressions.” Of course it’s calculated only once, it’s a default value, not a default expression. Somehow answering the question for the thousandth time made those words click into place and make a connection I hadn’t realized before.
Maybe this seems obvious to others who have been fielding this question, but to me it was a satisfying alignment of the terminology and the semantics. I’d been using the words for years, but hadn’t seen them as so right before.
This is one of the reasons I’m always interested to help new learners: even well-trodden paths can reveal new insights.
November 29, 2023 11:30 PM UTC
Paolo Melchiorre
Pelican 4.9: classless Simple theme with semantic HTML
Introducing the updated version of the “Simple” theme in the new Pelican 4.9 version, with semantic and classless HTML and customizable out-of-the-box.
November 29, 2023 11:00 PM UTC
Django Weblog
2024 DSF Board Election Results
The 2024 DSF Board Election has closed, and the following candidates have been elected:
- Jacob Kaplan-Moss
- Sarah Abderemane
- Thibaud Colas
They will all serve two years for their term.
From the 2023 DSF board announcement, Chaim, Katie, Kátia, and Çağıl are continuing directors. They will have one year left to serve on their time.
Therefore, the combined 2024 DSF Board of Directors are:
- Chaim Kirby
- Katie McLaughlin
- Kátia Yoshime Nakamura
- Çağıl Uluşahin Sonmez
- Jacob Kaplan-Moss*
- Sarah Abderemane*
- Thibaud Colas*
* Elected to a two (2) year term
Congratulations to our winners and a huge thank you to our departing board members Aaron Bassett and Pete Baumgartner.
Thank you again to everyone who nominated themselves. Even if you were not successful, you gave the community the chance to make their voices heard in who they wanted to represent them.
November 29, 2023 10:10 PM UTC
Real Python
Single and Double Underscores in Python Names
Python has a few important naming conventions that are based on using either a single or double underscore character (_). These conventions allow you to differentiate between public and non-public names in APIs, write safe classes for subclassing purposes, avoid name clashes, and more.
Following and respecting these conventions allows you to write code that looks Pythonic and consistent in the eyes of other Python developers. This skill is especially useful when you’re writing code that’s intended for other developers to work with.
In this tutorial, you’ll:
- Learn about Python naming conventions that rely on using underscores (
_) - Differentiate public and non-public names by using a single leading underscore
- Use double leading underscores to leverage name mangling in Python classes
- Explore other common uses of underscores in Python names
To get the most out of this tutorial, you should be familiar with Python variables, constants, functions, modules, and especially classes and object-oriented programming.
Get Your Code: Click here to download the free sample code that shows you how to use single and double underscores in Python names.
Public Interfaces and Naming Conventions in Python
As a Python programmer, you’ll frequently work with public interfaces, or application programming interfaces (API). An API is a type of programming interface that offers a service to other parts of a program or other programs.
For example, the Python standard library has many modules and packages that provide certain services. To use these modules and packages, you need to access their public components, such as classes, functions, variables, constants, and modules. All these objects are part of the module or package’s public interface. They’re available for you to use directly in your code.
However, many of these packages and modules define objects that aren’t intended for direct access. These objects are for the internal use of the specific package or module only. They’re not part of its public interface.
In the context of object-oriented programming, languages like C++ and Java have the notion of public and private methods and attributes. In these languages, you can use these types of class members as follows:
- Public: You can use them in your own code or client code.
- Private: You can use them only from inside the defining class and its subclasses.
These languages have specific keywords and syntax to define public and private members in their classes. Once you declare a member as private, you can’t use it outside the class because the language restricts access. So, private members aren’t part of the class’s public interface, and there’s no way to access them.
In contrast, Python doesn’t have the notion of public and private members. It has neither dedicated keywords nor syntax for defining them. So, you can always access the members of a Python class.
If Python doesn’t have a specific syntax to define when an object is part of a public interface, then how do you tell your users that they can or can’t use a given class, method, function, variable, constant, or even module in their code?
To approach this question, the Python community has well-established naming conventions. You should observe these naming conventions to explicitly indicate whether other developers should directly use your variables, constants, functions, methods, and modules in external code.
Note that the naming conventions don’t restrict access to objects. They’re just a warning to other developers using your code. Because of this, Python doesn’t use the terms public and private. Instead, it uses the terms public and non-public.
In Python, if a name starts with a letter in uppercase or lowercase, then you should consider that name public and, therefore, part of the code’s API. In contrast, if a name starts with an underscore character (_), then you should consider that name non-public, meaning it’s not a part of the code’s API.
The Python community uses the underscore character (_) as part of other naming conventions. Here’s a summary of what PEP 8 says about using this character in names:
| Convention | Example | Meaning |
|---|---|---|
| Single leading underscore | _variable |
Indicates that the name is meant for internal use only |
| Single trailing underscore | class_ |
Avoids naming conflicts with Python keywords and built-in names |
| Double leading underscore | __attribute |
Triggers name mangling in the context of Python classes |
| Double leading and trailing underscore | __name__ |
Indicates special attributes and methods that Python provides |
| Single underscore | _ |
Indicates a temporary or throwaway variable |
Note that only two of these conventions enforce specific Python behaviors. Using double leading underscores triggers name mangling in Python classes. You’ll learn more about this behavior in the section on name mangling.
Additionally, those names with double leading and trailing underscores that are listed in the Python data model trigger internal behaviors in specific contexts. You’ll also learn more about this topic in the section on dunder names in Python.
Note: Python gives special treatment to the single underscore (_), but only within match … case statements. You’ll learn more about this later.
In the following section, you’ll learn more about using a single leading underscore when naming your Python objects.
Single Leading Underscore in Python Names
Read the full article at https://realpython.com/python-double-underscore/ »
[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]
November 29, 2023 02:00 PM UTC
Gocept Weblog
The final teardown
After writing so many teardowns in tests for software projects for customers, open source projects and in house software, it is now time for the gocept folks to finally work on a last teardown of the company itself. After 23 years of existence gocept will close the doors and stop operating business at 2023-11-30. The developers found a new home at the wonderful company Minddistrict. There we will work in a product team and build software to empower people to take care of their mental well-being.
Customers
We want to thank all our customers for their trust in us over the years and for the often multi year long collaboration. Sometimes gocept was a bit on the bleeding edge of technology and often that was shining through in the development work. But we did our best to provide the individual software applications to the needs of our customers.
Open Source Software
It was in the very essence of gocept to work with open source software. The company grew with Zope and contributed to the Zope ecosystem. Out of this relationship many events were supported like DZUG-Tagung, a EuroPython Conference and in particular in the last years a round of Zope sprint to ensure compatibility and stability in the Python 3 wonderland. We are very happy, that Minddistrict also builds its core product on zope.* packages and that we can further contribute to the open source world.
Most of the gocept.* packages on https://github.com/gocept have been archived or given to the people and companies, that still use them. In case you need to work with them, please contact mail@gocept.com or the respective maintainers on PyPI. We will be able to unarchive the package and if necessary transfer the release rights.
gocept folks
Some parts of gocept already left the company in 2014 and formed https://flyingcircus.io/. They are more successful than ever providing a very good and individual hosting and, more important, operation solutions for bigger applications and complex setups. They took over the dedication to software and to new technology, and they are good colleagues to work with.
The remaining 4 developers at gocept join the Minddistrict company and will work there in a bigger team to empower people to take care of their mental well-being in the Netherlands, in Germany and the DACH-region. We will still keep an office in Halle, so there might be a chance to stop by.
The last Christian of gocept (in a row of many) will continue his management work within the Saltlabs and the Koffij – with the mission to develop modern, contemporary and flexible workplaces in Halle and build communities that motivate and offer pleasant working environments.
gocept.cleanup()
In case you need anything or want to reach us, please find us on GitHub (or the place to be in the future) or write an email to mail@gocept.com.
November 29, 2023 10:13 AM UTC
November 28, 2023
PyCoder’s Weekly
Issue #605 (Nov. 28, 2023)
#605 – NOVEMBER 28, 2023
View in Browser »
Inline Dependencies for Small Python Scripts
Sabs wanted the ability to specify dependencies inside a single Python script, and although there are PEPs on this topic, their implementation is a ways off. Sabs decided to solve this problem directly: introducing pip.wtf a code snippet you copy into your script that does dependency loading through a function call.
PIP.WTF
Build Conway’s Game of Life With Python
In this step-by-step project, you’ll implement Conway’s Game of Life in Python. To make the game usable, you’ll create a user-friendly command-line interface (CLI) with several options that will allow you to run the game using different life patterns and configurations.
REAL PYTHON
Master Python With Pybites PDM 🚀
Are you stuck in tutorial paralysis? Crush imposter syndrome with the 12-week Pybites PDM Program. Learn advanced Python, build impactful real world apps, and get 1:1 mentorship. Stand out in tech interviews and elevate your developer journey. Join a thriving Python community. Enroll today! 🐍👩💻
PYBITES sponsor
The Categories of Bugs in Python Apps
This article categorizes errors in your Python code into three groups: type-checking, import explosions, and runtime errors. Learn how each is treated differently and how to better handle them in your programs.
TIN
Discussions
Python Jobs
Software Engineer - Intern (Summer 2024) (Dallas, TX, USA)
Articles & Tutorials
How to Render Markdown in a Django Application
In this tutorial, you’ll learn how to create and render web content in Django with the simplicity and flexibility of the Markdown text formatting language.
REAL PYTHON
10 Truths They Won’t Tell You About a Software Job
This opinion piece from Mensur addresses the wide gulf between what is learned in school and what is encountered in real world software development. He mentions topics ranging from the frequency of dealing with legacy code, to the reality of meetings and estimates. Associated HN discussion.
MENSUR DURAKOVIC
A Disorganized List of Maintainer Tasks
David is the maintainer of Flask and this list shows all that entails. Some things are obvious and others are things you might not be aware of. If you’re participating in the OSS community, it is good to know what is on here, if you just use libraries, appreciate all the work being done!
DAVID LORD
One Liners Python Edition
A collection of single-line Python snippets that cover common tasks in programming. Includes removing duplicates from lists, reversing strings, finding the most common element in a list, and much more.
MUHAMMAD RAZA
The Radiating Programmer
Love them or hate them, the daily stand-up is out there. It may have problems, but at its core, the three mains questions (what did you do? what are you planning? what is blocking you?) provide a tool for radiating information, and at core that’s a good idea.
JORGE MANRUBIA
Fixit 2: Meta’s Next-Generation Auto-Fixing Linter
Meta has a large collection of plug-ins for linting, which makes some of the usual solutions problematic at scale. This article introduces Fixit 2, an open source, pluggable linter.
AMETHYST REESE
Beginners Should Use Django, Not Flask
This opinion piece by Bite Code states why developers new to the web space should use Django, even though Flask has the simpler “hello world” which makes it attractive.
BITE CODE
Error Culture
This multi-part post talks about how getting notified about all errors leads to error fatigue from false positives and a culture where errors get ignored. Long term this can have disastrous consequences.
RYAN CHELEY
Python Basics Exercises: Modules and Packages
In this video course, you’ll practice separating your code into modules, using the import statement to access another module’s namespace, and creating Python packages.
REAL PYTHON course
How to Write Efficient Python Code: A Tutorial for Beginners
Are you a programmer looking to get better at Python? Learn some of Python’s features that’ll help you write more elegant and Pythonic code.
BALA PRIYA C • Shared by Bala Priya C
The Changing “Guarantees” Given by Python’s GIL
This article covers the changes the Global Interpreter Lock has undergone since Python 3.9 and how that impacts the assumptions you can make in your code.
STEFAN MARR
11 Fun Python Libraries to Make Your Day Better
This article covers 11 libraries that the author had fun playing with, including PyGame, Asciimatics, Arcade, and more.
MARINE GOSSELIN
datetime.utcnow() Is Now Deprecated
Python 3.12 has deprecated utcnow() and utcfromtimestamp(). This article covers why and what to use instead.
MIGUEL GRINBERG
Projects & Code
Simple Async Queue
SAQ-PY.READTHEDOCS.IO • Shared by Nickolas Grigoriadis
error-links: Add Links to Search Google to Your Tracebacks
GITHUB.COM/RODRIGOGIRAOSERRAO • Shared by Rodrigo Girão Serrão 🐍🚀
Events
Weekly Real Python Office Hours Q&A (Virtual)
November 29, 2023
REALPYTHON.COM
SPb Python Drinkup
November 30, 2023
MEETUP.COM
PyLadiesCon 2023
December 2 to December 4, 2023
PYLADIES.COM
XtremePython 2023
December 5 to December 6, 2023
XTREMEPYTHON.DEV
PyData Global 2023
December 6 to December 9, 2023
PYDATA.ORG
Pyjamas Conf 2023
December 9 to December 11, 2023
PYJAMAS.LIVE
Sciwork 2023
December 9 to December 11, 2023
SCIWORK.DEV
Happy Pythoning!
This was PyCoder’s Weekly Issue #605.
View in Browser »
[ Subscribe to 🐍 PyCoder’s Weekly 💌 – Get the best Python news, articles, and tutorials delivered to your inbox once a week >> Click here to learn more ]
November 28, 2023 07:30 PM UTC
Real Python
Advent of Code: Solving Puzzles With Python
Advent of Code is an online Advent calendar where you’ll find new programming puzzles offered each day from December 1 to 25. While you can solve the puzzles at any time, the excitement when new puzzles unlock is really something special. You can participate in Advent of Code in any programming language—including Python!
With the help of this Code Conversation, you’ll be ready to start solving puzzles and earning your first gold stars.
In this video course, you’ll learn:
- What an online Advent calendar is
- How solving puzzles can advance your programming skills
- How you can participate in Advent of Code
Advent of Code puzzles are designed to be approachable by anyone with an interest in problem-solving. You don’t need a heavy computer science background to participate. Instead, Advent of Code is a great arena for learning new skills and testing out new features of Python.
[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]
November 28, 2023 02:00 PM UTC
ListenData
How to Get Unique Values in a Column in Pandas DataFrame
This tutorial explains how to get unique values from a column in Pandas DataFrame, along with examples.
df['columnName'].unique()To read this article in full, please click here
November 28, 2023 12:17 PM UTC
Python Bytes
#362 You can deprecate a global variable?
<strong>Topics covered in this episode:</strong><br> <ul> <li><a href="https://vadimkravcenko.com/shorts/habits-of-great-software-engineers"><strong>Habits of great software engineers</strong></a></li> <li><a href="https://flask.palletsprojects.com/en/3.0.x/changes/"><strong>Flask 3.0</strong></a></li> <li><a href="https://realpython.com/conway-game-of-life-python"><strong>Build Conway's Game of Life With Python</strong></a></li> <li><a href="https://github.com/MarcoGorelli/polars-business"><strong>polars business</strong></a></li> <li><strong>Extras</strong></li> <li><strong>Joke</strong></li> </ul><a href='https://www.youtube.com/watch?v=G2sLEhjIRHA' style='font-weight: bold;'>Watch on YouTube</a><br> <p><strong>About the show</strong></p> <p>Sponsored by <a href="https://pythonbytes.fm/scout"><strong>Scout APM</strong></a></p> <p><strong>Connect with the hosts</strong></p> <ul> <li>Michael: <a href="https://fosstodon.org/@mkennedy"><strong>@mkennedy@fosstodon.org</strong></a></li> <li>Brian: <a href="https://fosstodon.org/@brianokken"><strong>@brianokken@fosstodon.org</strong></a></li> <li>Show: <a href="https://fosstodon.org/@pythonbytes"><strong>@pythonbytes@fosstodon.org</strong></a></li> </ul> <p>Join us on YouTube at <a href="https://pythonbytes.fm/stream/live"><strong>pythonbytes.fm/live</strong></a> to be part of the audience. Usually Tuesdays at 11am PT. Older video versions available there too.</p> <p><strong>Brian #1:</strong> <a href="https://vadimkravcenko.com/shorts/habits-of-great-software-engineers"><strong>Habits of great software engineers</strong></a></p> <ul> <li>As we wind up the year, many people are thinking about goals for the new year.</li> <li>Here’s a decent list to think about <ul> <li>Focusing beyond the code </li> <li>Efficiency / Antifragility</li> <li>Joy of tinkering </li> <li>Knowing the why </li> <li>Thinking in systems </li> <li>Tech detox </li> <li>The art of approximation</li> <li>Transferring Knowledge to Other Problems</li> <li>Making Hard Things Easy</li> <li>Playing the Long Game</li> <li>Developing a Code Nose</li> <li>Strong Opinions loosely held</li> </ul></li> </ul> <p><strong>Michael #2:</strong> <a href="https://flask.palletsprojects.com/en/3.0.x/changes/"><strong>Flask 3.0</strong></a></p> <ul> <li>Deprecate the <code>__version__</code> attribute. Use feature detection, or <code>importlib.metadata.version("flask")</code>, instead. #5230 <ul> <li>How do you <a href="https://github.com/pallets/flask/pull/5242/files">even do that</a>?</li> </ul></li> <li><p>This is news to me:</p> <pre><code>[build-system] requires = ["setuptools", "wheel"] build-backend = "setuptools.build_meta" [metadata] name = "your-package-name" version = "0.1.0" </code></pre></li> <li><p>Remove previously deprecated code. <a href="https://github.com/pallets/flask/pull/5223">#5223</a></p></li> </ul> <p><strong>Brian #3:</strong> <a href="https://realpython.com/conway-game-of-life-python"><strong>Build Conway's Game of Life With Python</strong></a></p> <ul> <li>Leodanis Pozo Ramos</li> <li>CLI curses version</li> <li>Nice walk through of breaking the problem into parts.</li> </ul> <p><strong>Michael #4:</strong> <a href="https://github.com/MarcoGorelli/polars-business"><strong>polars business</strong></a></p> <ul> <li>It's a plugin for Polars, which allows you to do business day arithmetic.</li> <li>The big advantage of using this directly (as opposed to converting to pandas/numpy, using their business day tools, and then converting back) is that polars-business fits right in with the Polars lazy API. This means you'll still be able to get the gains from the Polars query optimiser without having to step into eager execution.</li> <li>All you need to use is it is <code>pip install polars-business</code></li> <li>Written in Rust, but end-users doesn't need Rust to run it, Python is all you need.</li> </ul> <p><strong>Extras</strong> </p> <p>Brian:</p> <ul> <li>BLACKFRIDAY code still works for 50% off <a href="https://courses.pythontest.com/p/complete-pytest-course">The Complete pytest Course,</a> Full Course + Community Access, through Nov 30</li> <li>Also Debugging chapter is up, and it includes a small TDD example.</li> </ul> <p>Michael:</p> <ul> <li><a href="https://twitter.com/kennethreitz42/status/1726816318303752511">Dear Python Community</a> by Kenneth Reitz</li> <li><a href="https://discuss.python.org/t/python-3-13-0-alpha-2/39379">Python 3.13a2 out and Major new features of the 3.13 series, compared to 3.12</a></li> <li>Thank you Black Friday supporters. </li> </ul> <p><strong>Joke:</strong> </p> <ul> <li><a href="https://www.reddit.com/r/programminghumor/comments/171i41r/select/">ai vs dev</a></li> </ul>
November 28, 2023 08:00 AM UTC
Python⇒Speed
Using Polars in a Pandas world
Polars is a dataframe-based library that can be faster, more memory efficient, and often simpler to use than Pandas. It’s also much newer, and correspondingly less popular. In November 2023:
- Polars had ~2.6 million downloads from PyPI.
- Pandas had ~140 million downloads!
Because of Pandas’ popularity and decade and a half of availability, there are many third-party libraries with built-in support for Pandas, and others that specifically extend Pandas. Many plotting and visualization libraries will accept Pandas dataframes as an input, for example, and GeoPandas adds geographical data types to Pandas dataframes. If you’re using Polars, can you use these libraries? And if so, how?
In this article we’ll cover the various integration options you have between Polars and third-party libraries originally designed to work with Pandas:
- It Just Works.
- Manual conversion to Pandas dataframes, and how to do it with essentially zero cost.
- Manual interoperability via files.
- Other alternatives.
November 28, 2023 12:00 AM UTC
TypeThePipe
Boosting Python development speed with Ruff: An all-in-one lightning fast linter
pre > code.sourceCode { white-space: pre; position: relative; } pre > code.sourceCode > span { display: inline-block; line-height: 1.25; } pre > code.sourceCode > span:empty { height: 1.2em; } .sourceCode { overflow: visible; } code.sourceCode > span { color: inherit; text-decoration: inherit; } div.sourceCode { margin: 1em 0; } pre.sourceCode { margin: 0; } @media screen { div.sourceCode { overflow: auto; } } @media print { pre > code.sourceCode { white-space: pre-wrap; } pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; } } pre.numberSource code { counter-reset: source-line 0; } pre.numberSource code > span { position: relative; left: -4em; counter-increment: source-line; } pre.numberSource code > span > a:first-child::before { content: counter(source-line); position: relative; left: -1em; text-align: right; vertical-align: baseline; border: none; display: inline-block; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; padding: 0 4px; width: 4em; color: #aaaaaa; } pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; } div.sourceCode { } @media screen { pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; } } code span.al { color: #ff0000; font-weight: bold; } /* Alert */ code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */ code span.at { color: #7d9029; } /* Attribute */ code span.bn { color: #40a070; } /* BaseN */ code span.bu { color: #008000; } /* BuiltIn */ code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */ code span.ch { color: #4070a0; } /* Char */ code span.cn { color: #880000; } /* Constant */ code span.co { color: #60a0b0; font-style: italic; } /* Comment */ code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */ code span.do { color: #ba2121; font-style: italic; } /* Documentation */ code span.dt { color: #902000; } /* DataType */ code span.dv { color: #40a070; } /* DecVal */ code span.er { color: #ff0000; font-weight: bold; } /* Error */ code span.ex { } /* Extension */ code span.fl { color: #40a070; } /* Float */ code span.fu { color: #06287e; } /* Function */ code span.im { color: #008000; font-weight: bold; } /* Import */ code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */ code span.kw { color: #007020; font-weight: bold; } /* Keyword */ code span.op { color: #666666; } /* Operator */ code span.ot { color: #007020; } /* Other */ code span.pp { color: #bc7a00; } /* Preprocessor */ code span.sc { color: #4070a0; } /* SpecialChar */ code span.ss { color: #bb6688; } /* SpecialString */ code span.st { color: #4070a0; } /* String */ code span.va { color: #19177c; } /* Variable */ code span.vs { color: #4070a0; } /* VerbatimString */ code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */

The Need for Speed. Discover the Ruff module, Ruff rules and the migration from Black
In the world of software development, every second counts. For Python developers, the time spent on code linting can sometimes be a real productivity killer. But what if there was a tool that could make this process lightning-fast, giving you nearly real-time feedback as you code? Meet Ruff, the game-changing Python linter that’s about to revolutionize your development workflow.
Python is renowned for its simplicity and readability, making it a go-to language for many developers. However, as projects grow in complexity, the build times and code analysis can slow down significantly. This is where Ruff steps in. It’s based on a simple but powerful idea: Python tooling could be much, much faster.
Imagine a Python linter that’s approximately 150 times faster than Flake8 on macOS. That’s Ruff for you. It’s not just slightly faster; it’s blazingly fast. For instance, when linting the entire CPython codebase from scratch, it’s the difference between sitting around for 12+ seconds and getting almost instant feedback in just 300-500 milliseconds. It’s a game-changer that saves you time and makes your development process more efficient.
How Ruff Works
Ruff is written in Rust, but here’s the magic: as a user, you won’t even notice it’s not written in Python. It leverages Rust Python’s AST parser and implements its own AST traversal, visitor abstraction, and lint-rule logic. It supports Python 3.10 and 3.11, including the new pattern matching syntax. Ruff is also pip-installable, making it easy to incorporate into your workflow.
Ruff is built on two core hypotheses: Python tooling could be rewritten in more performant languages, and an integrated toolchain can tap into efficiencies that aren’t available to a disparate set of tools. Ruff exemplifies these ideas by generating all violations in a single pass and even autofixing issues without a noticeable performance penalty.
If you’re curious to experience the speed and efficiency of Ruff for yourself, you can try it today by running or the package manager you are using, like Poetry poetry add ruff.
As linter, as it is stated in the Ruff documentation, ” designed as a drop-in replacement for Flake8 (plus dozens of plugins), isort, pydocstyle, pyupgrade, autoflake, and more.” Ruff has a set of over 700 rules in its linter functionallity.
You can start by running it for all current folder files:
If you want to run the Ruff formatter for the first time, as a starting step I would recommend you to use ruff format . --diff. The --diffflag allows you to review the modifications before they are applied. It is thought as a drop-in replacement for Black.
Integrating Ruff in you project with Pyproject yaml and pre-commit
Till today, there is not a mantained way to migrate from Black to Ruff) as it would be a one-timer and costly to ongoing support.
With the following code you will see
Suposse the folowing pyproject.toml file that could be usual in a data related project:
[tool.poetry]
name = "data-project"
version = "1.0.0"
description = "Python 🐍 service that is intended to do wonderful things✧"
authors = ["TypeThePipe.com"]
readme = "README.md"
classifiers = [
"Topic :: Software Development :: Libraries :: Python Modules",
"Programming Language :: Python :: 3.11",
]
[tool.poetry.dependencies]
python = "~3.11.0"
sqlmodel = "^0.0.8"
pydantic = "^1.10.4"
numpy = "^1.24.2"
pandas = "^1.5.3"
scipy = "^1.10.1"
statsmodels = "^0.13.5"
rollbar = "^0.16.3"
sqlalchemy = "1.4.35"
alembic = "^1.10.2"
psycopg2 = "^2.9.5"
[tool.poetry.group.dev.dependencies]
black = "^23.1"
flake8 = "^4.0"
isort = "^5.10"
flakeheaven = "^0.11"
pytest = "^7.2"
pytest-postgresql = "^4.1.1"
psycopg = "^3.1.8"
coverage = {version = "^7.2.1", extras = ["toml"]}
pre-commit = "^2.17.0"
mypy = "^1.0.1"
pytest-dependency = "^0.5.1"
dvc = { version = "^2.10.2", extras = ["s3"] }
mkdocs = "^1.4.2"
mkdocstrings = "^0.19.1"
mkdocs-material = "^9.0.5"
[tool.poetry.group.notebook.dependencies]
jupyterlab = "^3.6.1"
notebook = "^6.5.2"
plotly = "^5.13.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
#####
# Typing
#####
[tool.isort]
# Black Compatibility
profile = "black"
[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
ignore_missing_imports = true
exclude = ["nb"]
#####
# Flake8
#####
[tool.flakeheaven]
exclude = ["README.md"]
format = "stat"
max_line_length = 120 # YOLO
show_source = true
[tool.flakeheaven.plugins]
pyflakes = ["+*", "-F401"]
# enable only codes from S100 to S199
flake8-bandit = ["-*", "+S1??"]
flake8-docstrings = ["+*"]
flake8-bugbear = ["+*"]
flake8-builtins = ["+*", "-A003"]
flake8-comprehensions = ["+*"]
flake8-eradicate = ["+*"]
flake8-isort = ["+*"]
flake8-mutable = ["+*"]
flake8-pytest-style = ["+*"]
pyflakes = ["+*"]
pylint = ["+*"]Then you could change all the linter as a replacement for Flake8, isort, autoflake and pyupgrade.
This Ruff code would be the replacement of the Flake8 pyproject section. We use the tool.rufffor generic configuration and the tool.ruff.lint for specific Ruff rules you want to apply in your linter. Those selected ones would be the replacement of the previous configuration.
[tool.ruff]
line-length = 120 # YOLO
target-version = "py311"
[tool.ruff.lint]
select = [
"F", #Pyflakes
"B", #flake8-bugbear
"A", #flake8-builtins
"C4", #flake8-comprehensions
"ERA", #eradicate
"I", #isort
"PT", #flake8-pytest-style
"C90", #mccabe
"N", #pep8-naming
"E", #pycodestyle error
"PL" #Pylint
]
ignore = ["A003"]
[tool.ruff.format]
quote-style = "single"
indent-style = "tab"You have to take into account that the Bandit Ruff port is still an ongoing track of work, so I’ve not included it and let it as an extra step of my pre-commit file. You can keep update about this integration in this Github Issue.
Also you can find the pre-commit file useful, as it is an incredible and necessary tool while building robust code projects.
#.pre-commit-config.yaml file
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.1.6
hooks:
# Run the linter.
- id: ruff
args: [ --fix ]
# Run the formatter.
- id: ruff-format
- repo: https://github.com/tox-dev/pyproject-fmt
rev: "1.5.2"
hooks:
- id: pyproject-fmtWhat is the difference between using tool.lint and tool.ruff.lint?
The tool.ruff.lint is now preferred the way to configure the linter, despite it is sill experimental and may include changes.
Stay updated on Ruff and Python tips
Hopefully, this post has helped you become familiar with a newcomer to the Python linter and pre-commit hook landscape, say hi to Ruff.
If you want to stay updated…
#mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; width:100%;} #mc_embed_signup .button { background-color: #0294A5; /* Green */ color: white; transition-duration: 0.4s; } #mc_embed_signup .button:hover { background-color: #379392 !important; }November 28, 2023 12:00 AM UTC
November 27, 2023
Mike Driscoll
Episode 23 – The Ruff Formatter with Charlie Marsh
The Ruff linter is a Python linter written in Rust that is super fast. The people behind Ruff recently released a new tool that is a part of Ruff that allows Ruff to format your Python code using the Black rules.
In this episode, I speak with Charlie Marsh, the original creator of Ruff and the founder of Astral, the company behind Ruff.
In this episode, we talked about:
- The Ruff formatter and linter
- How Ruff came about
- The hardest features to create
- The future of Ruff
- Python vs Rust
- and more!
The post Episode 23 – The Ruff Formatter with Charlie Marsh appeared first on Mouse Vs Python.
November 27, 2023 03:21 PM UTC
Real Python
The Python Rich Package: Unleash the Power of Console Text
Python’s Rich package is a tool kit that helps you generate beautifully formatted and highlighted text in the console. More broadly, it allows you to build an attractive text-based user interface (TUI).
Why would you choose a TUI over a graphical user interface, or GUI? Sometimes a text display feels more appropriate. Why use a full-blown GUI for a simple application, when an elegant text interface will do? It can be refreshing to work with plain text. Text works in almost any hardware environment, even on an SSH terminal or a single-board computer display. And many applications don’t need the complexity of a full graphical windowing system.
In this tutorial, you’ll learn how Rich can help you:
- Enhance the user interface of command-line tools
- Improve the readability of console output
- Create attractive dashboard displays for real-time tabular data
- Generate well-formatted reports
Will McGugan, the author of Rich, has also developed the Textual package. Whereas Rich is a rich-text tool kit, Textual is a full application framework built on Rich. It provides application base classes, an event-driven architecture, and more.
There’s a lot you can do with Rich on its own, and its support for engaging, dynamic displays may well be sufficient for your app. By following this tutorial, you’ll experiment with many of the cool features of Rich, and you’ll finish up by using your skills to build a dynamically scrolling tabular display of crypto prices:
To fully understand Rich’s syntax for animations, you should have a good grasp of context managers. But if you’re a bit rusty, don’t worry! You’ll get a quick refresher in this tutorial.
Get Your Code: Click here to download free sample code that shows you how to use Rich for more beautiful Python code and apps.
Installing Rich
You can start using Rich very quickly. As always when starting a new project or investigation, it’s best to create a virtual environment first, to avoid polluting your system’s Python installation.
It’s quite possible to install Rich and use it with the built-in Python REPL, but for a better developer experience, you may want to include support for Jupyter notebooks. Here’s how you can install Rich so that it’ll work with either the REPL or Jupyter:
Now that you’ve installed Rich in your new virtual environment, you can test it and also get a nice overview of its capabilities:
(venv) $ python -m rich
Running this command will make lots of magic happen. Your terminal will fill with color, and you’ll see several options for customizing your text-based user interface:
Apart from displaying colorful text in a variety of styles, this demo also illustrates a few more of Rich’s exciting features.
You can wrap and justify text. You can easily display any Unicode characters, as well as a wide choice of emojis. Rich renders Markdown and offers a syntax for creating elegantly formatted tables.
There’s much more that you can do with Rich, as you’ll discover throughout this tutorial. You can also try some of the command-line demos that Rich has thoughtfully provided for its subpackages, so you can get a flavor of what each one can do without writing any code.
Here are a few that you can try from your OS console. Execute them one by one to get a feel for the power of Rich:
(venv) $ python -m rich.table
(venv) $ python -m rich.progress
(venv) $ python -m rich.status
Most of these demos are very short, but if necessary, you can always interrupt them with Ctrl+C.
With the installation done, you’re ready to start exploring Rich.
Read the full article at https://realpython.com/python-rich-package/ »
[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]
November 27, 2023 02:00 PM UTC
Python GUIs
How to Create a Custom Title Bar for a PyQt Window — Customize Your Python App's Title Bars
PyQt provides plenty of tools for creating unique and visually appealing graphical user interfaces (GUIs). One aspect of your applications that you may not have considered customizing is the title bar. The title bar is the topmost part of the window, where your users find the app's name, window controls & other elements.
This part of the window is usually drawn by the operating system or desktop environment and it's default look & feel may not gel well with the rest of your application. However, you may want to customize it to add additional functionality. For example, in web browsers the document tabs are now typically collapsed into the title bar to maximize available space for viewing pages.
In this tutorial, you will learn how to create custom title bars in PyQt. By the end of this tutorial, you will have the necessary knowledge to enhance your PyQt applications with personalized and (hopefully!) stylish title bars.
Creating Frameless Windows in PyQt
The first step to providing a PyQt application with a custom title bar is to remove the default title bar and window decoration provided by the operating system. If we don't take this step, we'll end up with multiple title bars at the top of our windows.
In PyQt, we can create a frameless window using the setWindowFlags() method available on all QWidget subclasses, including QMainWindow. We call this method, passing in the FramelessWindowHint flag, which lives in the Qt namespace under the WindowType enumeration.
Here's the code of a minimal PyQt app whose main window is frameless:
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QApplication, QMainWindow
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Custom Title Bar")
self.resize(400, 200)
self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
if __name__ == "__main__":
app = QApplication([])
window = MainWindow()
window.show()
app.exec()
After importing the required classes, we create a window by subclassing QMainWindow. In the class initializer method, we set the window's title and resize the window using the resize() method. Then we use the setWindowFlags() to make the window frameless. The rest is the usual boilerplate code for creating PyQt applications.
If you run this app from your command line, you'll get the following window on your screen:
A frameless window in PyQt
As you can see, the app's main window doesn't have a title bar or any other decoration. It's only a gray rectangle on your screen.
Because the window has no buttons, you need to press Alt-F4 on Windows and Linux or Cmd+Q on macOS to close the app.
This isn't very helpful, of course, but we'll be adding back in our custom title bar shortly.
Setting Up the Main Window
Before creating our custom title bar, we'll finish the initialization of our app's main window, import some additional classes and create the window's central widget and layouts.
Here's the code update:
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import (
QApplication,
QLabel,
QMainWindow,
QVBoxLayout,
QWidget,
)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Custom Title Bar")
self.resize(400, 200)
self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
central_widget = QWidget()
self.title_bar = CustomTitleBar(self)
work_space_layout = QVBoxLayout()
work_space_layout.setContentsMargins(11, 11, 11, 11)
work_space_layout.addWidget(QLabel("Hello, World!", self))
centra_widget_layout = QVBoxLayout()
centra_widget_layout.setContentsMargins(0, 0, 0, 0)
centra_widget_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
centra_widget_layout.addWidget(self.title_bar)
centra_widget_layout.addLayout(work_space_layout)
central_widget.setLayout(centra_widget_layout)
self.setCentralWidget(central_widget)
# ...
First, we import the QLabel, QVBoxLayout, and QWidget classes. In our window's initializer, we create a central widget by instantiating QWidget(). Next, we create an instance attribute called title_bar by instantiating a class called CustomTitleBar. We still need to implement this class -- we'll do this in a moment.
The next step is to create a layout for our window's workspace. In this example, we're using a QVBoxLayout, but you can use the layout that better fits your needs. We also set some margins for the layout content and added a label containing the phrase "Hello, World!".
Next, we create a global layout for our central widget. Again, we use a QVBoxLayout. We set the layout's margins to 0 and aligned it on the top of our frameless window. In this layout, we need to add the title bar at the top and the workspace at the bottom. Finally, we set the central widget's layout and the app's central widget.
That's it! We have all the boilerplate code we need for our window to work correctly. Now we're ready to write our custom title bar.
Creating a Custom Title Bar for a PyQt Window
In this section, we will create a custom title bar for our main window. To do this, we will create a new class by inheriting from QWidget. First, go ahead and update your imports like in the code below:
from PyQt6.QtCore import QSize, Qt
from PyQt6.QtGui import QPalette
from PyQt6.QtWidgets import (
QApplication,
QHBoxLayout,
QLabel,
QMainWindow,
QStyle,
QToolButton,
QVBoxLayout,
QWidget,
)
# ...
Here, we've imported a few new classes. We will use these classes as building blocks for our title bar. Without further ado, let's get into the title bar code. We'll introduce the code in small consecutive chunks to facilitate the explanation. Here's the first piece:
# ...
class CustomTitleBar(QWidget):
def __init__(self, parent):
super().__init__(parent)
self.setAutoFillBackground(True)
self.setBackgroundRole(QPalette.ColorRole.Highlight)
self.initial_pos = None
title_bar_layout = QHBoxLayout(self)
title_bar_layout.setContentsMargins(1, 1, 1, 1)
title_bar_layout.setSpacing(2)
In this code snippet, we create a new class by inheriting from QWidget. This way, our title bar will have all the standard features and functionalities of any PyQt widgets. In the class initializer, we set autoFillBackground to true because we want to give a custom color to the bar. The next line of code sets the title bar's background color to QPalette.ColorRole.Highlight, which is a blueish color.
The next line of code creates and initializes an instance attribute called initial_pos. We'll use this attribute later on when we deal with moving the window around our screen.
The final three lines of code allow us to create a layout for our title bar. Because the title bar should be horizontally oriented, we use a QHBoxLayout class to structure it.
The piece of code below deals with our window's title:
# ...
class CustomTitleBar(QWidget):
def __init__(self, parent):
# ...
self.title = QLabel(f"{self.__class__.__name__}", self)
self.title.setStyleSheet(
"""font-weight: bold;
border: 2px solid black;
border-radius: 12px;
margin: 2px;
"""
)
self.title.setAlignment(Qt.AlignmentFlag.AlignCenter)
if title := parent.windowTitle():
self.title.setText(title)
title_bar_layout.addWidget(self.title)
The first line of new code creates a title attribute. It's a QLable object that will hold the window's title. Because we want to build a cool title bar, we'd like to add some custom styling to the title. To do this, we use the setStyleSheet() method with a string representing a CSS style sheet as an argument. The style sheet tweaks the font, borders, and margins of our title label.
Next, we center the title using the setAlignment() method with the Qt.AlignmentFlag.AlignCenter flag as an argument.
In the conditional statement, we check whether our window has a title. If that's the case, we set the text of our title label to the current window's title. Finally, we added the title label to the title bar layout.
The next step in our journey to build a custom title bar is to provide standard window controls. In other words, we need to add the minimize, maximize, close, and normal buttons. These buttons will allow our users to interact with our window. To create the buttons, we'll use the QToolButton class.
Here's the required code:
# ...
class CustomTitleBar(QWidget):
def __init__(self, parent):
# ...
# Min button
self.min_button = QToolButton(self)
min_icon = self.style().standardIcon(
QStyle.StandardPixmap.SP_TitleBarMinButton
)
self.min_button.setIcon(min_icon)
self.min_button.clicked.connect(self.window().showMinimized)
# Max button
self.max_button = QToolButton(self)
max_icon = self.style().standardIcon(
QStyle.StandardPixmap.SP_TitleBarMaxButton
)
self.max_button.setIcon(max_icon)
self.max_button.clicked.connect(self.window().showMaximized)
# Close button
self.close_button = QToolButton(self)
close_icon = self.style().standardIcon(
QStyle.StandardPixmap.SP_TitleBarCloseButton
)
self.close_button.setIcon(close_icon)
self.close_button.clicked.connect(self.window().close)
# Normal button
self.normal_button = QToolButton(self)
normal_icon = self.style().standardIcon(
QStyle.StandardPixmap.SP_TitleBarNormalButton
)
self.normal_button.setIcon(normal_icon)
self.normal_button.clicked.connect(self.window().showNormal)
self.normal_button.setVisible(False)
In this code snippet, we define all the required buttons by instantiating the QToolButton class. The minimize, maximize, and close buttons follow the same pattern. We create the button, define an icon for the buttons at hand, and set the icon using the setIcon() method.
Note that we use the standard icons that PyQt provides. For example, the minimize button uses the SP_TitleBarMinButton icon. Similarly, the maximize and close buttons use the SP_TitleBarMaxButton and SP_TitleBarCloseButton icons. We find all these icons in the QStyle.StandardPixmap namespace.
Finally, we connect the button's clicked() signal with the appropriate slot. For the minimize buttons, the proper slot is .showMinimized(). For the maximize and close buttons, the right slots are .showMaximized() and close(), respectively. All these slots are part of the main window's class.
The normal button at the end of the above code uses the SP_TitleBarNormalButton icon and showNormal() slot. This button has an extra setting. We've set its visibility to False, meaning that the button will be hidden by default. It'll only appear when we maximize the window to allow us to return to the normal state.
Now that we've created and tweaked the buttons, we must add them to our title bar. To do this, we can use the following for loop:
# ...
class CustomTitleBar(QWidget):
def __init__(self, parent):
# ...
buttons = [
self.min_button,
self.normal_button,
self.max_button,
self.close_button,
]
for button in buttons:
button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
button.setFixedSize(QSize(28, 28))
button.setStyleSheet(
"""QToolButton { border: 2px solid white;
border-radius: 12px;
}
"""
)
title_bar_layout.addWidget(button)
This loop iterates over our four buttons in a predefined order. The first thing to do inside the loop is to define the focus policy of each button. We don't want these buttons to steal focus from buttons in the window's working space , therefore we set their focus policy to NoFocus.
Next, we set a fixed size of 28 by 28 pixels for the three buttons using the setFixedSize() method with a QSize object as an argument.
Our main goal in this section is to create a custom title bar. A handy way to customize the look and feel of PyQt widgets is to use CSS style sheets. In the above piece of code, we use the setStyleSheet() method to apply a custom CSS style sheet to our four buttons. The sheet defines a white and round border for each button.
The final line in the above code calls the addWidget() method to add each custom button to our title bar's layout. That's it! We're now ready to give our title bar a try. Go ahead and run the application from your command line. You'll see a window like the following:
A PyQt window with a custom title bar
This is pretty simple styling, but you get the idea. You can tweak the title bar further, depending on your needs. For example, you can change the colors and borders, customize the title's font, add other widgets to the bar, and more.
We'll apply some nicer styles later, once we have the functionality in place! Keep reading.
Even though the title bar looks different, it has limited functionality. For example, if you click the maximize button, then the window will change to its maximized state. However, the normal button won't show up to allow you to return the window to its previous state.
In addition to this, if you try to move the window around your screen, you'll quickly notice a problem: it's impossible to move the window!
In the following sections, we'll write the necessary code to fix these issues and make our custom title bar fully functional. To kick things off, let's start by fixing the state issues.
Updating the Window's State
To fix the issue related to the window's state, we'll write two new methods. We need to override one method and write another. In the MainWindow class, we'll override the changeEvent() method. The changeEvent() method is called directly by Qt whenever the window state changes: for example if the window is maximized or hidden. By overriding this event we can add our own custom behavior.
Here's the code that overrides the changeEvent() method:
from PyQt6.QtCore import QSize, Qt, QEvent
# ...
class MainWindow(QMainWindow):
# ...
def changeEvent(self, event):
if event.type() == QEvent.Type.WindowStateChange:
self.title_bar.window_state_changed(self.windowState())
super().changeEvent(event)
event.accept()
This method is fairly straightforward. We check the event type to see if it is a WindowStateChange. If that's the case, we call the window_state_changed() method of our custom title bar, passing the current window's state as an argument. In the final two lines, we call the parent class's changeEvent() method and accept the event to signal that we've correctly processed it.
Here's the implementation of our window_state_changed() method:
# ...
class CustomTitleBar(QWidget):
# ...
def window_state_changed(self, state):
if state == Qt.WindowState.WindowMaximized:
self.normal_button.setVisible(True)
self.max_button.setVisible(False)
else:
self.normal_button.setVisible(False)
self.max_button.setVisible(True)
This method takes a window's state as an argument. Depending on the value of the state parameter we will optionally show and hide the maximize & restore buttons.
First, if the window is currently maximized we will show the normal button and hide the maximize button. Alternatively, if the window is currently not maximized we will hide the normal button and show the maximize button.
The effect of this, together with the order we added the buttons above, is that when you maximize the window the maximize button will appear to be replaced with the normal button. When you restore the window to it's normal size, the normal button will be replaced with the maximize button.
Go ahead and run the app again. Click the maximize button. You'll note that when the window gets maximized, the middle button changes its icon. Now you have access to the normal button. If you click it, then the window will recover its previous state.
Handling Window's Moves
Now it's time to write the code that enables us to move the window around the screen while holding your mouse's left-click button on the title bar. To fix this issue, we only need to add code to the CustomTitleBar class.
In particular, we need to override three mouse events:
mousePressEvent()will let us know when the user clicks on our custom title bar using the mouse's left-click button. This may indicate that the window movement should start.mouseMoveEvent()will let us process the window movements.mouseReleaseEvent()will let us know when the user has released the mouse's left-click button so that we can stop moving the window.
Here's the code that overrides the mousePressEvent() method:
# ...
class CustomTitleBar(QWidget):
# ...
def mousePressEvent(self, event):
if event.button() == Qt.MouseButton.LeftButton:
self.initial_pos = event.position().toPoint()
super().mousePressEvent(event)
event.accept()
In this method, we first check if the user clicks on the title bar using the mouse's left-click button. If that's the case, then we update our initial_pos attribute to the clicked point. Remember that we defined initial_pos and initialized it to None back in the __init__() method of CustomTitleBar.
Next, we need to override the mousePressEvent() method. Here's the required code:
# ...
class CustomTitleBar(QWidget):
# ...
def mouseMoveEvent(self, event):
if self.initial_pos is not None:
delta = event.position().toPoint() - self.initial_pos
self.window().move(
self.window().x() + delta.x(),
self.window().y() + delta.y(),
)
super().mouseMoveEvent(event)
event.accept()
This if statement in mouseMoveEvent() checks if the initial_pos attribute is not None. If this condition is true, then the if code block executes because we have a valid initial position.
The first line in the if code block calculates the difference, ordelta, between the current and initial mouse positions. To get the current position, we call the position() method on the event object and convert that position into a QPoint object using the toPoint() method.
The following four lines update the position of our application's main window by adding the delta values to the current window position. The move() method does the hard work of moving the window.
In summary, this code updates the window position based on the movement of our mouse. It tracks the initial position of the mouse, calculates the difference between the initial position and the current position, and applies that difference to the window's position.
Finally, we can complete the mouseReleaseEvent() method:
# ...
class CustomTitleBar(QWidget):
# ...
def mouseReleaseEvent(self, event):
self.initial_pos = None
super().mouseReleaseEvent(event)
event.accept()
This method's implementation is pretty straightforward. Its purpose is to reset the initial position by setting it back to None when the mouse is released, indicating that the drag is complete.
That's it! Go ahead and run your app again. Click on your custom title bar and move the window around while holding the mouse's left-click button. Can you move the window? Great! Your custom title bar is now fully functional.
The completed code for the custom title bar is shown below.
from PyQt6.QtCore import QSize, Qt, QEvent
from PyQt6.QtGui import QPalette
from PyQt6.QtWidgets import (
QApplication,
QHBoxLayout,
QLabel,
QMainWindow,
QStyle,
QToolButton,
QVBoxLayout,
QWidget,
)
class CustomTitleBar(QWidget):
def __init__(self, parent):
super().__init__(parent)
self.setAutoFillBackground(True)
self.setBackgroundRole(QPalette.ColorRole.Highlight)
self.initial_pos = None
title_bar_layout = QHBoxLayout(self)
title_bar_layout.setContentsMargins(1, 1, 1, 1)
title_bar_layout.setSpacing(2)
self.title = QLabel(f"{self.__class__.__name__}", self)
self.title.setStyleSheet(
"""font-weight: bold;
border: 2px solid black;
border-radius: 12px;
margin: 2px;
"""
)
self.title.setAlignment(Qt.AlignmentFlag.AlignCenter)
if title := parent.windowTitle():
self.title.setText(title)
title_bar_layout.addWidget(self.title)
# Min button
self.min_button = QToolButton(self)
min_icon = self.style().standardIcon(
QStyle.StandardPixmap.SP_TitleBarMinButton
)
self.min_button.setIcon(min_icon)
self.min_button.clicked.connect(self.window().showMinimized)
# Max button
self.max_button = QToolButton(self)
max_icon = self.style().standardIcon(
QStyle.StandardPixmap.SP_TitleBarMaxButton
)
self.max_button.setIcon(max_icon)
self.max_button.clicked.connect(self.window().showMaximized)
# Close button
self.close_button = QToolButton(self)
close_icon = self.style().standardIcon(
QStyle.StandardPixmap.SP_TitleBarCloseButton
)
self.close_button.setIcon(close_icon)
self.close_button.clicked.connect(self.window().close)
# Normal button
self.normal_button = QToolButton(self)
normal_icon = self.style().standardIcon(
QStyle.StandardPixmap.SP_TitleBarNormalButton
)
self.normal_button.setIcon(normal_icon)
self.normal_button.clicked.connect(self.window().showNormal)
self.normal_button.setVisible(False)
# Add buttons
buttons = [
self.min_button,
self.normal_button,
self.max_button,
self.close_button,
]
for button in buttons:
button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
button.setFixedSize(QSize(28, 28))
button.setStyleSheet(
"""QToolButton { border: 2px solid white;
border-radius: 12px;
}
"""
)
title_bar_layout.addWidget(button)
def window_state_changed(self, state):
if state == Qt.WindowState.WindowMaximized:
self.normal_button.setVisible(True)
self.max_button.setVisible(False)
else:
self.normal_button.setVisible(False)
self.max_button.setVisible(True)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Custom Title Bar")
self.resize(400, 200)
self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
central_widget = QWidget()
self.title_bar = CustomTitleBar(self)
work_space_layout = QVBoxLayout()
work_space_layout.setContentsMargins(11, 11, 11, 11)
work_space_layout.addWidget(QLabel("Hello, World!", self))
centra_widget_layout = QVBoxLayout()
centra_widget_layout.setContentsMargins(0, 0, 0, 0)
centra_widget_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
centra_widget_layout.addWidget(self.title_bar)
centra_widget_layout.addLayout(work_space_layout)
central_widget.setLayout(centra_widget_layout)
self.setCentralWidget(central_widget)
def changeEvent(self, event):
if event.type() == QEvent.Type.WindowStateChange:
self.title_bar.window_state_changed(self.windowState())
super().changeEvent(event)
event.accept()
def window_state_changed(self, state):
self.normal_button.setVisible(state == Qt.WindowState.WindowMaximized)
self.max_button.setVisible(state != Qt.WindowState.WindowMaximized)
def mousePressEvent(self, event):
if event.button() == Qt.MouseButton.LeftButton:
self.initial_pos = event.position().toPoint()
super().mousePressEvent(event)
event.accept()
def mouseMoveEvent(self, event):
if self.initial_pos is not None:
delta = event.position().toPoint() - self.initial_pos
self.window().move(
self.window().x() + delta.x(),
self.window().y() + delta.y(),
)
super().mouseMoveEvent(event)
event.accept()
def mouseReleaseEvent(self, event):
self.initial_pos = None
super().mouseReleaseEvent(event)
event.accept()
if __name__ == "__main__":
app = QApplication([])
window = MainWindow()
window.show()
app.exec()
Making it a little more beautiful
So far we've covered the technical aspects of styling our window with a custom title bar, and added the code to make it function as expected. But it doesn't look great. In this section we'll take our existing code & tweak the styling and buttons to produce something that's a little more professional looking.
One common reason for wanting to apply custom title bars to a window is to integrate the title bar with the rest of the application. This technique is called a unified title bar and can be seen in some popular applications such as web browsers, or Spotify.

In this section we'll look at how we can reproduce the same effect in PyQt using a combination of stylesheets & icons. Below is a screenshot of the final result which we'll be building.

As you can see the window & the toolbar blend nicely together and the window has rounded corners. There are a few different ways to do this, but we'll cover a simple approach using Qt stylesheets to apply styling over the entire window.
In order to customize the shape of the window, we need to first tell the OS to stop drawing the default window outline and background for us. We do that by setting a window attribute on the window. This is similar to the flags we already discussed, in that it turns on & off different window manager behaviors.
# ...
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Custom Title Bar")
self.resize(400, 200)
self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
# ...
We've added a call to self.setAttribute which sets the attribute Qt.WidgetAttribute.WA_TranslucentBackground on the window. If you run the code now you will see the window has become transparent, with only the widget text & toolbar visible.
Next we'll tell Qt to draw a new custom background for us. If you've worked with QSS before, the most obvious way to apply curved edges to the window using QSS stylesheets would be to set border-radius: styles on the main window directly, e.g.
#...
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# ...
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
self.setStyleSheet("background-color: gray; border-radius: 10px;")
#...
However, if you try this you'll notice that it doesn't work. If you enable a translucent background, the background of the window is not drawn (including your styles). If you don't set translucent background, the window is filled to the edges with a solid color ignoring the border radius.
.
The good news is that, with a bit of lateral thinking, there is a simple solution. We already know that we can construct interfaces by nesting widgets in layouts. Since we can't style the border-radius of a window, but we can style any other widget, the solution is to simply add a container widget into our window & apply the curved-edge and background styles to that.
On our MainWindow object we already have a central widget which contains our layout, so we can apply the styles there.
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# ...
central_widget = QWidget()
# This container holds the window contents, so we can style it.
central_widget.setObjectName("Container")
central_widget.setStyleSheet("""#Container {
background: qlineargradient(x1:0 y1:0, x2:1 y2:1, stop:0 #051c2a stop:1 #44315f);
border-radius: 5px;
}""")
self.title_bar = CustomTitleBar(self)
# ...
We've taken the existing central_widget object and assigned an object name to it. This is a ID which we can use to refer to the widget from QSS, to apply our styles specifically to that widget.
If you're familiar with CSS you might expect that IDs like #Container must be unique. However, they are not: you can give multiple widgets the same object name if you like. So you can re-use this technique and QSS on multiple windows in your application without problems.
With this style applied on our window, we have a nice gradient background with curved corners.
Unfortunately, the title bar we created is drawn filled, and so the background and curved corners of our window are over-written. To make things look coherent we need to make our title bar also transparent by removing the background color & auto-fill behavior we set earlier.
We don't need to set any flags or attributes this widget because it is not a window. A QWidget object is transparent by default.
We can also make some tweaks to the style of the title label, such as adjusting the font size and making the title capitalized using text-transform: uppercase -- feel free to customize this yourself.
class CustomTitleBar(QWidget):
def __init__(self, parent):
super().__init__(parent)
# self.setAutoFillBackground(True) # <-- remove
# self.setBackgroundRole(QPalette.ColorRole.Highlight) # <-- remove
self.initial_pos = None
title_bar_layout = QHBoxLayout(self)
title_bar_layout.setContentsMargins(1, 1, 1, 1)
title_bar_layout.setSpacing(2)
self.title = QLabel(f"{self.__class__.__name__}", self)
self.title.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.title.setStyleSheet("""
QLabel { text-transform: uppercase; font-size: 10pt; margin-left: 48px; }
""")
QSS is very similar to CSS, especially for text styling.
The margin-left: 48px is to compensate for the 3 * 16px window icons on the right hand side so the text align centrally.
The icons are currently using built-in Qt icons which are a little bit plain & ugly. Next let's update the icons, using custom SVG icons of simple colored circles, for the minimize, maximize, close & restore buttons.
from PyQt6.QtGui import QIcon
# ...
class CustomTitleBar(QWidget):
def __init__(self, parent):
super().__init__(parent)
# ...
# Min button
self.min_button = QToolButton(self)
min_icon = QIcon()
min_icon.addFile('min.svg')
self.min_button.setIcon(min_icon)
self.min_button.clicked.connect(self.window().showMinimized)
# Max button
self.max_button = QToolButton(self)
max_icon = QIcon()
max_icon.addFile('max.svg')
self.max_button.setIcon(max_icon)
self.max_button.clicked.connect(self.window().showMaximized)
# Close button
self.close_button = QToolButton(self)
close_icon = QIcon()
close_icon.addFile('close.svg') # Close has only a single state.
self.close_button.setIcon(close_icon)
self.close_button.clicked.connect(self.window().close)
# Normal button
self.normal_button = QToolButton(self)
normal_icon = QIcon()
normal_icon.addFile('normal.svg')
self.normal_button.setIcon(normal_icon)
self.normal_button.clicked.connect(self.window().showNormal)
self.normal_button.setVisible(False)
# ...
This code follows the same basic structure as before, but instead of using the built-in icons here we're loading our icons from SVG images. These images are very simple, consisting of a single circle in green, red or yellow for the different states mimicking macOS.
The normal.svg file for returning a maximized window to normal size shows a semi-transparent green circle for simplicity's sake, but you can include iconography and hover behaviors on the buttons if you prefer.
You can download these icons & all source code for this tutorial here: https://downloads.pythonguis.com/custom-title-bar-pyqt6.zip
The final step is to iterate through the created buttons, adding them to title bar layout. This is slightly tweaked from before to remove the border styling replacing it with simple padding & setting the icon sizes to 16px. Because we are using SVG files the icons will automatically scale to the available space.
class CustomTitleBar(QWidget):
def __init__(self, parent):
super().__init__(parent)
# ...
for button in buttons:
button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
button.setFixedSize(QSize(16, 16))
button.setStyleSheet(
"""QToolButton {
border: none;
padding: 2px;
}
"""
)
title_bar_layout.addWidget(button)
And that's it! With these changes, you can now run your application and you'll see a nice sleek modern-looking UI with unified title bar and custom controls.
The final result, showing our unified title bar and window design.
The complete code is shown below:
from PyQt6.QtCore import QEvent, QSize, Qt
from PyQt6.QtGui import QIcon
from PyQt6.QtWidgets import (
QApplication,
QHBoxLayout,
QLabel,
QMainWindow,
QStyle,
QToolButton,
QVBoxLayout,
QWidget,
)
class CustomTitleBar(QWidget):
def __init__(self, parent):
super().__init__(parent)
self.initial_pos = None
title_bar_layout = QHBoxLayout(self)
title_bar_layout.setContentsMargins(1, 1, 1, 1)
title_bar_layout.setSpacing(2)
self.title = QLabel(f"{self.__class__.__name__}", self)
self.title.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.title.setStyleSheet(
"""
QLabel { text-transform: uppercase; font-size: 10pt; margin-left: 48px; }
"""
)
if title := parent.windowTitle():
self.title.setText(title)
title_bar_layout.addWidget(self.title)
# Min button
self.min_button = QToolButton(self)
min_icon = QIcon()
min_icon.addFile("min.svg")
self.min_button.setIcon(min_icon)
self.min_button.clicked.connect(self.window().showMinimized)
# Max button
self.max_button = QToolButton(self)
max_icon = QIcon()
max_icon.addFile("max.svg")
self.max_button.setIcon(max_icon)
self.max_button.clicked.connect(self.window().showMaximized)
# Close button
self.close_button = QToolButton(self)
close_icon = QIcon()
close_icon.addFile("close.svg") # Close has only a single state.
self.close_button.setIcon(close_icon)
self.close_button.clicked.connect(self.window().close)
# Normal button
self.normal_button = QToolButton(self)
normal_icon = QIcon()
normal_icon.addFile("normal.svg")
self.normal_button.setIcon(normal_icon)
self.normal_button.clicked.connect(self.window().showNormal)
self.normal_button.setVisible(False)
# Add buttons
buttons = [
self.min_button,
self.normal_button,
self.max_button,
self.close_button,
]
for button in buttons:
button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
button.setFixedSize(QSize(16, 16))
button.setStyleSheet(
"""QToolButton {
border: none;
padding: 2px;
}
"""
)
title_bar_layout.addWidget(button)
def window_state_changed(self, state):
if state == Qt.WindowState.WindowMaximized:
self.normal_button.setVisible(True)
self.max_button.setVisible(False)
else:
self.normal_button.setVisible(False)
self.max_button.setVisible(True)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Custom Title Bar")
self.resize(400, 200)
self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
central_widget = QWidget()
# This container holds the window contents, so we can style it.
central_widget.setObjectName("Container")
central_widget.setStyleSheet(
"""#Container {
background: qlineargradient(x1:0 y1:0, x2:1 y2:1, stop:0 #051c2a stop:1 #44315f);
border-radius: 5px;
}"""
)
self.title_bar = CustomTitleBar(self)
work_space_layout = QVBoxLayout()
work_space_layout.setContentsMargins(11, 11, 11, 11)
work_space_layout.addWidget(QLabel("Hello, World!", self))
centra_widget_layout = QVBoxLayout()
centra_widget_layout.setContentsMargins(0, 0, 0, 0)
centra_widget_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
centra_widget_layout.addWidget(self.title_bar)
centra_widget_layout.addLayout(work_space_layout)
central_widget.setLayout(centra_widget_layout)
self.setCentralWidget(central_widget)
def changeEvent(self, event):
if event.type() == QEvent.Type.WindowStateChange:
self.title_bar.window_state_changed(self.windowState())
super().changeEvent(event)
event.accept()
def window_state_changed(self, state):
self.normal_button.setVisible(state == Qt.WindowState.WindowMaximized)
self.max_button.setVisible(state != Qt.WindowState.WindowMaximized)
def mousePressEvent(self, event):
if event.button() == Qt.MouseButton.LeftButton:
self.initial_pos = event.position().toPoint()
super().mousePressEvent(event)
event.accept()
def mouseMoveEvent(self, event):
if self.initial_pos is not None:
delta = event.position().toPoint() - self.initial_pos
self.window().move(
self.window().x() + delta.x(),
self.window().y() + delta.y(),
)
super().mouseMoveEvent(event)
event.accept()
def mouseReleaseEvent(self, event):
self.initial_pos = None
super().mouseReleaseEvent(event)
event.accept()
if __name__ == "__main__":
app = QApplication([])
window = MainWindow()
window.show()
app.exec()
Conclusion
In this tutorial, we have learned the fundamentals of creating custom title bars in PyQt. To do this, we have combined PyQt's widgets, layouts, and styling capabilities to create a visually appealing title bar for a PyQt app.
With this skill under your belt, you're now ready to create title bars that align perfectly with your application's unique style and branding. This will allow you to break away from the standard window decoration provided by your operating system and add a personal touch to your user interface.
Now let your imagination run and transform your PyQt application's UX.
November 27, 2023 01:00 PM UTC
PyCharm
What is the Django Web Framework?
In this blog post, we’ll introduce you to web frameworks and explore what they are and why they’re vital if you want to develop apps rapidly without compromising on security. More importantly, we’ll explain exactly what the Django web framework is – an open-source web framework for building web applications using Python. Whether you’re a […]
November 27, 2023 08:00 AM UTC
scikit-learn
My mentored internship at scikit-learn
How it is to be an Intern at scikit-learn
My name is Stefanie Senger, and I recently concluded a five-month mentored internship at scikit-learn, that had been funded by NumFocus as a Small Development Grant with a clear focus on fostering diversity in open-source projects. The idea to couple a grant with mentorship traces back to Maren Westermann’s initiative. She envisioned a pathway to integrate more female coders into scikit-learn through internships and support. Scikit-learn would profit from fresh perspectives and some disruption. I was the guinea pig for an initial experiment, as Maren later told me.
Starting the Internship
As someone transitioning from a non-technical background to coding, working on scikit-learn was a big thing for me. I had participated in and taught at a data science boot camp, searching diligently for a first role in the field. I never doubted I could tackle more difficult tech challenges over time, but I knew there was much to learn. Scikit-learn had a heavy-tech aura to me, and when I discovered the internship ad, I just thought: this. I was genuinely taken aback when accepted for the role, though. There are many more experienced people looking for such an opportunity, after all.
When I got to know better both my mentors, Adrin Jalali and Guillaume Lemaitre, it became quickly clear that only effort was required, and I could ask them any question along the way. I felt very welcome in the community, also by the other people I interacted with on GitHub.
What I Worked on
I began by working on documentation and examples such as “Multi-class AdaBoosted Decision Trees,” to make those more comprehensive and helpful for users. Then some maintenance tasks on the code that were repetitive so I could find out what to do from other contributors’ pull requests. Guillaume discovered that one AdaBoost algorithm required deprecation, and it fell on me to execute this. I had never looked at such a huge code base with so many layers of abstraction, and I had to learn quite some more Python to be able to go ahead. I even got the opportunity to present an “Intro to scikit-learn” workshop at EuroSciPy, the European conference on the scientific use of Python in Basel, where I also got to know many other contributors and people from the scikit-learn team at Inria.
Adrin introduced me to the challenging task of implementing a new feature for metadata routing, developed over many years by the scikit-learn community. It allows users to set metadata, such as sample weights, in meta estimators, that can be routed to sub-estimators and other algorithms that are able to consume it. This was partly uncharted territory and meant finding solutions where there was no predefined path and adapting tests to match the expected behavior. In the last two months of my internship, I implemented metadata routing into some meta-estimators, which was tremendously difficult but, once accomplished, has nourished my professional confidence since.
Mentorship in Action
Let me describe how the mentoring worked because Guillaume’s and Adrin’s support was invaluable. They would both literally drop their tasks when I had questions and right away hint me in the right direction. I met Adrin twice a week, and we would co-work while I would throw questions at him. Guillaume was available remotely, and I knew he would jump into a video call with me when I needed help. They both gave reviewing my PRs a priority, and I got feedback on my work regularly.
It was essential to have mentors signaling that it’s okay to be learning and to propose tasks to me. If I had come into the project individually, I might have hesitated to take on most of the issues I ended up working on, fearing that my skills were insufficient and that I would hinder the progress of the project rather than help it. The mentoring setting gave me a justification to try things that I wasn’t sure if I could do.
Becoming a Community Member
Looking ahead, I will continue contributing to scikit-learn. As I’ve gotten to know quite a few of the other contributors in person, I now feel part of the community. I know they care about values like openness and diversity, that I share, and while acknowledging the complexity of the code base, I know what I can learn from taking on issues and the sense of accomplishment when merging my solution into the main branch. And I love contributing to something meaningful, which is something I had always sought.
November 27, 2023 12:00 AM UTC
November 26, 2023
Paolo Melchiorre
2024 Django Software Foundation board nomination
My self-nomination statement for the 2024 Django Software Foundation (DSF) board of directors elections
November 26, 2023 11:00 PM UTC
TestDriven.io
Django REST Framework and Elasticsearch
This tutorial looks at how to integrate Django REST Framework with Elasticsearch.
November 26, 2023 10:28 PM UTC
November 25, 2023
CodersLegacy
pydub Tutorial: Audio Manipulation in Python
In this comprehensive tutorial, we will explore the powerful pydub library, a Python package that simplifies the process of working with audio files. Whether you are a music enthusiast, a data scientist, or a developer looking to integrate audio processing into your applications, pydub has got you covered.
Getting Started with Pydub
Before we dive into the wonders of Pydub, let’s make sure you have it installed. Open your terminal or command prompt and type:
pip install pydub
Now that Pydub is installed, let’s import it into our Python script or Jupyter notebook:
from pydub import AudioSegmentSetup ffmpeg
If you’re working with audio files that use certain codecs (e.g. mp3), you might need to install additional dependencies, such as ffmpeg. Without this, there is a good chance you will not be able to even load any audio files, much less play them.
To install this dependency, visit the official website and download the bundle for your respective OS. Unzip the folder, and you will see a bin folder, which contains a bunch of EXE’s. Copy the path to this bin folder (path should include the bin folder).
You now have two options.
- You can add this path to your environment variables.
- You can use this path inside your code, when initializing the AudioSegment class (imported in the previous step).
pydub.AudioSegment.ffmpeg = "/absolute/path/to/ffmpeg/bin"Loading and Playing Audio
The first step in audio manipulation is loading a sound file, and being able to play it. In order to load an audio file using pydub, we will use the AudioSegment class we imported in the previous step.
audio = AudioSegment.from_file("countdown.mp3", format="mp3")Playing audio is a little bit trickier, as pydub is an audio manipulation library, not an audio player library. It can only natively play .wav files, which are compatible with native Python. We have two options,
- Convert the audio into .wav format.
- Download a library like Python simpleaudio, or pyAudio which are design specifically to play a wide range of audio files.
The second option is recommended due to several issues which commonly occur on windows systems with the first option. All you have to do is install either simpleaudio or pyAudio, and pydub will automatically use them internally to play the audio. No need to make any changes to your code.
pip install pyAudio
pip install simpleaudio
Once you have installed either one of the two libraries, run the following:
from pydub import AudioSegment
from pydub.playback import play
# Load an audio file
audio = AudioSegment.from_file("countdown.mp3", format="mp3")
# Play the audio
play(audio)If you want an audio file of your own to practice on and follow along with this tutorial, here is a download link to the one that we are using:
Download “Sample Audio (countdown.mp3)”
countdown.mp3 – Downloaded 30 times – 410.29 KBSlicing, Concatenating, and Repeating
Pydub makes it easy to slice and concatenate audio segments using list indexing. It uses milliseconds as the unit of measurement, allowing you to be very specific about where you slice from.
# Slice the audio from 1 to 3 seconds
audio1 = audio[1000:3000]
# Slice the audio from 6 to 9 seconds
audio2 = audio[6000:9000]
# Concatenate two audio segments
combined = audio1 + audio2Every operation you perform returns a new audio segment, which means that the “segments” you sliced are also AudioSegment objects. It is also worth nothing, that AudioSegment objects are immutable, meaning that applying an operation on them returns a new object, instead of modifying the existing object.
You can check the duration of a audio segment by accessing the duration_seconds attribute.
without_the_middle.duration_secondsYou can also repeat the audio clip by using the multiplication operator:
# repeat the clip twice
repeated_audio = audio * 2Applying Effects
Add creative effects to your audio with Pydub’s built-in effects:
# Apply a fade-in over the first 5 seconds
faded_in = audio.fade_in(5000)
# Apply a fade-out over the last 5 seconds
faded_out = audio.fade_out(5000)
# Increase the volume by 10 dB
louder = audio + 10
# Lower the volume by 10 dB
louder = audio - 10You can get the volume of an audio clip by using the dBFS property, which is the decibels relative to the maximum possible loudness.
loudness = audio.dBFSYou can also check for the volume at specific points:
loudness = audio[1000].dBFSThis will return -infinity if the volume is zero.
Changing Audio Parameters
You can adjust various parameters of an audio file, such as the sample rate, channels, and bit depth:
# Change the sample rate to 44100 Hz
audio = audio.set_frame_rate(44100)
# Set the number of channels to mono
audio = audio.set_channels(1)
# Set the bit depth to 16 bits
audio = audio.set_sample_width(2)You can also get the current framerate of an audio clip using the following property:
audio.frame_rateFiltering Audio Frequencies
Filtering allows you to manipulate the frequency content of your audio:
# Apply a low-pass filter with a cutoff frequency of 1000 Hz
low_pass = audio.low_pass_filter(1000)
# Apply a high-pass filter with a cutoff frequency of 1000 Hz
high_pass = audio.high_pass_filter(1000)Exporting Audio
After making changes to your audio, you might want to save the result. Pydub makes exporting audio just as simple:
# Export the modified audio to wav format
audio.export("output.wav", format="wav")
# Export the modified audio to FLAC format
audio.export("output.flac", format="flac")Overlaying Audio with Pydub
Pydub provides a powerful method called overlay() that allows you to combine two audio segments to play simultaneously. This method is available on any audio segment object, and takes as a parameter another audio segment object to be overlaid. If the audio segment object to be overlaid is longer than the audio segment, then it will be cut-off at the end.
from pydub import AudioSegment
# Load two audio files
audio1 = AudioSegment.from_file("audio1.wav")
audio2 = AudioSegment.from_file("audio2.wav")
# Overlay sounds to play simultaneously
combined_audio = audio1.overlay(audio2)You can loop the overlaid sound to extend its duration. If audio2 is shorter than audio1, it will be repeated until audio1 ends:
# Repeat sound2 until sound1 ends
new_audio = audio1.overlay(audio2, loop=True)Conclusion
Congratulations! You’ve now embarked on a journey through the exciting landscape of audio manipulation in Python with Pydub. From basic tasks like loading and playing audio to advanced manipulations such as applying effects and filtering, Pydub empowers you to explore the creative and technical aspects of audio processing.
This tutorial only scratches the surface of Pydub and its capabilities. As you continue your exploration, don’t forget to check out the official Pydub documentation for more in-depth information and examples. Now, armed with this knowledge, go forth and create amazing audio projects with Python and Pydub!
The post pydub Tutorial: Audio Manipulation in Python appeared first on CodersLegacy.
November 25, 2023 08:49 AM UTC
November 24, 2023
Go Deh
Google Bard vs Microsoft Bing Chat: Vanity questions
I ask two questions about my activities online and compare the outputs from the two AI tools.
Google Bard
Sure, here is a Rosetta Code task in the style of user paddy3118:
Task:
Write a program that generates the Godeh series. The Godeh series is an infinite sequence of integers that is defined by the following recursive formula:
godeh(n) = {
1, if n = 1
godeh(n // 2), if n is even
godeh(n // 2) + godeh(n // 2 + 1), if n is odd
}
Input:
An integer n.
Output:
The first n terms of the Godeh series.
Example:
Input: 10
Output:
1
1
2
1
3
2
3
1
4
3
Solution:
def godeh(n):
if (n == 1):
return 1
elif (n % 2 == 0):
return godeh(n // 2)
else:
return godeh(n // 2) + godeh(n // 2 + 1)
n = 10
for i in range(1, n + 1):
print(godeh(i))
Paddy3118's style:
Paddy3118's programs are typically well-written and easy to understand. They are also often accompanied by clear and concise explanations of the code. In this task, Paddy3118 would likely provide a brief explanation of the Godeh series and the recursive formula used to define it. He would then provide a Python implementation of the program, along with an example of how to use it.
Here is an example of a task that Paddy3118 might write:
Task:
Write a program that generates the prime numbers up to a specified input.
Input:
An integer n.
Output:
A list of all the prime numbers up to n.
Example:
Input: 100
Output:
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
Solution:
def is_prime(n):
if (n <= 1):
return False
if (n <= 3):
return True
if (n % 2 == 0 or n % 3 == 0):
return False
i = 5
while (i * i <= n):
if (n % i == 0 or n % (i + 2) == 0):
return False
i += 6
return True
def prime_numbers_up_to(n):
primes = []
for i in range(2, n + 1):
if (is_prime(i)):
primes.append(i)
return primes
n = 100
primes = prime_numbers_up_to(n)
print(primes)
Microsoft Bing Chat
…
Bard vs Bing
- Bard attempts to write a task in my style, and gives some tips on my style.
- Bing cannot create the task, but does extract some info on me.
- Clearly a Bard win.
- Flattery will get them nowhere! (But keep it up mind you).
Google Bard #2
what does paddy3118 do online?
Paddy3118 is an active participant in the online programming community, particularly in areas related to mathematics and algorithms. They have authored numerous programs and solutions to coding challenges, demonstrating expertise in various programming languages and problem-solving techniques. Paddy3118's contributions can be found across various platforms, including:
GitHub: Paddy3118 has a notable presence on GitHub, where they have authored over 50 programs covering a wide range of topics, including:
Mathematical algorithms: Implementing various mathematical concepts and algorithms, such as the Godeh series generator, Collatz conjecture tester, and prime number generator.
Problem-solving challenges: Tackling coding challenges from platforms like Codewars, showcasing their ability to solve complex problems efficiently.
Data manipulation and analysis: Demonstrating proficiency in handling and analyzing data through programs like the factorial calculator and Fibonacci sequence generator.
Their GitHub repositories often include detailed explanations of the code, making them valuable learning resources for other programmers.
Stack Overflow: Paddy3118 is an active contributor on Stack Overflow, a question-and-answer website for programmers. They have answered over 1,000 questions with an average score of 4.5 out of 5, indicating their deep understanding of programming concepts and their willingness to share their knowledge with others.
Codewars: Paddy3118 is a skilled competitor on Codewars, a coding challenge platform. They have solved over 1,000 coding challenges and currently rank in the top 1% of users. This achievement highlights their ability to think creatively and apply their programming expertise to solve complex problems.
GitHub Discussions: Paddy3118 actively engages in discussions on GitHub Discussions, a forum where users discuss technical topics. They provide helpful advice, share insights, and contribute to the overall knowledge base of the community.
Overall, Paddy3118 is a valuable member of the online programming community, recognized for their contributions to various platforms and their willingness to share their knowledge and expertise with others.
Microsoft Bing Chat #2
Bard vs Bing #2
- Bard has found me on numerous sites and attempted to summarise my contributions to them and comments that I like to help
- Bing states up front that it could not find me but then quotes an extract based on my RosettaCode talk page.
- None mention Python!?!
- Another win for Bard.

Stefanie Senger
François Goupil