Recently, lessons and meetings are increasingly held using web conferencing systems such as zoom. However, the major drawback of online lessons is that the blackboard shared on the screen will flow shortly after you make a note. In order to solve such a problem, I tried to make an exe application ** that can be saved as pdf by squeezing the registered range (shared screen part etc.) with one click. (* Let's take screenshots only if you have permission to take screenshots)
The app and its details can be found on GitHub, so I'd appreciate it if you could use it and comment (advice, etc.). \ <GitHub version ↓ >
This time, as a memo of the creation procedure, I will use a simplified full screenshot application ** 1. [Graphic (appearance) design](# 1-Graphic design) 2. [Event (action) settings](# 2-Event settings) 3. [Create Icon](# 3-Create Icon Self-style) 4. [exe conversion](# 4-exe conversion) ** I will write in the order of. \ <Simplified version ↓ >
・ Windows10 ・ Python 3.7.9 (up to 3.7.X if exe is converted with py2exe) ・ WxPython (phoenix) ・ Power Point ・ Py2exe (Installation details described later)
Make your app look with ** wxpython **. There is an easy-to-use tkinter in the standard library, but wxpython has a lot of event processing and you can make elaborate apps.
Basically, wx.Panel
(panel) is spread in wx.Frame
(frame, skeleton), and widgets such as wx.Button
(button) are placed on it.
Frames, panels, and widgets take almost the same arguments, but we'll focus on the ones we'll use this time.
argument | Description |
---|---|
parent | parent. Place yourself on the frame or panel you put in here. When the parent disappears, the child himself disappears. |
label / value | Set the characters to be displayed on yourself and the values that you hold. Which one can be used or not can be used for each widget. value is[Widget].GetLabel() /[Widget].GetValue() Get in,[Widget].SetLabel([value]) /[Widget].SetValue([value]) Can be changed with. |
pos | (x, y)In the form of a screen, orparent Own x against-Set the y coordinate. Upper left corner |
size | (w, h)Set your own width and height in the form of. |
There is also a way to arrange widgets with "sizar" such as wx.BoxSizer
without using pos
. Then, when the user changes the window size, each part can be moved or scaled according to the size. (This time, I will not use it because the window size is fixed)
Let's see the specific usage below.
sample_1.py
# coding: utf-8
import wx #Import wxpython
try: # ⏋
from ctypes import windll #| App resolution setting
windll.shcore.SetProcessDpiAwareness(True) #| (Prevents out-of-focus)
except: # |
pass # ⏌
app = wx.App() #Write this at the beginning and start wx
#GUI settings ──────────────────────────────────────────────── ─────────────────────────────────────────────────
frame = wx.Frame(parent=None, #Create a frame (outer frame)
title='Screenshot app',
pos=(0, 0), # pos=(x, y)Is the x of the frame position with the upper left of the screen as the origin-y coordinate
size=(400, 230), # size=(w, h)Is the width of the frame(w)And height(h)
style=wx.DEFAULT_FRAME_STYLE & ~(wx.RESIZE_BORDER | wx.MAXIMIZE_BOX))
#style is any additional setting.This time disable window resizing
panel = wx.Panel(parent=frame) #Create a panel on the frame
wx.StaticText(parent=panel, label='Destination:', #Create a string on the panel.pos is parent(panel)Relative coordinates on
pos=(20, 20))
wx.TextCtrl(parent=panel, value='Unselected', #Create a folder input field on the panel
pos=(150, 17), size=(160, -1), #to size"-1"Enter and leave it to wx
style=wx.TE_READONLY) # TE_Input can be disabled with READONLY
wx.Button(parent=panel, label='...',
pos=(320, 16), size=(40, 35))
wx.StaticText(parent=panel, label='Last screenshot:', pos=(20, 70))
wx.StaticText(parent=panel, label='―', pos=(150, 70), size=(210, -1),
style=wx.TE_CENTER) # TE_At CENTER,Characters can be centered within the width given by size
wx.Button(parent=panel, label='Screenshot',
pos=(20, 120), size=(340, 40))
# ───────────────────────────────────────────────────────────────────────────────────────────────────────
frame.Show() #Show frame when it looks
app.MainLoop() #Completed by accepting event input
When you run it, it will look good. Since no event is set, there is no response even if the button is pressed.
Set an event in the button or input field.
Basically, the contents to be processed for each operation are summarized in the function func_1 (event, [second argument], ...)
and linked with [widget] .Bind ([event type], func_1)
. I will.
Event types range from widget-specific ones such as wx.EVT_BUTTON
(when the button is pressed) and'wx.EVT_SLIDER' (when the slider value changes) to wx.EVT_CLOSE
(window closes). When) and so on.
sample_2.py
# coding: utf-8
import os
import re
from glob import glob
from datetime import date
from PIL import ImageGrab
import wx
try:
from ctypes import windll
windll.shcore.SetProcessDpiAwareness(True)
except:
pass
TODAY = format(date.today()) #Today's date
last_pic_num = 0 #Photo number
#Event function definition ─────────────────────────────────────────────── ─────────────────────────────────────────
def open_folder_dialog(event): #The first argument of the function used in the event"event"Write
"""
Folder the path of the folder selected in the dialog_Displayed in txctrl,
Get the last photo number in the folder, picture_Function to update info information
"""
global last_pic_num
dlg = wx.DirDialog(parent=None, message='Select save destination') #Dialog settings
if dlg.ShowModal() == wx.ID_OK: # ShowModal()Start with,Once selected, do the following: ↓
dirpath = dlg.GetPath() #Get the path to the selected folder,
folder_txctrl.SetValue(dirpath) #Update input field,
os.chdir(dirpath) #Move into that folder,
all_pictures = [p for p in glob(os.path.join(dirpath, '*.png')) #Create a photo list with today's date
if re.search('{}_\\d'.format(TODAY), p)]
if all_pictures: #If you have a photo,
last_pic_num = max(list(map(lambda x: int(re.findall('_(\\d+)', x)[0]), #Get the last number,
all_pictures)))
picture_info.SetLabel(str(last_pic_num)) #Update photo information
else:
last_pic_num = 0
picture_info.SetLabel('─')
scshot_button.Enable() #Enable screenshot button
def screen_shot(event):
"""
Screenshot the screen,Save with the advanced photo number, picture_Function to update info information
"""
global last_pic_num
last_pic_num += 1 #Move the number up by one,
picture_info.SetLabel(str(last_pic_num)) #Update photo information
screen_picture = ImageGrab.grab() #Take a screenshot,
save_name = TODAY + '_{}.png'.format(last_pic_num) # "2020-01-23_(number)"In the format of,
screen_picture.save(save_name) #Save
# ───────────────────────────────────────────────────────────────────────────────────────────────────────
app = wx.App()
#GUI settings ──────────────────────────────────────────────── ─────────────────────────────────────────────────
frame = wx.Frame(parent=None, title='Screenshot app', pos=(0, 0), size=(400, 230),
style=wx.DEFAULT_FRAME_STYLE & ~(wx.RESIZE_BORDER | wx.MAXIMIZE_BOX))
panel = wx.Panel(parent=frame)
wx.StaticText(parent=panel, label='Destination:', pos=(20, 20))
folder_txctrl = wx.TextCtrl(parent=panel, value='Unselected', pos=(100, 17), size=(210, -1), #Call later
style=wx.TE_READONLY)
folder_button = wx.Button(parent=panel, label='...', pos=(320, 16), size=(40, 35)) #Call later
wx.StaticText(parent=panel, label='Today's final screenshot number:', pos=(20, 70))
picture_info = wx.StaticText(parent=panel, label='―', pos=(200, 70), size=(160, -1), #Call later
style=wx.TE_CENTER)
scshot_button = wx.Button(parent=panel, label='Screenshot', pos=(20, 120), size=(340, 40)) #Call later
# ───────────────────────────────────────────────────────────────────────────────────────────────────────
scshot_button.Disable() #Disable the screenshot button until the save destination is selected
#Event setting ──────────────────────────────────────────────── ────────────────────────────────────────────
folder_button.Bind(wx.EVT_BUTTON, open_folder_dialog) #When you press the button,Execute the function of the second argument
scshot_button.Bind(wx.EVT_BUTTON, screen_shot) #note)At the end of the function"()"Do not put on.
# ───────────────────────────────────────────────────────────────────────────────────────────────────────
frame.SetIcon(wx.Icon('sample_icon.ico')) #Icon setting (error if the specified path does not exist)
frame.Show()
app.MainLoop()
I haven't created an icon, so I get an error when I run it, but when I press the screenshot button, the photo is saved in the selected folder.
A file with the extension .ico
is required, but thankfully there is a site on the net that can convert .png
→ .ico
, so make a png with PowerPoint and convert it. (It doesn't have to be PowerPoint)
.png
with. You can convert it to .ico
by using this conversion site Let's make a semi-transparent multi-icon favicon.ico!. .. This time, save it as "sample_icon.ico" in the same directory ** as sample_2.py
**. (You can put the appropriate path in wx.Icon ([PATH])
anywhere.)
When you execute "sample_2.py", you can see that the icon is set in the upper left of the window.Use ** py2exe **, which has a short startup time.
If you use versions up to python 3.4
pip install py2exe
When using python 3.5-3.7 (hereinafter referred to as 3.X), go to py2exe release site and py2exe-0.9.3.2-cp3X-none- Download win_amd64.whl
(64-bit) or py2exe-0.9.3.2-cp3X-none-win32.whl
(32-bit) and
py -3.X -m pip install 'Path of downloaded file'
You have to create a setup file called setup.py
. (Short to say)
Put it in the same directory ** as sample_2.py
** again.
setup.py
from distutils.core import setup
import py2exe
option = {'compressed': 1, 'optimize': 2 , 'bundle_files': 3, 'excludes': ['email', 'numpy', 'test', 'tkinter']}
setup(
options={'py2exe': option},
windows=[{'script': 'sample_2.py', 'icon_resources': [(1, 'sample_icon.ico')]}],
zipfile='lib\\libs.zip'
)
Start a command prompt in the directory containing sample_2.py
and setup.py
,
py -3.X -m setup py2exe
Then, I think that a dist
folder will be created in the folder, so put the .ico
file in it (or put it in the location of the description path) and you're done!
After that, create a shortcut for .exe
and put it on your desktop!
In addition to the above, I learned to use Threading to handle time-consuming event processing in a separate thread. It will not accept input until the processing is completed, and the application will be frozen lol
Recommended Posts