Qt状态机框架

概述

状态机框架提供了用于创建和执行状态图的类。这些概念和符号基于Harel的Statecharts:复杂系统的可视化形式(http://www.wisdom.weizmann.ac.il/~dharel/SCANNED.PAPERS/Statecharts.pdf),也是UML状态图的基础。状态机执行的语义基于状态图XML (SCXML)(http://www.w3.org/TR/scxml/)。

状态图提供了一种图形化的方式来模拟系统对刺激的反应。这是通过定义系统可能处于的状态,以及系统如何从一个状态移动到另一个状态(状态之间的转换)来完成的。事件驱动系统(如Qt应用程序)的一个关键特征是,行为通常不仅取决于最近的或当前的事件,还取决于在它之前的事件。使用状态图,这些信息很容易表达。

状态机框架提供了一个API和执行模型,可用于在Qt应用程序中有效地嵌入状态图的元素和语义。该框架与Qt的元对象系统紧密集成;例如,状态之间的转换可以由信号触发,状态可以配置为在{QObject}上设置属性和调用方法。Qt的事件系统用于驱动状态机。

状态机框架中的状态图是分层的。状态可以嵌套在其他状态中,状态机的当前配置由当前活动的一组状态组成。状态机的有效配置中的所有状态都有一个共同的祖先

状态机框架中的类

这些类由qt提供,用于创建事件驱动的状态机。

QAbstractState

The base class of states of a QStateMachine

QAbstractTransition

The base class of transitions between QAbstractState objects

QEventTransition

QObject-specific transition for Qt events

QFinalState

Final state

QHistoryState

Means of returning to a previously active substate(返回到先前活跃的子状态的方法)

QKeyEventTransition

Transition for key events

QMouseEventTransition

Transition for mouse events

QSignalTransition

Transition based on a Qt signal

QState

通用状态 for QStateMachine

QStateMachine

Hierarchical finite state machine(层次有限状态机)

QStateMachine::SignalEvent

Represents a Qt signal event

QStateMachine::WrappedEvent

Inherits QEvent and holds a clone of an event associated with a QObject

一个简单的状态机
为了演示状态机API的核心功能,让我们看一个小示例:一个具有三种状态s1、s2和s3的状态机。状态机由单个QPushButton控制;当点击按钮时,机器切换到另一种状态。最初,状态机处于状态s1。这台机器的状态图如下:

面的代码片段显示了创建这样一个状态机所需的代码。首先,我们创建状态机和状态:

    QStateMachine machine;QState *s1 = new QState();QState *s2 = new QState();QState *s3 = new QState();

然后,我们使用QState::addTransition()函数创建转换:

    s1->addTransition(button, &QPushButton::clicked, s2);s2->addTransition(button, &QPushButton::clicked, s3);s3->addTransition(button, &QPushButton::clicked, s1);

接下来,我们为机器添加状态并设置机器的初始状态:

    machine.addState(s1);machine.addState(s2);machine.addState(s3);machine.setInitialState(s1);

最后,我们启动状态机:

machine.start();

状态机异步执行,也就是说,它成为应用程序事件循环的一部分。

在状态进入或者离开时处理事情

上面的状态机只是从一种状态转换到另一种状态,它不执行任何操作。QState::assignProperty()函数可用于在进入状态时设置一个QObject的属性。在下面的代码片段中,应该分配给QLabel的文本属性的值是为每个状态指定的:

    s1->assignProperty(label, "text", "In state s1");s2->assignProperty(label, "text", "In state s2");s3->assignProperty(label, "text", "In state s3");

当输入任何状态时,标签的文本将相应地更改。

进入状态时发出QState::entered()信号,退出状态时发出QState::exited()信号。在下面的代码片段中,按钮的showMaximized()插槽将在状态s3进入时被调用,按钮的showMinimized()插槽将在s3退出时被调用:

    QObject::connect(s3, &QState::entered, button, &QPushButton::showMaximized);QObject::connect(s3, &QState::exited, button, &QPushButton::showMinimized);

自定义状态可以重新实现QAbstractState::onEntry()和QAbstractState::onExit()。

结束状态机

上一节中定义的状态机永远不会结束。为了使状态机能够完成,它需要有一个顶级的最终状态(QFinalState对象)。当状态机进入顶级最终状态时,机器将发出qstatemmachine::finished()信号并停止。
要在图中引入最终状态,所需要做的就是创建一个QFinalState对象,并将其用作一个或多个转换的目标。

通过状态分组来实现共享转换

假设我们希望用户能够通过单击quit按钮随时退出应用程序。为了实现这一点,我们需要创建一个最终状态,并将其作为与Quit按钮的clicked()信号相关联的转换的目标。我们可以在s1 s2 s3中添加一个变换;然而,这似乎是多余的,并且还必须记住在将来添加的每个新状态中添加这样的转换。

通过对状态s1、s2和s3进行分组,我们可以实现相同的行为(即单击Quit按钮退出状态机,而不管状态机处于哪种状态)。这是通过创建一个新的顶级状态并将三个原始状态设置为新状态的子状态来实现的。下图显示了新的状态机。

最初的三个状态被重新命名为s11, s12和s13,以反映它们现在是新的顶级状态s1的孩子。子状态隐式继承父状态的转换。这意味着现在添加一个从s1到最终状态s2的转换就足够了。添加到s1的新状态也将自动继承此转换。

对状态进行分组所需要做的就是在创建状态时指定适当的父级。您还需要指定哪个子状态是初始状态(即,当父状态是转换的目标时,状态机应该进入哪个子状态)。

    QState *s1 = new QState();QState *s11 = new QState(s1);QState *s12 = new QState(s1);QState *s13 = new QState(s1);s1->setInitialState(s11);machine.addState(s1);QFinalState *s2 = new QFinalState();s1->addTransition(quitButton, &QPushButton::clicked, s2);machine.addState(s2);machine.setInitialState(s1);QObject::connect(&machine, &QStateMachine::finished,QCoreApplication::instance(), &QCoreApplication::quit);

在本例中,我们希望应用程序在状态机结束时退出,因此机器的finished()信号连接到应用程序的quit()插槽。

子状态可以覆盖继承的转换。例如,下面的代码添加了一个转换,该转换有效地导致在状态机处于状态s12时忽略Quit按钮。

s12->addTransition(quitButton, &QPushButton::clicked, s12);

转换可以将任何状态作为其目标,即目标状态不必与源状态处于状态层次结构中的同一级别。

使用历史状态保存和恢复当前状态

想象一下,我们想要在上一节讨论的示例中添加一个“中断”机制;用户应该能够单击一个按钮,让状态机执行一些不相关的任务,之后状态机应该恢复它之前所做的任何事情(即返回到旧状态,在本例中是s11、s12和s13中的一个)。

这种行为可以很容易地使用历史状态进行建模。历史状态(QHistoryState对象)是一个伪状态,它表示父状态在父状态最后一次退出时所处的子状态。

历史状态被创建为我们希望记录当前子状态的状态的子状态;当状态机在运行时检测到存在这样的状态时,它会在父状态退出时自动记录当前(真正的)子状态。向历史状态的过渡实际上是向状态机先前保存的子状态的过渡;状态机自动将转换“转发”到真正的子状态。

下图显示了添加中断机制后的状态机。

下面的代码展示了它是如何实现的;在本例中,我们只是在输入s3时显示一个消息框,然后立即通过历史状态返回到s1的前一个子状态。

    QHistoryState *s1h = new QHistoryState(s1);QState *s3 = new QState();s3->assignProperty(label, "text", "In s3");QMessageBox *mbox = new QMessageBox(mainWindow);mbox->addButton(QMessageBox::Ok);mbox->setText("Interrupted!");mbox->setIcon(QMessageBox::Information);QObject::connect(s3, &QState::entered, mbox, &QMessageBox::exec);s3->addTransition(s1h);machine.addState(s3);s1->addTransition(interruptButton, &QPushButton::clicked, s3);

使用平行状态避免状态的组合爆炸

假设您想在单个状态机中为一辆汽车的一组互斥属性建模。假设我们感兴趣的属性是干净与脏,移动与不移动。它需要四个相互排斥的状态和八个转换才能表示并在所有可能的组合之间自由移动。

如果我们添加第三个属性(如红色vs蓝色),那么状态总数将翻倍,达到8个;如果我们再加上第四个属性(比如,封闭的vs可转换的),状态的总数将再次翻倍,达到16个。
使用并行状态,随着我们添加更多属性,状态和转换的总数呈线性增长,而不是呈指数增长。此外,可以将状态添加到并行状态或从并行状态中删除,而不会影响它们的任何兄弟状态。

要创建并行状态组,将QState::ParallelStates传递给QState构造函数。

    QState *s1 = new QState(QState::ParallelStates);// s11 and s12 will be entered in parallelQState *s11 = new QState(s1);QState *s12 = new QState(s1);

当进入一个并行状态组时,将同时进入它的所有子状态。各个子状态中的转换正常运行。然而,任何一个子状态都可以进行退出父状态的转换。当这种情况发生时,父状态及其所有子状态都将退出。

状态机框架中的并行性遵循交错语义。所有并行操作都将在事件处理的单个原子步骤中执行,因此没有任何事件可以中断并行操作。但是,事件仍将按顺序处理,因为机器本身是单线程的。举个例子:考虑这样一种情况:有两个转换退出同一个并行状态组,并且它们的条件同时为真。在这种情况下,两个事件中最后处理的事件将不会产生任何影响,因为第一个事件已经导致机器从并行状态退出。

检测一种复合状态是否已结束

子状态可能已经final(一个QFinalState对象);当进入最后一个子状态时,父状态发出QState::finished()信号。下图显示了一个复合状态s1,它在进入最终状态之前做了一些处理:

当进入s1的最终状态时,s1将自动发出finished()。我们使用一个信号转换来触发这个事件来触发状态改变:

s1->addTransition(s1, &QState::finished, s2);

当你想隐藏复合状态的内部细节时,在复合状态中使用最终状态是很有用的;也就是说,外部世界唯一能做的就是进入状态,并在状态完成其工作时获得通知。在构建复杂的(深度嵌套的)状态机时,这是一种非常强大的抽象和封装机制。(在上面的示例中,您当然可以直接从s1的done状态创建转换,而不是依赖s1的finished()信号,但结果是暴露并依赖s1的实现细节)。

对于并行状态组,QState::finished()信号在所有子状态进入最终状态时发出。

无目标转换

转换不需要有目标状态。没有目标的转换可以像其他任何转换一样触发;不同之处在于,当触发无目标转换时,它不会导致任何状态更改。这允许您在机器处于某种状态时对信号或事件作出反应,而不必离开该状态。例子:

QStateMachine machine;
QState *s1 = new QState(&machine);QPushButton button;
QSignalTransition *trans = new QSignalTransition(&button, &QPushButton::clicked);
s1->addTransition(trans);QMessageBox msgBox;
msgBox.setText("The button was clicked; carry on.");
QObject::connect(trans, QSignalTransition::triggered, &msgBox, &QMessageBox::exec);machine.setInitialState(s1);

每次单击按钮时都会显示消息框,但状态机将保持其当前状态(s1)。但是,如果将目标状态显式地设置(setTargetState)为s1,则每次都会退出并重新进入s1(例如,会发出QAbstractState::entered()和QAbstractState::exit()信号)。

通过事件触发转换

qstatemmachine运行自己的事件循环。对于信号转换(QSignalTransition对象),当QStateMachine拦截相应的信号时,它会自动向自己发送一个QStateMachine::SignalEvent;类似地,对于QObject事件转换(QEventTransition对象),发送QStateMachine::WrappedEvent。

您可以使用qstatemmachine::postEvent()将您自己的事件发布到状态机。

在向状态机发布自定义事件时,通常还可以从该类型的事件触发一个或多个自定义转换。要创建这样的转换,您可以子类化QAbstractTransition并重新实现QAbstractTransition::eventTest(),在这里您可以检查事件是否与您的事件类型匹配(以及可选的其他标准,例如事件对象的属性)。

这里我们定义了自己的自定义事件类型StringEvent,用于向状态机发送字符串:

struct StringEvent : public QEvent
{StringEvent(const QString &val): QEvent(QEvent::Type(QEvent::User+1)),value(val) {}QString value;
};

接下来,我们定义一个只在事件字符串匹配特定字符串时触发的转换(一个受保护的转换):

class StringTransition : public QAbstractTransition
{Q_OBJECTpublic:StringTransition(const QString &value): m_value(value) {}protected:bool eventTest(QEvent *e) override{if (e->type() != QEvent::Type(QEvent::User+1)) // StringEventreturn false;StringEvent *se = static_cast<StringEvent*>(e);return (m_value == se->value);}void onTransition(QEvent *) override {}private:QString m_value;
};

在eventTest()重新实现中,我们首先检查事件类型是否为所需类型;如果是,则将事件强制转换为StringEvent并执行字符串比较。
下面是一个使用自定义事件和转换的状态图:

下面是状态图的实现:

    QStateMachine machine;QState *s1 = new QState();QState *s2 = new QState();QFinalState *done = new QFinalState();StringTransition *t1 = new StringTransition("Hello");t1->setTargetState(s2);s1->addTransition(t1);StringTransition *t2 = new StringTransition("world");t2->setTargetState(done);s2->addTransition(t2);machine.addState(s1);machine.addState(s2);machine.addState(done);machine.setInitialState(s1);

一旦机器启动,我们就可以向它发布事件。

    machine.postEvent(new StringEvent("Hello"));machine.postEvent(new StringEvent("world"));

未由任何相关转换处理的事件将被状态机静默地使用。对状态进行分组并提供此类事件的默认处理可能很有用;例如,如下图所示:

对于深度嵌套的状态图,您可以在最合适的粒度级别上添加这种“回退”转换。

使用恢复策略自动恢复属性

在一些状态机中,将注意力集中在分配状态属性上,而不是在状态不再活动时恢复状态,这可能会很有用。如果您知道,当机器进入没有显式地为属性赋值的状态时,属性应该始终恢复到其初始值,那么您可以将全局恢复策略设置为qstatemmachine::RestoreProperties。

QStateMachine machine;
machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);

设置此恢复策略后,机器将自动恢复所有属性。如果它进入未设置给定属性的状态,它将首先搜索祖先的层次结构,以查看是否在那里定义了该属性。如果是,则属性将恢复为最近的祖先所定义的值。如果不是,它将恢复到其初始值(即在执行任何状态的属性分配之前的属性值)。
以以下代码为例:

    QStateMachine machine;machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);QState *s1 = new QState();s1->assignProperty(object, "fooBar", 1.0);machine.addState(s1);machine.setInitialState(s1);QState *s2 = new QState();machine.addState(s2);

