No obecně požadavky na překreslení (v tvém případě volání repaint) jsou slučovány, takže když už ve frontě událostí je jeden požadavek na repaint a přijde další, tak se sloučí do jednoho.
Ty děláš to, že v cyklu vždycky zavoláš repaint a pak sleep.
V tom případě, kdy to máš jako handler na klik tlačítka, tak se ti to neprovede, protože ty do té fronty událostí
nasypeš všechny ty repainty naráz, takže se sloučí do jednoho. Pokud se ptáš, proč se ti neprovedou
v tom mezičase, co voláš Thread.sleep, tak to bude podle mě kvůli tomu, že to zpracování událostí ví, že ten objekt, na kterém se má zavolat překreslení, má ještě nedokončené zpracování nějaké předchozí události (ten cyklus, ve kterém voláš repaint a sleep).
Že ti to funguje v konstruktoru je asi tím, že ten kód není spuštěn v nějaké obsluze události, jako když to provádíš v obsluze kliku na tlačítko.
Řešení:
Neměl by jsi používat Thread.sleep, ale použít timer a nedělat to v žádném cyklu v rámci jedné obsluhy události.