[ANN] QRCode.jl: Create QR Code within Julia

I’m preparing a poster for a conference next week, and you got me sold on this #betterposter thing @cormullion. I’ll be linking to one of my Julia packages on github, but I’d love to shove Julia in there if I can. But unlike you I’d rather play it a bit more safe and have the julia colors a bit washed out. So I added

setopacity(0.75)

which resulted in:
screenshot_2019-07-29_15%3A46%3A13

Hope that’s not “illigal” or something :grimacing:

2 Likes

:slight_smile: better watch Professor Johnson’s Keynote first in case I’m doing it wrong… You’re welcome to pan for gold in my code… :smile:

1 Like

I love UnicodePlots! I don’t think any QR code could read them though, so that’s probably not gonna work out. ImageInTerminal looks better, but it seems it’s very terminal-dependent!

This is amazing!!

Definitely watching that soon, thanks :slight_smile:

Looks great to me! Nothing illegal going on!

This is exactly what I hoped would happen when I decided to expose the BitArray instead of an image, I wanted people to use it in creative ways. Thanks guys! It was totally worth it ^^

2 Likes

If you wanted to, you could return an Array{Gray{Bool},2} (from Colors.jl) which would behave as an array with respect to functions like getindex but like an image for operations like show and save. Best of both worlds, somehow.

Interesting! However I might lose the ability to draw QR code in different colors, or do something more unexpected maybe. It’s very simple to transform to Array of Gray (Gray.(.! qrcode("hello"))), but the opposite is more complicated…

The inverse of that would be .! Bool.(ans), so not really complicated, but I see what you mean.

Ha, I also needed this a week ago (for an otherwise all-Julia poster…)

1 Like

The advantage of returning something that will show as an image is that interfaces like IJulia or Juno, will know what to do automatically:

34

3 Likes

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…

1 Like

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 :slight_smile:

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?
1 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 for Mode and its subtypes.
  • With a type QRCode to model your data, you may get away with exporting only QRCode as the public API:
    • qrcode would become the constructor QRCode
    • exportqrcode might become save(path, code::QRCode)
    • getversion and getmode might be handled several ways depending on your intent:
      • renamed as exported functions qrversion and qrmode
      • accessible as public fields of QRCode
      • documented as public non-exported functions QRCodes.version and QRCodes.mode

7 Likes

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:
03

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”.

1 Like