wxWidgets Accessing TopFrame of Another Process - c++

I have an application which has the parent frame as the MainFrame and two child frames, ChildA and ChildB. This should be a document oriented GUI such that when user clicks a document, it should be able to open another instance of FrameA.
When I click the executable of GUI MainFrame, ChildA and ChildB loads normally. However when the executable is running and when I click a document I realized that Windows is opening it as another process such that MainFrame, ChildA and ChildB loads again which I dont want cause I only want to open another instance of ChildA to show the contents of the document.
My code is as follows:
bool MainFrameApp::OnInit()
{
m_AnotherInstanceRunning=false;
m_checker = new wxSingleInstanceChecker;
if(m_checker->IsAnotherRunning()) m_AnotherInstanceRunning=true;
//Some other code
if(!m_AnotherInstanceRunning)
{
frame= new MainFrame(0L);
std::cout<<"Frame:"<<frame; //Prints the address
auto m_ChildA=new SomeChildFrame(frame);
frame->Show();
m_FrameA->Show(true);
wxTheApp->SetTopWindow(frame);
}
if(m_AnotherInstanceRunning)
{
frame=dynamic_cast<MainFrame*>(wxTheApp->GetTopWindow());
std::cout<<"Frame:"<<frame; //prints 0
frame->OpenDocument(frame,strDocDirectory,strDocName); //Does not work
return false;
}
My question is, when I click on a document how can I open that document within an already running MainFrame.
EDIT 1:
This is how I achieved it after countless of hours...
Below is the code for MainFrame.h
const int SERVER_ID=wxNewId();
const int SERVERSOCKET_ID=wxNewId();
const int CLIENTSOCKET_ID=wxNewId();
class MainFrame;
class MainFrameApp : public wxApp
{
bool m_AnotherInstanceRunning;
wxSocketServer* m_server;
wxString m_DocDirectoryPath;
wxString m_DocFullName; //including extension
protected:
virtual void OnServerEvent(wxSocketEvent& event);
virtual void OnServerSocketEvent(wxSocketEvent& event);
virtual void OnClientSocketEvent(wxSocketEvent& event);
public:
MainFrame* m_MainFrame;
wxSingleInstanceChecker* m_checker;
};
static const wxCmdLineEntryDesc cmdLineDesc[] =
{
{wxCMD_LINE_PARAM, NULL, NULL, "", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_MULTIPLE},
{wxCMD_LINE_NONE},
};
Below is the code for MainFrame.cpp
bool ScienceSuitApp::OnInit()
{
m_AnotherInstanceRunning=false;
m_checker = new wxSingleInstanceChecker;
if(m_checker->IsAnotherRunning()) m_AnotherInstanceRunning=true;
wxFileName exePath(wxStandardPaths::Get().GetExecutablePath());
glbStrExeDir=exePath.GetPath()+wxFileName::GetPathSeparator();
wxString strDocFullPath; //This is directory+name
if(argc>1)
{
wxCmdLineParser parser(cmdLineDesc, argc, argv);
parser.Parse();
strDocFullPath=argv[1];
wxFileName docPath(strDocFullPath);
m_DocDirectoryPath=docPath.GetPath()+wxFileName::GetPathSeparator();
m_DocFullName=docPath.GetFullName(); //we needed extension of document as well
}
if(!m_AnotherInstanceRunning)
{
//Some other code
//Here Process #1
m_MainFrame= new MainFrame(0L);
m_MainFrame->Show();
wxIPV4address addr;
addr.Service(3000);
m_server = new wxSocketServer(addr,wxSOCKET_WAITALL);
if (!m_server->Ok())
{
if (m_server->LastError() == wxSOCKET_INVPORT) wxMessageBox(wxT("Port in use!"));
m_server->Destroy();
}
m_server->SetEventHandler(*this, SERVER_ID);
m_server->SetNotify(wxSOCKET_CONNECTION_FLAG);
m_server->Notify(true);
Connect(SERVER_ID,wxID_ANY, wxEVT_SOCKET,wxSocketEventHandler(MainFrameApp::OnServerEvent));
Connect(SERVERSOCKET_ID,wxID_ANY,wxEVT_SOCKET,wxSocketEventHandler(MainFrameApp::OnServerSocketEvent));
}
if(m_AnotherInstanceRunning)
{
//Here is for Process #2
Connect(CLIENTSOCKET_ID,wxID_ANY,wxEVT_SOCKET,wxSocketEventHandler(MainFrameApp::OnClientSocketEvent));
wxIPV4address addr;
addr.Hostname(wxT("localhost"));
addr.Service(3000);
wxSocketClient* Socket = new wxSocketClient();
Socket->SetEventHandler(*this, CLIENTSOCKET_ID);
Socket->SetNotify(wxSOCKET_CONNECTION_FLAG | wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG);
Socket->Notify(true);
if(!Socket->Connect(addr, true)) wxMessageBox("Failed to connect to server");
return true;
}
return true;
}
void MainFrameApp::OnServerEvent(wxSocketEvent& event) //Process #1
{
wxSocketBase* sock = m_server->Accept(false);
sock->SetEventHandler(*this, SERVERSOCKET_ID);
sock->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG);
sock->Notify(true);
}
void MainFrameApp::OnServerSocketEvent(wxSocketEvent& event) //Process #1
{
wxSocketBase *sock = event.GetSocket();
wxCharBuffer buf(256);
switch(event.GetSocketEvent())
{
case wxSOCKET_CONNECTION:
{
break;
}
case wxSOCKET_INPUT:
{
sock->Read(buf.data(), 255);
wxString pathstr=wxString::FromUTF8(buf);
sock->Destroy();
wxFileName docPath(pathstr);
wxString DocDirectoryPath=docPath.GetPath()+wxFileName::GetPathSeparator();
wxString DocFullName=docPath.GetFullName(); //we needed extension of document as well
m_MainFrame->OpenDocument(m_MainFrame,DocDirectoryPath,DocFullName);
break;
}
case wxSOCKET_LOST:
{
sock->Destroy();
break;
}
}
}
void MainFrameApp::OnClientSocketEvent(wxSocketEvent& event) //Process #2
{
wxString str=m_DocDirectoryPath+m_DocFullName;
wxSocketBase* sock = event.GetSocket();
switch(event.GetSocketEvent())
{
case wxSOCKET_CONNECTION:
{
sock->Write(str.mb_str(),str.length());
}
}
//Exit the client process (Process #2) staying in the background, otherwise and no messages are sent
//Also we dont want to spawn background processes
exit(0);
}

