src/eric7/UI/FindLocationWidget.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 9039
3c8aa997bad8
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2004 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog to search for files.
8 """
9
10 import os
11 import sys
12
13 from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QUrl
14 from PyQt6.QtGui import QDesktopServices, QImageReader
15 from PyQt6.QtWidgets import (
16 QWidget, QHeaderView, QApplication, QTreeWidgetItem, QDialog,
17 QDialogButtonBox, QVBoxLayout
18 )
19
20 from EricWidgets.EricPathPicker import EricPathPickerModes
21
22 from .Ui_FindLocationWidget import Ui_FindLocationWidget
23
24 import UI.PixmapCache
25 from Utilities import direntries
26 import Utilities
27
28
29 class FindLocationWidget(QWidget, Ui_FindLocationWidget):
30 """
31 Class implementing a widget to search for files.
32
33 The occurrences found are displayed in a QTreeWidget showing the
34 filename and the pathname. The file will be opened upon a double click
35 onto the respective entry of the list or by pressing the open button.
36
37 @signal sourceFile(str) emitted to open a file in the editor
38 @signal designerFile(str) emitted to open a Qt-Designer file
39 @signal linguistFile(str) emitted to open a Qt-Linguist (*.ts) file
40 @signal trpreview([str]) emitted to preview Qt-Linguist (*.qm) files
41 @signal pixmapFile(str) emitted to open a pixmap file
42 @signal svgFile(str) emitted to open a SVG file
43 @signal umlFile(str) emitted to open an eric UML file
44 """
45 sourceFile = pyqtSignal(str)
46 designerFile = pyqtSignal(str)
47 linguistFile = pyqtSignal(str)
48 trpreview = pyqtSignal(list)
49 pixmapFile = pyqtSignal(str)
50 svgFile = pyqtSignal(str)
51 umlFile = pyqtSignal(str)
52
53 def __init__(self, project, parent=None):
54 """
55 Constructor
56
57 @param project reference to the project object
58 @type Project
59 @param parent parent widget of this dialog
60 @type QWidget
61 """
62 super().__init__(parent)
63 self.setupUi(self)
64
65 self.layout().setContentsMargins(0, 3, 0, 0)
66
67 self.searchDirPicker.setMode(EricPathPickerModes.DIRECTORY_MODE)
68
69 self.fileList.headerItem().setText(self.fileList.columnCount(), "")
70
71 self.stopButton.setEnabled(False)
72 self.stopButton.setIcon(UI.PixmapCache.getIcon("stopLoading"))
73 self.stopButton.setAutoDefault(False)
74 self.stopButton.clicked.connect(self.__stopSearch)
75
76 self.findButton.setIcon(UI.PixmapCache.getIcon("find"))
77 self.findButton.setAutoDefault(False)
78 self.findButton.clicked.connect(self.__searchFile)
79
80 self.clearButton.setEnabled(False)
81 self.clearButton.setIcon(UI.PixmapCache.getIcon("clear"))
82 self.clearButton.setAutoDefault(False)
83 self.clearButton.clicked.connect(self.__clearResults)
84
85 self.openButton.setEnabled(False)
86 self.openButton.setIcon(UI.PixmapCache.getIcon("open"))
87 self.openButton.setAutoDefault(False)
88 self.openButton.clicked.connect(self.__openFile)
89
90 self.__project = project
91 self.__project.projectOpened.connect(self.__projectOpened)
92 self.__project.projectClosed.connect(self.__projectClosed)
93
94 self.extsepLabel.setText(os.extsep)
95
96 self.__shouldStop = False
97
98 self.fileNameEdit.returnPressed.connect(self.__searchFile)
99 self.fileExtEdit.returnPressed.connect(self.__searchFile)
100
101 self.__projectClosed()
102
103 @pyqtSlot()
104 def __stopSearch(self):
105 """
106 Private slot to handle the stop button being pressed.
107 """
108 self.__shouldStop = True
109
110 @pyqtSlot()
111 def __openFile(self, itm=None):
112 """
113 Private slot to open a file.
114
115 It emits a signal depending on the file extension.
116
117 @param itm item to be opened
118 @type QTreeWidgetItem
119 """
120 if itm is None:
121 itm = self.fileList.currentItem()
122 if itm is not None:
123 fileName = itm.text(0)
124 filePath = itm.text(1)
125 fileExt = os.path.splitext(fileName)[1]
126 fullName = os.path.join(filePath, fileName)
127
128 if fileExt == ".ui":
129 self.designerFile.emit(fullName)
130 elif fileExt == ".ts":
131 self.linguistFile.emit(fullName)
132 elif fileExt == ".qm":
133 self.trpreview.emit([fullName])
134 elif fileExt in (".egj", ".e5g"):
135 self.umlFile.emit(fullName)
136 elif fileExt == ".svg":
137 self.svgFile.emit(fullName)
138 elif fileExt[1:] in QImageReader.supportedImageFormats():
139 self.pixmapFile.emit(fullName)
140 else:
141 if Utilities.MimeTypes.isTextFile(fullName):
142 self.sourceFile.emit(fullName)
143 else:
144 QDesktopServices.openUrl(QUrl(fullName))
145
146 @pyqtSlot()
147 def __searchFile(self):
148 """
149 Private slot to handle the search.
150 """
151 fileName = self.fileNameEdit.text()
152 fileExt = self.fileExtEdit.text()
153
154 self.findStatusLabel.clear()
155
156 patternFormat = (
157 "{0}{1}{2}"
158 if "*" in fileName or "?" in fileName else
159 "{0}*{1}{2}"
160 )
161
162 fileNamePatterns = [patternFormat.format(
163 fileName or "*", os.extsep, fileExt or "*")]
164
165 if not fileExt:
166 # search for files without extension as well
167 if "*" in fileName or "?" in fileName:
168 patternFormat = "{0}"
169 else:
170 patternFormat = "{0}*"
171
172 fileNamePatterns.append(patternFormat.format(fileName or "*"))
173
174 searchPaths = []
175 if (
176 self.searchDirCheckBox.isChecked() and
177 self.searchDirPicker.text() != ""
178 ):
179 searchPaths.append(self.searchDirPicker.text())
180 if self.projectCheckBox.isChecked():
181 searchPaths.append(self.__project.getProjectPath())
182 if self.syspathCheckBox.isChecked():
183 searchPaths.extend(sys.path)
184
185 self.fileList.clear()
186 locations = {}
187 self.__shouldStop = False
188 self.stopButton.setEnabled(True)
189 self.clearButton.setEnabled(False)
190 QApplication.processEvents()
191
192 for path in searchPaths:
193 if os.path.isdir(path):
194 files = direntries(path, True, fileNamePatterns,
195 False, self.checkStop)
196 if files:
197 for file in files:
198 fp, fn = os.path.split(file)
199 if fn in locations:
200 if fp in locations[fn]:
201 continue
202 else:
203 locations[fn].append(fp)
204 else:
205 locations[fn] = [fp]
206 QTreeWidgetItem(self.fileList, [fn, fp])
207 QApplication.processEvents()
208
209 del locations
210 self.stopButton.setEnabled(False)
211 self.fileList.sortItems(self.fileList.sortColumn(),
212 Qt.SortOrder.AscendingOrder)
213 self.fileList.header().resizeSections(
214 QHeaderView.ResizeMode.ResizeToContents)
215 self.fileList.header().resizeSection(0, self.width() // 2)
216 self.fileList.header().setStretchLastSection(True)
217
218 self.findStatusLabel.setText(self.tr(
219 "%n file(s) found", "", self.fileList.topLevelItemCount()))
220
221 self.clearButton.setEnabled(self.fileList.topLevelItemCount() != 0)
222
223 @pyqtSlot()
224 def __clearResults(self):
225 """
226 Private slot to clear the current search results.
227 """
228 self.fileList.clear()
229 self.clearButton.setEnabled(False)
230 self.openButton.setEnabled(False)
231
232 def checkStop(self):
233 """
234 Public method to check, if the search should be stopped.
235
236 @return flag indicating the search should be stopped
237 @rtype bool
238 """
239 QApplication.processEvents()
240 return self.__shouldStop
241
242 @pyqtSlot(str)
243 def on_searchDirPicker_textChanged(self, text):
244 """
245 Private slot to handle the textChanged signal of the search directory
246 edit.
247
248 @param text text of the search dir edit
249 @type str
250 """
251 self.searchDirCheckBox.setEnabled(text != "")
252
253 @pyqtSlot(QTreeWidgetItem, int)
254 def on_fileList_itemActivated(self, itm, column):
255 """
256 Private slot to handle the double click on a file item.
257
258 It emits the signal sourceFile or designerFile depending on the
259 file extension.
260
261 @param itm the double clicked listview item
262 @type QTreeWidgetItem
263 @param column column that was double clicked (ignored)
264 @type int
265 """
266 self.__openFile(itm)
267
268 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem)
269 def on_fileList_currentItemChanged(self, current, previous):
270 """
271 Private slot handling a change of the current item.
272
273 @param current current item
274 @type QTreeWidgetItem
275 @param previous prevoius current item
276 @type QTreeWidgetItem
277 """
278 self.openButton.setEnabled(current is not None)
279
280 @pyqtSlot()
281 def __projectOpened(self):
282 """
283 Private slot to handle a project being opened.
284 """
285 self.projectCheckBox.setEnabled(True)
286 self.projectCheckBox.setChecked(True)
287
288 @pyqtSlot()
289 def __projectClosed(self):
290 """
291 Private slot to handle a project being closed.
292 """
293 self.projectCheckBox.setEnabled(False)
294 self.projectCheckBox.setChecked(False)
295
296 @pyqtSlot()
297 def activate(self):
298 """
299 Public slot to activate this widget.
300 """
301 self.fileNameEdit.selectAll()
302 self.fileNameEdit.setFocus()
303
304
305 class FindLocationDialog(QDialog):
306 """
307 Class implementing a dialog to search for files.
308
309 The occurrences found are displayed in a QTreeWidget showing the
310 filename and the pathname. The file will be opened upon a double click
311 onto the respective entry of the list or by pressing the open button.
312
313 @signal sourceFile(str) emitted to open a file in the editor
314 @signal designerFile(str) emitted to open a Qt-Designer file
315 @signal linguistFile(str) emitted to open a Qt-Linguist (*.ts) file
316 @signal trpreview([str]) emitted to preview Qt-Linguist (*.qm) files
317 @signal pixmapFile(str) emitted to open a pixmap file
318 @signal svgFile(str) emitted to open a SVG file
319 @signal umlFile(str) emitted to open an eric UML file
320 """
321 sourceFile = pyqtSignal(str)
322 designerFile = pyqtSignal(str)
323 linguistFile = pyqtSignal(str)
324 trpreview = pyqtSignal(list)
325 pixmapFile = pyqtSignal(str)
326 svgFile = pyqtSignal(str)
327 umlFile = pyqtSignal(str)
328
329 def __init__(self, project, parent=None):
330 """
331 Constructor
332
333 @param project reference to the project object
334 @type Project
335 @param parent parent widget of this dialog (defaults to None)
336 @type QWidget (optional)
337 """
338 super().__init__(parent)
339 self.setWindowFlags(Qt.WindowType.Window)
340
341 self.__layout = QVBoxLayout()
342
343 self.__findWidget = FindLocationWidget(project, self)
344 self.__layout.addWidget(self.__findWidget)
345
346 self.__buttonBox = QDialogButtonBox(
347 QDialogButtonBox.StandardButton.Close,
348 Qt.Orientation.Horizontal,
349 self
350 )
351 self.__buttonBox.button(
352 QDialogButtonBox.StandardButton.Close).setAutoDefault(False)
353 self.__layout.addWidget(self.__buttonBox)
354
355 self.setLayout(self.__layout)
356 self.resize(600, 800)
357
358 # connect the widgets
359 self.__findWidget.sourceFile.connect(self.sourceFile)
360 self.__findWidget.designerFile.connect(self.designerFile)
361 self.__findWidget.linguistFile.connect(self.linguistFile)
362 self.__findWidget.trpreview.connect(self.trpreview)
363 self.__findWidget.pixmapFile.connect(self.pixmapFile)
364 self.__findWidget.svgFile.connect(self.svgFile)
365 self.__findWidget.umlFile.connect(self.umlFile)
366
367 self.__buttonBox.accepted.connect(self.accept)
368 self.__buttonBox.rejected.connect(self.reject)
369
370 def activate(self):
371 """
372 Public method to activate the dialog.
373 """
374 self.__findWidget.activate()
375
376 self.raise_()
377 self.activateWindow()
378 self.show()

eric ide

mercurial