src/eric7/UI/FindLocationWidget.py

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

eric ide

mercurial