(C++) 如何在Linux中实现类似于MS Windows的MessageBox?

8
我需要在Linux(SDL)应用程序中实现一个简单的图形消息框,类似于C++(gcc/g++ 4.4.0)中的Windows MessageBox。它只需显示标题、消息和一个确定或关闭按钮,并在单击该按钮时返回给调用函数。
SDL只使用X(11)来打开一个窗口进行(OpenGL)渲染。
我已经查看了一个关于GTK实现的类似线程,但这个实现似乎不能正常工作。
我还尝试了wxWidgets的wxMessageBox函数,但编译头文件会使编译器抛出有关include/c++/4.4.0/bits/stl_algobase.h语法错误的错误消息(gcc 4.4.0 32位openSuSE 11.1 32位)。使用wxWidgets还意味着必须链接大量库,将STL添加到我的应用程序中(否则不需要),以及其他可能的问题,因此我不想使用wxWidgets。
X11/motif(openmotif)具有我所需的功能(XmCreate{Error|Warning|InfoDialog}),但这些需要父部件(例如顶级窗口),我没有并且不接受这些的NULL参数。
所以我现在感到困惑。有没有一种简单的方法来做我想要的事情?或者至少有一种半简单/易于/直截了当的方法吗?如果是,请提供尽可能多的细节。
5个回答

6

3

我个人使用Qt4的QMessageBox

例子:

QMessageBox mb(QMessageBox::Question, "Title", "Message",  QMessageBox::Ok | QMessageBox::Cancel);
if(mb.exec() == QMessageBox::Ok) { do_stuff(); }

谢谢。请问Qt4库的名称是什么(-l<libname>)? - Razzupaltuff
1
@karx11erx:Qt不仅仅是一个小型库,它是一个完整的跨平台GUI(以及更多)解决方案。使用它需要的不仅仅是链接到特定的库。通常最好使用他们的构建系统。Qt通常是一个“全有或全无”的选择。 - Evan Teran
1
使用Qt4会导致gcc 4.4.0给我抛出很多错误,而且我不需要在我的应用程序上面再加一个庞然大物。 - Razzupaltuff
任何GUI工具包与不使用GUI工具包相比都会是一个“庞然大物”(想想Win32有多大!)。错误几乎肯定是由于未正确使用Qt构建系统造成的。 - Evan Teran

2

看起来你需要创建一个顶级的X11/Motif窗口。这里是一些代码帮助你入门:

#include <Xm/Xm.h> 
#include <Xm/PushB.h>

/* Prototype Callback function */

void pushed_fn(Widget , XtPointer , 
               XmPushButtonCallbackStruct *);


main(int argc, char **argv) 

{   Widget top_wid, button;
    XtAppContext  app;

    top_wid = XtVaAppInitialize(&app, "Push", NULL, 0,
        &argc, argv, NULL, NULL);

    button = XmCreatePushButton(top_wid, "Push_me", NULL, 0);

    /* tell Xt to manage button */
                XtManageChild(button);

                /* attach fn to widget */
    XtAddCallback(button, XmNactivateCallback, pushed_fn, NULL);

    XtRealizeWidget(top_wid); /* display widget hierarchy */
    XtAppMainLoop(app); /* enter processing loop */ 

}

void pushed_fn(Widget w, XtPointer client_data, 
               XmPushButtonCallbackStruct *cbs) 
  {   
     printf("Don't Push Me!!\n");
  }

这段内容是从这里复制而来,可能会给你提供更多关于此的指引。

SDL显然为OpenGL渲染创建了一个顶层窗口 - 我在最初的问题中提到了这一点。除非修改SDL,否则无法获取其句柄。 - Razzupaltuff
其实我只需要让X(11)为我显示一个窗口,但是从我在互联网上找到的关于X编程的资料来看,这方面的内容都不是很简单。 - Razzupaltuff
能否从SDL的X11窗口句柄创建一个Motif小部件? - Razzupaltuff
看起来是这样,但我真的不知道。你试过将X11窗口句柄直接传递给消息框函数吗?似乎那样可以解决问题。 - Matthew Talbert
我如何在程序内部终止XtAppMainLoop,或者在用户单击我的消息框的确定按钮后使其终止? - Razzupaltuff

2

