Source code for modules.GUI.decisionDialogWebView

import sys
from PyQt5 import QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtWebEngineWidgets import *
import pandas
from bs4 import BeautifulSoup
from utils.utils import WINDOWS


[docs]def dataframeToHTML(keywordsDataframe: pandas.DataFrame): """ Convert input dataframe to HTML table Table is generated using BeautifulSoup. :param keywordsDataframe: keywrods dataframe :return: HTML page with table """ html_doc = """ <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <!-- Bootstrap CSS --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous" /> <style> body, html { margin: 0; height: 100%; overflow: hidden; padding-bottom: 15px; } div, table { height: 100%; padding-bottom: 40px; } .max-cell-width { max-width: 350px; } .min-cell-width { min-width: 150px; } .table-striped tbody tr.highlight { background-color: lemonchiffon; } </style> </head> <body> <nav class="navbar navbar-expand-md navbar-dark bg-primary"> <a class="navbar-brand mx-auto" href="#">Select trace to execute</a> </nav> <div class="container-fluid pt-3" style="padding-bottom: 10px; margin-bottom: 10px;"> <table id="decisionTable" class="table table-sm table-responsive table-hover table-striped"> <thead class="thead-light"> <tr> <th scope="col">Case ID</th> <th scope="col">Category</th> <th scope="col">Application</th> <th scope="col">Events</th> <th scope="col">Hostname</th> <th scope="col">URL</th> <th scope="col">Keywords</th> <th scope="col">Path</th> <th scope="col">Clipboard</th> <th scope="col">Cells</th> <th scope="col">ID</th> </tr> </thead> <tbody> </tbody> </table> </div> <!-- Optional JavaScript --> <!-- jQuery first, then Popper.js, then Bootstrap JS --> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous" ></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous" ></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous" ></script> <script> function getSelectedTrace() { let caseid; let radios = document.getElementsByName("group1"); for (let i = 0, length = radios.length; i < length; i++) { if (radios[i].checked) { caseid = radios[i].value; break; } } return caseid; } $("#decisionTable tr").click(function () { $(this).find("th input:radio").prop("checked", true); $(this).addClass("highlight").siblings().removeClass("highlight"); }); </script> </body> </html> """ soup = BeautifulSoup(html_doc, 'lxml') tbody = soup.find('tbody') for trace in keywordsDataframe.values: caseID = trace[0] tr = soup.new_tag('tr', attrs={"class": "cursor-pointer"}) # radio th = soup.new_tag('th') div = soup.new_tag('div', attrs={"class": "form-check"}) radio_input = soup.new_tag("input", attrs={"class": "form-check-input", "name": "group1", "type": "radio", "value": str(caseID)}) label = soup.new_tag('label', attrs={"class": "form-check-label"}) label.string = str(caseID) div.append(radio_input) div.append(label) th.append(div) tr.append(th) # nested loop necessary to cycle through columns of the table for each row for column in range(1, len(trace)): # case id is already added above # URL, keywords, path and clipboard columns should be wider if they have content if column in range(5, 9) and trace[column]: td = soup.new_tag('td', attrs={"class": "text-break text-wrap", "style": "min-width: 350px; max-width: 600px;"}) else: td = soup.new_tag('td', attrs={"class": "text-break text-wrap min-cell-width max-cell-width"}) value = trace[column] td.string = str(value) tr.append(td) tbody.append(tr) return soup.prettify()
[docs]class DecisionDialogWebView(QDialog): """ Decision dialog to display and choose different decision in a routine. The input dataframe is shown in a HTML table generated at runtime using the dataframeToHTML() function and displayed in a webview. """
[docs] def __init__(self, df: pandas.DataFrame): """ Initialize decision dialog :param df: keywords dataframe :return: caseID of selected trace """ super(DecisionDialogWebView, self).__init__() self.setMinimumWidth(800) self.df = df self.selectedTrace = None self.__controls() self.__layout()
def __controls(self): self.browser = QWebEngineView() text = dataframeToHTML(self.df) self.browser.setHtml(text) def __layout(self): self.setWindowTitle("Decision point") self.browser.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) if WINDOWS: self.browser.setZoomFactor(1.8) self.vBox = QVBoxLayout() self.getTraceButton = QPushButton("Select trace") self.vBox.addWidget(self.browser) self.vBox.addWidget(self.getTraceButton, alignment=Qt.AlignCenter | Qt.AlignBottom) self.setLayout(self.vBox) self.getTraceButton.clicked.connect(self.updateBounds) def updateBounds(self): self.browser.page().runJavaScript("getSelectedTrace()", self.getBounds) def getBounds(self, trace): self.accept() self.selectedTrace = trace
# if __name__ == '__main__': # from io import StringIO # k = StringIO(""",case:concept:name,category,application,events,hostname,url,keywords,path,clipboard,cells,id # 0,1005090352791000,Browser,Chrome,typed,corsidilaurea.uniroma1.it,https://corsidilaurea.uniroma1.it/,,,,, # 1,1005090509725000,Browser,Chrome,"changeField, link","www.google.com, www.uniroma1.it","https://www.google.com/, https://www.uniroma1.it/it/",uniroma1,,,, # """) # df = pandas.read_csv(k, index_col=0).fillna('') # app = QtWidgets.QApplication(sys.argv) # window = DecisionDialogWebView(df) # window.show() # app.exec_()