Title: Show saved WiFi information in Python
This program displays information about the WiFi networks stored by your computer. When it starts, the program lists the known networks in the listbox on the left. Click one to see that network's details on the right.
The kinds of information displayed depend on the type of connection. In the picture on the right, for example, the "ATL Free Wi-Fi" network doesn't have a password so the Security Key field's value is "Not present." If the network had a password, the Security Key field would have the value "Present" and another field, Key Content, would show the network's password.
This is a fairly simple program based on a Python Coding Facebook post. This example makes three changes:
- The original was a command-line program. This one uses tkinter.
- The original used split to divide profile text at : characters but that doesn't work if a network's name contains a colon. (Something that is allowed, much to my annoyance.) This example looks for the first colon on a line.
- The original used subprocess.check_output. This example uses the new and improved subprocess.run.
Building the User Interface
When the program starts, it calls the following method to create its user interface. I often skip posting the user interface code because it's straightforward, but this time it's not straightforward.
def build_ui(self):
'''Build the user interface.'''
left_frame = tk.Frame(self.window)
left_frame.pack(side=tk.LEFT, fill=tk.Y, padx=5, pady=5)
# Make the listbox.
scrollbar = tk.Scrollbar(left_frame, orient=tk.VERTICAL)
listbox = tk.Listbox(left_frame, yscrollcommand=scrollbar.set,
width=35, height=40, exportselection=False)
listbox.pack(side=tk.LEFT, fill=tk.Y)
scrollbar.config(command=listbox.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
listbox.bind('<<ListboxSelect>>', self.name_selected)
self.wifi_listbox = listbox # Save it for later use.
# Detail textbox.
font = ('Courier New', 8, 'normal')
self.detail_text = scrolledtext.ScrolledText(self.window, font=font,
width=80)
self.detail_text.pack(side=tk.RIGHT, expand=True, fill=tk.BOTH,
padx=(0,5), pady=5)
The user interface consists of two main widgets: a Listbox on the left and a ScrolledText on the right.
To make the Listbox, the program creates a frame and puts a scroll bar and listbox inside it. Note that the code sets the listbox's exportselection property to False. If you don't do this, then when focus leaves the listbox it stops highlighting its selected item. Even worse, it clears its selection. In this program, that has a weird side effect. If you select a WiFi from the listbox and then click on the ScrolledText on the right, all is well. If you click and drag on the ScrolledText, however, the listbox loses focus and loses its selection. Then, because no WiFi is selected, the program clears the ScrolledText widget. It's all very annoying and makes me wonder what they were thinking when they gave the listbox this behavior.
Anyway, setting exportselection=False makes the listbox behave in a reasonable way.
After creating the listbox and scrollbar, the method sets the listbox's yscrollcommand to the scrollbar's set method so the listbox can update the scrollbar's position if you scroll the listbox. It also configures the scrollbar's command value to the listbox's yview method so the scrollbar can adjust the listbox's position.
After connecting the listbox and scrollbar, the method binds the listbox's ListboxSelect command to the name_selected method so that method is called when you select a WiFi entry.
Finally, the method creates the program's ScrolledText widget.
Listing WiFis
After it creates its user interface, the program calls the following method to list the known WiFis.
def list_wifis(self):
'''Fill the WiFi listbox.'''
# Get a list of the profiles.
profiles = subprocess.run('netsh wlan show profiles', shell=True,
capture_output=True, text=True).stdout
# Select lines that include "All User Profile."
lines = [line for line in profiles.split('\n')
if 'All User Profile' in line]
# Split at the first colon.
wifi_names = []
for line in lines:
wifi_names.append(line[line.find(':')+1:].strip())
wifi_names.sort()
for name in wifi_names:
self.wifi_listbox.insert('end', name)
This method uses subprocess.run to execute the command netsh wlan show profiles and save the command's output in the string profiles. The result looks something like this:
Profiles on interface Wi-Fi:
Group policy profiles (read only)
---------------------------------
User profiles
-------------
All User Profile : ATL Free Wi-Fi
All User Profile : Abraham Linksys
All User Profile : Bill Wi the Science Fi
All User Profile : DEN Airport Free WiFi 2.4G
All User Profile : Extremely Slow WiFi You Don't Want to Use
The code then parses this text. It splits the text into lines and extracts those that contain the string All User Profile. For those lines, it finds the first colon, extracts everything after that, and calls strip to remove whitespace at the ends.
Having gathered the network names, the code sorts the list and adds the names to the listbox.
Displaying Detail
When you select a WiFi from the listbox, the following method displays its details.
def name_selected(self, event):
w = event.widget
selection = w.curselection()
if not selection:
result = ''
else:
index = selection[0]
wifi = w.get(index)
result = subprocess.run(
f'netsh wlan show profile "{wifi}" key=clear',
shell=True, capture_output=True, text=True).stdout
self.detail_text.delete('1.0', tk.END)
self.detail_text.insert('1.0', result)
This method gets the listbox's current selection. If the selection is not empty, it is a tuple holding the indices of the selected items. In this example, you can only select one item at a time, so the selection is a tuple holding the index of that item.
The code gets the selected index and uses it to get the listbox's selected value. The code then executes a command with the following format:
netsh wlan show profile "ATL Free Wi-Fi" key=clear
The code then simply gets the result and dumps it into the ScrolledText widget without further processing. You could parse the result to find specific fields it you like.
Conclusion
Download the example to experiment with it and to see additional details.
|