1
# -*- coding: utf-8 -*-
3
# Copyright © 2009-2010 Pierre Raybaut
4
# Licensed under the terms of the MIT License
5
# (see spyderlib/__init__.py for details)
7
"""Customized combobox widgets"""
9
# pylint: disable=C0103
10
# pylint: disable=R0903
11
# pylint: disable=R0911
12
# pylint: disable=R0201
14
from spyderlib.qt.QtGui import (QComboBox, QFont, QToolTip, QSizePolicy,
16
from spyderlib.qt.QtCore import SIGNAL, Qt, QUrl, QTimer
21
from spyderlib.baseconfig import _
24
class BaseComboBox(QComboBox):
25
"""Editable combo box base class"""
26
def __init__(self, parent):
27
QComboBox.__init__(self, parent)
28
self.setEditable(True)
29
self.setCompleter(QCompleter(self))
32
def keyPressEvent(self, event):
33
"""Handle key press events"""
34
if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter:
35
self.add_current_text()
37
QComboBox.keyPressEvent(self, event)
39
def focusOutEvent(self, event):
40
"""Handle focus out event"""
41
# Calling asynchronously the 'add_current_text' to avoid crash
42
# https://groups.google.com/group/spyderlib/browse_thread/thread/2257abf530e210bd
43
QTimer.singleShot(50, self.add_current_text)
44
QComboBox.focusOutEvent(self, event)
47
def is_valid(self, qstr):
49
Return True if string is valid
50
Return None if validation can't be done
55
"""Action to be executed when a valid item has been selected"""
56
self.emit(SIGNAL('valid(bool)'), True)
58
def add_text(self, text):
59
"""Add text to combo box: add a new item if text is not found in
61
index = self.findText(text)
63
self.removeItem(index)
64
index = self.findText(text)
65
self.insertItem(0, text)
66
index = self.findText('')
68
self.removeItem(index)
69
self.insertItem(0, '')
70
self.setCurrentIndex(1)
72
self.setCurrentIndex(0)
74
def add_current_text(self):
75
"""Add current text to combo box history (convenient method)"""
76
valid = self.is_valid(self.currentText())
77
if valid or valid is None:
78
self.add_text(self.currentText())
82
class PatternComboBox(BaseComboBox):
83
"""Search pattern combo box"""
84
def __init__(self, parent, items=None, tip=None,
85
adjust_to_minimum=True):
86
BaseComboBox.__init__(self, parent)
88
self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength)
89
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
96
class EditableComboBox(BaseComboBox):
98
Editable combo box + Validate
100
def __init__(self, parent):
101
BaseComboBox.__init__(self, parent)
102
self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength)
104
self.connect(self, SIGNAL("editTextChanged(QString)"), self.validate)
105
self.connect(self, SIGNAL("activated(QString)"),
106
lambda qstr: self.validate(qstr, editing=False))
107
self.set_default_style()
108
self.tips = {True: _("Press enter to validate this entry"),
109
False: _('This entry is incorrect')}
111
def show_tip(self, tip=""):
113
QToolTip.showText(self.mapToGlobal(self.pos()), tip, self)
115
def set_default_style(self):
116
"""Set widget style to default"""
117
self.font.setBold(False)
118
self.setFont(self.font)
119
self.setStyleSheet("")
123
"""Action to be executed when a valid item has been selected"""
124
BaseComboBox.selected(self)
125
self.set_default_style()
127
def validate(self, qstr, editing=True):
128
"""Validate entered path"""
129
valid = self.is_valid(qstr)
130
if self.hasFocus() and valid is not None:
131
self.font.setBold(True)
132
self.setFont(self.font)
134
self.setStyleSheet("color:rgb(50, 155, 50);")
136
self.setStyleSheet("color:rgb(200, 50, 50);")
138
# Combo box text is being modified: invalidate the entry
139
self.show_tip(self.tips[valid])
140
self.emit(SIGNAL('valid(bool)'), False)
142
# A new item has just been selected
146
self.emit(SIGNAL('valid(bool)'), False)
148
self.set_default_style()
151
class PathComboBox(EditableComboBox):
153
QComboBox handling path locations
155
def __init__(self, parent, adjust_to_contents=False):
156
EditableComboBox.__init__(self, parent)
157
if adjust_to_contents:
158
self.setSizeAdjustPolicy(QComboBox.AdjustToContents)
160
self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength)
161
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
162
self.tips = {True: _("Press enter to validate this path"),
163
False: _('This path is incorrect.\n'
164
'Enter a correct directory path,\n'
165
'then press enter to validate')}
167
def is_valid(self, qstr=None):
168
"""Return True if string is valid"""
170
qstr = self.currentText()
171
return osp.isdir( unicode(qstr) )
174
"""Action to be executed when a valid item has been selected"""
175
EditableComboBox.selected(self)
176
self.emit(SIGNAL("open_dir(QString)"), self.currentText())
179
class UrlComboBox(PathComboBox):
181
QComboBox handling urls
183
def __init__(self, parent, adjust_to_contents=False):
184
PathComboBox.__init__(self, parent, adjust_to_contents)
185
self.disconnect(self, SIGNAL("editTextChanged(QString)"), self.validate)
187
def is_valid(self, qstr=None):
188
"""Return True if string is valid"""
190
qstr = self.currentText()
191
return QUrl(qstr).isValid()
194
def is_module_or_package(path):
195
"""Return True if path is a Python module/package"""
196
is_module = osp.isfile(path) and osp.splitext(path)[1] in ('.py', '.pyw')
197
is_package = osp.isdir(path) and osp.isfile(osp.join(path, '__init__.py'))
198
return is_module or is_package
200
class PythonModulesComboBox(PathComboBox):
202
QComboBox handling Python modules or packages path
203
(i.e. .py, .pyw files *and* directories containing __init__.py)
205
def __init__(self, parent, adjust_to_contents=False):
206
PathComboBox.__init__(self, parent, adjust_to_contents)
208
def is_valid(self, qstr=None):
209
"""Return True if string is valid"""
211
qstr = self.currentText()
212
return is_module_or_package(unicode(qstr))
215
"""Action to be executed when a valid item has been selected"""
216
EditableComboBox.selected(self)
217
self.emit(SIGNAL("open(QString)"), self.currentText())
1
# -*- coding: utf-8 -*-
3
# Copyright © 2009-2010 Pierre Raybaut
4
# Licensed under the terms of the MIT License
5
# (see spyderlib/__init__.py for details)
7
"""Customized combobox widgets"""
9
# pylint: disable=C0103
10
# pylint: disable=R0903
11
# pylint: disable=R0911
12
# pylint: disable=R0201
14
from spyderlib.qt.QtGui import (QComboBox, QFont, QToolTip, QSizePolicy,
16
from spyderlib.qt.QtCore import SIGNAL, Qt, QUrl, QTimer
21
from spyderlib.baseconfig import _
24
class BaseComboBox(QComboBox):
25
"""Editable combo box base class"""
26
def __init__(self, parent):
27
QComboBox.__init__(self, parent)
28
self.setEditable(True)
29
self.setCompleter(QCompleter(self))
32
def keyPressEvent(self, event):
33
"""Handle key press events"""
34
if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter:
35
if self.add_current_text_if_valid():
38
QComboBox.keyPressEvent(self, event)
40
def focusOutEvent(self, event):
41
"""Handle focus out event"""
42
# Calling asynchronously the 'add_current_text' to avoid crash
43
# https://groups.google.com/group/spyderlib/browse_thread/thread/2257abf530e210bd
44
QTimer.singleShot(50, self.add_current_text_if_valid)
45
QComboBox.focusOutEvent(self, event)
48
def is_valid(self, qstr):
50
Return True if string is valid
51
Return None if validation can't be done
56
"""Action to be executed when a valid item has been selected"""
57
self.emit(SIGNAL('valid(bool)'), True)
59
def add_text(self, text):
60
"""Add text to combo box: add a new item if text is not found in
62
index = self.findText(text)
64
self.removeItem(index)
65
index = self.findText(text)
66
self.insertItem(0, text)
67
index = self.findText('')
69
self.removeItem(index)
70
self.insertItem(0, '')
72
self.setCurrentIndex(1)
74
self.setCurrentIndex(0)
76
self.setCurrentIndex(0)
78
def add_current_text(self):
79
"""Add current text to combo box history (convenient method)"""
80
self.add_text(self.currentText())
82
def add_current_text_if_valid(self):
83
"""Add current text to combo box history if valid"""
84
valid = self.is_valid(self.currentText())
85
if valid or valid is None:
86
self.add_current_text()
90
class PatternComboBox(BaseComboBox):
91
"""Search pattern combo box"""
92
def __init__(self, parent, items=None, tip=None,
93
adjust_to_minimum=True):
94
BaseComboBox.__init__(self, parent)
96
self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength)
97
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
104
class EditableComboBox(BaseComboBox):
106
Editable combo box + Validate
108
def __init__(self, parent):
109
BaseComboBox.__init__(self, parent)
110
self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength)
112
self.connect(self, SIGNAL("editTextChanged(QString)"), self.validate)
113
self.connect(self, SIGNAL("activated(QString)"),
114
lambda qstr: self.validate(qstr, editing=False))
115
self.set_default_style()
116
self.tips = {True: _("Press enter to validate this entry"),
117
False: _('This entry is incorrect')}
119
def show_tip(self, tip=""):
121
QToolTip.showText(self.mapToGlobal(self.pos()), tip, self)
123
def set_default_style(self):
124
"""Set widget style to default"""
125
self.font.setBold(False)
126
self.setFont(self.font)
127
self.setStyleSheet("")
131
"""Action to be executed when a valid item has been selected"""
132
BaseComboBox.selected(self)
133
self.set_default_style()
135
def validate(self, qstr, editing=True):
136
"""Validate entered path"""
137
valid = self.is_valid(qstr)
138
if self.hasFocus() and valid is not None:
139
self.font.setBold(True)
140
self.setFont(self.font)
142
self.setStyleSheet("color:rgb(50, 155, 50);")
144
self.setStyleSheet("color:rgb(200, 50, 50);")
146
# Combo box text is being modified: invalidate the entry
147
self.show_tip(self.tips[valid])
148
self.emit(SIGNAL('valid(bool)'), False)
150
# A new item has just been selected
154
self.emit(SIGNAL('valid(bool)'), False)
156
self.set_default_style()
159
class PathComboBox(EditableComboBox):
161
QComboBox handling path locations
163
def __init__(self, parent, adjust_to_contents=False):
164
EditableComboBox.__init__(self, parent)
165
if adjust_to_contents:
166
self.setSizeAdjustPolicy(QComboBox.AdjustToContents)
168
self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength)
169
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
170
self.tips = {True: _("Press enter to validate this path"),
171
False: _('This path is incorrect.\n'
172
'Enter a correct directory path,\n'
173
'then press enter to validate')}
175
def is_valid(self, qstr=None):
176
"""Return True if string is valid"""
178
qstr = self.currentText()
179
return osp.isdir( unicode(qstr) )
182
"""Action to be executed when a valid item has been selected"""
183
EditableComboBox.selected(self)
184
self.emit(SIGNAL("open_dir(QString)"), self.currentText())
187
class UrlComboBox(PathComboBox):
189
QComboBox handling urls
191
def __init__(self, parent, adjust_to_contents=False):
192
PathComboBox.__init__(self, parent, adjust_to_contents)
193
self.disconnect(self, SIGNAL("editTextChanged(QString)"), self.validate)
195
def is_valid(self, qstr=None):
196
"""Return True if string is valid"""
198
qstr = self.currentText()
199
return QUrl(qstr).isValid()
202
def is_module_or_package(path):
203
"""Return True if path is a Python module/package"""
204
is_module = osp.isfile(path) and osp.splitext(path)[1] in ('.py', '.pyw')
205
is_package = osp.isdir(path) and osp.isfile(osp.join(path, '__init__.py'))
206
return is_module or is_package
208
class PythonModulesComboBox(PathComboBox):
210
QComboBox handling Python modules or packages path
211
(i.e. .py, .pyw files *and* directories containing __init__.py)
213
def __init__(self, parent, adjust_to_contents=False):
214
PathComboBox.__init__(self, parent, adjust_to_contents)
216
def is_valid(self, qstr=None):
217
"""Return True if string is valid"""
219
qstr = self.currentText()
220
return is_module_or_package(unicode(qstr))
223
"""Action to be executed when a valid item has been selected"""
224
EditableComboBox.selected(self)
225
self.emit(SIGNAL("open(QString)"), self.currentText())