If you want to prevent more than one instance of your application running at the same time, you can use wxSingleInstanceChecker to check if another one is running and use the IPC classes to send the name of the document to open to the other instance if it is.

Related

Glass - Slow camera \ FileObserver notification - XE12 - using ACTION_IMAGE_CAPTURE

I have basically implemented the new XE12\GDK2 cameramanager sample code to capture an image on application start. However, the notification to the FileObserver callback takes anywhere from 3 to 30 seconds to get the notification of the image file creation. Taking a picture using the default 'Take a Picture' app works just fine so I dont thin it is an OS\update issue.
My app's behavior is like:
- Take the picture
- Tap to accept
Wait 3 to 30 seconds
- Get the callback and the imageview is updated with the captured image.
I dont think I have modified a single line of the sample code provided in the GDK 2.0 camera tutorial. So wondering what I am missing.
I have attached the relevant section of the code below. Any tips\pointers highly appreciated.
#Override
protected void onStart() {
super.onStart();
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// String path = Environment.getExternalStorageDirectory().getPath();
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
private void processPictureWhenReady(final String picturePath) {
final File pictureFile = new File(picturePath);
if (pictureFile.exists()) {
// The picture is ready; process it. Takes 3-30 seconds to get here!
try {
Bitmap imageBitmap = BitmapFactory.decodeFile(picturePath);
int w = imageBitmap.getWidth();
int h = imageBitmap.getHeight();
Bitmap bm2 = Bitmap.createScaledBitmap(imageBitmap, w/2, h/2, true);
imageBitmap = bm2.copy(bm2.getConfig(), true);
//m_ImageView.setImageBitmap(bm2);
} catch (Exception e) {
Log.e("Exc", e.getMessage());
}
} else {
tm = System.currentTimeMillis();
// The file does not exist yet. Before starting the file observer, you
// can update your UI to let the user know that the application is
// waiting for the picture (for example, by displaying the thumbnail
// image and a progress indicator).
final File parentDirectory = pictureFile.getParentFile();
FileObserver observer = new FileObserver(parentDirectory.getPath()) {
// Protect against additional pending events after CLOSE_WRITE is
// handled.
private boolean isFileWritten;
#Override
public void onEvent(int event, String path) {
if (!isFileWritten) {
// For safety, make sure that the file that was created in
// the directory is actually the one that we're expecting.
File affectedFile = new File(parentDirectory, path);
isFileWritten = (event == FileObserver.CLOSE_WRITE
&& affectedFile.equals(pictureFile));
if (isFileWritten) {
stopWatching();
// Now that the file is ready, recursively call
// processPictureWhenReady again (on the UI thread).
runOnUiThread(new Runnable() {
#Override
public void run() {
processPictureWhenReady(picturePath);
}
});
}
}
}
};
observer.startWatching();
}
}
Answering my own question - though I got the clarifications from Jenny Murphy and John Feig :-). Hopefully it helps others.
To the first point - why is image capture using the sample code from the GDK guide so slow:
This is the expected behavior. The Glass camera intent (ACTION_IMAGE_CAPTURE) performs a ton of proprietary post-processing on the captured image - auto-HDR etc which takes time. This is cleverly disguised in the 'Take a picture' command by only displaying the preview image (which is available immediately.). As proof, try to find the image you just took in your time-line. You will not see it for several seconds (around 8 seconds on average in my experience.).
Frankly, unless you are ok just grabbing the preview image, the camera intent may not be very useful in most apps.
The solution is to use the Camera directly using default Android APIs. For convenience, I have pasted a snippet of this code. Please excuse if it is kind of basic for many of you. A lot of the code is copied from John Feig's GIFCamera glassware on GitHub
activity_main layout contains a SurfaceView called preview
<SurfaceView
android:id="#+id/preview"
android:layout_width="500dp"
android:layout_height="500dp"
android:layout_alignParentTop="true"
android:layout_marginTop="20dp"
/>
MainActivity.java
public class MainActivity extends Activity implements PhotoCallback {
public byte[] m_jpg = null;
Camera cam = null;
SurfaceHolder m_sh;
private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder hldr) {
m_sh = hldr;
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
myCapHandler2(); //Start Camera Preview etc.
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SurfaceView preview = (SurfaceView) findViewById(R.id.preview);
preview.getHolder().addCallback(mSurfaceHolderCallback);
}
public void myCapHandler2() {
//open camera
try {
cam = Camera.open(0);
Camera.Parameters params = cam.getParameters();
List<Size> sizes = params.getSupportedPreviewSizes();
params.setJpegQuality(90);
params.setPreviewFpsRange(30000, 30000);
params.setPictureSize(sizes.get(1).width, sizes.get(1).height);
params.setPreviewSize(sizes.get(1).width, sizes.get(1).height);
cam.setParameters(params);
try {
cam.setPreviewDisplay(m_sh);
}
catch (IOException e) {
e.printStackTrace();
}
// Important: Call startPreview() to start updating the preview
// surface. Preview must be started before you can take a picture.
cam.startPreview();
cam.takePicture(null, null,
new PhotoHandler(this));
} catch (Exception e) {
if (null != cam) {
cam.stopPreview();
cam.release();
}
}
}
#Override
public void pictureTaken(byte[] jpg) {
m_jpg = jpg;
//Picture captured - release the camera for other apps
cam.stopPreview();
cam.release();
}
#Override
public void onPause() {
if (null != cam) {
cam.stopPreview();
cam.release();
}
}
#Override
public void onDestroy() {
if (null != cam) {
cam.stopPreview();
cam.release();
}
}
}
PhotoHandler.java
import android.hardware.Camera;
import android.os.AsyncTask;
public class PhotoHandler implements Camera.PictureCallback {
private PhotoCallback photoCallback;
public PhotoHandler(PhotoCallback photoCallback) {
super();
this.photoCallback = photoCallback;
}
#Override
public void onPictureTaken(byte[] data, Camera camera) {
new ProcessCapturedImage().execute(data);
}
private class ProcessCapturedImage extends AsyncTask<byte[], Void, byte[]> {
#Override
protected byte[] doInBackground(byte[]... params) {
if (null == params || null == params[0])
return null;
return params[0];
}
#Override
protected void onPostExecute(byte[] params) {
photoCallback.pictureTaken(params);
}
}
}
PhotoCallback.java
public interface PhotoCallback {
public void pictureTaken(byte[] jpg);
}
All the best with your camera glassware.

