def __init__(self): self.dialog = QtGui.QDialog() self._gui = Ui_Web_Display() self._gui.setupUi(self.dialog) self._pathname = None # the unicode pathname of the object to display self._display = None # the resulting QString holding HTML that actually gets displayed self._trigger_return = [] # what to return from the trigger() method self._token_name = None # the name of whatever we're displaying (intervals, 2-grams, etc.) self._result_type = None # whether this is for a "table" or "image" # set "Ctrl + w" to close the window QtGui.QShortcut(QtGui.QKeySequence(Qt.CTRL + Qt.Key_W), self.dialog, self.dialog.accept, context=Qt.WindowShortcut)
class VisWebView(object): """ Display an HTML file. The class was designed for an HTML-format table as outputted by :meth:`pandas.DataFrame.to_html` or an image as outputted by our R script. """ # These shouldn't change at runtime, but they also shouldn't be publically accessible. # This is where the HTML output is outputted. _html_path = u'outputs/html_output.html' # Replace this text with the well-formatted <img /> tag. _image_replace = u'<!-- img tag goes here -->' # Replace this text with the well-formatted <table> from pandas. _table_replace = u'<!-- pandas table goes here -->' # Path of the HTML file for displaying images. _image_html = u'views/custom_image.html' # Path of the HTML file for displaying tables. _table_html = u'views/custom_table.html' # Replace this with the new table header. _old_header = """<thead> <tr style="text-align: right;"> <th></th> <th>data</th>""" def __init__(self): self.dialog = QtGui.QDialog() self._gui = Ui_Web_Display() self._gui.setupUi(self.dialog) self._pathname = None # the unicode pathname of the object to display self._display = None # the resulting QString holding HTML that actually gets displayed self._trigger_return = [] # what to return from the trigger() method self._token_name = None # the name of whatever we're displaying (intervals, 2-grams, etc.) self._result_type = None # whether this is for a "table" or "image" # set "Ctrl + w" to close the window QtGui.QShortcut(QtGui.QKeySequence(Qt.CTRL + Qt.Key_W), self.dialog, self.dialog.accept, context=Qt.WindowShortcut) def _make_img_tag(self): "Output the proper <img /> tag." return u'<img src="' + self._pathname + u'" />' def _make_table_header(self): "Output the proper <th> tag." return u'<thead><tr style="text-align: right;"><th>' + \ self._token_name + \ u'</th><th>Frequency</th>' def trigger(self, pathname, result_type, token_name=u'Object'): """ Set up the window and display the HTML-format table loaded from the indicated path. The return value tells you whether the user requested CSV- or Excel-format output, and the pathname for which they requested it. HTML-format is handled internally. :param pathname: The pathname of the table to display. :type pathname: ``basestring`` :param result_type: Whether ``pathname`` is a ``'table'`` or ``'image'``. :type result_type: ``basestring`` :param token_name: The name of objects being displayed, as it should appear in the table. The default is "Object." :type token_name: '`basestring`` :returns: A list of 2-tuples telling which type of output to save and where to save it. :rtype: list of (``basestring``, ``basestring``) Example return: >>> textview.trigger() [('CSV', '/home/christopher/results.csv'), \ ('CSV', '/home/christopher/to_send/results.csv'), \ ('Excel', '/home/christopher/results.xlsx')] """ self._pathname = unicode(pathname) self._token_name = token_name self._result_type = result_type # add our custom formatting to the file self._custom_formatting() # UI setup stuff self._gui.webview.load(QUrl.fromLocalFile(getcwd() + u'/' + VisWebView._html_path)) self.dialog.show() # Setup signals (dialog close is done automatically) and disable unusable buttons if u'table' == self._result_type: self._gui.btn_csv.clicked.connect(self._save_csv) self._gui.btn_html.clicked.connect(self._save_html) self._gui.btn_excel.clicked.connect(self._save_excel) self._gui.btn_png.setVisible(False) elif u'image' == self._result_type: self._gui.btn_png.clicked.connect(self._save_png) self._gui.btn_csv.setVisible(False) self._gui.btn_html.setVisible(False) self._gui.btn_excel.setVisible(False) # Show the form self.dialog.exec_() # ... (user does some stuff)... # if applicable, return the instructions for what to save and where if len(self._trigger_return) > 0: return self._trigger_return def _custom_formatting(self): """ Read the template HTML file and replace the "replace_me" comment with the appropriate data, assigning the result to the "self._display" variable. """ replace_with = u'' if u'table' == self._result_type: template_pathname = self._table_html replace_this = VisWebView._table_replace elif u'image' == self._result_type: template_pathname = self._image_html replace_this = VisWebView._image_replace # read HTML template template_file = open(template_pathname, 'r') template_str = template_file.read() template_file.close() if u'table' == self._result_type: # read the pandas-produced table table_file = open(self._pathname, 'r') replace_with = table_file.read() table_file.close() # replace the old table header with a better one (i.e., put in column names) replace_with = replace_with.replace(VisWebView._old_header, self._make_table_header()) elif u'image' == self._result_type: replace_with = self._make_img_tag() # replace the "replace me" comment self._display = template_str.replace(replace_this, replace_with) # save the HTML file (image-loading won't work unless we do this) try: html_file = open(VisWebView._html_path, 'w') html_file.write(self._display) html_file.close() except IOError as ioe: QtGui.QMessageBox.warning(None, u'Unable to Display Results', u'We could not display results because we could not write the HTML file.\n\n' + \ u'The error says:\n\n' + unicode(ioe), QtGui.QMessageBox.StandardButtons(\ QtGui.QMessageBox.Ok), QtGui.QMessageBox.Ok) def _save_png(self): self._save_button('png') def _save_csv(self): self._save_button('csv') def _save_html(self): self._save_button('html') def _save_excel(self): self._save_button('excel') def _save_button(self, format): """ Copy the file from its current path to a new one, effectively "saving" it for the user. .. note:: The "png" output format applies only and always to images. :param format: The format to save (csv, html, excel, png). :type format: ```basestring``` """ poss_formats = ['csv', 'html', 'excel', 'png'] if format not in poss_formats: if u'table' == self._result_type: format = poss_formats[0] # default to CSV else: format = 'png' # deal with the pathname new_path = unicode(QtGui.QFileDialog.getSaveFileName(\ None, u'Choose a File Name', u'', u'', None)) if new_path != u'': if 'png' == format: try: copyfile(self._pathname, new_path) except IOError as ioe: QMessageBox.warning(None, u'Error While Saving', u'Received an error saving your image:\n\n' + unicode(ioe), QMessageBox.StandardButtons(\ QMessageBox.Ok), QMessageBox.Ok) elif 'html' == format: # we have the HTML, so we can save it if u'.html' != new_path[-5:] and u'.htm' != new_path[-4:]: new_path += u'.html' try: html_file = open(new_path, 'w') html_file.write(self._display) html_file.close() except IOError as ioe: QtGui.QMessageBox.warning(None, u'Error While Saving Text', u'Received an error saving text:\n\n' + unicode(ioe), QtGui.QMessageBox.StandardButtons(\ QtGui.QMessageBox.Ok), QtGui.QMessageBox.Ok) # we don't have the CSV or Excel, so we have to ask the caller to do it for us elif 'csv' == format: self._trigger_return.append((u'CSV', new_path)) elif 'excel' == format: self._trigger_return.append((u'Excel', new_path))
class VisWebView(object): """ Display an HTML file. The class was designed for an HTML-format table as outputted by :meth:`pandas.DataFrame.to_html` or an image as outputted by our R script. """ # These shouldn't change at runtime, but they also shouldn't be publically accessible. # This is where the HTML output is outputted. _html_path = u'outputs/html_output.html' # Replace this text with the well-formatted <img /> tag. _image_replace = u'<!-- img tag goes here -->' # Replace this text with the well-formatted <table> from pandas. _table_replace = u'<!-- pandas table goes here -->' # Path of the HTML file for displaying images. _image_html = u'views/custom_image.html' # Path of the HTML file for displaying tables. _table_html = u'views/custom_table.html' # Replace this with the new table header. _old_header = """<thead> <tr style="text-align: right;"> <th></th> <th>data</th>""" def __init__(self): self.dialog = QtGui.QDialog() self._gui = Ui_Web_Display() self._gui.setupUi(self.dialog) self._pathname = None # the unicode pathname of the object to display self._display = None # the resulting QString holding HTML that actually gets displayed self._trigger_return = [] # what to return from the trigger() method self._token_name = None # the name of whatever we're displaying (intervals, 2-grams, etc.) self._result_type = None # whether this is for a "table" or "image" # set "Ctrl + w" to close the window QtGui.QShortcut(QtGui.QKeySequence(Qt.CTRL + Qt.Key_W), self.dialog, self.dialog.accept, context=Qt.WindowShortcut) def _make_img_tag(self): "Output the proper <img /> tag." return u'<img src="' + self._pathname + u'" />' def _make_table_header(self): "Output the proper <th> tag." return u'<thead><tr style="text-align: right;"><th>' + \ self._token_name + \ u'</th><th>Frequency</th>' def trigger(self, pathname, result_type, token_name=u'Object'): """ Set up the window and display the HTML-format table loaded from the indicated path. The return value tells you whether the user requested CSV- or Excel-format output, and the pathname for which they requested it. HTML-format is handled internally. :param pathname: The pathname of the table to display. :type pathname: ``basestring`` :param result_type: Whether ``pathname`` is a ``'table'`` or ``'image'``. :type result_type: ``basestring`` :param token_name: The name of objects being displayed, as it should appear in the table. The default is "Object." :type token_name: '`basestring`` :returns: A list of 2-tuples telling which type of output to save and where to save it. :rtype: list of (``basestring``, ``basestring``) Example return: >>> textview.trigger() [('CSV', '/home/christopher/results.csv'), \ ('CSV', '/home/christopher/to_send/results.csv'), \ ('Excel', '/home/christopher/results.xlsx')] """ self._pathname = unicode(pathname) self._token_name = token_name self._result_type = result_type # add our custom formatting to the file self._custom_formatting() # UI setup stuff self._gui.webview.load( QUrl.fromLocalFile(getcwd() + u'/' + VisWebView._html_path)) self.dialog.show() # Setup signals (dialog close is done automatically) and disable unusable buttons if u'table' == self._result_type: self._gui.btn_csv.clicked.connect(self._save_csv) self._gui.btn_html.clicked.connect(self._save_html) self._gui.btn_excel.clicked.connect(self._save_excel) self._gui.btn_png.setVisible(False) elif u'image' == self._result_type: self._gui.btn_png.clicked.connect(self._save_png) self._gui.btn_csv.setVisible(False) self._gui.btn_html.setVisible(False) self._gui.btn_excel.setVisible(False) # Show the form self.dialog.exec_() # ... (user does some stuff)... # if applicable, return the instructions for what to save and where if len(self._trigger_return) > 0: return self._trigger_return def _custom_formatting(self): """ Read the template HTML file and replace the "replace_me" comment with the appropriate data, assigning the result to the "self._display" variable. """ replace_with = u'' if u'table' == self._result_type: template_pathname = self._table_html replace_this = VisWebView._table_replace elif u'image' == self._result_type: template_pathname = self._image_html replace_this = VisWebView._image_replace # read HTML template template_file = open(template_pathname, 'r') template_str = template_file.read() template_file.close() if u'table' == self._result_type: # read the pandas-produced table table_file = open(self._pathname, 'r') replace_with = table_file.read() table_file.close() # replace the old table header with a better one (i.e., put in column names) replace_with = replace_with.replace(VisWebView._old_header, self._make_table_header()) elif u'image' == self._result_type: replace_with = self._make_img_tag() # replace the "replace me" comment self._display = template_str.replace(replace_this, replace_with) # save the HTML file (image-loading won't work unless we do this) try: html_file = open(VisWebView._html_path, 'w') html_file.write(self._display) html_file.close() except IOError as ioe: QtGui.QMessageBox.warning(None, u'Unable to Display Results', u'We could not display results because we could not write the HTML file.\n\n' + \ u'The error says:\n\n' + unicode(ioe), QtGui.QMessageBox.StandardButtons(\ QtGui.QMessageBox.Ok), QtGui.QMessageBox.Ok) def _save_png(self): self._save_button('png') def _save_csv(self): self._save_button('csv') def _save_html(self): self._save_button('html') def _save_excel(self): self._save_button('excel') def _save_button(self, format): """ Copy the file from its current path to a new one, effectively "saving" it for the user. .. note:: The "png" output format applies only and always to images. :param format: The format to save (csv, html, excel, png). :type format: ```basestring``` """ poss_formats = ['csv', 'html', 'excel', 'png'] if format not in poss_formats: if u'table' == self._result_type: format = poss_formats[0] # default to CSV else: format = 'png' # deal with the pathname new_path = unicode(QtGui.QFileDialog.getSaveFileName(\ None, u'Choose a File Name', u'', u'', None)) if new_path != u'': if 'png' == format: try: copyfile(self._pathname, new_path) except IOError as ioe: QMessageBox.warning(None, u'Error While Saving', u'Received an error saving your image:\n\n' + unicode(ioe), QMessageBox.StandardButtons(\ QMessageBox.Ok), QMessageBox.Ok) elif 'html' == format: # we have the HTML, so we can save it if u'.html' != new_path[-5:] and u'.htm' != new_path[-4:]: new_path += u'.html' try: html_file = open(new_path, 'w') html_file.write(self._display) html_file.close() except IOError as ioe: QtGui.QMessageBox.warning(None, u'Error While Saving Text', u'Received an error saving text:\n\n' + unicode(ioe), QtGui.QMessageBox.StandardButtons(\ QtGui.QMessageBox.Ok), QtGui.QMessageBox.Ok) # we don't have the CSV or Excel, so we have to ask the caller to do it for us elif 'csv' == format: self._trigger_return.append((u'CSV', new_path)) elif 'excel' == format: self._trigger_return.append((u'Excel', new_path))