Hello
What is a good modern way in Squeak to load a picture from the web? I am looking for an equivalent of | pngPicURL aStream | pngPicURL := 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Squeak-x11.png/617px-Squeak-x11.png'. aStream := HTTPSocket httpGet: pngPicURL. (ImageReadWriter formFromStream: aStream) asMorph openInHand And answer or just comments are welcome. Thank you. Kind regards Hannes |
What about ... ... I think we should add a #fromUrl: besides Form class >> #fromFileNamed: and there distinguish between file paths and HTTP urls etc: Form fromUrl: 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Squeak-x11.png/617px-Squeak-x11.png'. Best, Marcel
|
Thanks for answering, Marcel
On 3/21/19, Marcel Taeumel <[hidden email]> wrote: > What about ... > > Form fromBinaryStream: (WebClient httpGet: > 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Squeak-x11.png/617px-Squeak-x11.png') > contentStream. Unfortunately a debugger window comes up [1] > ... I think we should add a #fromUrl: besides Form class >> #fromFileNamed: > and there distinguish between file paths and HTTP urls etc: > > Form fromUrl: > 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Squeak-x11.png/617px-Squeak-x11.png'. Yes, that would be fine to create a new convenience method Form fromUrl: after we have figured out the solution. -- Hannes [1] SecureSocketStream(Object)>>doesNotUnderstand: #reset Receiver: SecureSocketStream[inbuf:4kb/outbuf:4kb] Arguments and temporary variables: aMessage: reset exception: MessageNotUnderstood: SecureSocketStream>>reset resumeValue: nil Receiver's instance variables: recentlyRead: 0 socket: a Socket[destroyed] inBuffer: #[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0...etc... outBuffer: #[71 69 84 32 47 119 105 107 105 112 101 100 105 97 47 99 111 109 10...etc... inNextToWrite: 1 outNextToWrite: 1 lastRead: 0 timeout: 45 autoFlush: true bufferSize: 4096 binary: true shouldSignal: true ssl: nil sendBuf: #[23 3 3 0 232 61 247 6 24 144 238 146 249 51 100 82 127 29 104 195 17...etc... readBuf: #[203 4 102 97 151 46 237 173 196 165 168 136 21 56 21 225 6 113 183 1...etc... decoded: a ReadStream certIssues: 0 [] in ImageReadWriter class>>formFromStream: Receiver: ImageReadWriter Arguments and temporary variables: <<error during printing> Receiver's instance variables: superclass: Object methodDict: a MethodDictionary(#atEnd->(ImageReadWriter>>#atEnd "a CompiledMethod...etc... format: 65537 instanceVariables: #('stream') organization: ('accessing' nextImage nextPutImage:) ('stream access' atEnd close...etc... subclasses: {PCXReadWriter . XBMReadWriter . JPEGReadWriter . JPEGReadWriter2 ....etc... name: #ImageReadWriter classPool: a Dictionary(#ImageNotStoredSignal->nil #MagicNumberErrorSignal->nil...etc... sharedPools: nil environment: nil category: #'Graphics-Files' |
It works if I replace
#contentStream with content asByteArray readStream Form fromBinaryStream: (WebClient httpGet: 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Squeak-x11.png/617px-Squeak-x11.png') content asByteArray readStream. On 3/21/19, H. Hirzel <[hidden email]> wrote: > Thanks for answering, Marcel > > On 3/21/19, Marcel Taeumel <[hidden email]> wrote: >> What about ... >> >> Form fromBinaryStream: (WebClient httpGet: >> 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Squeak-x11.png/617px-Squeak-x11.png') >> contentStream. > > Unfortunately a debugger window comes up [1] > > >> ... I think we should add a #fromUrl: besides Form class >> >> #fromFileNamed: >> and there distinguish between file paths and HTTP urls etc: >> >> Form fromUrl: >> 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Squeak-x11.png/617px-Squeak-x11.png'. > > Yes, that would be fine to create a new convenience method > > Form fromUrl: > > after we have figured out the solution. > > -- Hannes > > [1] > SecureSocketStream(Object)>>doesNotUnderstand: #reset > Receiver: SecureSocketStream[inbuf:4kb/outbuf:4kb] > Arguments and temporary variables: > aMessage: reset > exception: MessageNotUnderstood: SecureSocketStream>>reset > resumeValue: nil > Receiver's instance variables: > recentlyRead: 0 > socket: a Socket[destroyed] > inBuffer: #[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0...etc... > outBuffer: #[71 69 84 32 47 119 105 107 105 112 101 100 105 97 47 > 99 111 109 10...etc... > inNextToWrite: 1 > outNextToWrite: 1 > lastRead: 0 > timeout: 45 > autoFlush: true > bufferSize: 4096 > binary: true > shouldSignal: true > ssl: nil > sendBuf: #[23 3 3 0 232 61 247 6 24 144 238 146 249 51 100 82 127 > 29 104 195 17...etc... > readBuf: #[203 4 102 97 151 46 237 173 196 165 168 136 21 56 21 225 > 6 113 183 1...etc... > decoded: a ReadStream > certIssues: 0 > > [] in ImageReadWriter class>>formFromStream: > Receiver: ImageReadWriter > Arguments and temporary variables: > <<error during printing> > Receiver's instance variables: > superclass: Object > methodDict: a MethodDictionary(#atEnd->(ImageReadWriter>>#atEnd "a > CompiledMethod...etc... > format: 65537 > instanceVariables: #('stream') > organization: ('accessing' nextImage nextPutImage:) > ('stream access' atEnd close...etc... > subclasses: {PCXReadWriter . XBMReadWriter . JPEGReadWriter . > JPEGReadWriter2 ....etc... > name: #ImageReadWriter > classPool: a Dictionary(#ImageNotStoredSignal->nil > #MagicNumberErrorSignal->nil...etc... > sharedPools: nil > environment: nil > category: #'Graphics-Files' > |
Huh? WebResponse >> #contentStream has been there since 2012. Best, Marcel
|
Yes, WebResponse >> #contentStream is there but if I use it I get an
error message, see my earlier mail in this thread where I include the details. It seems that the stream is asked for #reset and that does not go well with the stream WebResponse uses. SecureSocketStream(Object)>>doesNotUnderstand: #reset So getting the whole content first and then to ask for a new stream is a workaround. It is a solution but maybe not the solution. More investigation needed.... HH On 3/22/19, Marcel Taeumel <[hidden email]> wrote: > Huh? WebResponse >> #contentStream has been there since 2012. > > Best, > Marcel > Am 21.03.2019 20:37:40 schrieb H. Hirzel <[hidden email]>: > It works if I replace > > #contentStream > > with > > content asByteArray readStream > > Form fromBinaryStream: (WebClient httpGet: > 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Squeak-x11.png/617px-Squeak-x11.png') > content asByteArray readStream. > > On 3/21/19, H. Hirzel wrote: >> Thanks for answering, Marcel >> >> On 3/21/19, Marcel Taeumel wrote: >>> What about ... >>> >>> Form fromBinaryStream: (WebClient httpGet: >>> 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Squeak-x11.png/617px-Squeak-x11.png') >>> contentStream. >> >> Unfortunately a debugger window comes up [1] >> >> >>> ... I think we should add a #fromUrl: besides Form class >> >>> #fromFileNamed: >>> and there distinguish between file paths and HTTP urls etc: >>> >>> Form fromUrl: >>> 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Squeak-x11.png/617px-Squeak-x11.png'. >> >> Yes, that would be fine to create a new convenience method >> >> Form fromUrl: >> >> after we have figured out the solution. >> >> -- Hannes >> >> [1] >> SecureSocketStream(Object)>>doesNotUnderstand: #reset >> Receiver: SecureSocketStream[inbuf:4kb/outbuf:4kb] >> Arguments and temporary variables: >> aMessage: reset >> exception: MessageNotUnderstood: SecureSocketStream>>reset >> resumeValue: nil >> Receiver's instance variables: >> recentlyRead: 0 >> socket: a Socket[destroyed] >> inBuffer: #[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 >> 0 0 0 0 0 0...etc... >> outBuffer: #[71 69 84 32 47 119 105 107 105 112 101 100 105 97 47 >> 99 111 109 10...etc... >> inNextToWrite: 1 >> outNextToWrite: 1 >> lastRead: 0 >> timeout: 45 >> autoFlush: true >> bufferSize: 4096 >> binary: true >> shouldSignal: true >> ssl: nil >> sendBuf: #[23 3 3 0 232 61 247 6 24 144 238 146 249 51 100 82 127 >> 29 104 195 17...etc... >> readBuf: #[203 4 102 97 151 46 237 173 196 165 168 136 21 56 21 225 >> 6 113 183 1...etc... >> decoded: a ReadStream >> certIssues: 0 >> >> [] in ImageReadWriter class>>formFromStream: >> Receiver: ImageReadWriter >> Arguments and temporary variables: >> >> Receiver's instance variables: >> superclass: Object >> methodDict: a MethodDictionary(#atEnd->(ImageReadWriter>>#atEnd "a >> CompiledMethod...etc... >> format: 65537 >> instanceVariables: #('stream') >> organization: ('accessing' nextImage nextPutImage:) >> ('stream access' atEnd close...etc... >> subclasses: {PCXReadWriter . XBMReadWriter . JPEGReadWriter . >> JPEGReadWriter2 ....etc... >> name: #ImageReadWriter >> classPool: a Dictionary(#ImageNotStoredSignal->nil >> #MagicNumberErrorSignal->nil...etc... >> sharedPools: nil >> environment: nil >> category: #'Graphics-Files' >> > > |
> On 22.03.2019, at 09:35, H. Hirzel <[hidden email]> wrote: > > Yes, WebResponse >> #contentStream is there but if I use it I get an > error message, see my earlier mail in this thread where I include the > details. > > It seems that the stream is asked for #reset and that does not go well > with the stream WebResponse uses. > > SecureSocketStream(Object)>>doesNotUnderstand: #reset > > So getting the whole content first and then to ask for a new stream is > a workaround. > It is a solution but maybe not the solution. > > More investigation needed.... No. The WebResponse stream is a non-resettable stream, it comes effectively from the network, so you can't meaningfully #reset it :) I can think of several ways to counter that, but not today :D Best regards -Tobias > > HH > > > On 3/22/19, Marcel Taeumel <[hidden email]> wrote: >> Huh? WebResponse >> #contentStream has been there since 2012. >> >> Best, >> Marcel >> Am 21.03.2019 20:37:40 schrieb H. Hirzel <[hidden email]>: >> It works if I replace >> >> #contentStream >> >> with >> >> content asByteArray readStream >> >> Form fromBinaryStream: (WebClient httpGet: >> 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Squeak-x11.png/617px-Squeak-x11.png') >> content asByteArray readStream. >> >> On 3/21/19, H. Hirzel wrote: >>> Thanks for answering, Marcel >>> >>> On 3/21/19, Marcel Taeumel wrote: >>>> What about ... >>>> >>>> Form fromBinaryStream: (WebClient httpGet: >>>> 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Squeak-x11.png/617px-Squeak-x11.png') >>>> contentStream. >>> >>> Unfortunately a debugger window comes up [1] >>> >>> >>>> ... I think we should add a #fromUrl: besides Form class >> >>>> #fromFileNamed: >>>> and there distinguish between file paths and HTTP urls etc: >>>> >>>> Form fromUrl: >>>> 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Squeak-x11.png/617px-Squeak-x11.png'. >>> >>> Yes, that would be fine to create a new convenience method >>> >>> Form fromUrl: >>> >>> after we have figured out the solution. >>> >>> -- Hannes >>> >>> [1] >>> SecureSocketStream(Object)>>doesNotUnderstand: #reset >>> Receiver: SecureSocketStream[inbuf:4kb/outbuf:4kb] >>> Arguments and temporary variables: >>> aMessage: reset >>> exception: MessageNotUnderstood: SecureSocketStream>>reset >>> resumeValue: nil >>> Receiver's instance variables: >>> recentlyRead: 0 >>> socket: a Socket[destroyed] >>> inBuffer: #[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 >>> 0 0 0 0 0 0...etc... >>> outBuffer: #[71 69 84 32 47 119 105 107 105 112 101 100 105 97 47 >>> 99 111 109 10...etc... >>> inNextToWrite: 1 >>> outNextToWrite: 1 >>> lastRead: 0 >>> timeout: 45 >>> autoFlush: true >>> bufferSize: 4096 >>> binary: true >>> shouldSignal: true >>> ssl: nil >>> sendBuf: #[23 3 3 0 232 61 247 6 24 144 238 146 249 51 100 82 127 >>> 29 104 195 17...etc... >>> readBuf: #[203 4 102 97 151 46 237 173 196 165 168 136 21 56 21 225 >>> 6 113 183 1...etc... >>> decoded: a ReadStream >>> certIssues: 0 >>> >>> [] in ImageReadWriter class>>formFromStream: >>> Receiver: ImageReadWriter >>> Arguments and temporary variables: >>> >>> Receiver's instance variables: >>> superclass: Object >>> methodDict: a MethodDictionary(#atEnd->(ImageReadWriter>>#atEnd "a >>> CompiledMethod...etc... >>> format: 65537 >>> instanceVariables: #('stream') >>> organization: ('accessing' nextImage nextPutImage:) >>> ('stream access' atEnd close...etc... >>> subclasses: {PCXReadWriter . XBMReadWriter . JPEGReadWriter . >>> JPEGReadWriter2 ....etc... >>> name: #ImageReadWriter >>> classPool: a Dictionary(#ImageNotStoredSignal->nil >>> #MagicNumberErrorSignal->nil...etc... >>> sharedPools: nil >>> environment: nil >>> category: #'Graphics-Files' >>> >> >> > |
Hello Tobias
On 3/22/19, Tobias Pape <[hidden email]> wrote: > >> On 22.03.2019, at 09:35, H. Hirzel <[hidden email]> wrote: >> >> Yes, WebResponse >> #contentStream is there but if I use it I get an >> error message, see my earlier mail in this thread where I include the >> details. >> >> It seems that the stream is asked for #reset and that does not go well >> with the stream WebResponse uses. >> >> SecureSocketStream(Object)>>doesNotUnderstand: #reset >> >> So getting the whole content first and then to ask for a new stream is >> a workaround. >> It is a solution but maybe not the solution. >> >> More investigation needed.... > > No. The WebResponse stream is a non-resettable stream, it comes effectively > from the network, so you can't meaningfully #reset it :) Sure, I am aware that WebResponse is a non-resettable stream. To reset the SecureSocketStream stream would mean that I have to get the content again from the web. This is not be what is wanted most of the time. So somewhere we need to - get the content of the WebResponse object - convert it to an in-memory stream. content asByteArray readStream. theUrl := 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Squeak-x11.png/617px-Squeak-x11.png'. Form fromBinaryStream: (WebClient httpGet: theUrl) content asByteArray readStream. > I can think of several ways to counter that, but not today :D Yes, I am interested in this, even in 3 .. 300 days time :-) in the meantime I use the following patch in my application (method name and class suggested by Marcel) -- a useful solution.. NEW method Form class>>fromUrl: anUrlString similar to Form fromFileNamed: fileName Yours and Marcel's feedback was helpful for me to get at this point. ................................................................................................................................... 'From Squeak5.2 of 13 December 2018 [latest update: #18221] on 22 March 2019 at 11:37:44 am'! !Form class methodsFor: '*Add-Ons-BaseImage52patches' stamp: 'hjh 3/22/2019 11:37'! fromUrl: anUrlString "It is advisable to call this in a separate process" self fromBinaryStream: (WebClient httpGet: anUrlString) content asByteArray readStream. ! ! ................................................................................................................................... An issue I had is that depending on the URL this may lock up the image, so it is advisable to put the call to #fromUrl: into another process. Regards Hannes |
On Fri, 22 Mar 2019, H. Hirzel wrote:
> Hello Tobias > > On 3/22/19, Tobias Pape <[hidden email]> wrote: >> >>> On 22.03.2019, at 09:35, H. Hirzel <[hidden email]> wrote: >>> >>> Yes, WebResponse >> #contentStream is there but if I use it I get an >>> error message, see my earlier mail in this thread where I include the >>> details. >>> >>> It seems that the stream is asked for #reset and that does not go well >>> with the stream WebResponse uses. >>> >>> SecureSocketStream(Object)>>doesNotUnderstand: #reset >>> >>> So getting the whole content first and then to ask for a new stream is >>> a workaround. >>> It is a solution but maybe not the solution. >>> >>> More investigation needed.... >> >> No. The WebResponse stream is a non-resettable stream, it comes effectively >> from the network, so you can't meaningfully #reset it :) > > Sure, I am aware that WebResponse is a non-resettable stream. > > To reset the SecureSocketStream stream would mean that I have to get > the content again from the web. This is not be what is wanted most of > the time. Or you could just pick the right converter based on the mime type of the response. #reset is sent by ImageReadWriter, because it uses trial and error to find the right converter for the image. Levente > > So somewhere we need to > - get the content of the WebResponse object > - convert it to an in-memory stream. > > > content asByteArray readStream. > > > theUrl := 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Squeak-x11.png/617px-Squeak-x11.png'. > > Form fromBinaryStream: (WebClient httpGet: theUrl) content > asByteArray readStream. > >> I can think of several ways to counter that, but not today :D > Yes, I am interested in this, even in 3 .. 300 days time :-) > > in the meantime I use the following patch in my application (method > name and class suggested by Marcel) -- a useful solution.. > > NEW method > Form class>>fromUrl: anUrlString > > similar to > > Form fromFileNamed: fileName > > > Yours and Marcel's feedback was helpful for me to get at this point. > > ................................................................................................................................... > > 'From Squeak5.2 of 13 December 2018 [latest update: #18221] on 22 > March 2019 at 11:37:44 am'! > > !Form class methodsFor: '*Add-Ons-BaseImage52patches' stamp: 'hjh > 3/22/2019 11:37'! > fromUrl: anUrlString > "It is advisable to call this in a separate process" > > self fromBinaryStream: (WebClient httpGet: anUrlString) content > asByteArray readStream. > ! ! > > ................................................................................................................................... > > An issue I had is that depending on the URL this may lock up the > image, so it is advisable to put the call to #fromUrl: into another > process. > > > Regards > > Hannes |
In reply to this post by Hannes Hirzel
Hi Hannes,
I have implemented this in various ways in my InternetArchive client library—a work in (slow) progress over the last 3+ years. Note I did not realize a similar library had been written for Sophie ten years ago. This one was based on the Python client library which uses the current InternetArchive API. I finally made it public in its current state here: Some examples for how to use it live in the IAExample* classes. The following will display the famous Byte cover and restore the display after a click: (IAItem named: 'byte-magazine-1981-08' ) displayItemTileJpeg Anyway, you can find my code in the 'displaying graphics' instance method category of IADownload. Also note that you may be interested in seeing Form class>>#openImageInWindow: Here is how I implemented some JPEG download & display in Squeak 5.2. These methods are taken from the IADownload and perhaps IAItem classes. getResponse "returns a WebResponse object" ^ response := WebClient httpGet: self url. downloadJpegIntoForm | s contents jww imageExtent form | s := ByteArray new writeStream. s binary. self getResponse. response streamTo: s size: response contentLength progress: nil. response close. contents := s contents. (JPEGReadWriter2 understandsImageFormat: (contents readStream)) ifFalse: [^ self error: 'cannot read jpeg']. jww := JPEGReadWriter2 new. imageExtent := jww imageExtent: contents. form := Form extent: imageExtent depth: 32. jww uncompress: contents into: form doDithering: false. ^ form displayJpeg | form | form := self downloadJpegIntoForm. Display restoreAfter: [ form displayOn: Display ]. displayJpegInMorph | form morph | form := self downloadJpegIntoForm. morph := BorderedMorph newBounds: (form bounds). Display restoreAfter: [ form displayOn: Display ]. displayJpegInWindow | form morph window | self flag: #seeFormClassSideForUtilityMethod. form := self downloadJpegIntoForm. morph := form asMorph. window := SystemWindow labelled: 'image'. window " addMorphCentered: (form asMorph);" addMorph: (form asMorph) frame: (0.0@0.0 corner: 1.0@1.0) . " addMorph: (form asMorph) fullFrame: (SystemWindow menuBoxFrame) ." window openInWorldExtent: (form extent scaleBy: 1.2 @ 1.2). Let me know if I can help further. I believe there is also code in there for displaying downloaded GIFs and animated GIFs but I could be wrong. Best, Tim
|
Self-reply:
I just noticed the #displayJpegInMorph method below is garbage and can be ignored. Also, I believe there is a higher-level abstraction above JPEGReadWriter2. If I recall correctly, this higher level abstraction is connected to the Services architecture and can display many different formats of image files from disk. I didn't dig into it much and could be mistaken, but with further research it could prove useful. Thanks again, Tim J
|
Free forum by Nabble | Edit this page |