Google Glass GDK CameraManager Intent

Does anyone know if when you use the GDK Cameramanager Intent to take a picture, is there a way to not show the preview or close it automatically? Capturing an image for use in app and don't want to have to tap to accept.
I probably have missed something.
Thanks,
You can try this:
Intent localIntent = new Intent("com.google.glass.action.TAKE_PICTURE_FROM_SCREEN_OFF");
localIntent.putExtra("should_finish_turn_screen_off", true);
localIntent.putExtra("should_take_picture", true);
localIntent.putExtra("screenshot_file_path", pathToFile);
startActivity(localIntent);
It will close your preview automatically after few seconds.
try this...
private void takePicture() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, 0);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 0 && resultCode == RESULT_OK) {
String picturePath=data.getStringExtra(CameraManager.EXTRA_PICTURE_FILE_PATH);
processPictureWhenReady(picturePath);
}
super.onActivityResult(requestCode, resultCode, data);
}
private void processPictureWhenReady(final String picturePath) {
final File pictureFile = new File(picturePath);
if (pictureFile.exists()) {
// The picture is ready; process it.
// Write your code here
} else {
final File parentDirectory = pictureFile.getParentFile();
FileObserver observer = new FileObserver(parentDirectory.getPath()) {
// Protect against additional pending events after CLOSE_WRITE is
// handled.
private boolean isFileWritten;
#Override
public void onEvent(int event, String path) {
if (!isFileWritten) {
// For safety, make sure that the file that was created in
// the directory is actually the one that we're expecting.
File affectedFile = new File(parentDirectory, path);
isFileWritten = (event == FileObserver.CLOSE_WRITE
&& affectedFile.equals(pictureFile));
if (isFileWritten) {
stopWatching();
// Now that the file is ready, recursively call
// processPictureWhenReady again (on the UI thread).
runOnUiThread(new Runnable() {
#Override
public void run() {
processPictureWhenReady(picturePath);
}
});
}
}
}};
observer.startWatching();
}
}

