【问题标题】:Handling different gui states处理不同的 gui 状态
【发布时间】:2019-03-12 19:56:47
【问题描述】:

所以我想在 Qt/cpp 中为服务器创建 gui 界面,该界面可以处于许多不同的状态,并且根据其状态,gui 中的按钮需要进行不同的设置,例如: 界面:

button1 - 未选中并允许点击

button2 - 禁用(灰显)

button3 - 禁用

button3 - 禁用

然后点击button1后 界面:

button1 - 选中

button2 - 可以点击

button3 - 可以点击

button3 - 可以点击

但是例如,如果服务器处于不同的状态并且您通过 gui 连接,则按钮应如下所示:

图形界面:

button1 - 选中

button2 - 可以点击

button3 - 无法点击

button3 - 无法点击

是否有一些既定的模式/方式可以直观地处理它?这里最大的问题是,如果服务器有很多不同的状态,需要在很多不同的配置中设置按钮。我唯一能想到的就是将所有按钮的状态映射到特定状态,但是……有很多按钮和很多状态。

【问题讨论】:

  • 对不起,我认为这对 SO 来说太宽泛了。我也认为你应该重新考虑你的设计。如果有很多按钮和很多状态,GUI 可能会非常混乱。是否有任何模式(例如,当且仅当这两个按钮被禁用时才会启用此按钮)?是否可以将服务器状态分组在一起,然后至少将一些按钮映射到更高级别的组?否则:将每个按钮的状态映射到按钮状态。
  • 对于这类问题我喜欢State模式。你可以阅读它here
  • 您可以使用Qt State Machine 来真正实现 Ptaq666 建议的State 模式
  • 已经提出的某种显式状态机是一个很好的开始。但是,如果您在每个状态中只启用了相当少的按钮选择,您可能 1. 将所有状态共有的按钮收集在一个或多个单独的容器中,以将它们适当地排列在 stacked layout 组件周围,该组件将仅显示特定状态的相关按钮。
  • @ymoreau 在引用其他网站时,指出cross-posting is frowned upon 通常会有所帮助

标签: c++ qt user-interface button design-patterns


【解决方案1】:

您可以尝试使用标志,其想法是,当事件发生并且您希望 GUI 更改时,您可以设置一个标志,然后在循环中调用该标志。您可以在下面看到一般的想法和概念。

如果您更改标志的状态,您将获得不同的输出,它会反复循环侦听事件,只需为处于不同状态的每个事件编写 GUI 代码。

#include <iostream>
using namespace std;

int getserverstatusfunction() {/*your code to check server status returns 0,1 or 2*/
    return 0;
}
UI.button.click(true) { getresult = 1; }; //change the UI state when the button is clicked

