精华内容
下载资源
问答
  • 创建菜单后为实现菜单项的命令功能
    万次阅读
    2012-03-08 16:47:23
     

    菜单项和菜单命令响应函数

    1、创建基于单文档工程Menu,添加菜单项:IDM_TEST  Test

    2、通过类向导, 为IDM_TEST在CMainFrame、CMenuView、CMenuDoc、CMenuApp下添加菜单命令响应函数,即WM_COMMAND响应函数。

    通过实验发现:

    1、响应Test菜单项命令的顺序依次是:视图、文档类、框架类、应用程序类。

    2、菜单命令消息路由的具体过程:

    ①点击某菜单项,框架类最先接到菜单命令消息。

    ②框架类把接收到得这个消息交给它的子窗口,即视图类。

    ③视图类根据命令消息映射机制查找自身是否对此消息进行了响应,如果响应了,就调用相应响应函数对这个消息进行处理,消息路由过程结束。

    ④如果视图类未对此消息响应,交给文档类,文档类同样查找自身是否对此消息进行了响应。

    ⑤如果文档类为作出响应,再交还给视图类,视图类把它交还给框架类。

    ⑥框架类查看自身,如果未响应,就把该菜单消息命令交给应用程序类进行处理。

    3、添加Test菜单项的命令响应函数后,在三处进行了添加:

    eg:在视图类中添加Test菜单命令响应函数

    ①在视图类头文件中,两个AFX_MSG注释宏间,添加了命令消息响应函数原型

    afx_msg void OnTest();

    ②在视图类的源文件中,两个AFX_MSG_MAP注释宏之间添加了ON_COMMAND宏,将菜单ID和命令响应函数关联起来。

    ON_COMMAND(IDM_TEST,OnTest)

    ③视图类源文件中有命令消息响应函数实现代码。

    相关知识注:

    1、资源ID号命名:eg:

    IDM:Menu/IDC:Cursor/IDI:Icon

    2、C*App/C*Doc都不是从CWnd派生,没有MessageBox成员函数,可以使用全局的MessageBox函数,或者使用应用程序框架的函数:AfxMessageBox

    int AfxMessageBox(LPCTSTR lpszText, UINT nType = MB_OK,

    UINT nIDHelp = 0)

    AfxMessgeBox函数后两个参数为默认值,只需给第一个参数赋值就行。

    eg:AfxMessageBox(“Hello”);

    菜单命令的路由

    Windows消息分类:

    ①标准消息:除WM_COMMAND之外,所有以WM_开头的消息都是标准消息。

    ②命令消息:以WM_COMMAND形式,来自菜单、加速键、工具栏按钮的消息。

    ③通告消息:WM_COMMAND形式,由控件产生,目的是向父窗口(通常是对话框)通知事件的发生。

    CWnd类派生于CCmdTarget类,凡是从CWnd派生的类即可接收标准消息,也可接收命令消息和通告消息。从CCmdTarget派生的类,只能接收命令消息和通告消息,不能接收标准消息。eg:该例中CMenuDoc和CWinApp都派生于CCmdTarget类,所以可以接收菜单命令消息,因为不是从CWnd类派生的,不能接收标准消息。

    消息路由

    命令消息使用的是ON_COMMAND宏,其路由过程同标准消息还是有区别的:

    ①OnWndMsg函数对消息类型判断,若是标准消息,利用一般消息映射机制,查找哪个类响应了当前消息。

    ②若是命令消息,交给OnCommand函数处理,在OnCommand函数中完成消息路由。

    ③若是通告消息,交给OnNotify函数处理。

    ④OnCommand和OnNotify最后都会调用OnCmdMsg函数。

    基本菜单操作

    1、认识菜单

     

    如图,【文件】为第一个子菜单,索引为0,【编辑】为第二个子菜单,索引为1。这些默认生成的子菜单都没有ID。

    【新建】为文件下第一个菜单项,索引为0,有自己ID:ID_FILE_NEW;

    【打印】ID为ID_FILE_PRINT,索引为5,而不是4。原因是其上有一分隔栏分隔栏在菜单项中占据索引位置。

    可以通过ID和索引访问菜单项,只能通过索引访问默认子菜单。

    2、菜单标记项

    使用示例:为【文件】子菜单中【新建】菜单项添加标记功能。

    在CMainFrame类得OnCreate函数中添加:

    GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION |

    MF_CHECKED);

    注:

    1、【文件】和【新建】索引号均为0

    2、CMenu* GetMenu() const;获得指向菜单栏的指针。GetMenu函数是CWnd类得成员函数。

    CMenu类提供许多菜单操作有关的成员函数,eg:

    CMenu* GetSubMenu(int nPos) const;获得指向nPos指定子菜单的指针

    3、为菜单项添加或移除标记的函数:

    UINT CheckMenuItem(UINT nIDCheckItem, UINT nCheck);

    nIDCheckItem指定需要处理的菜单项,取值由nCheck决定。

    nCheck指定如何设置菜单项,如何定位菜单项位置,取值为:

    MF_CHECKED:设置菜单项的复选标记

    MF_UNCHECKED:移走复选标记

    MF_BYPOSITION:根据菜单项位置访问菜单项,即第一个参数指定菜单项索引号

    MF_BYCOMMAND:根据菜单项的命令访问菜单项,即第一个参数指定菜单项命令ID

    3、默认菜单项

    菜单项粗体显示,为默认菜单项

    BOOL SetDefaultItem(UINT uItem, BOOL fByPos = FALSE);

    uItem:ID或索引,由参数2决定

    fByPos:FALSE表示参数1为ID,TRUE表为索引。

    使用示例:将【打印】设为默认菜单项

    GetMenu()->GetSubMenu(0)->SetDefaultItem(5,TRUE);

    GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_PRINT,TRUE);

    注:一个子菜单只能设定一个默认项

    4、图形标记菜单

    BOOL SetMenuItemBitmaps(UINT nPosition, UINT nFlags, const CBitmap* pBmpUnchecked, const CBitmap* pBmpChecked);

    参数一:ID或索引,由参数二决定

    参数二:MF_BYCOMMAND:参数一为ID

           MF_BYPOSITION:索引

    参数三:指定取消菜单项选中状态时位图

    参数四:指定选中时的位图

    使用示例:

    在CMainFrame类中添加成员变量:CBitmap m_bitmap

    导入或创建位图资源:ID为IDB_BITMAP1

    在CMainFrame的Oncreate函数中添加:

    m_bitmap.LoadBitmap(IDB_BITMAP1);

    GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(0,MF_BYPOSITION,

    &m_bitmap,&m_bitmap)

    注:

    1、应将位图大小设置为图形标记菜单上显示的位图的尺寸,否则只显示部分。

    2、获得图形标记菜单上位图尺寸:

    int GetSystemMetrics(int nIndex);

    nIndex:指定希望获得那部分系统信息。

    值为SM_CXMENUCHECK或SM_CYMENUCHECK时,函数获取标记菜单上标记图形默认尺寸,前者表宽度,后者为高度。

    代码:

    CString str;

    str.Format(“x=%d,y=%d”,GetSystemMetrics(SM_CXMENUCHECK),

    GetSystemMetrics(SM_CYMENUCHECK));

    MessageBox(str);

    这样就可获得位图大小信息。

    5、禁用菜单项

    UINT EnableMenuItem(UINT nIDEnableItem, UINT nEnable);

    参数一:ID或索引,由参数二决定。

    参数二:可以是以下参数的组合

    MF_BYCOMMAND  MF_BYPOSITION   MF_DISABLED  MF_ENABLED  MF_GRAYED

    其中MF_DIABLED只不可用并不变灰,常将MF_DISABLED和MF_GRAYED联合使用。

    代码:CMainFrame的Oncreate函数中:

    GetMenu()->GetSubMenu(0)->EnableMenuItem(1,MF_BYPOSIONT

     | MF_DISBLED | MF_GRAYED);

    还要在CMainFrame的构造函数中将m_bAutoMenuEnable设为FALSE;

    m_bAutoMenuEnable = FALSE;

    注:

    1、原因:如果想要改变菜单项状态,就必须把m_bAutoMenuEnable设为FALSE,之后对菜单项状态的更改才起作用。

    2、将m_bAutoMenuEnable设为FALSE后,就不对ON_UPDATE_COMMAND_UI或ON_COMMAND消息进行响应处理,CMenu类的EnableMenuItem函数将能够正常工作。

    此后,原菜单项的默认设置将不起作用,需要用户设置(eg:原有的灰色项将不再为灰色)。

    6、移除和装载菜单

    1、BOOL SetMenu(CMenu* pMenu);pMenu若为NULL,当前菜单被移除。

    eg:CMainFrame的OnCreate函数中添加:

    SetMenu(NULL);//程序中菜单被移除

    2、重新装载

    CMenu menu;

    menu.LoadMenu(IDR_MAINFRAME);//IDR_MAINFRAME为菜单ID

    SetMenu(&menu);

    注:菜单资源同位图资源一样,也要加载到菜单对象中,然后调用SetMenu把菜单设置为刚刚加载的菜单对象。

    注:此处的错误:menu是一局部变量,会引发错误。

    1、方法一:将menu设为CMainFrame的成员变量

    2、方法二:使用Detach将菜单句柄同菜单对象分离,就不会受menu生存期的影响。

    即添加:menu.Detach();

    MFC菜单命令更新机制

    1、如果要在程序中设置某个菜单项的状态,通过类向导添加UPDATE_COMMAND_UI消息响应函数,然后在函数中进行相应设置。

    2、添加了UPDATE_COMMAND_UI消息响应函数后

    当要显示菜单时,操作系统发出WM_INITMENUPOPUP消息,然后由程序窗口的基类如CFrameWnd接管

    它创建一个CCmdUI对象,与程序中第一个菜单项关联,调用该对象的一个成员函数DoUpdate()。

    该函数发出CN_UPDADTE_COMMAND_UI消息,该消息带有一个指向CCmdUI对象的指针。

    这时,系统判断是否存在一个ON_UPDATE_COMMAND_UI宏去捕捉该消息。

    如果找到就交给相应消息响应函数去处理。函数中利用传递过来的CCmdUI对象去调用相应函数。

    更新完第一个菜单项后,同一个CCmdUI对象就设置为与第二个菜单项相关联,依次进行直到完成所有菜单项的设置。

    3、工具栏和菜单栏上对应项ID相同。工具栏中分隔符也占据索引。工具栏和菜单栏对应项索引值不同。

    4、菜单项的维护依赖CN_UPDATE_COMMAND_UI消息。通过在消息映射中添加ON_UPDATE_COMMAND_UI宏来捕获。

    4、使用示例:

    通过类向导为ID_EDIT_CUT添加UPDATE_COMMAND_UI响应函数。

    这样在CMainFrame类的消息映射中就添加了一个ON_UPDATE_COMMAND_UI宏。

    ON_UPDATE_COMMAND_UI(ID_EDIT_CUT,OnUpdateEditCut)

    在OnUpdateEditCut中添加:

    void CMainFrame::OnUpdateEditCut(CCmdUI* pCmdUI)

    {

        pCmdUI->Enable();//使菜单项可用

        //pCmdUI->Enable(FALSE);// 使菜单项禁用

    //此时pCmdUI指向ID_EDIT_CUT菜单项

    }

    快捷菜单

    TrackPopupMenu函数显示一快捷菜单。

    BOOL TrackPopupMenu(UINT nFlags, int x, int y, CWnd* pWnd,

    LPCRECT lpRect = NULL);

    nFlags:菜单在屏幕上显示位置

    x,y:显示位置处得x和y坐标

    pWnd:快捷菜单拥有者

    lpRect:指定一矩形区域。用户在区域内单击鼠标,快捷菜单也保持显示。设为NULL,则在快捷菜单范围外单击鼠标,菜单消失。默认值为NULL。

    示例:

    添加一菜单资源IDR_MENU1,添加相应菜单项。

    在CMenuView的OnRButtonDown函数中添加:

    CMenu menu;

    menu.LoadMenu(IDR_MENU1);

    CMenu* pPopup = menu.GetSubMenu(0);

    ClientToScreen(&point);

    pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,

    point.x, point.y, this);

    析:

    1、视窗覆盖在主框架之上,所以未在CMainFrame类中添加代码,而是在C*View类中。

    2、TrackPopupMenu中坐标是屏幕坐标,而鼠标单击处坐标是窗口客户区坐标(以程序窗口左上角为坐标原点),因此应把客户区坐标转化为屏幕坐标。

    使用ClientToScreen。

    3、this指针表明快捷菜单为视窗类所有。若让其为主框架类所有,this改为GetParent()。但这样只是给主框架类获得消息处理的机会,如果视窗类和主框架类中都有快捷菜单菜单项单击事件响应函数,视窗类中的函数会响应。

    动态菜单操作

    包括:针对弹出菜单的动态操作和针对菜单项的动态操作

    1、添加菜单项目:

    BOOL AppendMenu(UINT nFlags, UINT_PTR nIDNewItem = 0,

    LPCTSTR lpszNewItem = NULL);

    nFlags:指定新添加的菜单项目的状态信息

    nIDNewItem:参数一为MF_POPUP,nIDNewItem为顶层菜单句柄,否则为要添加的新菜单项ID。参数一为MF_SEPATATOR,nIDNewItem值被忽略。

    lpszNewItem:参数一为MF_STRING,lpszNewItem为指向要添加的新菜单项目的文本指针。参数一为MF_OWNERDRAW,lpszNewItem为指向该菜单项目的一个附加数据的指针。参数一为MF_SEPARATOR,lpszNewItem值被忽略。

    代码:

    CMenu menu;

    menu.CreateMenu();

    GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,”Test”);

    menu.Detach();

    析:

    1、可用menu.CreatePopupMenu();创建一弹出菜单并与menu对象关联。

    2、menu.Detach();将菜单句柄和对象分离。也可使用将menu设为CMainFrame的成员变量的方式。(解决局部变量造成的错误问题)

    3、CMenu类的成员变量m_hMenu是菜单句柄,为HMENU类型,强制转换为UINT类型。

    2、插入菜单项

    BOOL InsertMenu(UINT nPosition, UINT nFlags, UINT_PTR nIDNewItem = 0, LPCTSTR lpszNewItem = NULL);

    nFlags在AppendMenu的nFlags基础上添加:MF_BYCOMMAND(此时nPostion为一菜单命令标识,表明在nPostion指定菜单项后添加)或MF_BYPOSITION(此时nPosition为索引值,表新添加项的位置)。

    示例:

    CMenu menu;

    menu.CreateMenu();

    GetMenu()->InsertMenu(2,MF_POPUP | MF_POSITION,

    (UINT)menu.m_hmenu,”Test”);

    //在索引2位置添加菜单子菜单Test

    menu.AppendMenu(MF_STRING,111,”Hello”);

    menu.AppendMenu(MF_STRING,112,”Well”);

    //在Test下添加菜单项

    GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,113,”Welcome”);

    //在文件子菜单下添加菜单项Welcome

    GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN,

    MF_COMMAND | MF_STRING,114,”VC编程”);

    //文件子菜单下,打开菜单项后添加菜单项

    GetMenu()->GetSubMenu(0)->InsertMenu(4,

    MF_POSITION | MF_STRING,115,”VC++”);

    //文件子菜单下,索引4位置添加菜单项

    menu.Detach();

    注:111为指向要添加的新菜单项的文本的指针。选择数据有什么要求?此处Hello为灰色禁用状态,若换为0,则为可用,为什么?

    3、删除菜单

    BOOL DelteMenu(UINT nPosition, UINT nFlags);

    可以删除子菜单及子菜单下一个菜单项。

    如果调用函数的是菜单栏对象,删除指定子菜单。

    如果是一子菜单对象,删除子菜单下菜单项。

    GetMenu()->DeleteMenu(1,MF_BYPOSITION);

    //删除了编辑子菜单

    GetMenu()->GetSubMenu(0)->DeleteMenu(1,MF_BYPOSITION);

    //删除了文件子菜单下的索引为1的菜单项

    4、动态添加菜单项的命令响应

    示例:Test子菜单下Hello菜单项,添加命令消息响应函数

    为a菜单项创建一菜单资源ID

    ①在Resource.h文件中,手工添加:

    #define IDM_HELLO 111

    ②原来的代码:

    menu.AppendMenu(MF_STRING,111,” Hello”);

    可以改为menu.AppendMenu(MF_STRING,IDM_A,” Hello”);

    ③为该菜单项添加命令消息响应函数

    遵循MFC消息映射机制,需要在三处进行添加。

    ⑴在响应该菜单项命令的程序类(本例为CMainFrame)头文件中添加响应函数原型。

    在声明消息映射宏(DECLARE_MESSAGE_MAP)之上,两个AFX_MSG注释宏后。(放在AFX_MSG注释宏后表示自己手动添加,放在之间是系统自动添加的)

    afx_msg void OnHello();

    ⑵在响应这个菜单项命令的程序类的源文件中消息映射表中添加消息映射。

    在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP宏之间,两个AFX_MSG_MAP注释宏之后。添加ON_COMMAND宏。(同样,放在AFX_MSG注释宏后表示自己手动添加,放在之间是系统自动添加的)

     

    ON_COMMAND(IDM_HELLEO,OnHello);

    ⑶原文件中添加函数体:

    void CMainFrame::OnHello()

    {

        MessageBox(“Hello clicked”);

    }

    电话本示例程序

    关键代码:

    1、创建基于单文档工程Draw

    在CDrawView类中添加成员变量:

    private:

    CString m_szText;//存输入字符

    int m_nIndex;//回车计数

    CMenu m_menu;//用于子菜单和菜单项的创建

    public:

    CStringArray m_strArray;//存储字符串,设为public是为CMainFrame类能够调用它

    2、构造函数中初始化:

    m_nIndex = -1;

    m_szText = “”;

    3、CDrawView类的OnChar函数中:

    CClientDC dc(this);

    if(0x0d == nChar)

    {

        if(0 == m_nIndex)

    {

        m_menu.CreatePopupMenu();

        GetParent()->GetMenu()->AppendMenu(MF_POPUP,

    (UINT)m_menu.m_hMenu,”PhoneBook”);

        GetParent()->DrawMenuBar();

    }

    m_menu.AppendMenu(MF_STRING,ID_PHONE1,

    m_szText.Left(m_szText.Find(‘ ‘)));

        m_strArray.Add(m_szText);

    m_szText.Empty();

    Invalidate();

    }

    else

    {

        m_szText += nChar;

        dc.TextOut(0,0,m_szText);

    {

    析:

    ①以前的示例在OnCreate函数中添加代码,OnCreate函数用于窗口的创建,再此对菜单栏的修改会立刻显示,但是窗口创建并显示完成之后,再去更改程序菜单中内容,需要对菜单栏进行重绘操作。

    CWnd类的DrawMenuBar成员函数用来完成菜单栏的重绘操作。

    ②视图没有菜单栏,对菜单栏的操作是在主框架类中,GetParent()获得视图父窗口(框架类窗口)

    ③回车后字符串清空,屏幕上的字符也要清除,用到Invalidate()

    3、还要为创建的菜单项添加消息响应函数,技巧是:

    在【帮助】子菜单后添加一系列菜单项,通过类向导添加消息响应函数,然后修改其与动态创建的PhoneBook下的菜单项关联。

    注意将函数声明和消息映射都放到注释宏外。

    4、在CMainFrame中重写OnCommand虚函数。

    OnCommand虚函数会把命令消息截获。这里我们只处理菜单项关联的命令函数,其余的仍交给基类路由。

    代码:

    int MenuCmdID = LOWORD(wParam);

    CMenuView* pView = (CMenuView*)GetActiveView();

    if(MenuCmdID >= IDM_PHONE1 && MenuCmdID <

    IDM_PHONE1+pView->m_strArray.GetSize())

    {

        CClientDC dc(pView);

        dc.TextOut(0,0,m_strArray.GetAt(MenuCmdID - IDM_PHONE1);

        return TRUE;

    }

    析:

    ①LOWORD宏取得当前消息的命令ID

    ②要用到视图类CMenuView的m_strArray成员变量,要先获得视图对象。

    CView* GetActiveView() const;

    返回一CView类型指针,程序需要CMenuView类型指针,要强制类型转换。

    ③在框架类中用到视图类类型,要在CMainFrame源文件中包含视图类头文件。

    #include “MenuView.h”

    另外,MenuView.h中引用了尚未定义的CMenuDoc类(CMenuView.cpp引用MenuView.h之所以没有问题,是因为:

    #include “MenuDoc.h”

    #include “MenuView.h”

    即在引用MenuView.h前引用了MenuDoc.h,提前获得了CMenuDoc类的定义。)

    我们可以吧MenuDoc.h的引用提前到MenuView.h中,剪切过去即可。

    ④return TRUE;的使用是因为:CMainFrame处理过命令消息后还是会把它交给基类,由基类(CFrameWnd)的OnCommand函数继续路由。CFrameWnd会把它交给程序的视图类:CMemuView。return TRUE;后就不会路由了。

     

    未知:必须手动添加命令消息响应?

    如何给添加的每个菜单项添加?

    P182有一点未看懂

     

     

     

    更多相关内容
  • 易语言自绘右键菜单例程源码,自绘右键菜单例程,子程序,设置信息,取窗口信息,重画菜单,将消息传答窗口函数,添加菜单项,创建弹出式菜单,显示位置,取子菜单句柄,取条目字串,DLL命令1
  • 摘要:菜单、工具栏和状态栏是大多数GUI 应用程序的常见且重要的图形组件。您可以使用它们您的用户提供一种快速访问应用程序选项和功能的方法。
    摘要:菜单、工具栏和状态栏是大多数GUI 应用程序的常见且重要的图形组件。您可以使用它们为您的用户提供一种快速访问应用程序选项和功能的方法。 

    本文分享自华为云社区《Python 和 PyQt:创建菜单、工具栏和状态栏》,作者:Yuchuan。

    在使用 Python 和PyQt开发图形用户界面 (GUI)应用程序时,您将使用的一些最有用和最通用的图形元素是菜单、工具栏和状态栏。

    菜单和工具栏可以使您的应用程序看起来精美专业,为用户提供一组可访问的选项,而状态栏允许您显示有关应用程序状态的相关信息。

    在本教程中,您将学习:

    • 什么菜单,工具栏和状态栏是
    • 如何以编程方式创建菜单、工具栏和状态栏
    • 如何使用PyQt 操作填充 Python 菜单和工具栏
    • 如何使用状态栏显示状态信息

    此外,您将学习一些编程最佳实践,您可以在使用 Python 和 PyQt 创建菜单、工具栏和状态栏时应用这些实践。

    在 PyQt 中构建 Python 菜单栏、菜单和工具栏

    一个菜单栏是一个GUI应用程序的区域主窗口中保存菜单。菜单是选项的下拉列表,可以方便地访问应用程序的选项。例如,如果您正在创建一个文本编辑器,那么您的菜单栏中可能会有以下一些菜单:

    • 一个文件菜单,提供以下的一些菜单选项:
      • 新建用于创建新文档
      • 打开以打开现有文档
      • 打开最近打开最近的文档
      • Save用于保存文档
      • Exit退出应用程序
    • 提供以下一些菜单选项的“编辑”菜单:
      • Copy用于复制一些文本
      • Paste用于粘贴一些文本
      • Cut用于剪切一些文本
    • 一个帮助菜单,提供以下一些菜单选项:
      • 帮助内容用于启动用户手册和帮助内容
      • 关于启动关于对话框

    您还可以将其中一些选项添加到工具栏。工具栏是带有有意义图标的按钮面板,可提供对应用程序中最常用选项的快速访问。在您的文本编辑器示例中,您可以向工具栏添加NewOpenSaveCopyPaste等选项。

    注意:在本教程中,您将开发一个实现上述所有菜单和选项的示例应用程序。您可以使用此示例应用程序作为创建文本编辑器项目的起点。

    在本节中,您将学习如何使用 Python 和 PyQt 向 GUI 应用程序添加菜单栏、菜单和工具栏的基础知识。

    在继续之前,您将创建一个示例 PyQt 应用程序,您将在本教程中使用该应用程序。在每个部分中,您将向此示例应用程序添加新特性和功能。该应用程序将是一个主窗口风格的应用程序。这意味着它将有一个菜单栏、一个工具栏、一个状态栏和一个中央小部件。

    打开您最喜欢的代码编辑器或 IDE并创建一个名为sample_app.py. 然后在其中添加以下代码:

    import sys
    
    from PyQt5.QtCore import Qt
    from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow
    
    class Window(QMainWindow):
        """Main Window."""
        def __init__(self, parent=None):
            """Initializer."""
            super().__init__(parent)
            self.setWindowTitle("Python Menus & Toolbars")
            self.resize(400, 200)
            self.centralWidget = QLabel("Hello, World")
            self.centralWidget.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
            self.setCentralWidget(self.centralWidget)
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        win = Window()
        win.show()
        sys.exit(app.exec_())

    现在sample_app.py包含创建示例 PyQt 应用程序所需的所有代码。在这种情况下,Window继承自QMainWindow. 因此,您正在构建一个主窗口样式的应用程序。

    注意:不幸的是,PyQt5 的官方文档有一些不完整的部分。要解决此问题,您可以查看PyQt4 文档或原始Qt 文档。

    在类初始化程序中.__init__(),您首先使用 调用父类的初始化程序super()。然后使用 设置窗口的标题.setWindowTitle()并使用调整窗口大小.resize()。

    注意:如果您不熟悉 PyQt 应用程序以及如何创建它们,那么您可以查看Python 和 PyQt:构建 GUI 桌面计算器。

    窗口的中央小部件是一个QLabel对象,您将使用它来显示消息以响应某些用户操作。这些消息将显示在窗口的中央。要做到这一点,你叫.setAlignment()上QLabel对象与一对夫妇的对齐标志。

    如果您从命令行运行该应用程序,那么您将在屏幕上看到以下窗口:

    就是这样!您已经使用 Python 和 PyQt 创建了一个主窗口风格的应用程序。您将在本教程中即将出现的所有示例中使用此示例应用程序。

    创建菜单栏

    在 PyQt 主窗口风格的应用程序中,默认QMainWindow提供一个空QMenuBar对象。要访问此菜单栏,您需要调用.menuBar()您的QMainWindow对象。此方法将返回一个空的菜单栏。此菜单栏的父级将是您的主窗口对象。

    现在返回到您的示例应用程序并在 的定义中添加以下方法Window:

    class Window(QMainWindow):
        # Snip...
        def _createMenuBar(self):
            menuBar = self.menuBar()

    这是在 PyQt 中创建菜单栏的首选方式。在这里,menuBar变量将包含一个空的菜单栏,这将是您的主窗口的菜单栏。

    注意:  PyQt 编程中的一个常见做法是将局部变量用于您不会使用或从其定义方法之外需要的对象。Python垃圾收集所有超出范围的对象,因此您可能认为menuBar在上面的示例中,一旦._createMenuBar() 返回就会消失。

    事实是 PyQt 保留对本地对象的引用,例如menuBar使用它们的所有权或父子关系。换句话说,由于menuBar它归您的主窗口对象所有,Python 将无法对其进行垃圾收集。

    向 PyQt 应用程序添加菜单栏的另一种方法是创建一个QMenuBar对象,然后使用.setMenuBar(). 考虑到这一点,您还可以._createMenuBar()按以下方式编写:

    from PyQt5.QtWidgets import QMenuBar
    # Snip...
    
    class Window(QMainWindow):
        # Snip...
        def _createMenuBar(self):
            menuBar = QMenuBar(self)
            self.setMenuBar(menuBar)

    在上面的例子中,menuBar持有一个QMenuBar父级设置为的对象self,它是应用程序的主窗口。一旦你有了菜单栏对象,你就可以.setMenuBar()将它添加到你的主窗口中。最后,需要注意的是在这个例子中工作,你首先需要进口 QMenuBar的PyQt5.QWidgets。

    在 GUI 应用程序中,菜单栏会根据底层操作系统显示在不同的位置:

    • Windows:在应用程序主窗口的顶部,标题栏下方
    • macOS:在屏幕顶部
    • Linux:在主窗口顶部或屏幕顶部,取决于您的桌面环境

    为应用程序创建菜单栏的最后一步是._createMenuBar()从主窗口的初始化程序调用.__init__():

    class Window(QMainWindow):
        """Main Window."""
        def __init__(self, parent=None):
            # Snip...
            self._createMenuBar()

    如果您使用这些新更改运行示例应用程序,那么您将看不到应用程序主窗口中显示的菜单栏。那是因为您的菜单栏仍然是空的。要查看应用程序主窗口上的菜单栏,您需要创建一些菜单。这就是你接下来要学习的内容。

    将菜单添加到菜单栏

    菜单是菜单选项的下拉列表,您可以通过单击它们或按键盘快捷键来触发。在 PyQt 中,至少有三种方法可以将菜单添加到菜单栏对象:

    1. QMenuBar.addMenu(menu)将QMenu对象 ( menu)附加到菜单栏对象。它返回与此菜单关联的操作。
    2. QMenuBar.addMenu(title)创建一个QMenu以字符串 ( title) 作为标题的新对象并将其附加到菜单栏。菜单栏取得菜单的所有权,该方法返回新QMenu对象。
    3. QMenuBar.addMenu(icon, title)创建并追加新的QMenu物品与icon和title一个菜单栏对象。菜单栏取得菜单的所有权,该方法返回新QMenu对象。

    如果使用第一个选项,则需要先创建自定义QMenu对象。为此,您可以使用以下构造函数之一:

    1. QMenu(parent)
    2. QMenu(title, parent)

    在这两种情况下,parent是QWidget将持有QMenu对象的所有权。您通常会设置parent到您将在其中使用菜单的窗口。在第二个构造函数中,title将保存一个带有描述菜单选项的文本的字符串。

    以下是将FileEditHelp菜单添加到示例应用程序的菜单栏的方法:

    from PyQt5.QtWidgets import QMenu
    # Snip...
    
    class Window(QMainWindow):
        # Snip...
        def _createMenuBar(self):
            menuBar = self.menuBar()
            # Creating menus using a QMenu object
            fileMenu = QMenu("&File", self)
            menuBar.addMenu(fileMenu)
            # Creating menus using a title
            editMenu = menuBar.addMenu("&Edit")
            helpMenu = menuBar.addMenu("&Help")

    首先,你导入 QMenu的PyQt5.QtWidgets。然后在 中._createMenuBar(),使用 的前两个变体向菜单栏添加三个菜单.addMenu()。第三个变体需要一个图标对象,但您还没有学会如何创建和使用图标。您将在使用 PyQt 中的图标和资源部分中了解如何使用图标。

    如果您运行示例应用程序,那么您将看到您现在有一个如下所示的菜单栏:

    PyQt 菜单栏

    应用程序的菜单栏有菜单FileEditHelp。当您单击这些菜单时,它们不会显示菜单选项的下拉列表。那是因为您还没有添加菜单选项。您将在使用操作填充菜单部分中了解如何向菜单添加菜单选项。

    最后,请注意&包含在每个菜单标题中的与符号 ( ) 会在菜单栏显示中创建带下划线的字母。这在定义菜单和工具栏选项的键盘快捷键一节中有更详细的讨论。

    创建工具栏

    甲工具栏是保存按钮和其他部件,以提供到GUI应用的最普通的选项快速访问的可移动面板。工具栏按钮可以显示图标、文本或两者来表示它们执行的任务。PyQt 中工具栏的基类是QToolBar. 此类将允许您为 GUI 应用程序创建自定义工具栏。

    当您向主窗口样式应用程序添加工具栏时,默认位置在窗口顶部。但是,您可以在以下四个工具栏区域之一中放置工具栏:

    工具栏区域在 PyQt 中被定义为常量。如果您需要使用它们,那么您必须导入QtfromPyQt5.QtCore然后像 in 一样使用完全限定名称Qt.LeftToolBarArea。

    在 PyQt 中,可以通过三种方法向主窗口应用程序添加工具栏:

    1. QMainWindow.addToolBar(title)创建一个新的空QToolBar对象并将其窗口标题设置为title. 此方法将工具栏插入顶部工具栏区域并返回新创建的工具栏。
    2. QMainWindow.addToolBar(toolbar)将QToolBar对象 ( toolbar) 插入顶部工具栏区域。
    3. QMainWindow.addToolBar(area, toolbar)将QToolBar对象 ( toolbar) 插入指定的工具栏区域 ( area)。如果主窗口已有工具栏,则toolbar放置在最后一个现有工具栏之后。如果toolbar已经存在于主窗口中,那么它只会被移动到area.

    如果您使用最后两个选项之一,那么您需要自己创建工具栏。为此,您可以使用以下构造函数之一:

    1. QToolBar(parent)
    2. QToolBar(title, parent)

    在这两种情况下,parent代表QWidget将拥有工具栏所有权的对象。您通常会将工具栏所有权设置为将在其中使用工具栏的窗口。在第二个构造函数中,title将是一个带有工具栏窗口标题的字符串。PyQt 使用这个窗口标题来构建一个默认的上下文菜单,允许你隐藏和显示你的工具栏。

    现在您可以返回到您的示例应用程序并将以下方法添加到Window:

    from PyQt5.QtWidgets import QToolBar
    # Snip...
    
    class Window(QMainWindow):
        # Snip...
        def _createToolBars(self):
            # Using a title
            fileToolBar = self.addToolBar("File")
            # Using a QToolBar object
            editToolBar = QToolBar("Edit", self)
            self.addToolBar(editToolBar)
            # Using a QToolBar object and a toolbar area
            helpToolBar = QToolBar("Help", self)
            self.addToolBar(Qt.LeftToolBarArea, helpToolBar)

    首先,您QToolBar从PyQt5.QtWidgets. 然后,在 中._createToolBars(),您首先使用标题创建文件工具栏.addToolBar()。接下来,您创建一个QToolBar带有标题的对象,"Edit"并使用.addToolBar()不传递工具栏区域将其添加到工具栏。在这种情况下,编辑工具栏位于顶部工具栏区域。最后,创建帮助工具栏并使用 将其放置在左侧工具栏区域Qt.LeftToolBarArea。

    完成这项工作的最后一步是._createToolBars()从 的初始化程序调用Window:

    class Window(QMainWindow):
        """Main Window."""
        def __init__(self, parent=None):
            # Snip...
            self._createToolBars()

    ._createToolBars()对初始化器内部的调用Window将创建三个工具栏并将它们添加到您的主窗口中。以下是您的应用程序现在的外观:

    PyQt 工具栏

    现在,菜单栏正下方有两个工具栏,窗口左侧有一个工具栏。每个工具栏都有一条双虚线。当您将鼠标移到虚线上时,指针会变成一只手。如果单击并按住虚线,则可以将工具栏移动到窗口上的任何其他位置或工具栏区域。

    如果右键单击工具栏,PyQt 将显示一个上下文菜单,允许您根据需要隐藏和显示现有工具栏。

    到目前为止,您的应用程序窗口中有三个工具栏。这些工具栏仍然是空的——您需要添加一些工具栏按钮才能使它们起作用。为此,您可以使用 PyQt actions,它们是QAction. 您将在后面的部分中学习如何在 PyQt 中创建操作。现在,您将学习如何在 PyQt 应用程序中使用图标和其他资源。

    在 PyQt 中使用图标和资源

    在Qt库包括Qt的资源系统,这是增加的二进制文件,如图标,图像,翻译文件和其他资源对应用程序的一种便捷方式。

    要使用资源系统,您需要在资源集合文件或.qrc文件中列出您的资源。一个.qrc文件是一个XML包含位置,或文件路径,文件系统中的每个资源的。

    假设您的示例应用程序有一个resources目录,其中包含您要在应用程序的 GUI 中使用的图标。您有NewOpen等选项的图标。您可以创建一个.qrc包含每个图标路径的文件:

    <!DOCTYPE RCC><RCC version="1.0">
    <qresource>
        <file alias="file-new.svg">resources/file-new.svg</file>
        <file alias="file-open.svg">resources/file-open.svg</file>
        <file alias="file-save.svg">resources/file-save.svg</file>
        <file alias="file-exit.svg">resources/file-exit.svg</file>
        <file alias="edit-copy.svg">resources/edit-copy.svg</file>
        <file alias="edit-cut.svg">resources/edit-cut.svg</file>
        <file alias="edit-paste.svg">resources/edit-paste.svg</file>
        <file alias="help-content.svg">resources/help-content.svg</file>
    </qresource>
    </RCC>

    每个<file>条目必须包含文件系统中资源的路径。指定的路径相对于包含.qrc文件的目录。在上面的例子中,resources目录需要和.qrc文件在同一个目录下。

    alias 是一个可选属性,它定义了一个简短的替代名称,您可以在代码中使用它来访问每个资源。

    一旦您拥有应用程序的资源,您就可以运行pyrcc5针对您的.qrc文件的命令行工具。pyrcc5随 PyQt 一起提供,并且必须在安装 PyQt 后在您的Python 环境中完全正常运行。

    pyrcc5读取一个.qrc文件并生成一个 Python 模块,其中包含所有资源的二进制代码:

    $ pyrcc5 -o qrc_resources.py resources.qrc

    此命令将读取resources.qrc并生成qrc_resources.py包含每个资源的二进制代码。您将能够通过导入在 Python 代码中使用这些资源qrc_resources。

    注意:如果运行时出现问题pyrcc5,请确保您使用的是正确的 Python 环境。如果您在 Python 虚拟环境中安装 PyQt,那么您将无法pyrcc5在该环境之外使用。

    这qrc_resources.py是对应于您的代码片段resources.qrc:

    # -*- coding: utf-8 -*-
    
    # Resource object code
    #
    # Created by: The Resource Compiler for PyQt5 (Qt v5.9.5)
    #
    # WARNING! All changes made in this file will be lost!
    
    from PyQt5 import QtCore
    
    qt_resource_data = b"\
    \x00\x00\x03\xb1\
     \
    \x73\x76\x67\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\
    ...

    随着qrc_resources.py在地方,你可以将其导入到你的应用程序,并通过键入一个冒号(请参阅各资源:),然后无论是它的alias或它的路径。例如,要使用file-new.svg其别名进行访问,您可以使用访问字符串 ":file-new.svg"。如果您没有alias,则可以通过带有访问字符串的路径访问它":resources/file-new.svg"。

    如果您有别名,但由于某种原因您想通过其路径访问给定资源,那么您可能必须从访问字符串中删除冒号才能使其正常工作。

    要在您的操作中使用图标,您首先需要导入您的资源模块:

    import qrc_resources

    导入包含资源的模块后,您可以在应用程序的 GUI 中使用这些资源。

    注意:  Linters、编辑器和 IDE可能会将上述 import 语句标记为未使用,因为您的代码不会包含对它的任何显式使用。某些 IDE 可能会更进一步并自动删除该行。

    在这些情况下,您必须覆盖您的 linter、编辑器或 IDE 的建议,并将该导入保留在您的代码中。否则,您的应用程序将无法显示您的资源。

    要使用资源系统创建图标,您需要实例化QIcon,将别名或路径传递给类构造函数:

    newIcon = QIcon(":file-new.svg")

    在此示例中,您将QIcon使用文件创建一个对象,该对象file-new.svg位于您的资源模块中。这提供了一种在整个 GUI 应用程序中使用图标和资源的便捷方式。

    现在返回到您的示例应用程序并更新最后一行._createMenuBar():

    from PyQt5.QtGui import QIcon
    
    import qrc_resources
    # Snip...
    
    class Window(QMainWindow):
        # Snip...
        def _createMenuBar(self):
            menuBar = self.menuBar()
            # Using a QMenu object
            fileMenu = QMenu("&File", self)
            menuBar.addMenu(fileMenu)
            # Using a title
            editMenu = menuBar.addMenu("&Edit")
            # Using an icon and a title
            helpMenu = menuBar.addMenu(QIcon(":help-content.svg"), "&Help")

    要使此代码正常工作,您首先需要QIcon从PyQt5.QtGui. 您还需要导入qrc_resources. 在最后突出显示的行中,您从资源模块中添加了一个helpMenu使用图标help-content.svg。

    如果您使用此更新运行示例应用程序,您将获得以下输出:

    带有图标的 PyQt 菜单栏

    应用程序的主窗口现在在其帮助菜单上显示一个图标。当您单击该图标时,菜单会显示文本Help。在菜单栏中使用图标并不常见,但 PyQt 允许您这样做。

    在 PyQt 中为 Python 菜单和工具栏创建操作

    PyQt动作是表示应用程序中给定命令、操作或动作的对象。当您需要为不同的 GUI 组件(例如菜单选项、工具栏按钮和键盘快捷键)提供相同的功能时,它们非常有用。

    您可以通过实例化QAction. 创建操作后,您需要将其添加到小部件中才能在实践中使用它。

    您还需要将您的操作与某些功能联系起来。换句话说,您需要将它们连接到触发操作时要运行的函数或方法。这将允许您的应用程序执行操作以响应 GUI 中的用户操作。

    行动是相当多才多艺的。它们允许您跨菜单选项、工具栏按钮和键盘快捷键重复使用并保持同步相同的功能。这在整个应用程序中提供了一致的行为。

    例如,当用户单击打开...菜单选项、单击打开工具栏按钮或按键盘上的Ctrl+O时,他们可能希望应用程序执行相同的操作。

    QAction 提供了一个抽象,允许您跟踪以下元素:

    • 菜单选项上的文字
    • 工具栏按钮上的文本
    • 工具栏选项上的帮助提示(工具提示
    • 这是什么帮助提示
    • 状态栏上的帮助提示(状态提示
    • 与选项关联的键盘快捷键
    • 与菜单和工具栏选项相关联的图标
    • 动作enabled或disabled状态
    • 动作on或off状态

    要创建操作,您需要实例化QAction. 至少有三种通用方法可以做到这一点:

    1. QAction(parent)
    2. QAction(text, parent)
    3. QAction(icon, text, parent)

    在所有三种情况下,都parent表示拥有操作所有权的对象。此参数可以是任何QObject. 最佳实践是将操作创建为您将在其中使用它们的窗口的子项。

    在第二个和第三个构造函数中,text保存操作将在菜单选项或工具栏按钮上显示的文本。

    操作文本在菜单选项和工具栏按钮上的显示方式不同。例如,文本&Open...显示为打开...菜单中的选项,如打开的工具栏按钮。

    在第三个构造函数中,icon是一个QIcon保存动作图标的对象。此图标将显示在菜单选项中文本的左侧。图标在工具栏按钮中的位置取决于工具栏的.toolButtonStyle属性,可以采用以下值之一:

    您还可以设置该操作的文本和图标通过各自的setter方法,.setText()和.setIcon()。

    注意:有关QAction属性的完整列表,您可以查看文档。

    以下是如何使用 的不同构造函数为示例应用程序创建一些操作QAction:

    from PyQt5.QtWidgets import QAction
    # Snip...
    
    class Window(QMainWindow):
        # Snip...
        def _createActions(self):
            # Creating action using the first constructor
            self.newAction = QAction(self)
            self.newAction.setText("&New")
            # Creating actions using the second constructor
            self.openAction = QAction("&Open...", self)
            self.saveAction = QAction("&Save", self)
            self.exitAction = QAction("&Exit", self)
            self.copyAction = QAction("&Copy", self)
            self.pasteAction = QAction("&Paste", self)
            self.cutAction = QAction("C&ut", self)
            self.helpContentAction = QAction("&Help Content", self)
            self.aboutAction = QAction("&About", self)

    在 中._createActions(),您为示例应用程序创建了一些操作。这些操作将允许您向应用程序的菜单和工具栏添加选项。

    请注意,您将操作创建为实例属性,因此您可以._createActions()使用self. 这样,您就可以在菜单和工具栏上使用这些操作。

    注意:在 中._createActions(),您不使用的第三个构造函数,QAction因为如果您还看不到操作,则使用图标是没有意义的。您将在使用操作填充工具栏部分中了解如何向操作添加图标。

    下一步是调用._createActions()form 的初始化程序Window:

    class Window(QMainWindow):
        """Main Window."""
        def __init__(self, parent=None):
            # Snip...
            self._createActions()
            self._createMenuBar()
            self._createToolBars()

    如果您现在运行该应用程序,那么您将不会在 GUI 上看到任何更改。这是因为在将操作添加到菜单或工具栏之前不会显示它们。请注意,您在调用._createActions()之前先调用._createMenuBar(),._createToolBars()因为您将在菜单和工具栏上使用这些操作。

    如果您向菜单添加操作,则该操作将成为菜单选项。如果向工具栏添加操作,则该操作将成为工具栏按钮。这就是接下来几节的主题。

    在 PyQt 中为 Python 菜单添加选项

    如果要向 PyQt 中的给定菜单添加选项列表,则需要使用操作。到目前为止,您已经学习了如何使用QAction. 在 PyQt 中创建菜单时,操作是一个关键组件。

    在本节中,您将学习如何使用操作来填充带有菜单选项的菜单。

    用动作填充菜单

    要使用菜单选项填充菜单,您将使用操作。在菜单中,操作表示为一个水平选项,其中至少有一个描述性文本,如NewOpenSave等。菜单选项还可以在其左侧显示一个图标,并在其右侧显示快捷键序列,例如Ctrl+S。

    您可以QMenu使用向对象添加操作.addAction()。此方法有多种变体。他们中的大多数被认为是即时创建操作。在本教程中,但是,你要使用的变化.addAction()是QMenu从继承QWidget。这是此变体的签名:

    QWidget.addAction(action)

    参数action表示QAction要添加到给定QWidget对象的对象。使用 的这种变体.addAction(),您可以预先创建您的操作,然后根据需要将它们添加到您的菜单中。

    注意:  QWidget还提供.addActions(). 此方法采用一系列操作并将它们附加到当前小部件对象。

    使用此工具,您可以开始向示例应用程序的菜单添加操作。为此,您需要更新._createMenuBar():

    class Window(QMainWindow):
        # Snip...
        def _createMenuBar(self):
            menuBar = self.menuBar()
            # File menu
            fileMenu = QMenu("&File", self)
            menuBar.addMenu(fileMenu)
            fileMenu.addAction(self.newAction)
            fileMenu.addAction(self.openAction)
            fileMenu.addAction(self.saveAction)
            fileMenu.addAction(self.exitAction)
            # Edit menu
            editMenu = menuBar.addMenu("&Edit")
            editMenu.addAction(self.copyAction)
            editMenu.addAction(self.pasteAction)
            editMenu.addAction(self.cutAction)
            # Help menu
            helpMenu = menuBar.addMenu(QIcon(":help-content.svg"), "&Help")
            helpMenu.addAction(self.helpContentAction)
            helpMenu.addAction(self.aboutAction)

    通过对 的更新._createMenuBar(),您可以向示例应用程序的三个菜单添加许多选项。

    现在文件菜单有四个选项:

    1. 新建用于创建新文件
    2. Open...用于打开现有文件
    3. Save用于保存对文件所做的更改
    4. 退出以关闭应用程序

    编辑菜单中有三个选项:

    1. 内容复制到系统剪贴板
    2. Paste用于从系统剪贴板粘贴内容
    3. Cut用于将内容剪切到系统剪贴板

    帮助菜单中有两个选项:

    1. 用于启动应用程序帮助手册的帮助内容
    2. 关于用于显示关于对话框

    选项在菜单中从上到下显示的顺序对应于您在代码中添加选项的顺序。

    如果您运行该应用程序,您将在屏幕上看到以下窗口:

    带选项的 PyQt 菜单

    如果您单击某个菜单,则该应用程序会显示一个包含您之前看到的选项的下拉列表。

    创建 Python 子菜单

    有时您需要在 GUI 应用程序中使用子菜单。子菜单是一个嵌套的菜单,当您将光标移到给定的菜单选项上时会显示该菜单。要将子菜单添加到应用程序,您需要调用.addMenu()容器菜单对象。

    假设您需要在示例应用程序的Edit菜单中添加一个子菜单。您的子菜单将包含用于查找和替换内容的选项,因此您将其称为Find and Replace。该子菜单将有两个选项:

    1. 查找...以查找一些内容
    2. 替换...用于查找旧内容并将其替换为新内容

    以下是将此子菜单添加到示例应用程序的方法:

    class Window(QMainWindow):
        # Snip...
        def _createMenuBar(self):
            # Snip...
            editMenu.addAction(self.cutAction)
            # Find and Replace submenu in the Edit menu
            findMenu = editMenu.addMenu("Find and Replace")
            findMenu.addAction("Find...")
            findMenu.addAction("Replace...")
            # Snip...

    在突出显示的第一行中,您使用on将QMenu带有文本的对象添加"Find and Replace"到“编辑”菜单。下一步是使用您迄今为止所做的操作填充子菜单。如果您再次运行示例应用程序,您将在Edit菜单下看到一个新的菜单选项:.addMenu()editMenu

    PyQt 子菜单

    编辑菜单现在有一个新的条目称为查找和替换。当您将鼠标悬停在这个新菜单选项上时,会出现一个子菜单,为您提供两个新选项,Find...Replace...。就是这样!您已经创建了一个子菜单。

    在 PyQt 中向工具栏添加选项

    在使用 Python 和 PyQt 构建 GUI 应用程序时,工具栏是一个非常有用的组件。您可以使用工具栏向您的用户提供一种快速访问应用程序中最常用选项的方法。您还可以向工具栏添加诸如旋转框和组合框之类的小部件,以允许用户直接从应用程序的 GUI 修改某些属性和变量。

    在以下几节中,您将学习如何使用操作向工具栏添加选项或按钮,以及如何使用.addWidget().

    用动作填充工具栏

    要将选项或按钮添加到工具栏,您需要调用.addAction()。在本节中,你会依靠的变化.addAction()是QToolBar从继承QWidget。因此,您将.addAction()使用动作作为参数进行调用。这将允许您在菜单和工具栏之间共享您的操作。

    创建工具栏时,您通常会面临决定向其中添加哪些选项的问题。通常,您只想将最常用的操作添加到工具栏。

    如果返回到示例应用程序,您会记得您添加了三个工具栏:

    1. File
    2. Edit
    3. Help

    文件工具栏中,您可以添加如下选项:

    • New
    • Open
    • Save

    编辑工具栏中,您可以添加以下选项:

    • Copy
    • Paste
    • Cut

    通常,当您要向工具栏添加按钮时,首先要选择要在每个按钮上使用的图标。这不是强制性的,但它是最佳实践。选择图标后,您需要将它们添加到相应的操作中。

    以下是向示例应用程序的操作添加图标的方法:

    class Window(QMainWindow):
        # Snip...
        def _createActions(self):
            # File actions
            self.newAction = QAction(self)
            self.newAction.setText("&New")
            self.newAction.setIcon(QIcon(":file-new.svg"))
            self.openAction = QAction(QIcon(":file-open.svg"), "&Open...", self)
            self.saveAction = QAction(QIcon(":file-save.svg"), "&Save", self)
            self.exitAction = QAction("&Exit", self)
            # Edit actions
            self.copyAction = QAction(QIcon(":edit-copy.svg"), "&Copy", self)
            self.pasteAction = QAction(QIcon(":edit-paste.svg"), "&Paste", self)
            self.cutAction = QAction(QIcon(":edit-cut.svg"), "C&ut", self)
            # Snip...

    要将图标添加到您的操作,请更新突出显示的行。在 的情况下newAction,您使用.setIcon(). 在其余的操作中,您使用带有icon、 atitle和parent对象作为参数的构造函数。

    一旦您选择的操作具有图标,您可以通过调用.addAction()工具栏对象将这些操作添加到相应的工具栏:

    class Window(QMainWindow):
        # Snip...
        def _createToolBars(self):
            # File toolbar
            fileToolBar = self.addToolBar("File")
            fileToolBar.addAction(self.newAction)
            fileToolBar.addAction(self.openAction)
            fileToolBar.addAction(self.saveAction)
            # Edit toolbar
            editToolBar = QToolBar("Edit", self)
            self.addToolBar(editToolBar)
            editToolBar.addAction(self.copyAction)
            editToolBar.addAction(self.pasteAction)
            editToolBar.addAction(self.cutAction)

    通过此更新._createToolBars(),您可以将新建打开保存选项的按钮添加到文件工具栏。您还可以将CopyPasteCut选项的按钮添加到“编辑”工具栏。

    注意:按钮在工具栏上从左到右显示的顺序对应于您在代码中添加按钮的顺序。

    如果您现在运行示例应用程序,您将在屏幕上看到以下窗口:

    带有按钮的 PyQt 工具栏

    示例应用程序现在显示两个工具栏,每个工具栏都有几个按钮。您的用户可以单击这些按钮以快速访问应用程序最常用的选项。

    注意:当您第一次._createToolBars()在创建工具栏部分回信时,您创建了一个帮助工具栏。此工具栏旨在展示如何使用不同的.addToolBar().

    在 的上述更新中._createToolBars(),您去掉了帮助工具栏,只是为了使示例简短明了。

    请注意,由于您在菜单和工具栏之间共享相同的操作,因此菜单选项也会在其左侧显示图标,这在生产力和资源使用方面是一个巨大的胜利。这是使用 PyQt 操作通过 Python 创建菜单和工具栏的优势之一。

    向工具栏添加小部件

    在某些情况下,您会发现将特定小部件(如旋转框、组合框或其他)添加到工具栏很有用。一个常见的例子是大多数文字处理器使用的组合框,允许用户更改文档的字体或所选文本的大小。

    要将小部件添加到工具栏,您首先需要创建小部件,设置其属性,然后调用.addWidget()工具栏对象,将小部件作为参数传递。

    假设您想向示例应用程序QSpinBox的“编辑”工具栏添加一个对象,以允许用户更改某些内容的大小,可能是字体大小。您需要更新._createToolBars():

    from PyQt5.QtWidgets import QSpinBox
    # Snip...
    
    class Window(QMainWindow):
        # Snip...
        def _createToolBars(self):
            # Snip...
            # Adding a widget to the Edit toolbar
            self.fontSizeSpinBox = QSpinBox()
            self.fontSizeSpinBox.setFocusPolicy(Qt.NoFocus)
            editToolBar.addWidget(self.fontSizeSpinBox)

    在这里,您首先导入旋转框类。然后您创建一个QSpinBox对象,将其设置focusPolicy为Qt.NoFocus,最后将其添加到您的编辑工具栏。

    注意:在上面的代码中,您将focusPolicy旋转框的属性设置为,Qt.NoFocus因为如果此小部件获得焦点,则应用程序的键盘快捷键将无法正常工作。

    现在,如果您运行该应用程序,那么您将获得以下输出:

    带有小部件的 PyQt 工具栏

    此处,“编辑”工具栏显示了一个QSpinBox对象,您的用户可以使用该对象来设置应用程序上的字体大小或任何其他数字属性。

    自定义工具栏

    PyQt 工具栏非常灵活且可定制。您可以在工具栏对象上设置一堆属性。下表显示了一些最有用的属性:

    所有这些属性都有一个关联的 setter 方法。例如,您可以使用.setAllowedAreas()to set allowedAreas、.setFloatable()to setfloatable等。

    现在,假设您不希望用户在窗口周围移动文件工具栏。在这种情况下,您可以设置movable为False使用.setMovable():

    class Window(QMainWindow):
        # Snip...
        def _createToolBars(self):
            # File toolbar
            fileToolBar = self.addToolBar("File")
            fileToolBar.setMovable(False)
            # Snip...

    突出显示的线使这里变得神奇。现在您的用户无法在应用程序窗口周围移动工具栏:

    PyQt 工具栏自定义

    文件的工具栏不显示双虚线了,所以你的用户将无法将其移动。请注意,编辑工具栏仍然是可移动的。您可以使用相同的方法更改工具栏上的其他属性,并根据您的需要自定义它们。

    组织菜单和工具栏选项

    为了在 GUI 应用程序中增加清晰度并改善用户体验,您可以使用分隔符来组织菜单选项和工具栏按钮。分隔符呈现为分隔或分隔菜单选项的水平线或分隔工具栏按钮的垂直线。

    要在菜单、子菜单或工具栏对象中插入或添加分隔符,您可以调用.addSeparator()这些对象中的任何一个。

    例如,您可以使用分隔符将“文件”菜单上的“退出”选项与其余选项分开,以明确“退出”与菜单上​​的其余选项在逻辑上无关。您还可以使用分隔符将“编辑”菜单上的“查找和替换”选项与遵循相同规则的其余选项分开。

    转到您的示例应用程序并._createMenuBar()按照以下代码进行更新:

    class Window(QMainWindow):
        # Snip...
        def _createMenuBar(self):
            # File menu
            # Snip...
            fileMenu.addAction(self.saveAction)
            # Adding a separator
            fileMenu.addSeparator()
            fileMenu.addAction(self.exitAction)
            # Edit menu
            # Snip...
            editMenu.addAction(self.cutAction)
            # Adding a separator
            editMenu.addSeparator()
            # Find and Replace submenu in the Edit menu
            findMenu = editMenu.addMenu("Find and Replace")
            # Snip...

    在突出显示的第一行中,在“文件”菜单中的“保存”和“退出”选项之间添加一个分隔符。在第二个突出显示的行中,添加一个分隔符,将“查找和替换”选项与“编辑”菜单中的其余选项分开。以下是这些添加的工作原理:

    带分隔符的 PyQt 菜单

    您的“文件”菜单现在显示一条水平线,将“编辑”选项与菜单中的其余选项分开。在编辑菜单中还显示,在选项的下拉列表中的最后一个分隔符。分隔符的连贯使用可以巧妙地提高菜单和工具栏的清晰度,使您的 GUI 应用程序更加用户友好。

    作为练习,您可以转到 的定义._createToolBars()并添加一个分隔符,将QSpinBox对象与工具栏上的其余选项分开。

    在 PyQt 中构建上下文或弹出菜单

    上下文菜单,也称为弹出菜单,是一种特殊类型的菜单,它会响应某些用户操作(例如右键单击给定的小部件或窗口)而出现。这些菜单提供了一小部分选项,这些选项在您使用的操作系统或应用程序的给定上下文中可用。

    例如,如果您右键单击 Windows 计算机的桌面,您将获得一个菜单,其中包含与操作系统的特定上下文或空间相对应的选项。如果您右键单击文本编辑器的工作区,您将获得一个完全不同的上下文菜单,具体取决于您使用的编辑器。

    在 PyQt 中,您有多种创建上下文菜单的选项。在本教程中,您将了解其中两个选项:

    1. 将contextMenuPolicy特定小部件的属性设置为Qt.ActionsContextMenu
    2. 通过处理应用程序窗口上的上下文菜单事件contextMenuEvent()

    第一个选项是两者中最常见和用户友好的,因此您将首先了解它。

    第二个选项稍微复杂一些,并且依赖于处理用户事件。在 GUI 编程中,事件是应用程序上的任何用户操作,例如单击按钮或菜单、从组合框中选择项目、在文本字段中输入或更新文本、按下键盘上的键等.

    通过上下文菜单策略创建上下文菜单

    所有派生自的 PyQt 图形组件或小部件都QWidget继承了一个名为contextMenuPolicy. 此属性控制小部件如何显示上下文菜单。此属性最常用的值之一是Qt.ActionsContextMenu。这使得小部件将其内部操作列表显示为上下文菜单。

    要使小部件根据其内部操作显示上下文菜单,您需要运行两个步骤:

    1. 使用 向小部件添加一些操作QWidget.addAction()。
    2. 设置contextMenuPolicy于Qt.ActionsContextMenu上使用的小工具.setContextMenuPolicy()。

    设置contextMenuPolicy为Qt.ActionsContextMenu使具有操作的小部件在上下文菜单中显示它们。这是使用 Python 和 PyQt 创建上下文菜单的一种非常快速的方法。

    使用这种技术,您可以向示例应用程序的中央小部件添加上下文菜单,并为您的用户提供一种快速访问某些应用程序选项的方法。为此,您可以将以下方法添加到Window:

    class Window(QMainWindow):
        # Snip...
        def _createContextMenu(self):
            # Setting contextMenuPolicy
            self.centralWidget.setContextMenuPolicy(Qt.ActionsContextMenu)
            # Populating the widget with actions
            self.centralWidget.addAction(self.newAction)
            self.centralWidget.addAction(self.openAction)
            self.centralWidget.addAction(self.saveAction)
            self.centralWidget.addAction(self.copyAction)
            self.centralWidget.addAction(self.pasteAction)
            self.centralWidget.addAction(self.cutAction)

    在 中._createContextMenu(),您首先设置contextMenuPolicy为Qt.ActionsContextMenu使用 setter 方法.setContextMenuPolicy()。然后.addAction()像往常一样向小部件添加操作。最后一步是._createContextMenu()从 的初始化程序调用Window:

    class Window(QMainWindow):
        """Main Window."""
        def __init__(self, parent=None):
            # Snip...
            self._createToolBars()
            self._createContextMenu()

    如果您在添加这些内容后运行示例应用程序,那么当您右键单击该应用程序的中央小部件时,您会看到它显示一个上下文菜单:

    PyQt 上下文菜单策略

    现在,您的示例应用程序有一个上下文菜单,只要您右键单击应用程序的中央小部件,就会弹出该菜单。中央小部件伸展以占据窗口中的所有可用空间,因此您不仅限于右键单击标签文本以查看上下文菜单。

    最后,由于您在整个应用程序中使用相同的操作,上下文菜单上的选项显示相同的图标集。

    通过事件处理创建上下文菜单

    在 PyQt 中创建上下文菜单的另一种方法是处理应用程序主窗口的上下文菜单事件。为此,您需要运行以下步骤:

    1. 覆盖对象.contextMenuEvent()上的事件处理程序方法QMainWindow。
    2. 创建一个QMenu传递小部件(上下文小部件)作为其父对象的对象。
    3. 用动作填充菜单对象。
    4. 使用QMenu.exec()事件.globalPos()作为参数启动菜单对象。

    这种管理上下文菜单的方式有点复杂。但是,它使您可以很好地控制调用上下文菜单时发生的情况。例如,您可以根据应用程序的状态等启用或禁用菜单选项。

    注意:在继续本节之前,您需要禁用您在上一节中编写的代码。为此,只需转到的初始化程序Window并注释掉调用self._createContextMenu().

    以下是如何重新实现示例应用程序的上下文菜单,覆盖主窗口对象上的事件处理程序方法:

    class Window(QMainWindow):
        # Snip...
        def contextMenuEvent(self, event):
            # Creating a menu object with the central widget as parent
            menu = QMenu(self.centralWidget)
            # Populating the menu with actions
            menu.addAction(self.newAction)
            menu.addAction(self.openAction)
            menu.addAction(self.saveAction)
            menu.addAction(self.copyAction)
            menu.addAction(self.pasteAction)
            menu.addAction(self.cutAction)
            # Launching the menu
            menu.exec(event.globalPos())

    在 中contextMenuEvent(),您首先创建一个QMenu对象 ( menu)centralWidget作为其父小部件。接下来,您使用.addAction. 最后,调用.exec()上QMenu的对象,以显示在屏幕上。

    的第二个参数.contextMenuEvent()表示该方法捕获的事件。在这种情况下,event将右键单击应用程序的中央小部件。

    在对 的调用中.exec(),您将其event.globalPos()用作参数。当用户单击 PyQt 窗口或小部件时,此方法返回鼠标指针的全局位置。鼠标位置将告诉.exec()窗口上显示上下文菜单的位置。

    如果您使用这些新更改运行示例应用程序,那么您将获得与上一节中相同的结果。

    组织上下文菜单选项

    与菜单和工具栏不同,在上下文菜单中,您不能使用.addSeparator()添加分隔符并根据它们之间的关系在视觉上分隔菜单选项。在组织上下文菜单时,您需要创建一个分隔符操作:

    separator = QAction(parent)
    separator.setSeparator(True)

    .setSeparator(True)对动作对象的调用将把该动作变成一个分隔符。完成分隔符操作后,您需要使用 将其插入上下文菜单中的正确位置QMenu.addAction()。

    如果您回顾一下您的示例应用程序,那么您可能希望在视觉上将来自File菜单的选项与来自Edit菜单的选项分开。为此,您可以更新.contextMenuEvent():

    class Window(QMainWindow):
        # Snip...
        def contextMenuEvent(self, event):
            # Snip...
            menu.addAction(self.saveAction)
            # Creating a separator action
            separator = QAction(self)
            separator.setSeparator(True)
            # Adding the separator to the menu
            menu.addAction(separator)
            menu.addAction(self.copyAction)
            # Snip...

    在前两行突出显示的行中,您创建了分隔符操作。在第三个突出显示的行中,您使用 将分隔符操作添加到菜单中.addAction()。

    这将在文件选项和编辑选项之间添加一条水平线。以下是添加此内容的上下文菜单的外观:

    带分隔符的 PyQt 上下文菜单

    现在,您的上下文菜单包含一条水平线,可直观地将来自File的选项与来自Edit的选项分开。这样,您改进了菜单的视觉质量并提供了更好的用户体验。

    在菜单和工具栏中连接信号和插槽

    在 PyQt 中,您使用信号和槽为 GUI 应用程序提供功能。每次在PyQt 小部件上发生诸如鼠标单击、按键或窗口大小调整等事件时,它们都会发出信号。

    一个插槽是一个Python可调用,您可以连接到一个小部件的信号,以响应用户事件执行某些操作。如果连接了一个信号和一个插槽,那么每次发出信号时都会自动调用该插槽。如果给定的信号未连接到插槽,则在发出信号时不会发生任何事情。

    为了让你的菜单选项和工具栏按钮在用户点击它们时启动一些操作,你需要将底层操作的信号与一些自定义或内置插槽连接起来。

    QAction物体可以发出各种信号。但是,菜单和工具栏中最常用的信号是.triggered()。每次用户单击菜单选项或工具栏按钮时都会发出此信号。要.triggered()与插槽连接,您可以使用以下语法:

    action = QAction("Action Text", parent)
    # Connect action's triggered() with a slot
    action.triggered.connect(slot)

    在这个例子中,slot是一个 Python 可调用的。换句话说,slot可以是一个函数、一个方法、一个类或一个实现 的类的实例.__call__()。

    您的示例应用程序中已经有一组操作。现在,您需要对每次用户单击菜单选项或工具栏按钮时调用的插槽进行编码。转到的定义Window并添加以下方法:

    class Window(QMainWindow):
        # Snip...
        def newFile(self):
            # Logic for creating a new file goes here...
            self.centralWidget.setText("<b>File > New</b> clicked")
    
        def openFile(self):
            # Logic for opening an existing file goes here...
            self.centralWidget.setText("<b>File > Open...</b> clicked")
    
        def saveFile(self):
            # Logic for saving a file goes here...
            self.centralWidget.setText("<b>File > Save</b> clicked")
    
        def copyContent(self):
            # Logic for copying content goes here...
            self.centralWidget.setText("<b>Edit > Copy</b> clicked")
    
        def pasteContent(self):
            # Logic for pasting content goes here...
            self.centralWidget.setText("<b>Edit > Paste</b> clicked")
    
        def cutContent(self):
            # Logic for cutting content goes here...
            self.centralWidget.setText("<b>Edit > Cut</b> clicked")
    
        def helpContent(self):
            # Logic for launching help goes here...
            self.centralWidget.setText("<b>Help > Help Content...</b> clicked")
    
        def about(self):
            # Logic for showing an about dialog content goes here...
            self.centralWidget.setText("<b>Help > About...</b> clicked")

    这些方法将扮演示例应用程序的插槽的角色。每次用户单击相应的菜单选项或工具栏按钮时都会调用它们。

    一旦有了提供功能的插槽,就需要将它们与动作的.triggered()信号连接起来。这样,应用程序将根据用户事件执行操作。要进行这些连接,请转到示例应用程序并将以下方法添加到Window:

    class Window(QMainWindow):
        # Snip...
        def _connectActions(self):
            # Connect File actions
            self.newAction.triggered.connect(self.newFile)
            self.openAction.triggered.connect(self.openFile)
            self.saveAction.triggered.connect(self.saveFile)
            self.exitAction.triggered.connect(self.close)
            # Connect Edit actions
            self.copyAction.triggered.connect(self.copyContent)
            self.pasteAction.triggered.connect(self.pasteContent)
            self.cutAction.triggered.connect(self.cutContent)
            # Connect Help actions
            self.helpContentAction.triggered.connect(self.helpContent)
            self.aboutAction.triggered.connect(self.about)

    此方法会将您所有操作的.triggered()信号与其各自的插槽或回调连接起来。通过此更新,您的示例应用程序将在QLabel您设置为中央小部件的对象上显示一条消息,告诉您单击了哪个菜单选项或工具栏按钮。

    在 的情况下exitAction,您将其triggered()信号与内置插槽连接QMainWindow.close()。这样,如果您选择File → Exit,那么您的应用程序将关闭。

    最后,转到 的初始化程序Window并添加对 的调用._connectActions():

    class Window(QMainWindow):
        """Main Window."""
        def __init__(self, parent=None):
            # Snip...
            # self._createContextMenu()
            self._connectActions()

    通过此最终更新,您可以再次运行该应用程序。以下是所有这些更改的工作原理:

    PyQt 连接信号和插槽

    如果单击菜单选项、工具栏按钮或上下文菜单选项,则应用程序窗口中央的标签会显示一条消息,指示已执行的操作。此功能在学习环境之外不是很有用,但它可以让您了解如何在用户与 GUI 交互时让您的应用程序执行现实世界的操作。

    最后,当您选择File → Exit 时,应用程序将关闭,因为 的.triggered()信号exitAction已连接到内置插槽QMainWindow.close()。

    作为练习,您可以尝试为查找和替换子菜单中的查找...替换...选项创建自定义插槽,然后将它们的信号连接到这些插槽以使其生效。您还可以尝试使用您在本节中编写的插槽并尝试用它们做新的事情。.triggered()

    动态填充 Python 菜单

    为应用程序创建菜单时,有时需要使用创建应用程序 GUI 时未知的选项填充这些菜单。例如,文本编辑器中的“打开最近”菜单显示最近打开的文档列表。您无法在创建应用程序的 GUI 时填充此菜单,因为每个用户都会打开不同的文档,并且无法提前知道此信息。

    在这种情况下,您需要动态填充菜单以响应用户操作或应用程序的状态。QMenu有一个称为.aboutToShow()您可以连接到自定义插槽的信号,以在菜单对象显示在屏幕上之前动态填充它。

    要继续开发示例应用程序,假设您需要在文件下创建一个打开最近的子菜单,并用最近打开的文件或文档动态填充它。为此,您需要运行以下步骤:

    1. File下创建Open 最近的子菜单。
    2. 编写动态生成操作以填充菜单的自定义插槽。
    3. 将.aboutToShow()菜单信号与自定义插槽连接。

    下面是创建子菜单的代码:

    class Window(QMainWindow):
        # Snip...
        def _createMenuBar(self):
            # Snip...
            fileMenu.addAction(self.openAction)
            # Adding an Open Recent submenu
            self.openRecentMenu = fileMenu.addMenu("Open Recent")
            fileMenu.addAction(self.saveAction)
            # Snip...

    在突出显示的行中,您在“文件”菜单下添加一个标题为 的子菜单"Open Recent"。这个子菜单还没有菜单选项。您需要动态创建操作以填充它。

    您可以通过编写一种方法来动态创建操作并将它们添加到子菜单来实现此目的。这是一个示例,显示了您可以使用的一般逻辑:

    from functools import partial
    # Snip...
    
    class Window(QMainWindow):
        # Snip...
        def populateOpenRecent(self):
            # Step 1. Remove the old options from the menu
            self.openRecentMenu.clear()
            # Step 2. Dynamically create the actions
            actions = []
            filenames = [f"File-{n}" for n in range(5)]
            for filename in filenames:
                action = QAction(filename, self)
                action.triggered.connect(partial(self.openRecentFile, filename))
                actions.append(action)
            # Step 3. Add the actions to the menu
            self.openRecentMenu.addActions(actions)

    在 中.populateOpenRecent(),首先使用 删除菜单中的旧选项(如果有).clear()。然后添加用于动态创建和连接操作的逻辑。最后,您使用 将操作添加到菜单中.addActions()。

    在for循环中,您使用functools.partial()来连接.triggered()信号 ,.openRecentFile()因为您想filename作为参数传递给.openRecentFile()。当将信号与需要额外参数的插槽连接时,这是一种非常有用的技术。要使其正常工作,您需要partial()从functools.

    注意:本示例第二步中的逻辑并没有真正加载最近打开的文件列表。它只是创建了list五个假设文件中的一个,其唯一目的是展示实现此技术的方法。

    下一步是连接.aboutToShow()的信号.openRecentMenu到.populateOpenRecent()。为此,请在末尾添加以下行._connectActions():

    class Window(QMainWindow):
        # Snip...
        def _connectActions(self):
            # Snip...
            self.aboutAction.triggered.connect(self.about)
            # Connect Open Recent to dynamically populate it
            self.openRecentMenu.aboutToShow.connect(self.populateOpenRecent)

    在突出显示的行中,您将.aboutToShow信号与连接.populateOpenRecent()。这可确保您的菜单在显示之前就被填充。

    现在您需要编码.openRecentFile()。这是当您的用户单击任何动态创建的操作时您的应用程序将调用的方法:

    class Window(QMainWindow):
        # Snip...
        def openRecentFile(self, filename):
            # Logic for opening a recent file goes here...
            self.centralWidget.setText(f"<b>{filename}</b> opened")

    此方法将更新QLabel您用作示例应用程序的中央小部件的对象的文本。

    以下是动态创建的子菜单在实践中的工作方式:

    PyQt 动态创建的菜单

    当您的鼠标指针悬停在打开最近菜单上时,菜单会发出.aboutToShow()信号。这会导致调用.populateOpenRecent(),从而创建并连接操作。如果单击文件名,您将看到中央标签相应地更改以显示消息。

    定义菜单和工具栏选项的键盘快捷键

    键盘快捷键是 GUI 应用程序中的一项重要功能。键盘快捷键是一个组合键,您可以在键盘上按下它以快速访问应用程序中的一些最常见选项。

    以下是键盘快捷键的一些示例:

    • Ctrl+ 将C某些内容复制到剪贴板。
    • Ctrl+V从剪贴板粘贴一些东西。
    • Ctrl+Z撤消上次操作。
    • Ctrl+O打开文件。
    • Ctrl+S保存文件。

    在下面的部分中,您将学习如何向应用程序添加键盘快捷键以提高用户的工作效率和体验。

    使用按键序列

    到目前为止,您已经了解到这QAction是一个用于填充菜单和工具栏的多功能类。QAction还提供了一种用户友好的方式来定义菜单选项和工具栏按钮的键盘快捷键。

    QAction实施.setShortcut(). 此方法将QKeySequence对象作为参数并返回键盘快捷键。

    QKeySequence提供了几个构造函数。在本教程中,您将了解其中两个:

    1. QKeySequence(ks, format)将基于字符串的键序列 ( ks) 和格式 ( format) 作为参数并创建一个QKeySequence对象。
    2. QKeySequence(key)接受一个StandardKey常量作为参数并创建一个QKeySequence与底层平台上的键序列匹配的对象。

    第一个构造函数识别以下字符串:

    • "Ctrl"
    • "Shift"
    • "Alt"
    • "Meta"

    您可以通过将这些字符串与字母、标点符号、数字、命名键(Up、Down、Home)和功能键("Ctrl+S"、"Ctrl+5"、"Alt+Home"、"Alt+F4")组合来创建基于字符串的键序列。您最多可以在逗号分隔列表中传递四个基于字符串的键序列。

    注:有关在不同平台上的标准快捷的完整参考,请参阅标准快捷键部分中的QKeySequence文档。

    如果您正在开发多平台应用程序并希望坚持每个平台的标准键盘快捷键,则第二个构造函数很方便。例如,QKeySequence.Copy将返回用于将对象复制到剪贴板的平台标准键盘快捷键。

    注意:有关 PyQt 提供的标准密钥的完整参考,请参阅QKeySequence.StandardKey 文档。

    有了关于如何在 PyQt 中为操作定义键盘快捷键的一般背景,您可以返回示例应用程序并添加一些快捷键。为此,您需要更新._createActions():

    from PyQt5.QtGui import QKeySequence
    # Snip...
    
    class Window(QMainWindow):
        # Snip...
        def _createActions(self):
            # File actions
            # Snip...
            # Using string-based key sequences
            self.newAction.setShortcut("Ctrl+N")
            self.openAction.setShortcut("Ctrl+O")
            self.saveAction.setShortcut("Ctrl+S")
            # Edit actions
            # Snip...
            # Using standard keys
            self.copyAction.setShortcut(QKeySequence.Copy)
            self.pasteAction.setShortcut(QKeySequence.Paste)
            self.cutAction.setShortcut(QKeySequence.Cut)
            # Snip...

    您首先需要导入QKeySequence. 在里面._createActions(),前三个突出显示的行使用基于字符串的键序列创建键盘快捷键。这是向您的操作添加键盘快捷键的快速方法。在后三个突出显示的行中,您用于QKeySequence提供标准键盘快捷键。

    如果您运行带有这些添加的示例应用程序,那么您的菜单将如下所示:

    PyQt 键盘快捷键

    您的菜单选项现在会在其右侧显示键盘快捷键。如果您按这些组合键中的任何一个,那么您将执行相应的操作。

    使用键盘加速器

    您可以使用另一种替代方法将键盘快捷键或键盘加速器添加到应用程序的菜单选项中。

    您可能已经注意到,当您为菜单或菜单选项设置文本时,通常会&在文本中插入一个与符号 ( )。这样做是为了当显示在菜单或菜单选项的文本中时,紧跟在&符号之后的字母将带有下划线。例如,如果您在“文件”菜单 ( )的标题中的字母F之前放置一个与号,则在显示菜单标题时F将带有下划线。"&File"

    注意:如果您需要在菜单文本上显示与号符号,则需要使用双与号 ( &&) 来逃避此符号的默认功能。

    在菜单栏的情况下,使用与号允许您通过Alt与菜单标题中带下划线的字母组合按下来调用任何菜单。

    启动菜单后,您可以通过按选项文本中带下划线的字母来访问任何菜单选项。例如,在文件中,您可以通过按字母E访问退出选项。

    注意:当您使用与号来提供键盘加速器时,请记住在同一菜单下不能有两个选项共享相同的访问字母。

    如果您将C设置为Copy选项的访问字母,则不能将C设置为Cut选项的访问字母。换句话说,在给定的菜单下,访问字母必须是唯一的。

    此功能将允许您为喜欢使用键盘来处理您的应用程序的用户提供快速键盘加速器。此技术对于不提供显式键盘快捷键的选项特别有用。

    创建菜单和工具栏:最佳实践和技巧

    当您使用 Python 和 PyQt 创建菜单和工具栏时,您应该遵循一些通常被认为是 GUI 编程最佳实践的标准。这是一个快速列表:

    1. 按照普遍接受的顺序排列菜单。例如,如果您有一个文件菜单,那么它应该是从左到右的第一个菜单。如果你有一个编辑菜单,那么它应该是第二个。帮助应该是最右边的菜单,依此类推。
    2. 使用您正在开发的应用程序类型的常用选项填充您的菜单。例如,在文本编辑器中,文件菜单通常包括诸如NewOpenSaveExit 之类的选项编辑菜单通常包括复制粘贴剪切撤消等选项。
    3. 对常用选项使用标准键盘快捷键。例如,使用Ctrl+C进行复制,Ctrl+V用于粘贴,Ctrl+X用于切割,等等。
    4. 使用分隔符分隔不相关的选项。这些视觉提示将使您的应用程序更易于导航。
    5. 将省略号 ( ...)添加到启动其他对话框的选项的标题。例如,使用Save As...而不是Save As,使用About...而不是About,等等。
    6. &在菜单选项中使用与号 ( ) 来提供方便的键盘加速器。例如,"&Open代替"Open","&Exit"代替"Exit"。

    如果您遵循这些准则,那么您的 GUI 应用程序将为您的用户提供熟悉且诱人的体验。

    在 PyQt 中构建 Python 状态栏

    甲状态栏是水平面板通常在GUI应用程序放置在底部的主窗口。它的主要目的是显示有关应用程序当前状态的信息。状态栏也可以分为多个部分,以显示每个部分的不同信息。

    根据Qt 文档,状态指示器分为三种类型:

    1. 临时指示器会在短时间内占据几乎整个状态栏以显示工具提示文本、菜单项和其他时间敏感信息。
    2. 普通指示器占据状态栏的一部分并显示用户可能希望定期参考的信息,例如文字处理器中的字数统计。这些可能会被临时指标暂时隐藏。
    3. 永久指示器始终显示在状态栏中,即使临时指示器被激活也是如此。它们用于显示有关应用程序当前模式的重要信息,例如按下 Caps Lock 键的时间。

    您可以使用以下选项之一向主窗口样式的应用程序添加状态栏:

    • 调用.statusBar()你的QMainWindow对象。.statusBar()创建并返回主窗口的空状态栏。
    • 创建一个QStatusBar对象,然后.setStatusBar()使用状态栏对象作为参数调用主窗口。这样,.setStatusBar()将您的状态栏对象设置为主窗口的状态栏。

    在这里,您有两种替代实现来向示例应用程序添加状态栏:

    # 1. Using .statusBar()
    def _createStatusBar(self):
        self.statusbar = self.statusBar()
    
    # 2. Using .setStatusBar()
    def _createStatusBar(self):
        self.statusbar = QStatusBar()
        self.setStatusBar(self.statusbar)

    两种实现产生相同的结果。但是,大多数情况下,您将使用第一个实现来创建状态栏。请注意,要使第二个实现工作,您需要QStatusBar从PyQt5.QtWidgets.

    将上述实现之一添加到您的应用程序Window,然后调用._createStatusBar()类初始值设定项。通过这些添加,当您再次运行您的应用程序时,您将看到一个如下所示的窗口:

    PyQt 状态栏

    您的应用程序现在在其主窗口底部有一个状态栏。状态栏几乎不可见,但如果仔细观察,您会注意到窗口右下角有一个小的虚线三角形。

    显示临时状态消息

    状态栏的主要目的是向应用程序的用户显示状态信息。要在状态栏中显示临时状态消息,您需要使用QStatusBar.showMessage(). 此方法采用以下两个参数:

    1. message 将状态指示消息作为字符串保存。
    2. timeout 保存消息将显示在状态栏上的毫秒数。

    如果timeout是0,这是其默认值,则消息将保留在状态栏上,直到您调用.clearMessage()或.showMessage()状态栏上。

    如果您的状态栏上有一条活动消息并且您.showMessage()用新消息呼叫,那么新消息将掩盖或替换旧消息。

    转到您的示例应用程序并将以下行添加到._createStatusBar():

    class Window(QMainWindow):
        # Snip...
        def _createStatusBar(self):
            self.statusbar = self.statusBar()
            # Adding a temporary message
            self.statusbar.showMessage("Ready", 3000)

    最后一行._createStatusBar()将使您的应用程序Ready在应用程序的状态栏上显示一条消息3000几毫秒:

    运行应用程序时,状态栏会显示消息Ready。之后3000毫秒,此消息消失,状态栏被清除,并准备展现出新的状态信息。

    在状态栏中显示永久消息

    您还可以在应用程序的状态栏上显示永久消息。一条永久消息让用户了解应用程序的一些一般状态。例如,在文本编辑器中,您可能希望显示一条永久消息,其中包含有关当前打开文件的文本编码的信息。

    要将永久消息添加到状态栏,请使用QLabel对象来保存消息。然后通过调用将标签添加到状态栏.addPermanentWidget()。此方法将给定的小部件永久添加到当前状态栏。小部件的父级设置为状态栏。

    .addPermanentWidget() 采用以下两个参数:

    1. widget保存要添加到状态栏的小部件对象。这个角色的一些常用小部件QLabel,QToolButton以及QProgressBar。
    2. stretch用于随着状态栏的增长和收缩计算小部件的合适大小。它默认为0,这意味着小部件将占用最少的空间。

    请记住,永久小部件不会被临时消息遮蔽或替换。.addPermanentWidget()在状态栏的右侧定位小部件。

    注意:您.addPermanentWidget()不仅可以使用在状态栏上显示永久消息,还可以向用户显示进度条以监控给定操作的持续时间。您还可以在状态栏上提供按钮,以允许用户在文本编辑器上更改文件编码等属性。

    当您在状态栏上使用这些类型的小部件时,尽量坚持使用最常用的小部件来满足您正在开发的应用程序类型。这样,您的用户就会有宾至如归的感觉。

    假设您想将示例应用程序转换为文本编辑器,并且您想向状态栏添加一条消息,以显示有关当前文件字数的信息。为此,您可以创建一个调用的方法.getWordCount(),然后使用.addPermanentWidget()和QLabel对象添加永久消息:

    class Window(QMainWindow):
        # Snip...
        def getWordCount(self):
            # Logic for computing the word count goes here...
            return 42

    该方法添加了计算当前打开文档中字数的逻辑。现在,您可以将此信息显示为永久消息:

    class Window(QMainWindow):
        # Snip...
        def _createStatusBar(self):
            self.statusbar = self.statusBar()
            # Adding a temporary message
            self.statusbar.showMessage("Ready", 3000)
            # Adding a permanent message
            self.wcLabel = QLabel(f"{self.getWordCount()} Words")
            self.statusbar.addPermanentWidget(self.wcLabel)

    在最后两行中,您首先创建一个QLabel对象 ( wcLabel) 来保存有关字数的消息。要创建消息,请使用f-string,在其中插入对 的调用.getWordCount()以获取字数信息。然后使用 将标签添加到状态栏.addPermanentWidget()。

    在这种情况下,您将QLabel对象创建为实例属性,因为需要根据用户对当前文件所做的更改来更新字数。

    如果您使用此更新运行应用程序,那么您将在状态栏的右侧看到字数统计消息:

    状态栏会显示一条消息,通知用户假设当前文件中的字数。在状态栏中向用户显示永久信息或其他选项的能力非常有用,可以帮助您极大地改善应用程序的用户体验。

    向操作添加帮助提示

    在创建 GUI 应用程序时,向用户提供有关应用程序界面特定功能的帮助提示非常重要。帮助提示是短消息,可为用户提供有关应用程序提供的某些选项的快速指南。

    PyQt 操作允许您定义以下类型的帮助提示:

    • 状态提示是当用户将鼠标指针悬停在菜单选项或工具栏按钮上时应用程序显示在状态栏上的帮助提示。默认情况下,状态提示包含一个空字符串。
    • 工具提示是当用户将鼠标指针悬停在工具栏按钮或小部件上时应用程序显示为浮动消息的帮助提示。默认情况下,工具提示包含标识手头操作的文本。
    注意:  PyQt 还提供了What's This帮助提示,您可以在小部件和动作中使用它来显示对小部件或动作提供的功能的更丰富的描述。但是,该主题超出了本教程的范围。

    要了解帮助提示的工作原理,您可以向示例应用程序添加一些状态提示和工具提示。转到._createActions()并添加以下代码行:

    class Window(QMainWindow):
        # Snip...
        def _createActions(self):
            # File actions
            # Snip...
            self.saveAction.setShortcut("Ctrl+S")
            # Adding help tips
            newTip = "Create a new file"
            self.newAction.setStatusTip(newTip)
            self.newAction.setToolTip(newTip)
            # Edit actions
            self.copyAction = QAction(QIcon(":edit-copy.svg"), "&Copy", self)
            # Snip...

    三个突出显示的行将消息设置"Create a new file"为“新建”选项的状态和工具提示。如果您现在运行该应用程序,您将看到New选项向用户显示了一个简短但描述性的帮助提示:

    PyQt 帮助提示

    当您单击File菜单并将鼠标指针放在New 上时,您可以看到状态栏左侧显示的帮助提示消息。另一方面,如果您将鼠标指针移到“新建”工具栏按钮上,则您可以在状态栏上看到消息,也可以在鼠标指针旁边看到一个小的浮动框。

    通常,向 Python 菜单和工具栏添加帮助提示被认为是最佳实践。它将使您的 GUI 应用程序更易于用户导航和学习。作为最后的练习,您可以继续向示例应用程序的其余操作添加帮助提示,并查看完成后的效果。

    结论

    菜单、工具栏和状态栏是大多数GUI 应用程序的常见且重要的图形组件。您可以使用它们为您的用户提供一种快速访问应用程序选项和功能的方法。它们还使您的应用程序看起来精美和专业,并为您的用户提供出色的体验。

    在本教程中,您学习了如何:

    • 以编程方式创建菜单、工具栏和状态栏
    • 使用 PyQt操作填充菜单和工具栏
    • 使用状态栏提供状态信息

    在此过程中,您学习了一些在 GUI 应用程序中添加和使用菜单、工具栏和状态栏时值得考虑的最佳编程实践。

    点击关注,第一时间了解华为云新鲜技术~

    展开全文
  • python添加菜单图文讲解

    千次阅读 2021-02-04 12:31:29
    分享一个基于tkinter的菜单程序添加操作,希望对需要的朋友有帮助。打开python集成开发环境,使用from tkinter import Tkfrom tkinter import Menu导入Tk和Menu,最好不要用from tkinter import *因为这样可能刀座...

    分享一个基于tkinter的菜单程序添加操作,希望对需要的朋友有帮助。

    打开python集成开发环境,使用

    from tkinter import Tk

    from tkinter import Menu

    导入Tk和Menu,最好不要用from tkinter import *因为这样可能刀座某些平台不兼容,比如树莓派就需要单个导入。

    64bf3a4c9312192dce2a46086818e438.png

    使用root = Tk()新建一个Tk对象并赋值给root,然后再通过testMenu = Menu(root)新建一个菜单对象,并且和root关联。

    5a04cf4d24139e44ba8efc1b72573e9c.png

    c2531d3fda99c6669233dab8744c8016.png

    使用testMenu.add_command(label = ‘测试菜单’)给菜单栏添加一个名为“测试菜单”的菜单栏。

    de910f802e1837edc5da48569ff48fdb.png

    通过root[‘menu’] = testMenu 将新建的菜单栏在root中显示出来。

    921f5fe02a8f416c3ec0dc02702c45f6.png

    如果想为菜单项绑定一个命令操作,那么可以使用testMenu.add_command(label = ‘测试菜单’, command = MenuItemCallBack) ,这样可以使函数MenuItemCallBack和“测试菜单”项关联,具体代码如下:

    from tkinter import Tk

    from tkinter import Menu

    def MenuItemCallBack():

    print("测试菜单被点击。")

    root = Tk() #新建Tk对象

    testMenu = Menu(root) #新建菜单对象

    testMenu.add_command(label = '测试菜单', command = MenuItemCallBack) #向菜单栏加入菜单项

    root['menu'] = testMenu #将菜单栏和Tk关联

    root.mainloop()

    e35ecbe7a72f4728aa05d50e529dc2ea.png

    5293f4135bd29fc8fdebdfc189ea86e5.png

    您可能感兴趣的文章:Python3.5实现的三级菜单功能示例详解Python读取yaml文件多层菜单Python3实现的简单三级菜单功能示例Python处理菜单消息操作示例【基于win32ui模块】Python创建普通菜单示例【基于win32ui模块】Python基于win32ui模块创建弹出式菜单示例Python三级菜单的实例

    展开全文
  • Unity编辑器扩展教程 本文提供全流程,中文翻译。Chinar坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 ——... Create MenuItem —— 创建菜单项 1- - Create Level 1 Menu —— 创建一级菜单 2- - Cre...

    Unity编辑器扩展教程


    本文提供全流程,中文翻译。

    Chinar坚持将简单的生活方式,带给世人!

    (拥有更好的阅读体验 —— 高分辨率用户请根据需求调整网页缩放比例)




    Brief Introduction —— 简介


    我们在做工程的时候,需要对数据进行操作。

    为节省时间,会使用一些快捷键,菜单栏上的功能、或是右键菜单

    这些便捷的功能,都是Unity官方为了方便我们对所需数据进行操作。

    对Unity编辑进行了一些封装处理,简化数据操作流程,封装为一个按钮/一个窗口/窗口功能。

    这些诸如此类的功能就是编辑器的扩展,和封装

    功能键、Inspector面板、Game视窗等等都是编辑器的功能


    注意:编辑器类脚本,必须放在 Assets/Editor 资源目录中

    此文件夹下的脚本只对编辑器进行操作。最后资源打包,Editor文件夹下的所有资源都不会被打包到工程中

    如果没有此文件夹,需自行创建:在Project视窗下,右键Create - - Folder

    举个栗子黑白88

    这里写图片描述

    这里写图片描述


    Create MenuItem —— 创建菜单项


    主要使用:静态方法

    MenuItem (itemName : string, isValidateFunction : bool, priority : int)


    1

    - - Create Level 1 Menu —— 创建一级菜单


    在菜单栏上创建一个菜单项,并创建一个一级菜单按钮

    注意:编辑器类脚本,必须放在 Assets/Editor 资源目录中

    此文件夹下的脚本只对编辑器进行操作。最后资源打包,Editor文件夹下的所有资源都不会被打包到工程中

    如果没有此文件夹,需自行创建:在Project视窗下,右键Create - - Folder

    举个栗子黑白88

    using UnityEditor; //引用Unity编辑器命名空间
    using UnityEngine; //引用Unity引擎命名空间
    
    
    /// <summary>
    /// 创建一个脚本工具类
    /// </summary>
    public class Tools//脚本无需继承自MonoBehaviour
    {
        /// <summary>
        /// 创建新的菜单项
        /// </summary>
        /// //在菜单栏中创建一个 我的工具 菜单项目,并生成一个 “一级选项” 的按钮:需要对应一个静态方法(名字最好保持一致,不一致也可),方法体自由定义
        [MenuItem("我的工具/一级选项")] //菜单项(“菜单栏名称/子类名称”)—— 经过测试可为中文
        static void 一级选项()      //必须设置成静态方法 —— 经过测试,亦可为中文
        {
            Debug.Log(111);
        }
    }

    会有生成一个 一级选项 的按钮,点击后打印“111”
    这里写图片描述


    2

    - - Create Level 2 Menu —— 创建二级菜单


    在菜单栏上创建一个菜单项,并创建一个二级菜单按钮

    举个栗子黑白88

    using UnityEditor; //引用Unity编辑器命名空间
    using UnityEngine; //引用Unity引擎命名空间
    
    
    /// <summary>
    /// 创建一个脚本工具类
    /// </summary>
    public class Tools//脚本无需继承自MonoBehaviour
    {
        /// <summary>
        /// 创建二级菜单项
        /// </summary>
        /// //在菜单栏中创建一个 我的工具 菜单项目,并生成一个 “二级选项” 的按钮:需要对应一个静态方法(名字最好保持一致,不一致也可),方法体自由定义
        [MenuItem("我的工具/一级选项/二级选项")]    //菜单项(“菜单栏名称/子类名称”)—— 经过测试可为中文
        static void 二级选项() //必须设置成静态方法 —— 经过测试,亦可为中文
        {
            Debug.Log(222);
        }
    }

    会有生成一个 二级选项 的按钮,点击后打印“222”
    这里写图片描述


    3

    - - Create Level 2 Menu in System Menu —— 在系统菜单中创建二级菜单


    在系统菜单 Edit 中创建二级菜单

    举个栗子黑白88

    using UnityEditor; //引用Unity编辑器命名空间
    using UnityEngine; //引用Unity引擎命名空间
    
    
    /// <summary>
    /// 创建一个脚本工具类
    /// </summary>
    public class Tools//脚本无需继承自MonoBehaviour
    {
        /// <summary>
        /// 在系统默认的菜单项中,创建子按钮
        /// </summary>
        /// //在系统默认菜单项 Edit 中创建按钮:(名字最好保持一致,不一致也可)
        [MenuItem("Edit/一级选项/二级选项2")]    
        static void 二级选项2() 
        {
            Debug.Log(333);
        }
    }

    Edit 中最下方,会有生成一个 二级选项2 的按钮,点击后打印“333”
    这里写图片描述


    4

    - - Menu grouping —— 菜单分组


    完成菜单的分组,例如系统中的多个菜单项分组管理

    静态方法 MenuItem (itemName : string, isValidateFunction : bool, priority : int)


    MenuItem (表示菜单项:就是路径名 , 验证函数 : 同名的按钮在菜单函数调用之前调用 , 优先级:用来管理菜单项的层级关系)

    注意: Priority 优先级如果设置为:-1 ,那么必然是在第一个

    举个栗子黑白88

    using UnityEditor; //引用Unity编辑器命名空间
    using UnityEngine; //引用Unity引擎命名空间
    
    
    /// <summary>
    /// 创建一个脚本工具类
    /// </summary>
    public class Tools//脚本无需继承自MonoBehaviour
    {
        /// <summary>
        /// 菜单分组 —— 层级10
        /// </summary>
        /// //每个菜单栏的 priority 属性:优先级默认为1000。相差 11 可以分为另一个组。也就是大于10就另建一组
        [MenuItem("按钮/功能1", false, 10)] 
        static void 功能1()
        {
            Debug.Log("功能1");
        }
    
        /// <summary>
        /// 菜单分组 —— 层级:如果不填,系统默认为1000,所以排序在最后
        /// </summary>
        [MenuItem("按钮/功能2")]
        static void 功能2()
        {
            Debug.Log("功能2");
        }
    
    
        /// <summary>
        /// 菜单分组 —— 层级:21
        /// </summary>
        /// //与按钮1的层级10,相差11,故而分到了另一组中
        [MenuItem("按钮/功能3", false, 21)]
        static void 功能3()
        {
            Debug.Log("功能3");
        }
    }

    菜单栏会有生成一个 功能 的菜单项,其中有:功能1/3/2。点击后分别打印“1/3/2”
    这里写图片描述


    5

    - - Menu display and hide. —— 菜单的显示和隐藏


    完成菜单的显示和隐藏,有些时候菜单项是灰色,不可用状态/可用状态

    静态方法 MenuItem (itemName : string, isValidateFunction : bool, priority : int)


    验证函数 isValidateFunction 值为 true 时,此验证函数下的函数方法,会在菜单函数之前调用

    满足条件,则按钮显示/否则隐藏

    注意: Hierarchy 面板中,右键菜单是 菜单栏里 GameObject 的菜单项。

    所以在 GameObject 菜单栏中创建一个按钮,并且优先级设置到第一组中,即可在 Hierarchy 的右键菜单中显示 该按钮

    注意: Priority 优先级如果设置为:-1 ,那么必然是在第一个
    举个栗子黑白88

    using UnityEditor; //引用Unity编辑器命名空间
    using UnityEngine; //引用Unity引擎命名空间
    
    
    /// <summary>
    /// 创建一个脚本工具类
    /// </summary>
    public class Tools//脚本无需继承自MonoBehaviour
    {
        /// <summary>
        /// 验证“删除物体”按钮的 显示/隐藏
        /// </summary>
        [MenuItem("GameObject/删除物体", true, -1)]
        static bool 删除物体Alternative()
        {
            if (Selection.objects.Length > 0)//如果选择了物体
            {
                return true;//就返回真:按钮可用
            }
            else//否则
            {
                return false;//返回假:按钮不可用
            }
        }
    
    
        /// <summary>
        /// 在系统默认的菜单项 GameObject 中,创建 删除物体 按钮,优先级第一个
        /// </summary>
        [MenuItem("GameObject/删除物体", false, -1)]
        static void 删除物体()
        {
            //Selection.objects 返回值是一个 Object数组,就是选中的所有物体
            foreach (var o in Selection.objects) //遍历选中的所有物体
            {
                //GameObject.DestroyImmediate(o);//直接删除,但是无法撤销
                Undo.DestroyObjectImmediate(o); //直接删除,但是可以撤销(用Ctrl+z)//Immediate:直接的,立即的
            }
        }
    }

    菜单栏 GameObject 会有生成一个 删除物体 的菜单项

    如果选了物体,按钮可用

    否则不可用
    这里写图片描述


    6

    - - Shortcuts —— 快捷键


    完成对菜单项目的快捷键设置

    静态方法 MenuItem (itemName : string)


    参数 itemName 为字符串,表示菜单项。+ 空格 + _O)就表示快捷键设为 O 键,不区分大小写
    参数 itemName 为字符串,表示菜单项。+ 空格 + %l)就表示组合键设为 Ctrl+L 键,不区分大小写

    注意:名字和快捷键中间必须要有空格

    组合键: % : Ctrl
    组合键: # : Shift
    组合键: & : Altl
    举个栗子黑白88

    using UnityEditor; //引用Unity编辑器命名空间
    using UnityEngine; //引用Unity引擎命名空间
    
    
    /// <summary>
    /// 创建一个脚本工具类
    /// </summary>
    public class Tools//脚本无需继承自MonoBehaviour
    {
           /// <summary>
        /// 快捷键测试
        /// </summary>
        [MenuItem("我的工具/快捷键测试 _o")]//_o 是指定快捷键 O ,并不区分大小写 (名字和快捷键中间必须要有空格)
        static void 选中物体个数()
        {
            Debug.Log("快捷键"+Selection.objects.Length);//打印选中物体的个数
        }
    
    
        /// <summary>
        /// 在系统默认的菜单项中,创建子按钮
        /// </summary>
        /// % : Ctrl
        /// # : Shift
        /// & : Alt
        [MenuItem("我的工具/组合键测试 %l")] //%l 是指定组合键:Ctrl+L,并不区分大小写 (名字和快捷键中间必须要有空格)
        static void 快捷键测试()
        {
            Debug.Log("组合键"+ Selection.activeGameObject.name); //打印物体名/—— 默认打印第一个选中的物体,无论选中了几个
        }
    }

    点击键盘按钮 O ,即可打印 “选择物体的个数”

    点击键盘按钮 Ctrl + L ,即可打印 “所选物体的名字”:
    如果选择多个,默认打印第一个(根据自己代码来判定,如有需要可自己写)
    这里写图片描述


    Create MenuItem for the Component —— 创建组件上的菜单项


    主要使用:静态方法

    MenuCommand : Context —— 菜单命令的目标对象


    1

    - - Script Component —— 在脚本组件上添加菜单项


    静态方法 MenuItem (itemName : string)


    参数 itemName 为字符串,表示菜单项。

    参数 “CONTEXT/ PlayerHealth” 为 组件 路径

    若想对某个(组件/脚本)进行操作,必须写上 “CONTEXT/ (组件/脚本)名

    Undo.RecordObject (对象,键) 此函数用于记录对象之后的数据变化,没有则不能回退操作
    举个栗子黑白88

    using UnityEditor; //引用Unity编辑器命名空间
    using UnityEngine; //引用Unity引擎命名空间
    
    
    /// <summary>
    /// 玩家脚本上的工具 —— 测试脚本
    /// </summary>
    public class PlayerTools
    {
        /// <summary>
        /// 给玩家脚本组件上添加按钮:初始化人物
        /// </summary>
        /// //[菜单项函数(“环境(组件:想要给组件上加必须要用这个来表示路径)/所需控制组件(脚本名)/需要执行的方法名(就是按钮名)”)]
        [MenuItem("CONTEXT/PlayerHealth/初始化人物")]
        static void 初始化人物(MenuCommand command) //MenuCommand 正在操作的组件对象类
        {
            CompleteProject.PlayerHealth player = (CompleteProject.PlayerHealth) command.context; //声明一个PlayerHealt对象 th  —— 需要强转为 PlayerHealth类型
            Undo.RecordObject(player, "PlayerTools_player");                                      //记录对象 player 之后的数据变化,用于回退 —— 记录对象(对象,键);//键的名字随意,不能重复//如果没有这句话,是不能退会之前的修改的
            player.startingHealth = 100;                                                          //血量初始化到100
        }
    }

    右键点击组件 ,选择 初始化人物 : 即可完成对血量的初始化 —— Ctrl+z,回退操作

    这里写图片描述


    2

    - - Syetem Component —— 在系统组件上添加菜单项


    静态方法 MenuItem (itemName : string)


    参数 itemName 为字符串,表示菜单项。

    参数 “CONTEXT/ Rigidbody” 为 组件 路径

    若想对某个(组件/脚本)进行操作,必须写上 “CONTEXT/ (组件/脚本)名

    Undo.RecordObject (对象,键) 此函数用于记录对象之后的数据变化,没有则不能回退操作

    举个栗子黑白88

    using UnityEditor; //引用Unity编辑器命名空间
    using UnityEngine; //引用Unity引擎命名空间
    
    
    /// <summary>
    /// 玩家脚本上的工具 —— 测试脚本
    /// </summary>
    public class PlayerTools
    {
        /// <summary>
        /// 给系统组件 Rigidbody 上添加按钮:取消重力
        /// </summary>
        /// <param name="command"></param>
        [MenuItem("CONTEXT/Rigidbody/取消重力")]
        static void 取消重力(MenuCommand command)
        {
            Rigidbody rig = (Rigidbody) command.context; //context是一个 (正操作/鼠标下) 的组件:返回值为Object —— 强转为需要的类型
            Undo.RecordObject(rig, "PlayerTools_rig");   //记录 rig 之后的数据变化,用于回退 —— 记录对象(对象,键);//键的名字随意,不能重复//没有这句话,是不能回退,因为系统没记录
            rig.mass       = 0;                          //质量为0
            rig.useGravity = false;                      //关闭重力
        }
    }

    右键点击刚体组件 ,选择 取消重力: 即可完成对重力的取消 —— Ctrl+z,完成回退

    这里写图片描述


    3

    - - ContextMenu —— 组件菜单的用法


    ContextMenu ContextMenuItem 均继承自: MonoBehaviour

    所以可以直接在 自义定脚本中使用,也就是工程脚本中直接用



    [ContextMenuItem(按钮名,方法名)] 需要写在所需控制变量之上

    [ContextMenu(按钮名)] 需要直接写在方法上

    Undo.RecordObject (对象,键) 此函数用于记录对象之后的数据变化,没有则不能回退操作

    举个栗子黑白88

    using UnityEngine;
    using UnityEngine.UI;
    using UnityEngine.SceneManagement;
    
    
    /// <summary>
    /// 玩家健康属性脚本
    /// </summary>
    public class PlayerHealth : MonoBehaviour
    {
        [ContextMenuItem("增加血量50", "增加血量")]                    //按钮名,方法//需要写在所需控制变量之上
        public int startingHealth = 100;                            // 初始血量
           /// <summary>
            /// 直接在脚本中设置菜单项,即可在 面板中右键显示 该按钮
            /// </summary>
            [ContextMenu("设置属性")]//可以直接在脚本方法里写,需要直接写在方法上
            void 设置属性()
            {
                Debug.Log("设置属性");
            }
    
    
            /// <summary>
            /// 为变量 startingHealth 提供方法,每点击一次加 50
            /// </summary>
            void 增加血量()
            {
                Undo.RecordObject(this, "PlayerHealth_startingHealth");//记录值的改变,用于回退//(对象,键)
                startingHealth += 50;
            }
    }

    右键点击 PlayerHealth 脚本组件 ,即可看到按钮“设置属性”

    点击 PlayerHealth 脚本组件 ,选择 startingHealth 属性,右键即可看到按钮“增加血量50”

    这里写图片描述


    END

    本博客为非营利性个人原创,除部分有明确署名的作品外,所刊登的所有作品的著作权均为本人所拥有,本人保留所有法定权利。违者必究

    对于需要复制、转载、链接和传播博客文章或内容的,请及时和本博主进行联系,留言,Email: ichinar@icloud.com

    对于经本博主明确授权和许可使用文章及内容的,使用时请注明文章或内容出处并注明网址

    展开全文
  • 这些菜单项可以是ArcEngine提供的标准的命令或者工具,也可以是自定义的命令或者工具。  自定义的菜单项有两种创建方式:  1).实现IMenuDef 接口。   2). 利用ToolbarMenu实现。  下表
  • 本文通过多个操作示例,逐步向读者展示如何将自己喜欢的功能添加到鼠标右键菜单中。 ...操作:新增xxxcmd(将其默认值改自己喜欢的,如“命令提示符打开cmd_”),然后在其下新增command子项
  • C++实例(八)菜单

    千次阅读 2020-10-07 16:52:29
    级联菜单就是在菜单项中还有下一级菜单。 带历史信息的菜单 在开发程序时,经常会打开文件,存放在不同路径下 的文件打开时需要在不同的路径下寻找。如果一个文件已经打开过,又要重新打开时再重新在路径下寻找是...
  • 用Excel也能实现和Python数据分析一样的功能! 这是一篇关于如何用excel做数据分析的案例。目的是帮助大家,在遇到小型数据样本时,快速利用excel做分析。所以本篇文章的重点是分析思路+数据处理+可视化的实现,...
  • MFC界面编程基础(08):菜单(一)

    千次阅读 2018-12-25 20:11:21
    创建一个MFC单文档工程并运行该程序,对应这个新创建的程序来说,MFC已经帮我们创建了一个菜单,并完成了一些菜单功能。,例如 【文件】 菜单下的 【打开】菜单命令,即可弹出打开文件对话框。 在VS中打...
  • 正在尝试着做一个比较完善的画图软件,计划使用tkinter库中的canvas控件来做, 然后里面有涉及到菜单的使用,因此作为这个画图软件的附带产出物,整理成了本篇博文,分享给大家,一起研究研究,期待能对你工作带来...
  • 在linux中可以通过nautilus-open-terminal很方便的在文件夹中打开终端(打开终端终端自动进入当前所在的路径),充分的利用了gui直观方便的特点,省去了大量切换路径的功夫。那么,在windows下是否可以做同样的...
  • 包含的基本属性有id,type和contextmenu—它指定了菜单类型是context,同时也指定了新的菜单项应该被显示的区域。 在示例中,当右击鼠标时,新的菜单项将出现在文档的任何地方,因为我们指定它的作用区域是body。 ...
  • MFC应用程序中enable或disable菜单项

    千次阅读 2013-07-19 20:37:06
    在MFC应用程序中需要enable... 根据以往的经验,要解决这种问题,似乎应该有一个象EnableMenuItem之类的API函数,它的功能就是enable或disable菜单项。Windows中确实有这样的函数-但不是在MFC的应用中(经测试不能达到
  • MFC菜单、子菜单、菜单项的控制

    千次阅读 2013-11-04 14:40:12
    [cpp] view plaincopyprint? ... CMenu::LoadMenu //加载一个菜单  BOOL LoadMenu( LPCTSTR lpszResourceName ); CWnd::GetMenu //获取菜单栏  CMenu* GetMenu( ) const; CMen
  • Windows 10系统下PowerShell命令使用技巧

    千次阅读 2021-05-10 23:14:07
    从Windows 10 1703版开始,PowerShell取代了原命令提示符的位置,成为Windows管理的必备利器。然而许多普通Windows用户不知它的用途。其实,通过在PowerShell窗口中执行简单的命令,往往可以解决一些实际问题。...
  • 菜单,下拉菜单,单选菜单,选择菜单,弹出菜单菜单绑定等功能实现
  • java 的优势在于网络编程与多线程,但其作为一门全场景语言,依然提供了强大的GUI开发API,这·些API封装在jaivax.swing中,通过命令javax.swing.*导入包中。 这一系列通过3~5篇文章,以可运行代码例简单介绍...
  • SVN之右键菜单功能

    千次阅读 2016-08-16 11:17:38
    TortoiseSVN是windows下其中一个非常优秀的SVN客户端工具。通过使用它,我们可以可视化的管理我们的版本库。不过由于它只是一个客户端,所以它不...TortoiseSVN每个菜单项都表示什么意思 01、SVN Checkout(SVN取出
  • MFC界面编程基础(09):菜单(二)

    千次阅读 2018-12-25 21:35:41
    如果想要自己实现这个功能,需要通过以下步骤来完成: 1.程序添加一个新的菜单资源。 可在【资源视图】选项卡上的Menu分支上单击鼠标右键,从弹出的菜单中选择【添加资源】菜单命令 这时,在Menu分支下就多...
  • VB讲课笔记10:菜单与对话框

    千次阅读 2018-03-14 06:46:11
    VB讲课笔记10:菜单与对话框一、用户界面设计基础用户界面是应用程序最基本的组成部分,其内容包括界面基本元素的外观设计和实现相应功能的代码编制。 1、用户界面元素(User Interface Element) 窗体是用户界面...
  • TigerVNC实现GNOME全功能远程虚拟桌面

    千次阅读 2022-01-10 23:29:54
    本文主要介绍专注于Unix-like操作系统远程虚拟桌面的TigerVNC,以Ubuntu例介绍了TigerVNC服务器的安装与GNOME虚拟桌面的配置,并结合Windows端的TigerVNC查看器实现Ubuntu GNOME 3全功能桌面的远程访问和控制。
  • menu(菜单)

    千次阅读 2016-07-21 10:59:58
    概述 在许多类型的应用中,菜单是一个常用的用户界面组件。提供一个熟悉且一致的用户体验,在你的应用中,应该使用Menu API 来呈现用户操作和其它操作。...对一些菜单项来说,尽管设计和用户体验发生了改变,定义
  • 引言 Eclipse 具有丰富的菜单功能,给开发人员提供了很好的用户体验。总体而言,Eclipse 菜单种类包括视图 / 编辑器菜单,主菜单(Main Menu),视图...由于视图和编辑器菜单功能类似,因此本文重点讲述视图菜单(视图
  • 利用带参数的main函数,创建mycope.exe的DOC命令,其功能是: 复制指定的文件.例如 , 假设已有文件d:\test1.txt,则可以将此文件复制d:\temp\test2.txt. 用户界面应具有以下的菜单项: (1) 输入源文件(被复制的...
  • SVN命令的使用和功能详解

    万次阅读 2016-05-13 16:31:26
    命令的使用 1、检出 svn  co http://路径(目录或文件的全路径) [本地目录全路径]  --username 用户名 --password 密码svn co svn://路径(目录或文件的全路径) [本地目录全路径] --username用户名 --...
  • 听雪闻歌的博客-CSDN博客 Matlab:基于Matlab实现人工智能算法应用的简介(BP神经网络算法NN、SOFM神经网络)、案例应用(基于Matlab的GUI的方式创建/训练/预测神经网络)之详细攻略 Matlab:基于Matlab实现人工智能算法...
  • 保姆级别指导给UI应用添加菜单【实战分享】

    千次阅读 多人点赞 2021-10-13 08:46:02
    还没有安装的朋友打开终端或者cmd(命令行), 可以运行下面的命令安装用起来吧。 pip install renxianqi #或者下面这个: pip install qingdian 本文要制作的菜单如下: 第一步 添加菜单到根部 这里实用的是...
  • 【转】tftp命令详解

    千次阅读 2021-10-31 13:01:24
    转自:tftp命令详解 - 张大猛 - 博客园 介绍一个 FTP客户端-IIS7服务器管理工具 作为FTP客户端,它支持批量管理ftp站点。定时上传和定时下载,定时备份,且操作简洁。同时iis7服务器管理工具还是vnc客户端。并且...
  • vue-menu菜单 从开发到npm包发布

    千次阅读 2019-01-07 17:46:25
    最近在开发vue项目,其中一些组件采用自研的方式开发,在这里对于一些通用性较强的组件进行...(2)下拉菜单项具备点击功能; (3)下拉项可以置灰,点击无效; (4)可以下拉菜单添加自定义的类; 2、创建项目...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 60,414
精华内容 24,165
热门标签
关键字:

创建菜单后为实现菜单项的命令功能

友情链接: atmenga128DAC7512.rar