1#!/usr/bin/env python
2
3
4#############################################################################
5##
6## Copyright (C) 2013 Riverbank Computing Limited.
7## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
8## All rights reserved.
9##
10## This file is part of the examples of PyQt.
11##
12## $QT_BEGIN_LICENSE:BSD$
13## You may use this file under the terms of the BSD license as follows:
14##
15## "Redistribution and use in source and binary forms, with or without
16## modification, are permitted provided that the following conditions are
17## met:
18##   * Redistributions of source code must retain the above copyright
19##     notice, this list of conditions and the following disclaimer.
20##   * Redistributions in binary form must reproduce the above copyright
21##     notice, this list of conditions and the following disclaimer in
22##     the documentation and/or other materials provided with the
23##     distribution.
24##   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
25##     the names of its contributors may be used to endorse or promote
26##     products derived from this software without specific prior written
27##     permission.
28##
29## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
40## $QT_END_LICENSE$
41##
42#############################################################################
43
44
45from PyQt5.QtCore import (QDir, QIODevice, QFile, QFileInfo, Qt, QTextStream,
46        QUrl)
47from PyQt5.QtGui import QDesktopServices
48from PyQt5.QtWidgets import (QAbstractItemView, QApplication, QComboBox,
49        QDialog, QFileDialog, QGridLayout, QHBoxLayout, QHeaderView, QLabel,
50        QProgressDialog, QPushButton, QSizePolicy, QTableWidget,
51        QTableWidgetItem)
52
53
54class Window(QDialog):
55    def __init__(self, parent=None):
56        super(Window, self).__init__(parent)
57
58        browseButton = self.createButton("&Browse...", self.browse)
59        findButton = self.createButton("&Find", self.find)
60
61        self.fileComboBox = self.createComboBox("*")
62        self.textComboBox = self.createComboBox()
63        self.directoryComboBox = self.createComboBox(QDir.currentPath())
64
65        fileLabel = QLabel("Named:")
66        textLabel = QLabel("Containing text:")
67        directoryLabel = QLabel("In directory:")
68        self.filesFoundLabel = QLabel()
69
70        self.createFilesTable()
71
72        buttonsLayout = QHBoxLayout()
73        buttonsLayout.addStretch()
74        buttonsLayout.addWidget(findButton)
75
76        mainLayout = QGridLayout()
77        mainLayout.addWidget(fileLabel, 0, 0)
78        mainLayout.addWidget(self.fileComboBox, 0, 1, 1, 2)
79        mainLayout.addWidget(textLabel, 1, 0)
80        mainLayout.addWidget(self.textComboBox, 1, 1, 1, 2)
81        mainLayout.addWidget(directoryLabel, 2, 0)
82        mainLayout.addWidget(self.directoryComboBox, 2, 1)
83        mainLayout.addWidget(browseButton, 2, 2)
84        mainLayout.addWidget(self.filesTable, 3, 0, 1, 3)
85        mainLayout.addWidget(self.filesFoundLabel, 4, 0)
86        mainLayout.addLayout(buttonsLayout, 5, 0, 1, 3)
87        self.setLayout(mainLayout)
88
89        self.setWindowTitle("Find Files")
90        self.resize(700, 300)
91
92    def browse(self):
93        directory = QFileDialog.getExistingDirectory(self, "Find Files",
94                QDir.currentPath())
95
96        if directory:
97            if self.directoryComboBox.findText(directory) == -1:
98                self.directoryComboBox.addItem(directory)
99
100            self.directoryComboBox.setCurrentIndex(self.directoryComboBox.findText(directory))
101
102    @staticmethod
103    def updateComboBox(comboBox):
104        if comboBox.findText(comboBox.currentText()) == -1:
105            comboBox.addItem(comboBox.currentText())
106
107    def find(self):
108        self.filesTable.setRowCount(0)
109
110        fileName = self.fileComboBox.currentText()
111        text = self.textComboBox.currentText()
112        path = self.directoryComboBox.currentText()
113
114        self.updateComboBox(self.fileComboBox)
115        self.updateComboBox(self.textComboBox)
116        self.updateComboBox(self.directoryComboBox)
117
118        self.currentDir = QDir(path)
119        if not fileName:
120            fileName = "*"
121        files = self.currentDir.entryList([fileName],
122                QDir.Files | QDir.NoSymLinks)
123
124        if text:
125            files = self.findFiles(files, text)
126        self.showFiles(files)
127
128    def findFiles(self, files, text):
129        progressDialog = QProgressDialog(self)
130
131        progressDialog.setCancelButtonText("&Cancel")
132        progressDialog.setRange(0, files.count())
133        progressDialog.setWindowTitle("Find Files")
134
135        foundFiles = []
136
137        for i in range(files.count()):
138            progressDialog.setValue(i)
139            progressDialog.setLabelText("Searching file number %d of %d..." % (i, files.count()))
140            QApplication.processEvents()
141
142            if progressDialog.wasCanceled():
143                break
144
145            inFile = QFile(self.currentDir.absoluteFilePath(files[i]))
146
147            if inFile.open(QIODevice.ReadOnly):
148                stream = QTextStream(inFile)
149                while not stream.atEnd():
150                    if progressDialog.wasCanceled():
151                        break
152                    line = stream.readLine()
153                    if text in line:
154                        foundFiles.append(files[i])
155                        break
156
157        progressDialog.close()
158
159        return foundFiles
160
161    def showFiles(self, files):
162        for fn in files:
163            file = QFile(self.currentDir.absoluteFilePath(fn))
164            size = QFileInfo(file).size()
165
166            fileNameItem = QTableWidgetItem(fn)
167            fileNameItem.setFlags(fileNameItem.flags() ^ Qt.ItemIsEditable)
168            sizeItem = QTableWidgetItem("%d KB" % (int((size + 1023) / 1024)))
169            sizeItem.setTextAlignment(Qt.AlignVCenter | Qt.AlignRight)
170            sizeItem.setFlags(sizeItem.flags() ^ Qt.ItemIsEditable)
171
172            row = self.filesTable.rowCount()
173            self.filesTable.insertRow(row)
174            self.filesTable.setItem(row, 0, fileNameItem)
175            self.filesTable.setItem(row, 1, sizeItem)
176
177        self.filesFoundLabel.setText("%d file(s) found (Double click on a file to open it)" % len(files))
178
179    def createButton(self, text, member):
180        button = QPushButton(text)
181        button.clicked.connect(member)
182        return button
183
184    def createComboBox(self, text=""):
185        comboBox = QComboBox()
186        comboBox.setEditable(True)
187        comboBox.addItem(text)
188        comboBox.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
189        return comboBox
190
191    def createFilesTable(self):
192        self.filesTable = QTableWidget(0, 2)
193        self.filesTable.setSelectionBehavior(QAbstractItemView.SelectRows)
194
195        self.filesTable.setHorizontalHeaderLabels(("File Name", "Size"))
196        self.filesTable.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
197        self.filesTable.verticalHeader().hide()
198        self.filesTable.setShowGrid(False)
199
200        self.filesTable.cellActivated.connect(self.openFileOfItem)
201
202    def openFileOfItem(self, row, column):
203        item = self.filesTable.item(row, 0)
204
205        QDesktopServices.openUrl(QUrl(self.currentDir.absoluteFilePath(item.text())))
206
207
208if __name__ == '__main__':
209
210    import sys
211
212    app = QApplication(sys.argv)
213    window = Window()
214    window.show()
215    sys.exit(app.exec_())
216