Repaining listfield on editfield change Input

I am using Google place API in my application for searching location. When user input text in edit field then the API called and resulted output will shown in a list.
I implemented it successfully but the problem is that each time edit field text changes, the list is not repainting and output is added to the end of the list. I want every time the text changes in the edit text field, the list must remove its previous content that are invalid.
This can be seen in pictures:
For Implementing this, I have written this code:
public final class MyScreen extends MainScreen {
/**
* Creates a new MyScreen object
*/
private Vector _listElements;
ListField list;
JSONObject[] jsonobject;
EditField editfield;
String url = "https://maps.googleapis.com/maps/api/place/autocomplete/json?input=";
String[] locationName;
VerticalFieldManager verticalFieldManager = new VerticalFieldManager();
public MyScreen() {
ButtonField search = new ButtonField("Search");
_listElements = new Vector();
list = new ListField();
ListCallback _callback = new ListCallback(this);
// Set the displayed title of the screen
setTitle("Search Edit Field");
editfield = new EditField();
editfield.setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
list.invalidate();
createField();
}
});
}
});
list.setCallback(_callback);
add(editfield);
add(new SeparatorField());
verticalFieldManager.add(list);
add(verticalFieldManager);
}
protected void createField() {
ShowList();
reloadList();
}
private void reloadList() {
list.setSize(_listElements.size());
}
class ListCallback implements ListFieldCallback {
MyScreen listDemoScreen;
public ListCallback(MyScreen listDemoScreen) {
this.listDemoScreen = listDemoScreen;
}
public void drawListRow(ListField list, Graphics g, int index, int y,
int w) {
String text = (String) _listElements.elementAt(index);
list.setRowHeight(getFont().getHeight());
g.drawText(text, 0, y, 0, -1);
}
public Object get(ListField list, int index) {
return _listElements.elementAt(index);
}
public int indexOfList(ListField list, String prefix, int string) {
return _listElements.indexOf(prefix, string);
}
public int getPreferredWidth(ListField list) {
return Display.getWidth();
}
}
protected void ShowList() {
HttpConnection httpConn;
InputStream in;
ConnectionFactory connFact = new ConnectionFactory();
ConnectionDescriptor connDesc;
String response;
String fieldText = editfield.getText();
connDesc = connFact.getConnection(url + fieldText
+ "%#&sensor=true&key=xxxxxxxxxxxxx"
+ ConnectionType.getConnectionType());
if (connDesc != null) {
httpConn = (HttpConnection) connDesc.getConnection();
try {
int responseCode = httpConn.getResponseCode();
if (responseCode == HttpConnection.HTTP_OK) {
in = httpConn.openInputStream();
StringBuffer buf = new StringBuffer();
int read = -1;
while ((read = in.read()) != -1)
buf.append((char) read);
response = buf.toString();
try {
JSONObject object = new JSONObject(response);
JSONArray ar = object.getJSONArray("predictions");
jsonobject = new JSONObject[ar.length()];
locationName = new String[ar.length()];
list.invalidate();
for (int i = 0; i < ar.length(); i++) {
jsonobject[i] = ar.getJSONObject(i);
_listElements.addElement(jsonobject[i]
.getString("description"));
}
} catch (JSONException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
} else {
Dialog.alert("Connection not succeded");
}
}
protected boolean onSavePrompt() {
return true;
}
}
Update and Solution:
only modify this and this rest is working fine. As Peter Suggests, we can also put a Thread.sleep(time); in order to get the UI not blocked:
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
_listElements.removeAllElements();
createField();
}
});
I think your problem is simply that you do not clear the _listElements Vector when you request more data. So _listElements just gets bigger.
However there is a bigger problem here and that is that your code appears to be running networking operations on the Event Thread. What your should do in your changeListener. is start a Thread that requests the data, then repopulate the ListField when this tread gets data.
As a result of this change, the UI will not be blocked, and the List updates will become asynchronous, so your user could in fact enter another character into the EditField before the first Thread response comes back. To prevent this looking silly, you could delay the Thread processing for a fraction of second to see if another character is entered, and/or you could make sure that the EditField content was still the same as the requested characters before you repopulate it.
I personally prefer this asynchronous approach, but if it bothers you, you could put a 'please wait - loading' type screen to block the user until the response comes back.
Update
Remember that if you start a background Thread, you need to get back onto the Event Thread to do Ui Processing. Typically this is done simply by including your UI code within the run method of a Runnable that is invoked later, for example:
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
// Ui Code in here
}
});
You should only put Ui Updating code in the runnable. Networking processing, or any other blocking action, should NOT be included.

