Feeds
Oliver Davies' daily list: Find bugs sooner
Whilst speaking with Dave Liddament last week, I remembered a slide I've seen in some of his previous presentations, such as Effective Code Review.
On the slide is a graph that shows the amount of time needed to fix a bug at different stages of the development lifecycle.
Fixing a bug is the quickest and simplest when writing code.
It's harder when being code reviewed or QA or client tested, more so when it's being released, and the most difficult and expensive once it's in the maintenance phase.
Once it's live in the production environment, there's additional cleanup work to do which adds to the time and cost, and potentially damages your reputation.
The sooner you can find a bug, the better.
This is where tools such as automated testing, static analysis and CI pipelines shine and make it easier for you to find and fix potential bugs sooner.
KDE Goals - A New Cycle Begins
The KDE community has charted its course for the coming years, focusing on three interconnected paths that converge on a single point: community. These paths aim to improve user experience, support developers, and foster community growth.
Streamlined Application Development ExperienceThis goal focuses on improving the application development process. By making it easier for developers to create applications, KDE hopes to attract more contributors and deliver better software for both first-party and third-party applications. A notable task within this goal is enhancing the experience of building KDE apps with languages beyond C++, such as Rust or Python.
Champions: Nicolas Fella and Nate Graham
We care about your InputKDE has a diverse users base with unique input needs: artists using complex monitor and drawing tablet setups; gamers with controllers, fancy mice, and handhelds; users requiring accessibility features or using a language optimally types with complex input methods; students with laptops, 2-in-1s, and tablets — and more! While KDE has made significant progress in supporting these diverse sources of input over the years, there are still gaps to be addressed. This goal aims to close those gaps and deliver a truly seamless input experience for everyone.
Champions: Gernot Schiller, Jakob Petsovits and Joshua Goins
KDE Needs You! 🫵KDE’s growth depends on new contributors, but a lack of fresh involvement in key projects like Plasma, Kdenlive, Krita, GCompris, and others is a concern. This goal focuses on formalizing and enhancing recruitment processes, not just for individuals but also for institutions. Ensuring that bringing in new talent becomes a continuous and community-wide priority, vital for KDE's long-term sustainability.
Champions: Aniqa Khokhar, Johnny Jazeix and Paul Brown
Join us!Your voice, your code, and your ideas are what will shape the KDE of tomorrow — whether you're a user, developer, or contributor. Let’s go on this journey together and make these goals a reality!
Join the Matrix room and keep an eye on the website for the latest KDE Goals updates.
Python Morsels: Creating Python programs
Python programs are created in code editors, and can be run using system command prompt.
Table of contents
- The Python REPL
- Definitions: program, script, command-line, editor
- Creating a Python program in a code editor
- Running our Python program
- Printing to see program output
- Python programs are not launched from the REPL
- Different operating systems
- Python scripts are run from the command prompt or terminal
This is the Python REPL, also known as the Interactive Python Interpreter:
>>>How can I tell it's the Python REPL?
Well, the prompt shows three greater than signs (>>>), which indicates that we're in the Python REPL.
Typing all of your code in the Python REPL is pretty limiting.
Our code disappears right after we've typed it. Also, if we'd like someone to be able to import our code as a module, they can't.
Definitions: program, script, command-line, editorLet's make a Python program.
Read the full article: https://www.pythonmorsels.com/creating-python-programs/Django Weblog: Djangonaut Space - New session 2024
We are thrilled to announce that Djangonaut Space, a mentorship program, is open for applicants for our next cohort!
Djangonaut Space is holding a third session this year! This session will start on October 14th, 2024. We are accepting applications until September 14th, 2024. More details can be found in the website.
Djangonaut Space is a free, 8-week group mentoring program where individuals will work self-paced in a semi-structured learning environment. It seeks to help members of the community who wish to level up their current Django code contributions and potentially take on leadership roles in Django in the future.
“I signed up for this program with the goal of starting my journey as a contributor, but I ended up gaining so much more. In this community, I found incredible people who not only guide you toward solutions but also encourage and celebrate every achievement along the way.” - Raffaella, DjangonautIf you have questions, they are holding an AMA session on Zoom next week. See their social media account for more details:
Wim Leers: XB week 14: early christmas tree
How does cta1href sound to you? Gibberish, right? :D
Jesse “jessebaker” Baker pointed out that Experience Builder (XB) in its current state was subjecting its users to such nonsense! Fortunately, thanks to every Single Directory Component (SDC) specifying a title for each prop, we were able to automatically generate the much more readable CTA 1 link — thanks to foundations Ben “bnjmnm” Mullins did in #3461422 from 3 weeks prior.
Utkarsh “utkarsh_33” and Omkar “omkar-pd” Deshpande eliminated an extraneous “preview” request from the client, hence improving performance (as well as sanity).
Ben & Jesse made XB’s Cypress end-to-end tests leap massively ahead (especially compared to Drupal core’s use of Nightwatch), by introducing cypress-terminal-report. The resulting test failure output on GitLab CI makes it far easier to figure out where something is going wrong: a big productivity boost!
Two weeks ago I alluded to it, and now it finally happened: after months of getting basic infrastructure off the ground, we now finally were able to Kyle “ctrladel” Einecker’s set of representative SDCs that Lauri approved, Ivan “finnsky” Berdinsky and I reviewed, and Ted “tedbow” Bowman pushed across the finish line.
(Not everything Kyle proposed landed, because XB and the SDC subsystem do not yet have all the capabilities needed for some of the SDCs he wrote — see the follow-up if you’re interested.)
Issue #3446722, image by me.
Some of the people working full-time on XB are doing so using DDEV. And running end-to-end tests that use WebDriver with both the test runner and the system under test living in a Docker container turns out to be quite challenging! Besides us, we know that many (most?) in the community use a DDEV-based development environment, and we’d love to welcome as many contributors as possible. Not being able to run the most important tests of all then is of course quite a problem.
That’s why Travis “traviscarden” Carden had been diligently (he actually joined a few weeks ago!) working on making that painless. The result: the ddev-drupal-xb-dev DDEV add-on — once installed, running (and seeing!) the XB end-to-end tests requires only ddev xb-cypress-open :)
Missed a prior week? See all posts tagged Experience Builder.
Goal: make it possible to follow high-level progress by reading ~5 minutes/week. I hope this empowers more people to contribute when their unique skills can best be put to use!
For more detail, join the #experience-builder Slack channel. Check out the pinned items at the top!
Feliksas “f.mazeikis” Mazeikis is back full-time on XB, and he’s started working on the super important #3463999: Auto-create/update Component config entities for all discovered SDCs that meet XB’s minimum criteria. It’s a critical piece in making Lauri’s product vision come to life: it will ensure that any SDC that we’re confident will work in XB becomes available automatically.1 While working on that, he discovered that there was a pretty big oversight in the StorablePropShape work I landed two weeks ago: in some places Drupal core does not distinguish between “instance settings” and “storage settings” at all (and nor did the code I landed then), but in some places it actually does. Felix was running into that now, thanks to config validation, and so he fixed that.
Many of the things that happened this week were on the “enablement” side of things. Nonetheless, the XB UI also made progress:
- Harumi “hooroomoo” Jang and Jesse landed the updated “insert” UX and hierarchy view, based on the updated design.
- But, most importantly, nicely rounding out this post by combining “enablement” and improved UX: Bálint “balintbrews” Kléri implemented error boundaries. In principle, you should never see these, but we all occasionally have internet connection issues. At those times, Bálint’s “Try again” addition works beautifully :)
(And as a bonus it accelerates debugging failing server responses!)
Issue #3461431, image by Bálint.
Thanks to Travis for reviewing this!
Week 14 was August 12–18, 2024.
-
At this early stage, it’ll happen indiscriminately. Later, we’ll ensure that on production environments no new SDCs will be made available to the Content Creator without explicit approval by the Site Builder. See the first steps towards auditability of the available XB components if you’re interested in this aspect. ↩︎
The Drop Times: Vincenzo Gambino: A Drupal Architect from Palermo
Python Engineering at Microsoft: Python in Visual Studio Code – September 2024 Release
We’re excited to announce the September 2024 release of the Python and Jupyter extensions for Visual Studio Code!
This release includes the following announcements:
- Django unit test support
- Go to definition from inlay hints with Pylance
If you’re interested, you can check the full list of improvements in our changelogs for the Python, Jupyter and Pylance extensions.
Django unit test supportWe are excited to announce support for one of our most requested features: you can now discover and run Django unit tests through the Test Explorer!
In order to enable this feature, you will need to add a MANAGE_PY_PATH environment variable, pointing to your Django application’s manage.py file. To do so, you can follow these steps:
- Set "python.testing.unittestEnabled": true, in your settings.json file.
- Add MANAGE_PY_PATH as an environment variable:
- Create a .env file at the root of your project.
- Add MANAGE_PY_PATH='<path-to-manage.py>' to the .env file, replacing <path-to-manage.py> with the path to your application’s manage.py file.
Tip: You can copy the path by right clicking on the file in the Explorer view and selecting Copy Path.
- Add Django test arguments to "python.testing.unittestArgs": [] in the settings.json file as needed, and remove any arguments that are not compatible with Django.
Note: By default, the Python extension looks for and loads .env files at the project root. If your .env file is not at the project root or you are using VS Code variable substitution, add "python.envFile": "${workspaceFolder}/<path-to-.env>" to your settings.json file, so the Python extension can load the environment variables in this file when running and discovering tests. See our Python environment variables docs for more information on environment variables.
Navigate to the Testing view, and select the Refresh Tests button to have your Django tests displayed!
For troubleshooting tips, please see our Django testing docs. As you explore this newly added feature, please provide feedback and report any issues in our vscode-python repo or by using the Python: Report Issue command.
Go to definition from inlay hints with PylanceWhen enabling inlay hints with Pylance, you can now more conveniently navigate to a type’s definition through Ctrl+Click or Cmd+Click when hovering over it.
Other Changes and EnhancementsWe have also added small enhancements and fixed issues requested by users that should improve your experience working with Python and Jupyter Notebooks in Visual Studio Code. Some notable changes include:
- You can now access the VS Code Native REPL for Python from the Command Palette (Ctrl/Cmd + Shift + P) using Python: Start Native REPL(@vscode-python#23727)
- VS Code Native REPL for Python now starts at the project folder (@vscode-python#23821)
- Strings are now normalized when sending commands to the VS Code Native REPL (@vscode-python#23743)
- You can now restart the debugger when debugging tests through the debug control widget (@vscode-python#23752)
As we are planning and prioritizing future work, we value your feedback! Below are a few issues we would love feedback on:
- Design proposal for test coverage in (@vscode-python#22827)
Try out these new improvements by downloading the Python extension and the Jupyter extension from the Marketplace, or install them directly from the extensions view in Visual Studio Code (Ctrl + Shift + X or ⌘ + ⇧ + X). You can learn more about Python support in Visual Studio Code in the documentation. If you run into any problems or have suggestions, please file an issue on the Python VS Code GitHub page.
The post Python in Visual Studio Code – September 2024 Release appeared first on Python.
Real Python: The Real Python Podcast – Episode #219: Astrophysics and Astronomy With Python & PyCon Africa 2024
Are you interested in practicing your Python skills while learning how to solve astrophysics and astronomy problems? Christopher Trudeau is back on the show this week, bringing another batch of PyCoder's Weekly articles and projects.
[ 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 ]
mark.ie: The Confident: Mark Conroy's new Drupal agency
I've got some big news.
Web Review, Week 2024-36
On my way to Akademy, looking forward to meeting people there. Even though I’m traveling with spotty Internet access for now, let’s not loose good habits. Here is my web review for the week 2024-36.
The Internet Archive just lost its appeal over ebook lending - The VergeTags: tech, copyright, law, library
This is really bad news… Clearly the publishers cartel would try to outlaw libraries if they were invented today.
https://www.theverge.com/2024/9/4/24235958/internet-archive-loses-appeal-ebook-lending
Tags: tech, privacy, surveillance, advertisement
There, now this seems like a real thing… your phone recording you while you’re not aware for advertisement purposes. Nice surveillance apparatus. Thanks but no thanks.
https://futurism.com/the-byte/facebook-partner-phones-listening-microphone
Tags: tech, ai, machine-learning, gpt, art, learning, cognition
An excellent essay about generative AI and art. Goes deep in the topic and explains very well how you can hardly make art with those tools. It’s just too remote from how they work. I also particularly like the distinction between skill and intelligence. Indeed, we can make highly skilled but not intelligent systems using this technology.
https://www.newyorker.com/culture/the-weekend-essay/why-ai-isnt-going-to-make-art
Tags: tech, ai, machine-learning, gpt, criticism
Does a good job listing the main myths the marketing around generative AI is built on. Don’t fall for the marketing, exert critical thinking and rely on real properties of those systems.
https://www.techpolicy.press/challenging-the-myths-of-generative-ai/
Tags: tech, linux, rust, kernel, foss, politics
Interesting analysis. For sure the Rust for Linux drama tells something about the Linux kernel community and its complicated social norms.
https://sporks.space/2024/09/05/is-linux-collapsing-under-its-own-weight-on-rust-for-linux/
Tags: tech, linux, rust, kernel, politics, foss
Politics in the Linux kernel can indeed be tough. The alternative path proposed to the Rust-for-Linux team is indeed an interesting one, it could bear interesting results quickly.
https://drewdevault.com/2024/08/30/2024-08-30-Rust-in-Linux-revisited.html
Tags: tech, semantic, programming, protocols, language
Interesting view about the LSP specification, where it shines, and where it falls short.
https://www.michaelpj.com/blog/2024/09/03/lsp-good-bad-ugly.html
Tags: tech, python, community, data-visualization
Indeed this is a much better visualization. It shows quite well how the Python programmers pool is growing.
https://two-wrongs.com/python-programmers-experience
Tags: tech, oauth, security
Nice post explaining the basics of OAuth. If you wonder why the flow seems so convoluted, this article is for you.
https://stack-auth.com/blog/oauth-from-first-principles
Tags: tech, databases
Lots of things to keep in mind when dealing with databases. This is a nice list of “must know” for developers, false assumptions are widespread (and I fall in some of those traps myself from time to time).
https://rakyll.medium.com/things-i-wished-more-developers-knew-about-databases-2d0178464f78
Tags: tech, performance, latency
An old article but a good reminder: you have to choose between latency and throughput, you can’t have both in the same system.
Tags: tech, security, safety, memory, sandbox
Interesting point. As the memory safety of our APIs will increase, can we reduce the amount of sandboxing we need? This will never remove completely the need if only for logic bugs, but surely we could become more strategic about it.
https://alexgaynor.net/2024/aug/30/impact-of-memory-safety-on-sandboxing/
Tags: tech, tests, storage
That sounds like a very interesting tool to simulate and test potential data loss scenarios. This is generally a bit difficult to do, should make it easier.
https://github.com/dsrhaslab/lazyfs
Tags: tech, programming, craftsmanship
Good set of advices on naming variables, types, etc. Indeed this makes things easier to find in code bases.
https://morizbuesing.com/blog/greppability-code-metric/
Tags: tech, architecture, design, documentation, communication
Very good article. I wish I’d see more organisations writing such design documents. They help a lot, and that allows to have a way to track changes in the design. To me it’s part of the minimal set of documentation you’d want on any non trivial project.
https://ntietz.com/blog/reasons-to-write-design-docs/
Tags: tech, values, organization, team, management
Aligning people with differing core values in a team is indeed necessary but difficult. It can kill your project for small teams, for larger teams you will likely need to think your organization keeping the misalignment in mind.
https://rtpg.co/2024/08/31/cost-of-a-values-gap/
Tags: tech, hiring, interviews
Good set of advices. I wish more people applying for a job would follow them.
https://vurt.org/articles/twelve-rules/
Tags: colors, cognition, funny
One of those essentials questions in life now has some form of answer. Where is the blue/green boundary for you?
Bye for now!
Matt Layman: Kamal On A Droplet - Building SaaS #201
DrupalEasy: How to step down successfully as a Drupal leader
In my 15+ years in the Drupal community, I've been fortunate to have been able to lead a few Drupal-related groups and I sometimes find myself in the position of encouraging other leaders - who are experiencing burnout - on how to gracefully step down from leadership positions after multiple years of service.
When I say "groups," I'm talking about things like:
- Drupal event organizers
- Drupal module/theme/project maintainers
- Drupal initiative leaders
- Drupal working group leaders
It seems counter-intuitive to encourage folks to step away from things they have successfully led, but I'm very fond of the concept that the true sign of a healthy organization is a successful change in leadership to make way for new perspectives, insights and ultimately fresh ideas.
In this article, I'll share some of my thoughts on my experiences in doing this exact thing with two prominent Drupal groups: the Florida DrupalCamp organizing team and the Drupal Community Working Group.
Being a leader in the Drupal community comes with responsibilities, but it also comes with prestige. Leaders tend to be more visible and therefore able to promote themselves or their organizations to their advantage.
BackgroundMy leadership positions were gratifying, and I was still committed to them, but from my perspective, I had remained in them longer than was good for the organization or for me. But, I had an incredibly strong drive to ensure that I left the group in better shape than when I joined.
I was one of the original organizers of Florida DrupalCamp and ended up being the leader of the team by attrition. The other original organizers became less involved as the years went on, and I ended up taking on more-and-more duties. There wasn't a breaking point, but I realized that things weren't heading in the right direction.
For the Drupal Community Working Group, I was added to an incredibly strong team dealing with really difficult issues, but without a structured plan for length of terms or any other way to protect the mental health of its members.
In both cases, I was incredibly proud of the work we were doing, but didn't see a clear path to roll over leave either team in a healthy manner.
The good newsFrom my perspective, there are two things people need to do in order to successfully step down from leadership positions:
- Train your replacement(s).
- Codify roles and responsibilities.
Neither of these two steps can be done overnight.
The detailsTrain your replacement(s)You (yes, you) need to make a concerted effort to identify, approach, and ask someone (or in many cases, "someones") to fill your role when you leave. Once you find these magical people, then it is (again) up to you to train them in what you do. It is important that you communicate not only the work involved in being a leader, but also the advantages that come with the role.
For Florida DrupalCamp, I made it known well in advance that I was looking to step down as its leader (but willing to stay on in a lesser capacity). I knew it would be good for the event and community if there was new leadership. I told the other organizers as well as mentioning it during the event's opening and closing sessions. Most importantly, I did it early and spoke about it often. This directly led to several people stepping up.
This will likely be a time-consuming process, but it will make the team stronger. It will force you to document and organize what you do, and just the act of explaining it to someone else will allow you and your replacement to identify things that need to be documented as well as possible opportunities for efficiency gains.
Assume that you'll need to be training your replacement for at least a few months, but the timeframe really depends on the cadence of your team's primary tasks.
Codify roles and responsibilitiesThis was especially important for the Drupal Community Working Group, as prior to my joining the group, there weren't any guidelines for length of term, how the leader was selected, and how to step away gracefully. Under the leadership of George DeMet, our team implemented all of these, and more. Both George and I led the team for more years than was probably healthy for either of us, but by the time I stepped away, there were clear guidelines for all of these things (with a significant focus on the mentally draining Conflict Resolution Team).
For less formal teams, this could be as simple as a wiki page or an issue in the project's queue with what you and the other leaders do, what your boundaries are, and what your plans for the future are. This can be especially effective when someone makes a request of you that you feel is above-and-beyond - it is nice to have a document that you could point to where roles and responsibilities are detailed.
I'll admit that I skipped this step when stepping down as leader of the Florida DrupalCamp organizing team, as I wasn't leaving the team completely - I just stepped down into a lesser role but was always available to the new leaders for questions and advice.
Getting startedThere are many Drupal groups that have informal leadership roles, with many leaders who definitely feel that if they leave, then the group will fall. Clearly, this is not a healthy situation.
In this case, my advice is this: start by writing up a document/drupal.org page that describes what you do as leader and share it with the rest of the group. Then, be proactive and find a potential replacement and start the training process using the document as a guide.
No replacementIt should be obvious that the "finding your replacement" step requires a human being other than yourself being involved. But what happens if you can't find someone…
This situation can be stressful and heartbreaking at the same time, but I have a strong opinion on this - if you find yourself in this situation, then maybe it is time for the team to be disbanded or go dormant. If there's not enough interest in the community to keep the group alive, it's not your responsibility to sacrifice your time/money/mental-health. My advice is to write up your thoughts, announce your intentions (and time frame) and post it to all members of the group. This can be done in a way that sets up a future leader to use the codified roles and responsibilities as a framework to get things moving again. In a way, you're still training your replacement - just not in realtime.
Will there be people who are disappointed and/or angry with you for "abandoning" the group? Perhaps, but you'll need to do your best to ignore those folks and focus on setting up the next leader for success.
I would suggest that you keep things simple and focus on the main goal of always leaving the group in a positive manner, setting up future leaders for success.
Thanks to AmyJune Hineline, Adam Varn, Mike Herchel, George DeMet, and Gwendolyn Anello (who reviews pretty much everything I write) for reviewing this post prior to publication.
Kanopi Studios: A Handy Visual Guide to Drupal Versions, from 7 to Modern Drupal
If yours is one of the 42% of Drupal sites that are still using Drupal 7, we’re writing this post specifically with you in mind. After all, you’ve probably heard the news by now; as of January 5th, 2025, everyone’s beloved, trusted Drupal version 7 will reach its end-of-life. If you haven’t done so already, […]
The post A Handy Visual Guide to Drupal Versions, from 7 to Modern Drupal appeared first on Kanopi Studios.
mark.ie: My LocalGov Drupal contributions for week-ending September 6th, 2024
One of those weeks where we got lots and lots of smaller issues cleared up, and a new module released, and a very quirky bug discovered.
Horizontal Digital Blog: Why we migrated our blog from Wordpress to Drupal
Horizontal Digital Blog: Drupal's bundle classes offer granular control over node URLs
Tag1 Consulting: Migrating Your Data from D7 to D10: Migrating field widget settings
Today, we continue with the next field-related migration: widgets. While doing so, we will find out that new migrations might uncover issues or misconfigurations with already executed migrations.
Read more mauricio Thu, 09/05/2024 - 04:30Python Software Foundation: Pallets projects added to scope of PSF CVE Numbering Authority
Today, the PSF is expanding our CNA scope to also include Pallets projects, such as Flask, Jinja, Click, and Quart. For a complete list, see the Pallets organization on GitHub. Please report any security vulnerabilities for these projects following the Pallets security policy.
This work is being done to learn how the PSF can better serve Python's large ecosystem of projects in the context of the CVE ecosystem. The PSF previously published a guide on how open source projects can become their own CVE Numbering Authorities. You can learn more about the CVE CNA program on the CVE website.
Pallets is a fiscal sponsoree of the Python Software Foundation. Fiscal sponsorship is a key plank of the PSF’s mission in supporting the Python community. The PSF supports 20 fiscal sponsorees including regional PyCons, Python Meetup and User Groups, and Python projects. Learn more about our Fiscal Sponsorees on our website and consider supporting the groups with a US-tax deductible donation.
Sandro Tosi: TL;DR belongs at the top of an article
TL;DR
- if you are writing an article and plan to add a TL;DR section, then put it at the very top, right after the title.
- that's it, no excuses, end of discussion.
If the reason for "Too Long; Didn't Read" to exist is to avoid the reader to go thru the whole article to get its main points, then the natural place to present it is at the very top of said article.
So if you're planning on writing something and to add a TL;DR section (you don't have to, of course, but if you do that work too) then please position it at the very beginning of your work.
Python GUIs: Build a Translation Application Using Tkinter and OpenAI — Use ChatGPT to Translate Your Text from Python
Translation tools have existed for many years and are incredibly useful if you're learning a new language or wanting to read foreign websites. One of the most popular tools is Google Translate , but there is now another alternative: using OpenAI's ChatGPT tool to translate text.
In this tutorial, we'll build a desktop translator application to translate natural language using ChatGPT APIs. We'll be building the UI using the Tkinter library from the Python standard library:
Example translation of text via OpenAI
Table of Contents- Installing the Required Packages
- Building the Window
- Creating the GUI for the Translator App
- Getting a List of Languages
- Building the Input UI
- Getting an OpenAI API Key
- Implementing the Translation Functionality
- Conclusion
Our Translator uses the openai library to perform the actual translation via OpenAI's ChatGPT tool. Tkinter is already available in the standard library.
The first task will be to set up a Python virtual environment. Open the terminal, and run the following commands:
- Windows
- macOS/Linux
Working through these instructions, first we create a root directory for the Translator app. Next we create and activate a Python virtual environment for the project. Finally, we install the openai package.
Next, create a file named translator.py in the root of your project. Also add a folder called images/ where you'll store the icons for the application. The folder structure should look like this:
python translator/ &boxv &boxvr&boxh&boxh images/ &boxv &boxvr&boxh&boxh arrow.png &boxv &boxur&boxh&boxh logo.png &boxv &boxur&boxh&boxh translator.pyThe images for this project can be downloaded here.
The images/ folder contains the two icons that you'll use for the application. The translator.py is the app's source file.
Building the WindowOpen the translator.py file with your favorite Python code editor. We'll start by creating our main window:
python import tkinter as tk class TranslatorApp(tk.Tk): def __init__(self): super().__init__() self.title("Language Translator") self.resizable(width=False, height=False) if __name__ == "__main__": app = TranslatorApp() app.mainloop()This code imports Tkinter and then defines the application's main class, which we have called TranslatorApp. This class will hold the application's main window and allow us to run the main loop.
Importing tkinter under the alias tk is a common convention in Tkinter code.
Inside the class we define the __init__() method, which handles initialization of the class. In this method, we first call the initializer __init__() of the parent class, tk.Tk, to initialize the app's window. Then, we set the window's title using the title() method. To make the window unresizable, we use the resizable() method with width and height set to False.
At the bottom of the code, we have the if __name__ == "__main__" idiom to check whether the file is being run directly as an executable program. Inside the condition block we first create an instance of TranslatorApp and then run the application's main loop or event loop.
If you run this code, you'll get an empty Tkinter window on your desktop:
python $ python translator.pyThe empty Tkinter window
Creating the GUI for the Translator AppNow that the main window is set up, let's start adding widgets to build the GUI. To do this, we'll create a method called setup_ui(), as shown below:
python import tkinter as tk class TranslatorApp(tk.Tk): def __init__(self): super().__init__() self.title("Language Translator") self.resizable(width=False, height=False) self.setup_ui() def setup_ui(self): frame = tk.Frame(self) frame.pack(padx=10, pady=10) if __name__ == "__main__": app = TranslatorApp() app.mainloop()The setup_ui() method will define the application's GUI. In this method, we first create a frame widget using the tk.Frame class whose master argument is set to self (the application's main window). Next, we position the frame inside the main window using the pack() geometry manager, using padx and pady arguments to set some padding around the frame.
Finally, we add the call to self.setup_ui() to the __init__() method.
We'll continue to develop the UI by adding code to the setup_ui() method.
Net, we'll add the app's logo. In the setup_ui() method add the following code below the frame definition:
python import tkinter as tk class TranslatorApp(tk.Tk): def __init__(self): super().__init__() self.title("Language Translator") self.resizable(width=False, height=False) self.setup_ui() def setup_ui(self): frame = tk.Frame(self) frame.pack(padx=10, pady=10) self.logo = tk.PhotoImage(file="images/logo.png") tk.Label(frame, image=self.logo).grid(row=0, column=0, sticky="w") if __name__ == "__main__": app = TranslatorApp() app.mainloop()This code loads the logo using the tk.PhotoImage class. To resize it, we use the subsample() method. Then, we add the logo to the frame using a tk.Label widget. The label takes the frame and the logo as arguments. Finally, to position the logo, we use the grid() geometry manager with appropriate values for the row, column, and sticky arguments.
The sticky argument determines which side of a cell the widget should align -- North (top), South (bottom), East (right) or West (left). Here we're aligning it on the Wiest or left of the cell with "w":
Tkinter window with the OpenAI logo in it
Getting a List of LanguagesWe need list of languages to shown in the dropdown. There are various lists available online. But since we're using OpenAI for the translations, why not use it to give us the list of languages too. Since this is just for testing purposes, lets grab the top 20 human languages (by first and second language speakers).
We can prompt ChatGPT with something like:
Give me a list of the top 20 human languages with the most first and second language speakers in Python list format
..and it will return the following list:
python languages = [ "English", "Mandarin Chinese", "Hindi", "Spanish", "French", "Standard Arabic", "Bengali", "Russian", "Portuguese", "Urdu" ]I'm going to add Dutch to the list, because it's my second language. Feel free to add your own languages to the list.
Adding the InterfaceLet's start adding some inputs to the UIs. First we'll create the language selection drop down boxes:
python import tkinter as tk import tkinter.ttk as ttk LANGUAGES = [ "English", "Mandarin Chinese", "Hindi", "Spanish", "French", "Standard Arabic", "Bengali", "Russian", "Portuguese", "Urdu", "Dutch", # Gekoloniseerd. ] DEFAULT_SOURCE = "English" DEFAULT_DEST = "Dutch" class TranslatorApp(tk.Tk): def __init__(self): super().__init__() self.title("Language Translator") self.resizable(width=False, height=False) self.setup_ui() def setup_ui(self): frame = tk.Frame(self) frame.pack(padx=10, pady=10) self.logo = tk.PhotoImage(file="images/logo.png") tk.Label(frame, image=self.logo).grid(row=0, column=0, sticky="w") # Source language combobox self.from_language = ttk.Combobox(frame, values=LANGUAGES) self.from_language.current(LANGUAGES.index(DEFAULT_SOURCE)) self.from_language.grid(row=1, column=0, sticky="we") # Arrow icon self.arrows = tk.PhotoImage(file="images/arrow.png").subsample(15, 15) tk.Label(frame, image=self.arrows).grid(row=1, column=1) # Destination language combobox self.to_language = ttk.Combobox(frame, values=LANGUAGES) self.to_language.current(LANGUAGES.index(DEFAULT_DEST)) self.to_language.grid(row=1, column=2, sticky="we") if __name__ == "__main__": app = TranslatorApp() app.mainloop()We have added our language list as the constant LANGUAGES. We also define the default languages for when the application starts up, using constants DEFAULT_SOURCE and DEFAULT_DEST.
Next, we create two combo boxes to hold the list of source and destination languages. The combo boxes are created using the ttk.Combobox class. One to the left and another to the right. Between the combo boxes, we've also added an arrow icon loaded using the tk.PhotoImage class. Again, we've added the icon to the app's window using ttk.Label.
Both combo boxes take frame and values as arguments. The values argument populates the combo boxes with languages. To specify the default language, we use the current() method, looking up the position of our default languages in the languages list with .index().
To position the combo boxes inside the frame, we use the grid() geometry manager with the appropriate arguments. Run the application, and you will see the following window:
Source and destination languages
With the source and destination combo boxes in place, let's add three more widgets: two scrollable text widgets and a button. The scrollable text on the left will hold the source text, while the scrollable text on the right will hold the translated text. The button will allow us to run the actual translation.
Building the Input UIGet back to the code editor and update the setup_ui() method as follows. Note that we also need to import the ScrollText class:
python import tkinter as tk import tkinter.ttk as ttk from tkinter.scrolledtext import ScrolledText LANGUAGES = [ "English", "Mandarin Chinese", "Hindi", "Spanish", "French", "Standard Arabic", "Bengali", "Russian", "Portuguese", "Urdu", "Dutch", ] DEFAULT_SOURCE = "English" DEFAULT_DEST = "Dutch" class TranslatorApp(tk.Tk): def __init__(self): super().__init__() self.title("Language Translator") self.resizable(width=False, height=False) self.setup_ui() def setup_ui(self): frame = tk.Frame(self) frame.pack(padx=10, pady=10) self.logo = tk.PhotoImage(file="images/logo.png").subsample(5, 5) tk.Label(frame, image=self.logo).grid(row=0, column=0, sticky="w") # Source language combobox languages = [lang.title() for lang in LANGUAGES.values()] self.from_language = ttk.Combobox(frame, values=languages) self.from_language.current(languages.index(DEFAULT_SOURCE)) self.from_language.grid(row=1, column=0, sticky="we") # Arrow icon self.arrows = tk.PhotoImage(file="images/arrow.png").subsample(15, 15) tk.Label(frame, image=self.arrows).grid(row=1, column=1) # Destination language combobox self.to_language = ttk.Combobox(frame, values=languages) self.to_language.current(languages.index(DEFAULT_DEST)) self.to_language.grid(row=1, column=2, sticky="we") # Source text self.from_text = ScrolledText( frame, font=("Dotum", 16), width=50, height=20, ) self.from_text.grid(row=2, column=0) # Translated text self.to_text = ScrolledText( frame, font=("Dotum", 16), width=50, height=20, state="disabled", ) self.to_text.grid(row=2, column=2) # Translate button self.translate_button = ttk.Button( frame, text="Translate", command=self.translate, ) self.translate_button.grid(row=3, column=0, columnspan=3, pady=10) def translate(self): pass if __name__ == "__main__": app = TranslatorApp() app.mainloop()In the code snippet, we use the ScrolledText class to create the two scrolled text areas. Both text areas take frame, font, width, and height as arguments. The second text area also takes state as an additional argument. Setting state to "disabled" allows us to create a read-only text area.
Then, we use the ttk.Button class to create a button with frame, text, and command as arguments. The command argument allows us to bind the button's click event to the self.translate() method, which we will define in a moment. For now, we've added a placeholder.
To position all these widgets on the app's window, we use the grid() geometry manager. Now, the app will look something like the following:
Translator app's GUI
Our translation app's GUI is ready! Finally, we can start adding functionality to the application.
Getting an OpenAI API KeyYou can use OpenAPI's APIs for free, with some limitations. To get an OpenAI API key you will need to create an account. Once you have created an account go ahead and get an API key.
Click "Create new secret key" in the top right hand corner to create a key. Give the key a name (it doesn't matter what you use) and then click "Create secret key". Copy the resulting key and keep it safe. You'll need it in the next step.
Implementing the Translation FunctionalityWe'll implement the language translation functionality in the translate() method. This gets the current text from the UI and then uses openai to perform the translation. We need a few more imports, and to create the OpenAI client instance at the top of the application:
python import tkinter as tk import tkinter.ttk as ttk from tkinter.messagebox import showerror from tkinter.scrolledtext import ScrolledText import httpcore from openai import OpenAI client = OpenAI( api_key="<YOUR API KEY HERE>" )Here we've imported the showerror helper for displaying error boxes in our application. We've imported httpcore which we'll use to handle HTTP errors when accessing the API. Finally, we've added an import for the OpenAI class from openai. This is what handles the actual translation.
To use it, we create an instance of the class as OpenAI. Replace <YOUR API KEY HERE> with the API key you generated on OpenAI just now.
We'll continue by implementing the translate() method. Below we're just showing the function itself:
python class TranslatorApp(tk.Tk): # ... def translate(self): source_language = self.from_language.get() destination_language = self.to_language.get() text = self.from_text.get(1.0, tk.END).strip() try: completion = client.chat.completions.create( messages=[ {"role": "system", "content": "You are a language interpreter."}, { "role": "user", "content": ( f"Translate the following text from {source_language} " f"to {destination_language}, only reply with the text: " f"{text}" ), }, ], model="gpt-3.5-turbo", ) reply = completion.choices[0].message.content except httpcore.ConnectError: showerror( title="Error", message="Make sure you have an internet connection", ) return except Exception as e: showerror( title="Error", message=f"An unexpected error occurred: {e}", ) return self.to_text.config(state="normal") self.to_text.delete(1.0, tk.END) self.to_text.insert(tk.END, reply) self.to_text.config(state="disabled")The translate() method handles the entire translation process. It starts by retrieving the source and destination languages from the corresponding combo boxes, and the input text from the box on the left.
If any of these are not defined, we use a showerror dialog to inform the user of the problem.
Once we have the source and destination language and some text to translate, we can perform the actual translation through ChatGPT. First, we give the language model a hint about what we want it to do -- interpret language:
python {"role": "system", "content": "You are a language interpreter."},Next we build the message we want it to respond to. We ask it to translate the provided text from the source to destination language, and to respond with only the translated text. If we don't specify this, we'll get some additional description or context.
You might want to experiment with asking for the text and context separately, as that is often helpful when learning languages.
python { "role": "user", "content": ( f"Translate the following text from {source_language} " f"to {destination_language}, only reply with the text: " f"{text}" ), },The created completion is submitted to the API and we can retrieve the resulting text from the object:
python reply = completion.choices[0].message.contentIf the call to translate() finds a connection error, then we tell the user to check their internet connection. To handle any other exceptions, we catch the generic Exception class and display an error message with the exception details.
If the translation is successful, then we enable the destination scrolled area, display the translated text, and disable the area again so it remains read-only.
The complete final code is shown below:
python import tkinter as tk import tkinter.ttk as ttk from tkinter.messagebox import showerror from tkinter.scrolledtext import ScrolledText import httpcore from openai import OpenAI client = OpenAI( api_key="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" ) LANGUAGES = [ "English", "Mandarin Chinese", "Hindi", "Spanish", "French", "Standard Arabic", "Bengali", "Russian", "Portuguese", "Urdu", "Dutch", ] DEFAULT_SOURCE = "English" DEFAULT_DEST = "Dutch" class TranslatorApp(tk.Tk): def __init__(self): super().__init__() self.title("Language Translator") self.resizable(width=False, height=False) self.setup_ui() def setup_ui(self): frame = tk.Frame(self) frame.pack(padx=10, pady=10) self.logo = tk.PhotoImage(file="images/logo.png") tk.Label(frame, image=self.logo).grid(row=0, column=0, sticky="w") # Source language combobox self.from_language = ttk.Combobox(frame, values=LANGUAGES) self.from_language.current(LANGUAGES.index(DEFAULT_SOURCE)) self.from_language.grid(row=1, column=0, sticky="we") # Arrow icon self.arrows = tk.PhotoImage(file="images/arrow.png").subsample(15, 15) tk.Label(frame, image=self.arrows).grid(row=1, column=1) # Destination language combobox self.to_language = ttk.Combobox(frame, values=LANGUAGES) self.to_language.current(LANGUAGES.index(DEFAULT_DEST)) self.to_language.grid(row=1, column=2, sticky="we") # Source text self.from_text = ScrolledText( frame, font=("Dotum", 16), width=50, height=20, ) self.from_text.grid(row=2, column=0) # Translated text self.to_text = ScrolledText( frame, font=("Dotum", 16), width=50, height=20, state="disabled", ) self.to_text.grid(row=2, column=2) # Translate button self.translate_button = ttk.Button( frame, text="Translate", command=self.translate, ) self.translate_button.grid(row=3, column=0, columnspan=3, pady=10) def translate(self): source_language = self.from_language.get() destination_language = self.to_language.get() text = self.from_text.get(1.0, tk.END).strip() try: completion = client.chat.completions.create( messages=[ {"role": "system", "content": "You are a language interpreter."}, { "role": "user", "content": ( f"Translate the following text from {source_language} " f"to {destination_language}, only reply with the text: " f"{text}" ), }, ], model="gpt-3.5-turbo", ) reply = completion.choices[0].message.content except httpcore.ConnectError: showerror( title="Error", message="Make sure you have an internet connection", ) return except Exception as e: showerror( title="Error", message=f"An unexpected error occurred: {e}", ) return self.to_text.config(state="normal") self.to_text.delete(1.0, tk.END) self.to_text.insert(tk.END, reply) self.to_text.config(state="disabled") if __name__ == "__main__": app = TranslatorApp() app.mainloop()The finished app is shown below:
The completed Translator app
ConclusionIn this tutorial we built a Translator application using the Tkinter GUI library from the Python standard library. We worked step by step through building the UI using a grid layout, and then implemented the language translation functionality with openai & ChatGPT.
Try and take what you've learnt in this tutorial & applying it to your own projects!