I am trying to get the themes from the gnome-look.org and trying to create widgets by scraping the website.
I wanted to show the window first while updating the GtkWidgets necessary in the background via another Thread.
Here is my code
[code]
#!/usr/bin/python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GdkPixbuf
import requests
import sys
import gi
import shutil
from bs4 import BeautifulSoup
import dryscrape
import json
import urllib.parse
import concurrent.futures
import threading
class ReadGnomeLook:
def format_bytes(self, size):
# 2**10 = 1024
power = 2**10
n = 0
power_labels = {0 : '', 1: 'KB', 2: 'MB', 3: 'GB'}
while size > power:
size /= power
n += 1
return str("{:.2f}".format(size)) +' ' + str(power_labels[n])
def getDownloadLinks(self, childURL):
#childURL = "https://www.gnome-look.org/s/Gnome/p/1519633"
childURL = childURL+"#files-panel"
session = dryscrape.Session()
session.set_attribute('auto_load_images', False)
session.visit(childURL)
response = session.body()
soup = BeautifulSoup(response, features='lxml')
downloadlink = []
allscripts = soup.find_all('script', {"src":False})
for each_script in range(len(allscripts)):
content = str(allscripts[each_script]).split("var")
for indx in content:
if 'filesJson' in str(indx):
content = indx.replace('filesJson = ','').replace(';','')
content = json.loads(content)
links = []
for each_item in content:
if each_item['active'] == '1':
links.append({'name':each_item['name'],'type':each_item['type'],'size':format_bytes(int(each_item['size'])),'md5sum':each_item['md5sum'],'title':each_item['title'],'description':each_item['description'],'url':urllib.parse.unquote(each_item['url'])})
for each in links:
downloadlink.append(each)
return downloadlink
def readWebpage(self, URL):
myProducts = []
baseURL="https://www.gnome-look.org"
#URL = "https://www.gnome-look.org/browse/cat/132/order/latest/"
session = dryscrape.Session()
session.set_header('Host','www.gnome-look.org')
session.visit(URL)
response = session.body()
soup = BeautifulSoup(response, features='lxml')
#print(soup)
#soup.find(class="product-browse-item-info")
mydivs = soup.find_all("div", {"class": "product-browse-item picture"})
for mydiv in mydivs:
myProducts.append([
{'name' : mydiv.div.a.findAll("div",{"class":"product-browse-item-info"})[0].h2.text},
{'category' : mydiv.div.a.findAll("div",{"class":"product-browse-item-info"})[0].findAll("span")[0].text},
{'author' : mydiv.div.a.findAll("div",{"class":"product-browse-item-info"})[0].findAll("span")[1].b.text},
{'img' : mydiv.div.a.div.img['src']},
{'href' : mydiv.div.a['href']}
])
productCatalog = []
for elements in myProducts:
productCatalog.append([
{
'Name':elements[0]['name'],
'Category': elements[1]['category'],
'Author': elements[2]['author'],
'Image': elements[3]['img'],
'Link': baseURL + elements[4]['href'],
#'DownloadLinks': getDownloadLinks(baseURL + elements[4]['href'])
}
])
return productCatalog
class AppicationWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Themes Manager")
# Main Application Window
# self.set_title("Themes Manager v1.0")
#self.set_default_size(400, 400)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("destroy",Gtk.main_quit)
# Create Image and Title Grid
#self.image = self.getImageFromWeb('https://media.wired.com/photos/592697678d4ebc5ab806acf7/master/w_2560%2Cc_limit/GooglePlay.jpg')
#self.image.set_from_file("android-download.png")
#image.set_size(200,200)
print("Before 1st Show all")
self.show_all()
z = threading.Thread(target=self.doProcessing(),daemon=True)
z.start()
print("Started Z thread")
def doProcessing(self):
# Grid for Full Icon Themes
self.gridfulliconthemes = Gtk.FlowBox(valign = Gtk.Align.START)
self.gridfulliconthemesscroll = Gtk.ScrolledWindow(hexpand=True, vexpand=True) # Create scroll window
self.gridfulliconthemesscroll.add(self.gridfulliconthemes) # Adds the TreeView to the scroll container
self.getProductCatalog()
## Start
self.URLs = []
self.labels = []
self.images = []
self.threads = []
for each_item in self.productCatalog:
image = Gtk.Image()
image.new_from_file('/tmp/82682596e6c89475b2f21221d5dc61927887.png')
self.images.append(image)
self.labels.append(Gtk.Label("loading"))
for each_item in range(0,len(self.productCatalog)):
#print(each_item[0]['Name'])
self.URLs.append(self.productCatalog[each_item][0]['Image'])
vertical_box = Gtk.Box()
vertical_box.set_homogeneous(True)
vertical_items = Gtk.FlowBox(valign = Gtk.Align.START)
vertical_items.set_max_children_per_line(1)
label = Gtk.Label()
label.set_text(self.productCatalog[each_item][0]['Name'])
label.set_line_wrap(True)
label.set_max_width_chars(10)
label.set_hexpand(True)
self.labels.append(label)
#image = Gtk.Image()
#self.images.append(image)
vertical_items.add(self.images[each_item])
vertical_items.add(self.labels[each_item])
vertical_box.add(vertical_items)
vertical_box.connect("button-press-event", self.do_anything)
self.gridfulliconthemes.add(vertical_box)
## End
# Create Notebook to add to the Window
self.notebook = Gtk.Notebook()
self.add(self.notebook)
self.fullicontheme = Gtk.Label()
self.fullicontheme.set_text("Full Icon Themes")
self.gtkthemes = Gtk.Label()
self.gtkthemes.set_text("Gtk 3/4 Themes")
self.gnomeshellthemes = Gtk.Label()
self.gnomeshellthemes.set_text("Gnome Shell Themes")
self.fulliconthemepage = Gtk.Label()
self.fulliconthemepage.set_text("Full Icon Themes Page")
self.gtkthemespage = Gtk.Label()
self.gtkthemespage.set_text("GTK themes Page")
self.gnomeshellthemespage = Gtk.Label()
self.gnomeshellthemespage.set_text("Gnome Shell Themes Page")
#notebook.append_page(fullicontheme, Gtk.Label("Icon Page"))
self.notebook.append_page(self.gridfulliconthemesscroll, self.fulliconthemepage)
self.notebook.append_page(self.gtkthemes, self.gtkthemespage)
self.notebook.append_page(self.gnomeshellthemes, self.gnomeshellthemespage)
self.notebook.set_tab_reorderable(self.gridfulliconthemesscroll, True)
#self.add(hb)
#self.show_all()
#threadtemp = threading.Thread(target=self.getImageFromWeb(each_item[0]['Image']))
#self.threads.append(threadtemp)
#self.getAllImages()
x = threading.Thread(target=self.getAllImages(),daemon=True)
x.start()
self.show_all()
def getProductCatalog(self):
# Download Links from GnomeLook.org
URL = "https://www.gnome-look.org/s/Gnome/browse/cat/132/page/2/ord/latest/"
readgnomelook = ReadGnomeLook()
self.productCatalog = readgnomelook.readWebpage(URL)
#print(json.dumps(productCatalog, sort_keys=False, indent=4))
def getAllImages(self):
for i in range(0,len(self.productCatalog)):
#self.images.append(self.getImageFromWeb(self.productCatalog[i][0]['Image']))
#self.images[i] = self.getImageFromWeb(self.productCatalog[i][0]['Image'],self.images[i])
with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:
future = executor.submit(self.getImageFromWeb, self.productCatalog[i][0]['Image'], self.images[i])
# #self.images.append(future.result())
self.images[i]= future.result()
# #print(type(self.images[i]))
def do_anything(self):
print("clicked on box")
def getImageFromWeb(self, URL,image):
filename = '/tmp/'+URL.split("/")[-1]
try:
f = open(filename)
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
filename=filename,
width=100,
height=100,
preserve_aspect_ratio=False)
Gtk.Image.set_from_pixbuf(image, pixbuf)
#image.set_from_file(filename)
#print("Got the image : " + filename)
#del r
return image
except IOError:
#print("File not accessible")
r = requests.get(URL,stream=True)
if r.status_code == 200:
with open(filename,'wb') as f:
r.raw.decode_content = True
shutil.copyfileobj(r.raw, f)
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
filename=filename,
width=200,
height=200,
preserve_aspect_ratio=False)
Gtk.Image.set_from_pixbuf(image, pixbuf)
#image.set_from_file(filename)
#print("Got the image : " + filename)
return image
else:
#print("Failed to get the image : " + filename)
return None
del r
window = AppicationWindow()
#window.connect("destroy",Gtk.main_quit)
#window.show_all()
Gtk.main()
[/code]
Code works fine. But in below code, the thread doProcessing() is getting completed and then I am seeing the "Started Z thread"
print("Before 1st Show all")
self.show_all()
z = threading.Thread(target=self.doProcessing(),daemon=True)
z.start()
print("Started Z thread")
As I see, doProcessing should start in background and "Started Z thread" should be printed immediately but that's not happening.
Am I missing anything here ? Any help is appreciated.
Thanks, Debasish
z = threading.Thread(target=self.doProcessing(),daemon=True)
threading.Thread wants a function not the result of a function, thus try:
z = threading.Thread(target=self.doProcessing,daemon=True)
Related
I'm making a system that receives a list of login data (username and password), then opens each user and password in a window, consuming the same function that I receive as user and password parameters.
Below is a reduced example of what I'm doing.
from multiprocessing.dummy import Process
import PySimpleGUI as sg
from time import sleep
import threading
import concurrent.futures
from concurrent.futures import ThreadPoolExecutor
import multiprocessing as mp
from multiprocessing import Process
import os, json
from datetime import datetime, timezone
import time
import random
HOME = './Dados'
def current_location(self):
geometry = self.TKroot.geometry()
location = geometry[geometry.find('+') + 1:].split('+')
x, y = int(location[0]), int(location[1])
return(x, y+-30)
sg.Window.current_location = current_location
def function_thread(usuario, senha):
global window
window['output'].print(f'Login {usuario} e {senha}')
for i in range(5):
#print(f'Tarefas no perfil {usuario}')
#window['output'].update(str('Tarefas no perfil {usuario}'))
time.sleep(random.randint(1,2))
horaatual = datetime.today().strftime('[%H:%M:%S:%f] - ')
window['output'].print(horaatual+': Tarefas: '+ str(i))
#sleep(2)
#window.write_event_value('output', i)
#window.write_event_value('output', usuario)
window['output'].print(f'Completado {usuario}')
#sleep(1)
x=-7
y=515
def abrir_abas():
global x,y, usuario_insta, senha_insta
window2 = None
cont = 0
#list_user = [('perfil1','senha1'),('perfil2', 'senha2'),('perfil3', 'senha3'),('perfil4', 'senha4'),('perfil5','senha5'),('perfil6', 'senha6'),('perfil7', 'senha7'),('perfil8', 'senha8')]
'''
executor = concurrent.futures.ProcessPoolExecutor(8)
futures = [executor.submit(function_thread(group[0],group[1])for group in list_user)]
concurrent.futures.wait(futures)
'''
with open(os.path.join(HOME, f'Config/abas.txt'), 'r') as f:
abas = f.readlines()
lista_conta = []
for index_lista in range(len(abas)):
abas[index_lista] = abas[index_lista].rstrip('\n')
if abas[index_lista] != '' or None:
lista_conta.append(abas[index_lista])
#with concurrent.futures.ProcessPoolExecutor() as executor:
for i_lista_conta in range(len(lista_conta)):
qtd_lista_conta = int(len(lista_conta)/2)
if i_lista_conta % 2 == 0:
usuario = lista_conta[i_lista_conta].split(' ')
usuario_insta = usuario[0]
senha_insta = usuario[1]
#self.output(usuario_insta, senha_insta)
titulo = 'TAB '+usuario_insta
window2 = output(usuario_insta, senha_insta, loc=(x,y))
window2.TKroot.title(titulo)
t1 = threading.Thread(target=function_thread(usuario_insta, senha_insta), daemon=True)
t1.start()
y=y+-30
y=y
cont = cont + 1
'''
processos = []
for i_lista_conta in range(len(lista_conta)):
qtd_lista_conta = int(len(lista_conta)/2)
if i_lista_conta % 2 == 0:
usuario_insta = lista_conta[i_lista_conta]
senha_insta = lista_conta[i_lista_conta+1]
#self.output(usuario_insta, senha_insta)
titulo = 'TAB '+usuario_insta
window2 = output(loc=(x,y))
window2.TKroot.title(titulo)
#threading.Thread(target=self.thread_acao(usuario_insta, senha_insta), daemon=True).start()
p = Process(target=function_thread(usuario_insta, senha_insta))
p.start()
processos.append(p)
y=y+-30
y=y
cont = cont + 1
'''
#for process in processos:
# process.join()
'''
for i in list_user:
titulo = 'TAB '+i[0]
#with concurrent.futures.ProcessPoolExecutor() as executor:
# window2 = executor.map(output(loc=(x,y)))
window2 = output(loc=(x,y))
window2.TKroot.title(titulo)
#with concurrent.futures.ProcessPoolExecutor() as executor:
# executor.map(function_thread(i[0],i[1]))
#threading.Thread(target=function_thread(i[0],i[1]), daemon=True).start()
Process(target=function_thread(i[0],i[1])).start()
y=y+-30
y=y
cont = cont + 1
sleep(2)
'''
def output(login, senha, loc=(-7, 515))->sg.Window:
global window
voltar = 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAF8WlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTAzLTAzVDA5OjUwOjM0LTAzOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMC0wNS0wMlQxNzo1Nzo0Mi0wMzowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wNS0wMlQxNzo1Nzo0Mi0wMzowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo0MGZlY2VlMS0yYmE4LTI4NGQtYTk4NC0zYjQxNTIxNzA1ZGQiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo2MmQ1MzYwYi0zMTQ5LTI2NDAtYWZhMC03YzdiNjkzNDFkY2YiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowZDY2MTNmZi0wMGIzLWMzNDQtYTgzOS1jMTUwZTc4ZGZlODEiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjBkNjYxM2ZmLTAwYjMtYzM0NC1hODM5LWMxNTBlNzhkZmU4MSIgc3RFdnQ6d2hlbj0iMjAyMC0wMy0wM1QwOTo1MDozNC0wMzowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDIxLjAgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo0MGZlY2VlMS0yYmE4LTI4NGQtYTk4NC0zYjQxNTIxNzA1ZGQiIHN0RXZ0OndoZW49IjIwMjAtMDUtMDJUMTc6NTc6NDItMDM6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4wIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4M/LCOAAABD0lEQVQ4y43SMSvFURjH8XPvVchCScpwB2VRykIpG7u7KLeMZGGQd6BMXoDZwAtQUgaTkZKNRalPSgYZrAbn1On0v/f+Tz2d0/P0fM/z/J4nIBTWyN5rOMU5zrCD4RhrIvRKHsEFXmLiIU5wjycsJUioqCDgCtcYrYgd+D/zZQWteO/FX3J/I5acKjzGTVULAXfYiu+hHi2O4w0LZWAy/j5XIWhpl+iUgGk8YLYGYAJjVYDHmoCQNGhmApaAVpp3P0BuJSAMqiRgFSvRMRM1SCIuYr0fJKCDTyzHNX3GFNr4wG6+ur1a2MY7urjFBl5x1C85FzFBvvGFn7iyoa6IaQqb+MV+4a81hSRSe1BSbn+7uNbAq7k3vAAAAABJRU5ErkJggg=='
pause = 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAABYlAAAWJQFJUiTwAAAGvmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTA1LTAyVDE3OjQ4OjI0LTAzOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMC0wNS0wM1QxMTowNjo0NC0wMzowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wNS0wM1QxMTowNjo0NC0wMzowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo0ODFlYzZjZS1jYTNhLWIxNDgtYjlmYy0yZmZmYjVhNzI3ZTEiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo1NmE4ZDEzNC0wNmIzLWVkNGMtOWYwYi1kMjk4YmU1YTlmYzAiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo0ZmQ2MzhkYy05YzYyLTFjNGUtYjg2Ny03ZDNmOTdlMmE5YTgiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjRmZDYzOGRjLTljNjItMWM0ZS1iODY3LTdkM2Y5N2UyYTlhOCIgc3RFdnQ6d2hlbj0iMjAyMC0wNS0wMlQxNzo0ODoyNC0wMzowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDIxLjAgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo0N2MwYWRlMy02NmVjLWVjNDEtYjE1YS0zNmI2ZmZmOGEzNWYiIHN0RXZ0OndoZW49IjIwMjAtMDUtMDJUMTc6NTI6MTYtMDM6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4wIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6NDgxZWM2Y2UtY2EzYS1iMTQ4LWI5ZmMtMmZmZmI1YTcyN2UxIiBzdEV2dDp3aGVuPSIyMDIwLTA1LTAzVDExOjA2OjQ0LTAzOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+sKbvRwAAAWBJREFUOI3t1L2KFFEQBeCvZ8afBZENFmEEBU0MxGQfQBE00jfYh9DUFxBBQzMx8A00MlVMDQVRFARxXDN1BWfH6W2DPpe5jLA7xnqS+1NV51Rfuk4zmUwE6ziR/R5G+IFJznJ3CkfwK+c5PmE6Ho+NLHAW59CF4BC+YgffkrOBTRzFLDltYu+L4nrInuNYFGuhO7iV4ge4XuW0GOItLmF7hDEuhGwLr1I8x31cDvEaLuIR7uWzZxG4jdOFUJQ6vMDHqrsPOJN9F4I3ES04mXUOg7zXHpq8UY3jJTGEbe5qbFRxA/ujOyD+R85BhH+N/4T/ImGzQs2+/+ZAP9xlBD8vxXdwuBIrDlSj1AxZOEbxuy29cwxzv2lhXcXSruC13sKmuFZ3PtKbQasf+LtL6jPcyH6Kh7iJq0t5j/GuEP7EFzzBd+ymmzVs42WKWjzD+XS3m44bPM3zaLpulflfHb8BEgRaa6It0wUAAAAASUVORK5CYII='
col_v = sg.Column(
[
[sg.Button(image_data=pause, size=(None,20), key='Executar',button_color=(sg.theme_background_color(), sg.theme_background_color()), border_width=0)],
[sg.Text('')],
[sg.Button(image_data=voltar, key='voltar',tooltip="Voltar",button_color=(sg.theme_background_color(), sg.theme_background_color()), border_width=0)],
], k='c_v', visible=True)
main_layout =[
#[sg.Titlebar(title = texto, key = 'titulo')],
[sg.StatusBar('O Rei Dos BOTs',justification='center', font='Arial 12')],
[col_v,sg.Multiline(size=(20,2),expand_x=True, expand_y=True, key='output')],
[sg.StatusBar('INICIANDO', k='status+', auto_size_text=True, justification='center', font=('Arial', 12, 'bold'), text_color='#FFFFFF', background_color=None, s=(None, 1))],
]
window = sg.Window('ABA', size=(253,181), finalize=True, location=loc, background_color=None, auto_size_text=True, font='Arial 7', resizable=True, auto_size_buttons=False,right_click_menu = False, margins=(1, 1)).Layout(main_layout).Finalize()
#with concurrent.futures.ProcessPoolExecutor() as executor:
# executor.map(function_thread(login, senha))
return window
def main_output():
global window, usuario_insta, senha_insta
sg.theme('Topanga')
layout = [ [sg.Text('Janela')],
[sg.Text(size=(10,1), key='-OUT-')],
[sg.Button('Executar'), sg.Button('Sair')],
[sg.StatusBar('INICIANDO', k='status', auto_size_text=True, justification='center', font='Arial 12', text_color='#FFFFFF', background_color=None, s=(None, 1))],
]
window = sg.Window('Janela principal', layout, size=(253,181), use_default_focus=False,finalize=True, use_custom_titlebar=False, no_titlebar=False)
while True:
event, values = window.read()
if event == sg.WIN_CLOSED or event == 'Sair':
break
if event == 'Executar':
window.hide()
#with ThreadPoolExecutor() as executor:
# executor.map(abrir_abas())
#threading.Thread(target=abrir_abas(), daemon=True).start()
abrir_abas()
#threading.Thread(target=abrir_abas(), args=(window,), daemon=False).start()
#with concurrent.futures.ProcessPoolExecutor() as executor:
# executor.map(abrir_abas())
if event == 'Ok':
print('Completado')
if event == 'voltar':
print('Voltar')
window.close()
#threading.Thread(target=main_output(), args=(window,), daemon=True).start()
main_output()
I can only do this by opening the first window and waiting for the function to complete and then opening the second window and so on.
The function is looping and can take more than 24 hours.
I am trying to create a calendar app in python.As for now I have manage to create the Gtk.window, a Gtk.Notebook with two pages (page1 month calendar, page 2 week calendar) added to the Gtk.window.I have used Gtk.Calendar for month and from signal 'day-selected' I used a function to get_date().Also I used a Gtk.TextView with Gtk.TextBuffer to input text (as a note).
First question : how to connect the date with the input text and store it in a list or dictionary so i can save it later to a file.
Second:I have activate details with self.calendar.set_property("show-details", True) and defined a function self.calendar.set_detail_func(self.cal_entry) for details.
def cal_entry(self, calendar, year, month, date):
if self.textbuffer is not None:
self.calendar.mark_day(self.day)
How to mark the days with notes(text in Gtk.TextBuffer) because the above function does not work correctly.
Below is a snippet of the code:
class Window(Gtk.ApplicationWindow):
def __init__(self, app):
super(Window, self).__init__(title="Live Calendar", application=app)
self.set_default_size(-1, 400)
self.set_resizable(False)
self.set_border_width(10)
self.create_notebook()
self.entry()
self.refresh_icon()
def refresh_icon(self):
#refreshing the icon of the app
print(get_filename())
subject_lines = read_file(subject)
index = [i for i in range(len(subject_lines)) \
if subject_lines[i].startswith("Icon=")][0]
subject_lines[index] = "Icon=" + get_filename() + "\n"
write_file(subject, subject_lines)
self.set_icon_from_file(get_filename())
timer = threading.Timer(60, self.refresh_icon)
timer.start()
def create_notebook(self):
# Open Buttton
self.openbutton = Gtk.Button("Open")
self.openbutton.set_tooltip_text("Open Notes")
self.openbutton.connect("clicked", self.on_open_clicked)
# Save Button
self.savebutton = Gtk.Button("Save")
self.savebutton.set_tooltip_text("Save Notes")
self.savebutton.connect("clicked", self.on_save_clicked)
# Header
self.header = Gtk.HeaderBar(title="Live Calendar")
self.header.set_property("show_close_button", True)
self.header.pack_start(self.openbutton)
self.header.pack_start(self.savebutton)
self.set_titlebar(self.header)
#page1 month calendar
self.page1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
self.calendar = Gtk.Calendar()
self.calendar.set_property("show-week-numbers", True)
self.calendar.set_detail_height_rows(2)
self.calendar.set_detail_width_chars(2)
self.calendar.set_property("show-details", True)
self.calendar.set_detail_func(self.cal_entry)
self.__connect_signals()
self.page1.add(self.calendar)
#note taking
self.sw = Gtk.ScrolledWindow()
self.sw.set_hexpand(True)
self.sw.set_vexpand(True)
self.textview = Gtk.TextView()
self.textview.set_editable(True)
self.textview.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
self.textbuffer = self.textview.get_buffer()
self.sw.add(self.textview)
self.page1.pack_start(self.sw, True, True, 0)
#page2 week calendar
......a lot of code.......
#create notebook
self.notebook = Gtk.Notebook()
self.notebook.set_tab_pos(0)
self.notebook.append_page(self.page1, Gtk.Label('Month'))
self.notebook.append_page(self.page2, Gtk.Label('Week'))
self.add(self.notebook)
def __connect_signals(self):
self.day_selected_handle = self.calendar.connect('day-selected', self.entry)
def entry(self, *args):
self.page1.remove(self.label)
self.year, self.month, self.day = self.calendar.get_date()
self.month = self.month + 1
self.entrydate = datetime.date(self.year, self.month, self.day)
self.notedate = self.entrydate.strftime("%d/%m/%y")
self.text = self.entrydate.strftime("%d/%m/%y write your notes here...")
self.label = Gtk.Label(self.text)
self.page1.pack_start(self.label, False, False, 0)
self.show_all()
def cal_entry(self, calendar, year, month, date):
if self.textbuffer is not None:
self.calendar.mark_day(self.day)
#save into file
def on_save_clicked(self, widget):
dialog = Gtk.FileChooserDialog("Save file", self,
Gtk.FileChooserAction.SAVE,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
response = dialog.run()
if response == Gtk.ResponseType.OK:
save_file = dialog.get_filename()
start_iter = self.textbuffer.get_start_iter()
end_iter = self.textbuffer.get_end_iter()
text = self.textbuffer.get_text(start_iter, end_iter, True)
with open(save_file, 'w') as f:
f.write(text)
elif response == Gtk.ResponseType.CANCEL:
dialog.destroy()
dialog.destroy()
# open and read the file
def on_open_clicked(self, widget):
dialog = Gtk.FileChooserDialog("Please choose a file", self,
Gtk.FileChooserAction.OPEN,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
filter_text = Gtk.FileFilter()
filter_text.set_name("Text files")
filter_text.add_mime_type("text/plain")
dialog.add_filter(filter_text)
response = dialog.run()
if response == Gtk.ResponseType.OK:
selected_file = dialog.get_filename()
with open(selected_file, 'r') as f:
data = f.read()
self.textbuffer.set_text(data)
elif response == Gtk.ResponseType.CANCEL:
dialog.destroy()
dialog.destroy()
def quitApp(self, par):
app.quit()
class Application(Gtk.Application):
def __init__(self):
super(Application, self).__init__()
def do_activate(self):
self.win = Window(self)
self.win.show_all()
def do_startup(self):
Gtk.Application.do_startup(self)
app = Application()
app.run(sys.argv)
Any help will be appriciated.
Thanks in advance
For the first question: Depends, but probably a dictionary indexed by a triple for y/m/d
d = dict()
d[(2019, 10, 1)] = "your notes..."
Second question: The way details work is that cal_entry(self, calendar, year, month, day) must return a string to be displayed below the day, in the calendar. So you have to get the text from the dictionary above. You can do that with:
def cal_entry(self, calendar, year, month, day):
self.calendar.mark_day(day)
return dict.get((year, month, day))
Maybe this sample code can help:
#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class NotePopover(Gtk.Popover):
def __init__(self, app):
Gtk.Popover.__init__(self)
self.set_size_request(200, 300)
grid = Gtk.Grid()
grid.set_orientation(Gtk.Orientation.VERTICAL)
scrolled_window = Gtk.ScrolledWindow()
self.text_view = Gtk.TextView()
scrolled_window.add(self.text_view)
grid.add(scrolled_window)
button = Gtk.Button(label='Add')
grid.add(button)
grid.show_all()
self.add(grid)
scrolled_window.set_vexpand(True)
scrolled_window.set_hexpand(True)
button.connect('clicked', self.on_button_clicked)
self.app = app
def do_popup(self):
self.popup()
def on_button_clicked(self, widget):
year = app.win.calendar.get_property('year')
month = app.win.calendar.get_property('month')
day = app.win.calendar.get_property('day')
start_iter = self.text_view.get_buffer().get_start_iter()
end_iter = self.text_view.get_buffer().get_end_iter()
app.notes_dict[(year, month, day)] = self.text_view.get_buffer().get_text(start_iter, end_iter, True)
app.win.do_update()
self.text_view.get_buffer().set_text("")
self.popdown()
class Window(Gtk.Window):
def __init__(self, app):
Gtk.Window.__init__(self)
self.set_title('Test GtkCalendar')
self.set_default_size(400, 300)
header_bar = Gtk.HeaderBar()
header_bar.set_show_close_button(True)
self.set_titlebar(header_bar)
button = Gtk.Button(label="Add note")
button.get_style_context().add_class('suggested-action')
button.connect('clicked', self.on_button_clicked)
header_bar.pack_start(button)
grid = Gtk.Grid()
grid.set_orientation(Gtk.Orientation.VERTICAL)
self.calendar = Gtk.Calendar()
scrolled_window = Gtk.ScrolledWindow()
self.text_view = Gtk.TextView()
self.add(grid)
grid.add(self.calendar)
grid.add(scrolled_window)
scrolled_window.add(self.text_view)
self.calendar.set_hexpand(True)
self.calendar.set_halign(Gtk.Align.CENTER)
scrolled_window.set_vexpand(True)
scrolled_window.set_hexpand(True)
self.popover = NotePopover(app)
self.popover.set_relative_to(button)
self.connect('destroy', Gtk.main_quit)
self.calendar.connect('day-selected', self.calendar_changed)
self.calendar.connect('month-changed', self.calendar_changed)
self.show_all()
self.app = app
def on_button_clicked(self, widget):
self.popover.do_popup()
def calendar_changed(self, widget):
self.do_update()
def do_update(self):
year = app.win.calendar.get_property('year')
month = app.win.calendar.get_property('month')
day = app.win.calendar.get_property('day')
text = app.notes_dict.get((year, month, day))
if text is not None:
self.text_view.get_buffer().set_text(text)
else:
self.text_view.get_buffer().set_text("")
class Application:
def __init__(self):
self.notes_dict = dict()
self.win = Window(self)
if __name__ == '__main__':
app = Application()
Gtk.main()
I've implemented news website scraper that scrapes by using Selenium web driver to access dynamic web pages and BeautifulSoup to retrieve the content. While parsing websites, I'm also writing scraped data to MongoDB storage and downloading pictures. I want to implement full news search by given category or by text, that appears in the news content. What can be the suggestions in terms of parallelization/adding async code to speed up the performance?
# -*- coding: utf-8 -*-
import os
import json
import requests
from bs4 import BeautifulSoup
from mongo_setup import Database
import gridfs
from selenium import webdriver
from selenium.common.exceptions import WebDriverException
import time
import logging
import re
import pymongo
PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
DRIVER_BIN = os.path.join(PROJECT_ROOT, "bin/chromedriver")
class Scraper:
tsn_resource = 'https://tsn.ua/'
ukrnet_resource = 'https://www.ukr.net/'
db_name = 'scraper_db'
category_coll = 'categories'
articles_coll = 'articles'
def __init__(self, limit=10):
self.limit = limit # max number of articles per category
self.db = Database(self.db_name).connect_db()
self.category_coll = self.init_collection(self.category_coll)
self.articles_coll = self.init_collection(self.articles_coll)
self.logger = self.init_logger()
self.driver = webdriver.Chrome(executable_path = DRIVER_BIN)
self.image_storage = os.path.join(PROJECT_ROOT, "image_storage/")
def init_logger(self):
'''
Initialize log file.
'''
logger = logging.getLogger('scraper_app')
logger.setLevel(logging.INFO)
# create a file handler
handler = logging.FileHandler('scraper_logfile.log')
handler.setLevel(logging.INFO)
# create a logging format
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
# add the handlers to the logger
logger.addHandler(handler)
return logger
def init_collection(self, name):
if name in self.db.collection_names():
self.db[name].drop()
return self.db[name]
def insert_one_to_collection(self, data, collection):
try:
collection.insert_one(data)
except pymongo.errors.DuplicateKeyError:
pass
def insert_many_to_collection(self, data, collection):
try:
collection.insert_many(data)
except pymongo.errors.DuplicateKeyError:
pass
def download_image(self, image_url):
'''
download images from news articles
to local storage
'''
if not image_url.startswith(("data:image", "javascript")):
local_filename = image_url.split('/')[-1].split("?")[0]
r = requests.get(image_url, stream=True, verify=False)
with open(self.image_storage + local_filename, 'wb') as f:
for chunk in r.iter_content(chunk_size=1024):
f.write(chunk)
def upload_image_to_mongo(self, image_url):
response = requests.get(image_url, stream=True)
fs = gridfs.GridFS(self.db)
img = response.raw.read()
fs.put(img, filename=local_filename)
def get_page_content(self, url):
try:
self.driver.get(url)
except WebDriverException:
self.driver = webdriver.Chrome(executable_path = DRIVER_BIN)
page = self.driver.page_source
return page
def parse_page_content(self, url, parser_lib):
page_obj = self.get_page_content(url)
soup = BeautifulSoup(page_obj, parser_lib)
return soup
def tsn_categories(self):
categories = self.gather_categories(self.tsn_resource, 'ul.c-app-nav-more-list li a')
return categories
def ukrnet_categories(self):
categories = self.gather_categories(self.ukrnet_resource, 'h2.feed__section--title a')
return categories
def gather_categories(self, url, selector):
categories = []
soup = self.parse_page_content(url, "html.parser")
all_categories = soup.select(selector)
for item in all_categories:
category = {}
link = str(item.attrs.get('href'))
if link.startswith('javascript'):
continue
if not link.startswith('https:'):
link = 'https:' + link
category['link'] = link
category['name'] = item.get_text().strip()
categories.append(category)
self.insert_many_to_collection(categories, self.category_coll)
return categories
def search_by_category(self, category_name):
category_name = category_name.decode('utf-8')
category_list = []
category_list += self.tsn_categories()
category_list += self.ukrnet_categories()
category_obj = next(item for item in category_list if item['name'] == category_name)
link = category_obj['link']
if 'ukr.net' in link:
articles = self.get_ukrnet_articles(category_name, link)
else:
articles = self.get_tsn_articles(category_name, link)
return articles
def get_ukrnet_articles(self, category_name, url):
'''
retrieve all articles from ukr.net by given category link
'''
count = 0
result = []
soup = self.parse_page_content(url, "html.parser")
all_articles = soup.select('div.im-tl a')
for item in all_articles:
if count <= self.limit:
article = {}
link = item.attrs.get('href')
article['link'] = link
article['category'] = category_name
article['content'] = item.contents[0].encode('utf-8')
result.append(article)
self.insert_one_to_collection(article, self.articles_coll)
else:
break
count += 1
return result
def get_tsn_articles(self, category_name, url):
'''
retrieve all articles from tsn.ua by given category link
'''
count = 0
result = []
data = [] # temporary storage
# first parse through the list of articles
soup = self.parse_page_content(url, "html.parser")
all_articles = soup.select('div.c-entry-embed a.c-post-img-wrap')
for item in all_articles:
# iterate limit amount of articles
if count <= self.limit:
article = {}
link = item.attrs.get('href')
img_src = item.find('img').get('src')
if link.endswith(".html"):
article['link'] = link
if img_src is not None:
article['img_src'] = img_src
self.download_image(img_src)
article['category'] = category_name
data.append(article)
count += 1
else:
break
# then iterate over each article
for article in data:
new_soup = self.parse_page_content(article['link'], "html5lib")
news_content = new_soup.select('div.e-content p')
text_content = [] # article content
for chunk in news_content:
text_content.append(chunk.get_text().strip(''))
article_text = ' '.join(text_content)
news_header = new_soup.select('div.c-post-meta h1') # article title
if news_header:
header_text = "".join(news_header[0].contents)
article_image = new_soup.find('figure', class_='js-lightgallery')
if article_image:
img_src = article_image.find('img').get('src') # articles image
self.download_image(img_src)
news_chunk = {}
news_chunk['category'] = article['category']
news_chunk['link'] = article['link']
news_chunk['title'] = header_text
# news_chunk['title'] = ''
news_chunk['content'] = article_text
news_chunk['images'] = []
if 'img_src' in article:
news_chunk['images'].append(article['img_src']) # caption image
if article_image:
news_chunk['images'].append(img_src) # article image
result.append(news_chunk)
self.insert_one_to_collection(news_chunk, self.articles_coll)
return result
def search_by_text(self, text):
category_links = []
category_links += self.ukrnet_categories()
category_links += self.tsn_categories()
result = self.website_search_by_text(text, category_links)
return result
def website_search_by_text(self, text_searched, category_links):
result = []
text_searched = text_searched.decode('utf-8')
for link in category_links:
article = {}
soup = self.parse_page_content(link['link'], "html.parser")
all_articles = soup.find_all('a', text=re.compile(text_searched))
for item in all_articles:
article['link'] = item.attrs.get('href')
article['category'] = link['name']
article['content'] = (item.contents[0].strip()).encode('utf-8')
self.insert_one_to_collection(article, self.articles_coll)
result.append(article)
return result
def collect_ukrnet_articles(self):
'''
outdated
'''
categories = self.ukrnet_categories()
for category in categories:
count = 0
soup = self.parse_page_content(category['link'], "html.parser")
all_articles = soup.select('div.im-tl a')
for item in all_articles:
# only 10 first articles
if count < self.limit:
article = {}
link = item.attrs.get('href')
article['link'] = link
article['category'] = category['name']
article['content'] = item.contents[0].encode('utf-8')
self.insert_one_to_collection(article, self.articles_coll)
else:
break
count += 1
def run(self):
self.search_by_category('Economics', self.tsn_categories())
self.search_by_text('Economics')
self.driver.quit()
if __name__ == '__main__':
scraper = Scraper()
scraper.run()
scrapy is a solid python framework that automatically does things async/parallel.
There's also multiprocessing that's been conveniently put into one package.
And then there's multithreading, also conveniently put into one package.
With the multithreading library there's a way to call the function you're trying to thread with map() and then pass the lists/variables you're trying to use with it. map(your_func, your_list)
I don't remember the exact link, or structure for it, but it's a quick google search away. Really makes it easier.
I am writing a fuzzy search application which finds words in text even if words have mistakes. I wrote gui form but it freezed because of heavy calculation. So I created class inherited from QThread and emitted signals from it to gui so that progress bar started to work and gui form wasn't freezed anymore. BUT I should also create console version of this application where I don't need gui form but I need methods that written in class inherited from QThread. But it's impossible without using PyQT library which strange to use in console version. So I don't know how to solve this problem. My teacher recommended to use threading but I didn't find how to emit signals from Thread class as I colud do in QThread.
That's a QThread class
import text_methods
from PyQt5.QtCore import pyqtSignal, QThread
class FuzzySearch(QThread):
sig_words_count = pyqtSignal(int)
sig_step = pyqtSignal(int)
sig_done = pyqtSignal(bool)
sig_insertions = pyqtSignal(str)
sig_insertions_indexes = pyqtSignal(list)
def __init__(self, text, words, case_sensitive):
super().__init__()
self.text = text
self.words = words
self.case_sensitive = case_sensitive
self.insertions_indexes = {}
self.text_dict = {}
def run(self):
self.get_insertions_info(self.text, self.words)
def find_insertions_of_word(self, word, word_number):
word_insertions = {}
for textword in self.text_dict.keys():
if text_methods.is_optimal_distance(word, textword):
word_insertions[textword] = self.text_dict[textword]
for index in self.text_dict[textword]:
self.insertions_indexes[index] = index + len(textword)
self.sig_step.emit(word_number)
return word_insertions
'''Get information about insertions of words in the text'''
def find_insertions(self, text, words):
word_number = 1
insertions = {}
self.text_dict = text_methods.transform_text_to_dict(text, self.case_sensitive)
words_list = text_methods.transform_words_to_list(words, self.case_sensitive)
self.sig_words_count.emit(len(words_list))
for word in words_list:
print(word_number)
insertions[word] = self.find_insertions_of_word(word, word_number)
word_number += 1
self.insertions_indexes = sorted(self.insertions_indexes.items())
return insertions
'''Get information about insertions of words in the text in special format'''
def get_insertions_info(self, text, words):
insertions = self.find_insertions(text, words)
insertions_info = ''
for word in insertions.keys():
insertions_info += 'Вы искали слово "' + word + '"\n'
if len(insertions[word]) == 0:
insertions_info += ' По этому запросу не было найдено слов\n'
else:
insertions_info += ' По этому запросу были найдены слова:\n'
for textword in insertions[word].keys():
insertions_info += ' "' + textword + '" на позициях: '
insertions_info += ", ".join([str(i) for i in insertions[word][textword]])
insertions_info += '\n'
self.sig_done.emit(True)
self.sig_insertions.emit(insertions_info)
self.sig_insertions_indexes.emit(self.insertions_indexes)
self.quit()
As you see there are a lot of emitted signals that I transfer to gui module where they connect to slots in class FindButton in method find_insertions:
from PyQt5.QtWidgets import QHBoxLayout, QVBoxLayout, QApplication, QWidget,\
QLabel, QPushButton, QTextEdit, QFileDialog,\
QMessageBox, QProgressBar, QCheckBox
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
from fuzzysearch import FuzzySearch
import sys
class OpenButton(QPushButton):
def __init__(self, name, font, textedit):
super().__init__(name, font=font)
self.textedit = textedit
self.clicked.connect(self.open_dialog)
def open_dialog(self):
fname = QFileDialog.getOpenFileName(self, 'Open file', '/home')
if fname[0]:
with open(fname[0], 'r') as f:
data = f.read()
self.textedit.setText(data)
class FindButton(QPushButton):
def __init__(self, name, font, text, words, result, window):
super().__init__(name, font=font)
self.window = window
self.textedit = text
self.wordsedit = words
self.resultedit = result
self.checkbox = window.case_sensitive_checkbox
self.clicked.connect(self.find_insertions)
def find_insertions(self):
text = self.textedit.toPlainText()
words = self.wordsedit.toPlainText()
if text == '':
QMessageBox.information(self, 'Нет текста',
'Текст не был введен. \nВведите текст.')
elif words == '':
QMessageBox.information(self, 'Нет слов',
'Слова не были введены. \nВведите слова через запятую.')
else:
self.setDisabled(True)
self.text_editor = TextEditor(text, self.textedit)
self.fuzzy_search = FuzzySearch(text, words, self.checkbox.checkState())
self.fuzzy_search.sig_words_count.connect(self.window.progress_bar.setMaximum)
self.fuzzy_search.sig_step.connect(self.window.progress_bar.setValue)
self.fuzzy_search.sig_done.connect(self.setEnabled)
self.fuzzy_search.sig_insertions.connect(self.resultedit.setText)
self.fuzzy_search.sig_insertions_indexes.connect(self.text_editor.mark)
self.fuzzy_search.start()
class TextEditor:
def __init__(self, text, textedit):
self.text = text
self.textedit = textedit
def mark(self, to_mark):
self.textedit.clear()
current_index = 0
for item in to_mark:
self.write_not_marked_text(self.text[current_index:item[0]])
self.write_marked_text(self.text[item[0]:item[1]])
current_index = item[1]
self.write_not_marked_text(self.text[current_index:])
def write_not_marked_text(self, text):
font = QFont("Times", 10)
font.setItalic(False)
font.setBold(False)
self.textedit.setCurrentFont(font)
self.textedit.setTextColor(Qt.black)
self.textedit.insertPlainText(text)
def write_marked_text(self, text):
font = QFont("Times", 10)
font.setItalic(True)
font.setBold(True)
self.textedit.setCurrentFont(font)
self.textedit.setTextColor(Qt.red)
self.textedit.insertPlainText(text)
class Window(QWidget):
def __init__(self, font):
super().__init__()
self.standard_font = font
self.text_edit_font = QFont("Times", 10)
text_label = QLabel("Введите или откройте текст",
font=self.standard_font)
words_label = QLabel("Введите или откройте слова (через запятую)",
font=self.standard_font)
result_label = QLabel("Результат",
font=self.standard_font)
text_edit = QTextEdit(font=self.text_edit_font)
words_edit = QTextEdit(font=self.text_edit_font)
result_edit = QTextEdit(font=self.text_edit_font)
self.case_sensitive_checkbox = QCheckBox('Учитывать регистр')
self.case_sensitive_checkbox.setFont(self.standard_font)
self.progress_bar = QProgressBar()
self.progress_bar.setValue(0)
open_btn1 = OpenButton("Открыть", self.standard_font, text_edit)
open_btn2 = OpenButton("Открыть", self.standard_font, words_edit)
find_btn = FindButton("Найти слова в тексте", self.standard_font,
text_edit, words_edit, result_edit, self)
text_label_box = QHBoxLayout()
text_label_box.addWidget(text_label, alignment=Qt.AlignLeft)
text_label_box.addWidget(open_btn1, alignment=Qt.AlignRight)
words_label_box = QHBoxLayout()
words_label_box.addWidget(words_label, alignment=Qt.AlignLeft)
words_label_box.addWidget(open_btn2, alignment=Qt.AlignRight)
words_box = QVBoxLayout()
words_box.addLayout(words_label_box)
words_box.addWidget(words_edit)
result_box = QVBoxLayout()
result_box.addWidget(result_label, alignment=Qt.AlignLeft)
result_box.addWidget(result_edit)
bottom_box = QHBoxLayout()
bottom_box.addLayout(words_box)
bottom_box.addLayout(result_box)
find_and_progress_box = QHBoxLayout()
find_and_progress_box.addWidget(find_btn, alignment=Qt.AlignLeft)
find_and_progress_box.addWidget(self.case_sensitive_checkbox)
find_and_progress_box.addWidget(self.progress_bar)
main_box = QVBoxLayout()
main_box.addLayout(text_label_box)
main_box.addWidget(text_edit)
main_box.addLayout(bottom_box)
main_box.addLayout(find_and_progress_box)
self.setLayout(main_box)
self.setGeometry(300, 300, 1100, 700)
self.setWindowTitle('Нечеткий поиск')
self.show()
def start_application():
app = QApplication(sys.argv)
w = Window(QFont("Times", 12))
sys.exit(app.exec_())
And it works perfectly. But it will not work in console version because without QEventLoop QThread will not work:
import fuzzysearch
class ConsoleVersion():
def __init__(self, text, words):
self.text = text
self.words = words
def search_words_in_text(self):
with self.text:
with self.words:
self.f = fuzzysearch.FuzzySearch(self.text.read(), self.words.read(), False)
self.f.sig_insertions.connect(self.get_insertions)
self.f.start()
def get_insertions(self, insertions):
print(insertions)
and in main file I wrote parsing arguments and choise between two versions
import argparse
import gui
import console_version
def parse_args():
parser = argparse.ArgumentParser(description='Fuzzy search in text')
parser.add_argument('-g', '--graphics', help='graphical version', action='store_true')
parser.add_argument('-c', '--console', help='console version', nargs=2, type=argparse.FileType('r'), metavar=('TEXTFILE', 'WORDSFILE'))
return parser.parse_args()
if __name__ == '__main__':
args = parse_args()
if args.graphics:
gui.start_application()
if args.console:
cv = console_version.ConsoleVersion(args.console[0], args.console[1])
cv.search_words_in_text()
and module text_methods:
from re import split, sub
def transform_text_to_dict(text, case_sensitive):
text_dict = {}
index = 0
if case_sensitive:
splitted_text = split("[^'а-яА-ЯA-Za-z0-9_-]", text)
else:
splitted_text = split("[^'а-яА-ЯA-Za-z0-9_-]", text.lower())
for element in splitted_text:
if element not in text_dict:
text_dict[element] = []
text_dict[element].append(index)
index += len(element) + 1
return text_dict
def transform_words_to_list(words, case_sensitive):
words = sub("^\s+|\n|\r|\s+$", '', words)
if case_sensitive:
return split(' *, *', words)
else:
return split(' *, *', words.lower())
'''Damerau-Levenstein'''
def find_distance(word1: str, word2: str):
len1, len2 = len(word1), len(word2)
if len1 > len2:
word1, word2 = word2, word1
len1, len2 = len2, len1
current_row = range(len1 + 1)
previous_row = range(len1 + 1)
pre_previous_row = range(len1 + 1)
for i in range(1, len2 + 1):
if i == 1:
previous_row, current_row = current_row, [i] + [0] * len1
else:
pre_previous_row, previous_row, current_row = previous_row, current_row, [i] + [0] * len1
for j in range(1, len1 + 1):
add = previous_row[j] + 1
delete = current_row[j - 1] + 1
change = previous_row[j - 1]
if word1[j - 1] != word2[i - 1]:
change += 1
if word1[j - 1] == word2[i - 2] and word1[j - 2] == word2[i - 1]:
transpose = pre_previous_row[j - 2] + 1
current_row[j] = min(add, delete, change, transpose)
else:
current_row[j] = min(add, delete, change)
return current_row[len1]
def is_optimal_distance(word1 : str, word2 : str):
distance = find_distance(word1, word2)
l = min(len(word1), len(word2))
return distance <= l // 4
so what can you advise me?
Qt so that to handle the tasks always needs a loop that is created internally for it one must construct an object of the type QCoreApplication, QGuiApplication or QApplication, this needs it for example for this case for QThread that is not a thread but a thread handler for that is monitoring the state of the thread, if you do not place the application is closed immediately since the run method is not executed in the main thread.
if args.console:
app = QCoreApplication(sys.argv)
cv = console_version.ConsoleVersion(args.console[0], args.console[1])
cv.search_words_in_text()
sys.exit(app.exec_())
The following block of code is part of the class Dataview(QWidget), which is called in main.py. It creates 3 tables; bids, asks and history and automaticly fetches data from various exchanges via API.
The application works, but when viewing in Task Manager my memory usage keeps getting bigger and bigger - which I think is due to way I run these threads. What am I doing wrong here?
EDIT:
MAIN.PY
import sys
from PyQt4 import QtCore, QtGui, Qt
from datetime import datetime
import time
import thread
from apis import API_Bitstamp_usd
class Dataview(QtGui.QFrame):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setFixedSize(152,730)
self.setStyleSheet('background: #444;background-color: QLinearGradient(x1:0, y1:0,x2:0, y2:1,stop:1 #212121,stop:0.4 #343434/*,stop:0.2 #343434,stop:0.1 #ffaa00*/);margin-bottom:-1px;padding-bottom:1px;')
self.setup_table_asks()
self.setup_table_bids()
self.setup_table_hist()
self.setup_label()
self.start_threads()
def setup_table_asks(self):
self.table_asks = QtGui.QTableWidget(self)
self.table_asks.setGeometry(10,30,132,180)
self.table_asks.setStyleSheet('color: lightblue; background: #444;background-color: QLinearGradient(x1:0, y1:0,x2:0, y2:1,stop:1 #212121,stop:0.4 #343434/*,stop:0.2 #343434,stop:0.1 #ffaa00*/);margin-bottom:-1px;padding-bottom:1px;')
self.table_asks.setFrameShadow(QtGui.QFrame.Raised)
self.table_asks.horizontalHeader().hide()
self.table_asks.verticalHeader().hide()
self.table_asks.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.table_asks.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.table_asks.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.table_asks.setSelectionMode(QtGui.QAbstractItemView.NoSelection)
self.table_asks.setShowGrid(False)
self.table_asks.setRowCount(300)
self.table_asks.setColumnCount(3)
self.table_asks.setColumnWidth(0,50)
self.table_asks.setColumnWidth(1,40)
self.table_asks.setColumnWidth(2,40)
self.table_asks.setCursor(QtCore.Qt.SizeVerCursor)
self.table_asks.scrollToBottom()
def setup_table_bids(self):
self.table_bids = QtGui.QTableWidget(self)
self.table_bids.setGeometry(10,230,132,180)
self.table_bids.setStyleSheet('color: lightblue; background: #444;background-color: QLinearGradient(x1:0, y1:0,x2:0, y2:1,stop:1 #212121,stop:0.4 #343434/*,stop:0.2 #343434,stop:0.1 #ffaa00*/);margin-bottom:-1px;padding-bottom:1px;')
self.table_bids.setFrameShadow(QtGui.QFrame.Raised)
self.table_bids.horizontalHeader().hide()
self.table_bids.verticalHeader().hide()
self.table_bids.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.table_bids.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.table_bids.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.table_bids.setSelectionMode(QtGui.QAbstractItemView.NoSelection)
self.table_bids.setShowGrid(False)
self.table_bids.setRowCount(300)
self.table_bids.setColumnCount(3)
self.table_bids.setColumnWidth(0,50)
self.table_bids.setColumnWidth(1,40)
self.table_bids.setColumnWidth(2,40)
self.table_bids.setCursor(QtCore.Qt.SizeVerCursor)
def setup_table_hist(self):
self.table_hist = QtGui.QTableWidget(self)
self.table_hist.setGeometry(10,414,132,206)
self.table_hist.setStyleSheet('color: lightblue;background: #444;background-color: QLinearGradient(x1:0, y1:0,x2:0, y2:1,stop:1 #212121,stop:0.4 #343434/*,stop:0.2 #343434,stop:0.1 #ffaa00*/);margin-bottom:-1px;padding-bottom:1px;')
self.table_hist.setFrameShadow(QtGui.QFrame.Raised)
self.table_hist.horizontalHeader().hide()
self.table_hist.verticalHeader().hide()
self.table_hist.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.table_hist.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.table_hist.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.table_hist.setSelectionMode(QtGui.QAbstractItemView.NoSelection)
self.table_hist.setShowGrid(False)
self.table_hist.setRowCount(150)
self.table_hist.setColumnCount(3)
self.table_hist.setColumnWidth(0,45)
self.table_hist.setColumnWidth(1,45)
self.table_hist.setColumnWidth(2,40)
self.table_hist.setCursor(QtCore.Qt.SizeVerCursor)
def setup_label(self):
self.label = QtGui.QLabel(self)
self.label.setGeometry(10,210,132,20)
self.label.setFrameShadow(QtGui.QFrame.Sunken)
self.label.setText('last_price')
self.label.setStyleSheet('color: lightblue;background: #444;background-color: QLinearGradient(x1:0, y1:0,x2:0, y2:1,stop:1 #212121,stop:0.4 #343434/*,stop:0.2 #343434,stop:0.1 #ffaa00*/);margin-bottom:-1px;padding-bottom:1px;')
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.title = QtGui.QLabel(self)
self.title.setGeometry(10,10,132,20)
self.title.setStyleSheet('color: silver;background: #444;background-color: QLinearGradient(x1:0, y1:0,x2:0, y2:1,stop:1 #212121,stop:0.4 #343434/*,stop:0.2 #343434,stop:0.1 #ffaa00*/);margin-bottom:-1px;padding-bottom:1px;')
self.title.setText('Bitstamp')
self.title.setAlignment(QtCore.Qt.AlignCenter)
def start_threads(self):
thread.start_new_thread(self.fill_table_bids, ())
thread.start_new_thread(self.fill_table_asks, ())
thread.start_new_thread(self.fill_table_hist, ())
def fill_table_bids(self):
while True:
ticker = API_Bitstamp_usd()
x = ticker.get_depth()
bids = x[0]
row = 0
depth = 0
for i in bids:
price = i[0]
qty = i[1]
depth = format(float(depth) + float(qty), '.1f')
self.table_bids.setRowHeight(row, 12)
tw = QtGui.QTableWidgetItem(str(price))
self.table_bids.setItem(row,0,tw)
tw = QtGui.QTableWidgetItem(str(qty))
tw.setTextAlignment(5)
self.table_bids.setItem(row,1,tw)
tw = QtGui.QTableWidgetItem(str(depth))
tw.setTextAlignment(5)
self.table_bids.setItem(row,2,tw)
row = row + 1
time.sleep(2)
def fill_table_asks(self):
while True:
ticker = API_Bitstamp_usd()
x = ticker.get_depth()
asks = x[1]
row = 0
depth = 0
for i in asks[:300]:
depth = depth + float(i[1])
for i in reversed(asks[:300]):
price, qty = i[0], i[1]
depth = format(float(depth) - float(qty), '.1f')
self.table_asks.setRowHeight(row, 12)
tw = QtGui.QTableWidgetItem(str(price))
self.table_asks.setItem(row,0,tw)
tw = QtGui.QTableWidgetItem(str(qty))
tw.setTextAlignment(5)
self.table_asks.setItem(row,1,tw)
if depth > 0:
tw = QtGui.QTableWidgetItem(str(depth))
tw.setTextAlignment(5)
self.table_asks.setItem(row,2,tw)
row = row + 1
time.sleep(2)
def fill_table_hist(self):
while True:
ticker = API_Bitstamp_usd()
x = ticker.get_history()
row = 0
for i in x:
timestamp = datetime.fromtimestamp(float(i[0])).strftime('%H:%M:%S')
price = i[1]
qty = i[2]
type = i[3]
self.table_hist.setRowHeight(row, 12)
tw = QtGui.QTableWidgetItem(str(timestamp))
self.table_hist.setItem(row,0,tw)
tw = QtGui.QTableWidgetItem(str(price))
tw.setTextAlignment(5)
self.table_hist.setItem(row,1,tw)
tw = QtGui.QTableWidgetItem(str(qty))
tw.setTextAlignment(5)
if type == 'sell':
tw.setTextColor(QtGui.QColor('red'))
else:
tw.setTextColor(QtGui.QColor('green'))
self.table_hist.setItem(row,2,tw)
row = row + 1
self.label.setText(str(x[0][1]))
time.sleep(2)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = Dataview()
myapp.show()
sys.exit(app.exec_())
APIS.PY
import urllib2
import json
class API_Bitstamp_usd:
def __init__(self):
self.url = 'https://www.bitstamp.net/api/'
def get_depth(self):
try:
url = self.url + 'order_book/'
json_obj = urllib2.urlopen(url)
data = json.load(json_obj)
asks_, bids_ = [], []
for i in data['bids']:
price, qty = format(float(i[0]), '.2f'), format(float(i[1]), '.2f')
bids_.append([float(price), float(qty)])
for i in data['asks']:
price, qty = format(float(i[0]), '.2f'), format(float(i[1]), '.2f')
asks_.append([float(price), float(qty)])
return bids_, asks_
except:
pass
def get_history(self):
try:
url = self.url + 'transactions/'
json_obj = urllib2.urlopen(url)
data = json.load(json_obj)
history_ = []
for i in data:
timestamp, price, amount, type = i['date'], format(float(i['price']), '.3f'), format(float(i['amount']), '.2f'), ''
history_.append([timestamp, float(price), float(amount), type])
return history_
except:
pass