假设机器启动时属性fooBar是0.0。当机器处于状态s1时,属性将为1.0,因为状态显式地将此值赋给它。当机器处于状态s2时,没有显式地为属性定义值,因此它将隐式地恢复为0.0。
如果我们使用嵌套状态,父类会为该属性定义一个值,所有没有显式赋值给该属性的后代都会继承这个值

    QStateMachine machine;machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);QState *s1 = new QState();s1->assignProperty(object, "fooBar", 1.0);machine.addState(s1);machine.setInitialState(s1);QState *s2 = new QState(s1);s2->assignProperty(object, "fooBar", 2.0);s1->setInitialState(s2);QState *s3 = new QState(s1);

这里s1有两个子节点:s2和s3。当输入s2时,属性fooBar的值将为2.0,因为这是为状态显式定义的。当机器处于状态s3时,没有为状态定义值,但是s1将属性定义为1.0,因此这是将分配给fooBar的值

动画属性分配

状态机API与Qt中的动画API连接,允许在状态中分配属性时自动动画化。
假设我们有以下代码:

    QState *s1 = new QState();QState *s2 = new QState();s1->assignProperty(button, "geometry", QRectF(0, 0, 50, 50));s2->assignProperty(button, "geometry", QRectF(0, 0, 100, 100));s1->addTransition(button, &QPushButton::clicked, s2);

