or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

buttons.mddialog-notification.mddisplay-widgets.mdindex.mdinput-controls.mdlayout-animation.mdlist-view-widgets.mdmaterial-effects.mdmenu-command.mdmultimedia.mdsettings-config.mdtheme-styling.mdwindow-navigation.md

layout-animation.mddocs/

0

# Layout and Animation

1

2

Advanced layout managers, smooth scrolling, animated containers, and transition effects for fluid user experiences. These components provide modern animation patterns and flexible layout options.

3

4

## Capabilities

5

6

### Advanced Layout Managers

7

8

Enhanced layout managers that provide flexible positioning and automatic resizing with fluent design principles.

9

10

```python { .api }

11

class FlowLayout(QLayout):

12

def __init__(self, parent=None, needAni: bool = False, isTight: bool = False): ...

13

def addItem(self, item: QLayoutItem): ...

14

def addWidget(self, widget: QWidget): ...

15

def setAnimation(self, needAni: bool): ...

16

def setTightMode(self, isTight: bool): ...

17

def setSpacing(self, spacing: int): ...

18

def setContentsMargins(self, left: int, top: int, right: int, bottom: int): ...

19

20

class ExpandLayout(QVBoxLayout):

21

def __init__(self, parent=None): ...

22

def addWidget(self, widget: QWidget): ...

23

def setExpandingWidget(self, widget: QWidget): ...

24

25

class VBoxLayout(QVBoxLayout):

26

def __init__(self, parent=None): ...

27

def addWidget(self, widget: QWidget, stretch: int = 0, alignment: Qt.Alignment = Qt.Alignment()): ...

28

```

29

30

**Usage Example:**

31

```python

32

from qfluentwidgets import FlowLayout, ExpandLayout, VBoxLayout

33

34

# Flow layout with animation

35

flow_layout = FlowLayout(self, needAni=True, isTight=False)

36

flow_layout.setSpacing(10)

37

38

# Add widgets that flow horizontally then wrap

39

for i in range(10):

40

button = PushButton(f"Button {i+1}", self)

41

flow_layout.addWidget(button)

42

43

container = QWidget()

44

container.setLayout(flow_layout)

45

46

# Expanding layout for resizable content

47

expand_layout = ExpandLayout(self)

48

49

header = TitleLabel("Header Section")

50

content = QTextEdit()

51

footer = BodyLabel("Footer Section")

52

53

expand_layout.addWidget(header)

54

expand_layout.addWidget(content) # This will expand

55

expand_layout.addWidget(footer)

56

57

expand_layout.setExpandingWidget(content)

58

59

# Enhanced VBox layout

60

vbox = VBoxLayout(self)

61

vbox.addWidget(TitleLabel("Title"), 0, Qt.AlignCenter)

62

vbox.addWidget(content_widget, 1) # Stretch factor 1

63

vbox.addWidget(button_widget, 0, Qt.AlignRight)

64

```

65

66

### Scroll Areas

67

68

Enhanced scrolling components with smooth animations and fluent design integration.

69

70

```python { .api }

71

class ScrollArea(QScrollArea):

72

def __init__(self, parent=None): ...

73

def setScrollAnimation(self, enabled: bool): ...

74

def setBorderVisible(self, visible: bool): ...

75

def setViewportMargins(self, left: int, top: int, right: int, bottom: int): ...

76

77

class SmoothScrollArea(ScrollArea):

78

def __init__(self, parent=None): ...

79

def setSmoothMode(self, mode: SmoothMode): ...

80

def smoothMode(self) -> SmoothMode: ...

81

def setScrollSpeed(self, speed: float): ...

82

83

class SingleDirectionScrollArea(ScrollArea):

84

def __init__(self, orient: Qt.Orientation = Qt.Vertical, parent=None): ...

85

def setOrientation(self, orient: Qt.Orientation): ...

86

87

class SmoothScrollBar(QScrollBar):

88

def __init__(self, parent=None): ...

89

def setSmoothMode(self, mode: SmoothMode): ...

90

def setScrollSpeed(self, speed: float): ...

91

```

92

93

**Usage Example:**

94