int main() {
    bool running;
    while (running){
    int getresult = getserverstatusfunction();

    if (getresult == 0)
    {
        cout << "Draw flag one interface code\n";
    }
    else if (getresult == 1)
    {
        cout << "Draw flag two interface code\n";
    }
    else {
        cout << "Draw flag three interface code\n";
    }
    system("pause");
    return 0;
}

【讨论】:

    【解决方案2】:

    我发现做到这一点的最好方法就是使用一个插槽方法(例如 UpdateAllButtonStates() 会更新您的所有按钮和复选框,例如:

    void MyWindow::UpdateAllButtonStates()   // Qt-slot method
    {
       const bool shouldButton1BeEnabled = [...];
       button1->setEnabled(shouldButton1BeEnabled);
    
       const bool shouldButton2BeEnabled = [...];
       button2->setEnabled(shouldButton2BeEnabled);
    
       [... and so on for all enables and checked/unchecked states in your GUI]
    }
    

    ...那么,只要您的程序的内部状态发生任何变化,可能需要更新 GUI 中的一个或多个按钮/复选框,请显式调用此方法,或设置将调用它的信号/插槽连接给你。

    这样做的好处是简单 - 使用这种方法,保证在任何内部状态更改后您的 GUI 小部件将更新到预期状态是微不足道的,因为只有一个代码 -编写和调试的路径。另一种选择(尝试为程序中每个可能的状态更改提出正确的过渡行为)很快就会导致难以处理的复杂性,以及无休止的调试和麻烦。

    您可能认为缺点是效率低下——毕竟,我们正在更新所有按钮,即使在任何情况下,可能只有其中一个发生了变化——但是 Qt 的代码足够聪明,可以调用 setEnabled(false)已禁用的按钮是无操作的(同样在已启用的按钮上调用 setEnabled(true) 等),因此重绘小部件像素的重量级代码只会在小部件的状态实际发生变化时执行.

    UpdateAllButtonStates() 内部计算 shouldButton1BeEnabled 等的逻辑确实执行了很多,但它通常最终是非常微不足道的逻辑,因此结果并不重要。但是,如果由于某种原因该逻辑变得昂贵,您可以选择通过使用异步执行和布尔“脏位”来降低执行 UpdateAllButtonStates() 的频率,例如:

    void MyWindow::ScheduleUpdateAllButtonStates()   // Qt-slot method
    {
       if (_dirtyBit == false)
       {
          _dirtyBit = true;
          QTimer::singleShot(0, this, SLOT(UpdateAllButtonStates()));
       }
    }
    
    void MyWindow::UpdateAllButtonStates()
    {
       if (_dirtyBit == false) return; 
       _dirtyBit = false;
    
       // Update button enables/checked states as previously, here
    }
    

    ...然后让您所有的内部状态更改代码调用ScheduleUpdateAllButtonStates(),而不是直接调用UpdateAllButtonStates();优点是即使ScheduleUpdateAllButtonStates() 被连续调用500 次,它也只会导致UpdateAllButtonStates() 在Qt 事件循环的下一次迭代中被调用一次。

    【讨论】:

      【解决方案3】:

      启用/禁用按钮 UI 逻辑可能非常脏且难以管理和跟踪。此外,有时我们希望处于特定状态并希望进行微小的更改,例如仅更改一个按钮的状态。这是一种方法。它是通用的,但您必须在您的 UI 中相应地删除它。

      #include <iostream>
      
      class UIState
      {
      protected:
          bool btn1;
          bool btn2;
          bool btn3;
      
      public:
          UIState()
          {
              btn1 = false;
              btn2 = false;
              btn3 = false;
          }
      
          virtual void setBtn1State(bool new_state)
          {
              btn1 = new_state;
      
              std::cout << btn1 << btn2 << btn3 << std::endl;
          };
          virtual void setBtn2State(bool new_state)
          {
              btn2 = new_state;
      
              std::cout << btn1 << btn2 << btn3 << std::endl;
          };
          virtual void setBtn3State(bool new_state)
          {
              btn3 = new_state;
      
              std::cout << btn1 << btn2 << btn3 << std::endl;
          };
      };
      
      class UIStateAllEnabled : public UIState
      {
      
      public:
          UIStateAllEnabled()
          {
              btn1 = true;
              btn2 = true;
              btn3 = true;
      
              std::cout << btn1 << btn2 << btn3 << std::endl;
          }
      
      };
      
      class UIStateAllDisabled : public UIState
      {
      
      public:
          UIStateAllDisabled()
          {
              btn1 = false;
              btn2 = false;
              btn3 = false;
      
              std::cout << btn1 << btn2 << btn3 << std::endl;
          }
      
      };
      
      class UI
      {
          UIState * currentState;
      
      public:
          UI()
          {
              currentState = NULL;
          }
      
          ~UI()
          {
              if (currentState != NULL)
              {
                  delete currentState;
                  std::cout << "deleted current state" << std::endl;
              }
          }
      
          void setState(UIState * new_state)
          {
              // should also check for if already current state?
      
              UIState * prevState = currentState;
              currentState = new_state;
      
              if (prevState != NULL)
              {
                  delete prevState;
      
                  std::cout << "deleted previous state" << std::endl;
              }
          }
      
          void setBtn1State(bool new_state)
          {
              currentState->setBtn1State(new_state);
          };
      
          void setBtn2State(bool new_state)
          {
              currentState->setBtn2State(new_state);
          };
      
          void setBtn3State(bool new_state)
          {
              currentState->setBtn3State(new_state);
          };
      };
      
      
      int main()
      {
          UI ui;
      
          // enable all buttons
          ui.setState(new UIStateAllEnabled);
      
          // Now say you want to change state of a particular button within this state.
          ui.setBtn1State(false);
          ui.setBtn3State(false);
      
          // switch to a completely new state, disable all buttons
          ui.setState(new UIStateAllDisabled);
      
          // customize within that sate
          ui.setBtn3State(true);
      
          return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 2022-07-05
        • 1970-01-01
        • 2017-11-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多