这里我们定义了用户界面的两种状态。在s1中,按钮很小,而在s2中,按钮更大。如果我们点击按钮从s1转换到s2,按钮的几何形状将在进入给定状态后立即设置。但是,如果我们希望过渡平滑,我们所需要做的就是创建一个QPropertyAnimation并将其添加到过渡对象中。

    QState *s1 = new QState();QState *s2 = new QState();s1->assignProperty(button, "geometry", QRectF(0, 0, 50, 50));s2->assignProperty(button, "geometry", QRectF(0, 0, 100, 100));QSignalTransition *transition = s1->addTransition(button, &QPushButton::clicked, s2);transition->addAnimation(new QPropertyAnimation(button, "geometry"));

为有问题的属性添加动画意味着当进入状态时,属性分配将不再立即生效。相反,动画将在进入状态时开始播放,并平滑地为属性分配动画。因为我们没有设置动画的开始值或结束值,这些将隐式设置。动画的开始值将是动画开始时属性的当前值,结束值将根据为状态定义的属性分配来设置。

如果将状态机的全局恢复策略设置为qstatemmachine::RestoreProperties,则还可以为属性恢复添加动画。

检测到所有属性都已设置为某种状态

当使用动画来分配属性时,状态不再定义机器处于给定状态时属性将具有的确切值。当动画运行时,该属性可能有任何值,这取决于动画。