Windows Form Show vs ShowDialog

I have a small application that I am trying to create for windows. I am running into an issue with mixing a background thread designed to process some data. This background engine someone needs to both update the application gui (A windows Form) and get information from it.
Here is the basic application main.
int main() {
Engine engine;
Gui g;
engine.run(); // creates a new thread for engine logic
g.ShowDialog();
bool running = false;
while(1)
{
// Update gui with information from the engine
g.update(engine.GetState());
// transition to running
if(g.isRunning() && !running)
{
engine.play();
running = true;
}
// transition to stopped
else if(!g.isRunning() && running)
{
engine.stop();
running = false;
}
}
}
My main problem comes from the fact that Gui class is managed. See Class declaration below.
public ref class Gui : public System::Windows::Forms::Form
I am not really able to mix these two things, at first I had wanted to just throw the Engine into the Gui but that doesn't work since it is unmanaged.
You will note that the problem here is calling ShowDialog() as this makes the dialog modal and no code afterwards is executed. However, if I use Show() ... the Gui simply doesn't update or process any inputs.
SOLUTION:
I created a background worker in the Gui class so the engine is contained within the Gui but is ran on another thread.
void InitializeBackgoundWorker()
{
this->backgroundWorker1 = gcnew System::ComponentModel::BackgroundWorker;
backgroundWorker1->DoWork += gcnew DoWorkEventHandler( this, &Gui::backgroundWorker1_DoWork );
backgroundWorker1->RunWorkerAsync( );
}
delegate void UpdateCallback(int hp, int maxhp);
void UpdateGui(int hp, int maxhp)
{
this->playerHealthBar->Value = ((float)(hp)/(float)(maxhp) * 100.0f);
};
void backgroundWorker1_DoWork( Object^ sender, DoWorkEventArgs^ e )
{
aBotEngine engine;
while(true)
{
engine.runnable(NULL);
array<Object^>^args = gcnew array<Object^>(2);
args[0] = engine.getPlayerHp();
args[1] = engine.getPlayerMaxHp();
this->playerHealthBar->Invoke(gcnew UpdateCallback(this, &Gui::UpdateGui), args);
}
};
As far as I can tell this is the proper way to have background thread that updates your windows form. I'm sure its not the only way.
void InitializeBackgoundWorker()
{
this->backgroundWorker1 = gcnew System::ComponentModel::BackgroundWorker;
backgroundWorker1->DoWork += gcnew DoWorkEventHandler( this, &Gui::backgroundWorker1_DoWork );
backgroundWorker1->RunWorkerAsync( );
}
delegate void UpdateCallback(int hp, int maxhp);
void UpdateGui(int hp, int maxhp)
{
this->playerHealthBar->Value = ((float)(hp)/(float)(maxhp) * 100.0f);
};
void backgroundWorker1_DoWork( Object^ sender, DoWorkEventArgs^ e )
{
aBotEngine engine;
while(true)
{
engine.runnable(NULL);
array<Object^>^args = gcnew array<Object^>(2);
args[0] = engine.getPlayerHp();
args[1] = engine.getPlayerMaxHp();
this->playerHealthBar->Invoke(gcnew UpdateCallback(this, &Gui::UpdateGui), args);
}
};

