eric6/E5Gui/E5PathPicker.py

changeset 6942
2602857055c5
parent 6670
acebf0124108
child 7229
53054eb5b15a
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2015 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a path picker widget.
8 """
9
10 from __future__ import unicode_literals
11
12 import os
13
14 try:
15 from enum import Enum
16 except ImportError:
17 from ThirdParty.enum import Enum
18
19 from PyQt5.QtCore import pyqtSignal, Qt, QFileInfo, QCoreApplication, QDir
20 from PyQt5.QtWidgets import QWidget, QHBoxLayout, QToolButton, QSizePolicy
21
22 from . import E5FileDialog
23 from .E5LineEdit import E5ClearableLineEdit
24 from .E5Completers import E5FileCompleter, E5DirCompleter
25 from .E5ComboBox import E5ClearableComboBox
26
27 import UI.PixmapCache
28
29
30 class E5PathPickerModes(Enum):
31 """
32 Class implementing the path picker modes.
33 """
34 OpenFileMode = 0
35 OpenFilesMode = 1
36 SaveFileMode = 2
37 SaveFileEnsureExtensionMode = 3
38 SaveFileOverwriteMode = 4
39 DirectoryMode = 5
40 DirectoryShowFilesMode = 6
41 CustomMode = 99
42 NoMode = 100
43
44
45 class E5PathPickerBase(QWidget):
46 """
47 Class implementing the base of a path picker widget consisting of a
48 line edit or combo box and a tool button to open a file dialog.
49
50 @signal textChanged(path) emitted when the entered path has changed
51 (line edit based widget)
52 @signal editTextChanged(path) emitted when the entered path has changed
53 (combo box based widget)
54 @signal pathSelected(path) emitted after a path has been selected via the
55 file dialog
56 @signal aboutToShowPathPickerDialog emitted before the file dialog is shown
57 @signal pickerButtonClicked emitted when the picker button was pressed and
58 the widget mode is custom
59 """
60 DefaultMode = E5PathPickerModes.NoMode
61
62 textChanged = pyqtSignal(str)
63 editTextChanged = pyqtSignal(str)
64 pathSelected = pyqtSignal(str)
65 aboutToShowPathPickerDialog = pyqtSignal()
66 pickerButtonClicked = pyqtSignal()
67
68 def __init__(self, parent=None, useLineEdit=True):
69 """
70 Constructor
71
72 @param parent reference to the parent widget
73 @type QWidget
74 @param useLineEdit flag indicating the use of a line edit
75 @type bool
76 """
77 super(E5PathPickerBase, self).__init__(parent)
78
79 self.__lineEditKind = useLineEdit
80
81 self.__mode = E5PathPicker.DefaultMode
82 self.__editorEnabled = True
83
84 self._completer = None
85 self.__filters = ""
86 self.__defaultDirectory = ""
87 self.__windowTitle = ""
88
89 self.__layout = QHBoxLayout()
90 self.__layout.setSpacing(0)
91 self.__layout.setContentsMargins(0, 0, 0, 0)
92 self.setLayout(self.__layout)
93
94 if useLineEdit:
95 self._editor = E5ClearableLineEdit(
96 self, QCoreApplication.translate(
97 "E5PathPickerBase", "Enter Path Name"))
98 else:
99 self._editor = E5ClearableComboBox(
100 self, QCoreApplication.translate(
101 "E5PathPickerBase", "Enter Path Name"))
102
103 self.__button = QToolButton(self)
104 self.__button.setToolButtonStyle(Qt.ToolButtonIconOnly)
105 self.__button.setIcon(UI.PixmapCache.getIcon("open.png"))
106
107 self.__layout.addWidget(self._editor)
108 self.__layout.addWidget(self.__button)
109
110 self.__button.clicked.connect(self.__showPathPickerDialog)
111 if useLineEdit:
112 self._editor.textEdited.connect(self.__pathEdited)
113 self._editor.textChanged.connect(self.textChanged)
114 else:
115 self._editor.editTextChanged.connect(self.editTextChanged)
116
117 self.setFocusProxy(self._editor)
118 self.setFocusPolicy(Qt.StrongFocus)
119 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
120
121 self.__button.setEnabled(self.__mode != E5PathPickerModes.NoMode)
122
123 def __pathEdited(self, path):
124 """
125 Private slot handling editing of the path.
126
127 @param path current text of the path line edit
128 @type str
129 """
130 if self._completer and not self._completer.popup().isVisible():
131 self._completer.setRootPath(QDir.toNativeSeparators(path))
132
133 def setMode(self, mode):
134 """
135 Public method to set the path picker mode.
136
137 @param mode picker mode
138 @type E5PathPickerModes
139 """
140 assert mode in E5PathPickerModes
141
142 oldMode = self.__mode
143 self.__mode = mode
144
145 if mode != oldMode or (self.__lineEditKind and not self._completer):
146 if self.__lineEditKind and self._completer:
147 # Remove current completer
148 self._editor.setCompleter(None)
149 self._completer = None
150
151 if mode != E5PathPickerModes.NoMode:
152 if self.__lineEditKind:
153 # Set a new completer
154 if mode == E5PathPickerModes.DirectoryMode:
155 self._completer = E5DirCompleter(self._editor)
156 else:
157 self._completer = E5FileCompleter(self._editor)
158
159 # set inactive text
160 if mode == E5PathPickerModes.OpenFilesMode:
161 self._editor.setInactiveText(
162 self.tr("Enter Path Names separated by ';'"))
163 else:
164 self._editor.setInactiveText(
165 self.tr("Enter Path Name"))
166 self.__button.setEnabled(self.__mode != E5PathPickerModes.NoMode)
167
168 def mode(self):
169 """
170 Public method to get the path picker mode.
171
172 @return path picker mode
173 @rtype E5PathPickerModes
174 """
175 return self.__mode
176
177 def setPickerEnabled(self, enable):
178 """
179 Public method to set the enabled state of the file dialog button.
180
181 @param enable flag indicating the enabled state
182 @type bool
183 """
184 self.__button.setEnabled(enable)
185
186 def isPickerEnabled(self):
187 """
188 Public method to get the file dialog button enabled state.
189
190 @return flag indicating the enabled state
191 @rtype bool
192 """
193 return self.__button.isEnabled()
194
195 def clear(self):
196 """
197 Public method to clear the current path or list of paths.
198 """
199 self._editor.clear()
200
201 def clearEditText(self):
202 """
203 Public method to clear the current path.
204 """
205 if not self.__lineEditKind:
206 self._editor.clearEditText()
207
208 def _setEditorText(self, text):
209 """
210 Protected method to set the text of the editor.
211
212 @param text text to set
213 @type str
214 """
215 if self.__lineEditKind:
216 self._editor.setText(text)
217 else:
218 self._editor.setEditText(text)
219 if text and self._editor.findText(text) == -1:
220 self._editor.insertItem(0, text)
221
222 def _editorText(self):
223 """
224 Protected method to get the text of the editor.
225
226 @return text of the editor
227 @rtype str
228 """
229 if self.__lineEditKind:
230 return self._editor.text()
231 else:
232 return self._editor.currentText()
233
234 def setText(self, path, toNative=True):
235 """
236 Public method to set the current path.
237
238 @param path path to be set
239 @type str
240 @param toNative flag indicating to convert the path into
241 a native format
242 @type bool
243 """
244 if self.__mode == E5PathPickerModes.OpenFilesMode:
245 self._setEditorText(path)
246 else:
247 if toNative:
248 path = QDir.toNativeSeparators(path)
249 self._setEditorText(path)
250 if self._completer:
251 self._completer.setRootPath(path)
252
253 def text(self, toNative=True):
254 """
255 Public method to get the current path.
256
257 @param toNative flag indicating to convert the path into
258 a native format
259 @type bool
260 @return current path
261 @rtype str
262 """
263 if self.__mode == E5PathPickerModes.OpenFilesMode:
264 if toNative:
265 return ";".join(
266 [QDir.toNativeSeparators(path)
267 for path in self._editorText().split(";")])
268 else:
269 return self._editorText()
270 else:
271 if toNative:
272 return os.path.expanduser(
273 QDir.toNativeSeparators(self._editorText()))
274 else:
275 return os.path.expanduser(self._editorText())
276
277 def setEditText(self, path, toNative=True):
278 """
279 Public method to set the current path.
280
281 @param path path to be set
282 @type str
283 @param toNative flag indicating to convert the path into
284 a native format
285 @type bool
286 """
287 self.setText(path, toNative=toNative)
288
289 def currentText(self, toNative=True):
290 """
291 Public method to get the current path.
292
293 @param toNative flag indicating to convert the path into
294 a native format
295 @type bool
296 @return current path
297 @rtype str
298 """
299 return self.text(toNative=toNative)
300
301 def setPath(self, path, toNative=True):
302 """
303 Public method to set the current path.
304
305 @param path path to be set
306 @type str
307 @param toNative flag indicating to convert the path into
308 a native format
309 @type bool
310 """
311 self.setText(path, toNative=toNative)
312
313 def path(self, toNative=True):
314 """
315 Public method to get the current path.
316
317 @param toNative flag indicating to convert the path into
318 a native format
319 @type bool
320 @return current path
321 @rtype str
322 """
323 return self.text(toNative=toNative)
324
325 def paths(self, toNative=True):
326 """
327 Public method to get the list of entered paths.
328
329 @param toNative flag indicating to convert the path into
330 a native format
331 @type bool
332 @return entered paths
333 @rtype list of str
334 """
335 if self.__mode == E5PathPickerModes.OpenFilesMode:
336 return self.path(toNative=toNative).split(";")
337 else:
338 return [self.path(toNative=toNative)]
339
340 def firstPath(self, toNative=True):
341 """
342 Public method to get the first path of a list of entered paths.
343
344 @param toNative flag indicating to convert the path into
345 a native format
346 @type bool
347 @return first path
348 @rtype str
349 """
350 return self.paths(toNative=toNative)[0]
351
352 def lastPath(self, toNative=True):
353 """
354 Public method to get the last path of a list of entered paths.
355
356 @param toNative flag indicating to convert the path into
357 a native format
358 @type bool
359 @return last path
360 @rtype str
361 """
362 return self.paths(toNative=toNative)[-1]
363
364 def setEditorEnabled(self, enable):
365 """
366 Public method to set the path editor's enabled state.
367
368 @param enable flag indicating the enable state
369 @type bool
370 """
371 if enable != self._editorEnabled:
372 self._editorEnabled = enable
373 self._editor.setEnabled(enable)
374
375 def editorEnabled(self):
376 """
377 Public method to get the path editor's enabled state.
378
379 @return flag indicating the enabled state
380 @rtype bool
381 """
382 return self._editorEnabled
383
384 def setDefaultDirectory(self, directory):
385 """
386 Public method to set the default directory.
387
388 @param directory default directory
389 @type str
390 """
391 self.__defaultDirectory = directory
392
393 def defaultDirectory(self):
394 """
395 Public method to get the default directory.
396
397 @return default directory
398 @rtype str
399 """
400 return self.__defaultDirectory
401
402 def setWindowTitle(self, title):
403 """
404 Public method to set the path picker dialog window title.
405
406 @param title window title
407 @type str
408 """
409 self.__windowTitle = title
410
411 def windowTitle(self):
412 """
413 Public method to get the path picker dialog's window title.
414
415 @return window title
416 @rtype str
417 """
418 return self.__windowTitle
419
420 def setFilters(self, filters):
421 """
422 Public method to set the filters for the path picker dialog.
423
424 Note: Multiple filters must be separated by ';;'.
425
426 @param filters string containing the file filters
427 @type str
428 """
429 self.__filters = filters
430
431 def filters(self):
432 """
433 Public methods to get the filter string.
434
435 @return filter string
436 @rtype str
437 """
438 return self.__filters
439
440 def setNameFilters(self, filters):
441 """
442 Public method to set the name filters for the completer.
443
444 @param filters list of file name filters
445 @type list of str
446 """
447 if self._completer:
448 self._completer.model().setNameFilters(filters)
449
450 def setButtonToolTip(self, tooltip):
451 """
452 Public method to set the tool button tool tip.
453
454 @param tooltip text to be set as a tool tip
455 @type str
456 """
457 self.__button.setToolTip(tooltip)
458
459 def buttonToolTip(self):
460 """
461 Public method to get the tool button tool tip.
462
463 @return tool tip text
464 @rtype str
465 """
466 return self.__button.toolTip()
467
468 def setEditorToolTip(self, tooltip):
469 """
470 Public method to set the editor tool tip.
471
472 @param tooltip text to be set as a tool tip
473 @type str
474 """
475 self._editor.setToolTip(tooltip)
476
477 def editorToolTip(self):
478 """
479 Public method to get the editor tool tip.
480
481 @return tool tip text
482 @rtype str
483 """
484 return self._editor.toolTip()
485
486 def __showPathPickerDialog(self):
487 """
488 Private slot to show the path picker dialog.
489 """
490 if self.__mode == E5PathPickerModes.NoMode:
491 return
492
493 if self.__mode == E5PathPickerModes.CustomMode:
494 self.pickerButtonClicked.emit()
495 return
496
497 self.aboutToShowPathPickerDialog.emit()
498
499 windowTitle = self.__windowTitle
500 if not windowTitle:
501 if self.__mode == E5PathPickerModes.OpenFileMode:
502 windowTitle = self.tr("Choose a file to open")
503 elif self.__mode == E5PathPickerModes.OpenFilesMode:
504 windowTitle = self.tr("Choose files to open")
505 elif self.__mode in [
506 E5PathPickerModes.SaveFileMode,
507 E5PathPickerModes.SaveFileEnsureExtensionMode,
508 E5PathPickerModes.SaveFileOverwriteMode]:
509 windowTitle = self.tr("Choose a file to save")
510 elif self.__mode == E5PathPickerModes.DirectoryMode:
511 windowTitle = self.tr("Choose a directory")
512
513 directory = self._editorText()
514 if not directory and self.__defaultDirectory:
515 directory = self.__defaultDirectory
516 if self.__mode == E5PathPickerModes.OpenFilesMode:
517 directory = os.path.expanduser(directory.split(";")[0])
518 else:
519 directory = os.path.expanduser(directory)
520 if not os.path.isabs(directory) and self.__defaultDirectory:
521 directory = os.path.join(self.__defaultDirectory, directory)
522 directory = QDir.fromNativeSeparators(directory)
523
524 if self.__mode == E5PathPickerModes.OpenFileMode:
525 path = E5FileDialog.getOpenFileName(
526 self,
527 windowTitle,
528 directory,
529 self.__filters)
530 path = QDir.toNativeSeparators(path)
531 elif self.__mode == E5PathPickerModes.OpenFilesMode:
532 paths = E5FileDialog.getOpenFileNames(
533 self,
534 windowTitle,
535 directory,
536 self.__filters)
537 path = ";".join([QDir.toNativeSeparators(path)
538 for path in paths])
539 elif self.__mode == E5PathPickerModes.SaveFileMode:
540 path = E5FileDialog.getSaveFileName(
541 self,
542 windowTitle,
543 directory,
544 self.__filters,
545 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
546 path = QDir.toNativeSeparators(path)
547 elif self.__mode == E5PathPickerModes.SaveFileEnsureExtensionMode:
548 path, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
549 self,
550 windowTitle,
551 directory,
552 self.__filters,
553 None,
554 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
555 path = QDir.toNativeSeparators(path)
556 if path:
557 ext = QFileInfo(path).suffix()
558 if not ext:
559 ex = selectedFilter.split("(*")[1].split(")")[0]
560 if ex:
561 path += ex
562 elif self.__mode == E5PathPickerModes.SaveFileOverwriteMode:
563 path = E5FileDialog.getSaveFileName(
564 self,
565 windowTitle,
566 directory,
567 self.__filters)
568 path = QDir.toNativeSeparators(path)
569 elif self.__mode == E5PathPickerModes.DirectoryMode:
570 path = E5FileDialog.getExistingDirectory(
571 self,
572 windowTitle,
573 directory,
574 E5FileDialog.Options(E5FileDialog.ShowDirsOnly))
575 path = QDir.toNativeSeparators(path)
576 while path.endswith(os.sep):
577 path = path[:-1]
578 elif self.__mode == E5PathPickerModes.DirectoryShowFilesMode:
579 path = E5FileDialog.getExistingDirectory(
580 self,
581 windowTitle,
582 directory,
583 E5FileDialog.Options(E5FileDialog.DontUseNativeDialog))
584 path = QDir.toNativeSeparators(path)
585 while path.endswith(os.sep):
586 path = path[:-1]
587
588 if path:
589 self._setEditorText(path)
590 self.pathSelected.emit(path)
591
592 def setReadOnly(self, readOnly):
593 """
594 Public method to set the path picker to read only mode.
595
596 @param readOnly flag indicating read only mode
597 @type bool
598 """
599 try:
600 self._editor.setReadOnly(readOnly)
601 except AttributeError:
602 self._editor.setEditable(not readOnly)
603 self.setPickerEnabled(not readOnly)
604
605 def isReadOnly(self):
606 """
607 Public method to check the path picker for read only mode.
608
609 @return flg indicating read only mode
610 @rtype bool
611 """
612 try:
613 return self._editor.isReadOnly()
614 except AttributeError:
615 return not self._editor.isEditable()
616
617 ##################################################################
618 ## Methods below emulate some of the QComboBox API
619 ##################################################################
620
621 def addItems(self, pathsList):
622 """
623 Public method to add paths to the current list.
624
625 @param pathsList list of paths to add
626 @type list of str
627 """
628 self._editor.addItems(pathsList)
629
630 def addItem(self, path):
631 """
632 Public method to add a paths to the current list.
633
634 @param path path to add
635 @type str
636 """
637 self._editor.addItem(path)
638
639 def setPathsList(self, pathsList):
640 """
641 Public method to set the paths list.
642
643 @param pathsList list of paths
644 @type list of str
645 """
646 self.clear()
647 self.addItems(pathsList)
648
649 def setCurrentIndex(self, index):
650 """
651 Public slot to set the current index.
652
653 @param index index of the item to set current
654 @type int
655 """
656 self._editor.setCurrentIndex(index)
657
658 def setInsertPolicy(self, policy):
659 """
660 Public method to set the insertion policy of the combo box.
661
662 @param policy insertion policy
663 @type QComboBox.InsertPolicy
664 """
665 self._editor.setInsertPolicy(policy)
666
667 def setSizeAdjustPolicy(self, policy):
668 """
669 Public method to set the size adjust policy of the combo box.
670
671 @param policy size adjust policy
672 @type QComboBox.SizeAdjustPolicy
673 """
674 self._editor.setSizeAdjustPolicy(policy)
675
676
677 class E5PathPicker(E5PathPickerBase):
678 """
679 Class implementing a path picker widget consisting of a line edit and a
680 tool button to open a file dialog.
681 """
682 def __init__(self, parent=None):
683 """
684 Constructor
685
686 @param parent reference to the parent widget
687 @type QWidget
688 """
689 super(E5PathPicker, self).__init__(parent, useLineEdit=True)
690
691
692 class E5ComboPathPicker(E5PathPickerBase):
693 """
694 Class implementing a path picker widget consisting of a combobox and a
695 tool button to open a file dialog.
696 """
697 def __init__(self, parent=None):
698 """
699 Constructor
700
701 @param parent reference to the parent widget
702 @type QWidget
703 """
704 super(E5ComboPathPicker, self).__init__(parent, useLineEdit=False)
705
706 def getPathItems(self):
707 """
708 Public method to get the list of remembered paths.
709
710 @return list od remembered paths
711 @rtype list of str
712 """
713 paths = []
714 for index in range(self._editor.count()):
715 paths.append(self._editor.itemText(index))
716 return paths

eric ide

mercurial