在某些情况下,能够检测何时为属性分配了由状态定义的值是很有用的。

假设我们有以下代码:

    QMessageBox *messageBox = new QMessageBox(mainWindow);messageBox->addButton(QMessageBox::Ok);messageBox->setText("Button geometry has been set!");messageBox->setIcon(QMessageBox::Information);QState *s1 = new QState();QState *s2 = new QState();s2->assignProperty(button, "geometry", QRectF(0, 0, 50, 50));connect(s2, &QState::entered, messageBox, SLOT(exec()));s1->addTransition(button, &QPushButton::clicked, s2);

当点击按钮时,机器将切换到状态s2,该状态将设置按钮的几何形状,然后弹出消息框提醒用户几何形状已更改。

在不使用动画的正常情况下,这将按预期操作。但是,如果在s1和s2之间的转换上设置了按钮几何形状的动画,则动画将在输入s2时开始,但是在动画完成运行之前,几何形状属性实际上不会达到其定义的值。在这种情况下,消息框将在按钮的几何形状实际设置之前弹出。

为了确保消息框在几何图形实际达到最终值之前不会弹出,我们可以使用状态的propertiesAssigned()信号。propertiesAssigned()信号将在属性被赋予最终值时发出,无论这是立即执行还是在动画播放完成后执行。

    QMessageBox *messageBox = new QMessageBox(mainWindow);messageBox->addButton(QMessageBox::Ok);messageBox->setText("Button geometry has been set!");messageBox->setIcon(QMessageBox::Information);QState *s1 = new QState();QState *s2 = new QState();s2->assignProperty(button, "geometry", QRectF(0, 0, 50, 50));QState *s3 = new QState();connect(s3, &QState::entered, messageBox, SLOT(exec()));s1->addTransition(button, &QPushButton::clicked, s2);s2->addTransition(s2, &QState::propertiesAssigned, s3);

