I wanted a GUI-type tool that opens pages with the same search tag at once on EC sites and work posting sites.
・ (The json class is an improvement of the previously made one and made it a common part) ・ Implemented Tkinter combo box handling class and made it a common component -Set the GUI with the main method, read the data, and open the web page selected by the GUI.
・ Windows 10 -Python 3.7.0 ・ Development is Pycharm
The GUI looks like this.
webSiteDetails.json
{
"WebSiteA": {
"OnlyOpenPage": true,
"PageSet": [
"http://foobar.com",
"http://foobar.com",
"http://foobar.com",
"http://foobar.com",
"http://foobar.com",
"http://foobar.com",
"http://foobar.com",
"http://foobar.com",
"http://foobar.com"
]
},
"WebSiteB": {
"OnlyOpenPage": true,
"PageSet": [
"http://foobar.com",
"http://foobar.com"
]
},
"WebSiteC": {
"OnlyOpenPage": true,
"PageSet": [
"http://foobar.com",
"http://foobar.com",
"http://foobar.com",
"http://foobar.com"
]
},
"WebSiteD": {
"OnlyOpenPage": true,
"PageSet": [
"http://foobar.com",
"http://foobar.com",
"http://foobar.com",
"http://foobar.com"
]
},
"WebSiteE": {
"OnlyOpenPage": true,
"PageSet": [
"http://foobar.com",
"http://foobar.com",
"http://foobar.com"
]
}
}
The "OnlyOpenPage" property was added When the scraping function implementation is also considered in the future Just open it Scraping processing is given, I wanted to make a distinction.
JsonHandler.py
import json
class JsonHandler:
def __init__(self, jsonPath):
#Read json file
self.file = open(jsonPath, 'r', encoding="utf-8_sig")
self.jsonData = json.load(self.file)
#Primary nest json data acquisition
def getParam_OneNest(self, args):
return self.jsonData[args]
#Get json data list of primary nest
def getAll_OneNest(self):
# list = []
# for d in self.jsonData.keys():
# list.append(d)
list = [d for d in self.jsonData.keys()]
return list
#Secondary nest json data acquisition
def getParam_TwoNest(self, args1, args2):
return self.jsonData[args1][args2]
def __del__(self):
#File close
self.file.close()
I changed the one I created before (*) a little. Because it was necessary to get only the data of the primary nest at once. [※:https://qiita.com/dede-20191130/items/65b0f4c3c2b5c7f97546]
list = [d for d in self.jsonData.keys()]
This is a list comprehension format.
TkHandler_Combo_1.py
import tkinter as tk
import tkinter.ttk as ttk
class TkHandler_Combo_1:
"""Common parts for GUI. Create a combo box"""
def __init__(self, rootTitle='title', rootGeometry='640x480'):
#Creating a root frame
self.root = tk.Tk()
self.root.title(rootTitle)
#The size of the root window as the argument value
self.root.geometry(rootGeometry)
#Close event settings
self.root.protocol("WM_DELETE_WINDOW", self.onClosing)
self.frame = None
self.label = None
self.combo = None
self.button = None
self.isDestroyed = False
def createFrame(self):
#Creating a frame
self.frame = ttk.Frame(self.root, width=self.root.winfo_width() - 20, height=self.root.winfo_height() - 20,
padding=10)
self.frame.pack()
def createLabel(self, myText="Please select"):
#Creating a label
self.label = ttk.Label(self.frame, text=myText, padding=(5, 5))
self.label.pack()
def createCombo(self, myWidth=None, myState='readonly', myValue=None, ):
#width setting
myWidth = myWidth or self.frame.winfo_width() - 20
#Creating a combo box
self.combo = ttk.Combobox(self.frame, width=myWidth, state=myState)
self.combo["values"] = myValue
#The default value(index=0)Set to
self.combo.current(0)
#Combo box placement
self.combo.pack(padx=5, pady=5, fill=tk.X)
def createButton(self, myText="Run", myFunc=None):
"""Take a function as an argument"""
#Function settings
myFunc = myFunc or (lambda: self.dummy())
#Creating a button
self.button = tk.Button(text=myText, command=lambda: myFunc(self.combo.get()))
#* ↑↑ Initially implemented like this
# if myFunc:
# self.button = tk.Button(text=myText, command=lambda: myFunc(self.combo.get()))
# else:
# self.button = tk.Button(text=myText, command=lambda: self.dummy())
#Button placement
self.button.pack(padx=5, pady=5, )
def dummy(self, arg=''):
pass
def mainLoop(self):
self.root.mainloop()
def onClosing(self):
self.root.destroy()
self.isDestroyed = True
def __del__(self):
if not self.isDestroyed:
self.root.destroy()
#Close event settings
self.root.protocol("WM_DELETE_WINDOW", self.onClosing)
Syntax to set a method that will be called when the window is closed without pressing the run button. Erase the frame before closing with onClosing Prevent errors when the destructor (\ __ del__ function) works.
def createCombo(self, myWidth=None, myState='readonly', myValue=None, ):
#width setting
myWidth = myWidth or self.frame.winfo_width() - 20
If an argument is specified for myWidth, it will continue to have that value, If the default argument None is left, a value that is 20 less than the width value of the frame will be entered.
I referred to this article for how to use or. https://blog.pyq.jp/entry/python_kaiketsu_181016
The reason why I didn't put "self.frame.winfo_width () -20" directly in the default argument is It seems that you cannot take the members of the field that your own class has as an argument. If anyone knows that there is a possible way, please let me know.
def createButton(self, myText="Run", myFunc=None):
"""Take a function as an argument"""
#Function settings
myFunc = myFunc or (lambda: self.dummy())
#Creating a button
self.button = tk.Button(text=myText, command=lambda: myFunc(self.combo.get()))
#* ↑↑ Initially implemented like this
# if myFunc:
# self.button = tk.Button(text=myText, command=lambda: myFunc(self.combo.get()))
# else:
# self.button = tk.Button(text=myText, command=lambda: self.dummy())
Tkinter button object as an event when pressed A function is assigned to the command argument.
Therefore, when the createButton method is called You have to prepare a function on the caller side.
However, when making it a common part, I wanted to prepare a default argument in case the function is not specified.
At first I tried this.
def createButton(self, myText="Run", myFunc=lambda: pass):
This didn't work. It seems difficult to set a lambda function as an argument.
So, define a dummy method dummy in the class I tried to substitute it.
Here, as mentioned above, "I can't take the member of the field that my class has as an argument", so I wrote as follows.
def createButton(self, myText="Run", myFunc=None):
"""Take a function as an argument"""
# if myFunc:
# self.button = tk.Button(text=myText, command=lambda: myFunc(self.combo.get()))
# else:
# self.button = tk.Button(text=myText, command=lambda: self.dummy())
This was fine, but it's a bit verbose Writing an expression that defines the button field twice is twice as difficult as maintenance.
Therefore, it was rewritten as follows.
def createButton(self, myText="Run", myFunc=None):
"""Take a function as an argument"""
#Function settings
myFunc = myFunc or (lambda: self.dummy())
#Creating a button
self.button = tk.Button(text=myText, command=lambda: myFunc(self.combo.get()))
QuickBrowsing.py
import os
import subprocess
import sys
#Reset module search path
#Make it possible to start by double-clicking
sys.path.append(os.getenv("HOMEDRIVE") + os.getenv("HOMEPATH") + r"\PycharmProjects\CreateToolAndTest")
from Commons.JsonHandler import JsonHandler
from Commons.TkHandler_Combo_1 import TkHandler_Combo_1
import webbrowser
from time import sleep
# global
jsonHandler = None
siteList = None
tkHandler = None
siteName = None
def Main():
global jsonHandler
global siteList
global tkHandler
global siteName
#Item acquisition
jsonHandler = JsonHandler(
r'C:\Users\dede2\PycharmProjects\CreateToolAndTest\Tool_Python/QuickBrowsing/webSiteDetails.json')
siteList = jsonHandler.getAll_OneNest()
#Form display
tkHandler = TkHandler_Combo_1('Quick browsing', '640x200')
tkHandler.createFrame()
tkHandler.createLabel('Select a website for batch display.')
tkHandler.createCombo(myValue=siteList)
tkHandler.createButton(myFunc=getSiteName)
tkHandler.mainLoop()
#If the site is not set, it ends normally
#If the web page is not registered, it ends normally
if siteName == None or not jsonHandler.getParam_TwoNest(siteName, 'OnlyOpenPage'):
exit(0)
#Open websites in order
subprocess.Popen("start chrome /new-tab www.google.com --new-window", shell=True)
sleep(1)
browser = webbrowser.get(r'"' + os.getenv(r'ProgramFiles(x86)') + \
r'\Google\Chrome\Application\chrome.exe" %s')
for url in jsonHandler.getParam_TwoNest(siteName, 'PageSet'):
browser.open(url)
def getSiteName(argName=''):
global tkHandler
global siteName
tkHandler.onClosing()
siteName = argName
if __name__ == '__main__':
Main()
siteList = jsonHandler.getAll_OneNest()
abridgement
tkHandler.createCombo(myValue=siteList)
Get only the site name headline from json Make it an item in the drop-down list.
tkHandler.createButton(myFunc=getSiteName)
abridgement
def getSiteName(argName=''):
global tkHandler
global siteName
tkHandler.onClosing()
siteName = argName
After pressing the GUI button, I put a value in a global variable and keep it.
After checking, use the open_new method of the webbrowser module. It says that it can be opened in a new window.
** But when I try to move it, it doesn't work, ** ** It opens as a new tab in the original window. ** **
Apparently, it seems to be such a specification. https://docs.python.org/ja/3/library/webbrowser.html
webbrowser.open_new(url) If possible, open the url in a new window in your default browser, otherwise open the url in just one window in your browser.
webbrowser.open_new_tab(url) If possible, open the url in a new page ("tab") in the default browser, otherwise behave like open_new ().
If chrome was originally open, as a tab for that window Expected to open.
#Open websites in order
subprocess.Popen("start chrome /new-tab www.google.com --new-window", shell=True)
sleep(1)
browser = webbrowser.get(r'"' + os.getenv(r'ProgramFiles(x86)') + \
r'\Google\Chrome\Application\chrome.exe" %s')
for url in jsonHandler.getParam_TwoNest(siteName, 'PageSet'):
browser.open(url)
Open the chrome browser in a new window with the subprocess.Popen function After waiting for a second I decided to open all the pages of the selected site in that window at once.
It may be possible to implement it more elegantly.
The latest tools and source code are available here. ↓ https://github.com/dede-20191130/CreateToolAndTest/tree/master/Tool_Python/QuickBrowsing
Please comment if you have any supplements.
Implemented a check box in the GUI to prevent the tool from exiting even after pressing the process button.
The following child class was created by inheritance. (Since the camel registration method for functions / variables does not seem to be the python standard Changed to use the snake signature method. )
TkHandler_Combo_1_CanKeep.py
import tkinter as tk
# Common parts in my GitHub
# https://github.com/dede-20191130/CreateToolAndTest
from Commons.TkHandler_Combo_1 import TkHandler_Combo_1
class TkHandler_Combo_1_CanKeep(TkHandler_Combo_1):
"""
Implemented a check box to not exit the tool even after pressing the process button
"""
def __init__(self, rootTitle='title', rootGeometry='640x480'):
super(TkHandler_Combo_1_CanKeep, self).__init__(rootTitle, rootGeometry)
self.keeping_check = None
self.bl = None
def create_keep_check(self, text='check box', is_initial=True):
self.bl = tk.BooleanVar(value=is_initial)
self.keeping_check = tk.Checkbutton(self.frame, text=text, variable=self.bl)
self.keeping_check.pack()
self.bl = tk.BooleanVar(value=is_initial)
self.keeping_check = tk.Checkbutton(self.frame, text=text, variable=self.bl)
BooleanVar is a Widget variable. Reference: https://suzutaka-programming.com/tkinter-variable/
Variables that can be specified in the -text variable and -variable options.
You can dynamically change the label string and get the entered value.
Rewrote as follows.
QuickBrowsing.py
import os
import subprocess
import sys
#Reset module search path
#Make it possible to start by double-clicking
sys.path.append(os.getenv("HOMEDRIVE") + os.getenv("HOMEPATH") + r"\PycharmProjects\CreateToolAndTest")
# Common parts in my GitHub
# https://github.com/dede-20191130/CreateToolAndTest
from Commons.JsonHandler import JsonHandler
from Commons.TkHandler_Combo_1_CanKeep import TkHandler_Combo_1_CanKeep
import webbrowser
from time import sleep
# global
jsonHandler = None
siteList = None
tkHandler = None
siteName = None
def Main():
global jsonHandler
global siteList
global tkHandler
global siteName
#Item acquisition
jsonHandler = JsonHandler(
r'C:/Users/UserName/MyFolder/Foo.json')
siteList = jsonHandler.getAll_OneNest()
#Form display
tkHandler = TkHandler_Combo_1_CanKeep('Quick browsing', '640x200')
tkHandler.createFrame()
tkHandler.createLabel('Select a website for batch display.')
tkHandler.createCombo(myValue=siteList)
tkHandler.create_keep_check('Check if it does not end after execution.')
tkHandler.createButton(myFunc=get_name_and_open)
tkHandler.mainLoop()
def get_name_and_open(argName=''):
global tkHandler
global siteName
if not tkHandler.bl.get():
tkHandler.onClosing()
siteName = argName
open_in_order()
def open_in_order():
global jsonHandler
global siteName
#If the site is not set, it ends normally
#If the web page is not registered, it ends normally
if siteName == None or not jsonHandler.getParam_TwoNest(siteName, 'OnlyOpenPage'):
exit(0)
#Open websites in order
subprocess.Popen("start chrome /new-tab www.google.com --new-window", shell=True)
sleep(1)
browser = webbrowser.get(r'"' + os.getenv(r'ProgramFiles(x86)') + \
r'\Google\Chrome\Application\chrome.exe" %s')
for url in jsonHandler.getParam_TwoNest(siteName, 'PageSet'):
browser.open(url)
if __name__ == '__main__':
Main()
if not tkHandler.bl.get():
tkHandler.onClosing()
You can freely check and uncheck the check boxes on the GUI. If the execute button is pressed when it is on, the onclose method is not called.
Recommended Posts