整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

关于画面构成与layout(上) - 平松祯史讲la

关于画面构成与layout(上) - 平松祯史讲layout

者:mjn/Anitama 封面来源:GAINAX

layout是分镜到原画的过渡环节,分镜大多数很小很草没有细节,在layout步骤决定一个镜头中所有细节和背景,也关系到人物动作。

如何设计layout是老大难的问题,很多人问,于是这次平松祯史专门在博客写了一篇文章来讲解layout,作为一个超老手原画的意见,本文无论对于业内还是爱好者,都非常有学习参考的价值。

需要注意的一点是,当下国产动画很多时候都忽视layout的作用,分镜干了layout的工作,原画只管人物动作,这样不但动作本身衔接性不好,而且整体透视感空间感非常差。

下文是文章的节选翻译。

原文地址:http://ameblo.jp/tadashi-hiramatz/entry-12110925284.html

gainax《飞跃巅峰》的分镜、layout、原画、成片的实例

入江泰浩在推特上公开的《请问要来点兔子》第二季11集的layout

问的最多的问题是“如何画layout”,其实并不是那么难的问题。

只是因为所包含的要素太多,不同作品不同场合下,意义会有所不同,原画也一样,一般论无法充分解释,解释了还可能产生误解。所以,我觉得新人原画还是在了解基本知识的基础上,进行实战学习比较好。(译者注:本来layout的意思仅仅是构图,但是现在很多情况下layout绘制者同时需要完成草原画,即第一原画,所以第一原画和构图统称为layout的情况较多。)

动画制作的作业工程每一个环节都有它的目的,不确切明白相关环节的意义,做出来的东西会让观众看不懂,有些必要的东西无法正确传达给观众,进而导致作品的失败。

所以明白画面构成、layout是什么、为什么,这点是必须的。

虽然无法用一般论说明清楚,我还是试试看吧,虽然可能会含有个人的主观想法。

layout是分镜之后的工程,下面的基础都是分镜合适的情况,要是分镜有问题那怎么办?那就是另外的问题了。

(译者注,国内对于分镜这个词也有歧义,在某些国内动画公司里,分镜等同于layout,片尾职位表中的分镜实际干了画面构成layout的活,原画则是单纯做人物动作的绘制,不同于日式制作流程,日式流程中原画负责layout,整个画面的掌握权都在原画手上。这也是比较科学合理的分配。)

地点 时间 气氛——layout必须的三要素

layout是构成故事的影像制作的第一步,也是从演出到原画的渡桥。

演出负责把脚本做成分镜,把文字做成可以影像化的一卡一卡连续小设计图,正确理解读取脚本是前提。(译者注:虽然在片尾我们经常看到演出和分镜(絵コンテ)是分开写的,但是在动画制作早期,分镜和演出不分开,单记演出名,并不设分镜职位,做了分镜的人理所当然负责这集的演出工作,所以这里分镜和演出统称为演出,和anitama谈论的演出、演出tag是一个意思。现在因为种种原因演出和分镜经常是分开的,但是考虑到分镜演出对整体作业的影响我们还是统称为演出。)

对于原画则是以正确理解分镜、读取演出意图为前提,才能画layout。

5W1H——“什么时候?在哪里?谁?在做什么?为了什么?怎么做?”是必须基本明确的,总结成简要三点则是:地点、时间、气氛。

把这三点明确恰当得表现出来就是layout的目的。所以说,要画某一镜头之前,不明白这个场景和发生在这个场景的故事是画不了的,最起码不明白前后镜头的关系是不能画的。

简单说明这三要素。

- 地点——也就是舞台。在哪个地方。这个地方有什么。天气怎么样。

- 时间——上午下午黄昏夜晚,具体是哪个时间带。时间是如何变化的。

- 气氛——人物是什么样的心情、目的、动作。即使是人物不出现的的镜头也能包含气氛。

分镜并不需要把上述要素表现得太清楚,layout本身就需要原画读取理解分镜之后收集影像中的必要要素。虽然写的是演出到原画的渡桥,其实是把之后的所有作业工程都交渡过去了。

演出(分镜)上的三要素是“画面大小、被拍人物的角度、视野角”,三者本身之间密切相关、相互影响。