```python

95

from qfluentwidgets import SmoothScrollArea, SingleDirectionScrollArea, SmoothMode

96

97

# Smooth scrolling area

98

scroll_area = SmoothScrollArea(self)

99

scroll_area.setSmoothMode(SmoothMode.COSINE)

100

scroll_area.setScrollSpeed(1.2)

101

scroll_area.setBorderVisible(False)

102

103

# Create scrollable content

104

content_widget = QWidget()

105

content_layout = QVBoxLayout(content_widget)

106

107

for i in range(50):

108

item = CardWidget()

109

item.setFixedHeight(80)

110

content_layout.addWidget(item)

111

112

scroll_area.setWidget(content_widget)

113

scroll_area.setWidgetResizable(True)

114

115

# Single direction scrolling

116

horizontal_scroll = SingleDirectionScrollArea(Qt.Horizontal, self)

117

horizontal_scroll.setFixedHeight(120)

118

119

# Horizontal content

120

h_content = QWidget()

121

h_layout = QHBoxLayout(h_content)

122

123

for i in range(20):

124

card = CardWidget()

125

card.setFixedSize(100, 80)

126

h_layout.addWidget(card)

127

128

horizontal_scroll.setWidget(h_content)

129

```

130

131

### Animated Stacked Widgets

132

133

Container widgets with smooth transitions between pages and content areas.

134

135

```python { .api }

136

class PopUpAniStackedWidget(QStackedWidget):

137

def __init__(self, parent=None): ...

138

def addWidget(self, widget: QWidget) -> int: ...

139

def setCurrentIndex(self, index: int): ...

140

def setCurrentWidget(self, widget: QWidget): ...

141

def setPopUpAni(self, isPopUp: bool): ...

142

def setAniDirection(self, direction: PopUpAniDirection): ...

143

144

class OpacityAniStackedWidget(QStackedWidget):

145

def __init__(self, parent=None): ...

146

def addWidget(self, widget: QWidget) -> int: ...

147

def setCurrentIndex(self, index: int): ...

148

def setDuration(self, duration: int): ...

149

150

class PopUpAniDirection(Enum):

151

BOTTOM_TO_TOP = 0

152

TOP_TO_BOTTOM = 1

153

LEFT_TO_RIGHT = 2

154

RIGHT_TO_LEFT = 3

155

```

156

157

**Usage Example:**

158

```python

159

from qfluentwidgets import PopUpAniStackedWidget, OpacityAniStackedWidget, PopUpAniDirection

160

161

# Pop-up animation stacked widget

162

popup_stack = PopUpAniStackedWidget(self)

163

popup_stack.setAniDirection(PopUpAniDirection.BOTTOM_TO_TOP)

164

165

# Add pages

166

home_page = QWidget()

167

settings_page = QWidget()

168

about_page = QWidget()

169

170

popup_stack.addWidget(home_page)

171

popup_stack.addWidget(settings_page)

172

popup_stack.addWidget(about_page)

173

174

# Smooth transitions between pages

175

def switch_to_settings():

176

popup_stack.setCurrentWidget(settings_page)

177

178

def switch_to_home():

179

popup_stack.setCurrentWidget(home_page)

180

181

# Opacity animation stacked widget

182

fade_stack = OpacityAniStackedWidget(self)

183

fade_stack.setDuration(300) # 300ms fade duration

184

185

# Add content with fade transitions

186

for i in range(3):

187

page = QWidget()

188

label = TitleLabel(f"Page {i+1}")

189

layout = QVBoxLayout(page)

190

layout.addWidget(label)

191

fade_stack.addWidget(page)

192

193

# Navigate with fade effects

194

fade_stack.setCurrentIndex(1)

195

```

196

197

### Progress Indicators

198

199

Visual progress indicators with smooth animations and modern styling.

200

201

```python { .api }

202

class ProgressBar(QProgressBar):

203

def __init__(self, parent=None): ...

204

def setRange(self, min: int, max: int): ...

205

def setValue(self, value: int): ...

206

def setCustomBarColor(self, color: QColor): ...

207

208

class IndeterminateProgressBar(ProgressBar):

209

def __init__(self, parent=None): ...

210

def start(self): ...

211

def stop(self): ...

212

def pause(self): ...

213

def resume(self): ...

214

def setSpeed(self, speed: float): ...

215

216

class ProgressRing(QWidget):

217

def __init__(self, parent=None): ...

218

def setRange(self, min: int, max: int): ...

219

def setValue(self, value: int): ...

220

def setStrokeWidth(self, width: int): ...

221

def setCustomRingColor(self, color: QColor): ...

222

223

class IndeterminateProgressRing(ProgressRing):

224

def __init__(self, parent=None): ...

225

def start(self): ...

226

def stop(self): ...

227

def setSpeed(self, speed: float): ...

228

```

229

230

**Usage Example:**

231

