wxWidgets: Loading Symbols from a DLL (and using ::Connect)
[Apologies if the entry is a little unclear, I just threw this together in about 10 minutes, I wanna get it out there... it's fucking hard to find any sort of information when it comes to this at the moment]
Quick entry, gonna turn this into a tutorial soon. The code is a bit of a mess, since I was just throwing stuff together while I was figuring this out. I’ll tidy it up when it’s tutorial tiems.
Basically, I recently developed a [url=http://snip.gd/plugins.php]plugin system[/url] for my [url=http://snip.gd/]snippet manager[/url]. The way it works is simple: It loads symbols from a DLL into the application, which can be called from a dynamically created menu. First off, the main code [this is the messy bit for the time being]
This code opens a subdirectory within the working directory called “plugins”, and grabs all the DLLs from them. If the DLL contains a symbol called SnippetManagerPluginMain (the only required symbol for the plugins, I kept them simple) it will add it to the menu.
Additionally, it also checks for two optional optional symbols, which are pretty cool. They simply return strings, which assign the name and the keyboard shortcut for the plugin. If neither is set, it has no keyboard shortcut, and the name defaults to the name of the DLL file.
/// <plugin SYSTEM> <!-- ALPHA! --> #if defined(__WXMSW__) // windows plugins - load ./plugins/*.dll if(wxDir::Exists(wxT("./plugins/"))) { wxDir plugin_dir(wxT("./plugins/")); if(plugin_dir.HasFiles()) { wxArrayString files; wxDir::GetAllFiles(wxT("./plugins/"), &files); wxMenu* plugin_menu = new wxMenu(wxT("")); wxDynamicLibrary* lib; wxString label; char* ks; bool showmenu = false; for(unsigned int i = 0; i < files.GetCount(); i++) { lib = new wxDynamicLibrary(); if(files.Item(i).Find(wxT(".dll")) != -1) { lib->Load(files[i]); if(lib->HasSymbol(L"SnippetManagerPluginMain")) { showmenu = true; if(lib->HasSymbol(L"SnippetManagerPluginName")) { label = wxString(((SnippetManagerPluginName)lib->GetSymbol(L"SnippetManagerPluginName"))(), wxConvUTF8); } else { label = files[i].AfterFirst('\\').AfterFirst('\\').BeforeLast('.'); } this->plugins.push_back((SnippetManagerPlugin)lib->GetSymbol(L"SnippetManagerPluginMain")); if(lib->HasSymbol(L"SnippetManagerKeyboardShortcut")) { ks = ((SnippetManagerKeyboardShortcut)lib->GetSymbol(L"SnippetManagerKeyboardShortcut"))(); label += L"\t" + wxString(ks, wxConvUTF8); } plugin_menu->Append(wxID_PLUGIN + i, label); Connect(wxID_PLUGIN + i, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(Snippet_ManagerFrame::handle_plugin)); } } } if(showmenu) { mbar->Append(plugin_menu, wxT("&Plugins")); } } } #endif /// </plugin>[
In order to bind the events to the menu, the ::Connect method is called. This is an alternative to using EVENT_TABLE, and allows events to be allocated dynamically at runtime. wxID_PLUGIN is a custom variable, and basically, any of the menu items within the menu will call the main frame’s [il]handle_plugin[/il] method. The important part, however, is the fact that each one has a different ID, even though they use the same method.
The method itself then uses the ID of the event calling it to lookup the DLL’s symbol, which were added in this->plugins.
void Snippet_ManagerFrame::handle_plugin(wxCommandEvent &e) { this->plugins[e.GetId() - wxID_PLUGIN](this->ui->uid.mb_str(), fill_p_snip(this->ui->get_active_stc()->snip, this->ui->get_active_stc()->GetText())); }
Of course, some of the code here doesn’t really mean jack all, since there are custom methods and stuff used in it. But the base of it is clear. I’ll try to write another (small) application and a tutorial covering the steps over the weekend.
Until then, have fun! :)
wxWidgets: Threading, and using the Clipboard
wxWidgets: Threading, and using the Clipboard
Two for one in this tutorial. We're going to create a thread using wxWidgets, and make it monitor the clipboard for text. Then, if we detect a change, we're going to add the new contents to a list. It's a relatively simple process, but unfortunately wxWidgets isn't the most documented GUI toolkit out there. So, let's get started. I'm gonna pile all the code into a single file instead of breaking my classes up into seperate files, just for ease of navigation in this tutorial. The code is relatively short anyway, with only 62 lines. First off, as with any other program, we're going to want to get our includes done. We'll be needing three: The standard wx header, the clipboard header, and the thread header.#include <wx/wx.h> // standard wx header #include <wx/clipbrd.h> // clipboard - we'll monitor the clipboard for text #include <wx/thread.h> // include threads!
ClipLogger class - this will inherit wxThread, and have two variables: One to hold the most recent text that we got from the clipboard, and one which is a pointer to a wxListBox object -- the object on the main window which we'll be updating.
class ClipLogger : public wxThread { public: ClipLogger(wxListBox*); private: void* Entry(); // the entry point to the thread wxString LatestText; // store the last text so we can check for changes wxListBox* list; // the list to update on a text change };
ClipLogger::ClipLogger(wxListBox* l) { this->list = l; }
Entry() method of the class, like so:
void* ClipLogger::Entry() {
wxTextDataObject temp;while (true) {
wxSleep(int time), where time is the length of time to sleep in seconds.
wxSleep(1);
temp variable
- Update the list and remember this is the most recent (so as not to constantly add the same data to the list)
- Close the clipboard
if (wxTheClipboard->Open()) { // try to open the clipboard if (wxTheClipboard->IsSupported(wxDF_TEXT)) { // if the clipboard contains text wxTheClipboard->GetData(temp); // get the data from the clipboard if (this->LatestText != temp.GetText()) { // if it's changed, we want to update if (temp.GetText() != wxT("")) { // if it's not an empty string this->LatestText = temp.GetText(); // update the "LatestText" this->list->Append(temp.GetText()); // append to the list } } } } wxTheClipboard->Close(); // close the clipboard
} }
class threaded_app : public wxApp { public: bool OnInit(void); };
OnInit() should be nothing new at this point either.
bool threaded_app::OnInit(void) { // quickly create a wxFrame to display a window wxFrame* f = new wxFrame(NULL, wxID_ANY, wxT("Threaded App!")); // add a list to the frame we created wxListBox* list = new wxListBox(f, wxID_ANY); // display the frame f->Show(true);
OnInit() that we need to do is actually create an instance of our thread and run it, like so:
// pass the list to the clipboard monitor so it knows what to update ClipLogger* cl = new ClipLogger(list); // construct our thread cl->Create(); // we have to create a thread before we can run it cl->Run(); // run our thread
OnInit() and IMPLEMENT_APP:
return true; } IMPLEMENT_APP(threaded_app)
#include <wx/wx.h> // standard wx header #include <wx/clipbrd.h> // clipboard - we'll monitor the clipboard for text #include <wx/thread.h> // include threads! class ClipLogger : public wxThread { public: ClipLogger(wxListBox*); private: void* Entry(); // the entry point to the thread wxString LatestText; // store the last text so we can check for changes wxListBox* list; // the list to update on a text change }; ClipLogger::ClipLogger(wxListBox* l) { this->list = l; } void* ClipLogger::Entry() { wxTextDataObject temp; // create a "wxTextDataObject" to get the info from the clipboard while (true) { // our thread will loop wxSleep(1); // sleep for 1 second, make the thread less cpu-hungry if (wxTheClipboard->Open()) { // try to open the clipboard if (wxTheClipboard->IsSupported(wxDF_TEXT)) { // if the clipboard contains text wxTheClipboard->GetData(temp); // get the data from the clipboard if (this->LatestText != temp.GetText()) { // if it's changed, we want to update if (temp.GetText() != wxT("")) { // if it's not an empty string this->LatestText = temp.GetText(); // update the "LatestText" this->list->Append(temp.GetText()); // append to the list } } } } wxTheClipboard->Close(); // close the clipboard } } class threaded_app : public wxApp { public: bool OnInit(void); }; bool threaded_app::OnInit(void) { // quickly create a wxFrame to display a window wxFrame* f = new wxFrame(NULL, wxID_ANY, wxT("Threaded App!")); // add a list to the frame we created wxListBox* list = new wxListBox(f, wxID_ANY); // display the frame f->Show(true); // pass the list to the clipboard monitor so it knows what to update ClipLogger* cl = new ClipLogger(list); // construct our thread cl->Create(); // we have to create a thread before we can run it cl->Run(); // run our thread return true; } IMPLEMENT_APP(threaded_app)
wxWidgets: Keyboard Shortcuts without a Menu
Quick entry. Sort of a preview. I’m working on a tutorial for dream.in.code about creating keyboard shortcuts in a wxWidgets application (particularly using C++), and figured I’d share the code here, too.
(After I’ve finally gotten around to installing this syntax highlighting plugin for WordPress)
Aaaaannddd… Done!
Right. Back onto the original topic. Damn sidetracked mind. So, when I write a tutorial, I write a whole chunk of code first, and then add the tutorial content around it. Here’s the code for allocating keyboard shortcuts to do different stuff in wxWidgets without using menu items.
// basic setup code, I'll fly past this. If you're not familiar, check out this tutorial: // http://www.dreamincode.net/forums/showtopic66948.htm #include <wx/wx.h> class app : public wxApp { public: virtual bool OnInit(); private: wxTextCtrl* txt; wxCheckBox* chk; void key_shortcut(wxKeyEvent&); DECLARE_EVENT_TABLE() }; // if you're not familiar with events, check out this tutorial: // http://www.dreamincode.net/forums/showtopic67058.htm BEGIN_EVENT_TABLE(app, wxApp) EVT_KEY_DOWN(app::key_shortcut) END_EVENT_TABLE() bool app::OnInit() { wxFrame* win = new wxFrame(NULL, wxID_ANY, wxT("Keyboard Shortcuts, No Menu!"), wxDefaultPosition, wxSize(250, 125)); win->Show(true); this->txt = new wxTextCtrl(win, wxID_ANY, wxT("Press Ctrl+G to append text to me"), wxPoint(0,0), wxSize(250, 50), wxTE_MULTILINE); this->chk = new wxCheckBox(win, wxID_ANY, wxT("Press Ctrl+Left to toggle me."), wxPoint(0, 55), wxDefaultSize); return true; } // this is the method I'll be focusing on. The key_shortcut event which we create // to do fancy stuff without adding billions of menu items void app::key_shortcut(wxKeyEvent& e) { // of course, it doesn't have to be the control key. You can use others: // http://docs.wxwidgets.org/stable/wx_wxkeyevent.html if(e.GetModifiers() == wxMOD_CONTROL) { switch(e.GetKeyCode()) { case 'G': // can return the upper ASCII value of a key // do whatever you like for a Ctrl+G event here! this->txt->AppendText(wxT(" gabehabe ftw!")); break; case WXK_LEFT: // we also have special keycodes for non-ascii values. // get a full list of special keycodes here: // http://docs.wxwidgets.org/stable/wx_keycodes.html this->chk->SetValue(!this->chk->GetValue()); break; default: // do nothing break; } } } IMPLEMENT_APP(app)
Link to follow when the tutorial is completed and approved.
As promised, here’s the tutorial in all it’s glory:
wxWidgets keyboard shortcuts with no menu
Categories
Blogroll
- My other blog
- Beki Sutcliffe
- Charlie Baker
- Denis Delimarschi
- Derek Ackley
- Ian Mellett
- Joe Otero
- Josh Erickson
- Ryan Alford
- Cupcake Hax0r (I write here!)