eric7/EricWidgets/EricPathPicker.py

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

eric ide

mercurial