1 # Copyright (c) 2008 David Aguilar
2 """This module provides miscellaneous Qt utility functions.
4 from __future__
import division
, absolute_import
, unicode_literals
10 from PyQt4
import QtGui
11 from PyQt4
import QtCore
12 from PyQt4
.QtCore
import Qt
13 from PyQt4
.QtCore
import SIGNAL
16 from cola
import gitcfg
17 from cola
import utils
18 from cola
import resources
19 from cola
.decorators
import memoize
20 from cola
.i18n
import N_
21 from cola
.interaction
import Interaction
22 from cola
.models
.prefs
import FONTDIFF
23 from cola
.widgets
import defs
24 from cola
.compat
import ustr
27 KNOWN_FILE_MIME_TYPES
= [
28 ('text', 'script.png'),
29 ('image', 'image.png'),
30 ('python', 'script.png'),
31 ('ruby', 'script.png'),
32 ('shell', 'script.png'),
33 ('perl', 'script.png'),
34 ('octet', 'binary.png'),
37 KNOWN_FILE_EXTENSIONS
= {
38 '.java': 'script.png',
39 '.groovy': 'script.png',
47 def connect_action(action
, fn
):
48 """Connectc an action to a function"""
49 action
.connect(action
, SIGNAL('triggered()'), fn
)
52 def connect_action_bool(action
, fn
):
53 """Connect a triggered(bool) action to a function"""
54 action
.connect(action
, SIGNAL('triggered(bool)'), fn
)
57 def connect_button(button
, fn
):
58 """Connect a button to a function"""
59 button
.connect(button
, SIGNAL('clicked()'), fn
)
62 def button_action(button
, action
):
63 """Make a button trigger an action"""
64 connect_button(button
, action
.trigger
)
67 def connect_toggle(toggle
, fn
):
68 toggle
.connect(toggle
, SIGNAL('toggled(bool)'), fn
)
72 return QtGui
.QApplication
.activeWindow()
75 def hbox(margin
, spacing
, *items
):
76 return box(QtGui
.QHBoxLayout
, margin
, spacing
, *items
)
79 def vbox(margin
, spacing
, *items
):
80 return box(QtGui
.QVBoxLayout
, margin
, spacing
, *items
)
87 def box(cls
, margin
, spacing
, *items
):
91 layout
.setMargin(margin
)
92 layout
.setSpacing(spacing
)
99 elif isinstance(i
, QtGui
.QWidget
):
101 elif isinstance(i
, (QtGui
.QHBoxLayout
, QtGui
.QVBoxLayout
,
102 QtGui
.QFormLayout
, QtGui
.QLayout
)):
104 elif isinstance(i
, (int, long)):
110 def form(margin
, spacing
, *widgets
):
111 layout
= QtGui
.QFormLayout()
112 layout
.setMargin(margin
)
113 layout
.setSpacing(spacing
)
114 layout
.setFieldGrowthPolicy(QtGui
.QFormLayout
.ExpandingFieldsGrow
)
116 for idx
, (label
, widget
) in enumerate(widgets
):
117 if isinstance(label
, (str, ustr
)):
118 layout
.addRow(label
, widget
)
120 layout
.setWidget(idx
, QtGui
.QFormLayout
.LabelRole
, label
)
121 layout
.setWidget(idx
, QtGui
.QFormLayout
.FieldRole
, widget
)
126 def grid(margin
, spacing
, *widgets
):
127 layout
= QtGui
.QGridLayout()
128 layout
.setMargin(defs
.no_margin
)
129 layout
.setSpacing(defs
.spacing
)
133 if isinstance(item
, QtGui
.QWidget
):
134 layout
.addWidget(*row
)
135 elif isinstance(item
, QtGui
.QLayoutItem
):
141 def splitter(orientation
, *widgets
):
142 layout
= QtGui
.QSplitter()
143 layout
.setOrientation(orientation
)
144 layout
.setHandleWidth(defs
.handle_width
)
145 layout
.setChildrenCollapsible(True)
146 for idx
, widget
in enumerate(widgets
):
147 layout
.addWidget(widget
)
148 layout
.setStretchFactor(idx
, 1)
152 def prompt(msg
, title
=None, text
=''):
153 """Presents the user with an input widget and returns the input."""
156 result
= QtGui
.QInputDialog
.getText(active_window(), msg
, title
,
157 QtGui
.QLineEdit
.Normal
, text
)
158 return (ustr(result
[0]), result
[1])
161 def create_listwidget_item(text
, filename
):
162 """Creates a QListWidgetItem with text and the icon at filename."""
163 item
= QtGui
.QListWidgetItem()
164 item
.setIcon(QtGui
.QIcon(filename
))
169 class TreeWidgetItem(QtGui
.QTreeWidgetItem
):
171 TYPE
= QtGui
.QStandardItem
.UserType
+ 101
173 def __init__(self
, path
, icon
, deleted
):
174 QtGui
.QTreeWidgetItem
.__init
__(self
)
176 self
.deleted
= deleted
177 self
.setIcon(0, cached_icon_from_path(icon
))
178 self
.setText(0, path
)
184 def paths_from_indexes(model
, indexes
,
185 item_type
=TreeWidgetItem
.TYPE
,
187 """Return paths from a list of QStandardItemModel indexes"""
188 items
= [model
.itemFromIndex(i
) for i
in indexes
]
189 return paths_from_items(items
, item_type
=item_type
, item_filter
=item_filter
)
192 def paths_from_items(items
,
193 item_type
=TreeWidgetItem
.TYPE
,
195 """Return a list of paths from a list of items"""
196 if item_filter
is None:
197 item_filter
= lambda x
: True
198 return [i
.path
for i
in items
199 if i
.type() == item_type
and item_filter(i
)]
203 def cached_icon_from_path(filename
):
204 return QtGui
.QIcon(filename
)
207 def mkicon(icon
, default
=None):
208 if icon
is None and default
is not None:
210 elif icon
and isinstance(icon
, (str, ustr
)):
211 icon
= QtGui
.QIcon(icon
)
215 def confirm(title
, text
, informative_text
, ok_text
,
216 icon
=None, default
=True,
217 cancel_text
=None, cancel_icon
=None):
218 """Confirm that an action should take place"""
219 msgbox
= QtGui
.QMessageBox(active_window())
220 msgbox
.setWindowModality(Qt
.WindowModal
)
221 msgbox
.setWindowTitle(title
)
223 msgbox
.setInformativeText(informative_text
)
225 icon
= mkicon(icon
, ok_icon
)
226 ok
= msgbox
.addButton(ok_text
, QtGui
.QMessageBox
.ActionRole
)
229 cancel
= msgbox
.addButton(QtGui
.QMessageBox
.Cancel
)
230 cancel_icon
= mkicon(cancel_icon
, discard_icon
)
231 cancel
.setIcon(cancel_icon
)
233 cancel
.setText(cancel_text
)
236 msgbox
.setDefaultButton(ok
)
238 msgbox
.setDefaultButton(cancel
)
240 return msgbox
.clickedButton() == ok
243 class ResizeableMessageBox(QtGui
.QMessageBox
):
245 def __init__(self
, parent
):
246 QtGui
.QMessageBox
.__init
__(self
, parent
)
247 self
.setMouseTracking(True)
248 self
.setSizeGripEnabled(True)
250 def event(self
, event
):
251 res
= QtGui
.QMessageBox
.event(self
, event
)
252 event_type
= event
.type()
253 if (event_type
== QtCore
.QEvent
.MouseMove
or
254 event_type
== QtCore
.QEvent
.MouseButtonPress
):
255 maxi
= QtCore
.QSize(1024*4, 1024*4)
256 self
.setMaximumSize(maxi
)
257 text
= self
.findChild(QtGui
.QTextEdit
)
259 expand
= QtGui
.QSizePolicy
.Expanding
260 text
.setSizePolicy(QtGui
.QSizePolicy(expand
, expand
))
261 text
.setMaximumSize(maxi
)
265 def critical(title
, message
=None, details
=None):
266 """Show a warning with the provided title and message."""
269 mbox
= ResizeableMessageBox(active_window())
270 mbox
.setWindowTitle(title
)
271 mbox
.setTextFormat(Qt
.PlainText
)
272 mbox
.setText(message
)
273 mbox
.setIcon(QtGui
.QMessageBox
.Critical
)
274 mbox
.setStandardButtons(QtGui
.QMessageBox
.Close
)
275 mbox
.setDefaultButton(QtGui
.QMessageBox
.Close
)
277 mbox
.setDetailedText(details
)
281 def information(title
, message
=None, details
=None, informative_text
=None):
282 """Show information with the provided title and message."""
285 mbox
= QtGui
.QMessageBox(active_window())
286 mbox
.setStandardButtons(QtGui
.QMessageBox
.Close
)
287 mbox
.setDefaultButton(QtGui
.QMessageBox
.Close
)
288 mbox
.setWindowTitle(title
)
289 mbox
.setWindowModality(Qt
.WindowModal
)
290 mbox
.setTextFormat(Qt
.PlainText
)
291 mbox
.setText(message
)
293 mbox
.setInformativeText(informative_text
)
295 mbox
.setDetailedText(details
)
296 # Render git-cola.svg into a 1-inch wide pixmap
297 pixmap
= git_icon().pixmap(96)
298 mbox
.setIconPixmap(pixmap
)
302 def question(title
, msg
, default
=True):
303 """Launches a QMessageBox question with the provided title and message.
304 Passing "default=False" will make "No" the default choice."""
305 yes
= QtGui
.QMessageBox
.Yes
306 no
= QtGui
.QMessageBox
.No
312 result
= (QtGui
.QMessageBox
313 .question(active_window(), title
, msg
, buttons
, default
))
314 return result
== QtGui
.QMessageBox
.Yes
317 def tree_selection(tree_item
, items
):
318 """Returns an array of model items that correspond to the selected
319 QTreeWidgetItem children"""
321 count
= min(tree_item
.childCount(), len(items
))
322 for idx
in range(count
):
323 if tree_item
.child(idx
).isSelected():
324 selected
.append(items
[idx
])
329 def tree_selection_items(tree_item
):
330 """Returns selected widget items"""
332 for idx
in range(tree_item
.childCount()):
333 child
= tree_item
.child(idx
)
334 if child
.isSelected():
335 selected
.append(child
)
340 def selected_item(list_widget
, items
):
341 """Returns the model item that corresponds to the selected QListWidget
343 widget_items
= list_widget
.selectedItems()
346 widget_item
= widget_items
[0]
347 row
= list_widget
.row(widget_item
)
354 def selected_items(list_widget
, items
):
355 """Returns an array of model items that correspond to the selected
357 item_count
= len(items
)
359 for widget_item
in list_widget
.selectedItems():
360 row
= list_widget
.row(widget_item
)
362 selected
.append(items
[row
])
366 def open_file(title
, directory
=None):
367 """Creates an Open File dialog and returns a filename."""
368 return ustr(QtGui
.QFileDialog
369 .getOpenFileName(active_window(), title
, directory
))
372 def open_files(title
, directory
=None, filter=None):
373 """Creates an Open File dialog and returns a list of filenames."""
374 return (QtGui
.QFileDialog
375 .getOpenFileNames(active_window(), title
, directory
, filter))
378 def opendir_dialog(title
, path
):
379 """Prompts for a directory path"""
381 flags
= (QtGui
.QFileDialog
.ShowDirsOnly |
382 QtGui
.QFileDialog
.DontResolveSymlinks
)
383 return ustr(QtGui
.QFileDialog
384 .getExistingDirectory(active_window(),
388 def save_as(filename
, title
='Save As...'):
389 """Creates a Save File dialog and returns a filename."""
390 return ustr(QtGui
.QFileDialog
391 .getSaveFileName(active_window(), title
, filename
))
395 """Given a basename returns a QIcon from the corresponding cola icon."""
396 return QtGui
.QIcon(resources
.icon(basename
))
399 def copy_path(filename
, absolute
=True):
400 """Copy a filename to the clipboard"""
404 filename
= core
.abspath(filename
)
405 set_clipboard(filename
)
408 def set_clipboard(text
):
409 """Sets the copy/paste buffer to text."""
412 clipboard
= QtGui
.QApplication
.clipboard()
413 clipboard
.setText(text
, QtGui
.QClipboard
.Clipboard
)
414 clipboard
.setText(text
, QtGui
.QClipboard
.Selection
)
418 def persist_clipboard():
419 """Persist the clipboard
421 X11 stores only a reference to the clipboard data.
422 Send a clipboard event to force a copy of the clipboard to occur.
423 This ensures that the clipboard is present after git-cola exits.
424 Otherwise, the reference is destroyed on exit.
426 C.f. https://stackoverflow.com/questions/2007103/how-can-i-disable-clear-of-clipboard-on-exit-of-pyqt4-application
429 clipboard
= QtGui
.QApplication
.clipboard()
430 event
= QtCore
.QEvent(QtCore
.QEvent
.Clipboard
)
431 QtGui
.QApplication
.sendEvent(clipboard
, event
)
434 def add_action_bool(widget
, text
, fn
, checked
, *shortcuts
):
435 action
= _add_action(widget
, text
, fn
, connect_action_bool
, *shortcuts
)
436 action
.setCheckable(True)
437 action
.setChecked(checked
)
441 def add_action(widget
, text
, fn
, *shortcuts
):
442 return _add_action(widget
, text
, fn
, connect_action
, *shortcuts
)
445 def _add_action(widget
, text
, fn
, connect
, *shortcuts
):
446 action
= QtGui
.QAction(text
, widget
)
449 action
.setShortcuts(shortcuts
)
450 action
.setShortcutContext(Qt
.WidgetWithChildrenShortcut
)
451 widget
.addAction(action
)
455 def set_selected_item(widget
, idx
):
456 """Sets a the currently selected item to the item at index idx."""
457 if type(widget
) is QtGui
.QTreeWidget
:
458 item
= widget
.topLevelItem(idx
)
460 widget
.setItemSelected(item
, True)
461 widget
.setCurrentItem(item
)
464 def add_items(widget
, items
):
465 """Adds items to a widget."""
470 def set_items(widget
, items
):
471 """Clear the existing widget contents and set the new items."""
473 add_items(widget
, items
)
476 def icon_name_for_filename(filename
):
477 """Returns an icon name based on the filename."""
478 mimetype
= mimetypes
.guess_type(filename
)[0]
479 if mimetype
is not None:
480 mimetype
= mimetype
.lower()
481 for filetype
, icon_name
in KNOWN_FILE_MIME_TYPES
:
482 if filetype
in mimetype
:
484 extension
= os
.path
.splitext(filename
)[1]
485 return KNOWN_FILE_EXTENSIONS
.get(extension
.lower(), 'generic.png')
488 def icon_from_filename(filename
):
489 icon_name
= icon_name_for_filename(filename
)
490 return cached_icon_from_path(resources
.icon(icon_name
))
493 def create_treeitem(filename
, staged
=False, deleted
=False, untracked
=False):
494 """Given a filename, return a TreeListItem suitable for adding to a
495 QListWidget. "staged", "deleted, and "untracked" control whether to use
496 the appropriate icons."""
498 icon_name
= 'removed.png'
500 icon_name
= 'staged-item.png'
502 icon_name
= 'untracked.png'
504 icon_name
= icon_name_for_filename(filename
)
505 return TreeWidgetItem(filename
, resources
.icon(icon_name
), deleted
=deleted
)
509 def cached_icon(key
):
510 """Maintain a cache of standard icons and return cache entries."""
511 style
= QtGui
.QApplication
.instance().style()
512 return style
.standardIcon(key
)
516 """Return a standard icon for a directory."""
517 return cached_icon(QtGui
.QStyle
.SP_DirIcon
)
521 """Return a standard icon for a file."""
522 return cached_icon(QtGui
.QStyle
.SP_FileIcon
)
526 """Return a standard Apply icon"""
527 return cached_icon(QtGui
.QStyle
.SP_DialogApplyButton
)
531 return cached_icon(QtGui
.QStyle
.SP_FileDialogNewFolder
)
535 """Return a standard Save icon"""
536 return cached_icon(QtGui
.QStyle
.SP_DialogSaveButton
)
540 """Return a standard Ok icon"""
541 return cached_icon(QtGui
.QStyle
.SP_DialogOkButton
)
545 """Return a standard open directory icon"""
546 return cached_icon(QtGui
.QStyle
.SP_DirOpenIcon
)
550 """Return a standard open directory icon"""
551 return cached_icon(QtGui
.QStyle
.SP_DialogHelpButton
)
555 return theme_icon('list-add', fallback
='add.svg')
559 return theme_icon('list-remove', fallback
='remove.svg')
562 def open_file_icon():
563 return theme_icon('document-open', fallback
='open.svg')
567 """Return a standard open directory icon"""
568 return theme_icon('configure', fallback
='options.svg')
572 """Return a filter icon"""
573 return theme_icon('view-filter.png')
576 def dir_close_icon():
577 """Return a standard closed directory icon"""
578 return cached_icon(QtGui
.QStyle
.SP_DirClosedIcon
)
581 def titlebar_close_icon():
582 """Return a dock widget close icon"""
583 return cached_icon(QtGui
.QStyle
.SP_TitleBarCloseButton
)
586 def titlebar_normal_icon():
587 """Return a dock widget close icon"""
588 return cached_icon(QtGui
.QStyle
.SP_TitleBarNormalButton
)
593 Return git-cola icon from X11 theme if it exists.
594 Else fallback to default hardcoded icon.
596 return theme_icon('git-cola.svg')
600 """Returna standard Refresh icon"""
601 return cached_icon(QtGui
.QStyle
.SP_BrowserReload
)
605 """Return a standard Discard icon"""
606 return cached_icon(QtGui
.QStyle
.SP_DialogDiscardButton
)
610 """Return a standard Close icon"""
611 return cached_icon(QtGui
.QStyle
.SP_DialogCloseButton
)
614 def add_close_action(widget
):
615 """Adds close action and shortcuts to a widget."""
616 return add_action(widget
, N_('Close...'),
617 widget
.close
, QtGui
.QKeySequence
.Close
, 'Ctrl+Q')
620 def center_on_screen(widget
):
621 """Move widget to the center of the default screen"""
622 desktop
= QtGui
.QApplication
.instance().desktop()
623 rect
= desktop
.screenGeometry(QtGui
.QCursor().pos())
624 cy
= rect
.height()//2
626 widget
.move(cx
- widget
.width()//2, cy
- widget
.height()//2)
629 def default_size(parent
, width
, height
):
630 """Return the parent's size, or the provided defaults"""
631 if parent
is not None:
632 width
= parent
.width()
633 height
= parent
.height()
634 return (width
, height
)
637 def theme_icon(name
, fallback
=None):
638 """Grab an icon from the current theme with a fallback
640 Support older versions of Qt checking for fromTheme's availability.
643 if hasattr(QtGui
.QIcon
, 'fromTheme'):
644 base
, ext
= os
.path
.splitext(name
)
646 qicon
= QtGui
.QIcon
.fromTheme(base
, icon(fallback
))
648 qicon
= QtGui
.QIcon
.fromTheme(base
)
649 if not qicon
.isNull():
651 return icon(fallback
or name
)
654 def default_monospace_font():
657 if utils
.is_darwin():
659 font
.setFamily(family
)
664 font_str
= gitcfg
.current().get(FONTDIFF
)
666 font
= default_monospace_font()
667 font_str
= ustr(font
.toString())
672 font_str
= diff_font_str()
674 font
.fromString(font_str
)
678 def create_button(text
='', layout
=None, tooltip
=None, icon
=None):
679 """Create a button, set its title, and add it to the parent."""
680 button
= QtGui
.QPushButton()
681 button
.setCursor(Qt
.PointingHandCursor
)
686 if tooltip
is not None:
687 button
.setToolTip(tooltip
)
688 if layout
is not None:
689 layout
.addWidget(button
)
693 def create_action_button(tooltip
=None, icon
=None):
694 button
= QtGui
.QPushButton()
695 button
.setFixedSize(QtCore
.QSize(16, 16))
696 button
.setCursor(Qt
.PointingHandCursor
)
698 if tooltip
is not None:
699 button
.setToolTip(tooltip
)
701 pixmap
= icon
.pixmap(QtCore
.QSize(16, 16))
702 button
.setIcon(QtGui
.QIcon(pixmap
))
706 def hide_button_menu_indicator(button
):
710 %(name)s::menu-indicator {
714 if name
== 'QPushButton':
720 button
.setStyleSheet(stylesheet
% {'name': name
})
723 class DockTitleBarWidget(QtGui
.QWidget
):
725 def __init__(self
, parent
, title
, stretch
=True):
726 QtGui
.QWidget
.__init
__(self
, parent
)
727 self
.label
= label
= QtGui
.QLabel()
733 self
.setCursor(Qt
.OpenHandCursor
)
735 self
.close_button
= create_action_button(
736 tooltip
=N_('Close'), icon
=titlebar_close_icon())
738 self
.toggle_button
= create_action_button(
739 tooltip
=N_('Detach'), icon
=titlebar_normal_icon())
741 self
.corner_layout
= hbox(defs
.no_margin
, defs
.spacing
)
748 self
.main_layout
= hbox(defs
.small_margin
, defs
.spacing
,
749 label
, separator
, self
.corner_layout
,
750 self
.toggle_button
, self
.close_button
)
751 self
.setLayout(self
.main_layout
)
753 connect_button(self
.toggle_button
, self
.toggle_floating
)
754 connect_button(self
.close_button
, self
.toggle_visibility
)
756 def toggle_floating(self
):
757 self
.parent().setFloating(not self
.parent().isFloating())
758 self
.update_tooltips()
760 def toggle_visibility(self
):
761 self
.parent().toggleViewAction().trigger()
763 def set_title(self
, title
):
764 self
.label
.setText(title
)
766 def add_corner_widget(self
, widget
):
767 self
.corner_layout
.addWidget(widget
)
769 def update_tooltips(self
):
770 if self
.parent().isFloating():
771 tooltip
= N_('Attach')
773 tooltip
= N_('Detach')
774 self
.toggle_button
.setToolTip(tooltip
)
777 def create_dock(title
, parent
, stretch
=True):
778 """Create a dock widget and set it up accordingly."""
779 dock
= QtGui
.QDockWidget(parent
)
780 dock
.setWindowTitle(title
)
781 dock
.setObjectName(title
)
782 titlebar
= DockTitleBarWidget(dock
, title
, stretch
=stretch
)
783 dock
.setTitleBarWidget(titlebar
)
784 if hasattr(parent
, 'dockwidgets'):
785 parent
.dockwidgets
.append(dock
)
789 def create_menu(title
, parent
):
790 """Create a menu and set its title."""
791 qmenu
= QtGui
.QMenu(parent
)
792 qmenu
.setTitle(title
)
796 def create_toolbutton(text
=None, layout
=None, tooltip
=None, icon
=None):
797 button
= QtGui
.QToolButton()
798 button
.setAutoRaise(True)
799 button
.setAutoFillBackground(True)
800 button
.setCursor(Qt
.PointingHandCursor
)
805 button
.setToolButtonStyle(Qt
.ToolButtonTextBesideIcon
)
806 if tooltip
is not None:
807 button
.setToolTip(tooltip
)
808 if layout
is not None:
809 layout
.addWidget(button
)
813 def mimedata_from_paths(paths
):
814 """Return mimedata with a list of absolute path URLs"""
816 abspaths
= [core
.abspath(path
) for path
in paths
]
817 urls
= [QtCore
.QUrl
.fromLocalFile(path
) for path
in abspaths
]
819 mimedata
= QtCore
.QMimeData()
820 mimedata
.setUrls(urls
)
822 # The text/x-moz-list format is always included by Qt, and doing
823 # mimedata.removeFormat('text/x-moz-url') has no effect.
824 # C.f. http://www.qtcentre.org/threads/44643-Dragging-text-uri-list-Qt-inserts-garbage
826 # gnome-terminal expects utf-16 encoded text, but other terminals,
827 # e.g. terminator, prefer utf-8, so allow cola.dragencoding
828 # to override the default.
829 paths_text
= subprocess
.list2cmdline(abspaths
)
830 encoding
= gitcfg
.current().get('cola.dragencoding', 'utf-16')
831 moz_text
= core
.encode(paths_text
, encoding
=encoding
)
832 mimedata
.setData('text/x-moz-url', moz_text
)
837 def path_mimetypes():
838 return ['text/uri-list', 'text/x-moz-url']
841 class BlockSignals(object):
842 """Context manager for blocking a signals on a widget"""
844 def __init__(self
, *widgets
):
845 self
.widgets
= widgets
849 for w
in self
.widgets
:
850 self
.values
[w
] = w
.blockSignals(True)
853 def __exit__(self
, exc_type
, exc_val
, exc_tb
):
854 for w
in self
.widgets
:
855 w
.blockSignals(self
.values
[w
])
858 # Syntax highlighting
860 def rgba(r
, g
, b
, a
=255):
871 def make_format(fg
=None, bg
=None, bold
=False):
872 fmt
= QtGui
.QTextCharFormat()
874 fmt
.setForeground(fg
)
876 fmt
.setBackground(bg
)
878 fmt
.setFontWeight(QtGui
.QFont
.Bold
)
883 Interaction
.critical
= staticmethod(critical
)
884 Interaction
.confirm
= staticmethod(confirm
)
885 Interaction
.question
= staticmethod(question
)
886 Interaction
.information
= staticmethod(information
)