为什么是特写?为什么用仰视的镜头?为什么用望远构图?等等之类的都应该有其演出意图。

过于追求细节估计说不完说不尽,这次就概括成“地点、时间、气氛”。

最重要的是气氛。地点、时间、动作都应该为气氛的表现服务。气氛、人物的感觉不变化,人物的动作也不会产生。即使机械作画也一样有感觉和趋势存在。

演出方面,气氛、感觉不变化,镜头也不变化。虽然不同演出家的镜头使用方法有差别,但是多数都是基于固定镜头(FIX),要使镜头发生变化必须有让其产生变化的必要性存在。人物的动作也同理。

分镜画得无论多细致还是多草多简单,都应该能让工程后面的制作人员能明白这个作品想要表现什么、这个场景事件有什么意图、镜头需要表现出怎样的演出效果、登场人物的心情感受等等。场景不同,可能分镜三要素的优先顺序有不同。会话场景和打斗场景必要的要素肯定有不同,不同镜头之间也可能有不同。

本文仅供Anitama发表,任何单位或个人,不得以任何形式刊载本文的部分或全部内容。

官方网站:http://www.anitama.cn

官方微博:@AnimeTamashii

微信公众号:AniTama

合作邮箱:bd@anitama.cn

原文: m.anitama.cn/article/1a3d15c14218472a?utm_source=toutiao

过本文章,你可以掌握以下内容:

  1. Pyside6 Layout介绍
  2. Pyside6 Layout布局解释及示例
  3. 自定义Layout,实现部件自动换行

1、Pyside6 Layout介绍

QtWidgets.QLayout是Qt中用于管理窗口或对话框中小部件(控件)布局的基类。它是一个抽象基类,定义了所有布局类共有的接口和行为。QLayout及其子类的主要作用是自动管理小部件的位置和大小,以便于创建整洁和灵活的图形用户界面(GUI),对于复杂界面布局是很重要的,主要作用包括:

  • 自动管理小部件位置:QLayout自动计算小部件的位置,使得小部件根据布局规则排列,无需手动指定每个小部件的具体位置。
  • 自动调整大小:当窗口大小变化时,QLayout能够自动调整其管理的小部件的大小和位置,确保布局的一致性和响应性。
  • 简化界面设计:通过使用布局,开发者可以更加专注于界面的结构设计,而不是具体的位置和大小调整,从而简化了界面设计过程。
  • 支持嵌套:布局可以嵌套使用,即一个布局可以包含其他布局,这允许创建复杂的界面结构

Layout继承关系图

2、Pyside6 Layout布局解释及示例

以下是继承自QtWidgets.QLayout的布局,每种布局有对应的行为。

布局

行为

对应html

QHBoxLayout

线性水平布局

类似于display: flex; flex-direction: row;的Flexbox布局

QVBoxLayout

线性垂直布局

类似于display: flex; flex-direction: column;的Flexbox布局

QGridLayout

在可转位网格 XxY 中

类似于html的table行和列

QStackedLayout

堆叠 (z) 于彼此前面

类似于css的z-index,并控制隐藏显示效果

QHBoxLayout

QHBoxLayout是Qt中的一个布局管理器类,用于按水平方向排列小部件。它继承自QLayout,提供了一种简便的方式来自动管理窗口或对话框中小部件的位置和大小。使用QHBoxLayout,可以将小部件从左到右依次排列,而不需要手动指定每个小部件的具体位置。

QHBoxLayout继承关系图

简单的示例如下:

import sys
from PySide6.QtWidgets import *
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("QHBoxLayout之水平布局")
        self.setMinimumSize(400,300)
        layout=QHBoxLayout()
        layout.addWidget(QPushButton("1"))
        layout.addWidget(QPushButton("2"))
        layout.addWidget(QPushButton("3"))
        widget=QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)

if __name__=='__main__':
    app=QApplication(sys.argv)
    window=MainWindow()
    window.show()
    app.exec()

QHBoxLayout示例效果图

QVBoxLayout

