Im writing a PyQt app that takes some input in one widget, and then processes some text files.
What ive got at the moment is when the user clicks the "process" button a seperate window with a QTextEdit in it pops up, and ouputs some logging messages.
On Mac OS X this window is refreshed automatically and you cna see the process.
On Windows, the window reports (Not Responding) and then once all the proccessing is done, the log output is shown. Im assuming I need to refresh the window after each write into the log, and ive had a look around at using a timer. etc, but havnt had much luck in getting it working.
Below is the source code. It has two files, GUI.py which does all the GUI stuff and MOVtoMXF that does all the processing.
GUI.py
import os
import sys
import MOVtoMXF
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Form(QDialog):
def process(self):
path = str(self.pathBox.displayText())
if(path == ''):
QMessageBox.warning(self, "Empty Path", "You didnt fill something out.")
return
xmlFile = str(self.xmlFileBox.displayText())
if(xmlFile == ''):
QMessageBox.warning(self, "No XML file", "You didnt fill something.")
return
outFileName = str(self.outfileNameBox.displayText())
if(outFileName == ''):
QMessageBox.warning(self, "No Output File", "You didnt do something")
return
print path + " " + xmlFile + " " + outFileName
mov1 = MOVtoMXF.MOVtoMXF(path, xmlFile, outFileName, self.log)
self.log.show()
rc = mov1.ScanFile()
if( rc < 0):
print "something happened"
#self.done(0)
def __init__(self, parent=None):
super(Form, self).__init__(parent)
self.log = Log()
self.pathLabel = QLabel("P2 Path:")
self.pathBox = QLineEdit("")
self.pathBrowseB = QPushButton("Browse")
self.pathLayout = QHBoxLayout()
self.pathLayout.addStretch()
self.pathLayout.addWidget(self.pathLabel)
self.pathLayout.addWidget(self.pathBox)
self.pathLayout.addWidget(self.pathBrowseB)
self.xmlLabel = QLabel("FCP XML File:")
self.xmlFileBox = QLineEdit("")
self.xmlFileBrowseB = QPushButton("Browse")
self.xmlLayout = QHBoxLayout()
self.xmlLayout.addStretch()
self.xmlLayout.addWidget(self.xmlLabel)
self.xmlLayout.addWidget(self.xmlFileBox)
self.xmlLayout.addWidget(self.xmlFileBrowseB)
self.outFileLabel = QLabel("Save to:")
self.outfileNameBox = QLineEdit("")
self.outputFileBrowseB = QPushButton("Browse")
self.outputLayout = QHBoxLayout()
self.outputLayout.addStretch()
self.outputLayout.addWidget(self.outFileLabel)
self.outputLayout.addWidget(self.outfileNameBox)
self.outputLayout.addWidget(self.outputFileBrowseB)
self.exitButton = QPushButton("Exit")
self.processButton = QPushButton("Process")
self.buttonLayout = QHBoxLayout()
#self.buttonLayout.addStretch()
self.buttonLayout.addWidget(self.exitButton)
self.buttonLayout.addWidget(self.processButton)
self.layout = QVBoxLayout()
self.layout.addLayout(self.pathLayout)
self.layout.addLayout(self.xmlLayout)
self.layout.addLayout(self.outputLayout)
self.layout.addLayout(self.buttonLayout)
self.setLayout(self.layout)
self.pathBox.setFocus()
self.setWindowTitle("MOVtoMXF")
self.connect(self.processButton, SIGNAL("clicked()"), self.process)
self.connect(self.exitButton, SIGNAL("clicked()"), self, SLOT("reject()"))
self.ConnectButtons()
class Log(QTextEdit):
def __init__(self, parent=None):
super(Log, self).__init__(parent)
self.timer = QTimer()
self.connect(self.timer, SIGNAL("timeout()"), self.updateText())
self.timer.start(2000)
def updateText(self):
print "update Called"
AND MOVtoMXF.py
import os
import sys
import time
import string
import FileUtils
import shutil
import re
class MOVtoMXF:
#Class to do the MOVtoMXF stuff.
def __init__(self, path, xmlFile, outputFile, edit):
self.MXFdict = {}
self.MOVDict = {}
self.path = path
self.xmlFile = xmlFile
self.outputFile = outputFile
self.outputDirectory = outputFile.rsplit('/',1)
self.outputDirectory = self.outputDirectory[0]
sys.stdout = OutLog( edit, sys.stdout)
class OutLog():
def __init__(self, edit, out=None, color=None):
"""(edit, out=None, color=None) -> can write stdout, stderr to a
QTextEdit.
edit = QTextEdit
out = alternate stream ( can be the original sys.stdout )
color = alternate color (i.e. color stderr a different color)
"""
self.edit = edit
self.out = None
self.color = color
def write(self, m):
if self.color:
tc = self.edit.textColor()
self.edit.setTextColor(self.color)
#self.edit.moveCursor(QtGui.QTextCursor.End)
self.edit.insertPlainText( m )
if self.color:
self.edit.setTextColor(tc)
if self.out:
self.out.write(m)
self.edit.show()
If any other code is needed (i think this is all that is needed) then just let me know.
Any Help would be great.
Mark
It looks like your are running an external program, capturing its output into a QTextEdit. I didn't see the code of Form.process, but I am guessing on windows your function waits for the external program to finish, then quickly dumps everything to the QTextEdit.
If your interface really is waiting for the other process to finish, then it will hang in the manner you describe. You'll need to look at subprocess or perhaps even popen to get the program's output in a "non-blocking" manner.
The key to avoiding "(Not Responding)" is to call QApplication.processEvents a few times every few seconds. The QTimer is not going to help in this case, because if Qt cannot process its events, it cannot call any signal handlers.
Related
I am developing an EEL project, and I needed to create a file dialog on the python side in order to preprocess data before sending it to javascript.
I tried to use tk.filedialog.askopenfilename, but that somehow froze the javascript event loop.
I found an answer on StackOverflow that used wxpython to create a non-blocking file picker. However, when I run the code below, the file picker always starts minimized.
However, once you use the file picker once, it works perfectly the second time.
Any help appreciated.
import base64
import json
from tkinter import Tk
Tk().withdraw()
from tkinter.filedialog import askopenfilename
import PIL.Image
import eel
import numpy as np
import wx
# Reusable wxpython App instance for the creation of non-blocking popup dialogs
app=wx.App(None)
eel.init("public")
def encode(bts):
return base64.b64encode(bts)
def array_to_json(array):
return json.dumps({
"shape": list(array.shape),
"dtype": str(array.dtype),
"data":list(np.ravel(array).astype(float)) # not efficient but quite clean
})
#eel.expose
def load_image(path):
return array_to_json(np.asarray(PIL.Image.open(path)))
#eel.expose
def pick_image():
# return askopenfilename()
""" --- Adapted from https://stackoverflow.com/a/59177064/5166365"""
style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.STAY_ON_TOP | wx.DIALOG_NO_PARENT | wx.MAXIMIZE
dialog = wx.FileDialog(None, "Open File", wildcard="*", style=style)
dialog.Iconize(False)
dialog.Maximize()
dialog.Raise()
path = ""
if dialog.ShowModal() == wx.ID_OK:
path = dialog.GetPath()
else:
path = ""
return path
""" --- """
eel.start("index.html")
I was able to get it working with the following code. I used a regular window instead of a wx.Dialog or similar class.
class FancyFilePickerApplication:
def __init__(self):
self.app = wx.App()
self.frame = wx.Frame(None,title="Fancy File Picker")
self.build_ui()
##private
def build_ui(self):
self.vertical_sizer = wx.BoxSizer(wx.VERTICAL)
self.control_panel = wx.Panel(self.frame,wx.ID_ANY)
self.horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.control_panel.SetSizer(self.horizontal_sizer)
self.frame.SetSizer(self.vertical_sizer)
self.dir_ctrl = wx.GenericDirCtrl(self.frame,wx.ID_ANY,wx.EmptyString,wx.DefaultPosition,wx.Size(400,300))
self.vertical_sizer.Add(self.dir_ctrl,wx.SizerFlags().Expand())
self.vertical_sizer.Add(self.control_panel,wx.SizerFlags().Expand())
self.scale_factor_label = wx.StaticText(self.control_panel,wx.ID_ANY,"Scale Factor: ")
self.scale_factor_textbox = wx.TextCtrl(self.control_panel,wx.ID_ANY)
self.open_button = wx.Button(self.control_panel,wx.ID_ANY,"Open")
self.horizontal_sizer.Add(self.scale_factor_label,wx.SizerFlags().Expand())
self.horizontal_sizer.Add(self.scale_factor_textbox,wx.SizerFlags().Expand())
self.horizontal_sizer.Add(self.open_button,wx.SizerFlags().Expand())
self.open_button.Bind(wx.EVT_BUTTON,lambda evt:self.submit())
self.frame.Bind(wx.EVT_CLOSE,self.cancel)
def open(self, file_picked_callback):
self.file_picked_callback = file_picked_callback
self.frame.Fit()
self.frame.Center()
self.frame.Show()
self.frame.Raise()
self.frame.ToggleWindowStyle(wx.STAY_ON_TOP)
self.app.MainLoop()
##private
def submit(self):
filepath =self.dir_ctrl.GetFilePath()
scale_factor_text = self.scale_factor_textbox.GetValue()
scale_factor = 1.0 if not scale_factor_text.strip() else float(scale_factor_text)
self.file_picked_callback(filepath,scale_factor)
self.frame.Destroy()
##private
def cancel(self,evt):
self.file_picked_callback("",0)
self.frame.Destroy()
I have an application where one of its functions is to use pytube to download YouTube videos. Everything works perfectly except for updating the QProgressBar with the download progress. Please see below code:
import pytube
import PyQt5.QtWidgets as QtWidgets
import PyQt5.QtCore as QtCore
class Worker(QtCore.QObject):
finished = QtCore.pyqtSignal()
progress = QtCore.pyqtSignal(str, int, int)
def __init__(self, url, save_path, fname):
super(Worker, self).__init__()
self.url = url
self.save_path = save_path
self.fname = fname
self.perc_dl = 0
self.download_video()
def run(self):
if self.perc_dl < 100:
self.progress.emit('Downloading: {}%'.format(str(self.perc_dl)), int(self.perc_dl), 100)
print(self.perc_dl)
else:
self.progress.emit('Done!', 100, 100)
self.finished.emit()
def download_video(self):
yt = pytube.YouTube(self.url, on_progress_callback=self.progress_function)
stream = yt.streams.filter(progressive=True, file_extension='mp4').get_highest_resolution()
stream.download(output_path=self.save_path, filename=self.fname)
def progress_function(self, stream, chunk, bytes_remaining):
curr = stream.filesize - bytes_remaining
per_downloaded = round((curr / stream.filesize) * 100, 1)
self.perc_dl = per_downloaded
self.run()
class DownloadFromYT(QtWidgets.QDialog):
def __init__(self, url, save_path, fname):
super(DownloadFromYT, self).__init__()
self.url = url
self.save_path = save_path
self.fname = fname
self.vLayoutMaster = QtWidgets.QVBoxLayout()
self.hLayout = QtWidgets.QHBoxLayout()
self.hLayout.setAlignment(QtCore.Qt.AlignRight)
self.label = QtWidgets.QLabel()
self.label.setText('Downloading video')
self.pBar = QtWidgets.QProgressBar()
self.pBar.setInvertedAppearance(True)
self.pBar.setTextVisible(True)
self.pBar.setFixedWidth(200)
self.pBar.setAlignment(QtCore.Qt.AlignCenter)
self.pBar.setMaximum(100)
self.pBar.setFormat('Downloading: ')
self.noButton = QtWidgets.QPushButton('No')
self.noButton.setFixedWidth(50)
self.noButton.hide()
self.yesButton = QtWidgets.QPushButton('Yes')
self.yesButton.setFixedWidth(50)
self.yesButton.hide()
self.vLayoutMaster.addWidget(self.label)
self.vLayoutMaster.addSpacing(10)
self.vLayoutMaster.addWidget(self.pBar)
self.vLayoutMaster.addSpacing(20)
self.hLayout.addWidget(self.noButton)
self.hLayout.addWidget(self.yesButton)
self.vLayoutMaster.addLayout(self.hLayout)
# Signals / slots
self.noButton.clicked.connect(self.reject)
self.yesButton.clicked.connect(self.accept)
# Widget
self.setLayout(self.vLayoutMaster)
self.setWindowTitle('Downloading')
self.setFixedSize(250, 100)
self.show()
# Init download
self.run_thread(url, save_path, fname)
def run_thread(self, url, save_path, fname):
self.thrd = QtCore.QThread()
self.worker = Worker(url, save_path, fname)
self.worker.moveToThread(self.thrd)
self.thrd.start()
self.thrd.started.connect(self.worker.run)
self.worker.finished.connect(self.thrd.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.worker.progress.connect(self.show_progress)
self.thrd.finished.connect(self.thrd.deleteLater)
def show_progress(self, label, n, total):
self.pBar.setFormat(label)
self.pBar.setMaximum(total)
self.pBar.setValue(n)
QtCore.QCoreApplication.processEvents()
if label == 'Done!':
self.thrd.quit()
self.noButton.show()
self.yesButton.show()
self.label.setText('Video has been downloaded. Would you like to set the chosen\n'
'file path as the "Local file" path?')
I have used this basic structure in other parts of my application to display and update a progress bar with no issues, however for some reason here, self.pBar refuses to update when the progress signal is emitted from the Worker class. As you can see, I have a print() statement in the run method to check that the code is actually entering that if block and periodically printing out the download progress to the console, which it is. The application updates when the thread has finished running, so I get the final, completely filled progress bar, but in between the start and end the progress bar does not update as expected.
Any help would be greatly appreciated!
The problem seems to be that despite all your effort to wire up all that thread-worker components correctly, you are still running self.download_video() in the main (UI) thread. And therefore it is blocking refreshing of the UI. If you want to run in in the secondary thread, you must start it in Worker.run() function and not in the Worker constructor. If you wire up all threading correctly, then you should not need to call processEvents() at all to refresh UI.
If you are unsure about the thread in which the download function runs, print the thread ID in your print function and compare it to thread id of the main UI thread. Have a look at https://doc.qt.io/qt-5/qthread.html#currentThread
This is a repost of my previous post which I deleted. In the first post I asked if someone knows a way of creating bookmarks with PyQT5, however, originally, I did not post what issues I am having now, with my way. I have 3 toolbars, in my web browser.
For additional buttons as - exit, minimize, maximize and etc.
For navigation in the web.
Bookmarks all are created with the instance of QToolBar().
Bookmarks toolbar code
self.bookmark_bar = QToolBar('Bookmark')
self.bookmark_bar.setIconSize(QSize(12, 12))
self.bookmark_bar.setMovable(False)
self.addToolBar(self.bookmark_bar)
After the tool bars are created the buttons are added, as there is a lot of code there I will show just the final result as a screenshot and the last few lines of code in init function.
The last few lines of code:
self.add_new_tab(QUrl('http://www.google.com'), 'Home Page')
self.bookmarks_load()
self.show()
self.setWindowTitle('Tempo')
Everything is fine with the Web Browser itself, as everything is working except the bookmarks. The line of code 'bookmarks_load()' load the bookmarks from a .txt file. As for now in the .txt document the bookmark is youtube.com (does not matter which link is used). The bookmarks functioncs:
Add a website to a bookmark.txt
def Bookmark(self):
try:
qurl = QUrl(self.urlbar.text())
print('Here we are using the QUrl toString method: %s ---> Type: %s' % (qurl.toString(), type(qurl)))
url = qurl.toString()
print('Here we are storing the new string to a new variable: %s ---> Type: %s' % (url, type(url)))
b = open(os.path.join('bookmarks', 'bookmarks.txt'), "wb")
self.bookmarks_write = pickle.dump(url, b)
b.close()
except:
print("Crash - Bookmarks not stored")
self.bookmark_btn.setText("★")
Load bookmarks from the document file.
def bookmarks_load(self):
try:
bookmarks_open = open(os.path.join('bookmarks', 'bookmarks.txt'), 'rb')
self.bookmarks_write = pickle.load(bookmarks_open)
bookmarks_open.close()
self.create_bookmarks()
except:
bookmarks_open = open(os.path.join('bookmarks', 'bookmarks.txt'), 'wb')
bookmarks = 'http://www.stackoverflow.com'
self.bookmarks_write = pickle.dump(bookmarks, bookmarks_open)
bookmarks_open.close()
self.create_bookmarks()
print('Crash - Did not load bookmarks')
Create the bookmarks 'button', when pressed open a new tab with that website.
def create_bookmarks(self):
bookmark_list = []
try:
for book in self.bookmarks_write.split():
print(book)
bookmark_list.append(book)
print(bookmark_list)
except:
print("Something went wrong with the list")
try:
button = QAction(QIcon(os.path.join('images', 'tab_icon.PNG')), 'Open bookmark', self)
button.triggered.connect(self.add_new_tab(QUrl(bookmark_list[0]), 'New'))
self.bookmark_bar.addAction(button)
except:
print('Button is causing the error')
Now this is the part which I start having issues. If I remove the - triggered.connect line and I do not add any functionality to that 'button' everything launches and works, without any errors. It stores and can load the bookmarks. However, when that line is added, it crashes and the button is not created(The app does not exit as there is an except statement which catches the error - which pyqt does not show what error it is.). This is the add_new_tab() function:
def add_new_tab(self, qurl=None, label='Blank'):
if qurl is None:
qurl = QUrl('')
web_browser = QWebEngineView()
web_browser.setUrl(qurl)
web_browser.adjustSize()
index = self.tabs.addTab(web_browser, label)
self.tabs.setCurrentIndex(index)
web_browser.urlChanged.connect(lambda qurl, web_browser=web_browser:
self.update_urlbar(qurl, web_browser))
web_browser.loadFinished.connect(lambda _, i=index, web_browser=web_browser:
self.tabs.setTabText(i, web_browser.page().title()))
Originally I open tabs by 'double clicking' on the tab bar with this function:
def tab_open_doubleclick(self, index):
if index == -1:
self.add_new_tab()
As you can see on the trigger - I do pass the link as QUrl and I do add a test label. The problem I have it somehow does not want to work and I can not find why, because Python PyQT5 does not show an error, it just closes with the return code.
Screenshots as explanation:
Link not added to a bookmarks.txt
Link added to bookmarks.txt
Pickle does dump the link in .txt
The "except" statement is run, while the triggered.connect line is not commented out.
The app continues to run, but the loaded bookmarks buttons are not there.
In the following example I show how to add the functionality of the BookMark, for it use QSettings to store the data but for this you must use setOrganizationName(), setOrganizationDomain() and setApplicationName(). Also I added the functionality of obtaining the title of the web page. The BookMarkToolBar class has the bookmarkClicked method that returns the url associated with the BookMark and the title.
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets
class BookMarkToolBar(QtWidgets.QToolBar):
bookmarkClicked = QtCore.pyqtSignal(QtCore.QUrl, str)
def __init__(self, parent=None):
super(BookMarkToolBar, self).__init__(parent)
self.actionTriggered.connect(self.onActionTriggered)
self.bookmark_list = []
def setBoorkMarks(self, bookmarks):
for bookmark in bookmarks:
self.addBookMarkAction(bookmark["title"], bookmark["url"])
def addBookMarkAction(self, title, url):
bookmark = {"title": title, "url": url}
fm = QtGui.QFontMetrics(self.font())
if bookmark not in self.bookmark_list:
text = fm.elidedText(title, QtCore.Qt.ElideRight, 150)
action = self.addAction(text)
action.setData(bookmark)
self.bookmark_list.append(bookmark)
#QtCore.pyqtSlot(QtWidgets.QAction)
def onActionTriggered(self, action):
bookmark = action.data()
self.bookmarkClicked.emit(bookmark["url"], bookmark["title"])
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.defaultUrl = QtCore.QUrl()
self.tabs = QtWidgets.QTabWidget()
self.tabs.setTabsClosable(True)
self.setCentralWidget(self.tabs)
self.urlLe = QtWidgets.QLineEdit()
self.urlLe.returnPressed.connect(self.onReturnPressed)
self.favoriteButton = QtWidgets.QToolButton()
self.favoriteButton.setIcon(QtGui.QIcon("images/star.png"))
self.favoriteButton.clicked.connect(self.addFavoriteClicked)
toolbar = self.addToolBar("")
toolbar.addWidget(self.urlLe)
toolbar.addWidget(self.favoriteButton)
self.addToolBarBreak()
self.bookmarkToolbar = BookMarkToolBar()
self.bookmarkToolbar.bookmarkClicked.connect(self.add_new_tab)
self.addToolBar(self.bookmarkToolbar)
self.readSettings()
def onReturnPressed(self):
self.tabs.currentWidget().setUrl(QtCore.QUrl.fromUserInput(self.urlLe.text()))
def addFavoriteClicked(self):
loop = QtCore.QEventLoop()
def callback(resp):
setattr(self, "title", resp)
loop.quit()
web_browser = self.tabs.currentWidget()
web_browser.page().runJavaScript("(function() { return document.title;})();", callback)
url = web_browser.url()
loop.exec_()
self.bookmarkToolbar.addBookMarkAction(getattr(self, "title"), url)
def add_new_tab(self, qurl=QtCore.QUrl(), label='Blank'):
web_browser = QtWebEngineWidgets.QWebEngineView()
web_browser.setUrl(qurl)
web_browser.adjustSize()
web_browser.urlChanged.connect(self.updateUlrLe)
index = self.tabs.addTab(web_browser, label)
self.tabs.setCurrentIndex(index)
self.urlLe.setText(qurl.toString())
def updateUlrLe(self, url):
self.urlLe.setText(url.toDisplayString())
def readSettings(self):
setting = QtCore.QSettings()
self.defaultUrl = setting.value("defaultUrl", QtCore.QUrl('http://www.google.com'))
self.add_new_tab(self.defaultUrl, 'Home Page')
self.bookmarkToolbar.setBoorkMarks(setting.value("bookmarks", []))
def saveSettins(self):
settings = QtCore.QSettings()
settings.setValue("defaultUrl", self.defaultUrl)
settings.setValue("bookmarks", self.bookmarkToolbar.bookmark_list)
def closeEvent(self, event):
self.saveSettins()
super(MainWindow, self).closeEvent(event)
if __name__ == '__main__':
import sys
QtCore.QCoreApplication.setOrganizationName("eyllanesc.org")
QtCore.QCoreApplication.setOrganizationDomain("www.eyllanesc.com")
QtCore.QCoreApplication.setApplicationName("MyApp")
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I'm building a gui front-end for a project I've been working on, and I'm having trouble figuring out this specific aspect of PyQT. Long ago there was a post that I had that I think would've solved the problem but I can't quite find it.
Regardless, I have the following files, main.py and playlistcreator.py respectively:
(main.py)
import getpass
import playlistcreator
import os
import sys
import sip
sip.setapi('QString', 2)
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
# Window class
class Window(QWidget):
def __init__(self): # constructor for Window (passes "self" -aka Window- to __init__ to initialize it)
super(Window, self).__init__() # inherits from QMainWindow
self.setGeometry(50, 50, 300, 150) # Set window dimensions
self.setWindowTitle("Google Music Playlist Transfer") # Set window title
self.setWindowIcon(QIcon('gmusic.png')) # Set window icon
self.login = QWidget() # login widget ("front page")
self.processing = QWidget() # on successful login - being processing data
self.file_inputs = QWidget() # after processing, ask user where they would like to store data
# call functions
self.loginUI()
self.processingUI()
self.file_inputsUI()
# create stacked widget and add layouts to stack
self.Stack = QStackedWidget(self)
self.Stack.addWidget(self.login)
self.Stack.addWidget(self.processing)
self.Stack.addWidget(self.file_inputs)
main_box = QHBoxLayout(self)
main_box.addWidget(self.Stack)
self.setLayout(main_box)
self.show()
def loginUI(self):
# Set email field
self.email = QLineEdit()
self.email.setMaxLength(110)
self.email.setAlignment(Qt.AlignLeft)
# Set password field
self.pwd = QLineEdit()
self.pwd.setAlignment(Qt.AlignLeft)
self.pwd.setEchoMode(QLineEdit.Password)
# Form layout
form_layout = QFormLayout()
form_layout.addRow("Email: ", self.email)
form_layout.addRow("Password: ", self.pwd)
# Login button
self.login_btn = QPushButton("Login", self) # login button
self.login_btn.clicked.connect(self.process_login) # tell button what to do
self.login_btn.resize(self.login_btn.sizeHint())
# Quit button
self.quit_btn = QPushButton("Exit", self) # exit button
self.quit_btn.clicked.connect(self.close_application) # tell button what to do
# Error label layout
self.error_layout = QHBoxLayout()
# Button box layout
button_box = QHBoxLayout()
button_box.addStretch(1)
button_box.addWidget(self.login_btn)
button_box.addWidget(self.quit_btn)
# input layout (main layout for "home")
input_box = QVBoxLayout()
input_box.addLayout(self.error_layout)
input_box.addLayout(form_layout)
input_box.addLayout(button_box)
self.login.setLayout(input_box)
def processingUI(self):
# setup layout
layout = QHBoxLayout()
# alert user that we're grabbing track data
self.progress_label = QLabel("Grabbing tracks from Google Music. . .")
self.progress_label.setStyleSheet("color: rgb(0, 100, 0);")
layout.addWidget(self.progress_label)
# Get users list of "thumbs up" tracks from Google Music
# set layout
favorite_tracks = self.get_tracks
self.processing.setLayout(layout)
def file_inputsUI(self):
layout = QFormLayout()
# Set text field for directory
self.dir_textbox = QTextEdit(self)
# Add textbox to layout
layout.addRow("Music Directory: ", self.dir_textbox)
self.file_inputs.setLayout(layout)
def close_application(self):
confirm = QMessageBox.question(self, 'Exit Confirmation',
"Are you sure you want to exit?",
QMessageBox.Yes | QMessageBox.No)
if confirm == QMessageBox.Yes:
sys.exit()
else:
pass
def process_login(self):
email_input = str(self.email.text())
pwd_input = str(self.pwd.text())
login_successful = playlistcreator.login(email_input, pwd_input)
if login_successful:
self.Stack.setCurrentIndex(1)
else:
self.error_label = QLabel("Please check your email/password!")
self.error_label.setStyleSheet("color: rgb(255, 0, 0);")
self.error_layout.addWidget(self.error_label)
def get_tracks(self):
tracks = playlistcreator.get_favorite_tracks()
print("You have ", len(tracks), " favorite tracks!")
return tracks
# def open_dir_dialog(self):
# directory = QFileDialog.getExistingDirectory(self, 'Select USB Drive Location')
# self.myTextBox.setText(fileName)
def main():
# Create an PyQT5 application object.
app = QApplication(sys.argv)
GUI = Window()
sys.exit(app.exec_())
# # Input Music Directory
# music_dir = input("Please put the path to your music folder: ")
# list_of_paths = playlistcreator.findmp3(music_dir, favorite_tracks)
# # Input playlist file save location
# playlist_path = input("Where would you like to save the playlist?: ")
# playlist_name = input("What would you like to name the playlist? ")
# playlist_name += ".m3u8"
# # Testing appending file extension to string
# playlistcreator.addtoplaylist(list_of_paths, playlist_path, playlist_name)
# playlistcreator.logout()
main()
(playlistcreator.py)
import os
from gmusicapi import Mobileclient
from mutagen.easyid3 import EasyID3
api = Mobileclient()
songCount = 0
favorite_files_path = []
logged_in = False
def login(email, password):
library = []
if "#" not in email:
email += "#gmail.com"
logged_in = api.login(email, password, Mobileclient.FROM_MAC_ADDRESS)
return logged_in
def get_favorite_tracks():
print("Getting songs from Google Music. . .")
library = api.get_all_songs()
print("There are", len(library), "items in your music library")
good_songs = [song for song in library if song['rating'] == '5']
print("You have", len(good_songs), "favorite tracks!")
return good_songs
def logout():
api.logout()
def findmp3(rootFolder, favoriteTracks):
print("Searching for favorite tracks. . .")
for directory, subdirectory, files in os.walk(rootFolder, topdown=False): # for all files in directory
global songCount
global favorite_files_path
for mp3 in files: # for files in
if mp3.endswith(".mp3"): # if file ends with .mp3
file_path = os.path.join(directory, mp3) # concatenate to create full path
try:
file_tags = EasyID3(file_path) # grab file tags from mp3 file
except:
file_tags = EasyID3() # create tags if none are found
file_tags.save(file_path) # save file to have new tags
if "title" in file_tags:
fileTitle = file_tags["title"][0] # TIT2 = corresponding tag in mutagen for song title
else:
fileTitle = "None"
if "artist" in file_tags:
fileArtist = file_tags["artist"][0] # TPE = corresponding tag in mutagen for artist
else:
fileArtist = "None"
if "album" in file_tags:
fileAlbum = file_tags["album"][0] # TALB = corresponding tag in mutagen for album
else:
fileAlbum = "None"
for track in favoriteTracks:
gMusicTitle = track["title"]
gMusicArtist = track["artist"]
gMusicAlbum = track["album"]
if fileTitle in gMusicTitle and fileArtist in gMusicArtist and fileAlbum in gMusicAlbum: # if file tags (artist, album, title) match gMusic tags
songCount += 1
if songCount == 1:
print("Found 1 song")
else:
print("Found", songCount, "songs") # print updated count of found tracks
favorite_files_path.append(file_path) # add all found files to a list (via their paths)
break # break out of "for track in favorite tracks"
return favorite_files_path
def addtoplaylist(paths, playlist_path, playlist_name):
# Open file (or create if it does not exist)
# change to given directory
try:
os.chdir(playlist_path)
except Exception as err:
print(err)
# open file - if it does not exist, create it
with open(playlist_name, 'a+', encoding="UTF-8") as playlistFile:
print("Adding tracks...", end="")
for track in paths:
print(".", end="")
playlistFile.write(track + '\n')
playlistFile.close()
The problem I've been running into is when the user logs in they hit the processingUI window (which states that the program is grabbing all their tracks from Google) but it doesn't actually seem to be running the get_tracks() method (which is what actually gets the user's tracks from Google), as the program hangs there and doesn't do anything.
I've tested the program without a GUI using straight command line, and it works flawlessly, but I'm not sure what the issue is with the GUI - do I need to thread the execution to start when the proper "window" is brought to the front?
I am trying to ideally download thousands of PDF from a given website. However, for some reason it won't even download 100 PDF's. I am not sure why. Here is the code:
#!/usr/bin/env python
import time
from pyPdf import PdfFileWriter, PdfFileReader
import StringIO
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
from xhtml2pdf import pisa
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import*
from PyQt4.QtWebKit import *
class Foo(QWidget):
def __init__(self, parent=None):
super(Foo, self).__init__(parent)
self.count = -1
text_file = open("input.txt", "r")
self.params = text_file.read().split('\n')
self.url = 'http://www.asdfasdfasdf.com/Property.aspx?mode=details&pin={0}'
self.gridLayout = QGridLayout(self)
#self.tabWidget = QTabWidget(self)
#self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1)
self.mapper = QSignalMapper(self)
self.mapper.mapped.connect(self.on_mapper_mapped)
for i in range(100):
grabber = QWebView()
grabber.loadFinished.connect(self.mapper.map)
self.mapper.setMapping(grabber, i)
#self.tabWidget.addTab(grabber, "opener {0}".format(str(i)))
grabber.loadFinished.emit(True)
#pyqtSlot(int)
def on_mapper_mapped(self, gNumber):
self.count += 1
if self.count < len(self.params):
#gParam = self.params[self.count]
gParam = self.params[self.count]
opener = self.mapper.mapping(gNumber)
opener.load(QUrl(self.url.format(gParam)))
printer = QPrinter()
#setting format
printer.setPageSize(QPrinter.A4)
printer.setOutputFormat(QPrinter.PdfFormat)
#export file as c:\tem_pdf.pdf
PIDString = gParam[:2] + '-' + gParam[2:4] + '-' + gParam[4:7] + '-' + gParam[7:10] + '-' + gParam[10:14]
printer.setOutputFileName(PIDString + '.pdf')
def convertIt():
opener.print_(printer)
QObject.connect(opener, SIGNAL("loadFinished(bool)"), convertIt)
print str(self.count) + ' of ' + str(len(self.params))
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
main = Foo()
#main.show()
app.exec_()
sys.exit
Ideally I would like to add a footer as well but it bugs out if I try to do that. Input.txt has 100 numbers ( just for test, I need it to work on 85000). It works on real small numbers like 5 or 10 but won't do 100. Is there a limit to the QwebView instance? Should I manage that in order to make it work?
When I run this code for 100 PDF, it will print out 20 PDFS. If i Change the range to something smaller than 100, it will have duplicate PDF's but certainly more than 20.
Also, I get this error:
qpainter::begin: returned false
I think since you are trying to make a "PyQt Application" you should firstly make use of what it offers your.
Try have some look into it QNetworkAccessMAnager to have control of all your requests and all else, QNetworkRequest, QNetworkReply.
It will even take care of your threads and have your parallelism as desired. It won't freeze your app while it's being downloaded.
Have a small look into this code of mine. It doesn't do exactly what you want but is kinda a really good filtered example for what you need to have all set up.
# Subclass QNetworkAccessManager Here
from PyQt5.QtCore import QByteArray
from PyQt5.QtCore import QFile, pyqtSlot
from PyQt5.QtCore import QIODevice
from PyQt5.QtCore import QUrl
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtNetwork import QNetworkAccessManager
from PyQt5.QtNetwork import QNetworkRequest
class NetworkAccessManager(QNetworkAccessManager):
signal_add_image = pyqtSignal()
dialog = None
download_finished = False
message_buffer = None
reply = None
def __init__(self):
QNetworkAccessManager.__init__(self)
self.reply = self.get(QNetworkRequest(QUrl("")))
# Save image data in QByteArray buffer to the disk (google_image_logo.png
# in the same directory)
#pyqtSlot()
def slot_finished(self):
image_file = QFile("resources/browser_images/image_required_browser")
if image_file.open(QIODevice.ReadWrite):
image_file.write(self.message_buffer)
image_file.close()
self.signal_add_image.emit()
# QMessageBox.information(None, "Hello!", "File has been saved!")
else:
pass
# QMessageBox.critical(None, "Hello!", "Error saving file!")
self.download_finished = True
self.dialog.close()
# Append current data to the buffer every time readyRead() signal is
# emitted
#pyqtSlot()
def slot_read_data(self):
self.message_buffer += self.reply.readAll()
def request_image(self, url, progress_bar, dialog):
self.reply.deleteLater()
self.download_finished = False
self.dialog = dialog
self.message_buffer = QByteArray()
url = QUrl(url)
req = QNetworkRequest(url)
req.setRawHeader(b'User-Agent',
b'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36')
self.reply = self.get(req)
self.reply.readyRead.connect(self.slot_read_data)
self.reply.finished.connect(self.slot_finished)
self.reply.downloadProgress.connect(progress_bar)
def get_reply(self):
return self.reply
def done(self):
return self.download_finished
def set_reply(self, reply):
self.reply = reply
def del_reply(self):
self.reply.deleteLater()
NETWORK_ACCESS_MANAGER = NetworkAccessManager()
Hope it brings you some light ")