eric6/Helpviewer/Network/FileReply.py

changeset 6942
2602857055c5
parent 6891
93f82da09f22
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2012 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a network reply class for directory resources.
8 """
9
10 from __future__ import unicode_literals
11 try:
12 str = unicode
13 except NameError:
14 pass
15
16 from PyQt5.QtCore import Qt, QByteArray, QTimer, QDir, QIODevice, QUrl, QBuffer
17 from PyQt5.QtGui import QPixmap
18 from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
19 from PyQt5.QtWebKit import QWebSettings
20
21 import UI.PixmapCache
22
23 import Utilities
24
25
26 dirListPage_html = """\
27 <?xml version="1.0" encoding="UTF-8" ?>
28 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
29 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
30 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
31 <head>
32 <title>{0}</title>
33 <style type="text/css">
34 body {{
35 padding: 3em 0em;
36 background: -webkit-gradient(linear, left top, left bottom, from(#85784A),
37 to(#FDFDFD), color-stop(0.5, #FDFDFD));
38 background-repeat: repeat-x;
39 }}
40 #box {{
41 background: white;
42 border: 1px solid #85784A;
43 width: 80%;
44 padding: 30px;
45 margin: auto;
46 -webkit-border-radius: 0.8em;
47 }}
48 h1 {{
49 font-size: 130%;
50 font-weight: bold;
51 border-bottom: 1px solid #85784A;
52 }}
53 th {{
54 background-color: #B8B096;
55 color: black;
56 }}
57 table {{
58 border: solid 1px #85784A;
59 margin: 5px 0;
60 width: 100%;
61 }}
62 tr.odd {{
63 background-color: white;
64 color: black;
65 }}
66 tr.even {{
67 background-color: #CEC9B8;
68 color: black;
69 }}
70 .modified {{
71 text-align: left;
72 vertical-align: top;
73 white-space: nowrap;
74 }}
75 .size {{
76 text-align: right;
77 vertical-align: top;
78 white-space: nowrap;
79 padding-right: 22px;
80 }}
81 .name {{
82 text-align: left;
83 vertical-align: top;
84 white-space: pre-wrap;
85 width: 100%
86 }}
87 {1}
88 </style>
89 </head>
90 <body>
91 <div id="box">
92 <h1>{2}</h1>
93 {3}
94 <table align="center" cellspacing="0" width="90%">
95 {4}
96 </table>
97 </div>
98 </body>
99 </html>
100 """
101
102
103 class FileReply(QNetworkReply):
104 """
105 Class implementing a network reply for directory resources.
106 """
107 def __init__(self, url, parent=None):
108 """
109 Constructor
110
111 @param url requested FTP URL (QUrl)
112 @param parent reference to the parent object (QObject)
113 """
114 super(FileReply, self).__init__(parent)
115
116 self.__content = QByteArray()
117 self.__units = ["Bytes", "KB", "MB", "GB", "TB",
118 "PB", "EB", "ZB", "YB"]
119
120 if url.path() == "":
121 url.setPath("/")
122 self.setUrl(url)
123
124 QTimer.singleShot(0, self.__loadDirectory)
125
126 def abort(self):
127 """
128 Public slot to abort the operation.
129 """
130 # do nothing
131 pass
132
133 def bytesAvailable(self):
134 """
135 Public method to determined the bytes available for being read.
136
137 @return bytes available (integer)
138 """
139 return self.__content.size()
140
141 def isSequential(self):
142 """
143 Public method to check for sequential access.
144
145 @return flag indicating sequential access (boolean)
146 """
147 return True
148
149 def readData(self, maxlen):
150 """
151 Public method to retrieve data from the reply object.
152
153 @param maxlen maximum number of bytes to read (integer)
154 @return string containing the data (bytes)
155 """
156 if self.__content.size():
157 len_ = min(maxlen, self.__content.size())
158 buffer = bytes(self.__content[:len_])
159 self.__content.remove(0, len_)
160 return buffer
161 else:
162 return b""
163
164 def __cssLinkClass(self, icon, size=32):
165 """
166 Private method to generate a link class with an icon.
167
168 @param icon icon to be included (QIcon)
169 @param size size of the icon to be generated (integer)
170 @return CSS class string (string)
171 """
172 cssString = \
173 """a.{{0}} {{{{\n"""\
174 """ padding-left: {0}px;\n"""\
175 """ background: transparent url(data:image/png;base64,{1})"""\
176 """ no-repeat center left;\n"""\
177 """ font-weight: bold;\n"""\
178 """}}}}\n"""
179 pixmap = icon.pixmap(size, size)
180 imageBuffer = QBuffer()
181 imageBuffer.open(QIODevice.ReadWrite)
182 if not pixmap.save(imageBuffer, "PNG"):
183 # write a blank pixmap on error
184 pixmap = QPixmap(size, size)
185 pixmap.fill(Qt.transparent)
186 imageBuffer.buffer().clear()
187 pixmap.save(imageBuffer, "PNG")
188 return cssString.format(
189 size + 4,
190 str(imageBuffer.buffer().toBase64(), encoding="ascii"))
191
192 def __loadDirectory(self):
193 """
194 Private slot loading the directory and preparing the listing page.
195 """
196 qdir = QDir(self.url().toLocalFile())
197 dirItems = qdir.entryInfoList(
198 QDir.AllEntries | QDir.Hidden | QDir.NoDotAndDotDot,
199 QDir.Name | QDir.DirsFirst)
200
201 u = self.url()
202 if not u.path().endswith("/"):
203 u.setPath(u.path() + "/")
204
205 baseUrl = self.url().toString()
206 basePath = u.path()
207
208 linkClasses = {}
209 iconSize = QWebSettings.globalSettings().fontSize(
210 QWebSettings.DefaultFontSize)
211
212 parent = u.resolved(QUrl(".."))
213 if parent.isParentOf(u):
214 icon = UI.PixmapCache.getIcon("up.png")
215 linkClasses["link_parent"] = \
216 self.__cssLinkClass(icon, iconSize).format("link_parent")
217 parentStr = self.tr(
218 """ <p><a class="link_parent" href="{0}">"""
219 """Change to parent directory</a></p>"""
220 ).format(parent.toString())
221 else:
222 parentStr = ""
223
224 row = \
225 """ <tr class="{0}">"""\
226 """<td class="name"><a class="{1}" href="{2}">{3}</a></td>"""\
227 """<td class="size">{4}</td>"""\
228 """<td class="modified">{5}</td>"""\
229 """</tr>\n"""
230 table = self.tr(
231 """ <tr>"""
232 """<th align="left">Name</th>"""
233 """<th>Size</th>"""
234 """<th align="left">Last modified</th>"""
235 """</tr>\n"""
236 )
237
238 i = 0
239 for item in dirItems:
240 name = item.fileName()
241 if item.isDir() and not name.endswith("/"):
242 name += "/"
243 child = u.resolved(QUrl(name.replace(":", "%3A")))
244
245 if item.isFile():
246 size = item.size()
247 unit = 0
248 while size:
249 newSize = size // 1024
250 if newSize and unit < len(self.__units):
251 size = newSize
252 unit += 1
253 else:
254 break
255
256 sizeStr = self.tr("{0} {1}", "size unit")\
257 .format(size, self.__units[unit])
258 linkClass = "link_file"
259 if linkClass not in linkClasses:
260 icon = UI.PixmapCache.getIcon("fileMisc.png")
261 linkClasses[linkClass] = \
262 self.__cssLinkClass(icon, iconSize).format(linkClass)
263 else:
264 sizeStr = ""
265 linkClass = "link_dir"
266 if linkClass not in linkClasses:
267 icon = UI.PixmapCache.getIcon("dirClosed.png")
268 linkClasses[linkClass] = \
269 self.__cssLinkClass(icon, iconSize).format(linkClass)
270 table += row.format(
271 i == 0 and "odd" or "even",
272 linkClass,
273 child.toString(),
274 Utilities.html_encode(item.fileName()),
275 sizeStr,
276 item.lastModified().toString("yyyy-MM-dd hh:mm"),
277 )
278 i = 1 - i
279
280 content = dirListPage_html.format(
281 Utilities.html_encode(baseUrl),
282 "".join(linkClasses.values()),
283 self.tr("Listing of {0}").format(basePath),
284 parentStr,
285 table
286 )
287 self.__content = QByteArray(content.encode("utf8"))
288 self.__content.append(512 * b' ')
289
290 self.open(QIODevice.ReadOnly | QIODevice.Unbuffered)
291 self.setHeader(
292 QNetworkRequest.ContentTypeHeader, "text/html; charset=UTF-8")
293 self.setHeader(
294 QNetworkRequest.ContentLengthHeader, self.__content.size())
295 self.setAttribute(QNetworkRequest.HttpStatusCodeAttribute, 200)
296 self.setAttribute(QNetworkRequest.HttpReasonPhraseAttribute, "Ok")
297 self.metaDataChanged.emit()
298 self.downloadProgress.emit(
299 self.__content.size(), self.__content.size())
300 self.readyRead.emit()
301 self.finished.emit()

eric ide

mercurial