Go to QuArK Web Site
Putting a Command on a Menu
Updated 05 Apr 2018
Upper levels:
QuArK Information Base
3. Advanced customization
3.6. Plug-Ins
3.6.1. Plugin Tutorial

 3.6.1.2. Putting a Command on a Menu

 [ Prev - Up - Next ] 

A plugin needs to do four things:

  1. Get noticed by QuArK
  2. Import resources from the rest of the program
  3. Define the actions that the plugin will perform
  4. Provide menu commands, buttons or some other facility for the user to initiate the actions

And it should also register itself. So we'll do these five things in turn.


 Index


 Setting up

tiglari - 05 Apr 2018   [ Top ] 

What we'll be doing is making a replacement maptagside plugin. The real one is interconnected with some other plugins which we'll have to disable, & it would not be a good idea to mess with your regular QuArK installation, so you should make a second one, and in that installation, remove the files `plugins\mb2caulk' and `plugins\maptagside'.

Since we're going to be making a series of new maptagside plugins, it might be convenient to call them maptagside0.py, maptagside01.py; you can disable the older ones and keep them from loading by renaming them `tagside0.py', etc (since plugins are only loaded by the mapeditor if their names begin with q- or map-, or mb2- for Q3 engine games).


 Importing

tiglari - 05 Apr 2018   [ Top ] 

What you need to import depends on what you're doing; what we'll need for the whole plugin is this:

import quarkx
import quarkpy.mapmenus
import quarkpy.mapentities
import quarkpy.qmenu
import quarkpy.mapeditor
import quarkpy.mapcommands
from quarkpy.maputils import *
We will gradually explain what all this means.


 Action

tiglari - 05 Apr 2018   [ Top ] 

To produce the action we want, we need this:

def tagSideClick(m):
    quarkx.msgbox("This command does nothing", MT_INFORMATION, MB_OK)

This defines a function `tagSideClick', which gets a parameter `m', which is the actual menu item that invoked the action. We don't use this here, but we will later on. The body of the function calls the `msgbox' function from the module `quarkx', which is imported in the first import statement. Note that this works a bit differently from say an  #include  in C; when we import a module, we can't just use the names of the functions in it, but have to `qualify' those names by putting the module name in front, separated by a period.

This is to improve readability, at the expense of a a bit of typing on the part of the writer of the program; the name of the important function when it's used states where it came from, so the reader has a better chance of finding out what it does. We will soon see how to use unqualified imported names as well.

As for the actual  msgbox  function, it produces little `message box' dialogs that you can respond to by pushing buttons; its first argument is the string to be displayed in the message box, the second a `type' which determines the graphic icons associated with the box, and third a flag that says that the messagebox has an OK button.

Symbolic names for the useful values for these arguments are defined in the file  quarkpy\qtils.py , under the heading `# quarkx.msgbox'. So you can look there to see what the possibilities are. But hey, we haven't imported this module, and the symbols aren't qualified anyway, so what's going on?

What we have done is imported the module quarkpy.maputils, in a somewhat different way, in the last line of the import statement.  from <module name%gt;  import *  means `import everything that's defined in the named module, and use it without qualification'. And furthermore,  quarkpy.maputils,  a general grab-bag of utility functions for the map editor, imports  quarkpy.qeditor  in this way, which itself does the same to  quarkpy.qutils.py . So everything defined in all of these modules is available to our plugin, without qualifying the names.

This is convenient for the writer, but can be confusing for the reader. Even more so because symbols defined in later imports will override definitions from earlier ones. So this import style should only be used for a limited number of widely used utility files, which people working on the program can be expected to learn about soon.

One more thing with the import statements, what's the deal with the dots? Python modules are religiously identified as files, and folders are taken to be `super modules' containing sub-modules. So all of the files in quarkpy make up the quarkpy module, and to import them into a file outside of quarkpy you need the quarkpy. qualification. Inside quarkpy you don't, so the statement that  maputils.py  uses to import  qeditor.py  is just:

from qeditor import *


 Putting it on a menu

tiglari - 05 Apr 2018   [ Top ] 

So now that we've got some code that does something, we need to install a menu item so the user can have it done:

quarkpy.mapcommands.items.append(qmenu.item("&Do Nothing", TagSideClick))
On important point is that the text of this line has to start at the same horizontal position as the `def' line above, so that the whole thing looks like this:
def tagSideClick (m):
    quarkx.msgbox("This command does nothing", MT_INFORMATION, MB_OK)
quarkpy.mapcommands.items.append(quarkpy.qmenu.item("&Do Nothing", tagSideClick))
rather than say like this:
def tagSideClick (m):
    quarkx.msgbox("This command does nothing", MT_INFORMATION, MB_OK)
    quarkpy.mapcommands.items.append(qmenu.item("&Do Nothing", tagSideClick))
The reason is that Python uses indendation to impose block-structure, so that the first version first defines the TagSideClick function and then puts an item on the mapcommands menu, while the second version defines a TagSideClick function that first produces a message-box and then adds something to the menu (so that something will never show up unless the TagSideClick function gets called, which isn't going to happen). The blank line on other hand is just make the presentation clearer, and doesn't affect the operation of the program.

And it remains to explain what this menu-item-adding line actually does. quarky.mapcommands is the module where the `commands' menu is defined. It creates a list-valued variable `items', and `append' is a built-in Python function for attaching something to a list.

What we append to this list is an something created by the `item' function, defined in the module quarkpy.qmenu. We don't have to import this module because that job is already done by the qeditor.py module. This has among its import statements:

import qmenu
So when we import everything from quarkpy.maputils, and it imports everything from quarkpy.qeditor, we wind up getting qmenu, but we have to qualify the stuff in it with `qmenu'. This lets us use obvious words like `item', without having to use clumsy prefixing conventions, etc. to prevent name collisions.

So when this is all typed out and saved into a file `maptagside.py' in in the plugins directory, you should be able to start up QuArK, fire up the map editor, find the `Do Nothing' command on the command menu, click on it, and see the resulting message box.

If it doesn't work, possible explanations are:

  • A syntax error in the program, in which case you'll be looking at the QuArK console, which will have some kind of error message.
  • Your module-file's name doesn't start with `map', in which case QuArK will work but your new command won't appear on the menu.
  • Something else, tell me when you manage to figure out it.

If all else fails, the file  maptagside0.py  in plugin_examples.zip contains what is hopefully a working version of the code for this section.

I'll also say a bit more about what goes on when modules are loaded. When a module is loaded, it is first `compiled' into a file with the same name and extension .pyc, which will load more quickly. If the current .py file is older than the corresponding .pyc file, the compilation stage is skipped, and the .pyc file is loaded straight in. So you can delete all the .pyc files if you want to, but it will slow things down the first time QuArK is run.


 Registering

tiglari - 05 Apr 2018   [ Top ] 

Finally, a properly dressed plugin should announce itself to QuArK, so it will be listed under Options|List of Plugins. This is done by including an Info statement, normally at the beginning of the file:

Info = {
   "plug-in":       "Side Tag & Glue",
   "desc":          "Basic Side tagging and gluing to tagged side",
   "date":          "1999",
   "author":        "tiglari",
   "author e-mail": "tiglari@hexenworld.net",
   "quark":         "Version 6" }

So now we've got a proper plugin; next is to get it to do something useful.



Copyright (c) 2022, GNU General Public License by The QuArK (Quake Army Knife) Community - https://quark.sourceforge.io/

 [ Prev - Top - Next ]