#383: Textinator and Building macOS Apps with Python Transcript
00:00 For all the amazing powers of Python, deploying packaged apps that leverage native OS-level
00:05 capabilities isn't really one of them, but it can be done. We have a great guest,
00:10 Rhett Turnbull, here to tell us how he built his distributable macOS app, Textinator,
00:16 that uses macOS's native vision recognition framework through Python.
00:20 This is Talk Python to Me, episode 338, recorded September 25th, 2022.
00:26 Welcome to Talk Python to Me, a weekly podcast on Python. This is your host, Michael Kennedy.
00:44 Follow me on Twitter where I'm @mkennedy and keep up with the show and listen to past episodes
00:49 at talkpython.fm and follow the show on Twitter via at Talk Python. We've started streaming most of
00:55 our episodes live on YouTube. Subscribe to our YouTube channel over at talkpython.fm
01:00 slash YouTube to get notified about upcoming shows and be part of that episode.
01:04 Rhett, welcome to Talk Python to Me.
01:07 Thanks, Michael. Glad to be here.
01:08 I'm glad to have you here. So many interesting things to cover on the show. I'm really,
01:13 really excited. Let's start with the excitement, I guess, of saying we're both excited about Mac
01:18 computers. It's about, I guess, a one-third split, maybe not quite that many for Mac developers in the
01:26 Python space. But there's a lot of people who write code either on a Mac or for people who use a Mac.
01:32 And it would be nice to make a little bit more of a native thing with Python. And so we're going to
01:36 talk about this really awesome app that you wrote. And here we are because of a tweet follow-up for the
01:44 Python Byte show. You sent a really cool tweet. And I'm like, this is amazing. We have to talk about
01:47 this. And so we're going to talk about building Mac apps with Python, like native apps that go into the
01:52 dock, not just something that could run a script, but a thing you could get a regular user to believe
01:58 as an app, and they will call it an app. And the other is you also happen to work for the U.S.
02:04 Space Force, which is very interesting. So we'll touch on that maybe a little bit at the end. There's
02:08 some programming tie-ins as well, right?
02:10 Yeah. Yeah, definitely. Sounds good.
02:12 Awesome. Well, I'm, again, happy to have you here. Before we get into that, though, let's start with
02:19 your story. How do you get into programming, Python?
02:21 I'm a hobbyist programmer, self-taunt, but I've been programming since I was eight years old. And
02:26 roughly around 1981, my dad brought home a TRS-80 Model 3 from Radio Shack. It was one of those
02:34 all-in-ones. It's a 12-inch black and white screen, built-in keyboard, and it had a five
02:38 and a quarter-inch floppy, which was a pretty big deal for those days.
02:41 That is a big deal. I was going to ask, did it have a cassette player or did it have a floppy?
02:45 It did not. It had the floppy actually built into the consoles and all-in-one.
02:49 People must have been jealous. You're like, where's your cassette player? No, no, let me show you.
02:53 It's this huge thing.
02:54 Yeah. And I don't remember how much it was, but it was the floppy disk held, but it was like 80K or
03:00 something. It was a lot of for back then. He had it for work, but I was, I just was fascinated by it.
03:05 I thought, you know, I love the idea that you can, this machine can respond to what I tell it to do
03:11 and do something completely different. And so I got into programming. He taught me a little bit of
03:16 basic. It came with basic, handy basic installed on there. I got into that a little bit. And then
03:21 from there, I moved into a Commodore 64 later and IBM PC. And then eventually found my way to the
03:28 Mac and just, yeah, I've been using the Mac for a long time. I found Python about four years ago.
03:34 I've used a number of different languages through the years. Probably most of the work I've done
03:38 was in Perl. A lot of Fortran in college. I'm an engineer by trade. And back then we used Fortran.
03:44 In fact, I programmed Fortran on the backs back in the day.
03:47 Cool. What type of engineering?
03:49 Astronautical engineering. So everything dealing with space, satellites, rockets,
03:55 spacecraft, all of this type of engineering, it sounds like. Yeah.
03:57 That was a lot of fun. But we did a lot of Fortran. But, you know, I taught myself Perl
04:01 along the way and I used that for years. That was sort of my go-to anytime I needed to get something
04:05 done. And then I remember a few years ago, I read an article about Python. It was the up and coming
04:11 thing. And so I thought, well, let me give this a try. So I started playing around with it and I've
04:16 done almost exclusively Python since then. It's been a lot of fun.
04:19 Well, so once you found it, you're like, all right, this is a, this place, I feel comfortable in
04:23 this place and this language and I want to do more and just stuck with it.
04:26 Yeah. It's an easy language to learn, right? But it grows with you is what I found with Python.
04:32 And, you know, you can obviously build industrial grade apps with Python, but it's really easy to
04:38 start out too. And I will say, I just, I actually first came across Python, oh, quite a few years ago
04:44 in the late nineties when I discovered Perl. And I'd come from a C and Fortran background. And I remember
04:50 looking at this whole white space thing and thinking, there's no way that I can program with
04:54 that. I need the curly braces. I need the semicolon. And now, you know, to be fair back then, you know,
05:00 on a windows PC programming, it was literally with notepad. There wasn't black, there weren't Pepe,
05:06 you know, there weren't any formatters, any of that kind of stuff. Right. So, so white space was hard
05:10 actually getting it right. But, you know, once I got over that, I actually really liked the white
05:15 space. Now I think it makes the code a lot easier to read and cleaner. And, I,
05:19 yeah, I really have enjoyed working with Python. Yeah. The white space, I do feel like there's a
05:24 certain number of people that just look at it and go, nope, next language. What else am I going to,
05:28 what else might I learn today? Cause that one's out, but it really is something that it seems like
05:33 it's a big deal. And then you kind of work with it. You're like, oh, especially now the tools are just
05:38 auto indenting for you, you know, you're like, oh, this is kind of, it's just omnipresent, but it's,
05:44 you don't have to think about it so much. Exactly.
05:46 Cool. And you hit enter and like, oh, I'm indented already. That's cool. Just keep typing,
05:49 you know? So very neat. How about now? What are you doing these days? I mentioned something about
05:53 the space force. Yeah. So I work for the space force. I'm an off, actually an officer in the
05:57 space force. I've been a career air force officer. I've been in the air force since 1995 as an
06:03 astronomical engineer. And then last year had an opportunity to transfer from the air force into
06:08 the space force and being an astronomical engineer and a guy who's worked in space my whole
06:12 career, that was a no brainer. It's like, yeah, it seems like exactly.
06:15 That's exactly where I, where I should be. And so today I'm an officer in the space force
06:21 and my day job is I'm a chief engineer for an organization called space systems command,
06:26 which is part of the space force that develops and acquires all of our new satellite systems,
06:31 rocket systems, ground systems, all of our networks and infrastructure, that kind of thing. So
06:35 that's a lot of fun. I get to do a lot of fun engineering in my day job and do get to do Python
06:41 on the weekend. Yeah. Do you get to run any Python on any of the satellites? I know that some of the
06:46 satellites are controlled with Python or API, not necessarily for the space force, but you know,
06:52 like some of the scientific ones. Yeah. So none of the one systems that I've used are using Python
06:56 to actually directly interact with the satellite, but we are using Python and a number of different
07:01 places. Some of the apps that we run that help process data or analyze display data to those kinds
07:08 of things. But the data science dashboard thing that happens to data after it gets out of space.
07:14 Yeah, exactly.
07:14 Comes back. Yeah. Well, well, that sounds like a super, super fun job. And I was talking to somebody
07:20 get together just earlier this week. Everyone's kind of fascinated that I can have a job that is
07:26 podcasting. They're like, okay, well, who are you interviewing this week? Well, I'm going to interview
07:29 the guy who does cool Python stuff and he works at the space force. And someone else is like, oh,
07:35 that's amazing. And then someone else is, what's the space force? So maybe for those of us who don't
07:40 know, we know what the air force is. What's the space force? Yeah, sure. The space force is the
07:44 newest of the military services. We were established just in December of 2019 as, and sort of birthed out
07:52 of the air force. And so the initial people that came over to space force came from the air force,
07:58 although we've now brought in people from all the other services, army, Marines, Navy, to be part of
08:04 space force, people who transfer just like I did. And the job of the space force is to organize, train,
08:10 and equip guardians is what we call people in the space force to do space operations for the nation.
08:16 And so if you, you know, things like GPS, for example, which I know everybody's familiar with,
08:20 if you pull out your phone and use Waze or a maps app, or even do baking, which all relies on GPS timing
08:27 signals, those kinds of things. We've even used GPS in a corn maze recently. Yeah. Yeah. So all,
08:31 all of that is coming from a signal from a satellite in space that is run by the space force. I mean,
08:38 we do a lot of other things to weather missile warning communications, the remote sensing,
08:43 all sorts of different missions that the space force does, but we do all of the space systems
08:49 that support the rest of the services, you know, communications, navigation, location, all those
08:55 kinds of things. Sounds fascinating. And I suspect that many of these things were happening often under
09:00 the air force banner or some other, other banner, right? Yeah. And now it's just more formalized,
09:05 right? Yeah, exactly. So the air force has been doing this mission for a long time. In fact,
09:10 the air force originally designed and launched the GPS constellation that is still in operations today
09:15 and is now run by the space force. But what we found was that space was becoming more and more
09:21 important. Certainly everybody's familiar with what SpaceX and a lot of the other commercial space
09:26 companies are doing a lot of really exciting things going on, but it's a lot more happening in space.
09:30 The importance of space to our economy and our nation is increasing. And quite frankly,
09:35 the threat to all of that is increasing. A number of other countries have paid a lot of attention to
09:40 what we're doing in space and, and they are actively working on ways to deny the U.S. the ability to use
09:47 some of those capabilities if they want to. We've seen some of that play out in, you know, things going
09:52 on in Europe right now, in Ukraine, for example. So because of the increased threat and the increase of
09:59 importance of space to our economy and our, and our nation, we decided that we really wanted a team
10:04 that was focused only on space. And that's all we did because for the air force, it was always,
10:09 you know, an extra job in addition to everything else that they're doing. And so this, the only job
10:14 space force has is doing space operations for the country. And so we have guardians, as I mentioned,
10:21 focused on doing that job day in and day out.
10:23 Well, very cool. It strikes me that this is one of the areas of the defense and military industry
10:30 that is the least hands-on. It's, you know, the most, you almost never get to interact with,
10:37 you know, things in space directly, right? Even with cameras necessarily, right? It's a little bit
10:42 different than running over a hill or something.
10:44 Well, it is different. And, you know, let me, I, first, I just want to add that, that anything I,
10:49 I'm excited about space force and happy to talk about it, but anything I say is my opinion
10:53 and not necessarily the opinion of the space force, the department of defense.
10:57 Absolutely. Yeah. Thanks for saying that.
10:58 Yeah. But I will tell you, you know, you're right, Michael, that we, people that interact
11:03 with the space domain as part of the space force, except for the very small number of astronauts
11:07 we have that work with NASA, largely do that through a computer screen. We do that through a piece
11:13 of glass, a keyboard and a network. And that makes the space force different than the other
11:19 services as you, as you alluded to. But that also means that we really want a workforce that's really
11:25 understands that, right. That understands the technical domain. That's really digitally fluent.
11:31 Space force is really focused on building digital service and having a digitally native workforce that
11:36 really understands computers, understands coding, understands cloud infrastructures, and, you know,
11:42 those kinds of things, all the underlying technologies that we need to do our job.
11:46 We're really focused on building a workforce that understands those things.
11:50 Yeah. That's what I was hinting at is I feel like a lot of the coding skills are somewhat in the same
11:56 realm as the kind of things that you know, you all need to do. Probably just a ton of custom software as
12:02 well. Yeah, definitely. We use a lot of commercial software, but we also do a lot of our own custom
12:07 software. We have a couple of software factories where we've got people from the space force guardians
12:12 who actually write code for other guardians to use as part of their day job.
12:16 Nice. Well, if we have some extra time at the end, we'll come back to that. But let's switch over to
12:21 our main topic, I guess. And let's start with the genesis of this idea. And you created this project
12:29 called Textinator. And it's open source and Python, obviously, which is why we're talking about it.
12:35 And I'll link to the GitHub repo. And it says that the about for it says simple macOS status bar menu
12:42 bar app to automatically detect text and screenshots, which is pretty awesome. So yeah, it's one of those
12:49 apps. There's just an icon in the upper right by by the clock would be in the task bar, a little hidden
12:55 down there if it was a Windows app, but it's a Mac app. So that's it lives up in the menu bar alone.
13:01 And what does it do?
13:02 So it's really simple. If you take a screenshot, if Textinator is running, and you take a screenshot,
13:07 it will search that screenshot for any text that it finds in the screenshot, and copy that text to
13:12 the clipboard. That's all it does. Super simple. But you know, a useful thing if you've got, you know,
13:17 trying to take a screenshot, there's text on the screen, like on a link on a video or something,
13:22 and you can't copy and paste it. All you got to do is screenshot it and Textinator will pick it up and
13:27 stick it on your clipboard. That's fantastic. Does it automatically capture that? Is it just
13:31 constantly looking for screenshots and then clipboard, you know, doing text recognition and then clipboarding
13:37 that? Or do you have to trigger it?
13:39 No, it'll do it all automatically. And what it's doing is it's using this Mac built in spotlight
13:45 feature behind the scenes. And basically, it sets up a query that's running in the background all the
13:51 time. And the query says, anytime there's a new screenshot, send me an alert. And then so when
13:57 it gets that alert from the operating system that there's a new screenshot, it then it does the
14:02 processing and copies everything to the clipboard. Otherwise, it's just sitting there waiting for an
14:05 alert to pop up.
14:06 Yeah, fantastic. I said this show sort of originated from a tweet. So just as a user, I talked about this
14:13 app called Text Sniper, which I thought was pretty cool in the Mac App Store that lets you kind of
14:20 screenshot an area of text and will capture it. It costs money and you got to get it at the App Store
14:26 and so on. And you said in your tweet said, that's a really cool idea. And it inspired me to create
14:32 Textinator. I love the name as well. And I just think it's fascinating. You were able to knock this
14:37 out pretty quickly in Python.
14:39 Yeah, the initial, it's grown a little bit because I've tried to put a few bells and whistles on it.
14:44 But the initial, I knocked it out on a Saturday afternoon, the initial capability. And it was
14:49 300 lines of code. It's fairly simple. And it was fairly simple because I don't have to do all the
14:55 hard work. I'm calling Mac framework API calls that are built into the Mac to do the hard work of,
15:03 hey, find the screenshot, detect the text, put the text on the clipboard, those kinds of things.
15:08 And so it was actually fairly easy to throw together. And then I'm using another Python
15:13 package called Rumps that does all the heavy lifting of actually, hey, putting an app into the menu bar
15:19 with an icon and all those kinds of things.
15:22 Rumps is fantastic. It has such a ridiculous name, but it's so easy to build Mac apps with.
15:28 Yeah, I've used it for quite a few different things. I've got a couple apps running right
15:34 now on my Mac that I use every day that I built out of Python and Rumps. It's a really useful tool.
15:39 That's cool. I'm starting to think of more that maybe I should create, but I have one as well.
15:43 And maybe I'll talk about it a bit when we compare notes on yours. But yeah, super cool. This is so
15:50 useful. You know, there's all these different times you're like, I really just want that text.
15:55 And in macOS, you hit command shift four and you can select just a region of the screen. You don't
16:01 have to screenshot the whole screen, which you'd get like, you know, the menu bar and like the
16:04 navigation, like all you can just say, I want this section and it will grab that and then turn it
16:10 into text, which is just amazing. I've used it for watching a video course or some kind of
16:16 presentation. I'm like, I want that URL. They said, go to the huge long URL, just select snap,
16:22 paste into, you know, paste the text of the URL and go, right?
16:26 Yeah, exactly. It's so useful for those kinds of things. And, you know, I do want to add,
16:32 this was inspired by, you mentioning text sniper, I think on the, on the Python bytes podcast,
16:37 right? And text sniper is a great app. It's, you know, it's only like 10 bucks. So I mean,
16:41 you should definitely check it out. But when you mentioned that I had already been working with
16:46 playing around with the vision framework, which is the max ML powered computer vision framework,
16:52 right? Yeah. Yeah. And so I'd been playing around with that and I knew that it would,
16:56 it was fairly easy to do this optical character recognition tech, you know, and grab the text from
17:01 a picture. So I thought, well, I bet you, I could recreate that. And I did play around a little bit
17:06 to try to be able to actually draw the crosshairs on the screen myself and, and let you do that.
17:10 And in Python, you know, Python, that was actually kind of hard to do, but then, so then it dawned on
17:16 me, well, the Mac already comes with the ability to do that, the screenshot app. Then I just had to
17:21 figure out, okay, how do I grab that screenshot when it happens? And it turns out, you know,
17:25 there was a screenshot and then how do you get the tech? Yeah. And it turns out that that's super easy
17:29 to do. If you tell the Mac, tell me when there's a new screenshot, you set up a query that basically
17:36 runs just like you would, if you use spotlight on the Mac and it runs in the background,
17:40 anytime there's some new thing that matches your query, it lets you know, and then it runs your
17:46 code. Yeah. And that's amazing how easy it was to knock out the vision framework and some of the ML
17:51 tools. They're probably using the neural engine that's built into the latest chips as well.
17:56 They're probably taking advantage of that. Yeah, I'm sure they are. I've got an older MacBook and
18:01 they run fine on there too, but they're, they're using the GPU on that. So that uses whatever hardware
18:06 you have, but I'm sure if you've got an M1 more, you know, one of the newer Macs that's using the
18:10 neural engine and it's really surprising how accurate it is and how fast it is that they've
18:17 done a really good job with that. Yeah. As we look at this, we'll also see, you know,
18:21 people talk about, well, Python's slow. So how could you use Python to do this thing? I mean,
18:26 I think that's always a funny statement to say Python is slow because it's, it's slow until it's as fast
18:31 as C or as fast as fast. You know, like it lives in those weird bimodal worlds, right? It's like
18:38 really slow. They all of a sudden, oh yeah, just as fast. Yeah. All right. Yeah. I, so me, you know,
18:42 I've never found Python to be slow, but I guess it depends what you're doing with it. I'm not doing
18:47 really big, heavy data crunching, but you know, just as in this example, it's a Python app that's
18:52 doing the text detection, but the text detection is not running in Python. That's running in objective C or
18:59 Swift or whatever native language that Apple wrote it in. And we're just calling that from Python.
19:04 So the only slow part of that is actually setting up that call to translate between Python types and
19:10 objective C types and go back and forth. And fortunately, you know, there's a really great bridge for it
19:15 called PyOBJC, which is short for PyObjective C. Objective C is one of the languages that has been
19:22 around the Mac for a long time that Apple has used long time, though they're migrating a lot more to
19:26 Swift now, but PyOBJC does all that sort of translation across the bridge between Python
19:32 and objective C. So you don't, for the most part, don't really even really have to think about it.
19:37 Yeah, exactly. So what you did is you basically saw there was a screenshot and you say, you know,
19:42 send that information off to the objective C platform layer. And it just goes natively and
19:47 does its thing, which is, it's a pretty interesting bit of coordination. You know, you've got rumps
19:52 coordinating, working with the menu bar API and you've got these various low level OS platforms that you're
19:59 talking to through high objective C and yeah, well, we'll see how you built it up. But it's there's a lot of
20:04 neat moving pieces there.
20:14 So what's the point of view is that users may be encountering errors, slowdowns or crashes with your app right now. Would you even know it until they sent you that support email? How much better would it be to have the error or performance details immediately sent to you, including the call stack and values of local variables and the active user recorded in the report? With Sentry, this is not only possible, it's simple. In fact, we use Sentry on all the talk Python web properties. We've actually fixed a bug triggered by a user and had the upgrade.
20:44 ready to roll out as we got the support email. That was a great email to write back. Hey, we already saw your error and have already rolled out the fix. Imagine their surprise. Surprise and delight your users.
20:55 Create your Sentry account at talkpython.fm/sentry. And if you sign up with the code talkpython, all one word, it's good for two free months of Sentry's business plan, which will give you up to 20 times as many monthly events as well as other features. Create better software, delight your users and support the podcast. Visit talkpython.fm/sentry and use the coupon code talkpython.
21:22 Let's start with Python on the Mac because I know you have some opinions on how to get started there.
21:28 Yeah. It's a good experience on the Mac, but it does take a little bit of finagling, I think, to get Python right. And the first thing you got to start with is, hey, which Python do I put on? How do I install it? And there's a number of different ways. There's a thing called Homebrew for the Mac, which is a package manager that installs apps and command line tools on the Mac. And that's great.
21:49 If you need any kind of command line tool you need in the terminal, Homebrew can install it pretty fast.
21:55 And so what I've seen is a lot of people are tempted just to install Python that way. Unfortunately, that can run into a number of different problems because Homebrew also uses that same Python to manage a lot of the other packages and things that it's installing.
22:09 And so it might change that Python, update it, change something, and you don't realize it. And then now all of your stuff stops working.
22:16 So after a few false starts with that, I decided just don't use the Homebrew Python for the Mac. And what I recommend for most people is just download it from python.org and you get the Mac installer, install it and run it.
22:29 Because I develop a bunch of different open source packages and want to test them on different versions of Python, I build my own Python and I use PyE and V to manage that. And that way I can run a number of different versions of it.
22:41 Anaconda is also another great one for the Mac. But you really need to understand sort of the ins and outs of which Python you picked and what it's good for.
22:50 Yeah. There's an article that you linked to talking about this as Homebrew Python is not for you. We can do you as a developer.
22:56 This was by Justin Mayer. It's interesting that basically the fundamental problem that he talks about is that Homebrew might change the underlying Python version, even if you don't ask it to, because you might brew install glances or some other thing that needs Python.
23:13 And then it goes, well, great, we got a good new Python for that. Right. And that could potentially break your virtual environments.
23:19 Which is not amazing. Yeah. So yeah. Anyway, people can check this out and see what they think about this.
23:25 But yeah, I would. Yeah, I recommend that you don't use Homebrew for developing. Right. Homebrew is great for everything. I use it every day.
23:32 But yeah, if you want to develop Python, get your own Python that that is only going to get updated if you decide to update it.
23:39 Yeah, that makes a lot of sense.
23:40 You know, for me, I'm, I'm okay. If, if my virtual environment change, I have a whole bunch of hotkeys and shortcuts and aliases that I can just recreate, reset up those things.
23:52 So if something changes, I'll just drop into the terminal and blow it away and recreate it.
23:57 But that's because I've been doing it for a long time.
23:59 I know a lot of people, whether or not a virtual environment is active, where is Python? What is installed?
24:04 All those things become very frustrating to people.
24:07 Yeah. So this could help potentially avoid those, those troubles.
24:10 Out of curiosity, I did a brew list. See what I got installed here. Probably got about 120 different things installed. So yeah, like Richie in the audience, which says a brew is my go-to. I'm there. I'm there with it as well.
24:25 So yeah, the article, by the way, mentions this thing, which I have not used ASDF, which is, I don't even know what to make of the name. I mean, it's just like, I just, just hit left fingers, just go down and this is my app, right?
24:40 But apparently this is about installing, you can use this to install different tools and frameworks. Basically it's for CLI for managing different runtimes of Ruby and Perl. And there's a plugin for Python. Have you played with this?
24:56 I have not. It looks really cool, but I've got my Mac to the point where it works and I just don't mess with it.
25:02 Mine is so janky, honestly, just as a sidebar talking about this is I got this, this is a Mac mini M1 and I got it in, or I ordered it right when it came out basically or a week after. And so I got it right away. And many of the things that you would pip install or certainly brew install, it would say, well, we don't have that for your platform.
25:22 It took about a month before I could reliably pip install everything I needed because the wheel didn't exist for, you know, the ARM 64 of whatever. And so I've got the Intel version of brew put on here. And then I've got the C++ tools for Apple Silicon.
25:40 And so when I try to do certain things that drop down to the compiler, like high MV, which I've tried and I can't get it to work. There's some mispatch about like the compiler flags and what platform it thinks it's on. I'm, I'm tempted to just reformat the whole computer now that everything's stabilized, but I am waiting until October because if they ship a new, much, much better one, I might not even go through that process.
26:03 But yeah, I would love to use pie MV. I haven't been able to get it to work. Maybe ASDF is the thing to do. I'll give it a try.
26:10 Yeah. Might be worth trying.
26:11 Certainly worth trying. I would say now that's great for creating and getting your Python there.
26:18 And one of the first things I guess that came to mind when I thought about what your app does, I'm like, oh, oh, this is going to be really tricky because I can get say a little rumps app to run.
26:28 But when you really get down to it, there's a lot of things about, is it from a signed developer? You know, what permissions does it request? Like, does it request the, the be a server or access your
26:40 file system or, you know, look at the clipboard? All these different things can get a little bit tricky. You want to talk about this permissions a bit?
26:48 Yeah, sure. And that's a tricky thing for every back app, whether it's in Python or not. Right. And so there's a, I guess, you know, there's a good thing and a bad thing there. From a user's perspective, Apple's done a really good job locking down the computer in privacy. Make sure that apps cannot access your data unless you explicitly allow them to access your data. From a developer's perspective, that means apps can't access your data unless you explicitly allow it. So you've got to keep that in mind as a developer that you've got to make sure that your app has the right to access your data.
27:18 And so there's a couple of different ways to do that. Every app, a .app bundle that ships on the Mac has an info.plist, a property list file that's basically just a dictionary of key values.
27:31 And that tell the app, you know, various settings for the app. A couple of those things are permissions or entitlements that you need to request and say, hey, I want my app to be able to do this. For example, for Textinator, one of the things it does is request access to the desktop. And it'll pop up and then you can grant access to the desktop. But until then, it won't be able to see the desktop.
27:53 Presumably because the default behavior of taking a screenshot is to drop the file onto the desktop. So you need to be able to go hunt for those files, right?
28:01 Yeah. In fact, when I first got the first version of Textinator working, it wasn't doing that properly. And so I was taking screenshots and it never found them. And because it just couldn't see the disk, that the screenshots were happening.
28:14 But the whole query system is smart enough to say, hey, you don't have permission to see these screenshots. So I'm not even going to tell you about them. So it was never even getting alerted that it was getting a screenshot.
28:24 So I had to get that sorted out. And then if you can change the default location of a screenshot and put it somewhere else. And if you do that, you actually have to go into the system settings, the privacy settings, and actually give Textinator full disk access so that it can see where those screenshots are, you know, if they're outside of your home directory.
28:43 Yeah, there is a screenshot application. And if you run it, basically, that's like your preferences. Or, you know, it's not just a hockey, but you can go and change your preferences for the screenshots.
28:52 Yeah, there's a property list setting, you can do it from the command line as well. But yeah, the screenshot app is the easiest way to do that.
28:59 Yeah. Okay, so basically, you go through the info.plist, and you just express, I need access to the disk? Or do you try to get there? And then macOS says, hey, this thing is trying to do it?
29:11 Yeah. So what I did in Textinator is one, in the info.plist you put in, what you actually put in there is a, the message that gets shown to the user when they actually try to access the desktop or whatever it is that you're trying to do.
29:24 Right. And so what I have Textinator do is when it first fires up, it tries to access the desktop to force that message to pop up so that you grant right away. Otherwise, you'll never see the screen, you'll never see the screenshots. So that's an easy way to just, you know, to force the user that way. So as they start it, they've already got their attention. It'll pop up a dialog box and say, hey, Textinator wants to access the desktop. Allow. And then now it has access.
29:47 I'm always suspicious of apps that pop up these little, you know, enter your admin password. Yeah. For whatever reason. But they do it just out of the blue. You know, it's one thing, like if I launch a new app and it says, there's an update. Do you want to update? You say yes.
30:02 Yes.
30:02 And it'll pop up. You're like, yep, this is trying to update itself. But if, you know, it's running and it just behind the scenes doesn't update or something.
30:09 Yeah.
30:09 It's like, oh, we need your password. You're like, yeah, no.
30:12 Yeah, I know what's going on here. So I think anyway, the reason I bring this up is I think it's a good idea to like, as they're interacting with it, just get those out of the way straight away.
30:20 Yeah, exactly. Otherwise, you're right. Well, Textinator once, you know, if it pops up later on and you've forgotten all about it, it does definitely look suspicious.
30:28 Yeah. Well, I'm a little unsure. I should have gone and installed this. But no, like if it's a timely one, it's okay.
30:37 Well, you know, the other thing about installing apps is that they have to be signed. And you get this really scary when you try to open it, you'll get a really scary warning saying, hey, this is signed by an unknown developer. It'll destroy your computer. Don't open. Right.
30:51 Kind of thing. And you actually have to right click and say open and and then say, yes, I really want to open this thing. And you only have to do that once. But the first time you do it, it does give you kind of the scary warning. So you got you do need to know where is this code coming from that I'm going to try to run.
31:05 So you build it with the ultimate. You built the app bundle with Py2 app, right?
31:10 Yes, that's right.
31:11 Yeah. I wonder if there's a way to sign if you have a developer account, if there's a way to sign.
31:16 Oh, yeah, I think you could. I don't have an Apple developer account. That's something you have to pay for. So I just then I just tinker around. So I don't haven't bothered with that. So it's mine is self signed. But yes, you can. Or you could do it after the fact. You could resign the app using the Apple signing tools, which you can run from the command line and then resign the app with your developer signature. And you could even do that and notarize it as well.
31:39 Yeah, then it would it would just run with no just right. Yeah, yeah, because sometimes depending on how you run it, it won't give you the option to run anyway. Like if you click if it's in the little download section, and you just pop up that little fan and you click it sometimes it'll just go this is from an unknown developer, we won't run it. But if you right click and say run, it'll say Oh, are you sure you want to run? Yes. You know? Yeah. Yeah. So it's, it can be annoying. It can be a bit of a roadblock. But yeah, yeah.
32:05 But yeah, but that's something to keep in mind if you, you know, a lot of things that Python developers do sometimes I know I do this is, is whip out a little tool to help a family member or friend or something, right? And so if you're going to send them that something to run, you've got to make sure you walk them through, okay, you've got to right click, and then say open, and then click allow access to the desktop or whatever it is that you're trying to do.
32:27 Exactly. Or if you're building this app for your club or your kids football team or soccer team or whatever, right, then you're handing it off to people who don't necessarily know that it's trustworthy or how to make it go. But you kind of got to talk them through it. It's probably if you have a developer account, it probably would be cool to do. I say that as somebody who has an Apple developer account and a rumps app, and it's not signed. So they should probably figure it out.
32:54 But in theory, I think it would be cool. I'm intrigued on how I might go about doing that now.
32:58 Yeah, I don't think it's that hard. I think you can just run the signing tools and add your signature on there.
33:04 Yeah. Well, it just has to be trusted from something that Apple trusts, right?
33:09 Yeah.
33:09 So let's talk about Py2App real quick. So to me, there's a bit of a danger that I might get into some ranty sidetracks here on this show. But to me, I think it's a really a big hole in Python's capabilities. Let's put it that way.
33:23 That there's not a way to say, press a button or run a command. And then I have a binary thing I give to people that from their perspective, is an application with an icon that goes into their start menu or their dock, that they click it and it comes out.
33:40 It does things. Or they even just run the command line and it does things, right?
33:43 The ability to send somebody an application that was created with Python is super lacking.
33:51 If they're not a developer on the other end receiving it or not a server admin receiving it.
33:56 Yeah, that's very true.
33:58 I'll add one caveat to that, Michael. If it's a command line tool, what I found is I've defaulted now just to use PipX.
34:05 Yes.
34:05 And they brew install PipX and then they can PipX install and they're good to go.
34:10 Yeah.
34:10 And it's super easy and it just works. And then inside the terminal, all your permissions are owned by the terminal.
34:17 So once you've granted the terminal access to your desktop or your photos or whatever, any Python app you run will have access, no problem.
34:24 That's an absolutely good point.
34:26 But for a GUI app, yeah, it's just way harder than it ought to be.
34:29 It is way harder than it ought to be. And I would love to see something built into Python.
34:33 I mean, right now, it seems like the big focus of so many of the people doing high-end work on Python is about making it faster, do more, take more advantage of hardware.
34:44 And I would not say sidetrack that work, but once that's kind of done, the low-hanging fruit is how do I get a shippable thing?
34:54 Because after that, you know, you start to open up things like, well, if I could send out a binary, maybe I could send out a binary for iOS or Android.
35:01 Exactly.
35:01 And then all of a sudden we have mobile and like, it just, it unlocks so much.
35:05 Talk Python to Me is partially supported by our training courses.
35:11 Do you want to learn Python, but you can't bear to subscribe to yet another service?
35:15 At Talk Python Training, we hate subscriptions too.
35:19 That's why our course bundle gives you full access to the entire library of courses for just one fair price.
35:24 That's right.
35:25 With the Everything Bundle, you save over 80% off the full price of our courses and you own them all forever.
35:31 That includes the courses published at the time of purchase, as well as courses released within about a year after the bundle.
35:38 That said, what we have now are a couple of programs and tools that will bundle up the Python runtime.
35:51 We're just going to scratch the surface.
35:53 There's a bunch of attempts on this, like PyOxidizer, which we won't touch on.
35:57 But if you're building a Mac app, probably the way to go is Py2 app because it's specifically about making macOS apps, right?
36:06 Yeah.
36:06 I've used PyInstaller for command line tools.
36:10 It'll bundle up.
36:12 Basically, it's a zip file with all your Python code in it that unzips a runtime and runs.
36:17 And then Py2 app for GUI apps.
36:19 There's another one called Beware, the Beware project that's really trying to solve that problem of, hey, I want to be able to run on Android and iOS and macOS and Windows.
36:29 I've tried it a couple of times.
36:31 It looks really promising, but I keep running into problems.
36:33 And I go back to Py2 app because I know how to make it work.
36:37 Yeah.
36:37 The briefcase project from, I think is what it's called.
36:40 Yeah.
36:41 It's very interesting.
36:41 But I'm holding out hope that they'll be able to solve some of these things.
36:45 They seem to be doing a lot of work on it.
36:47 But Py2 app, well, you run it.
36:49 And it will create basically an app bundle, which is how apps are distributed on your Mac.
36:54 One thing you'll notice if you go to the Textinator repo is that there's a setup.py.
37:00 And on all my other projects, I've switched to poetry and pyproject.toml.
37:04 Okay.
37:05 But I have not figured out how to make Py2 app work with that.
37:09 So Py2 app needs a setup.py.
37:11 And so I use the setup.py anytime I have to use Py2 app.
37:15 Okay.
37:16 How cool.
37:17 I'm looking at your setup.py on the Textinator repo.
37:21 You've got this options here.
37:23 And one of the things it has is icons, the various icons, which for your app, you actually need
37:29 all these different sizes of icons, which is kind of a bit of a hassle.
37:33 But then you give that file and it makes that the right, it does the right thing with it.
37:37 And then you also have a plist, which has things like lsuielement is true or ns, these names,
37:44 ns desktop folder usage description.
37:47 That's what you're talking about, right?
37:48 Yeah.
37:49 Those things in the plist settings there are the things that are going to go into that info.plist
37:53 for the app.
37:54 And Py2 app will stick them in there.
37:56 The lsuielement basically says, I want to run headless without being in the doc.
38:01 And so that's how the rumps apps works.
38:04 You'll get the little icon up in the status bar, but you won't see it in the doc.
38:08 And then the desktop folder usage description is that's the little message that will pop up
38:13 when Textinator tries to access the desktop the first time.
38:16 Right.
38:17 Okay.
38:17 So this is the way, one of the ways in which you configure the actual creation of the Mac
38:22 side of things, not just the bundling of the Python, right?
38:25 Exactly.
38:26 So that stuff, you know, those settings there will actually go into the app and Py2 app
38:30 will stick them in there.
38:31 Cool.
38:31 I just want to give a quick shout out to this app here as well.
38:36 I've had to create these icon sets for different apps and there's this thing called the icon
38:41 set studio and you just give it one icon and it will, it doesn't have very good reviews,
38:45 but it worked well for me.
38:46 Yeah.
38:47 Okay.
38:47 Good.
38:47 Yeah.
38:48 So, you know, there's that, but it, you basically give it one large icon and it'll create all
38:53 the different variations and one of those icon files and stuff for you.
38:56 So anyway, that's, that's a pretty cool one.
38:58 All right.
38:58 So Py2 app.
39:00 Yeah.
39:01 And then you just give it a command line to build your application, right?
39:06 Yeah.
39:06 You run Py2 app and it will create, it'll take, read in your setup.py, read in your, all the
39:13 files associated with your app and then bundle it up into the app file.
39:17 Yeah.
39:18 It takes a little bit of time and then there's a build and a dist folder and miraculously,
39:22 it's really delightful actually.
39:24 In the dist folder, I think it is, there's a, whatever you called your application.app.
39:31 It has the icon.
39:32 It looks like an app thing.
39:33 You can drag it to your applications folder.
39:36 You can put it in the doc, you know, if it's that kind of app or for the rumps one, I'm not
39:41 sure.
39:41 It makes a lot of sense to actually put it in the doc, but you know, it's as much an app
39:45 as far as macOS is concerned as all the other ones, right?
39:47 Exactly.
39:48 Yeah.
39:48 It's a native app and they'll run.
39:50 And I, yeah, for something like Textinator, I just put it in the applications folder and
39:54 then add it to the startup items or my login items so that it just starts running when I
40:00 log into the computer.
40:01 One thing to keep in mind is you might be tempted.
40:03 You get that first time you build it, that app in your dist folder.
40:07 If you run that, at least for Textinator, it won't work because it, that version of the
40:12 app won't have the permissions to see your desktop.
40:15 So, because it's got it, I guess the privacy settings are going to look at, hey, which app
40:21 and then which signature was signed?
40:23 And it's going to look for, hey, that version of it's got permission.
40:26 That also means that if you upgrade Textinator, you may have to go back into your settings
40:31 and give it permission again.
40:33 Okay.
40:34 Yeah.
40:34 Permissions are always tricky, but very cool.
40:38 My experience is this has worked and it's been really nice.
40:41 You can build a little .app files and it's like those smart bundles, right?
40:47 They're really folders, but they look like a single file.
40:49 Yeah.
40:50 It's a great way to handle.
40:51 I do.
40:51 I don't know if I agreed sufficiently enthusiastically with you, but PIP,
40:56 PIPX is absolutely the way to hand out command line tools these days.
41:01 People can get PIPX by just reinstalling it, which brings in Python that PIPX needs.
41:06 So that already is set.
41:08 Then if you just type PIPX, like for example, PIPX install PLS, right?
41:14 For example, that's a really nice one.
41:16 It's an LS replacement.
41:18 I think I've talked about it before, but it's like sort of a developer focused LS type
41:23 of experience.
41:23 And you can get like little icons like for readmes or license files or whatever.
41:28 But if you want this, you could go through the way of setting up and running on Python,
41:33 or you could just PIPX install PLS or glances or HTTP, HTTP, HTTP, IE, HTTP, right?
41:40 These, all of those things should just come in with PIPX.
41:43 They get automatically upgraded if you ask for it.
41:45 There's a lot of good stuff going on there.
41:47 But if you need to hand out an executable thing for Mac people, this Pi 2 app is really nice.
41:54 Yeah, it works great.
41:55 And it's, yeah, there's a few quirks to get it set up.
41:59 Like you have to use the setup.py and you have to make sure you get the right things in
42:02 that PLIST dictionary.
42:04 But once you figure those out, it's super easy.
42:07 It's also, I guess, worth pointing out, there's a Pi 2.exe for doing this for Windows folks,
42:13 right?
42:14 Windows people are like, well, the Pi 2 app doesn't do me any good.
42:17 But there's a Pi 2.exe.
42:19 And maybe more broadly, there's Pi Installer.
42:22 And honestly, I don't have enough experience between Pi 2.exe versus Pi Installer, which also
42:27 make Windows apps to say which you should choose.
42:30 But Pi Installer has been pretty solid too.
42:32 Yeah, I use Pi Installer for a different project of mine that's a command line tool.
42:36 And it works great.
42:38 I mean, I've got to build it.
42:39 That one, I think that it doesn't, Pi Installer doesn't sign it.
42:43 The Pi 2 app automatically signs it.
42:46 So you have to sign it yourself, whether you use an ad hoc signature or a developer signature.
42:51 But you can do that.
42:52 I just have a shell script that does that for me.
42:55 Okay.
42:55 Interesting.
42:56 I've used Pi Installer for GUI.
43:00 Have you checked out GUI?
43:01 G-O-O-E-Y?
43:02 I have, yeah.
43:03 Yeah, yeah.
43:04 I don't know what to make of it.
43:06 It's super interesting.
43:07 And it lets you take any command line tool and turn it into, well, as the name says, like
43:14 a GUI, where it takes the command line arguments and turns them into widget inputs.
43:19 Like a file might be a browse for a new file, or a yes or no might be a check on or off type
43:24 of thing.
43:24 What's your thoughts on GUI?
43:25 Will we go on for that?
43:27 I think for simple CLIs, it's a great tool.
43:30 I've never, I haven't used it, but I have played around with it a little bit.
43:33 And it's, you know, definitely less scary, I guess, in the command line for users that aren't
43:38 really comfortable in the command line.
43:39 I have one app that I, a command line app that I use Pi Installer for that is fairly complex.
43:46 It's got over 150 command line options and it's, it's a, it's a beast.
43:50 So something like that would, would not work.
43:53 I actually tried it because I was just curious to see how it would work.
43:55 And it just, it kind of blew up.
43:56 But it wasn't having it.
43:58 What was it?
43:59 Yeah.
43:59 It's just like, nope, not going to do this.
44:01 And I need a lot of tabs, a lot of tabs, please.
44:04 For your simple command line apps.
44:06 I think it's super useful.
44:07 Yeah.
44:07 Yeah.
44:07 Because I'm sure a lot of listeners are like, it'd be fantastic to build a little Python
44:12 app that just does a thing that my coworkers need instead of some manual process that they
44:17 do.
44:18 But then as soon as you start to say, and then you open up terminal and you start to type,
44:21 they're just like, nope.
44:22 Yeah.
44:23 Yeah.
44:23 Right.
44:24 But if you give them a GUI, it's, it's pretty nice.
44:26 So one thing I would love to see, and since we can't, I mean, we can't do an episode
44:30 without mentioning Will McCoogan is a two-week version of GUI that would just automatically
44:35 create a TUI interface for you from your command line app.
44:39 Yeah.
44:39 You're right.
44:40 We can't do a show without mentioning him.
44:41 The textual stuff, it's coming along.
44:45 Yeah.
44:46 It's, it's, I really impressive what he's been doing.
44:48 So I have a couple of projects in mind that I, when I have to actually get sit down and
44:53 have time to learn textual a little bit that I would love to play around with.
44:57 Absolutely.
44:57 I think it would be, you're right that that would be a pretty nice way to sort of a more
45:03 more terminal native way of going.
45:06 All right, we're going to build this, this UI for you, but here's how you get to it.
45:09 But the one that you and I both use to create apps real.
45:13 And the one that you've gone to create textinator with is rumps, which stands for ridiculously
45:18 uncomplicated macOS Python status bar apps.
45:22 Woo.
45:25 That's probably the most complicated thing about rumps is it's name, right?
45:28 Like it's amazing to use.
45:30 Tell people about rumps.
45:31 Yeah.
45:31 It's super easy.
45:32 Rumps does all the heavy lifting of creating the status bar app, which you can see if you're
45:37 watching the video on the screen there, you get a, either your name of your app or an icon
45:42 in this, in the status bar, and then you can click on it and you get some menus.
45:46 And so, and you can do that in, I don't know, like 10 lines of code with rumps.
45:51 It's just ridiculously easy.
45:52 The name is accurate, right?
45:54 It is ridiculously uncomplicated.
45:56 Yeah.
45:56 As you point out, so they've got a hello world type of thing that has four options on the menu
46:03 or when you click on the thing in the menu bar, it drops down to four options.
46:07 And one of the things that it does is also post a toast notification, you know, native
46:13 macOS notification as well.
46:15 And in order to make that happen, that really is like 10 lines of Python.
46:19 Exactly.
46:20 Yeah.
46:20 It's super simple.
46:21 And that, so that notification code isn't really hard to do, but you'd have to set it up.
46:26 You'd have to set up a notification handler and, and register it.
46:30 Yeah.
46:31 You know, there's a, there's quite several steps to do that.
46:33 And rumps does it.
46:34 You just one line rumps dot notification.
46:36 And boom, you can post a notification.
46:39 So the basic idea is you create a class.
46:42 It derives from rumps dot apps app.
46:45 And then you use a bunch of decorators like clicked to say when a menu item is selected.
46:51 And then there's a few other calls you can do.
46:53 You can say, you know, rumps dot alert, and that pops out a notification.
46:57 You can say rumps dot notification.
47:00 I guess that doesn't pop it out.
47:01 Sorry.
47:01 That's like a modal dialogue.
47:02 You can say rumps dot notification, and then it pops out the toast.
47:05 And then you just say app dot run.
47:06 And that's pretty much it, right?
47:08 Yeah.
47:09 That's, and you've got a full blown native Mac app written in Python that's up and running.
47:15 Yep.
47:15 The one missing element, which we've already given people the key to is this is still just
47:21 a dot py file.
47:23 And if you try to give this to your friends or distribute on the internet, it's going to
47:29 go very badly, right?
47:30 There's no app bundle.
47:31 There's no dot app file package thing.
47:34 There's no Python runtime, right?
47:36 And so that's why you need py2app is you take your rumps app and then you py2app it.
47:41 And then you have something to give out, right?
47:43 Exactly.
47:43 And once they get that, they don't even need Python on their machine because it comes
47:47 bundled with that.
47:48 And so it's, it makes, it's really nice.
47:50 It actually, py2app will bundle up your Python and all the libraries packages you need in the
47:56 app folder so that it's got its own copy.
47:58 How do you make it auto start with system start?
48:01 And I asked that as somebody who created a rumps app and it is clearly auto starting.
48:06 I just don't remember how I did it.
48:08 I know that you can.
48:09 I don't know how to do it automatically, but if you go to your system settings and go to
48:13 your login items, you can add it there, right?
48:16 So it's a couple extra steps that you've got to do as a user and just say, you click, click
48:20 the plus next to your login items and it'll pop up a file dialogue and you can pick the,
48:27 your app to run and it'll run that when you log in.
48:29 Yeah.
48:30 I'm pretty sure that that's what I did to get it to auto start.
48:32 I was just wondering if there might be some other clever way, but probably not.
48:36 Right.
48:36 Yeah.
48:36 I've seen, I know it's possible for an app to do that.
48:39 I've because I have apps that you run them and it says, Hey, do you want me to run that
48:43 login?
48:43 Yes.
48:44 And it does it.
48:45 And so I just have not explored doing that myself because that's not something I'm doing
48:49 all the time.
48:50 But there probably is a way whether you can do that from Python or the, if the permissionals
48:55 will work, I'm not really sure of it.
48:57 It's, it might be something worth looking into.
48:59 Yeah.
48:59 You know, this rumps project is super cool, but it, you know, it's, it feels like it's
49:05 not quite super active these days.
49:08 Right.
49:08 Yeah.
49:08 That is one downside.
49:10 There's a fair amount of activity on the repo, but if you look at the issues, there's a number
49:14 of issues that have been open a long time or PRs that haven't been merged in and it doesn't
49:20 run on Python 3.10, for example.
49:22 So you'll need to install Python 3.9 or earlier to use it.
49:26 So there, you know, there's a, there are a few downsides to it, but it's a, you know,
49:30 it's a great app and I hope that it continues to get a little bit of love because it really
49:36 is super useful.
49:36 It really is useful.
49:38 You're, I totally agree.
49:38 And I'm just thinking, you know, there could be like a startup set some preferences, right?
49:44 Where you just say, I would like to request to run at startup.
49:47 You know, if that's permission that already set, then just set it through whatever objective
49:52 C OS level thing you've got to do.
49:54 Like there's just little things like that, that would be really, really nice.
49:57 You know, the menu bar, those dropdowns, like to be able to associate a hot key with them,
50:03 or there's just a few things like, you know, dividers in menus, right?
50:06 Like there's just these, they seem like such low hanging fruit that I think this could get
50:11 a big upgrade.
50:12 It's pretty popular.
50:13 It has 2.8 thousand stars, but not a ton of traffic recently.
50:17 And it'd be awesome if people, either Jared KS, who originally created, keeps going,
50:23 or if they've lost interest, then, you know, someone else could pick it up and run with
50:27 it.
50:27 That'd be fun.
50:28 Yeah, definitely.
50:29 Oh, Richie on the audience says, creating the P list to execute your script is just having
50:35 it in a tilde library launch agents.
50:38 So maybe if you just copy a script to start your app over to there and it just launches.
50:42 Okay.
50:43 Yeah.
50:44 I'll look at that.
50:44 You'll have to request access to the library folder as well.
50:49 So I'll have to look into that.
50:51 But I've done that before the old, you know, the old fashioned way of actually creating a
50:55 P list by hand and sticking it in there to get something to start.
50:59 So thanks, Richie.
51:00 I'll take a look at that.
51:01 Yeah.
51:01 That's excellent advice.
51:02 This is in your user profile.
51:04 So maybe you don't have to ask, but maybe you do.
51:06 All right.
51:07 So rumps is a very important building block here for Textinator.
51:11 Pi to app, also super important.
51:15 And the one that I created is called URLify.
51:17 And it just does a bunch of stuff like, you know, creates URL slugs and like transforms text.
51:22 That might end up doing it all the time.
51:24 That's my little app.
51:25 So the one we've got the ability to build the app with Pi to app, we've got a way to
51:31 kind of create a shell that runs in the menu bar and constantly runs in the background, which
51:37 is, you know, as long as nobody closes the app, right?
51:39 It's just out of the way.
51:40 So that's really fantastic.
51:42 And now you need to start working with some of the OS level APIs.
51:46 Like you talked about hooking the event for the screenshot.
51:49 And those are Objective-C type things, right?
51:52 Exactly.
51:52 Yeah.
51:53 And so for those, I use PyOBJC, PyObjective-C, which is a bridge between Python and Objective-C.
52:00 And it's an amazing package.
52:03 If you're a Python programmer and you use a Mac, you really ought to get familiar with PyOBJC,
52:09 because what that does is exposes everything that Apple's built in, all the native capabilities
52:14 like vision and machine learning.
52:17 The most recent update to Textinator, I added QR code detection.
52:20 So if you screenshot a QR code, it'll tell you what the URL is.
52:25 Oh, wow.
52:25 Decode the QR code?
52:27 Yeah.
52:28 Decode the QR code and stick it on your clipboard.
52:30 Nice.
52:31 But all that's built in to the Apple frameworks and PyOBJC gives you access to call those fairly
52:37 easily.
52:37 There are some quirks to it because Objective-C itself is kind of a quirky language.
52:42 And so you've got to do the translation between Python and Objective-C.
52:47 You'll see a lot of camel case that you don't normally see in Python.
52:51 But once you figure those things out, it's super useful.
52:55 Yeah.
52:56 There's no attempt to Pythonify or make Objective-C like the low-level runtime supporting classes
53:03 to make them Pythonic.
53:05 Yeah.
53:05 And actually, I'm glad.
53:06 Yeah.
53:07 Because when you have to go Google, what does this function do?
53:10 Or what's this NS file handle?
53:13 And how do I use it?
53:14 It's a lot easier if you can just cut and paste.
53:16 Yeah.
53:16 For sure.
53:17 One of the things I just...
53:18 I'm looking at some of these examples.
53:20 Maybe there's some hints in here.
53:22 But one of the things that's super odd to me about Objective-C...
53:26 It is one of those languages.
53:27 I'm like, I'm going to try to learn this.
53:28 And after a while, I'm like, no.
53:30 No, it's just too weird.
53:33 And how you have these named...
53:36 These really oddly named methods and then variables, right?
53:41 Like, you can't just say string lower.
53:43 You've got to get like, you know...
53:46 I forget what the API is called, but there's like a weird set of incantations to sort of invoke
53:51 a lowercase type of behavior.
53:53 So how does that...
53:55 Is this...
53:55 I see like in the example here, it looks like there's a string specifying maybe one of the
54:02 arguments or something like file handle read completed colon is one of the strings last year.
54:07 So the way...
54:08 What's the deal with that?
54:08 Yeah, this one...
54:09 I'm not super familiar with this notification center API, but looking at what you have on
54:13 the screen there, you've got the notification center dot add observer underscore selector
54:19 underscore name underscore object underscore, which if you use my Objective-C, you're going
54:23 to see a lot of underscores like that, including the trailing underscore.
54:26 And what that does is the way Objective-C works.
54:28 And let me caveat, I am not an Objective-C programmer.
54:31 I tried a couple of times to learn and just did not grok it.
54:36 But I've gotten pretty good at interpreting Objective-C through the Python lens so that I
54:42 can use it in Python because I've got quite a few Python apps that use Objective-C calls.
54:47 So Objective-C has this concept of selectors, which are basically sending messages to an object
54:53 to call specific functions on that object.
54:56 Oh, I see.
54:57 And a method on an object could have different signatures.
55:00 And so you might sometimes call it with a different value or a different number of parameters
55:06 than you would a different time.
55:08 And so the selectors handle doing that.
55:10 And so what this is doing is each of those underscores is, think of it as every time you
55:15 see an underscore in a PyObjective-C method call, you need to have a variable.
55:19 You need to have an argument to that method call because it's passing that selector the value
55:25 that you want for that particular selector.
55:27 So that's why there's four underscores in this example you've got on the screen and four
55:32 different values you're passing in, arguments you're passing in.
55:35 So if I say add observer, underscore selector, underscore name, underscore object, I've got to
55:39 give it a selector the name of the object and also itself.
55:43 Yeah.
55:43 And if you went and looked that up on the Apple Docs, instead of underscores, you'd see colon.
55:49 So you'd see add observer, colon selector, colon name, colon object.
55:52 And so that's the decoder ring there.
55:55 If you're trying to translate to Python is replace the colons with underscores and then
56:00 add a trailing underscore.
56:01 And then you've got to figure out, okay, well, for that number of arguments, what are those
56:06 arguments and what type do they need to be?
56:07 Very interesting.
56:09 Okay.
56:10 So it's weird, but it's kind of weird in the sense of just it's mirroring Objective-C,
56:15 which is itself a bit of a unique language.
56:18 But that said, this is a cool library that gives you direct access to much of the operating
56:24 system, right?
56:25 Yeah, exactly.
56:26 So Apple has built some really great frameworks like the Vision, right, that does the text
56:33 detection for Textinator, which literally takes a few lines.
56:35 Tell us quick about the Vision one here, because this is one of the core building blocks used,
56:39 right?
56:39 Exactly.
56:39 Yeah.
56:40 So Vision does a number of different computer vision type tasks for you and really only takes
56:46 a few lines.
56:47 It does text detection, find barcodes or QR codes.
56:51 It'll find faces in an image, detect different objects in images, those kinds of things.
56:56 And it does it really well.
56:58 It runs on the neural engine, as you said at the beginning of the show.
57:02 So it's super fast.
57:04 And you get access to that all from Python.
57:07 If you use Objective-C, you can directly access that power with just a handful of lines of
57:11 code.
57:11 And there are a number of Python packages for doing that.
57:14 There's several different Python image-to-text packages out there, for example.
57:19 But they're all fairly compute intensive, or you've got to download these big models or whatever.
57:24 And if you're on a Mac, you can just use them what's already built into your Mac.
57:27 Yeah, absolutely.
57:29 And so, for example, in Textinator, you just say, vision.vn recognize text request.
57:35 Yeah.
57:35 And knit with completion handler.
57:37 You give it the callback.
57:38 Yeah.
57:38 So one thing you'll notice, you skip past it.
57:40 There was an alloc, and then it is something you've got to do in Objective-C.
57:44 You've got to allocate your memory.
57:46 And then there's a whole memory management piece of this you've got to keep in mind.
57:50 For the most part, PyOBJC handles that for you.
57:54 But every now and then, it can bite you if you have got a...
57:58 You're allocating a bunch of things and never deallocating them.
58:01 You can have a memory link.
58:03 Back to memory management.
58:05 Something you normally don't have to think about in Python.
58:07 Yeah.
58:08 And then maybe some of the other...
58:10 You've got the natural language ML APIs as well, right?
58:14 Yeah.
58:14 There's natural language processing built in.
58:16 All the things that the Mac does to do all those neat Mac things are, for the most part,
58:21 available to the developer through these frameworks.
58:24 Oh, excellent.
58:25 All right.
58:26 Very cool.
58:26 Yeah, I guess that's probably it for the building blocks and stuff.
58:30 Now that you've got this built, would you have done anything different?
58:33 Or are you happy with the way it's come together?
58:35 I think I was pretty happy with the way it came together.
58:38 It works pretty good.
58:40 The most current version will immediately ask for desktop access, like we were talking about.
58:46 I didn't do that in the first version.
58:47 I left it up to the user to go add that manually.
58:51 When I realized, hey, the default location for screenshots is the desktop and 99% of people
58:57 are going to have it there.
58:58 Let's just ask right away for the desktop access and then it just works out of the box.
59:02 But yeah, again, it was a fun little project.
59:05 The initial version took a weekend and it's grown a little bit since then.
59:10 But the whole thing is still only about 500 lines of code.
59:12 And it's a fully functional, useful app that I use every day.
59:16 And so it was really fun to be able to build that in Python.
59:19 There's something very satisfying about having your app do the thing, you know, right?
59:24 You can, of course, download, everyone downloads apps from the app store and various places.
59:28 But to go, but that's my code, right?
59:30 Yeah.
59:31 My thing caught that text out of there or whatever it is, right?
59:34 Yeah, exactly.
59:35 Yeah.
59:36 Excellent.
59:36 I hope this is an inspiration as well as a roadmap for people that want to build interesting things
59:42 for maybe for themselves, but also for end users in Python on the Mac, right?
59:47 Rumps, if you want a menu bar app, PyObjective-C to get access to the framework and platform,
59:54 PyToApp to build it in a way that you can hand it out.
59:57 Very cool.
59:58 Yeah.
59:58 A few basic building blocks and you can put something pretty slick together, you know, fairly quickly.
01:00:05 Yeah, I totally agree.
01:00:06 The little one that I built with Rumps came together surprisingly well and it does way less
01:00:12 than what yours does.
01:00:13 I'm really impressed with you.
01:00:14 Yeah.
01:00:14 This is great.
01:00:15 I've got another, yeah, another Rumps one that I use every day that all it does is tell
01:00:19 me when to plug in or unplug my laptop, you know, just to try to keep the battery percentage
01:00:24 and it has an icon of a plug and it changes color depending on whether I should plug in,
01:00:30 it's charging or discharging and whether I should, you know, and then it pops up an alert
01:00:35 to say, plug in your laptop now.
01:00:36 It's super simple, but really useful.
01:00:39 Absolutely.
01:00:39 Keep it in the range, not always 100% charged or, but don't let it go dead.
01:00:43 Yeah.
01:00:44 Yeah.
01:00:44 That's super simple.
01:00:45 Those kinds of things are really easy to write with Rumps and the various tools we talked about.
01:00:49 So definitely, definitely check that out.
01:00:51 All right.
01:00:52 Well, I think we are out of time, but super cool work on Textinator.
01:00:57 And like I said, a great roadmap for people to follow if they want to build things like
01:01:00 that.
01:01:01 So, Rhett, before you get out of here, final two questions.
01:01:03 If you're going to work on Textinator or other Python things, what editor are you using these
01:01:08 days?
01:01:09 I use VS Code.
01:01:10 Right on.
01:01:11 extensions, plugins that you're a fan of.
01:01:13 Obviously, Python.
01:01:14 If you don't hate yourself in one of these spaces on your own.
01:01:17 Yeah.
01:01:18 So I use a couple.
01:01:19 I use GitHub Copilot, which I really enjoy.
01:01:22 It is a solo hobbyist developer who only gets a few hours a week to write code.
01:01:27 It's really, it's kind of like having a peer programmer and it's really helpful.
01:01:31 Like that knowledgeable friend.
01:01:32 You're like, how do you connect SQLAlchemy to a database again?
01:01:35 Exactly.
01:01:36 Yeah.
01:01:36 It just does it for you.
01:01:38 It's really useful.
01:01:39 And the other one that I really like is Git Lens.
01:01:42 Yeah.
01:01:43 Git Lens is cool.
01:01:44 Yeah.
01:01:44 You can sort of, it'll put the Git history.
01:01:47 Yeah.
01:01:48 Right in line with the code and you can see what changed and when.
01:01:52 And I was using that yesterday to try to debug a problem and find, when did I change this
01:01:57 code?
01:01:57 And yeah, it's really, that was really useful.
01:02:00 Yeah.
01:02:00 I was looking at the CPython source code with VS Code and I had Git Lens and stuff.
01:02:06 And I was going through some section and I saw this change from like 1994 from Guido.
01:02:13 And it had, why that line was changed.
01:02:15 I'm like, okay, this is like a bit of archeology or some history going on here, you know?
01:02:21 Yeah.
01:02:21 That's neat.
01:02:22 Yeah.
01:02:22 Very neat.
01:02:22 Okay.
01:02:23 Well, definitely a good choice there.
01:02:24 And then a notable PyPI package.
01:02:27 I mean, Rumps is an obvious choice, but if you got something else that you want to throw
01:02:30 out, go for it.
01:02:31 Yeah.
01:02:31 Well, let me, I've got two for you.
01:02:33 One, if we talked about PyOVJC, if you're a Mac developer or you're a Python developer
01:02:39 who uses a Mac, definitely take the time to learn that because it will give you, you know,
01:02:43 give your Python superpowers.
01:02:44 And then the other one, completely unrelated that I really like is called TextX.
01:02:49 And it's basically a parser generator for Python.
01:02:53 It's a peg, creates a parsing expression grammar for you out of its own little modeling language.
01:02:59 But basically with, if you need to do, if you find that you're writing more than two
01:03:03 regexes to parse some code, TextX might be able to help you out because it will build a
01:03:09 parser for you out of its own sort of specification language that then gives you a Python class that
01:03:14 parses your text for you.
01:03:15 And I've used it in some other projects to actually create my own language to solve a specific,
01:03:20 you know, domain problem.
01:03:22 Yeah.
01:03:22 And it's super useful.
01:03:24 It's fairly easy to create a fairly powerful parser.
01:03:26 When I hear, I don't have to write regular expressions, it already makes me happy.
01:03:30 Yeah.
01:03:31 It's great.
01:03:32 Their description here is, in a nutshell, TextX will help you build a textual language in an
01:03:38 easy way.
01:03:38 You could invent your own language or build support for an already existing one.
01:03:42 That sounds fantastic.
01:03:43 Yeah.
01:03:44 Great recommendation.
01:03:44 All right.
01:03:45 Well, final call to action.
01:03:47 Before we go, people are interested maybe in building apps, Mac apps with Python in a broad
01:03:54 sense.
01:03:54 What advice you got for?
01:03:55 I'd say learn PyOBJC, play with rumps and Py2 app and go.
01:04:01 There's a lot of great projects already out there on GitHub.
01:04:03 Go look at the textinator source code or search for some others.
01:04:07 And I think for me, the easiest way to get started is always to look at what somebody else has done
01:04:12 than reading through a whole bunch of docs.
01:04:14 And so I'd say go find a project that piques your interest and go build something for your
01:04:17 Mac.
01:04:18 Yeah, absolutely.
01:04:19 All right.
01:04:19 Well, it's been great to get a look inside what you're doing with this app and how you
01:04:24 built it for Mac and also a bit of a peek inside the Space Force.
01:04:28 So thanks for being here.
01:04:29 Great.
01:04:29 Yeah.
01:04:29 Thanks, Michael.
01:04:30 It was a lot of fun.
01:04:30 You bet.
01:04:31 Bye.
01:04:31 Bye.
01:04:31 This has been another episode of Talk Python to Me.
01:04:35 Thank you to our sponsors.
01:04:36 Be sure to check out what they're offering.
01:04:38 It really helps support the show.
01:04:40 Take some stress out of your life.
01:04:42 Get notified immediately about errors and performance issues in your web or mobile applications with
01:04:47 Sentry.
01:04:47 Just visit talkpython.fm/sentry and get started for free.
01:04:52 And be sure to use the promo code talkpython, all one word.
01:04:56 Want to level up your Python?
01:04:58 We have one of the largest catalogs of Python video courses over at Talk Python.
01:05:02 Our content ranges from true beginners to deeply advanced topics like memory and async.
01:05:07 And best of all, there's not a subscription in sight.
01:05:10 Check it out for yourself at training.talkpython.fm.
01:05:12 Be sure to subscribe to the show.
01:05:14 Open your favorite podcast app and search for Python.
01:05:17 We should be right at the top.
01:05:18 You can also find the iTunes feed at /itunes, the Google Play feed at /play, and the
01:05:24 direct RSS feed at /rss on talkpython.fm.
01:05:28 We're live streaming most of our recordings these days.
01:05:31 If you want to be part of the show and have your comments featured on the air, be sure
01:05:35 to subscribe to our YouTube channel at talkpython.fm/youtube.
01:05:39 This is your host, Michael Kennedy.
01:05:41 Thanks so much for listening.
01:05:42 I really appreciate it.
01:05:43 Now get out there and write some Python code.
01:05:45 I'll see you next time.
01:06:06 Thank you.