```python

232

from qfluentwidgets import ProgressBar, IndeterminateProgressBar, ProgressRing

233

from PyQt5.QtCore import QTimer

234

235

# Standard progress bar

236

progress = ProgressBar(self)

237

progress.setRange(0, 100)

238

progress.setValue(0)

239

progress.setFixedSize(300, 6)

240

241

# Simulate progress

242

timer = QTimer()

243

current_value = 0

244

245

def update_progress():

246

global current_value

247

current_value += 1

248

progress.setValue(current_value)

249

if current_value >= 100:

250

timer.stop()

251

252

timer.timeout.connect(update_progress)

253

timer.start(50) # Update every 50ms

254

255

# Indeterminate progress for unknown duration

256

loading_bar = IndeterminateProgressBar(self)

257

loading_bar.setFixedSize(200, 4)

258

loading_bar.start()

259

260

# Progress ring

261

ring = ProgressRing(self)

262

ring.setRange(0, 100)

263

ring.setValue(75)

264

ring.setStrokeWidth(6)

265

ring.setFixedSize(50, 50)

266

267

# Spinning progress ring

268

spinner = IndeterminateProgressRing(self)

269

spinner.setFixedSize(40, 40)

270

spinner.setStrokeWidth(4)

271

spinner.start()

272

```

273

274

## Animation System

275

276

### Smooth Scrolling Configuration

277

278

```python { .api }

279

class SmoothMode(Enum):

280

NO_SMOOTH = 0

281

CONSTANT = 1

282

LINEAR = 2

283

QUADRATIC = 3

284

COSINE = 4

285

286

class SmoothScroll:

287

def __init__(self, widget: QAbstractScrollArea, orient: Qt.Orientation = Qt.Vertical): ...

288

def setSmoothMode(self, mode: SmoothMode): ...

289

def setScrollSpeed(self, speed: float): ...

290

def smoothMode(self) -> SmoothMode: ...

291

```

292

293

**Usage Example:**

294

```python

295

from qfluentwidgets import SmoothScroll, SmoothMode

296

297

# Apply smooth scrolling to any scroll area

298

list_widget = QListWidget(self)

299

smooth_scroll = SmoothScroll(list_widget, Qt.Vertical)

300

smooth_scroll.setSmoothMode(SmoothMode.COSINE)

301

smooth_scroll.setScrollSpeed(1.0)

302

303

# Different smooth modes:

304

# - NO_SMOOTH: Instant scrolling

305

# - CONSTANT: Constant speed

306

# - LINEAR: Linear acceleration

307

# - QUADRATIC: Quadratic easing

308

# - COSINE: Smooth cosine curve (recommended)

309

310

# Apply to table widget

311

table = QTableWidget(self)

312

table_smooth = SmoothScroll(table)

313

table_smooth.setSmoothMode(SmoothMode.LINEAR)

314

```

315

316

### Custom Animations

317

318

```python

319

from PyQt5.QtCore import QPropertyAnimation, QEasingCurve, QParallelAnimationGroup

320

321

class AnimatedWidget(QWidget):

322

def __init__(self, parent=None):

323

super().__init__(parent)

324

self.setupAnimations()

325

326

def setupAnimations(self):

327

# Fade animation

328

self.fade_animation = QPropertyAnimation(self, b"windowOpacity")

329

self.fade_animation.setDuration(300)

330

self.fade_animation.setEasingCurve(QEasingCurve.OutCubic)

331

332

# Scale animation

333

self.scale_animation = QPropertyAnimation(self, b"geometry")

334

self.scale_animation.setDuration(250)

335

self.scale_animation.setEasingCurve(QEasingCurve.OutBack)

336

337

# Parallel animation group

338

self.animation_group = QParallelAnimationGroup()

339

self.animation_group.addAnimation(self.fade_animation)

340

self.animation_group.addAnimation(self.scale_animation)

341

342

def show_animated(self):

343

# Fade in

344

self.fade_animation.setStartValue(0.0)

345

self.fade_animation.setEndValue(1.0)

346

347

# Scale from smaller

348

start_rect = self.geometry()

349

start_rect.setSize(start_rect.size() * 0.8)

350

end_rect = self.geometry()

351

352

self.scale_animation.setStartValue(start_rect)

353

self.scale_animation.setEndValue(end_rect)

354

355

self.show()

356

self.animation_group.start()

357

```

358

359

## Layout Utilities

360

361

### Responsive Layout Helpers

362

363