QVBoxLayout是Qt中的一个布局管理器类,用于垂直方向排列小部件。它继承自QLayout,提供了一种简便的方式来自动管理窗口或对话框中小部件的位置和大小。使用QVBoxLayout,可以将小部件从上到下依次排列,而不需要手动指定每个小部件的具体位置。

QVBoxLayout继承关系图

简单示例如下:

import sys
from PySide6.QtWidgets import *
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("QVBoxLayout之垂直布局")
        self.setMinimumSize(400, 300)
        layout=QVBoxLayout()
        layout.addWidget(QPushButton("1"))
        layout.addWidget(QPushButton("2"))
        layout.addWidget(QPushButton("3"))
        widget=QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)

if __name__=='__main__':
    app=QApplication(sys.argv)
    window=MainWindow()
    window.show()
    app.exec()

效果图如下:

QVBoxLayout示例效果图

QGridLayout

QGridLayout是Qt中一个非常强大的布局管理器,它提供一种网格式布局,这种布局由行和列组成(类似table),每个小部件占据网格中的一个或多个单元格。QGridLayout提供了灵活的方式来创建复杂的用户界面,使得小部件的布局可以精确控制,同时也能自动适应窗口大小的变化,主要特性有:

  • 行列管理:可以指定小部件应该放在网格的哪一行哪一列,甚至可以跨越多行多列。
  • 自动调整大小:当窗口大小改变时,QGridLayout会自动调整小部件的大小和位置,保持布局的整洁和一致性。
  • 最小宽度和拉伸因子:每列(或行)可以有一个最小宽度和一个拉伸因子,这决定了在可用空间中它们将如何分配额外的空间。
  • 间距和边距:可以设置小部件之间的间距(spacing())和布局边缘的边距(内容边距),以控制布局的外观。

QGridLayout继承关系图

示例代码如下:

import sys
from PySide6.QtWidgets import *
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("QGridLayout之网格布局")
        self.setMinimumSize(400, 300)
        layout=QGridLayout()
        # 为窗口部件设置样式表,添加边框
        self.setStyleSheet("QWidget { border: 2px solid black; }")
        layout.addWidget(QLabel('第0行第0列'), 0, 0)
        layout.addWidget(QLabel('第0行第1列'), 0, 1)
        layout.addWidget(QLabel('第1行第0列'), 1, 0)
        layout.addWidget(QLabel('第1行第1列'), 1, 1)
        widget=QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)

if __name__=='__main__':
    app=QApplication(sys.argv)
    window=MainWindow()
    window.show()
    app.exec()

效果图如下:

QGridLayout示例效果图

QStackedLayout

QStackedLayout是Qt中的一个布局管理器,它可以在相同的空间内堆叠多个小部件,但一次只显示一个小部件。这种布局非常适合用于实现向导、选项卡和其他需要在多个页面之间切换的界面。

QStackedLayout继承关系图

主要特性:

  • 堆叠小部件:在同一个布局空间内堆叠多个小部件。
  • 单一可见性:一次只有一个小部件可见。
  • 动态切换:可以编程方式动态切换当前可见的小部件

常用方法技巧:indexOf()函数返回小部件在该列表中的索引。可以使用addWidget()函数添加小部件到列表末尾,或者使用insertWidget()函数在给定索引处插入。removeWidget()函数从布局中移除给定索引的小部件。可以使用count()函数获取布局中包含的小部件数量。widget()函数返回给定索引位置的小部件。当前显示在屏幕上的小部件的索引由currentIndex()给出,并且可以使用setCurrentIndex()进行更改。以类似的方式,可以使用currentWidget()函数检索当前显示的小部件,并使用setCurrentWidget()函数进行更改。每当布局中的当前小部件发生变化或从布局中移除小部件时,分别会发出currentChanged()和widgetRemoved()信号。

示例代码如下:

import sys
from PySide6.QtGui import QPalette, QColor
from PySide6.QtWidgets import *
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QStackedLayout之堆叠布局")
        self.setMinimumSize(400, 300)
        pagelayout=QVBoxLayout()
        button_layout=QHBoxLayout()
        self.stacklayout=QStackedLayout()
        pagelayout.addLayout(button_layout)
        pagelayout.addLayout(self.stacklayout)
        btn=QPushButton("red")
        btn.pressed.connect(self.activate_tab_1)
        button_layout.addWidget(btn)
        self.stacklayout.addWidget(Color("red"))
        btn=QPushButton("green")
        btn.pressed.connect(self.activate_tab_2)
        button_layout.addWidget(btn)
        self.stacklayout.addWidget(Color("green"))
        btn=QPushButton("yellow")
        btn.pressed.connect(self.activate_tab_3)
        button_layout.addWidget(btn)
        self.stacklayout.addWidget(Color("yellow"))
        widget=QWidget()
        widget.setLayout(pagelayout)
        self.setCentralWidget(widget)

    def activate_tab_1(self):
        self.stacklayout.setCurrentIndex(0)

    def activate_tab_2(self):
        self.stacklayout.setCurrentIndex(1)

    def activate_tab_3(self):
        self.stacklayout.setCurrentIndex(2)

class Color(QWidget):
    def __init__(self, color):
        super(Color, self).__init__()
        self.setAutoFillBackground(True)
        palette=self.palette()
        palette.setColor(QPalette.Window, QColor(color))
        self.setPalette(palette)

if __name__=='__main__':
    app=QApplication(sys.argv)
    window=MainWindow()
    window.show()
    app.exec()

效果图如下:

QStackedLayout示例效果图


混合布局

使用QHBoxLayout、QVBoxLayout、QGridLayout、QStackedLayout这几种布局组合使用,来控制界面的整体风格视角,制作精美的布局效果

示例代码如下:

import sys
from PySide6.QtWidgets import *

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("混合布局")
        self.setMinimumSize(400, 300)
        self.setStyleSheet("QLabel { border: 1px solid blue; }")
        layout1=QHBoxLayout()
        layout2=QVBoxLayout()
        layout3=QVBoxLayout()
        layout1.setContentsMargins(0,0,0,0)
        layout1.setSpacing(20)
        layout2.addWidget(QLabel('hbox1-QVBoxLayout1'))
        layout2.addWidget(QLabel('hbox1-QVBoxLayout2'))
        layout2.addWidget(QLabel('hbox1-QVBoxLayout3'))
        layout1.addLayout(layout2)
        layout1.addWidget(QLabel('hbox2'))
        layout3.addWidget(QLabel('hbox2-QVBoxLayout1'))
        layout3.addWidget(QLabel('hbox2-QVBoxLayout2'))
        layout1.addLayout(layout3)
        widget=QWidget()
        widget.setLayout(layout1)
        self.setCentralWidget(widget)

if __name__=='__main__':
    app=QApplication(sys.argv)
    window=MainWindow()
    window.show()
    app.exec()

效果图如下:


混合布局效果图

注意:QLayout边框及样式不能通过这种方式来设置样式效果,需要指定Widget的样式

#无效果
self.setStyleSheet("QHBoxLayout { border: 1px solid black; }")
#有效果
self.setStyleSheet("QLabel { border: 1px solid blue; }")

自定义Layout,实现部件自动换行

由于QHBoxLayout、QVBoxLayout、QGridLayout、QStackedLayout布局放置的控件,不增加任何处理,生成的控件会固定住窗口大小,可以通过下面来动态排列控件。

示例代码如下:

import sys
from PySide6.QtCore import QRect, QSize, QPoint, Qt
from PySide6.QtWidgets import *