在这个例子中,当点击按钮时,机器将进入s2。它将保持在状态s2,直到几何属性被设置为QRect(0,0,50,50)。然后它会变成s3。当输入s3时,会弹出消息框。如果转换到s2有一个几何属性的动画,那么机器将停留在s2,直到动画完成播放。如果没有这样的动画,它将简单地设置属性并立即进入状态s3。

无论哪种方式,当机器处于状态s3时,您都可以保证属性geometry已被分配了定义的值。

如果将全局恢复策略设置为qstatemmachine::RestoreProperties,则状态将不会发出propertiesAssigned()信号,直到这些也被执行。

如果在动画完成之前退出状态会发生什么

如果一个状态有属性赋值,并且转换到该状态时有属性的动画,那么在属性赋值给该状态定义的值之前,可能会退出该状态。当存在不依赖于propertiesAssigned()信号的状态转换时尤其如此,如前一节所述。

状态机API保证由状态机分配的属性:

  • 具有显式分配给属性的值。
  • 当前被动画化为显式分配给属性的值。

如果在动画完成之前退出状态,则状态机的行为取决于转换的目标状态。如果目标状态显式地为属性赋值,则不会采取任何其他操作。将为该属性分配由目标状态定义的值。

如果目标状态没有给属性赋任何值,有两种选择:默认情况下,属性将被赋值为它离开的状态所定义的值(如果动画被允许完成播放,它将被赋值)。但是,如果设置了全局恢复策略,则将优先考虑全局恢复策略,并且将像往常一样恢复属性。

默认的动画

如前所述,您可以向过渡添加动画,以确保目标状态中的属性分配是动画的。如果你想要一个特定的动画用于一个给定的属性,而不管哪个转换被采用,你可以将它作为默认动画添加到状态机。当构造机器时,不知道由特定状态分配(或恢复)的属性时,这特别有用。

