Yair
Yair

Reputation: 38

How to keep open Tkcalendar after running code

I'm trying to print a PDF from website using Tkcalendar and PyQt5 in Python 3.8.5 for Windows 10

I can print a PDF file on the first attempt. But I can't print again because Tkcalendar closes, and I want to keep it open. What am I doing wrong?

Thanks.

import datetime
import requests
import re
import sys
from bs4 import BeautifulSoup
from PyQt5 import QtCore
from PyQt5 import QtWidgets
from PyQt5 import QtWebEngineWidgets
from tkinter import *
from tkinter import messagebox
from tkcalendar import *

def generar_pdf():
    
    fecha = datetime.datetime.strptime(cal.get_date(), "%d-%m-%Y").strftime("%Y-%m-%d")
    day = datetime.datetime.strptime(cal.get_date(), "%d-%m-%Y").strftime("%d")
    month = datetime.datetime.strptime(cal.get_date(), "%d-%m-%Y").strftime("%m")
    year = datetime.datetime.strptime(cal.get_date(), "%d-%m-%Y").strftime("%Y")
    strURL = "https://www.dof.gob.mx/index_111.php?year=" + year + "&month=" + month + "&day=" + day + "&edicion=MAT"

    req = requests.get(strURL)
    soup = BeautifulSoup(req.text, "lxml")

    for sub_heading in soup.find_all("a", href=True):

        if re.sub("[^\w .]", "", sub_heading.text) == "Tipo de cambio para solventar obligaciones denominadas en moneda extranjera pagaderas en la República Mexicana.":

            DOF_URL = "https://www.dof.gob.mx" + sub_heading.get("href")

        else:
            pass
    
    app = QtWidgets.QApplication(sys.argv)
    loader = QtWebEngineWidgets.QWebEngineView()
    loader.page().pdfPrintingFinished.connect(loader.close)
    loader.load(QtCore.QUrl(DOF_URL))

    def emit_pdf(finished):
        loader.page().printToPdf(fecha + ".pdf")
        messagebox.showinfo("PDF Generado", "Se generó el PDF con éxito.")

    loader.loadFinished.connect(emit_pdf)
    app.exec()

root = Tk()
root.geometry("400x400")

fecha_hoy = datetime.datetime.now()
dia_hoy = int(fecha_hoy.strftime("%d"))
mes_hoy = int(fecha_hoy.strftime("%m"))
año_hoy = int(fecha_hoy.strftime("%Y"))

cal = Calendar(root, selectmode="day", year=año_hoy, month=mes_hoy, day=dia_hoy, date_pattern="dd-mm-yyyy")
cal.pack(pady=20, fill="both", expand=True)

my_button = Button(cal, text="Generar PDF", command=generar_pdf)
my_button.pack(pady=20)

root.mainloop()

Upvotes: 1

Views: 192

Answers (1)

eyllanesc
eyllanesc

Reputation: 244369

The problem is that you are using 2 libraries that have 2 eventloops, a possible solution is to run the eventloops separately in different processes but that is unnecessarily complicating the application. A better solution is to write all the logic using a single library, in this one because there is no equivalent of Qt WebEngine in tkinter I will choose to use pyqt5:

from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets


SCRIPT_FIND_URL = """
var url = ""
var phrase = "Tipo de cambio para solventar obligaciones denominadas en moneda extranjera pagaderas en la República Mexicana."
var elements = document.getElementsByTagName("a");
for(const e of elements){
    if(e.text.includes(phrase))
        url = e.href
}
url
"""


class PageOffline(QtWebEngineWidgets.QWebEnginePage):
    finished = QtCore.pyqtSignal(bool)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.date_url = QtCore.QUrl()
        self.loadFinished.connect(self.handle_loaded)
        self.pdfPrintingFinished.connect(self.handle_pdf)

    def search(self, date):
        self.date = date
        self.date_url = QtCore.QUrl("https://www.dof.gob.mx/index_111.php")
        query = QtCore.QUrlQuery()
        query.addQueryItem("year", self.date.toString("yyyy"))
        query.addQueryItem("month", self.date.toString("MM"))
        query.addQueryItem("day", self.date.toString("dd"))
        query.addQueryItem("edicion", "MAT")
        self.date_url.setQuery(query)
        self.load(self.date_url)

    def handle_loaded(self, ok):
        if ok:
            if self.url() == self.date_url:
                self.runJavaScript(SCRIPT_FIND_URL, self.handle_url)
            else:
                filename = "{}.pdf".format(self.date.toString("yyyy-MM-dd"))
                self.printToPdf(filename)
        else:
            self.finished.emit(False)

    def handle_url(self, url):
        if url:
            pdf_url = QtCore.QUrl.fromUserInput(url)
            self.load(pdf_url)
        else:
            self.finished.emit(False)

    def handle_pdf(self, path, ok):
        self.finished.emit(ok)


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.page = PageOffline()

        self.button = QtWidgets.QPushButton("Generate pdf")
        self.calendar = QtWidgets.QCalendarWidget()

        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(self.calendar)
        lay.addWidget(self.button, alignment=QtCore.Qt.AlignCenter)

        self.button.clicked.connect(self.handle_clicked)
        self.page.finished.connect(self.handle_print_finished)

    def handle_clicked(self):
        date = self.calendar.selectedDate()
        self.page.search(date)
        self.button.setEnabled(False)

    def handle_print_finished(self, status):
        QtWidgets.QMessageBox.information(
            self,
            "Generación de PDF",
            "El PDF fue generado con éxito" if status else "La generación de PDF fallo",
        )
        self.button.setEnabled(True)


def main():
    import sys

    app = QtWidgets.QApplication(sys.argv)
    QtCore.QLocale.setDefault(QtCore.QLocale(QtCore.QLocale.Spanish))
    w = Widget()
    w.resize(400, 400)
    w.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

Upvotes: 1

Related Questions