class FlowLayout(QLayout):
    def __init__(self, parent=None, margin=0, spacing=-1):
        super(FlowLayout, self).__init__(parent)
        if parent is not None:
            self.setContentsMargins(margin, margin, margin, margin)
        self.setSpacing(spacing)
        self.items=[]

    def addItem(self, item):
        self.items.append(item)

    def count(self):
        return len(self.items)

    def itemAt(self, index):
        if index >=0 and index < len(self.items):
            return self.items[index]
        return None

    def takeAt(self, index):
        if index >=0 and index < len(self.items):
            return self.items.pop(index)
        return None

    def expandingDirections(self):
        return 0

    def hasHeightForWidth(self):
        return True

    def heightForWidth(self, width):
        height=self.doLayout(QRect(0, 0, width, 0), True)
        return height

    def setGeometry(self, rect):
        super(FlowLayout, self).setGeometry(rect)
        self.doLayout(rect, False)

    def sizeHint(self):
        return QSize(self.doLayout(QRect(0, 0, 10000, 0), True), 10000)

    def doLayout(self, rect, testOnly):
        x=rect.x()
        y=rect.y()
        lineHeight=0
        for item in self.items:
            wid=item.widget()
            spaceX=self.spacing() + wid.style().layoutSpacing(QSizePolicy.PushButton, QSizePolicy.PushButton,Qt.Horizontal)
            spaceY=self.spacing() + wid.style().layoutSpacing(QSizePolicy.PushButton, QSizePolicy.PushButton,Qt.Vertical)
            nextX=x + item.sizeHint().width() + spaceX
            if nextX - spaceX > rect.right() and lineHeight > 0:
                x=rect.x()
                y=y + lineHeight + spaceY
                nextX=x + item.sizeHint().width() + spaceX
                lineHeight=0
            if not testOnly:
                item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
            x=nextX
            lineHeight=max(lineHeight, item.sizeHint().height())

        return y + lineHeight - rect.y()
if __name__=='__main__':
    app=QApplication(sys.argv)
    mainWidget=QWidget()
    mainWidget.setMinimumSize(300, 200)
    layout=FlowLayout(mainWidget)
    for i in range(50):
        layout.addWidget(QPushButton(f'Button {i}'))
    mainWidget.setLayout(layout)
    mainWidget.show()
    sys.exit(app.exec_())

效果如下:

自定义Layout效果图

在程序员开发软件界面系统也都是有多种多样,每种开发语言基本上都有一种或多种界面引擎如:C skin, Direct UI , 还有金山、迅雷等厂商的界面SDK。

今天推荐一个轻量级、自由度高、使用方便的界面库 Htmlayout/Sciter。HTMLayout是一个免费的开源界面库(核心未开源),以DLL的方式运行,并提供一个API的调用接口和一系列的C++封装和sample例程。HTMLayout: 快速,轻量、嵌入式的,基于HTML/CSS渲染技术和布局管理的界面引擎组件,可以高效地解析和渲染HTML网页。其几乎支持所有的HTML元素和CSS3标准,并根据界面库的特征,做了很多有用的功能性扩展。

界面预览

这里着重地说一下在 aardio 软件中的使用,作者把 HTMLayout和 Sciter 制作成了扩展库, 并且免费开源, 通过这个扩展库你知道会一点点 HTML 和 CSS 知识就可以方便地制作各种漂亮的 ui。通过作者的努力你还可以直接在界面上使用现在最流行的字体图标,还使扩展库支持了模板功能,你可以像写PHP一样写桌面软件的界面HTML,虽然看起来简单的代码,但用起来会非常方便。

效果图

效果图

言归正传, 我们用aardio一步步来制作一个最简单的界面。

1、打开软件》新建工程》选择web界面》选择HTMLayout》创建工程

字体图标

效果图

aardio 里查看 main.aardio 源码

 import win.ui;
/*DSG{{*/
var winform=win.form(text="htmlayout";right=761;bottom=609;border="none")
winform.add()
/*}}*/

import web.layout; 
import web.layout.behavior.windowCommand;
import web.layout.behavior.tabs;
// 加载网页
var wbLayout=web.layout( winform );
wbLayout.go("\layout\ui.html");

if(_STUDIO_INVOKED){
	import web.layout.debug;
	wbLayout.attachEventHandler( web.layout.debug );
} 

//添加阴影边框
import win.ui.shadow;
win.ui.shadow( winform,50,3 );

winform.show() 
win.loopMessage();

从上面我们可以看出,软件的主要界面是由 ui.html,ui.css,tabs.css 这几个文件组成的。然后你可以根据自己软件界面的需要进行调整。

工程项目

以下是我自己这二天搭建的一个软件的界面,就是一个简单的软件基本框架。

演示图上

工程项目目录结构

如果你对htmlayout感兴趣,可以查看 https://bbs.aardio.com/forum.php?mod=forumdisplay&fid=128&page=1 了解更多更详细的教程。