QState *s1 = new QState();
QState *s2 = new QState();s2->assignProperty(object, "fooBar", 2.0);
s1->addTransition(s2);QStateMachine machine;
machine.setInitialState(s1);
machine.addDefaultAnimation(new QPropertyAnimation(object, "fooBar"));

嵌套状态机

QStateMachine是QState的子类。这允许一个状态机成为另一个机器的子状态。QStateMachine重新实现了QState::onEntry()并调用了QStateMachine::start(),这样当进入子状态机时,它将自动开始运行。

父状态机将子状态机视为状态机算法中的原子状态。子状态机是自包含的;它维护自己的事件队列和配置。特别要注意的是,子机器的配置()不是父机器配置的一部分(只有子机器本身是)。

子状态机的状态不能指定为父状态机中转换的目标;只有子状态机本身可以。相反,不能将父状态机的状态指定为子状态机中转换的目标。子状态机的finished()信号可用于触发父状态机中的转换。

The State Machine Framework | Qt Core 5.15.17

qml:The Declarative State Machine Framework | Qt QML 5.15.17

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/28445.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Web的UI自动化基础知识

目录 1 Web自动化入门基础1.1 自动化知识以及工具1.2 主流web自动化测试工具1.3 入门案例 2 使用工具的API2.1 元素定位2.1.1 id选择器2.1.2 name2.1.3 class_name选择器2.1.4 tag_name选择器2.1.5 link_text选择器2.1.6 partial_link_text选择器2.1.7 xpath选择器2.1.8 CSS选择…

mediamtx流媒体服务器测试

MediaMTX简介 在web页面中直接播放rtsp视频流&#xff0c;重点推荐&#xff1a;mediamtx&#xff0c;不仅仅是rtsp-CSDN博客 mediamtx github MediaMTX(以前的rtsp-simple-server)是一个现成的和零依赖的实时媒体服务器和媒体代理&#xff0c;允许发布&#xff0c;读取&…

可视化大屏开发系列——页面布局

页面布局是可视化大屏的基础&#xff0c;想要拥有一个基本美观的大屏&#xff0c;就得考虑页面整体模块的宽高自适应&#xff0c;我们自然就会想到具有强大灵活性flex布局&#xff0c;再借助百分比布局来辅助。至此&#xff0c;大屏页面布局问题即可得到解决。 写在前面&#x…

哪些数据管理知识领域需要做到数据全生命周期管理

一、数据生命周期 数据管理、数据治理、数据安全、元数据管理、数据治理等知识领域,都需要按照数据的生命周期开展管理工作。数据生命周期包括计划、设计/启用、创建/获取、存储/维护、使用、增强和处置。详见下图。 1.数据治理生命周期 1)规划:将数据要求与业务战略连接起…

PTA 6 - 20 汉诺塔问题(py 递归)

这道题是一道比较典型的递归问题&#xff0c;他跟斐波那契数列的本质是一样的&#xff0c;大家自己动手推理一下&#xff0c;非常好推 参考代码&#xff1a; def hanoi(n,a,b,c):global stepif n 1:print(a,"->",c)step 1else:hanoi(n-1,a,c,b)print(a,"…

查看npm版本异常,更新nvm版本解决问题

首先说说遇见的问题&#xff0c;基本上把nvm&#xff0c;npm的坑都排了一遍 nvm版本导致npm install报错 Unexpected token ‘.‘install和查看node版本都正确&#xff0c;结果查看npm版本时候报错 首先就是降低node版本… 可以说基本没用&#xff0c;如果要降低版本的话&…

用python纯手写一个日历

一、代码 # 月份名称数组 months ["January", "February", "March", "April", "May", "June","July", "August", "September", "October", "November", &qu…

深度解析RocketMq源码-持久化组件(二) MappedFileQueue

1.绪论 MappedFileQueue是commitLog中最核心的主组件。前面讲解commitLog的时候也曾说过&#xff0c;MappedFileQueue本质上就是一个MappedFile队列&#xff0c;而commitLog操纵Mmapped读写的时候&#xff0c;也是通过MappedFileQueue来实现的。 commitlog和mappedfilequeue和…

git下载路径

第一步 1进入官网&#xff1a;Git - Downloading Package 第二步 根据自己的系统选择对应版本下载

局域网内怎么访问另一台电脑?(2种方法)

