找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 274|回复: 5

[求助] 如何获取一个viewport能否进行滚动

[复制链接]
发表于 2026-5-24 23:36:38 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
本帖最后由 Aaron栩生阿龙 于 2026-5-24 23:40 编辑

最近在写代码的时候遇到一个问题,当我们写一个bar或者是vbar的时候,我们可以将它的 unscrollable 设置为'hide',这样当bar不能拖动(也就是对应的viewport内的组件小于viewport本身)的时候,bar会隐藏掉,但是实际上属于bar的那段空间依然会保留,只是不会绘制任何内容
那么这就引出一个问题:
如果我希望根据某个viewport能否滚动来应用不同的布局,应该怎么办呢?


---分界线---
下面写一下我的思考过程:
由于bar是知道viewport能否滚动的,所以我们来查看一下bar的代码:
在bar的render函数里有这一行:
[RenPy] 纯文本查看 复制代码
def render(self, width, height, st, at):
    # 一些代码
    range = self.adjustment.range # @ReservedAssignment
    # 一些代码
    if range <= 0:
        if self.style.unscrollable == "hide":
            self.hidden = True
            return renpy.display.render.Render(width, height)

也就是说,当他调整的这个adjustment对象的range<=0的时候,bar就会隐藏,那么这个adjustment对象是哪里来的呢?我们来看一下他的__init__函数:
[RenPy] 纯文本查看 复制代码
if adjustment is None:
    if isinstance(value, renpy.ui.BarValue):
        if isinstance(replaces, Bar):
            value.replaces(replaces.value)
        self.value = value
        adjustment = value.get_adjustment()


当bar的入参value是一个BarValue对象的时候就会获取这个BarValue的adjustment对象,对于viewport来说,我们使用的BarValue是XScrollValue和YScrollValue,我们以YScrollValue为例,来看看它的get_adjustment方法:
[RenPy] 纯文本查看 复制代码
def get_adjustment(self):
    w = renpy.get_widget(None, self.viewport)
    if not isinstance(w, Viewport):
        raise Exception("The displayable with id %r is not declared, or not a viewport." % self.viewport)
    return w.yadjustment

首先会获取入参(也就是这个self.viewport)的类型,如果是viewport就获取它的yadjustment对象,那么接下来就很简单了:
我们可以使用
[RenPy] 纯文本查看 复制代码
renpy.get_widget(None, viewport).yadjustment.range # 这里的入参viewport是viewport的id

来获取一个viewport是否能滚动:当range<=0的时候不能滚动,否则可以滚动
至此我们就大功告成了……吗?

---分割线---
这个方法有一个很大的问题,当我们点进这个界面的时候,它的值永远是大于0的,只有当界面更新的时候它才能显示实际的值,解决方法也很简单,将这个界面加入per_frame_screens里就好了,但是这无疑增加了性能消耗(虽然我个人是不太在意这些问题,一个视觉小说要多少性能呢?),另一个问题是这行代码不能写在viewport之前
所以:
有没有一种方法可以简单、快速、准确获取一个viewport能否进行滚动?
发表于 2026-5-25 16:05:50 | 显示全部楼层
楼主的核心需求是“根据某个viewport能否滚动来应用不同的布局”。追问一句,不同布局具体是指什么效果?viewport无法滚动时,整个viewport的尺寸扩大到原本scrollbar占的那部分空间,还是scrollbar的尺寸变为(0,0)?
回复 支持 抱歉

使用道具 举报

发表于 2026-5-26 05:16:10 | 显示全部楼层
我也没看懂“根据某个viewport能否滚动来应用不同的布局”是啥意思?
是指不能滚动时用固定布局,可以滚动时使用视口?应该不是吧,视口本来的功能就是自适应的
没想明白是怎么样的功能
回复 支持 抱歉

使用道具 举报

 楼主| 发表于 2026-5-28 23:49:51 | 显示全部楼层
被诅咒的章鱼 发表于 2026-5-25 16:05
楼主的核心需求是“根据某个viewport能否滚动来应用不同的布局”。追问一句,不同布局具体是指什么效果?vi ...

我本来这么写是为了方便大家理解,结果现在看起来反而不好理解了
举个例子,我希望vp不能滚动的时候xalign=0.0,可以滚动的时候xalign=1.0
或者不管这句话,我其实就是想知道“如何实时检测一个vp能否进行滚动”
回复 支持 抱歉

使用道具 举报

 楼主| 发表于 2026-5-28 23:50:32 | 显示全部楼层
Maz马 发表于 2026-5-26 05:16
我也没看懂“根据某个viewport能否滚动来应用不同的布局”是啥意思?
是指不能滚动时用固定布局,可以滚动 ...

见三楼(地板)回答(
回复 支持 抱歉

使用道具 举报

发表于 2026-5-29 14:27:14 | 显示全部楼层
楼主的最后一段总结了两个问题。分别是:
1. 界面初始化阶段,viewport组件绑定的adjustment对象的range初始值不为0,需要至少更新一次界面;
2. 使用 renpy.get_widget(None, viewport).yadjustment.range 函数必须在viewport的定义之后执行。

为什么?

Ren'Py的界面组件层级是个树形结构。每个组件(或者树的每个节点)的transform properties由自身的子组件决定。比如,某个vbox的xysize,是根据自身一堆子组件的xysize来计算的。初始化阶段先计算组件树叶子节点的各种transform properties,然后再往上,一层层算。没办法直接关联没有父子关系的组件,比如由另一个枝干上的组件尺寸决定当前节点尺寸。
当然,我们可以在界面最顶层定义一些“全局”变量,并在组件初始化阶段使用这些“全局”变量的值并忽略子组件的transform properties。相信楼主也是将 renpy.get_widget(None, viewport).yadjustment.range 的结果存储在某个“全局”变量中,来实现需要的效果。这也决定了界面初始化阶段只能使用变量的初始值,至少渲染一次界面之后才更新那些变量。

viewport组件实际只允许有一个子组件,如果设置了scrollbars,则会自动创建一个side组件,把viewport和两个scrollbar放在side组件合适的格子里。viewport是否能滚动,归根到底是看它的child_size是否大于viewport的xysize,不需要绕弯从BarValue或adjustment对象获取。使用 renpy.get_widget()renpy.get_widget_properties 获取子组件的child_size并与viewport自身的xysize比较后,可以根据结果修改viewport自身的property,但不能修改其他组件的,原因在于前面的说明。此做法可以在初始化阶段就改变一些viewport的效果。

同时,这是我问“什么样的不同布局”的原因。由于viewport实际只有一个子组件,很难说有什么“布局”。viewport之外其他组件相关的布局又不能在初始化阶段受viewport状态的影响。如果有动态布局的需求,则需要根据实际情况考虑其他方案。

回复 支持 抱歉

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|RenPy中文空间 ( 苏ICP备17067825号 )

GMT+8, 2026-6-15 00:57 , Processed in 0.037344 second(s), 8 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表