<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged library</title>
<link>https://coderlegion.com/tag/library</link>
<description>Powered by Question2Answer</description>
<item>
<title>Migrating a Real TypeScript OSS Library from tsup to tsdown 🔧</title>
<link>https://coderlegion.com/30504/migrating-a-real-typescript-oss-library-from-tsup-to-tsdown</link>
<description>&lt;blockquote&gt;&lt;p&gt;The config migration was small. Preserving the package contract was the interesting part.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Hoi hoi! 👋&lt;/p&gt;
&lt;p&gt;I'm &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru&quot;&gt;@nyaomaru&lt;/a&gt;, a frontend engineer who enjoyed looking for fungus this season. 😸🍄&lt;/p&gt;
&lt;p&gt;Recently, I migrated its build setup from &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/egoist/tsup&quot;&gt;&lt;code&gt;tsup&lt;/code&gt;&lt;/a&gt; to &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/rolldown/tsdown&quot;&gt;&lt;code&gt;tsdown&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At first, I expected this to be a very small task.&lt;/p&gt;
&lt;p&gt;Just remove &lt;code&gt;tsup&lt;/code&gt;.&lt;br&gt;
Just install &lt;code&gt;tsdown&lt;/code&gt;.&lt;br&gt;
Just change the config.&lt;br&gt;
Run the build.&lt;br&gt;
Done! 🎉&lt;/p&gt;
&lt;p&gt;And honestly...&lt;/p&gt;
&lt;p&gt;The config migration &lt;strong&gt;was&lt;/strong&gt; small.&lt;/p&gt;
&lt;p&gt;But there was one interesting problem.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The first &lt;code&gt;tsdown&lt;/code&gt; build succeeded while silently changing files that were already part of the package's public contract.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So this article isn't really an introduction to &lt;code&gt;tsdown&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Instead, I want to show what happened when I migrated a real published TypeScript library from &lt;code&gt;tsup&lt;/code&gt; to &lt;code&gt;tsdown&lt;/code&gt;, what actually changed, and how I verified that consumers would still receive the same package.&lt;/p&gt;
&lt;p&gt;Let's take a look! 👀&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-why-migrate-a-package-that-already-builds-correctly-&quot;&gt;🤔 Why Migrate a Package That Already Builds Correctly?&lt;/h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/is-kit&quot;&gt;&lt;code&gt;is-kit&lt;/code&gt;&lt;/a&gt; is a zero-dependency TypeScript library for runtime type guards.&lt;/p&gt;
&lt;p&gt;Its build setup was already pretty boring.&lt;/p&gt;
&lt;p&gt;And boring build systems are good. 😸&lt;/p&gt;
&lt;p&gt;The package had:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;one entry point&lt;/li&gt;
&lt;li&gt;ESM and CJS outputs&lt;/li&gt;
&lt;li&gt;bundled declaration files&lt;/li&gt;
&lt;li&gt;explicit &lt;code&gt;package.json&lt;/code&gt; exports&lt;/li&gt;
&lt;li&gt;a declaration banner&lt;/li&gt;
&lt;li&gt;a packed-package smoke test&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before the migration, it used&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Environment &lt;/th&gt;&lt;th&gt;Value&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Package &lt;/td&gt;&lt;td&gt;&lt;code&gt;is-kit@1.14.2&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Bundler &lt;/td&gt;&lt;td&gt;&lt;code&gt;tsup@8.5.1&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Entry   &lt;/td&gt;&lt;td&gt;&lt;code&gt;src/index.ts&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Output  &lt;/td&gt;&lt;td&gt;&lt;code&gt;ESM + CJS + bundled declarations&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Target  &lt;/td&gt;&lt;td&gt;&lt;code&gt;esnext&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I wasn't trying to solve a broken build.&lt;/p&gt;
&lt;p&gt;The motivation was mostly maintenance.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;tsdown&lt;/code&gt; is built around Rolldown, has an active ecosystem, and is explicitly designed as a migration path for projects currently using &lt;code&gt;tsup&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So the question wasn't&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Can &lt;code&gt;tsdown&lt;/code&gt; build this library?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It was&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Can I move the build setup to &lt;code&gt;tsdown&lt;/code&gt; without changing what existing consumers receive?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That's a much more useful question for a published package.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-the-package-contract-i-needed-to-preserve&quot;&gt;📦 The Package Contract I Needed to Preserve&lt;/h3&gt;
&lt;p&gt;For an application, changing an output filename may not matter much.&lt;/p&gt;
&lt;p&gt;For a library, it can be a breaking change.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;is-kit&lt;/code&gt; already exposes files through explicit package exports.&lt;/p&gt;
&lt;p&gt;The important paths were effectively&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;exports&quot;: {
    &quot;.&quot;: {
      &quot;types&quot;: &quot;./dist/index.d.ts&quot;,
      &quot;import&quot;: &quot;./dist/index.mjs&quot;,
      &quot;require&quot;: &quot;./dist/index.js&quot;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So I wanted the migration to preserve&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;dist/index.mjs
dist/index.js
dist/index.d.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;along with the existing ESM/CJS runtime behavior, declaration compatibility, export set, and declaration banner.&lt;/p&gt;
&lt;p&gt;In other words&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The source code was not the contract here. The packed npm package was.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That distinction became important very quickly.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-the-migration-looked-almost-trivial&quot;&gt;🛠️ The Migration Looked Almost Trivial&lt;/h3&gt;
&lt;p&gt;I replaced &lt;code&gt;tsup&lt;/code&gt; with &lt;code&gt;tsdown&lt;/code&gt; and replaced&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;tsup.config.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;with&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;tsdown.config.mts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I intentionally used &lt;code&gt;.mts&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The package itself is not &lt;code&gt;&quot;type&quot;: &quot;module&quot;&lt;/code&gt;, and using an ESM-specific config extension avoids having Node reinterpret the config file and emit related warnings.&lt;/p&gt;
&lt;p&gt;Most of the important options mapped almost directly.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { defineConfig } from &quot;tsdown&quot;;

export default defineConfig({
  entry: [&quot;src/index.ts&quot;],
  format: [&quot;esm&quot;, &quot;cjs&quot;],
  dts: true,
  clean: true,
  outDir: &quot;dist&quot;,
  target: &quot;esnext&quot;,
  banner: {
    dts: dtsBanner,
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The declaration banner also stayed dts-only with &lt;code&gt;banner: { dts: dtsBanner }&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So far, everything looked easy.&lt;/p&gt;
&lt;p&gt;Then I ran the build.&lt;/p&gt;
&lt;p&gt;It passed.&lt;/p&gt;
&lt;p&gt;And the package contract was wrong. 🙀&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-the-first-build-succeeded-and-changed-my-filenames&quot;&gt;💥 The First Build Succeeded and Changed My Filenames&lt;/h3&gt;
&lt;p&gt;This was the interesting part.&lt;/p&gt;
&lt;p&gt;Before the migration, the important generated files looked like this:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Build                      &lt;/th&gt;&lt;th&gt;Main generated files&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;tsup&lt;/code&gt;                     &lt;/td&gt;&lt;td&gt;&lt;code&gt;index.mjs&lt;/code&gt;, &lt;code&gt;index.js&lt;/code&gt;, &lt;code&gt;index.d.ts&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;default &lt;code&gt;tsdown&lt;/code&gt; migration &lt;/td&gt;&lt;td&gt;&lt;code&gt;index.mjs&lt;/code&gt;, &lt;code&gt;index.cjs&lt;/code&gt;, &lt;code&gt;index.d.mts&lt;/code&gt;, &lt;code&gt;index.d.cts&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code&gt;tsdown&lt;/code&gt; build completed successfully with exit code &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But my existing &lt;code&gt;package.json&lt;/code&gt; still expected&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;require → ./dist/index.js
types   → ./dist/index.d.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Those files no longer matched the generated output.&lt;/p&gt;
&lt;p&gt;If I had stopped at&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;pnpm build
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and published the package.&lt;/p&gt;
&lt;p&gt;CJS consumers and TypeScript resolution could have been broken.&lt;/p&gt;
&lt;p&gt;This was the most important lesson from the migration&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Build success ≠ package contract preserved.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A bundler only knows whether it successfully produced its output.&lt;/p&gt;
&lt;p&gt;It doesn't automatically mean that output still matches every promise your already-published package makes.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-fixing-the-public-contract-with-outextensions&quot;&gt;🔧 Fixing the Public Contract with &lt;code&gt;outExtensions&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a rel=&quot;nofollow&quot; href=&quot;https://tsdown.dev/guide/migrate-from-tsup&quot;&gt;&lt;code&gt;tsdown&lt;/code&gt; migration guide&lt;/a&gt; explicitly calls out the rename from &lt;code&gt;outExtension&lt;/code&gt; to &lt;code&gt;outExtensions&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;is-kit&lt;/code&gt;, I used it to preserve the existing filenames&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;outExtensions: ({ format }) =&amp;gt; ({
  dts: format === 'cjs' ? '.d.ts' : '.d.mts',
  js: format === 'cjs' ? '.js' : '.mjs',
}),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the relevant output became&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;dist/index.mjs
dist/index.js
dist/index.d.mts
dist/index.d.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and the existing exports continued to resolve correctly.&lt;/p&gt;
&lt;p&gt;I don't really consider the original output behavior a &lt;code&gt;tsdown&lt;/code&gt; bug.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;tsdown&lt;/code&gt; chooses extensions based on the package type and output format to avoid ambiguous module interpretation.&lt;/p&gt;
&lt;p&gt;That's reasonable.&lt;/p&gt;
&lt;p&gt;But for an existing package, &lt;strong&gt;reasonable new defaults are still changes&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If consumers already depend on your filenames, those filenames are part of your compatibility surface.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-verifying-the-published-package-with-a-smoke-test&quot;&gt;🧪 Verifying the Published Package with a Smoke Test&lt;/h3&gt;
&lt;p&gt;This is where an existing &lt;strong&gt;packed-package smoke test&lt;/strong&gt; in &lt;code&gt;is-kit&lt;/code&gt; helped a lot.&lt;/p&gt;
&lt;p&gt;I already had a &lt;code&gt;pnpm test:package&lt;/code&gt; command that builds the package, runs &lt;code&gt;npm pack&lt;/code&gt;, installs the generated tarball into a temporary project, and verifies it from a real consumer's perspective.&lt;/p&gt;
&lt;p&gt;Instead of testing this&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;src/index.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;it tests this&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;is-kit-1.14.2.tgz
        ↓
temporary consumer
        ↓
npm install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A library can work perfectly inside its own repository while still publishing a broken package, so this smoke test exercises the artifact that users would actually install.&lt;/p&gt;
&lt;p&gt;After the migration, I expanded the test to verify more of the package contract 👇&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Contract                                   &lt;/th&gt;&lt;th&gt;Result&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;exports[&quot;.&quot;].import&lt;/code&gt; → &lt;code&gt;./dist/index.mjs&lt;/code&gt; &lt;/td&gt;&lt;td&gt;✅ pass&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;exports[&quot;.&quot;].require&lt;/code&gt; → &lt;code&gt;./dist/index.js&lt;/code&gt; &lt;/td&gt;&lt;td&gt;✅ pass&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;exports[&quot;.&quot;].types&lt;/code&gt; → &lt;code&gt;./dist/index.d.ts&lt;/code&gt; &lt;/td&gt;&lt;td&gt;✅ pass&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Runtime dependencies                       &lt;/td&gt;&lt;td&gt;✅ 0&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;ESM exports                                &lt;/td&gt;&lt;td&gt;✅ same 83 exports&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CJS exports                                &lt;/td&gt;&lt;td&gt;✅ same 83 exports&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Declaration banner                         &lt;/td&gt;&lt;td&gt;✅ preserved&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;ESM runtime import                         &lt;/td&gt;&lt;td&gt;✅ pass&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CJS runtime require                        &lt;/td&gt;&lt;td&gt;✅ pass&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Packed TypeScript consumer                 &lt;/td&gt;&lt;td&gt;✅ pass&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I also installed the packed package into temporary consumer projects using TypeScript v5.7 through v7.0.&lt;/p&gt;
&lt;p&gt;All of them successfully resolved and consumed the generated declarations.&lt;/p&gt;
&lt;p&gt;This isn't testing whether each TypeScript version can generate declarations through &lt;code&gt;tsdown&lt;/code&gt;. It's testing the artifact my users actually install.&lt;/p&gt;
&lt;p&gt;So if a future build change accidentally alters an extension, export path, declaration file, or runtime behavior, &lt;strong&gt;the smoke test should catch it before publishing&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For a library migration like this, that is much more meaningful than simply asserting &quot;build exited successfully&quot;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-the-output-actually-got-bigger&quot;&gt;📏 The Output Actually Got Bigger&lt;/h3&gt;
&lt;p&gt;I was also curious about artifact size.&lt;/p&gt;
&lt;p&gt;This produced a result I didn't expect.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Metric             &lt;/th&gt;&lt;th&gt;   &lt;code&gt;tsup&lt;/code&gt; &lt;/th&gt;&lt;th&gt; &lt;code&gt;tsdown&lt;/code&gt; &lt;/th&gt;&lt;th&gt;Difference&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;JS + dts total     &lt;/td&gt;&lt;td&gt;116,503 B &lt;/td&gt;&lt;td&gt;144,007 B &lt;/td&gt;&lt;td&gt;&lt;strong&gt;+23.6%&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;ESM JavaScript     &lt;/td&gt;&lt;td&gt; 15,787 B &lt;/td&gt;&lt;td&gt; 29,410 B &lt;/td&gt;&lt;td&gt;&lt;strong&gt;+86.3%&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CJS JavaScript     &lt;/td&gt;&lt;td&gt; 19,318 B &lt;/td&gt;&lt;td&gt; 31,155 B &lt;/td&gt;&lt;td&gt;&lt;strong&gt;+61.3%&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;dts, one format    &lt;/td&gt;&lt;td&gt; 40,699 B &lt;/td&gt;&lt;td&gt; 41,721 B &lt;/td&gt;&lt;td&gt; &lt;strong&gt;+2.5%&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;npm pack&lt;/code&gt; tarball &lt;/td&gt;&lt;td&gt; 37,226 B &lt;/td&gt;&lt;td&gt; 42,366 B &lt;/td&gt;&lt;td&gt;&lt;strong&gt;+13.8%&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In this configuration, the &lt;code&gt;tsdown&lt;/code&gt; output retained more comments and region markers than the previous &lt;code&gt;tsup&lt;/code&gt; output.&lt;/p&gt;
&lt;p&gt;So the raw JavaScript became noticeably larger.&lt;/p&gt;
&lt;p&gt;Compression reduced the difference in the actual &lt;code&gt;npm tarball&lt;/code&gt;, but didn't remove it.&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;is-kit&lt;/code&gt;, this isn't particularly concerning.&lt;/p&gt;
&lt;p&gt;It is a small zero-dependency utility library, and we're talking about a few kilobytes in the final tarball.&lt;/p&gt;
&lt;p&gt;But it was still a useful reminder&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;A faster or newer bundler does not automatically mean a smaller artifact.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If package size is a strict constraint for your library, I would compare the generated files and decide on your minification strategy before migrating.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-about-build-performance-&quot;&gt;⏱️ What About Build Performance?&lt;/h3&gt;
&lt;p&gt;I also measured wall-clock build time.&lt;/p&gt;
&lt;p&gt;The old &lt;code&gt;tsup&lt;/code&gt; build&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1.50 s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new &lt;code&gt;tsdown&lt;/code&gt; build across three runs&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1.22 s
1.17 s
1.18 s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looking only at those numbers, it would be very tempting to say&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;tsdown&lt;/code&gt; made the build faster! 🚀&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But I don't think this benchmark supports that conclusion.&lt;/p&gt;
&lt;p&gt;I only have one measured &lt;code&gt;tsup&lt;/code&gt; run in this comparison.&lt;/p&gt;
&lt;p&gt;Also, this is a tiny single-entry library where declaration generation represents a large part of the build.&lt;/p&gt;
&lt;p&gt;The bundlers' own timing output was roughly&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;tsup
JavaScript: ~22 ms
dts:        ~707 ms

tsdown
complete:   ~717–755 ms
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The wall-clock result was better, but with only one &lt;code&gt;tsup&lt;/code&gt; sample and declaration generation dominating this tiny library, I don't think this is enough evidence to claim a meaningful performance improvement.&lt;/p&gt;
&lt;p&gt;But no, I'm not migrating because I saved around 300 ms.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-was-the-migration-actually-small-&quot;&gt;🐱 Was the Migration Actually Small?&lt;/h3&gt;
&lt;p&gt;For &lt;code&gt;is-kit&lt;/code&gt;, yes.&lt;/p&gt;
&lt;p&gt;There were no source-code changes.&lt;/p&gt;
&lt;p&gt;The migration was basically limited to&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;dependency
config
task documentation
package smoke assertions
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important options were almost one-to-one.&lt;/p&gt;
&lt;p&gt;But I think it's important to explain &lt;strong&gt;why&lt;/strong&gt; it stayed small.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;is-kit&lt;/code&gt; has&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;one entry&lt;/li&gt;
&lt;li&gt;zero runtime dependencies&lt;/li&gt;
&lt;li&gt;no bundler plugins&lt;/li&gt;
&lt;li&gt;no CSS pipeline&lt;/li&gt;
&lt;li&gt;no complicated code splitting&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;and, most importantly, it already had a way to test the packed package as a consumer.&lt;/p&gt;
&lt;p&gt;The migration becomes more interesting if your package relies on custom plugins, unusual entries, CSS processing, exact source maps, generated exports, strict byte-size limits, or older Node build environments.&lt;/p&gt;
&lt;p&gt;There is also a build-environment requirement worth checking.&lt;/p&gt;
&lt;p&gt;For the version tested here&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;tsdown 0.23.0
Rolldown 1.2.8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the relevant Node engine requirement is&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;^22.18.0 || ^24.11.0 || &amp;gt;=26.0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My CI uses&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Node 22.22.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;so that was fine.&lt;/p&gt;
&lt;p&gt;But if contributors still build your library with Node v20 or an older Node v22 version, this is something you need to solve before migrating.&lt;/p&gt;
&lt;p&gt;That doesn't necessarily mean your &lt;strong&gt;library consumers&lt;/strong&gt; must use Node v22.&lt;/p&gt;
&lt;p&gt;It's a build-tool requirement.&lt;/p&gt;
&lt;p&gt;Still, contributor and CI environments are part of the migration cost too.    😿&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-so-was-moving-from-tsup-to-tsdown-worth-it-&quot;&gt;🎯 So, Was Moving from tsup to tsdown Worth It?&lt;/h3&gt;
&lt;p&gt;For &lt;code&gt;is-kit&lt;/code&gt;, I think yes.&lt;/p&gt;
&lt;p&gt;But not because of performance.&lt;/p&gt;
&lt;p&gt;The migration preserved the package contract across ESM, CJS, declarations, exports, and the packed-package consumer tests.&lt;/p&gt;
&lt;p&gt;The configuration change was small and reviewable.&lt;/p&gt;
&lt;p&gt;My existing Node environment satisfies the new build requirement.&lt;/p&gt;
&lt;p&gt;And the build setup is now aligned with the actively evolving Rolldown ecosystem.&lt;/p&gt;
&lt;p&gt;The cost was also real&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;artifact size increased&lt;/li&gt;
&lt;li&gt;Node build requirements increased&lt;/li&gt;
&lt;li&gt;output extensions needed explicit configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So I wouldn't describe this as&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Everyone using tsup should migrate immediately because tsdown is faster!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That's not what this experiment showed.&lt;/p&gt;
&lt;p&gt;For me, the better conclusion is&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;If your project already meets the Node requirements and you want to move your build setup toward the Rolldown ecosystem, migrating a small library from tsup to tsdown can be a very reasonable change.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But verify the package you publish.&lt;/p&gt;
&lt;p&gt;Not only the source.&lt;br&gt;
Not only the config.&lt;br&gt;
And definitely not only the green build message. 😸&lt;/p&gt;
&lt;p&gt;Because the most interesting bug in this migration happened when the build said everything was fine.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If you'd like to see the package from this article in a real project, &lt;code&gt;is-kit&lt;/code&gt; is open source.&lt;/p&gt;
&lt;p&gt;It's a lightweight, zero-dependency TypeScript type guard library focused on runtime validation and safe narrowing.&lt;/p&gt;
&lt;p&gt;If it looks useful for your project, I'd be happy if you gave it a try and a ⭐ on GitHub is always appreciated.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/is-kit&quot;&gt;https://github.com/nyaomaru/is-kit&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Issues and PRs are welcome too. 😸&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30504/migrating-a-real-typescript-oss-library-from-tsup-to-tsdown</guid>
<pubDate>Fri, 09 Oct 2026 18:00:41 +0000</pubDate>
</item>
<item>
<title>Handling Exceptions Like a Pro in Java</title>
<link>https://coderlegion.com/4210/handling-exceptions-like-a-pro-in-java</link>
<description>&lt;h3 id=&quot;handling-exceptions-like-a-pro-in-java-stop-using-catch-all-blocks&quot;&gt;Handling Exceptions Like a Pro in Java: Stop Using catch-All Blocks&lt;/h3&gt;
&lt;p&gt;If you only handle &lt;em&gt;Exceptions&lt;/em&gt; in Java with just generic &lt;code&gt;try-catch&lt;/code&gt; then I appeal you to stop and try these below tips instead, you'll thank me later :)&lt;/p&gt;
&lt;p&gt;Let’s be honest — most of us start out doing the lazy thing:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;try {
    // risky code
} catch (Exception e) {
    e.printStackTrace();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looks safe. But what you've actually written is the software equivalent of sweeping all the dirt under the rug. No clue what went wrong. No proper recovery. Just vibes. Which I bet is a menace to mankind.&lt;/p&gt;
&lt;p&gt;Let’s fix that. Let's learn how real Java pros handle exceptions — with intention, clarity, and sometimes, a touch of functional programming.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;the-problem-with-catch-alls&quot;&gt;The Problem with Catch-Alls&lt;/h4&gt;
&lt;p&gt;Catching &lt;em&gt;Exception&lt;/em&gt; or worse, &lt;em&gt;Throwable&lt;/em&gt;, is like saying “I don’t know what might break, so I’ll just catch everything.” But that makes your app unpredictable and hard to debug.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;catch (Exception e) {
    // What are you even catching here? FileNotFound? NullPointer?
    // Did you miss an InterruptedException that should be re-thrown?
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You're hiding the bugs that should scream in your face during development.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;So rule no. 1:&lt;/strong&gt; Be specific. Catch only what you’re ready to handle.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;catch-the-exact-thing-you-re-solving&quot;&gt;Catch the Exact Thing You’re Solving&lt;/h4&gt;
&lt;p&gt;Let's say you're reading a file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;try {
    Files.readAllLines(Paths.get(&quot;data.txt&quot;));
} catch (IOException e) {
    System.err.println(&quot;Could not read the file: &quot; + e.getMessage());
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Much better! Now your intention is clear, and you're handling something specific.&lt;/p&gt;
&lt;p&gt;Bonus: This also helps you avoid accidentally swallowing critical runtime exceptions like &lt;code&gt;OutOfMemoryError&lt;/code&gt; or &lt;code&gt;InterruptedException&lt;/code&gt; — things that you should not handle casually.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;move-to-functional-style-exception-handling&quot;&gt;Move to Functional-Style Exception Handling&lt;/h4&gt;
&lt;p&gt;Functional programming has been slowly sipping tea and watching us drown in imperative chaos. But now it’s time to borrow some of its elegance.&lt;/p&gt;
&lt;p&gt;Enter libraries like &lt;strong&gt;&lt;em&gt;Vavr&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;With &lt;em&gt;Vavr&lt;/em&gt;, you wrap potentially failing logic into a Try, and then compose your actions in a clear, fluent way which is more readable and soothing to eyes &lt;/p&gt;
&lt;p&gt;Example :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;import io.vavr.control.Try;

Try&amp;lt;String&amp;gt; content = Try.of(() -&amp;gt; new String(Files.readAllBytes(Paths.get(&quot;data.txt&quot;))))
    .onFailure(e -&amp;gt; System.err.println(&quot;Something went wrong: &quot; + e.getMessage()))
    .map(String::toUpperCase);

content.forEach(System.out::println);

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Look at that! No try-catch noise, just pure, functional clarity. You’ve isolated the risky part and handled failure explicitly, without polluting your logic.&lt;/p&gt;
&lt;p&gt;You can even chain retries, fallbacks, and defaults like a boss:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;String result = Try.of(() -&amp;gt; riskyCall())
    .recover(throwable -&amp;gt; &quot;default&quot;)
    .get();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is &lt;strong&gt;&lt;em&gt;real&lt;/em&gt;&lt;/strong&gt; control over flow, not just catching and logging.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;checked-vs-unchecked-exceptions-handle-like-you-mean-it&quot;&gt;Checked vs Unchecked Exceptions — Handle Like You Mean It&lt;/h4&gt;
&lt;p&gt;Java makes this distinction. Checked exceptions (like IOException, SQLException) force you to handle or declare them. Unchecked exceptions (like &lt;code&gt;NullPointerException&lt;/code&gt;, &lt;code&gt;IllegalArgumentException&lt;/code&gt;) are on you to prevent.&lt;/p&gt;
&lt;p&gt;Here’s the mindset shift:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Checked&lt;/strong&gt;: These are expected problems (e.g., file not found, DB connection fails). Handle or bubble up with context.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Unchecked:&lt;/strong&gt; These are code smells and should be avoided upfront via validation and logic.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Don’t convert checked exceptions into unchecked ones just because it's easier. That’s like turning off the fire alarm because it’s loud.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;-clean-exception-strategy&quot;&gt;♂️ Clean Exception Strategy&lt;/h4&gt;
&lt;p&gt;Here’s what pros usually follow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Fail fast.&lt;/strong&gt; Don’t catch what you can’t handle meaningfully.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wrap with context&lt;/strong&gt;. If rethrowing, always add context.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use custom exceptions&lt;/strong&gt; to model your domain (e.g., &lt;code&gt;UserNotFoundException&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Don’t log and throw.&lt;/strong&gt; Do one — logging is for terminal exceptions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Don’t just suppress&lt;/strong&gt;. Silent catch blocks are crime scenes.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h4 id=&quot;-bonus-tools-that-help&quot;&gt;️ Bonus Tools That Help&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Lombok’s &lt;code&gt;@SneakyThrows&lt;/code&gt;: Can be useful, but also dangerous — it hides checked exceptions.&lt;/li&gt;
&lt;li&gt;Vavr’s &lt;code&gt;Try&lt;/code&gt;, &lt;code&gt;Either&lt;/code&gt;, &lt;code&gt;Option&lt;/code&gt;: Bring Scala-style control flow to Java, and let you reason about success/failure better.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ExceptionUtils&lt;/strong&gt; from Apache Commons: Helps in deep exception analysis.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h4&gt;
&lt;p&gt;If you're still throwing catch-all blocks and logging stack traces like confetti, it’s time to upgrade. Java gives you the tools to be precise, and functional programming gives you the grace.&lt;/p&gt;
&lt;p&gt;Your goal should not be to handle exceptions. It should be to own them.&lt;/p&gt;
&lt;p&gt;Make your exception handling tell a story — one that debuggers, teammates, and even future-you will appreciate.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Stop catching Exceptions, start catching intent.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
<category>API with Java</category>
<guid isPermaLink="true">https://coderlegion.com/4210/handling-exceptions-like-a-pro-in-java</guid>
<pubDate>Mon, 21 Jul 2025 19:21:30 +0000</pubDate>
</item>
<item>
<title>Clean Async State Management with Pinia Colada</title>
<link>https://coderlegion.com/3728/clean-async-state-management-with-pinia-colada</link>
<description>&lt;hr&gt;
&lt;p&gt;Managing async state—think loading spinners, error handling, stale data—is notoriously messy. If you’ve wrestled with nested subscriptions or redundant fetch logic in Vue+Pinia apps, &lt;strong&gt;Pinia Colada&lt;/strong&gt; might be the game-changer you need.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-is-pinia-colada-&quot;&gt;What Is Pinia Colada?&lt;/h3&gt;
&lt;p&gt;Pinia Colada is a powerful data-fetching layer built on top of Pinia. It eliminates boilerplate by providing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automatic &lt;strong&gt;caching&lt;/strong&gt; and &lt;strong&gt;deduplication&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Smart query &lt;strong&gt;invalidation&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Declarative API for &lt;strong&gt;queries&lt;/strong&gt; (data fetches) and &lt;strong&gt;mutations&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Support for &lt;strong&gt;optimistic updates&lt;/strong&gt;, SSR, and tree-shaken size of \~2 KB&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;quick-start&quot;&gt;Quick Start&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install pinia @pinia/colada
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then in your Vue app setup:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { createPinia } from 'pinia'
import { PiniaColada } from '@pinia/colada'

app.use(createPinia())
app.use(PiniaColada, {/* optional plugins */})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you’re ready to use &lt;code&gt;useQuery&lt;/code&gt; and &lt;code&gt;useMutation&lt;/code&gt; everywhere.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;example-querying-data&quot;&gt;Example: Querying Data&lt;/h3&gt;
&lt;p&gt;Instead of manual loading flags, error state, and fetch logic:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const { data, error, isPending, isLoading, refresh } = useQuery({
  key: ['users'],
  query: fetchUsers,
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then in the template, you can declaratively handle the three states:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;isPending&lt;/code&gt; — first fetch&lt;/li&gt;
&lt;li&gt;&lt;code&gt;isLoading&lt;/code&gt; — refreshes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;error&lt;/code&gt; — if broken&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data&lt;/code&gt; — once resolved&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;mutations-amp-invalidation&quot;&gt;Mutations &amp;amp; Invalidation&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const { mutate: addComment, isLoading } = useMutation({
  mutation: postComment,
  onSettled: async ({ id }) =&amp;gt; {
    await queryCache.invalidateQueries({ key: ['comments', id], exact: true })
  }
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This lets you optimistically update and ensure fresh data before closing modal dialogs or user notifications.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-you-ll-love-it&quot;&gt;Why You’ll Love It&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Minimal state boilerplate: No more juggling &lt;code&gt;isLoading&lt;/code&gt;, &lt;code&gt;isError&lt;/code&gt;, and &lt;code&gt;data&lt;/code&gt; manually.&lt;/li&gt;
&lt;li&gt;Built-in caching and deduplication for better UX and efficiency.&lt;/li&gt;
&lt;li&gt;Fully typed, tree-shakeable, zero deps (besides Pinia), and SSR-ready.&lt;/li&gt;
&lt;li&gt;Plugins like &lt;strong&gt;autoRefetch&lt;/strong&gt; and &lt;strong&gt;delay&lt;/strong&gt; help avoid flickers or stale data.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;real-world-feedback&quot;&gt;Real-World Feedback&lt;/h3&gt;
&lt;p&gt;Reddit users highlight Pinia Colada's advantage for API-driven state:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“This is what Pinia stores call service methods… API calls inside sweet local store. It’s perfect to avoid multiple fetches.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Others note:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“Pinia is for global app state. For API caching use Tanstack Query… Pinia Colada consolidates the conundrum.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;tl-dr-when-to-use-it&quot;&gt;TL;DR – When to Use It&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Use Pinia Colada when&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You want clean, reusable logic for API calls&lt;/li&gt;
&lt;li&gt;You care about efficient caching and UX consistency&lt;/li&gt;
&lt;li&gt;You prefer declarative state over imperative patterns&lt;/li&gt;
&lt;li&gt;You work with mutable server data and want stale-data control&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;want-to-go-further-&quot;&gt;Want to Go Further?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Explore &lt;strong&gt;optimistic updates&lt;/strong&gt;, SSR support, and GraphQL usage&lt;/li&gt;
&lt;li&gt;Combine with Vue Router Data Loaders and modular async handling&lt;/li&gt;
&lt;li&gt;Try plugins like auto-refetch and delay to polish UX&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Pinia Colada brings a modern, well-structured approach to async state in Vue apps—cutting boilerplate, reducing bugs, and improving developer happiness. Whether it's simple CRUD or complex dialogs or pagination, it's well worth a look.&lt;/p&gt;
&lt;hr&gt;
</description>
<category>Frontend Dev</category>
<guid isPermaLink="true">https://coderlegion.com/3728/clean-async-state-management-with-pinia-colada</guid>
<pubDate>Sat, 05 Jul 2025 08:33:04 +0000</pubDate>
</item>
<item>
<title>Exploring More in Tkinter</title>
<link>https://coderlegion.com/253/exploring-more-in-tkinter</link>
<description>&lt;p&gt;In this article, we'll delve into some of the more intricate aspects of &lt;strong&gt;Tkinter&lt;/strong&gt;, exploring advanced techniques that will empower you to create even more powerful and polished GUI applications. Throughout our exploration, we'll uncover invaluable tools such as the &lt;strong&gt;&lt;code&gt;ttk&lt;/code&gt; module&lt;/strong&gt; for enhanced widget styling, the &lt;strong&gt;Canvas&lt;/strong&gt; widget for versatile graphics rendering, and the &lt;strong&gt;Grid&lt;/strong&gt; geometry manager for precise layout control.  From &lt;strong&gt;custom themes&lt;/strong&gt; to &lt;strong&gt;file handling strategies&lt;/strong&gt;, gear up to elevate your Tkinter expertise to the next level.&lt;/p&gt;
&lt;p&gt;If you are new to Tkinter, I highly recommend you to thoroughly review &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/190/tkinter-library-in-python&quot;&gt;this article&lt;/a&gt; or the &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.python.org/3/library/tkinter.html&quot;&gt;official documentation&lt;/a&gt;. Familiarizing yourself with its contents will provide a solid foundation for understanding and effectively utilizing Tkinter.&lt;/p&gt;
&lt;p&gt;In the code snippets provided, add these lines in the beginning:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import tkinter as tk
from tkinter import ttk
window = tk.Tk()
window.geometry(&quot;457x335&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and this line at the end:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;window.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;custom-styles-and-themes&quot;&gt;Custom Styles and Themes&lt;/h2&gt;
&lt;p&gt;Tkinter enables customized visual presentation through custom styles and themes, using the &lt;code&gt;ttk&lt;/code&gt; module for personalized widget designs, ensuring consistency across platforms.&lt;/p&gt;
&lt;p&gt;To deepen our understanding, let's personalize our textbox, button, and label by customizing their appearance:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import tkinter as tk
from tkinter import ttk

def submit_text():
    text = text_entry.get()
    text_label.config(text=text, font=(&quot;Times New Roman&quot;, 12, &quot;bold&quot;), foreground=&quot;green&quot;)     #customize label

window = tk.Tk()
window.geometry(&quot;457x335&quot;)
window.title(&quot;Custom Styles&quot;)

style = ttk.Style()     #to define custom styles
style.configure(&quot;Custom.TEntry&quot;, foreground=&quot;blue&quot;, fieldbackground=&quot;lightblue&quot;)     #customize textbox
style.configure(&quot;Custom.TButton&quot;, foreground=&quot;red&quot;, background=&quot;red&quot;)     #customize button

text_entry = ttk.Entry(window, width=30, style=&quot;Custom.TEntry&quot;)
text_entry.pack(padx=10, pady=10)
submit_button = ttk.Button(window, text=&quot;Submit&quot;, command=submit_text, style=&quot;Custom.TButton&quot;)     #sends entered text to the submit_text() function
submit_button.pack(padx=10, pady=5)
text_label = ttk.Label(window)
text_label.pack(padx=10, pady=10)
window.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=2820964779047794875&quot; alt=&quot;Tkinter_custom&quot;&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;submit_text&lt;/code&gt; function retrieves your text from the &lt;code&gt;text_entry&lt;/code&gt; textbox and updates the &lt;code&gt;text_label&lt;/code&gt; with it. The label's appearance is customized using Times New Roman font, size 12, bold, and green color. Custom styles (&lt;code&gt;Custom.TEntry&lt;/code&gt; for the textbox and &lt;code&gt;Custom.TButton&lt;/code&gt; for the button) are defined and configured using the &lt;code&gt;ttk.Style()&lt;/code&gt; method to set properties like &lt;code&gt;foreground&lt;/code&gt; and &lt;code&gt;background&lt;/code&gt; colors.&lt;/p&gt;
&lt;h2 id=&quot;integration-with-python-libraries&quot;&gt;Integration with Python Libraries&lt;/h2&gt;
&lt;p&gt; Tkinter can be integrated with other Python libraries and tools to enhance its functionality:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integration with Matplotlib&lt;/strong&gt;: You can make your Tkinter app more informative by adding interactive charts to display data trends or visualizations. By integrating Matplotlib, a popular Python library for creating charts and graphs, you can easily embed dynamic plots directly into your Tkinter windows.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integration with Scikit-learn&lt;/strong&gt;: You can combine Tkinter with Scikit-learn to develop GUI applications for machine learning tasks such as classification, regression, and clustering. This integration enables us to build interactive interfaces for training models, evaluating performance, and making predictions.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;div-blue&quot;&gt; &lt;span class=&quot;alert-header&quot;&gt;Note:&lt;/span&gt;&lt;span class=&quot;alert-body&quot;&gt; In order to install sklearn library locally, run the command: `pip install scikit-learn`&lt;/span&gt; &lt;/div&gt;
&lt;div class=&quot;div-red&quot;&gt; &lt;span class=&quot;alert-header&quot;&gt;Caution:&lt;/span&gt; &lt;span class=&quot;alert-body&quot;&gt;Ensure local compatibility by installing `matplotlib` and `matplotlib.backends.backend_tkagg` before running this code.&lt;/span&gt; &lt;/div&gt;
&lt;p&gt;Let's delve into the concept using an example of Linear Regression applied to predict outcomes for the Diabetes dataset:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import tkinter as tk
from tkinter import ttk     #for themed widgets
import matplotlib.pyplot as plt
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error

diabetes = load_diabetes()     #Load the dataset
X_train, X_test, y_train, y_test = train_test_split(diabetes.data, diabetes.target, test_size=0.2, random_state=42)

window = tk.Tk()
window.geometry(&quot;650x500&quot;)
window.title(&quot;Diabetes Linear Regression&quot;)

def train_and_plot():
    model = LinearRegression()
    model.fit(X_train, y_train)     #train
    y_pred_train = model.predict(X_train)     #predict
    y_pred_test = model.predict(X_test)
    mse_train = mean_squared_error(y_train, y_pred_train)     #calculate mean squared error
    mse_test = mean_squared_error(y_test, y_pred_test)
    
    plt.figure(figsize=(8, 6))     #create the plot
    plt.scatter(y_test, y_pred_test, color='blue')
    plt.plot([min(y_test), max(y_test)], [min(y_test), max(y_test)], color='red', linestyle='--')
    plt.xlabel(&quot;Actual&quot;)
    plt.ylabel(&quot;Predicted&quot;)
    plt.title(&quot;Linear Regression Graph&quot;)
    plt.grid(True)
    plt.tight_layout()     #ensures the plot fits within the Tkinter window
    result_label.config(text=f&quot;Train MSE: {mse_train:.2f}\tTest MSE: {mse_test:.2f}&quot;)
    canvas = FigureCanvasTkAgg(plt.gcf(), master=window)     #converts the plot into a format that can be displayed in the Tkinter window
    canvas.draw()
    canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)     #adds the canvas widget to the Tkinter window.

train_button = ttk.Button(window, text=&quot;Train and Plot&quot;, command=train_and_plot)
train_button.pack(pady=10)
result_label = ttk.Label(window, text=&quot;&quot;)
result_label.pack()
window.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=10740781036814253574&quot; alt=&quot;Tkinter_interface&quot;&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;train_and_plot&lt;/code&gt; function trains a linear regression model on the dataset, predicts the target values, calculates the mean squared error for both training and testing sets, and then plots the predicted values against the actual values using matplotlib. It also displays the calculated mean squared errors on your GUI window.&lt;/p&gt;
&lt;h2 id=&quot;advanced-widgets&quot;&gt;Advanced Widgets&lt;/h2&gt;
&lt;p&gt;Tkinter provides a wide range of widgets beyond the basic ones like labels and buttons. You can explore and utilize widgets like treeview, notebook, progressbar, etc. to create sophisticated user interfaces.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A &lt;strong&gt;treeview&lt;/strong&gt; to display hierarchical data, like a file explorer.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; import tkinter as tk
 from tkinter import ttk     #for themed widgets
 window = tk.Tk()
 window.geometry(&quot;640x300&quot;)
 tree = ttk.Treeview(window)
 tree[&quot;columns&quot;] = (&quot;Fruits&quot;, &quot;Quantity&quot;)
 tree.heading(&quot;#0&quot;, text=&quot;Sr. No.&quot;)     #First column (index=0) will be Sr. No.
 tree.heading(&quot;Fruits&quot;, text=&quot;Fruits&quot;)
 tree.heading(&quot;Quantity&quot;, text=&quot;Quantity (kg)&quot;)
 tree.insert(&quot;&quot;, tk.END, text=&quot;1&quot;, values=(&quot;Apple&quot;, 30))
 tree.insert(&quot;&quot;, tk.END, text=&quot;2&quot;, values=(&quot;Orange&quot;, 25))
 tree.insert(&quot;&quot;, tk.END, text=&quot;3&quot;, values=(&quot;Mango&quot;, 29))
 tree.pack()
 window.mainloop()

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=17024339317789079325&quot; alt=&quot;treeview&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Treeview widget &lt;code&gt;tree&lt;/code&gt; consists of two columns: &quot;Fruits&quot; and &quot;Quantity (kg)&quot;. The &lt;code&gt;heading&lt;/code&gt; method sets headings for each column, and the &lt;code&gt;insert&lt;/code&gt; method adds items to the Treeview. Each item is inserted with a unique identifier &quot;Sr. No.&quot; and its corresponding values for &quot;Fruits&quot; and &quot;Quantity&quot; are entered.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A &lt;strong&gt;notebook&lt;/strong&gt; to organize your app into different tabs, like in a web browser.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; import tkinter as tk
 from tkinter import ttk     #for themed widgets
 window = tk.Tk()
 window.geometry(&quot;457x335&quot;)
 notebook = ttk.Notebook(window)
 tab1 = ttk.Frame(notebook)
 tab2 = ttk.Frame(notebook)
 tab3 = ttk.Frame(notebook)
 label1 = tk.Label(tab1, text=&quot;This is Tab 1&quot;)     #for tab 1
 label1.pack()
 entry2 = tk.Entry(tab2)     #for tab 2
 entry2.pack()
 check_var = tk.BooleanVar()
 check_button3 = tk.Checkbutton(tab3, text=&quot;This is Tab 3&quot;, variable=check_var)     #for tab 3
 check_button3.pack()
 notebook.add(tab1, text=&quot;Tab 1&quot;)
 notebook.add(tab2, text=&quot;Tab 2&quot;)
 notebook.add(tab3, text=&quot;Tab 3&quot;)
 notebook.pack()
 window.mainloop()

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=14879740221736993260&quot; alt=&quot;Tab1&quot;&gt;&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=14632322578103106639&quot; alt=&quot;Tab2&quot;&gt;&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=16524913512561924585&quot; alt=&quot;Tab3&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It creates a Notebook widget &lt;code&gt;notebook&lt;/code&gt;, which allows 3 tabs to be displayed, namely &lt;code&gt;Tab 1&lt;/code&gt;, &lt;code&gt;Tab 2&lt;/code&gt;, and &lt;code&gt;Tab 3&lt;/code&gt;, ceated using &lt;code&gt;ttk.Frame&lt;/code&gt;. &lt;code&gt;Tab 1&lt;/code&gt; contains a Label widget with the text &quot;This is Tab 1&quot;. &lt;code&gt;Tab 2&lt;/code&gt; contains an Entry widget for user input. &lt;code&gt;Tab 3&lt;/code&gt; contains a Checkbutton widget with the text &quot;This is Tab 3&quot;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A &lt;strong&gt;progressbar&lt;/strong&gt; to show how far along a task is, like when you're downloading a file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; import tkinter as tk
 from tkinter import ttk
 import time
    
 def start_task():
     progress_bar.start(10)     #start the progressbar (animation is updated every 10 milliseconds)
     window.after(3000, stop_task)     #simulate task completion after 3 seconds
    
 def stop_task():
     progress_bar.stop()      #stop the progressbar
    
 window = tk.Tk()
 window.geometry(&quot;457x335&quot;)
 progress_bar = ttk.Progressbar(window, orient=&quot;horizontal&quot;, length=200, mode=&quot;indeterminate&quot;)
 progress_bar.pack()
 start_button = tk.Button(window, text=&quot;Start Task&quot;, command=start_task)
 start_button.pack(pady=20)
 window.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=17085322627996677804&quot; alt=&quot;Tkinter_progressbar&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When the &quot;Start Task&quot; button is clicked, it triggers the &lt;code&gt;start_task&lt;/code&gt; function, which starts the progress bar animation using the &lt;code&gt;start&lt;/code&gt; method. The &lt;code&gt;after&lt;/code&gt; method is used to schedule the &lt;code&gt;stop_task&lt;/code&gt; function to run after 3 seconds. The &lt;code&gt;stop_task&lt;/code&gt; function stops the progress bar animation using the &lt;code&gt;stop&lt;/code&gt; method.&lt;/p&gt;
&lt;h2 id=&quot;advanced-layout-management&quot;&gt;Advanced Layout Management&lt;/h2&gt;
&lt;p&gt;Advanced Layout Management can be used to make your Tkinter app look polished. Layout management helps you move widgets around and make your app look just the way you want using  &lt;code&gt;grid&lt;/code&gt;, &lt;code&gt;place&lt;/code&gt;, and &lt;code&gt;pack&lt;/code&gt; managers.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Grid&lt;/strong&gt;&lt;br&gt;
You can place widgets in rows and columns, and they will organize neatly into place.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  import tkinter as tk
  window = tk.Tk()
  window.geometry(&quot;457x335&quot;)
  label1 = tk.Label(window, text=&quot;Subject&quot;)
  label2 = tk.Label(window, text=&quot;Marks&quot;)
  entry1 = tk.Entry(window)     #Widgets
  entry2 = tk.Entry(window)
  entry3 = tk.Entry(window)
  entry4 = tk.Entry(window)
  label1.grid(row=0, column=0)     #Layout
  label2.grid(row=0, column=1)
  entry1.grid(row=1, column=0)
  entry2.grid(row=1, column=1)
  entry3.grid(row=2, column=0)
  entry4.grid(row=2, column=1)
  window.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  &lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=6534270145641510254&quot; alt=&quot;Tkinter_grid&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This code sets up 2 labels &lt;code&gt;Subject&lt;/code&gt; and &lt;code&gt;Marks&lt;/code&gt; and 4 entry widgets for data input. The layout is organized with &lt;code&gt;Subject&lt;/code&gt; in row 0, column 0, &lt;code&gt;Marks&lt;/code&gt; in row 0, column 1. Entry widgets &lt;code&gt;entry1&lt;/code&gt; and &lt;code&gt;entry2&lt;/code&gt; occupy row 1, columns 0 and 1 respectively, while &lt;code&gt;entry3&lt;/code&gt; and &lt;code&gt;entry4&lt;/code&gt; are in row 2, columns 0 and 1.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pack&lt;/strong&gt;&lt;br&gt;
You can stack widgets on top of each other or arrange them side by side, and Tkinter will figure out how to fit them all in.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  import tkinter as tk
  window = tk.Tk()
  window.geometry(&quot;457x335&quot;)
  button1 = tk.Button(window, text=&quot;Left&quot;)     #Widgets
  button2 = tk.Button(window, text=&quot;Top&quot;)
  button3 = tk.Button(window, text=&quot;Right&quot;)
  button4 = tk.Button(window, text=&quot;Bottom&quot;)
  button1.pack(side=tk.LEFT)     #Layout
  button2.pack(side=tk.TOP)
  button3.pack(side=tk.RIGHT)
  button4.pack(side=tk.BOTTOM)
  window.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  &lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=15178167496495803908&quot; alt=&quot;Tkinter_pack&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The buttons &lt;code&gt;button1&lt;/code&gt;, &lt;code&gt;button2&lt;/code&gt;, &lt;code&gt;button3&lt;/code&gt; and &lt;code&gt;button4&lt;/code&gt; are positioned by providing the &lt;code&gt;side&lt;/code&gt; parameter with values &lt;code&gt;tk.LEFT&lt;/code&gt;, &lt;code&gt;tk.TOP&lt;/code&gt;, &lt;code&gt;tk.RIGHT&lt;/code&gt;, or &lt;code&gt;tk.BOTTOM&lt;/code&gt; respectively.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Place&lt;/strong&gt;&lt;br&gt;
You can specify exactly where you want each widget to go by giving it coordinates, like x and y coordinates on a graph.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  import tkinter as tk
  window = tk.Tk()
  window.geometry(&quot;457x335&quot;)
  label1 = tk.Label(window, text=&quot;x_50_y_50&quot;)     #Widgets
  label2 = tk.Label(window, text=&quot;x_200_y_200&quot;)
  label1.place(x=50, y=50)     #Layout
  label2.place(x=200, y=200)
  window.mainloop()

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  &lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=11723976143771039796&quot; alt=&quot;Tkinter_place&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;place&lt;/code&gt; method is used to position label widgets. &lt;code&gt;label1&lt;/code&gt; is placed at coordinates (50, 50), and &lt;code&gt;label2&lt;/code&gt; is placed at coordinates (200, 200).&lt;/p&gt;
&lt;h2 id=&quot;file-handling&quot;&gt;File Handling&lt;/h2&gt;
&lt;p&gt;Tkinter facilitates file handling through the &lt;code&gt;filedialog&lt;/code&gt; tool, enabling users to interactively select files on their computer. With this feature, your application can support tasks like creating, opening, saving, and editing various file types such as documents, images, or spreadsheets.&lt;/p&gt;
&lt;p&gt;Let's understand this using an example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import tkinter as tk
from tkinter import filedialog

def open_file():
    file_path = filedialog.askopenfilename()     #opens a dialog window to select an existing file or create a new one
    if not file_path:
        return
    with open(file_path, 'a+') as file:     #'a+' to append the text to the file
        file.write(&quot;January\n&quot;)
        file.write(&quot;February\n&quot;)
        file.write(&quot;March\n&quot;)
        file.write(&quot;April\n&quot;)
        file.write(&quot;May\n&quot;)
    status_label.config(text=f&quot;File '{file_path}' has been written and closed.&quot;)

window = tk.Tk()
window.geometry(&quot;457x335&quot;)
window.title(&quot;File Writer&quot;)
open_button = tk.Button(window, text=&quot;Open a File&quot;, command=open_file)
open_button.pack(pady=20)
status_label = tk.Label(window, text=&quot;&quot;)
status_label.pack()
root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=16945057073417130965&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=2325960918843338371&quot; alt=&quot;file_open_1&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=15519562952456146282&quot; alt=&quot;file_open_2&quot;&gt;&lt;/p&gt;
&lt;p&gt;When clicked, the button &quot;Open a File&quot; triggers the &lt;code&gt;open_file&lt;/code&gt; function, which opens a dialog window allowing you to select an existing file or create a new one. If a file is selected, the code appends the strings &quot;January&quot;, &quot;February&quot;...&quot;May&quot; to the file and the file is closed. The &lt;code&gt;status_label&lt;/code&gt; is then updated to display a message confirming the file write operation.&lt;/p&gt;
&lt;div class=&quot;div-blue&quot;&gt; &lt;span class=&quot;alert-header&quot;&gt;Note:&lt;/span&gt;  &lt;span class=&quot;alert-body&quot;&gt; The file will automatically be closed when the `with` block is exited. However, you may explicitly close the file by calling `file.close()`.&lt;/span&gt; &lt;/div&gt;
&lt;h2 id=&quot;modal-and-modeless-dialogs&quot;&gt;Modal and Modeless Dialogs&lt;/h2&gt;
&lt;p&gt;Tkinter offers both modal and modeless dialogs, ideal for user input, confirmation messages, or notifications. The &lt;code&gt;tkinter.messagebox&lt;/code&gt; module provides standard message boxes, while custom dialog windows can be created using &lt;code&gt;Toplevel&lt;/code&gt; widgets.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Modal dialogs&lt;/strong&gt; are windows that require the user to interact with them before returning to the main application window. They typically block interaction with other windows in the application until they are closed.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  import tkinter as tk
  from tkinter import messagebox

  def show_modal_dialog():
      messagebox.showinfo(&quot;Modal Dialog&quot;, &quot;Pay attention to this dialog box!&quot;)     #does not let you proceed until you click the &quot;OK&quot; button

  window = tk.Tk()
  window.geometry(&quot;457x335&quot;)
  window.title(&quot;Modal Dialog Example&quot;)
  modal_button = tk.Button(window, text=&quot;Click me&quot;, command=show_modal_dialog)
  modal_button.pack(padx=20, pady=10)
  window.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  &lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=5781727160550960337&quot; alt=&quot;Tkinter_modal_dialog&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Modeless dialogs&lt;/strong&gt; are windows that allow the user to interact with both the dialog and the main application window simultaneously. They do not block interaction with other windows in the application, allowing the user to switch between them freely.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  import tkinter as tk

  def show_modeless_dialog():
      modeless_dialog = tk.Toplevel(window)     #allows you to interact with other open windows
      modeless_dialog.title(&quot;Modeless Dialog&quot;)
      modeless_dialog.geometry(&quot;250x100&quot;)
      label = tk.Label(modeless_dialog, text=&quot;Have a nice day!&quot;)
      label.pack(padx=20, pady=10)

  window = tk.Tk()
  window.geometry(&quot;457x335&quot;)
  window.title(&quot;Modeless Dialog Example&quot;)
  modeless_button = tk.Button(window, text=&quot;Click me&quot;, command=show_modeless_dialog)
  modeless_button.pack(padx=20, pady=10)
  window.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  &lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=1548469847260034179&quot; alt=&quot;Tkinter_modaless_dialog&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When clicked, the button &quot;Click me&quot; triggers the &lt;code&gt;show_modeless_dialog&lt;/code&gt; function, which creates a modeless dialog box using the &lt;code&gt;Toplevel&lt;/code&gt; widget. It will not hinder your interaction with the other windows.&lt;/p&gt;
&lt;div class=&quot;div-green&quot;&gt; &lt;span class=&quot;alert-header&quot;&gt;Tip:&lt;/span&gt; &lt;span class=&quot;alert-body&quot;&gt; Using a class for modeless dialogs can organize and encapsulate dialog functionality, particularly for more intricate dialogs with multiple components and interactions.&lt;/span&gt; &lt;/div&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Tkinter&lt;/strong&gt; is a versatile Python &lt;strong&gt;GUI&lt;/strong&gt; framework that enhances user interaction and functionality. With features like &lt;strong&gt;custom themes&lt;/strong&gt;, &lt;strong&gt;threading&lt;/strong&gt;, &lt;strong&gt;file handling&lt;/strong&gt;, &lt;strong&gt;dialogs&lt;/strong&gt;, &lt;strong&gt;layout management&lt;/strong&gt;, &lt;strong&gt;advanced widgets&lt;/strong&gt;, and integration with libraries like Matplotlib and scikit-learn, Tkinter empowers developers to create dynamic desktop applications. By leveraging these capabilities, developers can craft unique GUIs that elevate user experience, making Tkinter a top choice for Python GUI projects.&lt;/p&gt;
</description>
<category>Project Dev</category>
<guid isPermaLink="true">https://coderlegion.com/253/exploring-more-in-tkinter</guid>
<pubDate>Tue, 23 Apr 2024 21:35:28 +0000</pubDate>
</item>
<item>
<title>Building a Calculator using tkinter</title>
<link>https://coderlegion.com/210/building-a-calculator-using-tkinter</link>
<description>&lt;p&gt;When working on a basic calculator with &lt;strong&gt;Tkinter&lt;/strong&gt;, a widely-used GUI toolkit for &lt;strong&gt;Python&lt;/strong&gt;, Tkinter streamlines the incorporation of user-friendly elements such as buttons, entries, grids, and custom styles that are necessary for crafting graphical user interfaces, making it particularly well-suited for applications. Its simplicity and efficiency in managing user interactions make it an ideal choice for projects of this nature.&lt;/p&gt;
&lt;h2 id=&quot;creating-the-calculator&quot;&gt;Creating the Calculator&lt;/h2&gt;
&lt;p&gt;Now let us actually dive into the process of building the calculator, one step at a time. All the required steps are given below; follow them one by one, and you will be able to develop an efficient and interactive calculator.&lt;/p&gt;
&lt;h3 id=&quot;step-1-setting-up-environment&quot;&gt;Step 1: Setting Up Environment&lt;/h3&gt;
&lt;p&gt;Before we start building our calculator, ensure that you have Python installed on your system. To do so, the following section gives a short guide on the steps to be followed for different platforms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For Windows:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download and install Python from the &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.python.org/downloads/&quot;&gt;official website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Verify by running the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; python --version
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;For Linux:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Open terminal and copy and paste the code&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; sudo apt-get update
 sudo apt-get install python3
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once installed verify by running the following command&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; python --version
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;For MacOs:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download and install Python from the &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.python.org/downloads/macos/&quot;&gt;official website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;For reference &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.python.org/3/using/mac.html&quot;&gt;Steps to install python on MacOs&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Next, we need to install Tkinter, which is included with Python by default. So, there's no need to install it separately&lt;/p&gt;
&lt;h3 id=&quot;step-2-importing-libraries&quot;&gt;Step 2: Importing Libraries&lt;/h3&gt;
&lt;p&gt;In Python, the tkinter module is used for creating GUI applications. &lt;br&gt;
Now let's import these required libraries along with other required modules:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import tkinter as tk
from tkinter import messagebox
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;tkinter&lt;/strong&gt;: This module provides classes and functions for creating GUI applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;messagebox&lt;/strong&gt;: This submodule allows us to display message boxes for showing information or prompting the user for confirmation.&lt;/p&gt;
&lt;h3 id=&quot;step-3-creating-the-calculator-class&quot;&gt;Step 3: Creating the Calculator Class&lt;/h3&gt;
&lt;p&gt;Create a class called  &lt;code&gt;class Calculator&lt;/code&gt; that will represent our calculator application:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Calculator:
    def __init__(self, master):
        self.master = master
        self.master.title(&quot;Simple Calculator&quot;)
        self.master.geometry(&quot;300x400&quot;)
        self.master.resizable(False, False)
        self.create_widgets()

    def create_widgets(self):
        # Create entry widget for displaying input and results
        self.entry = tk.Entry(self.master, width=20, font=('Arial', 14), bd=5, justify=&quot;right&quot;)
        self.entry.grid(row=0, column=0, columnspan=4, padx=10, pady=10)

        # Define buttons
        buttons = [
            ('7', 1, 0), ('8', 1, 1), ('9', 1, 2), ('/', 1, 3),
            ('4', 2, 0), ('5', 2, 1), ('6', 2, 2), ('*', 2, 3),
            ('1', 3, 0), ('2', 3, 1), ('3', 3, 2), ('-', 3, 3),
            ('0', 4, 0), ('.', 4, 1), ('=', 4, 2), ('+', 4, 3)
        ]

        # Create buttons using a loop
        for (text, row, col) in buttons:
            button = tk.Button(self.master, text=text, width=5, height=2, 
            font=('Arial', 14), command=lambda t=text: self.on_button_click(t))
            button.grid(row=row, column=col, padx=5, pady=5)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Initialization&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;init&lt;/code&gt; method in the Calculator class sets up the basic features of the calculator when it's first created. It's like laying out the blueprint for how the calculator will look and work. The &lt;code&gt;master&lt;/code&gt; part helps position the calculator in the right place on the screen. &lt;code&gt;self.master.title(&quot;Simple Calculator&quot;)&lt;/code&gt; gives the calculator window a name, like &quot;Simple Calculator&quot;. The &lt;code&gt;self.master.geometry(&quot;300x400&quot;)&lt;/code&gt; part determines how big the calculator window will be, making sure it's neither too small nor too big. The line&lt;code&gt;self.master.resizable(False, False)&lt;/code&gt; prevents users from changing the size of the calculator window, keeping it neat and tidy. Finally, &lt;code&gt;self.create_widgets()&lt;/code&gt; starts the process of actually creating all the buttons and numbers you see on the calculator screen. It's like setting up the stage before the show begins. So, with all these steps, the calculator is ready to go when you open it up!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Widget Creation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;create_widgets&lt;/code&gt; method orchestrates the construction of the calculator's user interface, beginning with the establishment of an entry widget named &lt;code&gt;self.entry&lt;/code&gt; for displaying input and results, meticulously configured with specific attributes such as width, font size, border width, and text alignment. Positioned within the window using the grid method, this entry widget ensures clarity and organization of numerical data. Subsequently, utilizing a list named &lt;code&gt;buttons&lt;/code&gt; containing tuples of button details, the method dynamically generates button widgets for each entry within the list. These buttons, equipped with attributes such as text labels and dimensions, are seamlessly integrated into the window using the grid method, with each button assigned a command to execute the &lt;code&gt;on_button_click&lt;/code&gt; method upon interaction, passing the corresponding button text as an argument. Through meticulous placement and configuration, the &lt;code&gt;create_widgets&lt;/code&gt; method establishes a coherent and functional user interface, essential for intuitive interaction and efficient calculation.&lt;/p&gt;
&lt;h3 id=&quot;step-4-implementing-button-click-functionality&quot;&gt;Step 4: Implementing Button Click Functionality&lt;/h3&gt;
&lt;p&gt;Next, we define the &lt;code&gt;on_button_click()&lt;/code&gt; method to handle button clicks:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def on_button_click(self, text):
     if text == '=':
          try:
              result = eval(self.entry.get())
              self.entry.delete(0, tk.END)
              self.entry.insert(tk.END, str(result))
          except Exception as e:
              messagebox.showerror(&quot;Error&quot;, &quot;Invalid Input&quot;)

          elif text == 'C':
              self.entry.delete(0, tk.END)

          else:
              self.entry.insert(tk.END, text)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this block of code, several conditional statements and actions are executed based on the text of the clicked button within the calculator interface. First, there's a check to see if the button text is equal to '=' (the equal sign). If so, the mathematical expression entered in the entry widget (self.entry) is evaluated using the &lt;code&gt;eval()&lt;/code&gt; function, and the result is stored in the &lt;code&gt;result&lt;/code&gt; variable. The contents of the entry widget are then cleared using &lt;code&gt;self.entry.delete(0, tk.END)&lt;/code&gt;, and the calculated result is inserted into the entry widget using &lt;code&gt;self.entry.insert(tk.END, str(result))&lt;/code&gt;. In case an exception occurs during evaluation, it's caught using a try-except block, and an error message box is displayed using &lt;code&gt;messagebox.showerror(&quot;Error&quot;, &quot;Invalid Input&quot;)&lt;/code&gt;. Lastly, the text of the clicked button is inserted into the entry widget at the end (tk.END) using &lt;code&gt;self.entry.insert(tk.END, text)&lt;/code&gt;, allowing users to append their input string. These actions ensure proper functionality and user feedback within the calculator interface, facilitating smooth interaction and accurate computation.&lt;/p&gt;
&lt;h3 id=&quot;step-5-driver-function&quot;&gt;Step 5: Driver Function&lt;/h3&gt;
&lt;p&gt;Lastly, we need to create an instance of the Calculator class and run the &lt;code&gt;main&lt;/code&gt; event loop:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; if __name__ == &quot;__main__&quot;:
     root = tk.Tk()
     app = Calculator(root)
     root.mainloop()   
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The line &lt;code&gt; if __name__ == &quot;__main__&quot;&lt;/code&gt; serves as a conditional statement that ensures that the subsequent code block is executed only if the Python script is directly run by the interpreter, rather than being imported as a module into another script. Following this condition, &lt;code&gt;root = tk.Tk()&lt;/code&gt; initializes a new instance of the Tkinter Tk class, establishing the main window or root window for the graphical user interface (GUI) application. This window acts as a container for all other GUI elements to be displayed. Subsequently, &lt;code&gt;app = Calculator(root)&lt;/code&gt; creates an instance of the Calculator class, utilizing the root window as an argument, thereby initializing the calculator application within this main window. Lastly, &lt;code&gt;root.mainloop()&lt;/code&gt; initiates the Tkinter event loop, a critical component that continuously listens for user interactions, such as button clicks and window resizing, effectively maintaining the responsiveness of the GUI. This function call essentially blocks further script execution until the main window is closed by the user, ensuring the seamless functioning of the GUI application.&lt;/p&gt;
&lt;h3 id=&quot;step-6-outcome-of-implementation&quot;&gt;Step 6: Outcome of Implementation&lt;/h3&gt;
&lt;p&gt;  &lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=7603950783710353489&quot; alt=&quot;Outcome&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;code-snippet&quot;&gt;Code snippet&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import tkinter as tk
from tkinter import messagebox

class Calculator:
    def __init__(self, master):
        self.master = master
        self.master.title(&quot;Simple Calculator&quot;)
        self.master.geometry(&quot;300x400&quot;)
        self.master.resizable(False, False)
        self.create_widgets()

    # creating the widgets
    
    def create_widgets(self):
        # Create entry widget for displaying input and results
        self.entry = tk.Entry(
            self.master, width=20, font=(&quot;Arial&quot;, 14), bd=5, justify=&quot;right&quot;
        )
        self.entry.grid(row=0, column=0, columnspan=4, padx=10, pady=10)

        # Define buttons
        buttons = [
            (&quot;7&quot;, 1, 0),
            (&quot;8&quot;, 1, 1),
            (&quot;9&quot;, 1, 2),
            (&quot;/&quot;, 1, 3),
            (&quot;4&quot;, 2, 0),
            (&quot;5&quot;, 2, 1),
            (&quot;6&quot;, 2, 2),
            (&quot;*&quot;, 2, 3),
            (&quot;1&quot;, 3, 0),
            (&quot;2&quot;, 3, 1),
            (&quot;3&quot;, 3, 2),
            (&quot;-&quot;, 3, 3),
            (&quot;0&quot;, 4, 0),
            (&quot;.&quot;, 4, 1),
            (&quot;=&quot;, 4, 2),
            (&quot;+&quot;, 4, 3),
        ]

        # Create buttons using a loop
        for text, row, col in buttons:
            button = tk.Button(
                self.master,
                text=text,
                width=5,
                height=2,
                font=(&quot;Arial&quot;, 14),
                command=lambda t=text: self.on_button_click(t),
            )
            button.grid(row=row, column=col, padx=5, pady=5)

    # funtion creation for on click feature
    def on_button_click(self, text):
        if text == &quot;=&quot;:
            try:
                result = eval(self.entry.get())
                self.entry.delete(0, tk.END)
                self.entry.insert(tk.END, str(result))
            except Exception as e:
                messagebox.showerror(&quot;Error&quot;, &quot;Invalid Input&quot;)

        elif text == &quot;C&quot;:
            self.entry.delete(0, tk.END)

        else:
            self.entry.insert(tk.END, text)

    # main funtion is defined                
    if __name__ == &quot;__main__&quot;:
        root = tk.Tk()
        app = Calculator(root)
        root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;understanding-code-functionality&quot;&gt;Understanding Code Functionality&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When the calculator starts, it creates a window with an entry widget. This widget is where you can input numbers and view the results.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The calculator has buttons for digits from 0 to 9, the decimal point '.', and arithmetic operations like addition '+', subtraction '-', multiplication '*', and division '/'. These buttons are arranged in a grid layout.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;div-green&quot;&gt; &lt;span class=&quot;alert-header&quot;&gt;Tip:&lt;/span&gt; &lt;span class=&quot;alert-body&quot;&gt;Implement input validation to avoid potential security vulnerabilities or crashes. &lt;/span&gt; &lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When a button is clicked, it triggers the &lt;code&gt;on_button_click&lt;/code&gt; function. If the button is an arithmetic operation or a digit, it adds that value to the entry widget. If the button is '=' (equal sign), it evaluates the expression in the entry widget and displays the result. If it's 'C' (clear), it clears the entry widget.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When '=' is pressed, it tries to evaluate the expression entered in the entry widget using Python's &lt;code&gt;eval()&lt;/code&gt; function. If the expression is valid, it computes the result and displays it in the entry widget. If there's an error, such as invalid input or division by zero, it shows an error message.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;div-red&quot;&gt; &lt;span class=&quot;alert-header&quot;&gt;Warning:&lt;/span&gt; &lt;span class=&quot;alert-body&quot;&gt;Avoid using the eval() function with untrusted input to prevent code injection vulnerabilities.&lt;/span&gt;  &lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The program keeps running in a loop, waiting for user interactions, until the window is closed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Users can input numbers and perform arithmetic operations by clicking the respective buttons. They can clear the input by clicking 'C' and get the result by clicking '='.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;calculator-functionality-flowchart&quot;&gt;Calculator Functionality Flowchart&lt;/h2&gt;
&lt;p&gt;Following is an illustration of the functionality of a simple calculator program through a clear and logical flowchart.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=14107652295060674798&quot; alt=&quot;Flowchart&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;testing-result&quot;&gt;Testing Result&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=250390329310505356&quot; alt=&quot;Testing 1&quot;&gt;        &lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=4300637587976812647&quot; alt=&quot;Testing 2&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;possible-errors-that-might-occur&quot;&gt;Possible Errors that might occur&lt;/h2&gt;
&lt;p&gt;In building a calculator using Tkinter, potential errors include syntax mistakes, input validation issues, and challenges with complex operations. Effective debugging is crucial for ensuring the calculator functions reliably. Have a look at some of the problems along with their solutions that are listed below.&lt;/p&gt;
&lt;h3 id=&quot;division-by-zero-error-&quot;&gt;Division by zero error:&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: If the user divides by zero, Python will raise a ZeroDivisionError.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=5214973538443658959&quot; alt=&quot;Invalid Input&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Handle division by zero within the try-except block.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;try:
    result = eval(self.entry.get())
    if result == float('inf'):
         messagebox.showerror(&quot;Error&quot;, &quot;Division by zero&quot;)
    else:
        self.entry.delete(0, tk.END)
        self.entry.insert(tk.END, str(result))
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;syntax-error-in-evaluation-&quot;&gt;Syntax error in evaluation:&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: If the user enters an invalid expression that can't be evaluated, it will raise a SyntaxError.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=11542053887820982095&quot; alt=&quot;Invalid Input 2&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Catch SyntaxError and display an appropriate error message.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;except SyntaxError:
messagebox.showerror(&quot;Error&quot;, &quot;Invalid Expression&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;nameerror-due-to-undefined-variable-&quot;&gt;NameError due to undefined variable:&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: The code checks for the '=' and 'C' buttons, but they're not defined in the button list.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Add the '=' and 'C' buttons to the list with their respective positions.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;buttons = [
   ...
   ('=', 4, 2), ('C', 4, 0)
]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;missing-import-statement-&quot;&gt;Missing import statement:&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: The code uses messagebox without importing it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Import messagebox from tkinter.&lt;/p&gt;
&lt;p&gt;from tkinter import messagebox&lt;/p&gt;
&lt;h3 id=&quot;unsupported-operations-&quot;&gt;Unsupported operations:&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: The calculator allows arbitrary evaluation of expressions &lt;code&gt;using eval()&lt;/code&gt;, which can lead to unintended consequences.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Restrict the input to a certain format or implement a safer method for evaluation, such as parsing the expression manually.&lt;br&gt;
Here's a safer approach to evaluate expressions using &lt;code&gt;ast.literal_eval()&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import ast
def evaluate_expression(self, expression):
    try:
        return ast.literal_eval(expression)
    except (SyntaxError, ValueError):
        raise ValueError(&quot;Invalid expression&quot;)
  
def on_button_click(self, text):
    if text == '=':
        try:
            result = self.evaluate_expression(self.entry.get())
            self.entry.delete(0, tk.END)
            self.entry.insert(tk.END, str(result))
        except ValueError as e:
            messagebox.showerror(&quot;Error&quot;, str(e))
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion-&quot;&gt;Conclusion:&lt;/h2&gt;
&lt;p&gt;In conclusion, developing a simple calculator using &lt;strong&gt;Tkinter&lt;/strong&gt; in Python, offers a practical introduction to GUI application development. Tkinter's intuitive design, coupled with its &lt;strong&gt;cross-platform compatibility&lt;/strong&gt;, empowers developers to create interactive interfaces effortlessly. By leveraging Tkinter's event-driven model, we constructed a calculator with &lt;strong&gt;basic arithmetic operations&lt;/strong&gt;, &lt;strong&gt;error handling&lt;/strong&gt;, and &lt;strong&gt;clear functionality&lt;/strong&gt;. Utilizing widgets like &lt;strong&gt;Entry&lt;/strong&gt; and &lt;strong&gt;Button&lt;/strong&gt;, we orchestrated a user-friendly interface, enhancing accessibility and the user experience.&lt;/p&gt;
&lt;p&gt;Throughout the process, we capitalized on &lt;strong&gt;object-oriented programming&lt;/strong&gt; principles, encapsulating functionality within the Calculator class. This modular approach facilitates code organization and maintenance, promoting scalability and extensibility. Moreover, the integration of error handling mechanisms, such as &lt;strong&gt;try-except blocks&lt;/strong&gt; and &lt;strong&gt;message boxes&lt;/strong&gt;, underscores the importance of robustness in software design. These measures ensure graceful handling of unexpected inputs, enhancing the calculator's reliability and user confidence.&lt;/p&gt;
&lt;p&gt;By adhering to best practices and leveraging Tkinter's capabilities, we've crafted a functional calculator application that demonstrates the synergy between Python's versatility and Tkinter's simplicity. This endeavor serves as a springboard for aspiring developers to delve deeper into GUI programming and explore the myriad possibilities of Python's ecosystem.&lt;/p&gt;
</description>
<category>Project Dev</category>
<guid isPermaLink="true">https://coderlegion.com/210/building-a-calculator-using-tkinter</guid>
<pubDate>Tue, 02 Apr 2024 20:46:16 +0000</pubDate>
</item>
<item>
<title>Tkinter library in Python</title>
<link>https://coderlegion.com/190/tkinter-library-in-python</link>
<description>&lt;p&gt;In this article, we'll dive into the world of &lt;strong&gt;Graphical User Interface (GUI)&lt;/strong&gt; development using Python's &lt;strong&gt;Tkinter&lt;/strong&gt; library. Tkinter stands as a versatile and user-friendly tool for beginners and advanced programmers alike, simplifying the creation of &lt;strong&gt;GUI applications&lt;/strong&gt;. We aim to demystify the process of working with Tkinter, from understanding its core widgets to leveraging its strengths and navigating its limitations. By the end, you'll be equipped with the essentials of Tkinter to enhance your Python GUI development projects.&lt;/p&gt;
&lt;h2 id=&quot;understanding-tkinter&quot;&gt;Understanding Tkinter&lt;/h2&gt;
&lt;p&gt;Tkinter, short for &lt;strong&gt;Tk interface&lt;/strong&gt;, is a built-in Python library designed for creating interactive GUI applications. Tkinter, built on the Tk GUI toolkit, streamlines the creation of windows, buttons, menus, and other graphical elements. As an integral part of the Python standard library, Tkinter smoothly integrates with Python projects, requiring no extra installations, for a seamless GUI development experience. The official python tkinter documentation offers in-depth explanations of Tkinter modules, classes, and methods. &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.python.org/3/library/tkinter.html&quot;&gt;Visit Documentation&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;div-green&quot;&gt; &lt;span class=&quot;alert-header&quot;&gt;Tip:&lt;/span&gt; &lt;span class=&quot;alert-body&quot;&gt;As you embark on your GUI development journey, start by sketching out the design and functionality of your application before jumping into the code. This can save time and streamline your development process.&lt;/span&gt; &lt;/div&gt;
&lt;h2 id=&quot;exploring-basic-widgets&quot;&gt;Exploring Basic Widgets&lt;/h2&gt;
&lt;p&gt;We'll begin by exploring some fundamental components you can construct using Tkinter. The following snippets must be preceeded by:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import tkinter as tk
window = tk.Tk()
window.geometry(&quot;457x335&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and followed by:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;window.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;div-red&quot;&gt; &lt;span class=&quot;alert-header&quot;&gt;Caution:&lt;/span&gt; &lt;span class=&quot;alert-body&quot;&gt;When working with widgets, remember to assign a unique name to each widget variable to avoid confusion, especially when your application starts to grow.&lt;/span&gt; &lt;/div&gt;
&lt;h3 id=&quot;label&quot;&gt;&lt;strong&gt;Label&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Presents either text or an image, lacking interactive features for users. For more information on &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.python.org/3/library/tkinter.html#label-widget&quot;&gt;label&lt;/a&gt; refer official documentation of python.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;label = tk.Label(window, text=&quot;This is a Label&quot;)
label.pack()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=15447304320421249354&quot; alt=&quot;Label&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;button&quot;&gt;&lt;strong&gt;Button&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;An interactive component that executes a specific operation upon being clicked, like dispatching form data or activating a particular function.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def button_click():
    print(&quot;Button clicked&quot;)

button = tk.Button(window, text=&quot;Click here!&quot;, command=button_click)
button.pack()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=17225166072514634203&quot; alt=&quot;button&quot;&gt;&lt;/p&gt;
&lt;p&gt;After clicking the button:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=13782699684249808265&quot; alt=&quot;ButtonClicked&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;entry&quot;&gt;&lt;strong&gt;Entry&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;A text field where users can input text or numbers. This component is frequently utilized for entering information or executing search operations.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;entry = tk.Entry(window)
entry.pack()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=17774851110350440973&quot; alt=&quot;Entry&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;text&quot;&gt;&lt;strong&gt;Text&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;A text field that provides a multi-line text editing area for users to input or display text.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;text = tk.Text(window, height=5, width=30)
text.pack()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=8046130723076572707&quot; alt=&quot;text&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;scale&quot;&gt;&lt;strong&gt;Scale&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Enables users to choose a value from a spectrum by moving a slider control. It is useful for selecting values like volume, brightness, etc.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def update_value(value):
    label.config(text=f&quot;Volume Level: {value}&quot;)

scale = tk.Scale(window, from_=0, to=100, orient=tk.HORIZONTAL, command=update_value) #create a scale to select values from 0 to 100

scale.pack()
label = tk.Label(window, text=&quot;Volume Level: &quot;)
label.pack()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=18163681093508975768&quot; alt=&quot;Scale&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;spinbox&quot;&gt;&lt;strong&gt;Spinbox&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;A numerical input field with up and down arrow buttons for incrementing or decrementing the value.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;spinbox = tk.Spinbox(window, from_=0, to=10)
spinbox.pack()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=6830706622157727616&quot; alt=&quot;Spinbox&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;separator&quot;&gt;&lt;strong&gt;Separator&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;A horizontal or vertical line to visually separate sections or groups of widgets.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from tkinter import ttk #for themed widgets

label_1 = tk.Label(window, text=&quot;This is above the separator.&quot;)
label_1.pack()

separator = ttk.Separator(window, orient=tk.HORIZONTAL)
separator.pack(fill=tk.X)

label_2 = tk.Label(window, text=&quot;This is below the separator.&quot;)
label_2.pack()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=16298979966054953561&quot; alt=&quot;Separator&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;checkbutton&quot;&gt;&lt;strong&gt;Checkbutton&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;A checkbox enabling users to toggle between selecting and deselecting an option.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;check_var = tk.BooleanVar() #To keep track of the checkbox's state (checked or unchecked)
check_button = tk.Checkbutton(window, text=&quot;Checkbox&quot;, variable=check_var)
check_button.pack()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=9114035529065436677&quot; alt=&quot;Checkbutton&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;radiobutton&quot;&gt;&lt;strong&gt;Radiobutton&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;A group of radio buttons, ensuring that users can select only one option from a list of choices.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;radio_var = tk.StringVar() #To track which radio button is selected
radio_1 = tk.Radiobutton(window, text=&quot;Option 1&quot;, variable=radio_var, value=&quot;Option 1&quot;)
radio_2 = tk.Radiobutton(window, text=&quot;Option 2&quot;, variable=radio_var, value=&quot;Option 2&quot;)
radio_1.pack()
radio_2.pack()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=2612228386099183090&quot; alt=&quot;Radiobutton&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;listbox&quot;&gt;&lt;strong&gt;Listbox&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;A widget presenting a list of items, allowing users to select one or more options from the list.&lt;/p&gt;
&lt;h3 id=&quot;scrollbar&quot;&gt;&lt;strong&gt;Scrollbar&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Permits users to navigate through content surpassing the widget's visible boundaries, like in a text field or list.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;listbox = tk.Listbox(window)
scrollbar = tk.Scrollbar(window)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y) #scrollbar will be at the right side of the window and expands vertically
listbox.pack(side=tk.LEFT, fill=tk.BOTH) #listbox will be at the left side of the window and expands horizontally and vertically
listbox.config(yscrollcommand=scrollbar.set) #Attach scrollbar to listbox
scrollbar.config(command=listbox.yview)

for i in range(30):
    listbox.insert(tk.END, f&quot;Item {i+1}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=13063392361028412068&quot; alt=&quot;Listbox&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=489762057271802018&quot; alt=&quot;Scrollbar&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;colorchooser&quot;&gt;&lt;strong&gt;ColorChooser&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;A dialog for users to select a color. It provides a graphical interface for choosing colors using a palette or entering RGB values.&lt;/p&gt;
&lt;p&gt;from tkinter import colorchooser&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def choose_color():
    color = colorchooser.askcolor()
    print(&quot;Selected color:&quot;, color)

button = tk.Button(window, text=&quot;Choose Color&quot;, command=choose_color)
button.pack()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=8330473602676781386&quot; alt=&quot;ColorChooser&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;frame&quot;&gt;&lt;strong&gt;Frame&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;A container used to group and organize other widgets. It is frequently used for layout purposes, creating sections or divisions within a window.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;frame = tk.Frame(window, bg=&quot;lightblue&quot;, bd=5)
frame.pack()

label = tk.Label(frame, text=&quot;Inside the Frame&quot;, bg=&quot;lightblue&quot;)
label.pack()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=9882681441102070094&quot; alt=&quot;Frame&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;canvas&quot;&gt;&lt;strong&gt;Canvas&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Provides a drawing area for creating graphics, shapes, and custom elements. It is used for creating diagrams, charts, and interactive visualizations.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;canvas = tk.Canvas(window, width=200, height=100, bg=&quot;white&quot;)
canvas.pack()
canvas.create_rectangle(50, 25, 150, 75, fill=&quot;red&quot;) #draw a red-coloured rectangle
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=3077622398634848951&quot; alt=&quot;Canvas&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;panedwindow&quot;&gt;&lt;strong&gt;PanedWindow&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;A container for arranging multiple panes or frames with adjustable sizes. It allows users to resize panes by dragging the divider between them.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;paned_window = tk.PanedWindow(window, orient=tk.HORIZONTAL)
paned_window.pack(fill=tk.BOTH, expand=True) #will expand both horizontally and vertically
frame1 = tk.Frame(paned_window, width=228, height=200, background=&quot;red&quot;)
frame2 = tk.Frame(paned_window, width=228, height=200, background=&quot;blue&quot;)
paned_window.add(frame1)
paned_window.add(frame2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=11535792397148202103&quot; alt=&quot;PanedWindow1&quot;&gt;&lt;/p&gt;
&lt;p&gt;After dragging the divider to the left:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=11391496175646540405&quot; alt=&quot;PanedWindow2&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;menu&quot;&gt;&lt;strong&gt;Menu&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Creates a menu bar or dropdown menu with options and commands. It is commonly used to include navigation, settings and additional functionalities into the application's interface.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def print_something():
    print(&quot;You chose the first option&quot;)

menu_bar = tk.Menu(window)

file_menu = tk.Menu(menu_bar, tearoff=0)
file_menu.add_command(label=&quot;Print Something&quot;, command=print_something)
file_menu.add_separator()
file_menu.add_command(label=&quot;Exit&quot;, command=window.quit)
menu_bar.add_cascade(label=&quot;Click to see options&quot;, menu=file_menu)
window.config(menu=menu_bar)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=1591595257121153453&quot; alt=&quot;Menu&quot;&gt;&lt;/p&gt;
&lt;p&gt;After selecting &quot;Print Something&quot;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=5942132127197068255&quot; alt=&quot;MenuOutput&quot;&gt;&lt;/p&gt;
&lt;p&gt;These elements serve as fundamental components for Tkinter GUI applications, frequently employed across diverse projects to develop intuitive interfaces.&lt;/p&gt;
&lt;p&gt;To delve deeper into Tkinter and explore its capabilities, you can start with the following sections of the Python documentation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tkinter — Python interface to Tcl/Tk&lt;/strong&gt;: This section provides the foundational knowledge required to create GUI applications with Tkinter, including widget classes, event handling, and more &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.python.org/3/library/tkinter.html&quot;&gt;Python Tkinter Documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Graphical User Interfaces with Tk&lt;/strong&gt;: This part of the documentation discusses the integration of Tk/Tcl with Python, offering insights into the architecture of Tkinter applications, the Tkinter module, and thematic widget extensions &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.python.org/3/library/tk.html&quot;&gt;Python GUI with Tk&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tkinter.ttk — Tk themed widgets&lt;/strong&gt;: For those interested in using themed widgets for a more modern GUI look, this section details the thematic widget set provided by Tk 8.5 and integrated into Tkinter &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.python.org/3/library/tkinter.ttk.html&quot;&gt;Themed Widgets&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion-1&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Tkinter&lt;/strong&gt; is a versatile and user-friendly toolkit for &lt;strong&gt;Python GUI development&lt;/strong&gt;, offering a &lt;strong&gt;rich set of widgets&lt;/strong&gt;, &lt;strong&gt;cross-platform compatibility&lt;/strong&gt;, and &lt;strong&gt;seamless integration&lt;/strong&gt; with Python code. Despite its limitations, Tkinter remains a popular choice for developers looking for efficient creation of simple to moderately complex GUI applications.&lt;/p&gt;
</description>
<category>Project Dev</category>
<guid isPermaLink="true">https://coderlegion.com/190/tkinter-library-in-python</guid>
<pubDate>Tue, 12 Mar 2024 14:34:38 +0000</pubDate>
</item>
</channel>
</rss>