案例&#xff1a;需要在局域网内远程电脑 “当我使用笔记本电脑时&#xff0c;有时需要获取保存在台式机上的文件&#xff0c;而两者都连接在同一个局域网上。我的台式机使用的是Windows 10企业版&#xff0c;而笔记本电脑则是Windows 10专业版。我想知道是否可以通过网络远程…

OpenCV计算形状之间的相似度ShapeContextDistanceExtractor类的使用

操作系统&#xff1a;ubuntu22.04OpenCV版本&#xff1a;OpenCV4.9IDE:Visual Studio Code编程语言&#xff1a;C11 1.功能描述 ShapeContextDistanceExtractor是OpenCV库中的一个类&#xff0c;主要用于计算形状之间的相似度或距离。它是基于形状上下文&#xff08;Shape Co…

26.1 WEB框架介绍

1. Web应用程序 1.1 应用程序有两种模式 应用程序的架构模式主要分为两种: C/S (客户端/服务器端)和B/S(浏览器/服务器端). * 1. C/S模式, 即客户端/服务器模式(Client/Server Model): 是一种分布式计算模式.它将应用程序的功能划分为客户端和服务器端两部分.在这种模式下, 客…

码住!详解时序数据库不同分类与性能对比

加速发展中的时序数据库&#xff0c;基于不同架构&#xff0c;最流行的类别是&#xff1f; 作为管理工业场景时序数据的新兴数据库品类&#xff0c;时序数据库凭借着对海量时序数据的高效存储、高可扩展性、时序分析计算等特性&#xff0c;一跃成为物联网时代工业领域颇受欢迎的…

C++升级软件时删除老版本软件的桌面快捷方式(附源码)

删除桌面快捷方式其实是删除桌面上的快捷方式文件,那我们如何去删除桌面快捷方式文件呢?软件可能已经发布过多个版本,其中的一些版本的快捷方式文件名称可能做了多次改动,程序中不可能记录每个版本的快捷方式名称,没法直接去删除快捷方式文件。本文就给出一种有效的处理办…

【GO-OpenCV】go-cv快速配置

最近对golang实现目标检测心血来潮&#xff0c;尝试在没有sudo权限的平台配置go-cv,有所发现&#xff0c;索性多个平台都做尝试 安装Go语言&#xff08;Golang&#xff09; 通过包管理器安装&#xff08;适用于Debian/Ubuntu&#xff09;(有点慢) 更新包列表&#xff1a; sud…

Linux命令2

文章目录 移动文件或目录mv格式 查找命令/文件存放位目录置which格式 查找文件或目录find格式查找类型多个查找条件逻辑运算符 移动文件或目录 mv 将文件或者目录移动到指定的位置 如果目标的位置和源位置相同&#xff0c;相当于改名操作 跨目录移动相当于window的剪切 格式…

C++ 算法教程

归并排序 #include<iostream> using namespace std; template <class T> void Merge(T data[],int start,int mid,int end) {int len1 mid - start 1, len2 end - mid;int i, j, k;T* left new int[len1];T* right new int[len2];for (i 0; i < len1; i)…

TF-IDF(Term Frequency-Inverse Document Frequency)

TF-IDF&#xff08;Term Frequency-Inverse Document Frequency&#xff09;是一种常用于信息检索和文本挖掘的统计方法&#xff0c;用以评估一个词语对于一个文件集或一个语料库中的其中一份文件的重要程度。它的重要性随着词语在文本中出现的次数成正比增加&#xff0c;但同时…

【SOEM主站】EtherCAT主站时钟偏移补偿

在进行EtherCAT主从通讯测试时&#xff0c;比较容易在DC配置出现错误&#xff0c;特别是使用到从站DC模式时&#xff0c;有时会报同步错误&#xff0c;有时即使没报错误伺服从站运行过程中也会出现电机轴的抖动。引起同步错误其中一个原因就是主站发送数据帧时间存在较大的抖动…

Hadoop+Spark大数据技术(微课版)总复习

图1 Hadoop开发环境 图2 HDFS 图3 MapReduce 图4 HBase 图5 Scala 图6 Spark 图7 Spark RDD 图8 &#xff08;不考&#xff09; 图9 Spark SQL 图10 Spark Streaming 图11 Spark GraphX 第一章 Hadoop大数据开发环境 hadoop是什么&#xff1f; &#xff08;判断题&#…