Ha, I also needed this a week ago (for an otherwise all-Julia poster…)
The advantage of returning something that will show
as an image is that interfaces like IJulia or Juno, will know what to do automatically:
That actually is a very good point. Is there a way to define some sort of show
function that would output an image without changing the structure? I guess I’d have to make it a struct
then…
Having those as .eps
would also be great such that those can be used in LaTeX during a build process.
That’s the idea - wrap it in a struct and override Base.show
. Though there’s an attractive simplicity in just returning a BitArray
If you do want to go with a wrapper type you might use QRCode
though you’d have to rename your package to QRCodes.jl (module name pluralization is an ecosystem convention to deal with this, eg Colors.Color
, Dates.Date
, Rotations.Rotation
etc etc)
If you made a wrapper, what interface would you want it to conform to other than having a nice default show
method? Eg, is it a graphical object, an AbstractMatrix
or something else entirely? Perhaps best to keep it simple:
struct QRCode
data::BitMatrix
# Add more things here, eg the code version,
# original source data string, etc
end
function Base.show(io::IO, code::QRCode)
# You can include the original text in a header line too if it's part of QRCode
# Minimal ANSI color text representation:
for i=1:size(code.data,1)
println(io, join([code.data[i,j] ? "\e[30m██" : "\e[97m██" for j=1:size(code.data,2)]))
end
end
# possibly also `show(io::IO, ::MIME"text/html", qrc::QRCode)` for IJulia and the like?
Looking at your API I think it would be useful to make the export list more minimal. Having a small export list means you introduce few concepts (types - nouns; functions - verbs) for users to learn about. A minimal set of exported names is also less likely to clash with other packages. Here’s some things to try:
- Avoid exporting really generic names like
getversion
if possible. The question to ask is: do I have a similarly generic meaning to go with each generically named function I export? If not, either make the names more specific, resist exporting them, or find a generic function from a different package which has the correct semantics and extend that. - It’s possible to model
ErrCorrLevel
with types like you’ve done but modelling it with values would be simpler. I’d be inclined to just pass symbol values:low
,:medium
,:high
for the error correction levels, then you can remove those extra types. Similarly forMode
and its subtypes. - With a type
QRCode
to model your data, you may get away with exporting onlyQRCode
as the public API:qrcode
would become the constructorQRCode
exportqrcode
might becomesave(path, code::QRCode)
getversion
andgetmode
might be handled several ways depending on your intent:- renamed as exported functions
qrversion
andqrmode
- accessible as public fields of
QRCode
- documented as public non-exported functions
QRCodes.version
andQRCodes.mode
- renamed as exported functions
https://github.com/rafaqz/UnicodeGraphics.jl can also convert arrays to braile and block chars. Pretty much the same thing but fast (for showing live simulations over ssh hahah)
Thank you very much for all the advice. It all looks like fun things to do with Julia, especially as a learning exercise. I think I’m leaning towards leaving it as simple as possible, add a few options for exporting graphics (png, eps, pdf, svg…) and let people go nuts if they want something more. But I’ll think about it more.
I’m surprised you tell me to export less things, I thought I was being minimalistic already ^^
But thank you, I’ll consider your advice.
As for ErrCorrLevel
and Mode
, I love type programming too much to give them up (I’m a Haskell kind of guy). I really enjoyed combining multiple dispatch and types to define the different encoding functions.
Thanks again for your feedback!
The problem with the block chars approach (used by UnicodeGraphics and ImageInTerminal) is that the line spacing can introduce thin white lines through the image. Not much of a problem for plots, but it makes QR-codes unscannable. For example, on my (MacOS default) terminal it looks like this:
Yeah it will in atom. It doesnt in a lot of linux terminal fonts which is the real use case. Its probably not usefull for this problem, I was just pointing out there are easier ways than unicodeplots to print arrays and in the repl as people were suggesting that
Great! Programming with multiple dispatch can be very satisfying. There are some circumstances where dispatching on types like this does make a lot sense (from an API standpoint). For example, if the user needs to be able to define their own type and expect the internals of the package to dispatch back to a user-defined function. I don’t know much about QR codes, but I guess they’re defined by a standard and not really extensible in this way. Another case would be when you’ve got tight code in an inner loop and you can’t afford the cost of dispatching on the value. Using types in that case could get a speedup because the compiler will generate type-specialized code for each “selector type”.