这是我的解决方案。我选择使用Motif(OpenMotif),因为它只需要相对较少的额外库(Xm、Xt、X11)。根据消息大小,我的实现会打开一个简单的消息框或一个更复杂的对话框,其中包含一个不可编辑的可滚动文本(后者来自Motif程序员手册,并为我的目的进行了调整)。

包含文件和全局数据:

#include <Xm/Xm.h>
#include <Xm/MwmUtil.h>
#include <Xm/MainW.h>
#include <Xm/CascadeB.h>
#include <Xm/MessageB.h>
#include <Xm/RowColumn.h>
#include <Xm/Form.h>
#include <Xm/PushBG.h>
#include <Xm/LabelG.h>
#include <Xm/PanedW.h>
#include <Xm/Text.h>
#include <Xm/DialogS.h>
#include <Xm/Command.h>

static XtAppContext appShell;

一个用于确定文本消息行数和最大列数的辅助函数:

static int MsgSize (char* pszMsg, int& nCols)
{
if (!(pszMsg && *pszMsg))
   return 0;
int nRows = 1;
nCols = 0;
for (char* p = pszMsg; *p && (pszMsg = strchr (p, '\n')); nRows++, p = ++pszMsg) {
   if (nCols < pszMsg - p)
      nCols = pszMsg - p;
   }
return nRows;
}

消息对话框关闭按钮的回调函数:

void DestroyShell (Widget widget, XtPointer clientData, XtPointer callData)
{
Widget shell = (Widget) clientData;
XtDestroyWidget (shell);
// tell the application event loop to terminate w/o terminating the application
XtAppSetExitFlag (appShell);
}

构建一个对话框,其中包含一个可滚动的、不可编辑的文本小部件和一个关闭按钮。该内容源自Motif程序员手册,并稍作修改(无图标,单个按钮,最小窗口装饰)。

void XmMessageDialog (const char* pszMsg, int nRows, int nCols, bool bError)
{
    Widget       msgBox, pane, msgText, form, widget;
    void         DestroyShell(Widget, XtPointer, XtPointer);
    Arg          args [10];
    int          n = 0;
    int          i;
    Dimension    h;

// Set up a DialogShell as a popup window. Set the delete window protocol response to XmDESTROY to make sure that
// the window goes away appropriately. Otherwise, it's XmUNMAP which means it'd be lost forever, since we're not storing
// the widget globally or statically to this function.
Widget topWid = XtVaAppInitialize (&appShell, "D2X-XL", NULL, 0, &argc, argv, NULL, NULL);
XtSetArg (args [0], XmNdeleteResponse, XmDESTROY);
msgBox = XmCreateDialogShell (topWid, bError ? const_cast<char*>("Error") : const_cast<char*>("Warning"), args, 1);
XtVaGetValues (msgBox, XmNmwmDecorations, &i, NULL);
i &= ~(MWM_DECOR_ALL | MWM_DECOR_MINIMIZE | MWM_DECOR_MAXIMIZE | MWM_DECOR_MENU);
XtVaSetValues (msgBox, XmNmwmDecorations, i, NULL);
XtVaGetValues (msgBox, XmNmwmFunctions, &i, NULL);
i &= ~(MWM_FUNC_ALL | MWM_FUNC_MINIMIZE | MWM_FUNC_MAXIMIZE | MWM_FUNC_CLOSE);
XtVaSetValues (msgBox, XmNmwmFunctions, i, NULL);
// Create a PanedWindow to manage the stuff in this dialog. PanedWindow won't let us set these to 0!
XtSetArg (args [0], XmNsashWidth, 1);
// Make small so user doesn't try to resize
XtSetArg (args [1], XmNsashHeight, 1);
pane = XmCreatePanedWindow (msgBox, const_cast<char*>("pane"), args, 2);
// Create a RowColumn in the form for Label and Text widgets. This is the control area.
form = XmCreateForm (pane, const_cast<char*>("form1"), NULL, 0);
// prepare the text for display in the ScrolledText object we are about to create.
n = 0;
XtSetArg (args [n], XmNscrollVertical, True); n++;
XtSetArg (args [n], XmNscrollHorizontal, False); n++;
XtSetArg (args [n], XmNeditMode, XmMULTI_LINE_EDIT); n++;
XtSetArg (args [n], XmNeditable, False); n++;
XtSetArg (args [n], XmNcursorPositionVisible, False); n++;
XtSetArg (args [n], XmNwordWrap, True); n++;
XtSetArg (args [n], XmNvalue, pszMsg); n++;
XtSetArg (args [n], XmNrows, min (nRows, 30)); n++;
XtSetArg (args [n], XmNcolumns, min (nCols, 120)); n++;
msgText = XmCreateScrolledText (form, const_cast<char*>("help_text"), args, n);
// Attachment values must be set on the Text widget's PARENT, the ScrolledWindow. This  is the object that is positioned.
XtVaSetValues (XtParent (msgText),
               XmNleftAttachment, XmATTACH_FORM,
               XmNtopAttachment, XmATTACH_FORM,
               XmNrightAttachment, XmATTACH_FORM,
               XmNbottomAttachment, XmATTACH_FORM,
               NULL);
XtManageChild (msgText);
XtManageChild (form);
// Create another form to act as the action area for the dialog
XtSetArg (args [0], XmNfractionBase, 5);
form = XmCreateForm (pane, const_cast<char*>("form2"), args, 1);
// The OK button is under the pane's separator and is attached to the left edge of the form. It spreads from
// position 0 to 1 along the bottom (the form is split into 5 separate grids via XmNfractionBase upon creation).
widget = XmCreatePushButtonGadget (form, const_cast<char*>("Close"), NULL, 0);
XtVaSetValues (widget,
               XmNtopAttachment, XmATTACH_FORM,
               XmNbottomAttachment, XmATTACH_FORM,
               XmNleftAttachment, XmATTACH_POSITION,
               XmNleftPosition, 2,
               XmNrightAttachment, XmATTACH_POSITION,
               XmNrightPosition, 3,
               XmNshowAsDefault, True,
               XmNdefaultButtonShadowThickness, 1,
               NULL);
XtManageChild (widget);
XtAddCallback (widget, XmNactivateCallback, DestroyShell, (XtPointer) msgBox);
// Fix the action area pane to its current height -- never let it resize
XtManageChild (form);
XtVaGetValues (widget, XmNheight, &h, NULL);
XtVaSetValues (form, XmNpaneMaximum, h, XmNpaneMinimum, h, NULL);
// This also pops up the dialog, as it is the child of a DialogShell
XtManageChild (pane);
}