Multithreading in wxWidgets GUI apps?

I'm having problem (basically, I'm confused.) while trying to create a worker thread for my wxWidgets GUI application that WILL MODIFY one of the GUI property itself. (In this case, wxTextCtrl::AppendText).
So far, I have 2 source files and 2 header files for the wx program itself (excluding my own libs, MySQL lib, etc), say MainDlg.cpp which contains a derived class of wxFrame called 'MainDlg' and MainForm.cpp which contains a derived class of wxApp called 'MainForm'.
MainForm.cpp
#include "MainHeader.h" // contains multiple header files
IMPLEMENT_APP(MainForm)
bool MainForm::OnInit()
{
MainDlg *Server = new MainDlg(wxT("App Server 1.0"), wxDEFAULT_FRAME_STYLE - wxRESIZE_BORDER - wxMAXIMIZE_BOX);
Editor->Show();
return true;
}
MainDlg.cpp:
#include "MainHeader.h"
BEGIN_EVENT_TABLE(MainDlg, wxFrame)
EVT_BUTTON(6, MainDlg::StartServer)
EVT_BUTTON(7, MainDlg::StopServer)
END_EVENT_TABLE()
CNETServerConnection *cnServCon;
std::string ServerIP, DBHost, DBUser, DBName, DBPass;
int UserCapacity, DBPort, ServerPort;
MYSQL *sqlhnd;
MainDlg::MainDlg(const wxString &title, long style) : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(301, 230), style)
{
cnServCon = new CNETServerConnection(100);
this->InitializeComponent();
}
void MainDlg::InitializeComponent()
{
this->SetTitle(wxT("App Server 1.0"));
this->SetSize(396, 260);
this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
this->Centre();
statBox = new wxTextCtrl(this, 4, wxT("Welcome to AppServer 1.0\n\n"), wxPoint(10, 10), wxSize(371, 141), wxTE_MULTILINE | wxTE_READONLY);
//.................................
}
void MainDlg::StartServer(wxCommandEvent &event)
{
this->startBtn->Enable(false);
this->AppendStatus(wxT("\nLoading server configuration... "));
//.................................
this->AppendStatus(wxT("OK\n\nServer ready!\n\n"));
// When the server is ready, I need to run a thread
// that will update the statBox (through AppendStatus func or wxTextCtrl::AppendText directly)
// regularly without interrupting the GUI itself.
// Because the thread will contain a while-loop
// to make the program keep receiving message from clients.
this->startBtn->Hide();
this->stopBtn->Show();
this->cmdBtn->Enable();
this->cmdBox->Enable();
}
void MainDlg::StopServer(wxCommandEvent &event)
{
//...................................
}
void MainDlg::AppendStatus(const wxString &message)
{
statBox->AppendText(message);
}
// Well, here is the function I'd like to run in a new thread
void MainDlg::ListenForMessages()
{
int MsgSender = 0;
while(1)
{
if(!cnServCon->GetNewMessage())
continue;
if(MsgSender = cnServCon->GetJoiningUser())
this->AppendStatus(wxT("Someone connected to the server."));
}
}
I also found an usage example of wxThread from Simple example of threading in C++:
class MessageThread : public wxThread
{
private:
MessageThread(const MessageThread &copy);
public:
MessageThread() : wxThread(wxTHREAD_JOINABLE)
{
}
void *Entry(void)
{
// My works goes here
return;
}
};
wxThread *CreateThread()
{
wxThread *_hThread = new MessageThread();
_hThread->Create();
_hThread->Run();
return _hThread;
}
But I don't know how to associate it with my program and make it able to modify my GUI property (statBox).
Any kind of help would be appreciated! :)
Thanks.
The easiest is to create an event type id, and use a wxCommandEvent using the type id, set its string member ("evt.SetText"), and send the event to one of your windows (using AddPendingEvent). In the handler of that event, you can then call AppendText on your control using the text you sent (evt.GetText), because you are in the GUI thread by then.
// in header
DECLARE_EVENT_TYPE(wxEVT_MY_EVENT, -1)
// in cpp file
DEFINE_EVENT_TYPE(wxEVT_MY_EVENT)
// the event macro used in the event table. id is the window id you set when creating
// the `wxCommandEvent`. Either use -1 or the id of some control, for example.
EVT_COMMAND(window-id, event-id, handler-function)
Here is an overview how it works: Custom Events.