```python

364

class ResponsiveLayout:

365

def __init__(self, widget: QWidget):

366

self.widget = widget

367

self.breakpoints = {

368

'mobile': 480,

369

'tablet': 768,

370

'desktop': 1024,

371

'large': 1440

372

}

373

374

def get_current_breakpoint(self):

375

width = self.widget.width()

376

if width < self.breakpoints['mobile']:

377

return 'mobile'

378

elif width < self.breakpoints['tablet']:

379

return 'tablet'

380

elif width < self.breakpoints['desktop']:

381

return 'desktop'

382

else:

383

return 'large'

384

385

def apply_responsive_layout(self):

386

breakpoint = self.get_current_breakpoint()

387

388

if breakpoint == 'mobile':

389

self.apply_mobile_layout()

390

elif breakpoint == 'tablet':

391

self.apply_tablet_layout()

392

else:

393

self.apply_desktop_layout()

394

395

# Usage

396

responsive = ResponsiveLayout(self)

397

self.resizeEvent = lambda event: responsive.apply_responsive_layout()

398

```

399

400

### Dynamic Layout Management

401

402

```python

403

class DynamicLayoutManager:

404

def __init__(self, container: QWidget):

405

self.container = container

406

self.widgets = []

407

self.current_layout = None

408

409

def add_widget(self, widget: QWidget):

410

self.widgets.append(widget)

411

self.rebuild_layout()

412

413

def remove_widget(self, widget: QWidget):

414

if widget in self.widgets:

415

self.widgets.remove(widget)

416

widget.setParent(None)

417

self.rebuild_layout()

418

419

def rebuild_layout(self):

420

# Clean up old layout

421

if self.current_layout:

422

while self.current_layout.count():

423

item = self.current_layout.takeAt(0)

424

if item.widget():

425

item.widget().setParent(None)

426

427

# Create new layout based on widget count

428

if len(self.widgets) <= 3:

429

self.current_layout = QHBoxLayout()

430

else:

431

self.current_layout = FlowLayout()

432

433

# Add widgets to new layout

434

for widget in self.widgets:

435

self.current_layout.addWidget(widget)

436

437

self.container.setLayout(self.current_layout)

438

```

439

440

## Performance Optimization

441

442

### Layout Performance Tips

443

444

```python

445

# 1. Use setUpdatesEnabled to batch layout changes

446

widget.setUpdatesEnabled(False)

447

for i in range(100):

448

layout.addWidget(create_widget())

449

widget.setUpdatesEnabled(True)

450

451

# 2. Pre-calculate sizes when possible

452

def optimize_flow_layout():

453

flow_layout.setTightMode(True) # Reduce spacing calculations

454

455

# Set fixed sizes when content is known

456

for widget in widgets:

457

if widget.hasFixedSize():

458

widget.setFixedSize(widget.sizeHint())

459

460

# 3. Use appropriate smooth modes

461

if is_low_end_device():

462

smooth_scroll.setSmoothMode(SmoothMode.LINEAR) # Faster

463

else:

464

smooth_scroll.setSmoothMode(SmoothMode.COSINE) # Smoother

465

```

466

467

### Animation Performance

468

469

```python

470

# Optimize animations for performance

471

class OptimizedAnimation:

472

def __init__(self, target: QWidget):

473

self.target = target

474

self.animation = QPropertyAnimation(target, b"geometry")

475

476

# Performance optimizations

477

self.animation.setDuration(200) # Shorter duration

478

self.animation.setEasingCurve(QEasingCurve.OutCubic) # Efficient curve

479

480

# Reduce updates during animation

481

self.animation.started.connect(lambda: target.setUpdatesEnabled(True))

482

self.animation.finished.connect(lambda: target.setUpdatesEnabled(True))

483

484

def animate_to_position(self, end_rect: QRect):

485

self.animation.setStartValue(self.target.geometry())

486

self.animation.setEndValue(end_rect)

487

self.animation.start()

488

```

489

490

## Best Practices

491

492

### Layout Guidelines

493

494

1. **Use FlowLayout** for responsive, wrapping content

495

2. **Use SmoothScrollArea** for better user experience

496

3. **Limit animation complexity** on lower-end devices

497

4. **Batch layout updates** when adding multiple widgets

498

5. **Test with different window sizes** for responsive behavior

499

500

### Animation Guidelines

501

502

1. **Keep animations under 300ms** for responsiveness

503

2. **Use appropriate easing curves** (OutCubic, OutQuart recommended)

504

3. **Avoid simultaneous complex animations**

505

4. **Provide animation disable options** for accessibility

506

5. **Test on various hardware** for performance