"""EasyGUI_Qt: procedural gui based on PyQt
EasyGUI_Qt is inspired by EasyGUI and contains a number
of different basic graphical user interface components
"""
import os
import sys
import traceback
import webbrowser
from collections import OrderedDict
if sys.version_info < (3,):
import ConfigParser as configparser
else:
import configparser
unicode = str
try:
from PyQt4 import QtGui, QtCore
qt_widgets = QtGui
_qt4 = True
except ImportError:
from PyQt5 import QtGui, QtCore
from PyQt5 import QtWidgets as qt_widgets
_qt4 = False
try:
from . import utils
from . import language_selector
from . import calendar_widget
from . import multichoice
from . import show_text_window
from . import multifields
except:
import utils
import language_selector
import calendar_widget
import multichoice
import show_text_window
import multifields
__all__ = [
'get_choice',
'get_list_of_choices',
'get_float',
'get_int',
'get_integer',
'get_string',
'get_many_strings',
'get_password',
'get_username_password',
'get_new_password',
'get_yes_or_no',
'get_continue_or_cancel',
'get_color_hex',
'get_color_rgb',
'get_date',
'get_directory_name',
'get_file_names',
'get_save_file_name',
'handle_exception',
'set_font_size',
'get_language',
'set_language',
'get_abort',
'show_message',
'show_file',
'show_text',
'show_code',
'show_html',
'find_help'
]
QM_FILES = None
class SimpleApp(qt_widgets.QApplication):
"""A simple extention of the basic QApplication
with added methods useful for working with dialogs
that are not class based.
"""
def __init__(self):
super(SimpleApp, self).__init__([])
self.translator = QtCore.QTranslator()
self.default_font = QtGui.QFont()
if sys.version_info < (3,) :
settings_path = ".easygui-qt2"
else:
settings_path = ".easygui-qt3"
self.config_path = os.path.join(os.path.expanduser("~"),
settings_path)
try:
self.load_config()
self.setFont(self.default_font)
except:
pass
self.save_config()
def save_config(self):
config = configparser.RawConfigParser()
config.add_section('Configuration')
config.set('Configuration', 'locale', self.config['locale'])
config.set('Configuration', 'font-size', self.config['font-size'])
with open(self.config_path, 'w') as configfile:
config.write(configfile)
def load_config(self):
# Todo: make more robust
config = configparser.RawConfigParser()
self.config = {}
try:
config.read(self.config_path)
self.config['locale'] = config.get('Configuration', 'locale')
self.set_locale(self.config['locale'], save=False)
self.config['font-size'] = config.getint('Configuration', 'font-size')
self.set_font_size(self.config['font-size'], save=False)
except:
print("Problem encountered in load_config.")
self.config = {'locale': 'default', 'font-size': 12}
return
def set_locale(self, locale, save=True):
"""Sets the language of the basic controls for PyQt
from a locale - provided that the corresponding qm files
are present in the PyQt distribution.
"""
global QM_FILES
if QM_FILES is None:
QM_FILES = utils.find_qm_files()
if locale in QM_FILES:
if self.translator.load("qt_" + locale, QM_FILES[locale]):
self.installTranslator(self.translator)
self.config['locale'] = locale
else:
print("language not available")
elif locale == "default" and self.config['locale'] != 'default':
self.removeTranslator(self.translator)
self.translator = QtCore.QTranslator()
self.config['locale'] = 'default'
elif self.config['locale'] in QM_FILES:
if self.translator.load("qt_" + self.config['locale'],
QM_FILES[self.config['locale']]):
self.installTranslator(self.translator)
if save:
self.save_config()
def set_font_size(self, font_size, save=True):
"""Internal method to set font size.
"""
try:
font_size = int(font_size)
except:
print("font_size should be an integer")
return
self.default_font.setPointSize(font_size)
self.config['font-size'] = font_size
self.setFont(self.default_font)
if save:
self.save_config()
#========== Message Boxes ====================#
[docs]def show_message(message="Message",
title="Title"):
"""Simple message box.
:param message: message string
:param title: window title
>>> import easygui_qt as easy
>>> easy.show_message()
.. image:: ../docs/images/show_message.png
"""
app = SimpleApp()
box = qt_widgets.QMessageBox(None)
box.setWindowTitle(title)
box.setText(message)
box.show()
box.raise_()
box.exec_()
app.quit()
[docs]def get_yes_or_no(message="Answer this question", title="Title"):
"""Simple yes or no question.
:param question: Question (string) asked
:param title: Window title (string)
:return: ``True`` for "Yes", ``False`` for "No",
and ``None`` for "Cancel".
>>> import easygui_qt as easy
>>> choice = easy.get_yes_or_no()
.. image:: ../docs/images/yes_no_question.png
"""
app = SimpleApp()
flags = qt_widgets.QMessageBox.Yes | qt_widgets.QMessageBox.No
flags |= qt_widgets.QMessageBox.Cancel
box = qt_widgets.QMessageBox()
box.show()
box.raise_()
reply = box.question(None, title, message, flags)
app.quit()
if reply==qt_widgets.QMessageBox.Cancel:
return None
return reply == qt_widgets.QMessageBox.Yes
[docs]def get_continue_or_cancel(message="Processed will be cancelled!",
title="Title",
continue_button_text="Continue",
cancel_button_text="Cancel"):
"""Continue or cancel question, shown as a warning (i.e. more urgent than
simple message)
:param question: Question (string) asked
:param title: Window title (string)
:param continue_button_text: text to display on button
:param cancel_button_text: text to display on button
:return: True for "Continue", False for "Cancel"
>>> import easygui_qt as easy
>>> choice = easy.get_continue_or_cancel()
.. image:: ../docs/images/get_continue_or_cancel.png
"""
app = SimpleApp()
message_box = qt_widgets.QMessageBox(qt_widgets.QMessageBox.Warning, title, message,
qt_widgets.QMessageBox.NoButton)
message_box.addButton(continue_button_text, qt_widgets.QMessageBox.AcceptRole)
message_box.addButton(cancel_button_text, qt_widgets.QMessageBox.RejectRole)
message_box.show()
message_box.raise_()
reply = message_box.exec_()
app.quit()
return reply == qt_widgets.QMessageBox.AcceptRole
#============= Color dialogs =================
[docs]def get_color_hex():
"""Using a color dialog, returns a color in hexadecimal notation
i.e. a string '#RRGGBB' or "None" if color dialog is dismissed.
>>> import easygui_qt as easy
>>> color = easy.get_color_hex()
.. image:: ../docs/images/select_color.png
"""
app = SimpleApp()
color = qt_widgets.QColorDialog.getColor(QtCore.Qt.white, None)
app.quit()
if color.isValid():
return color.name()
[docs]def get_color_rgb(app=None):
"""Using a color dialog, returns a color in rgb notation
i.e. a tuple (r, g, b) or "None" if color dialog is dismissed.
>>> import easygui_qt as easy
>>> easy.set_language('fr')
>>> color = easy.get_color_rgb()
.. image:: ../docs/images/select_color_fr.png
"""
app = SimpleApp()
color = qt_widgets.QColorDialog.getColor(QtCore.Qt.white, None)
app.quit()
if color.isValid():
return (color.red(), color.green(), color.blue())
#================ Date ===================
[docs]def get_date(title="Select Date"):
"""Calendar widget
:param title: window title
:return: the selected date as a ``datetime.date`` instance
>>> import easygui_qt as easy
>>> date = easy.get_date()
.. image:: ../docs/images/get_date.png
"""
app = SimpleApp()
cal = calendar_widget.CalendarWidget(title=title)
app.exec_()
date = cal.date.toPyDate()
return date
#================ language/locale related
[docs]def get_language(title="Select language", name="Language codes",
instruction=None):
"""Dialog to choose language based on some locale code for
files found on default path.
:param title: Window title
:param name: Heading for valid values of locale appearing in checkboxes
:param instruction: Like the name says; when set to None, a default
string is used which includes the current language used.
The first time an EasyGUI_Qt widget is created in a program, the PyQt
language files found in the standard location of the user's computer
are scanned and recorded; these provide some translations of standard
GUI components (like name of buttons). Note that "en" is not found
as a locale (at least, not on the author's computer) but using "default"
reverts the choice to the original (English here).
>>> import easygui_qt as easy
>>> easy.get_language()
.. image:: ../docs/images/get_language.png
"""
app = SimpleApp()
if instruction is None:
instruction = ('Current language code is "{}".'.format(
app.config['locale']))
selector = language_selector.LanguageSelector(app, title=title, name=name,
instruction=instruction)
selector.exec_()
app.quit()
[docs]def set_language(locale):
"""Sets the locale, if available
:param locale: standard code for locale (e.g. 'fr', 'en_CA')
Does not create a GUI widget, but affect the appearance of
widgets created afterwards
>>> import easygui_qt as easy
>>> easy.set_locale('es')
>>> # after setting the locale
>>> easy.get_yes_or_no()
.. image:: ../docs/images/after_set_locale.png
"""
app = SimpleApp()
app.set_locale(locale)
app.quit()
return locale
#=========== InputDialogs ========================
def get_common_input_flags():
'''avoiding copying same flags in all functions'''
flags = QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint
flags |= QtCore.Qt.WindowStaysOnTopHint
return flags
class VisibleInputDialog(qt_widgets.QInputDialog):
'''A simple InputDialog class that attempts to make itself automatically
on all platforms
'''
def __init__(self):
super(VisibleInputDialog, self).__init__()
self.show()
self.raise_()
[docs]def get_int(message="Choose a number", title="Title",
default_value=1, min_=0, max_=100, step=1):
"""Simple dialog to ask a user to select an integer within a certain range.
**Note**: **get_int()** and **get_integer()** are identical.
:param message: Message displayed to the user, inviting a response
:param title: Window title
:param default_value: Default value for integer appearing in the text
box; set to the closest of ``min_`` or ``max_``
if outside of allowed range.
:param min_: Minimum integer value allowed
:param max_: Maximum integer value allowed
:param step: Indicate the change in integer value when clicking on
arrows on the right hand side
:return: an integer, or ``None`` if "cancel" is clicked or window
is closed.
>>> import easygui_qt as easy
>>> number = easy.get_int()
.. image:: ../docs/images/get_int.png
If ``default_value`` is larger than ``max_``, it is set to ``max_``;
if it is smaller than ``min_``, it is set to ``min_``.
>>> number = easy.get_integer("Enter a number", default_value=125)
.. image:: ../docs/images/get_int2.png
"""
# converting values to int for launcher demo set_font_size which
# first queries the user for a value; the initial values are passed
# as strings by the subprocess module and need to be converted here
default_value = int(default_value)
min_ = int(min_)
max_ = int(max_)
app = SimpleApp()
dialog = VisibleInputDialog()
flags = get_common_input_flags()
if _qt4:
number, ok = dialog.getInteger(None, title, message,
default_value, min_, max_, step,
flags)
else:
number, ok = dialog.getInt(None, title, message,
default_value, min_, max_, step,
flags)
dialog.destroy()
app.quit()
if ok:
return number
get_integer = get_int
[docs]def get_float(message="Choose a number", title="Title", default_value=0.0,
min_=-10000, max_=10000, decimals=3):
"""Simple dialog to ask a user to select a floating point number
within a certain range and a maximum precision.
:param message: Message displayed to the user, inviting a response
:param title: Window title
:param default_value: Default value for value appearing in the text
box; set to the closest of ``min_`` or ``max_``
if outside of allowed range.
:param min_: Minimum value allowed
:param max_: Maximum value allowed
:param decimals: Indicate the maximum decimal precision allowed
:return: a floating-point number, or ``None`` if "cancel" is clicked
or window is closed.
>>> import easygui_qt as easy
>>> number = easy.get_float()
.. image:: ../docs/images/get_float.png
**Note:** depending on the locale of the operating system where
this is used, instead of a period being used for indicating the
decimals, a comma may appear instead; this is the case for
the French version of Windows for example. Therefore, entry of
floating point values in this situation will require the use
of a comma instead of a period. However, the internal representation
will still be the same, and the number passed to Python will be
using the familar notation.
"""
app = SimpleApp()
dialog = VisibleInputDialog()
flags = get_common_input_flags()
number, ok = dialog.getDouble(None, title, message,
default_value, min_, max_, decimals,
flags)
app.quit()
if ok:
return number
[docs]def get_string(message="Enter your response", title="Title",
default_response=""):
"""Simple text input box. Used to query the user and get a string back.
:param message: Message displayed to the user, inviting a response
:param title: Window title
:param default_response: default response appearing in the text box
:return: a string, or ``None`` if "cancel" is clicked or window
is closed.
>>> import easygui_qt as easy
>>> reply = easy.get_string()
.. image:: ../docs/images/get_string.png
>>> reply = easy.get_string("new message", default_response="ready")
.. image:: ../docs/images/get_string2.png
"""
app = SimpleApp()
dialog = VisibleInputDialog()
flags = get_common_input_flags()
text, ok = dialog.getText(None, title, message, qt_widgets.QLineEdit.Normal,
default_response, flags)
app.quit()
if ok:
if sys.version_info < (3,):
return unicode(text)
return text
[docs]def get_password(message="Enter your password", title="Title"):
"""Simple password input box. Used to query the user and get a string back.
:param message: Message displayed to the user, inviting a response
:param title: Window title
:return: a string, or ``None`` if "cancel" is clicked or window
is closed.
>>> import easygui_qt as easy
>>> password = easy.get_password()
.. image:: ../docs/images/get_password.png
"""
app = SimpleApp()
dialog = VisibleInputDialog()
flags = get_common_input_flags()
text, ok = dialog.getText(None, title, message, qt_widgets.QLineEdit.Password,
'', flags)
app.quit()
if ok:
if sys.version_info < (3,):
return unicode(text)
return text
[docs]def get_choice(message="Select one item", title="Title", choices=None):
"""Simple dialog to ask a user to select an item within a drop-down list
:param message: Message displayed to the user, inviting a response
:param title: Window title
:param choices: iterable (list or tuple) containing the names of
the items that can be selected.
:returns: a string, or ``None`` if "cancel" is clicked or window
is closed.
>>> import easygui_qt as easy
>>> choices = ["CPython", "Pypy", "Jython", "IronPython"]
>>> reply = easy.get_choice("What is the best Python implementation",
... choices=choices)
.. image:: ../docs/images/get_choice.png
"""
if choices is None:
choices = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5"]
app = SimpleApp()
dialog = VisibleInputDialog()
flags = get_common_input_flags()
choice, ok = dialog.getItem(None, title, message, choices, 0, False, flags)
app.quit()
if ok:
if sys.version_info < (3,):
return unicode(choice)
return choice
[docs]def get_username_password(title="Title", labels=None):
"""User name and password input box.
:param title: Window title
:param labels: an iterable containing the labels for "user name"
and "password"; if the value not specified, the
default values will be used.
:return: An ordered dict containing the fields item as keys, and
the input from the user (empty string by default) as value
Note: this function is a special case of ``get_many_strings`` where
the required masks are provided automatically..
>>> import easygui_qt as easy
>>> reply = easy.get_username_password()
>>> reply
OrderedDict([('User name', 'aroberge'), ('Password', 'not a good password')])
.. image:: ../docs/images/get_username_password.png
"""
if labels is None:
labels = ["User name", "Password"]
if len(labels) != 2:
_title = "Error found"
message = "labels should have 2 elements; {} were found".format(len(labels))
get_abort(title=_title, message=message)
masks = [False, True]
return get_many_strings(title=title, labels=labels, masks=masks)
[docs]def get_new_password(title="Title", labels=None):
"""Change password input box.
:param title: Window title
:param labels: an iterable containing the labels for "Old password"
and "New password" and "Confirm new password". All
three labels must be different strings as they are used
as keys in a dict - however, they could differ only by
a space.
:return: An ordered dict containing the fields item as keys, and
the input from the user as values.
Note: this function is a special case of ``get_many_strings`` where
the required masks are provided automatically..
>>> import easygui_qt as easy
>>> reply = easy.get_new_password()
.. image:: ../docs/images/get_new_password.png
"""
if not labels: # empty list acceptable for test
labels = ["Old password:", "New password:", "Confirm new password:"]
if len(labels) != 3:
_title = "Error found"
message = "labels should have 3 elements; {} were found".format(len(labels))
get_abort(title=_title, message=message)
masks = [True, True, True]
class Parent:
pass
parent = Parent()
app = SimpleApp()
dialog = multifields.MultipleFieldsDialog(labels=labels, masks=masks,
parent=parent, title=title)
dialog.exec_()
app.quit()
return parent.o_dict
[docs]def get_many_strings(title="Title", labels=None, masks=None):
"""Multiple strings input
:param title: Window title
:param labels: an iterable containing the labels for to use for the entries
:param masks: optional parameter.
:return: An ordered dict containing the labels as keys, and
the input from the user (empty string by default) as value
The parameter ``masks`` if set must be an iterable of the same
length as ``choices`` and contain either True or False as entries
indicating if the entry of the text is masked or not. For example,
one could ask for a username and password using get_many_strings
as follows [note that get_username_password exists and automatically
takes care of specifying the masks and is a better choice for this
use case.]
>>> import easygui_qt as easy
>>> labels = ["User name", 'Password']
>>> masks = [False, True]
>>> reply = easy.get_many_strings(labels=labels, masks=masks)
>>> reply
OrderedDict([('User name', 'aroberge'), ('Password', 'not a good password')])
.. image:: ../docs/images/get_many_strings.png
"""
class Parent:
pass
parent = Parent()
app = SimpleApp()
dialog = multifields.MultipleFieldsDialog(labels=labels, masks=masks,
parent=parent, title=title)
dialog.exec_()
app.quit()
class IndexedOrderedDict(OrderedDict):
def __getitem__(self,key):
if isinstance(key,int):
i=0
for v in self.values():
if i==key:
return v
i=i+1
return super().__getitem__(key)
return IndexedOrderedDict(parent.o_dict)
[docs]def get_list_of_choices(title="Title", choices=None):
"""Show a list of possible choices to be selected.
:param title: Window title
:param choices: iterable (list, tuple, ...) containing the choices as
strings
:returns: a list of selected items, otherwise an empty list.
>>> import easygui_qt as easy
>>> choices = easy.get_list_of_choices()
.. image:: ../docs/images/get_list_of_choices.png
"""
app = SimpleApp()
dialog = multichoice.MultipleChoicesDialog(title=title, choices=choices)
dialog.exec_()
app.quit()
if sys.version_info < (3,):
return [unicode(item) for item in dialog.selection]
return dialog.selection
#========== Files & directory dialogs
[docs]def get_directory_name(title="Get directory"):
'''Gets the name (full path) of an existing directory
:param title: Window title
:return: the name of a directory or an empty string if cancelled.
>>> import easygui_qt as easy
>>> easy.get_directory_name()
.. image:: ../docs/images/get_directory_name.png
By default, this dialog initially displays the content of the current
working directory.
'''
app = SimpleApp()
options = qt_widgets.QFileDialog.Options()
# Without the following option (i.e. using native dialogs),
# calling this function twice in a row made Python crash.
options |= qt_widgets.QFileDialog.DontUseNativeDialog
options |= qt_widgets.QFileDialog.DontResolveSymlinks
options |= qt_widgets.QFileDialog.ShowDirsOnly
directory = qt_widgets.QFileDialog.getExistingDirectory(None,
title, os.getcwd(), options)
app.quit()
if sys.version_info < (3,):
return unicode(directory)
return directory
[docs]def get_file_names(title="Get existing file names"):
'''Gets the names (full path) of existing files
:param title: Window title
:return: the list of names (paths) of files selected.
(It can be an empty list.)
>>> import easygui_qt as easy
>>> easy.get_file_names()
.. image:: ../docs/images/get_file_names.png
By default, this dialog initially displays the content of the current
working directory.
'''
app = SimpleApp()
if sys.version_info < (3,):
files = qt_widgets.QFileDialog.getOpenFileNames(None, title, os.getcwd(),
"All Files (*.*)")
files = [unicode(item) for item in files]
else:
options = qt_widgets.QFileDialog.Options()
options |= qt_widgets.QFileDialog.DontUseNativeDialog
files = qt_widgets.QFileDialog.getOpenFileNames(None, title, os.getcwd(),
"All Files (*.*)", options)
app.quit()
return files
[docs]def get_save_file_name(title="File name to save"):
'''Gets the name (full path) of of a file to be saved.
:param title: Window title
:return: the name (path) of file selected
The user is warned if the file already exists and can choose to
cancel. However, this dialog actually does NOT save any file: it
only return a string containing the full path of the chosen file.
>>> import easygui_qt as easy
>>> easy.get_save_file_name()
.. image:: ../docs/images/get_save_file_name.png
By default, this dialog initially displays the content of the current
working directory.
'''
app = SimpleApp()
if sys.version_info < (3,):
file_name = qt_widgets.QFileDialog.getSaveFileName(None, title, os.getcwd(),
"All Files (*.*)")
app.quit()
return unicode(file_name)
options = qt_widgets.QFileDialog.Options()
options |= qt_widgets.QFileDialog.DontUseNativeDialog # see get_directory_name
file_name = qt_widgets.QFileDialog.getSaveFileName(None, title, os.getcwd(),
"All Files (*.*)", options)
app.quit()
return file_name
#========= Font related
[docs]def set_font_size(font_size):
"""Simple method to set font size.
:param font_size: integer value
Does not create a GUI widget; but affects the appearance of
future GUI widgets.
>>> import easygui_qt as easy
>>> easy.set_font_size(20)
>>> easy.show_message()
.. image:: ../docs/images/set_font_size.png
"""
app = SimpleApp()
app.set_font_size(font_size)
app.quit()
print(font_size) # info for launcher
[docs]def show_file(file_name=None, title="Title", file_type="text"):
'''Displays a file in a window. While it looks as though the file
can be edited, the only changes that happened are in the window
and nothing can be saved.
:param title: the window title
:param file_name: the file name, (path) relative to the calling program
:param file_type: possible values: ``text``, ``code``, ``html``, ``python``.
By default, file_type is assumed to be ``text``; if set to ``code``,
the content is displayed with a monospace font and, if
set to ``python``, some code highlighting is done.
If the file_type is ``html``, it is processed assuming it follows
html syntax.
**Note**: a better Python code hightlighter would be most welcome!
>>> import easygui_qt as easy
>>> easy.show_file()
.. image:: ../docs/images/show_file.png
'''
app = SimpleApp()
editor = show_text_window.TextWindow(file_name=file_name,
title=title,
text_type=file_type)
editor.show()
app.exec_()
[docs]def show_text(title="Title", text=""):
'''Displays some text in a window.
:param title: the window title
:param code: a string to display in the window.
>>> import easygui_qt as easy
>>> easy.show_code()
.. image:: ../docs/images/show_text.png
'''
app = SimpleApp()
editor = show_text_window.TextWindow(title=title, text_type='text', text=text)
editor.resize(720, 450)
editor.show()
app.exec_()
[docs]def show_code(title="Title", text=""):
'''Displays some text in a window, in a monospace font.
:param title: the window title
:param code: a string to display in the window.
>>> import easygui_qt as easy
>>> easy.show_code()
.. image:: ../docs/images/show_code.png
'''
app = SimpleApp()
editor = show_text_window.TextWindow(title=title, text_type='code', text=text)
editor.resize(720, 450)
editor.show()
app.exec_()
[docs]def show_html(title="Title", text=""):
'''Displays some html text in a window.
:param title: the window title
:param code: a string to display in the window.
>>> import easygui_qt as easy
>>> easy.show_html()
.. image:: ../docs/images/show_html.png
'''
app = SimpleApp()
editor = show_text_window.TextWindow(title=title, text_type='html', text=text)
editor.resize(720, 450)
editor.show()
app.exec_()
[docs]def get_abort(message="Major problem - or at least we think there is one...",
title="Major problem encountered!"):
'''Displays a message about a problem.
If the user clicks on "abort", sys.exit() is called and the
program ends. If the user clicks on "ignore", the program
resumes its execution.
:param title: the window title
:param message: the message to display
>>> import easygui_qt as easy
>>> easy.get_abort()
.. image:: ../docs/images/get_abort.png
'''
app = SimpleApp()
reply = qt_widgets.QMessageBox.critical(None, title, message,
qt_widgets.QMessageBox.Abort | qt_widgets.QMessageBox.Ignore)
if reply == qt_widgets.QMessageBox.Abort:
sys.exit()
else:
pass
app.quit()
[docs]def handle_exception(title="Exception raised!"):
'''Displays a traceback in a window if an exception is raised.
If the user clicks on "abort", sys.exit() is called and the
program ends. If the user clicks on "ignore", the program
resumes its execution.
:param title: the window title
.. image:: ../docs/images/handle_exception.png
'''
try:
message = "\n".join(traceback.format_exception(sys.exc_info()[0],
sys.exc_info()[1] , sys.exc_info()[2]))
except AttributeError:
return "No exception was raised"
get_abort(title=title, message=message)
[docs]def find_help():
'''Opens a web browser, pointing at the documention about EasyGUI_Qt
available on the web.
'''
webbrowser.open('http://easygui-qt.readthedocs.org/en/latest/api.html')
if __name__ == '__main__':
try:
from demos import guessing_game
guessing_game.guessing_game()
except ImportError:
print("Could not find demo.")