消息框“确定”按钮的回调函数

void XmCloseMsgBox (Widget w, XtPointer clientData, XtPointer callData)
{
XtAppSetExitFlag (appShell);
}

决定使用简单或高级消息框,显示其中任意一个,并在用户单击其关闭/确定按钮时将其删除。

void XmMessageBox (const char* pszMsg, bool bError)
{
   Widget   topWid;
   int      nRows, nCols;

nRows = MsgSize (const_cast<char*>(pszMsg), nCols);
if ((nRows > 3) || (nCols > 360))
   XmMessageDialog (pszMsg, nRows, nCols, bError);
else { // use the built-in message box
   topWid = XtVaAppInitialize (&appShell, "D2X-XL", NULL, 0, &argC, argv, NULL, NULL);
   // setup message box text
   Arg args [1];
   XmString xmString = XmStringCreateLocalized (const_cast<char*>(pszMsg));
   XtSetArg (args [0], XmNmessageString, xmString);
   // create and label message box
   Widget xMsgBox = bError
                    ? XmCreateErrorDialog (topWid, const_cast<char*>("Error"), args, 1)
                    : XmCreateWarningDialog (topWid, const_cast<char*>("Warning"), args, 1);
   // remove text resource
   XmStringFree (xmString);
   // remove help and cancel buttons
   XtUnmanageChild (XmMessageBoxGetChild (xMsgBox, XmDIALOG_CANCEL_BUTTON));
   XtUnmanageChild (XmMessageBoxGetChild (xMsgBox, XmDIALOG_HELP_BUTTON));
   // add callback to the "close" button that signals closing of the message box
   XtAddCallback (xMsgBox, XmNokCallback, XmCloseMsgBox, NULL);
   XtManageChild (xMsgBox);
   XtRealizeWidget (topWid);
   }
XtAppMainLoop (appShell);
XtUnrealizeWidget (topWid);
XtDestroyApplicationContext (appShell);
}

1
我建议您寻找一个支持SDL作为后端的GUI库之一。其中一个这样的库是GG,它有类ThreeButtonDlg。当其Run()返回时,您可以查看其Result()。请参阅他们minimal示例中的Initial方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接